LCOV - code coverage report
Current view: top level - port - cpl_vsil_unix_stdio_64.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 392 435 90.1 %
Date: 2026-02-12 06:20:29 Functions: 39 39 100.0 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Implement VSI large file api for Unix platforms
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  **********************************************************************
       8             :  * Copyright (c) 2001, Frank Warmerdam
       9             :  * Copyright (c) 2010-2026, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************
      13             :  *
      14             :  * NB: Note that in wrappers we are always saving the error state (errno
      15             :  * variable) to avoid side effects during debug prints or other possible
      16             :  * standard function calls (error states will be overwritten after such
      17             :  * a call).
      18             :  *
      19             :  ****************************************************************************/
      20             : 
      21             : //! @cond Doxygen_Suppress
      22             : 
      23             : #if !defined(_WIN32)
      24             : 
      25             : // #define VSI_COUNT_BYTES_READ
      26             : // #define VSI_DEBUG
      27             : 
      28             : // Some unusual filesystems do not work if _FORTIFY_SOURCE in GCC or
      29             : // clang is used within this source file, especially if techniques
      30             : // like those in vsipreload are used.  Fortify source interacts poorly with
      31             : // filesystems that use fread for forward seeks.  This leads to SIGSEGV within
      32             : // fread calls.
      33             : //
      34             : // See this for hardening background info: https://wiki.debian.org/Hardening
      35             : #undef _FORTIFY_SOURCE
      36             : 
      37             : // 64 bit IO is only available on 32-bit android since API 24 / Android 7.0
      38             : // See
      39             : // https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md
      40             : #if !defined(__ANDROID_API__) || __ANDROID_API__ >= 24
      41             : #define _FILE_OFFSET_BITS 64
      42             : #endif
      43             : 
      44             : #include "cpl_port.h"
      45             : 
      46             : #include "cpl_vsi.h"
      47             : #include "cpl_vsi_virtual.h"
      48             : 
      49             : #include <cinttypes>
      50             : #include <cstddef>
      51             : #include <cstdio>
      52             : #include <cstring>
      53             : #include <dirent.h>
      54             : #include <errno.h>
      55             : #include <fcntl.h>
      56             : #include <sys/stat.h>
      57             : #ifdef HAVE_STATVFS
      58             : #include <sys/statvfs.h>
      59             : #endif
      60             : #include <sys/types.h>
      61             : #if HAVE_UNISTD_H
      62             : #include <unistd.h>
      63             : #endif
      64             : #ifdef HAVE_PREAD_BSD
      65             : #include <sys/uio.h>
      66             : #endif
      67             : 
      68             : #if defined(__MACH__) && defined(__APPLE__)
      69             : #define HAS_CASE_INSENSITIVE_FILE_SYSTEM
      70             : #include <stdio.h>
      71             : #include <stdlib.h>
      72             : #include <limits.h>
      73             : #endif
      74             : 
      75             : #include <algorithm>
      76             : #include <array>
      77             : #include <limits>
      78             : #include <new>
      79             : 
      80             : #include "cpl_config.h"
      81             : #include "cpl_conv.h"
      82             : #include "cpl_error.h"
      83             : #include "cpl_multiproc.h"
      84             : #include "cpl_string.h"
      85             : #include "cpl_vsi_error.h"
      86             : 
      87             : #if defined(UNIX_STDIO_64)
      88             : 
      89             : #ifndef VSI_OPEN64
      90             : #define VSI_OPEN64 open64
      91             : #endif
      92             : #ifndef VSI_LSEEK64
      93             : #define VSI_LSEEK64 lseek64
      94             : #endif
      95             : #ifndef VSI_STAT64
      96             : #define VSI_STAT64 stat64
      97             : #endif
      98             : #ifndef VSI_STAT64_T
      99             : #define VSI_STAT64_T stat64
     100             : #endif
     101             : #ifndef VSI_FTRUNCATE64
     102             : #define VSI_FTRUNCATE64 ftruncate64
     103             : #endif
     104             : 
     105             : #else /* not UNIX_STDIO_64 */
     106             : 
     107             : #ifndef VSI_OPEN64
     108             : #define VSI_OPEN64 open
     109             : #endif
     110             : #ifndef VSI_LSEEK64
     111             : #define VSI_LSEEK64 lseek
     112             : #endif
     113             : #ifndef VSI_STAT64
     114             : #define VSI_STAT64 stat
     115             : #endif
     116             : #ifndef VSI_STAT64_T
     117             : #define VSI_STAT64_T stat
     118             : #endif
     119             : #ifndef VSI_FTRUNCATE64
     120             : #define VSI_FTRUNCATE64 ftruncate
     121             : #endif
     122             : 
     123             : #endif /* ndef UNIX_STDIO_64 */
     124             : 
     125             : #ifndef BUILD_WITHOUT_64BIT_OFFSET
     126             : // Ensure we have working 64 bit API
     127             : static_assert(sizeof(VSI_LSEEK64(0, 0, SEEK_CUR)) == sizeof(vsi_l_offset),
     128             :               "File API does not seem to support 64-bit offset. "
     129             :               "If you still want to build GDAL without > 4GB file support, "
     130             :               "add the -DBUILD_WITHOUT_64BIT_OFFSET define");
     131             : static_assert(sizeof(VSIStatBufL::st_size) == sizeof(vsi_l_offset),
     132             :               "File API does not seem to support 64-bit file size. "
     133             :               "If you still want to build GDAL without > 4GB file support, "
     134             :               "add the -DBUILD_WITHOUT_64BIT_OFFSET define");
     135             : #endif
     136             : 
     137             : /************************************************************************/
     138             : /* ==================================================================== */
     139             : /*                       VSIUnixStdioFilesystemHandler                  */
     140             : /* ==================================================================== */
     141             : /************************************************************************/
     142             : 
     143             : struct VSIDIRUnixStdio;
     144             : 
     145             : class VSIUnixStdioFilesystemHandler final : public VSIFilesystemHandler
     146             : {
     147             :     CPL_DISALLOW_COPY_ASSIGN(VSIUnixStdioFilesystemHandler)
     148             : 
     149             : #ifdef VSI_COUNT_BYTES_READ
     150             :     vsi_l_offset nTotalBytesRead = 0;
     151             :     CPLMutex *hMutex = nullptr;
     152             : #endif
     153             : 
     154             :   public:
     155        1789 :     VSIUnixStdioFilesystemHandler() = default;
     156             : #ifdef VSI_COUNT_BYTES_READ
     157             :     ~VSIUnixStdioFilesystemHandler() override;
     158             : #endif
     159             : 
     160             :     VSIVirtualHandleUniquePtr Open(const char *pszFilename,
     161             :                                    const char *pszAccess, bool bSetError,
     162             :                                    CSLConstList /* papszOptions */) override;
     163             : 
     164             :     VSIVirtualHandleUniquePtr
     165             :     CreateOnlyVisibleAtCloseTime(const char *pszFilename,
     166             :                                  bool bEmulationAllowed,
     167             :                                  CSLConstList papszOptions) override;
     168             : 
     169             :     int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
     170             :              int nFlags) override;
     171             :     int Unlink(const char *pszFilename) override;
     172             :     int Rename(const char *oldpath, const char *newpath, GDALProgressFunc,
     173             :                void *) override;
     174             :     int Mkdir(const char *pszDirname, long nMode) override;
     175             :     int Rmdir(const char *pszDirname) override;
     176             :     char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
     177             :     GIntBig GetDiskFreeSpace(const char *pszDirname) override;
     178             :     int SupportsSparseFiles(const char *pszPath) override;
     179             : 
     180             :     bool IsLocal(const char *pszPath) const override;
     181             :     bool SupportsSequentialWrite(const char *pszPath,
     182             :                                  bool /* bAllowLocalTempFile */) override;
     183             :     bool SupportsRandomWrite(const char *pszPath,
     184             :                              bool /* bAllowLocalTempFile */) override;
     185             : 
     186             :     VSIDIR *OpenDir(const char *pszPath, int nRecurseDepth,
     187             :                     const char *const *papszOptions) override;
     188             : 
     189             :     static std::unique_ptr<VSIDIRUnixStdio>
     190             :     OpenDirInternal(const char *pszPath, int nRecurseDepth,
     191             :                     const char *const *papszOptions);
     192             : 
     193             : #ifdef HAS_CASE_INSENSITIVE_FILE_SYSTEM
     194             :     std::string
     195             :     GetCanonicalFilename(const std::string &osFilename) const override;
     196             : #endif
     197             : 
     198             : #ifdef VSI_COUNT_BYTES_READ
     199             :     void AddToTotal(vsi_l_offset nBytes);
     200             : #endif
     201             : };
     202             : 
     203             : /************************************************************************/
     204             : /* ==================================================================== */
     205             : /*                        VSIUnixStdioHandle                            */
     206             : /* ==================================================================== */
     207             : /************************************************************************/
     208             : 
     209             : class VSIUnixStdioHandle final : public VSIVirtualHandle
     210             : {
     211             :   public:
     212             :     enum class AccessMode : unsigned char
     213             :     {
     214             :         NORMAL,
     215             :         READ_ONLY,
     216             :         WRITE_ONLY,
     217             :         // In a+ mode, disable any optimization since the behavior of the file
     218             :         // pointer on Mac and other BSD system is to have a seek() to the end of
     219             :         // file and thus a call to our Seek(0, SEEK_SET) before a read will be a
     220             :         // no-op.
     221             :         APPEND_READ_WRITE,
     222             :     };
     223             : 
     224             :   private:
     225             :     friend class VSIUnixStdioFilesystemHandler;
     226             :     CPL_DISALLOW_COPY_ASSIGN(VSIUnixStdioHandle)
     227             : 
     228             :     int fd = -1;
     229             :     vsi_l_offset m_nFilePos = 0;  // Logical/user position
     230             :     static constexpr size_t BUFFER_SIZE = 4096;
     231             :     std::array<GByte, BUFFER_SIZE> m_abyBuffer{};
     232             :     // For read operations, current position within m_abyBuffer
     233             :     // This implies that m_nFilePos - m_nBufferCurPos + m_nBufferSize is the
     234             :     //current real/kernel file position.
     235             :     size_t m_nBufferCurPos = 0;  // Current position within m_abyBuffer
     236             :     // Number of valid bytes in m_abyBuffer (both for read and write buffering)
     237             :     size_t m_nBufferSize = 0;
     238             :     bool m_bBufferDirty = false;
     239             :     const AccessMode eAccessMode;
     240             :     enum class Operation : unsigned char
     241             :     {
     242             :         NONE,
     243             :         READ,
     244             :         WRITE
     245             :     };
     246             :     Operation eLastOp = Operation::NONE;
     247             :     bool bAtEOF = false;
     248             :     bool bError = false;
     249             : #ifdef VSI_COUNT_BYTES_READ
     250             :     vsi_l_offset nTotalBytesRead = 0;
     251             :     VSIUnixStdioFilesystemHandler *poFS = nullptr;
     252             : #endif
     253             : 
     254             :     std::string m_osFilename{};
     255             : #if defined(__linux)
     256             :     bool m_bUnlinkedFile = false;
     257             :     bool m_bCancelCreation = false;
     258             : #else
     259             :     std::string m_osTmpFilename{};
     260             : #endif
     261             : 
     262             :   public:
     263             :     VSIUnixStdioHandle(VSIUnixStdioFilesystemHandler *poFSIn, int fdIn,
     264             :                        AccessMode eAccessModeIn);
     265             :     ~VSIUnixStdioHandle() override;
     266             : 
     267             :     int Seek(vsi_l_offset nOffsetIn, int nWhence) override;
     268             :     vsi_l_offset Tell() override;
     269             :     size_t Read(void *pBuffer, size_t nBytes) override;
     270             :     size_t Write(const void *pBuffer, size_t nBytes) override;
     271             :     void ClearErr() override;
     272             :     int Eof() override;
     273             :     int Error() override;
     274             :     int Flush() override;
     275             :     int Close() override;
     276             :     int Truncate(vsi_l_offset nNewSize) override;
     277             : 
     278          55 :     void *GetNativeFileDescriptor() override
     279             :     {
     280          55 :         return reinterpret_cast<void *>(static_cast<uintptr_t>(fd));
     281             :     }
     282             : 
     283             :     VSIRangeStatus GetRangeStatus(vsi_l_offset nOffset,
     284             :                                   vsi_l_offset nLength) override;
     285             : #if defined(HAVE_PREAD64) || (defined(HAVE_PREAD_BSD) && SIZEOF_OFF_T == 8)
     286             :     bool HasPRead() const override;
     287             :     size_t PRead(void * /*pBuffer*/, size_t /* nSize */,
     288             :                  vsi_l_offset /*nOffset*/) const override;
     289             : #endif
     290             : 
     291             :     void CancelCreation() override;
     292             : };
     293             : 
     294             : /************************************************************************/
     295             : /*                         VSIUnixStdioHandle()                         */
     296             : /************************************************************************/
     297             : 
     298      133195 : VSIUnixStdioHandle::VSIUnixStdioHandle(
     299             : #ifndef VSI_COUNT_BYTES_READ
     300             :     CPL_UNUSED
     301             : #endif
     302             :     VSIUnixStdioFilesystemHandler *poFSIn,
     303      133195 :     int fdIn, AccessMode eAccessModeIn)
     304      133195 :     : fd(fdIn), eAccessMode(eAccessModeIn)
     305             : #ifdef VSI_COUNT_BYTES_READ
     306             :       ,
     307             :       poFS(poFSIn)
     308             : #endif
     309             : {
     310      133083 : }
     311             : 
     312             : /************************************************************************/
     313             : /*                        ~VSIUnixStdioHandle()                         */
     314             : /************************************************************************/
     315             : 
     316      265431 : VSIUnixStdioHandle::~VSIUnixStdioHandle()
     317             : {
     318      132376 :     VSIUnixStdioHandle::Close();
     319      265662 : }
     320             : 
     321             : /************************************************************************/
     322             : /*                               Close()                                */
     323             : /************************************************************************/
     324             : 
     325      269814 : int VSIUnixStdioHandle::Close()
     326             : 
     327             : {
     328      269814 :     if (fd < 0)
     329      136389 :         return 0;
     330             : 
     331             :     VSIDebug1("VSIUnixStdioHandle::Close(%d)", fd);
     332             : 
     333             : #ifdef VSI_COUNT_BYTES_READ
     334             :     poFS->AddToTotal(nTotalBytesRead);
     335             : #endif
     336             : 
     337      133425 :     int ret = VSIUnixStdioHandle::Flush();
     338             : 
     339             : #ifdef __linux
     340      265092 :     if (ret == 0 && !m_bCancelCreation && !m_osFilename.empty() &&
     341      132224 :         m_bUnlinkedFile)
     342             :     {
     343           1 :         ret = fsync(fd);
     344           1 :         if (ret == 0)
     345             :         {
     346             :             // As advised by "man 2 open" if the caller doesn't have the
     347             :             // CAP_DAC_READ_SEARCH capability, which seems to be the default
     348             : 
     349             :             char szPath[32];
     350           1 :             snprintf(szPath, sizeof(szPath), "/proc/self/fd/%d", fd);
     351           1 :             ret = linkat(AT_FDCWD, szPath, AT_FDCWD, m_osFilename.c_str(),
     352             :                          AT_SYMLINK_FOLLOW);
     353           1 :             if (ret != 0)
     354           0 :                 CPLDebug("CPL", "linkat() failed with errno=%d", errno);
     355             :         }
     356             :     }
     357             : #endif
     358             : 
     359      133287 :     int ret2 = close(fd);
     360      132811 :     if (ret == 0 && ret2 != 0)
     361           0 :         ret = ret2;
     362             : 
     363             : #if !defined(__linux)
     364             :     if (!m_osTmpFilename.empty() && !m_osFilename.empty())
     365             :     {
     366             :         ret = rename(m_osTmpFilename.c_str(), m_osFilename.c_str());
     367             :     }
     368             : #endif
     369             : 
     370      132811 :     fd = -1;
     371      132811 :     return ret;
     372             : }
     373             : 
     374             : /************************************************************************/
     375             : /*                           CancelCreation()                           */
     376             : /************************************************************************/
     377             : 
     378         519 : void VSIUnixStdioHandle::CancelCreation()
     379             : {
     380             : #if defined(__linux)
     381         519 :     if (!m_osFilename.empty() && !m_bUnlinkedFile)
     382             :     {
     383         451 :         unlink(m_osFilename.c_str());
     384         451 :         m_osFilename.clear();
     385             :     }
     386          68 :     else if (m_bUnlinkedFile)
     387          65 :         m_bCancelCreation = true;
     388             : #else
     389             :     if (!m_osTmpFilename.empty())
     390             :     {
     391             :         unlink(m_osTmpFilename.c_str());
     392             :         m_osTmpFilename.clear();
     393             :     }
     394             :     else if (!m_osFilename.empty())
     395             :     {
     396             :         unlink(m_osFilename.c_str());
     397             :         m_osFilename.clear();
     398             :     }
     399             : #endif
     400         519 : }
     401             : 
     402             : /************************************************************************/
     403             : /*                                Seek()                                */
     404             : /************************************************************************/
     405             : 
     406     4182420 : int VSIUnixStdioHandle::Seek(vsi_l_offset nOffsetIn, int nWhence)
     407             : {
     408     4182420 :     bAtEOF = false;
     409     4182420 :     bError = false;
     410             : 
     411             :     // Seeks that do nothing are still surprisingly expensive with MSVCRT.
     412             :     // try and short circuit if possible.
     413     4182420 :     if (eAccessMode != AccessMode::APPEND_READ_WRITE && nWhence == SEEK_SET &&
     414     3580400 :         nOffsetIn == m_nFilePos)
     415      740656 :         return 0;
     416             : 
     417             : #if !defined(UNIX_STDIO_64) && SIZEOF_UNSIGNED_LONG == 4
     418             :     if (nOffsetIn > static_cast<vsi_l_offset>(std::numeric_limits<long>::max()))
     419             :     {
     420             :         CPLError(
     421             :             CE_Failure, CPLE_AppDefined,
     422             :             "Attempt at seeking beyond long extent. Lack of 64-bit file I/O");
     423             :         return -1;
     424             :     }
     425             : #endif
     426             : 
     427     3441770 :     if (eLastOp == Operation::WRITE)
     428             :     {
     429      287125 :         eLastOp = Operation::NONE;
     430             : 
     431      287125 :         if (m_bBufferDirty && Flush() < 0)
     432           0 :             return -1;
     433             : 
     434      287125 :         const auto nNewPos = VSI_LSEEK64(fd, nOffsetIn, nWhence);
     435      287125 :         if (nNewPos < 0)
     436             :         {
     437           0 :             bError = true;
     438           0 :             return -1;
     439             :         }
     440             : 
     441      287125 :         m_nFilePos = nNewPos;
     442      287125 :         m_nBufferCurPos = 0;
     443      287125 :         m_nBufferSize = 0;
     444             :     }
     445             : 
     446             :     else
     447             :     {
     448     3154640 :         eLastOp = Operation::READ;
     449             : 
     450             :         vsi_l_offset nTargetPos;
     451             : 
     452             :         // Compute absolute target position
     453     3154640 :         switch (nWhence)
     454             :         {
     455     2747700 :             case SEEK_SET:
     456     2747700 :                 nTargetPos = nOffsetIn;
     457     2747700 :                 break;
     458       81490 :             case SEEK_CUR:
     459       81490 :                 nTargetPos = m_nFilePos + nOffsetIn;
     460       81490 :                 break;
     461      325172 :             case SEEK_END:
     462             :             {
     463      325172 :                 const auto nNewPos = VSI_LSEEK64(fd, nOffsetIn, SEEK_END);
     464      325075 :                 if (nNewPos < 0)
     465             :                 {
     466           0 :                     bError = true;
     467           0 :                     return -1;
     468             :                 }
     469      325075 :                 nTargetPos = nNewPos;
     470      325075 :                 break;
     471             :             }
     472         284 :             default:
     473         284 :                 return -1;
     474             :         }
     475             : 
     476             :         // Compute absolute position of the current buffer
     477     3154260 :         const auto nBufStartOffset = m_nFilePos - m_nBufferCurPos;
     478     3154260 :         const auto nBufEndOffset = nBufStartOffset + m_nBufferSize;
     479             : 
     480     3154260 :         if (nTargetPos >= nBufStartOffset && nTargetPos < nBufEndOffset)
     481             :         {
     482             :             // Seek inside current buffer - adjust buffer pos only
     483     2139440 :             m_nBufferCurPos = static_cast<size_t>(nTargetPos - nBufStartOffset);
     484     2139440 :             m_nFilePos = nTargetPos;
     485             :         }
     486             :         else
     487             :         {
     488             :             // Outside buffer, do real seek and invalidate buffer
     489     1014820 :             const auto nNewPos = VSI_LSEEK64(fd, nTargetPos, SEEK_SET);
     490     1014930 :             if (nNewPos < 0)
     491             :             {
     492           1 :                 bError = true;
     493           1 :                 return -1;
     494             :             }
     495             : 
     496     1014930 :             m_nFilePos = nNewPos;
     497     1014930 :             m_nBufferCurPos = 0;
     498     1014930 :             m_nBufferSize = 0;
     499             :         }
     500             :     }
     501             : 
     502             :     VSIDebug4("VSIUnixStdioHandle::Seek(%d," CPL_FRMT_GUIB ", %d) = %" PRIu64,
     503             :               fd, nOffsetIn, nWhence, static_cast<uint64_t>(m_nFilePos));
     504             : 
     505     3441500 :     return 0;
     506             : }
     507             : 
     508             : /************************************************************************/
     509             : /*                                Tell()                                */
     510             : /************************************************************************/
     511             : 
     512     3106110 : vsi_l_offset VSIUnixStdioHandle::Tell()
     513             : 
     514             : {
     515     3106110 :     return m_nFilePos;
     516             : }
     517             : 
     518             : /************************************************************************/
     519             : /*                               Flush()                                */
     520             : /************************************************************************/
     521             : 
     522      447800 : int VSIUnixStdioHandle::Flush()
     523             : 
     524             : {
     525             :     VSIDebug1("VSIUnixStdioHandle::Flush(%d)", fd);
     526             : 
     527      447800 :     if (m_bBufferDirty)
     528             :     {
     529      334058 :         m_bBufferDirty = false;
     530             : 
     531             :         // Sync kernel offset to our logical position before writing
     532      334058 :         if (VSI_LSEEK64(fd, m_nFilePos - m_nBufferSize, SEEK_SET) < 0)
     533             :         {
     534           0 :             return EOF;
     535             :         }
     536             : 
     537      334058 :         size_t nOff = 0;
     538      668116 :         while (m_nBufferSize > 0)
     539             :         {
     540      334058 :             errno = 0;
     541             :             const auto nWritten =
     542      334058 :                 write(fd, m_abyBuffer.data() + nOff, m_nBufferSize);
     543      334058 :             if (nWritten < 0)
     544             :             {
     545           0 :                 if (errno == EINTR)
     546           0 :                     continue;
     547           0 :                 return EOF;
     548             :             }
     549      334058 :             CPLAssert(static_cast<size_t>(nWritten) <= m_nBufferSize);
     550      334058 :             nOff += nWritten;
     551      334058 :             m_nBufferSize -= nWritten;
     552             :         }
     553             :     }
     554             : 
     555      447800 :     return 0;
     556             : }
     557             : 
     558             : /************************************************************************/
     559             : /*                                Read()                                */
     560             : /************************************************************************/
     561             : 
     562     8773660 : size_t VSIUnixStdioHandle::Read(void *pBuffer, size_t nBytes)
     563             : 
     564             : {
     565     8773660 :     if (nBytes == 0 || bAtEOF)
     566       38581 :         return 0;
     567     8735070 :     if (eAccessMode == AccessMode::WRITE_ONLY)
     568             :     {
     569           1 :         bError = true;
     570           1 :         errno = EINVAL;
     571           1 :         return 0;
     572             :     }
     573             : 
     574             :     /* -------------------------------------------------------------------- */
     575             :     /*      If a fwrite() is followed by an fread(), the POSIX rules are    */
     576             :     /*      that some of the write may still be buffered and lost.  We      */
     577             :     /*      are required to do a seek between to force flushing.   So we    */
     578             :     /*      keep careful track of what happened last to know if we          */
     579             :     /*      skipped a flushing seek that we may need to do now.             */
     580             :     /* -------------------------------------------------------------------- */
     581     8735070 :     if (eLastOp == Operation::WRITE)
     582             :     {
     583        2843 :         if (m_bBufferDirty && Flush() < 0)
     584             :         {
     585           0 :             bError = true;
     586           0 :             return -1;
     587             :         }
     588        2843 :         if (VSI_LSEEK64(fd, m_nFilePos, SEEK_SET) < 0)
     589             :         {
     590           0 :             bError = true;
     591           0 :             return -1;
     592             :         }
     593        2843 :         m_nBufferCurPos = 0;
     594        2843 :         m_nBufferSize = 0;
     595             :     }
     596             : 
     597     8735070 :     const size_t nTotal = nBytes;
     598     8735070 :     size_t nBytesRead = 0;
     599     8735070 :     GByte *const pabyDest = static_cast<GByte *>(pBuffer);
     600             : 
     601    17499800 :     while (nBytesRead < nTotal)
     602             :     {
     603     8826870 :         size_t nAvailInBuffer = m_nBufferSize - m_nBufferCurPos;
     604             : 
     605             :         // If buffer is empty
     606     8826870 :         if (nAvailInBuffer == 0)
     607             :         {
     608      867742 :             size_t nRemaining = nTotal - nBytesRead;
     609      867742 :             if (nRemaining >= BUFFER_SIZE)
     610             :             {
     611             :                 // Bypass buffer if large request
     612       71244 :                 errno = 0;
     613             :                 const ssize_t nBytesReadThisTime =
     614       71244 :                     read(fd, pabyDest + nBytesRead, nRemaining);
     615       71247 :                 if (nBytesReadThisTime <= 0)
     616             :                 {
     617       14190 :                     if (nBytesReadThisTime == 0)
     618       14185 :                         bAtEOF = true;
     619           5 :                     else if (errno == EINTR)
     620           0 :                         continue;
     621             :                     else
     622           5 :                         bError = true;
     623       62159 :                     break;
     624             :                 }
     625       57057 :                 m_nFilePos += nBytesReadThisTime;
     626       57057 :                 nBytesRead += nBytesReadThisTime;
     627             :             }
     628             :             else
     629             :             {
     630             :                 // Small request: Refill internal buffer
     631      796498 :                 errno = 0;
     632             :                 const ssize_t nBytesReadThisTime =
     633      796498 :                     read(fd, m_abyBuffer.data(), BUFFER_SIZE);
     634      796291 :                 if (nBytesReadThisTime <= 0)
     635             :                 {
     636       47969 :                     if (nBytesReadThisTime == 0)
     637       46897 :                         bAtEOF = true;
     638        1072 :                     else if (errno == EINTR)
     639           0 :                         continue;
     640             :                     else
     641        1072 :                         bError = true;
     642       47969 :                     break;
     643             :                 }
     644      748322 :                 m_nBufferCurPos = 0;
     645      748322 :                 m_nBufferSize = nBytesReadThisTime;
     646      748322 :                 nAvailInBuffer = nBytesReadThisTime;
     647             :             }
     648             :         }
     649             : 
     650             :         // Copy from buffer to user
     651     8764510 :         const size_t nToCopy = std::min(nTotal - nBytesRead, nAvailInBuffer);
     652     8764810 :         memcpy(pabyDest + nBytesRead, m_abyBuffer.data() + m_nBufferCurPos,
     653             :                nToCopy);
     654     8764680 :         nBytesRead += nToCopy;
     655     8764680 :         m_nBufferCurPos += nToCopy;
     656     8764680 :         m_nFilePos += nToCopy;
     657             :     }
     658             : 
     659             : #ifdef VSI_DEBUG
     660             :     const int nError = errno;
     661             :     VSIDebug4("VSIUnixStdioHandle::Read(%d,%%ld) = %" PRId64, fd,
     662             :               static_cast<long>(nBytes), static_cast<int64_t>(nBytesRead));
     663             :     errno = nError;
     664             : #endif
     665             : 
     666     8735040 :     eLastOp = Operation::READ;
     667             : 
     668             : #ifdef VSI_COUNT_BYTES_READ
     669             :     nTotalBytesRead += nBytesRead;
     670             : #endif
     671             : 
     672     8735040 :     return nBytesRead;
     673             : }
     674             : 
     675             : /************************************************************************/
     676             : /*                               Write()                                */
     677             : /************************************************************************/
     678             : 
     679     2401320 : size_t VSIUnixStdioHandle::Write(const void *pBuffer, size_t nBytes)
     680             : 
     681             : {
     682     2401320 :     if (nBytes == 0)
     683       22605 :         return 0;
     684     2378720 :     if (eAccessMode == AccessMode::READ_ONLY)
     685             :     {
     686           2 :         bError = true;
     687           2 :         errno = EINVAL;
     688           2 :         return 0;
     689             :     }
     690             : 
     691             :     /* -------------------------------------------------------------------- */
     692             :     /*      If a fwrite() is followed by an fread(), the POSIX rules are    */
     693             :     /*      that some of the write may still be buffered and lost.  We      */
     694             :     /*      are required to do a seek between to force flushing.   So we    */
     695             :     /*      keep careful track of what happened last to know if we          */
     696             :     /*      skipped a flushing seek that we may need to do now.             */
     697             :     /* -------------------------------------------------------------------- */
     698     2378720 :     if (eLastOp == Operation::READ)
     699             :     {
     700       39776 :         if (VSI_LSEEK64(fd, m_nFilePos, SEEK_SET) < 0)
     701             :         {
     702           0 :             bError = true;
     703           0 :             return -1;
     704             :         }
     705       39776 :         m_nBufferCurPos = 0;
     706       39776 :         m_nBufferSize = 0;
     707             :     }
     708             : 
     709     2378720 :     const size_t nTotal = nBytes;
     710     2378720 :     size_t nBytesWritten = 0;
     711     2378720 :     const GByte *const pabySrc = static_cast<const GByte *>(pBuffer);
     712     4771970 :     while (nBytesWritten < nTotal)
     713             :     {
     714     2393250 :         const size_t nRemaining = nTotal - nBytesWritten;
     715             : 
     716             :         // Bypass buffer if request > BUFFER_SIZE
     717     2393250 :         if (m_nBufferSize == 0 && nRemaining >= BUFFER_SIZE)
     718             :         {
     719             :             const auto nWritten =
     720       10743 :                 write(fd, pabySrc + nBytesWritten, nRemaining);
     721       10743 :             if (nWritten < 0)
     722             :             {
     723           0 :                 bError = true;
     724           0 :                 break;
     725             :             }
     726       10743 :             CPLAssert(static_cast<size_t>(nWritten) <= nRemaining);
     727       10743 :             m_nFilePos += nWritten;
     728       10743 :             nBytesWritten += nWritten;
     729       10743 :             continue;
     730             :         }
     731             : 
     732             :         // Small request: Fill internal buffer
     733     2382510 :         const size_t nFreeSpaceInBuffer = BUFFER_SIZE - m_nBufferSize;
     734     2382510 :         const size_t nToCopy = std::min(nRemaining, nFreeSpaceInBuffer);
     735     2382510 :         memcpy(m_abyBuffer.data() + m_nBufferSize, pabySrc + nBytesWritten,
     736             :                nToCopy);
     737             : 
     738     2382510 :         nBytesWritten += nToCopy;
     739     2382510 :         m_nBufferSize += nToCopy;
     740     2382510 :         m_nFilePos += nToCopy;
     741     2382510 :         m_bBufferDirty = true;
     742             : 
     743     2382510 :         if (m_nBufferSize == BUFFER_SIZE && Flush() < 0)
     744             :         {
     745           0 :             break;
     746             :         }
     747             :     }
     748             : 
     749             : #ifdef VSI_DEBUG
     750             :     const int nError = errno;
     751             :     VSIDebug4("VSIUnixStdioHandle::Write(%d,%ld) = %" PRId64, fd,
     752             :               static_cast<long>(nBytes), static_cast<int64_t>(nBytesWritten));
     753             :     errno = nError;
     754             : #endif
     755             : 
     756     2378720 :     eLastOp = Operation::WRITE;
     757             : 
     758     2378720 :     return nBytesWritten;
     759             : }
     760             : 
     761             : /************************************************************************/
     762             : /*                              ClearErr()                              */
     763             : /************************************************************************/
     764             : 
     765        8099 : void VSIUnixStdioHandle::ClearErr()
     766             : 
     767             : {
     768        8099 :     bAtEOF = false;
     769        8099 :     bError = false;
     770        8099 : }
     771             : 
     772             : /************************************************************************/
     773             : /*                               Error()                                */
     774             : /************************************************************************/
     775             : 
     776       62190 : int VSIUnixStdioHandle::Error()
     777             : 
     778             : {
     779       62190 :     return bError ? TRUE : FALSE;
     780             : }
     781             : 
     782             : /************************************************************************/
     783             : /*                                Eof()                                 */
     784             : /************************************************************************/
     785             : 
     786      157834 : int VSIUnixStdioHandle::Eof()
     787             : 
     788             : {
     789      157834 :     return bAtEOF ? TRUE : FALSE;
     790             : }
     791             : 
     792             : /************************************************************************/
     793             : /*                              Truncate()                              */
     794             : /************************************************************************/
     795             : 
     796         614 : int VSIUnixStdioHandle::Truncate(vsi_l_offset nNewSize)
     797             : {
     798         614 :     if (Flush() != 0)
     799           0 :         return -1;
     800         614 :     return VSI_FTRUNCATE64(fd, nNewSize);
     801             : }
     802             : 
     803             : /************************************************************************/
     804             : /*                           GetRangeStatus()                           */
     805             : /************************************************************************/
     806             : 
     807             : #ifdef __linux
     808             : #if !defined(MISSING_LINUX_FS_H)
     809             : #include <linux/fs.h>  // FS_IOC_FIEMAP
     810             : #endif
     811             : #ifdef FS_IOC_FIEMAP
     812             : #include <linux/types.h>   // for types used in linux/fiemap.h
     813             : #include <linux/fiemap.h>  // struct fiemap
     814             : #endif
     815             : #include <sys/ioctl.h>
     816             : #include <errno.h>
     817             : #endif
     818             : 
     819         544 : VSIRangeStatus VSIUnixStdioHandle::GetRangeStatus(vsi_l_offset
     820             : #ifdef FS_IOC_FIEMAP
     821             :                                                       nOffset
     822             : #endif
     823             :                                                   ,
     824             :                                                   vsi_l_offset
     825             : #ifdef FS_IOC_FIEMAP
     826             :                                                       nLength
     827             : #endif
     828             : )
     829             : {
     830             : #ifdef FS_IOC_FIEMAP
     831             :     // fiemap IOCTL documented at
     832             :     // https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt
     833             : 
     834             :     // The fiemap struct contains a "variable length" array at its end
     835             :     // As we are interested in only one extent, we allocate the base size of
     836             :     // fiemap + one fiemap_extent.
     837             :     GByte abyBuffer[sizeof(struct fiemap) + sizeof(struct fiemap_extent)];
     838         544 :     struct fiemap *psExtentMap = reinterpret_cast<struct fiemap *>(&abyBuffer);
     839         544 :     memset(psExtentMap, 0,
     840             :            sizeof(struct fiemap) + sizeof(struct fiemap_extent));
     841         544 :     psExtentMap->fm_start = nOffset;
     842         544 :     psExtentMap->fm_length = nLength;
     843         544 :     psExtentMap->fm_extent_count = 1;
     844         544 :     int ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
     845         544 :     if (ret < 0)
     846           0 :         return VSI_RANGE_STATUS_UNKNOWN;
     847         544 :     if (psExtentMap->fm_mapped_extents == 0)
     848           2 :         return VSI_RANGE_STATUS_HOLE;
     849             :     // In case there is one extent with unknown status, retry after having
     850             :     // asked the kernel to sync the file.
     851         542 :     const fiemap_extent *pasExtent = &(psExtentMap->fm_extents[0]);
     852         542 :     if (psExtentMap->fm_mapped_extents == 1 &&
     853         542 :         (pasExtent[0].fe_flags & FIEMAP_EXTENT_UNKNOWN) != 0)
     854             :     {
     855          20 :         psExtentMap->fm_flags = FIEMAP_FLAG_SYNC;
     856          20 :         psExtentMap->fm_start = nOffset;
     857          20 :         psExtentMap->fm_length = nLength;
     858          20 :         psExtentMap->fm_extent_count = 1;
     859          20 :         ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
     860          20 :         if (ret < 0)
     861           0 :             return VSI_RANGE_STATUS_UNKNOWN;
     862          20 :         if (psExtentMap->fm_mapped_extents == 0)
     863           0 :             return VSI_RANGE_STATUS_HOLE;
     864             :     }
     865         542 :     return VSI_RANGE_STATUS_DATA;
     866             : #else
     867             :     static bool bMessageEmitted = false;
     868             :     if (!bMessageEmitted)
     869             :     {
     870             :         CPLDebug("VSI", "Sorry: GetExtentStatus() not implemented for "
     871             :                         "this operating system");
     872             :         bMessageEmitted = true;
     873             :     }
     874             :     return VSI_RANGE_STATUS_UNKNOWN;
     875             : #endif
     876             : }
     877             : 
     878             : /************************************************************************/
     879             : /*                              HasPRead()                              */
     880             : /************************************************************************/
     881             : 
     882             : #if defined(HAVE_PREAD64) || (defined(HAVE_PREAD_BSD) && SIZEOF_OFF_T == 8)
     883         730 : bool VSIUnixStdioHandle::HasPRead() const
     884             : {
     885         730 :     return true;
     886             : }
     887             : 
     888             : /************************************************************************/
     889             : /*                               PRead()                                */
     890             : /************************************************************************/
     891             : 
     892       24481 : size_t VSIUnixStdioHandle::PRead(void *pBuffer, size_t nSize,
     893             :                                  vsi_l_offset nOffset) const
     894             : {
     895             : #ifdef HAVE_PREAD64
     896       24481 :     return pread64(fd, pBuffer, nSize, nOffset);
     897             : #else
     898             :     return pread(fd, pBuffer, nSize, static_cast<off_t>(nOffset));
     899             : #endif
     900             : }
     901             : #endif
     902             : 
     903             : /************************************************************************/
     904             : /* ==================================================================== */
     905             : /*                       VSIUnixStdioFilesystemHandler                  */
     906             : /* ==================================================================== */
     907             : /************************************************************************/
     908             : 
     909             : #ifdef VSI_COUNT_BYTES_READ
     910             : /************************************************************************/
     911             : /*                   ~VSIUnixStdioFilesystemHandler()                   */
     912             : /************************************************************************/
     913             : 
     914             : VSIUnixStdioFilesystemHandler::~VSIUnixStdioFilesystemHandler()
     915             : {
     916             :     CPLDebug(
     917             :         "VSI",
     918             :         "~VSIUnixStdioFilesystemHandler() : nTotalBytesRead = " CPL_FRMT_GUIB,
     919             :         nTotalBytesRead);
     920             : 
     921             :     if (hMutex != nullptr)
     922             :         CPLDestroyMutex(hMutex);
     923             :     hMutex = nullptr;
     924             : }
     925             : #endif
     926             : 
     927             : /************************************************************************/
     928             : /*                                Open()                                */
     929             : /************************************************************************/
     930             : 
     931             : VSIVirtualHandleUniquePtr
     932      213884 : VSIUnixStdioFilesystemHandler::Open(const char *pszFilename,
     933             :                                     const char *pszAccess, bool bSetError,
     934             :                                     CSLConstList /* papszOptions */)
     935             : 
     936             : {
     937      213884 :     int flags = O_RDONLY;
     938             :     int fd;
     939             :     using AccessMode = VSIUnixStdioHandle::AccessMode;
     940      213884 :     AccessMode eAccessMode = AccessMode::NORMAL;
     941      213884 :     if (strchr(pszAccess, 'w'))
     942             :     {
     943       33086 :         if (strchr(pszAccess, '+'))
     944        6369 :             flags = O_CREAT | O_TRUNC | O_RDWR;
     945             :         else
     946             :         {
     947       26717 :             eAccessMode = AccessMode::WRITE_ONLY;
     948       26717 :             flags = O_CREAT | O_TRUNC | O_WRONLY;
     949             :         }
     950       33086 :         fd = VSI_OPEN64(pszFilename, flags, 0664);
     951             :     }
     952      180798 :     else if (strchr(pszAccess, 'a'))
     953             :     {
     954         170 :         if (strchr(pszAccess, '+'))
     955             :         {
     956         120 :             eAccessMode = AccessMode::APPEND_READ_WRITE;
     957         120 :             flags = O_CREAT | O_RDWR;
     958             :         }
     959             :         else
     960          50 :             flags = O_CREAT | O_WRONLY;
     961         170 :         fd = VSI_OPEN64(pszFilename, flags, 0664);
     962             :     }
     963             :     else
     964             :     {
     965      180628 :         if (strchr(pszAccess, '+'))
     966        7774 :             flags = O_RDWR;
     967             :         else
     968      172854 :             eAccessMode = AccessMode::READ_ONLY;
     969      180628 :         fd = VSI_OPEN64(pszFilename, flags);
     970             :     }
     971      213338 :     const int nError = errno;
     972             : 
     973             :     VSIDebug3("VSIUnixStdioFilesystemHandler::Open(\"%s\",\"%s\") = %d",
     974             :               pszFilename, pszAccess, fd);
     975             : 
     976      213338 :     if (fd < 0)
     977             :     {
     978       80625 :         if (bSetError)
     979             :         {
     980       17398 :             VSIError(VSIE_FileError, "%s: %s", pszFilename, strerror(nError));
     981             :         }
     982       80625 :         errno = nError;
     983       80625 :         return nullptr;
     984             :     }
     985             : 
     986      265470 :     auto poHandle = std::make_unique<VSIUnixStdioHandle>(this, fd, eAccessMode);
     987      132687 :     poHandle->m_osFilename = pszFilename;
     988             : 
     989      132904 :     errno = nError;
     990             : 
     991      132904 :     if (strchr(pszAccess, 'a'))
     992         170 :         poHandle->Seek(0, SEEK_END);
     993             : 
     994             :     /* -------------------------------------------------------------------- */
     995             :     /*      If VSI_CACHE is set we want to use a cached reader instead      */
     996             :     /*      of more direct io on the underlying file.                       */
     997             :     /* -------------------------------------------------------------------- */
     998      227033 :     if (eAccessMode == AccessMode::READ_ONLY &&
     999       93862 :         CPLTestBool(CPLGetConfigOption("VSI_CACHE", "FALSE")))
    1000             :     {
    1001             :         return VSIVirtualHandleUniquePtr(
    1002           5 :             VSICreateCachedFile(poHandle.release()));
    1003             :     }
    1004             : 
    1005      133166 :     return VSIVirtualHandleUniquePtr(poHandle.release());
    1006             : }
    1007             : 
    1008             : /************************************************************************/
    1009             : /*                    CreateOnlyVisibleAtCloseTime()                    */
    1010             : /************************************************************************/
    1011             : 
    1012             : VSIVirtualHandleUniquePtr
    1013         168 : VSIUnixStdioFilesystemHandler::CreateOnlyVisibleAtCloseTime(
    1014             :     const char *pszFilename, bool bEmulationAllowed, CSLConstList papszOptions)
    1015             : {
    1016             : #ifdef __linux
    1017          36 :     static bool bIsLinkatSupported = []()
    1018             :     {
    1019             :         // Check that /proc is accessible, since we will need it to run linkat()
    1020             :         struct stat statbuf;
    1021          36 :         return stat("/proc/self/fd", &statbuf) == 0;
    1022         168 :     }();
    1023             : 
    1024             :     const int fd = bIsLinkatSupported
    1025         336 :                        ? VSI_OPEN64(CPLGetDirnameSafe(pszFilename).c_str(),
    1026             :                                     O_TMPFILE | O_RDWR, 0666)
    1027         168 :                        : -1;
    1028         168 :     if (fd < 0)
    1029             :     {
    1030         134 :         if (bIsLinkatSupported)
    1031             :         {
    1032         134 :             CPLDebugOnce("VSI",
    1033             :                          "open(O_TMPFILE | O_RDWR) failed with errno=%d (%s). "
    1034             :                          "Going through emulation",
    1035             :                          errno, VSIStrerror(errno));
    1036             :         }
    1037             :         return VSIFilesystemHandler::CreateOnlyVisibleAtCloseTime(
    1038         134 :             pszFilename, bEmulationAllowed, papszOptions);
    1039             :     }
    1040             : 
    1041             :     auto poHandle = std::make_unique<VSIUnixStdioHandle>(
    1042          68 :         this, fd, VSIUnixStdioHandle::AccessMode::NORMAL);
    1043          34 :     poHandle->m_osFilename = pszFilename;
    1044          34 :     poHandle->m_bUnlinkedFile = true;
    1045          34 :     return VSIVirtualHandleUniquePtr(poHandle.release());
    1046             : #else
    1047             :     if (!bEmulationAllowed)
    1048             :         return nullptr;
    1049             : 
    1050             :     std::string osTmpFilename = std::string(pszFilename).append("XXXXXX");
    1051             :     int fd = mkstemp(osTmpFilename.data());
    1052             :     if (fd < 0)
    1053             :     {
    1054             :         return VSIFilesystemHandler::CreateOnlyVisibleAtCloseTime(
    1055             :             pszFilename, bEmulationAllowed, papszOptions);
    1056             :     }
    1057             :     auto poHandle = std::make_unique<VSIUnixStdioHandle>(
    1058             :         this, fd, VSIUnixStdioHandle::AccessMode::NORMAL);
    1059             :     poHandle->m_osTmpFilename = std::move(osTmpFilename);
    1060             :     poHandle->m_osFilename = pszFilename;
    1061             :     return VSIVirtualHandleUniquePtr(poHandle.release());
    1062             : #endif
    1063             : }
    1064             : 
    1065             : /************************************************************************/
    1066             : /*                                Stat()                                */
    1067             : /************************************************************************/
    1068             : 
    1069      242179 : int VSIUnixStdioFilesystemHandler::Stat(const char *pszFilename,
    1070             :                                         VSIStatBufL *pStatBuf, int /* nFlags */)
    1071             : {
    1072      242179 :     return (VSI_STAT64(pszFilename, pStatBuf));
    1073             : }
    1074             : 
    1075             : /************************************************************************/
    1076             : /*                               Unlink()                               */
    1077             : /************************************************************************/
    1078             : 
    1079        5188 : int VSIUnixStdioFilesystemHandler::Unlink(const char *pszFilename)
    1080             : 
    1081             : {
    1082        5188 :     return unlink(pszFilename);
    1083             : }
    1084             : 
    1085             : /************************************************************************/
    1086             : /*                               Rename()                               */
    1087             : /************************************************************************/
    1088             : 
    1089         992 : int VSIUnixStdioFilesystemHandler::Rename(const char *oldpath,
    1090             :                                           const char *newpath, GDALProgressFunc,
    1091             :                                           void *)
    1092             : 
    1093             : {
    1094         992 :     return rename(oldpath, newpath);
    1095             : }
    1096             : 
    1097             : /************************************************************************/
    1098             : /*                               Mkdir()                                */
    1099             : /************************************************************************/
    1100             : 
    1101        1604 : int VSIUnixStdioFilesystemHandler::Mkdir(const char *pszPathname, long nMode)
    1102             : 
    1103             : {
    1104        1604 :     return mkdir(pszPathname, static_cast<int>(nMode));
    1105             : }
    1106             : 
    1107             : /************************************************************************/
    1108             : /*                               Rmdir()                                */
    1109             : /************************************************************************/
    1110             : 
    1111          63 : int VSIUnixStdioFilesystemHandler::Rmdir(const char *pszPathname)
    1112             : 
    1113             : {
    1114          63 :     return rmdir(pszPathname);
    1115             : }
    1116             : 
    1117             : /************************************************************************/
    1118             : /*                             ReadDirEx()                              */
    1119             : /************************************************************************/
    1120             : 
    1121       21875 : char **VSIUnixStdioFilesystemHandler::ReadDirEx(const char *pszPath,
    1122             :                                                 int nMaxFiles)
    1123             : 
    1124             : {
    1125       21875 :     if (strlen(pszPath) == 0)
    1126          17 :         pszPath = ".";
    1127             : 
    1128       43750 :     CPLStringList oDir;
    1129       21875 :     DIR *hDir = opendir(pszPath);
    1130       21875 :     if (hDir != nullptr)
    1131             :     {
    1132             :         // We want to avoid returning NULL for an empty list.
    1133       21812 :         oDir.Assign(static_cast<char **>(CPLCalloc(2, sizeof(char *))));
    1134             : 
    1135       21812 :         struct dirent *psDirEntry = nullptr;
    1136     2002150 :         while ((psDirEntry = readdir(hDir)) != nullptr)
    1137             :         {
    1138     1980370 :             oDir.AddString(psDirEntry->d_name);
    1139     1980330 :             if (nMaxFiles > 0 && oDir.Count() > nMaxFiles)
    1140           7 :                 break;
    1141             :         }
    1142             : 
    1143       21815 :         closedir(hDir);
    1144             :     }
    1145             :     else
    1146             :     {
    1147             :         // Should we generate an error?
    1148             :         // For now we'll just return NULL (at the end of the function).
    1149             :     }
    1150             : 
    1151       43750 :     return oDir.StealList();
    1152             : }
    1153             : 
    1154             : /************************************************************************/
    1155             : /*                          GetDiskFreeSpace()                          */
    1156             : /************************************************************************/
    1157             : 
    1158           3 : GIntBig VSIUnixStdioFilesystemHandler::GetDiskFreeSpace(const char *
    1159             : #ifdef HAVE_STATVFS
    1160             :                                                             pszDirname
    1161             : #endif
    1162             : )
    1163             : {
    1164           3 :     GIntBig nRet = -1;
    1165             : #ifdef HAVE_STATVFS
    1166             : 
    1167             : #ifdef HAVE_STATVFS64
    1168             :     struct statvfs64 buf;
    1169           3 :     if (statvfs64(pszDirname, &buf) == 0)
    1170             :     {
    1171           2 :         nRet = static_cast<GIntBig>(buf.f_frsize *
    1172           2 :                                     static_cast<GUIntBig>(buf.f_bavail));
    1173             :     }
    1174             : #else
    1175             :     struct statvfs buf;
    1176             :     if (statvfs(pszDirname, &buf) == 0)
    1177             :     {
    1178             :         nRet = static_cast<GIntBig>(buf.f_frsize *
    1179             :                                     static_cast<GUIntBig>(buf.f_bavail));
    1180             :     }
    1181             : #endif
    1182             : 
    1183             : #endif
    1184           3 :     return nRet;
    1185             : }
    1186             : 
    1187             : /************************************************************************/
    1188             : /*                        SupportsSparseFiles()                         */
    1189             : /************************************************************************/
    1190             : 
    1191             : #ifdef __linux
    1192             : #include <sys/vfs.h>
    1193             : #endif
    1194             : 
    1195           2 : int VSIUnixStdioFilesystemHandler::SupportsSparseFiles(const char *
    1196             : #ifdef __linux
    1197             :                                                            pszPath
    1198             : #endif
    1199             : )
    1200             : {
    1201             : #ifdef __linux
    1202             :     struct statfs sStatFS;
    1203           2 :     if (statfs(pszPath, &sStatFS) == 0)
    1204             :     {
    1205             :         // Add here any missing filesystem supporting sparse files.
    1206             :         // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
    1207           2 :         switch (static_cast<unsigned>(sStatFS.f_type))
    1208             :         {
    1209             :             // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
    1210           2 :             case 0xef53U:      // ext2, 3, 4
    1211             :             case 0x52654973U:  // reiser
    1212             :             case 0x58465342U:  // xfs
    1213             :             case 0x3153464aU:  // jfs
    1214             :             case 0x5346544eU:  // ntfs
    1215             :             case 0x9123683eU:  // brfs
    1216             :             // nfs: NFS < 4.2 supports creating sparse files (but reading them
    1217             :             // not efficiently).
    1218             :             case 0x6969U:
    1219             :             case 0x01021994U:  // tmpfs
    1220           2 :                 return TRUE;
    1221             : 
    1222           0 :             case 0x4d44U:  // msdos
    1223           0 :                 return FALSE;
    1224             : 
    1225           0 :             case 0x53464846U:  // Windows Subsystem for Linux fs
    1226             :             {
    1227             :                 static bool bUnknownFSEmitted = false;
    1228           0 :                 if (!bUnknownFSEmitted)
    1229             :                 {
    1230           0 :                     CPLDebug("VSI",
    1231             :                              "Windows Subsystem for Linux FS is at "
    1232             :                              "the time of writing not known to support sparse "
    1233             :                              "files");
    1234           0 :                     bUnknownFSEmitted = true;
    1235             :                 }
    1236           0 :                 return FALSE;
    1237             :             }
    1238             : 
    1239           0 :             default:
    1240             :             {
    1241             :                 static bool bUnknownFSEmitted = false;
    1242           0 :                 if (!bUnknownFSEmitted)
    1243             :                 {
    1244           0 :                     CPLDebug("VSI",
    1245             :                              "Filesystem with type %X unknown. "
    1246             :                              "Assuming it does not support sparse files",
    1247           0 :                              static_cast<int>(sStatFS.f_type));
    1248           0 :                     bUnknownFSEmitted = true;
    1249             :                 }
    1250           0 :                 return FALSE;
    1251             :             }
    1252             :         }
    1253             :     }
    1254           0 :     return FALSE;
    1255             : #else
    1256             :     static bool bMessageEmitted = false;
    1257             :     if (!bMessageEmitted)
    1258             :     {
    1259             :         CPLDebug("VSI", "Sorry: SupportsSparseFiles() not implemented "
    1260             :                         "for this operating system");
    1261             :         bMessageEmitted = true;
    1262             :     }
    1263             :     return FALSE;
    1264             : #endif
    1265             : }
    1266             : 
    1267             : /************************************************************************/
    1268             : /*                              IsLocal()                               */
    1269             : /************************************************************************/
    1270             : 
    1271        1476 : bool VSIUnixStdioFilesystemHandler::IsLocal(const char *
    1272             : #ifdef __linux
    1273             :                                                 pszPath
    1274             : #endif
    1275             : ) const
    1276             : {
    1277             : #ifdef __linux
    1278             :     struct statfs sStatFS;
    1279        1476 :     if (statfs(pszPath, &sStatFS) == 0)
    1280             :     {
    1281             :         // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
    1282         168 :         switch (static_cast<unsigned>(sStatFS.f_type))
    1283             :         {
    1284             :             // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
    1285           0 :             case 0x6969U:      // NFS
    1286             :             case 0x517bU:      // SMB
    1287             :             case 0xff534d42U:  // CIFS
    1288             :             case 0xfe534d42U:  // SMB2
    1289             :                 // (https://github.com/libuv/libuv/blob/97dcdb1926f6aca43171e1614338bcef067abd59/src/unix/fs.c#L960)
    1290           0 :                 return false;
    1291             :         }
    1292             :     }
    1293             : #else
    1294             :     static bool bMessageEmitted = false;
    1295             :     if (!bMessageEmitted)
    1296             :     {
    1297             :         CPLDebug("VSI", "Sorry: IsLocal() not implemented "
    1298             :                         "for this operating system");
    1299             :         bMessageEmitted = true;
    1300             :     }
    1301             : #endif
    1302        1476 :     return true;
    1303             : }
    1304             : 
    1305             : /************************************************************************/
    1306             : /*                      SupportsSequentialWrite()                       */
    1307             : /************************************************************************/
    1308             : 
    1309         166 : bool VSIUnixStdioFilesystemHandler::SupportsSequentialWrite(
    1310             :     const char *pszPath, bool /* bAllowLocalTempFile */)
    1311             : {
    1312             :     VSIStatBufL sStat;
    1313         166 :     if (VSIStatL(pszPath, &sStat) == 0)
    1314          62 :         return access(pszPath, W_OK) == 0;
    1315         104 :     return access(CPLGetDirnameSafe(pszPath).c_str(), W_OK) == 0;
    1316             : }
    1317             : 
    1318             : /************************************************************************/
    1319             : /*                        SupportsRandomWrite()                         */
    1320             : /************************************************************************/
    1321             : 
    1322         104 : bool VSIUnixStdioFilesystemHandler::SupportsRandomWrite(
    1323             :     const char *pszPath, bool /* bAllowLocalTempFile */)
    1324             : {
    1325         104 :     return SupportsSequentialWrite(pszPath, false);
    1326             : }
    1327             : 
    1328             : /************************************************************************/
    1329             : /*                           VSIDIRUnixStdio                            */
    1330             : /************************************************************************/
    1331             : 
    1332             : struct VSIDIRUnixStdio final : public VSIDIR
    1333             : {
    1334             :     struct DIRCloser
    1335             :     {
    1336         886 :         void operator()(DIR *d)
    1337             :         {
    1338         886 :             if (d)
    1339         886 :                 closedir(d);
    1340         886 :         }
    1341             :     };
    1342             : 
    1343             :     CPLString osRootPath{};
    1344             :     CPLString osBasePath{};
    1345             :     std::unique_ptr<DIR, DIRCloser> m_psDir{};
    1346             :     int nRecurseDepth = 0;
    1347             :     VSIDIREntry entry{};
    1348             :     std::vector<std::unique_ptr<VSIDIR>> aoStackSubDir{};
    1349             :     std::string m_osFilterPrefix{};
    1350             :     bool m_bNameAndTypeOnly = false;
    1351             : 
    1352             :     const VSIDIREntry *NextDirEntry() override;
    1353             : };
    1354             : 
    1355             : /************************************************************************/
    1356             : /*                          OpenDirInternal()                           */
    1357             : /************************************************************************/
    1358             : 
    1359             : /* static */
    1360         898 : std::unique_ptr<VSIDIRUnixStdio> VSIUnixStdioFilesystemHandler::OpenDirInternal(
    1361             :     const char *pszPath, int nRecurseDepth, const char *const *papszOptions)
    1362             : {
    1363        1796 :     std::unique_ptr<DIR, VSIDIRUnixStdio::DIRCloser> psDir(opendir(pszPath));
    1364         898 :     if (psDir == nullptr)
    1365             :     {
    1366          12 :         return nullptr;
    1367             :     }
    1368        1772 :     auto dir = std::make_unique<VSIDIRUnixStdio>();
    1369         886 :     dir->osRootPath = pszPath;
    1370         886 :     dir->nRecurseDepth = nRecurseDepth;
    1371         886 :     dir->m_psDir = std::move(psDir);
    1372         886 :     dir->m_osFilterPrefix = CSLFetchNameValueDef(papszOptions, "PREFIX", "");
    1373         886 :     dir->m_bNameAndTypeOnly = CPLTestBool(
    1374             :         CSLFetchNameValueDef(papszOptions, "NAME_AND_TYPE_ONLY", "NO"));
    1375         886 :     return dir;
    1376             : }
    1377             : 
    1378             : /************************************************************************/
    1379             : /*                              OpenDir()                               */
    1380             : /************************************************************************/
    1381             : 
    1382         254 : VSIDIR *VSIUnixStdioFilesystemHandler::OpenDir(const char *pszPath,
    1383             :                                                int nRecurseDepth,
    1384             :                                                const char *const *papszOptions)
    1385             : {
    1386         254 :     return OpenDirInternal(pszPath, nRecurseDepth, papszOptions).release();
    1387             : }
    1388             : 
    1389             : /************************************************************************/
    1390             : /*                            NextDirEntry()                            */
    1391             : /************************************************************************/
    1392             : 
    1393       24878 : const VSIDIREntry *VSIDIRUnixStdio::NextDirEntry()
    1394             : {
    1395       24878 : begin:
    1396       24878 :     if (VSI_ISDIR(entry.nMode) && nRecurseDepth != 0)
    1397             :     {
    1398        1288 :         CPLString osCurFile(osRootPath);
    1399         644 :         if (!osCurFile.empty())
    1400         644 :             osCurFile += '/';
    1401         644 :         osCurFile += entry.pszName;
    1402             :         auto subdir = VSIUnixStdioFilesystemHandler::OpenDirInternal(
    1403         644 :             osCurFile, nRecurseDepth - 1, nullptr);
    1404         644 :         if (subdir)
    1405             :         {
    1406         644 :             subdir->osRootPath = osRootPath;
    1407         644 :             subdir->osBasePath = entry.pszName;
    1408         644 :             subdir->m_osFilterPrefix = m_osFilterPrefix;
    1409         644 :             subdir->m_bNameAndTypeOnly = m_bNameAndTypeOnly;
    1410         644 :             aoStackSubDir.push_back(std::move(subdir));
    1411             :         }
    1412         644 :         entry.nMode = 0;
    1413             :     }
    1414             : 
    1415       25522 :     while (!aoStackSubDir.empty())
    1416             :     {
    1417       13187 :         auto l_entry = aoStackSubDir.back()->NextDirEntry();
    1418       13187 :         if (l_entry)
    1419             :         {
    1420       12543 :             return l_entry;
    1421             :         }
    1422         644 :         aoStackSubDir.pop_back();
    1423             :     }
    1424             : 
    1425       14103 :     while (const auto *psEntry = readdir(m_psDir.get()))
    1426             :     {
    1427             :         // Skip . and ..entries
    1428       13224 :         if (psEntry->d_name[0] == '.' &&
    1429        1771 :             (psEntry->d_name[1] == '\0' ||
    1430         891 :              (psEntry->d_name[1] == '.' && psEntry->d_name[2] == '\0')))
    1431             :         {
    1432             :             // do nothing
    1433             :         }
    1434             :         else
    1435             :         {
    1436       11461 :             CPLFree(entry.pszName);
    1437       11461 :             CPLString osName(osBasePath);
    1438       11461 :             if (!osName.empty())
    1439        7320 :                 osName += '/';
    1440       11461 :             osName += psEntry->d_name;
    1441             : 
    1442       11461 :             entry.pszName = CPLStrdup(osName);
    1443       11461 :             entry.nMode = 0;
    1444       11461 :             entry.nSize = 0;
    1445       11461 :             entry.nMTime = 0;
    1446       11461 :             entry.bModeKnown = false;
    1447       11461 :             entry.bSizeKnown = false;
    1448       11461 :             entry.bMTimeKnown = false;
    1449             : 
    1450       11461 :             CPLString osCurFile(osRootPath);
    1451       11461 :             if (!osCurFile.empty())
    1452       11461 :                 osCurFile += '/';
    1453       11461 :             osCurFile += entry.pszName;
    1454             : 
    1455             : #if !defined(__sun) && !defined(__HAIKU__)
    1456       11461 :             if (psEntry->d_type == DT_REG)
    1457        9337 :                 entry.nMode = S_IFREG;
    1458        2124 :             else if (psEntry->d_type == DT_DIR)
    1459         727 :                 entry.nMode = S_IFDIR;
    1460        1397 :             else if (psEntry->d_type == DT_LNK)
    1461        1397 :                 entry.nMode = S_IFLNK;
    1462             : #endif
    1463             : 
    1464       17904 :             const auto StatFile = [&osCurFile, this]()
    1465             :             {
    1466             :                 VSIStatBufL sStatL;
    1467        8952 :                 if (VSIStatL(osCurFile, &sStatL) == 0)
    1468             :                 {
    1469        8952 :                     entry.nMode = sStatL.st_mode;
    1470        8952 :                     entry.nSize = sStatL.st_size;
    1471        8952 :                     entry.nMTime = sStatL.st_mtime;
    1472        8952 :                     entry.bModeKnown = true;
    1473        8952 :                     entry.bSizeKnown = true;
    1474        8952 :                     entry.bMTimeKnown = true;
    1475             :                 }
    1476        8952 :             };
    1477             : 
    1478       11473 :             if (!m_osFilterPrefix.empty() &&
    1479          12 :                 m_osFilterPrefix.size() > osName.size())
    1480             :             {
    1481           6 :                 if (STARTS_WITH(m_osFilterPrefix.c_str(), osName.c_str()) &&
    1482           2 :                     m_osFilterPrefix[osName.size()] == '/')
    1483             :                 {
    1484             : #if !defined(__sun) && !defined(__HAIKU__)
    1485           1 :                     if (psEntry->d_type == DT_UNKNOWN)
    1486             : #endif
    1487             :                     {
    1488           0 :                         StatFile();
    1489             :                     }
    1490           1 :                     if (VSI_ISDIR(entry.nMode))
    1491             :                     {
    1492           1 :                         goto begin;
    1493             :                     }
    1494             :                 }
    1495           3 :                 continue;
    1496             :             }
    1497       11465 :             if (!m_osFilterPrefix.empty() &&
    1498           8 :                 !STARTS_WITH(osName.c_str(), m_osFilterPrefix.c_str()))
    1499             :             {
    1500           2 :                 continue;
    1501             :             }
    1502             : 
    1503       11455 :             if (!m_bNameAndTypeOnly
    1504             : #if !defined(__sun) && !defined(__HAIKU__)
    1505        2503 :                 || psEntry->d_type == DT_UNKNOWN
    1506             : #endif
    1507             :             )
    1508             :             {
    1509        8952 :                 StatFile();
    1510             :             }
    1511             : 
    1512       11455 :             return &(entry);
    1513             :         }
    1514        1768 :     }
    1515             : 
    1516         879 :     return nullptr;
    1517             : }
    1518             : 
    1519             : #ifdef VSI_COUNT_BYTES_READ
    1520             : /************************************************************************/
    1521             : /*                             AddToTotal()                             */
    1522             : /************************************************************************/
    1523             : 
    1524             : void VSIUnixStdioFilesystemHandler::AddToTotal(vsi_l_offset nBytes)
    1525             : {
    1526             :     CPLMutexHolder oHolder(&hMutex);
    1527             :     nTotalBytesRead += nBytes;
    1528             : }
    1529             : 
    1530             : #endif
    1531             : 
    1532             : /************************************************************************/
    1533             : /*                        GetCanonicalFilename()                        */
    1534             : /************************************************************************/
    1535             : 
    1536             : #ifdef HAS_CASE_INSENSITIVE_FILE_SYSTEM
    1537             : std::string VSIUnixStdioFilesystemHandler::GetCanonicalFilename(
    1538             :     const std::string &osFilename) const
    1539             : {
    1540             :     char szResolvedPath[PATH_MAX];
    1541             :     const char *pszFilename = osFilename.c_str();
    1542             :     if (realpath(pszFilename, szResolvedPath))
    1543             :     {
    1544             :         const char *pszFilenameLastPart = strrchr(pszFilename, '/');
    1545             :         const char *pszResolvedFilenameLastPart = strrchr(szResolvedPath, '/');
    1546             :         if (pszFilenameLastPart && pszResolvedFilenameLastPart &&
    1547             :             EQUAL(pszFilenameLastPart, pszResolvedFilenameLastPart))
    1548             :         {
    1549             :             std::string osRet;
    1550             :             osRet.assign(pszFilename, pszFilenameLastPart - pszFilename);
    1551             :             osRet += pszResolvedFilenameLastPart;
    1552             :             return osRet;
    1553             :         }
    1554             :         return szResolvedPath;
    1555             :     }
    1556             :     return osFilename;
    1557             : }
    1558             : #endif
    1559             : 
    1560             : /************************************************************************/
    1561             : /*                     VSIInstallLargeFileHandler()                     */
    1562             : /************************************************************************/
    1563             : 
    1564        1789 : void VSIInstallLargeFileHandler()
    1565             : 
    1566             : {
    1567        1789 :     VSIFileManager::InstallHandler(
    1568        3578 :         "", std::make_shared<VSIUnixStdioFilesystemHandler>());
    1569        1789 : }
    1570             : 
    1571             : #endif  // ndef WIN32
    1572             : 
    1573             : //! @endcond

Generated by: LCOV version 1.14