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: 393 438 89.7 %
Date: 2026-08-21 03:01:58 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        2101 :     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      147411 : VSIUnixStdioHandle::VSIUnixStdioHandle(
     299             : #ifndef VSI_COUNT_BYTES_READ
     300             :     CPL_UNUSED
     301             : #endif
     302             :     VSIUnixStdioFilesystemHandler *poFSIn,
     303      147411 :     int fdIn, AccessMode eAccessModeIn)
     304      147411 :     : fd(fdIn), eAccessMode(eAccessModeIn)
     305             : #ifdef VSI_COUNT_BYTES_READ
     306             :       ,
     307             :       poFS(poFSIn)
     308             : #endif
     309             : {
     310      147366 : }
     311             : 
     312             : /************************************************************************/
     313             : /*                        ~VSIUnixStdioHandle()                         */
     314             : /************************************************************************/
     315             : 
     316      293892 : VSIUnixStdioHandle::~VSIUnixStdioHandle()
     317             : {
     318      146876 :     VSIUnixStdioHandle::Close();
     319      294006 : }
     320             : 
     321             : /************************************************************************/
     322             : /*                               Close()                                */
     323             : /************************************************************************/
     324             : 
     325      297824 : int VSIUnixStdioHandle::Close()
     326             : 
     327             : {
     328      297824 :     if (fd < 0)
     329      150585 :         return 0;
     330             : 
     331             :     VSIDebug1("VSIUnixStdioHandle::Close(%d)", fd);
     332             : 
     333             : #ifdef VSI_COUNT_BYTES_READ
     334             :     poFS->AddToTotal(nTotalBytesRead);
     335             : #endif
     336             : 
     337      147239 :     int ret = VSIUnixStdioHandle::Flush();
     338             : 
     339             : #ifdef __linux
     340      293067 :     if (ret == 0 && !m_bCancelCreation && !m_osFilename.empty() &&
     341      146008 :         m_bUnlinkedFile)
     342             :     {
     343           3 :         ret = fsync(fd);
     344           3 :         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           3 :             snprintf(szPath, sizeof(szPath), "/proc/self/fd/%d", fd);
     351           3 :             ret = linkat(AT_FDCWD, szPath, AT_FDCWD, m_osFilename.c_str(),
     352             :                          AT_SYMLINK_FOLLOW);
     353           3 :             if (ret != 0)
     354           0 :                 CPLDebug("CPL", "linkat() failed with errno=%d", errno);
     355             :         }
     356             :     }
     357             : #endif
     358             : 
     359      147479 :     int ret2 = close(fd);
     360      146791 :     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      146791 :     fd = -1;
     371      146791 :     return ret;
     372             : }
     373             : 
     374             : /************************************************************************/
     375             : /*                           CancelCreation()                           */
     376             : /************************************************************************/
     377             : 
     378         550 : void VSIUnixStdioHandle::CancelCreation()
     379             : {
     380             : #if defined(__linux)
     381         550 :     if (!m_osFilename.empty() && !m_bUnlinkedFile)
     382             :     {
     383         482 :         unlink(m_osFilename.c_str());
     384         482 :         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         550 : }
     401             : 
     402             : /************************************************************************/
     403             : /*                                Seek()                                */
     404             : /************************************************************************/
     405             : 
     406     4925220 : int VSIUnixStdioHandle::Seek(vsi_l_offset nOffsetIn, int nWhence)
     407             : {
     408     4925220 :     bAtEOF = false;
     409     4925220 :     bError = false;
     410             : 
     411             :     // Seeks that do nothing are still surprisingly expensive with MSVCRT.
     412             :     // try and short circuit if possible.
     413     4925220 :     if (eAccessMode != AccessMode::APPEND_READ_WRITE && nWhence == SEEK_SET &&
     414     4291250 :         nOffsetIn == m_nFilePos)
     415      759915 :         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     4165300 :     if (eLastOp == Operation::WRITE)
     428             :     {
     429      298837 :         eLastOp = Operation::NONE;
     430             : 
     431      298837 :         if (m_bBufferDirty && Flush() < 0)
     432           0 :             return -1;
     433             : 
     434      298837 :         const auto nNewPos = VSI_LSEEK64(fd, nOffsetIn, nWhence);
     435      298837 :         if (nNewPos < 0)
     436             :         {
     437           0 :             bError = true;
     438           0 :             return -1;
     439             :         }
     440             : 
     441      298837 :         m_nFilePos = nNewPos;
     442      298837 :         m_nBufferCurPos = 0;
     443      298837 :         m_nBufferSize = 0;
     444             :     }
     445             : 
     446             :     else
     447             :     {
     448     3866470 :         eLastOp = Operation::READ;
     449             : 
     450             :         vsi_l_offset nTargetPos;
     451             : 
     452             :         // Compute absolute target position
     453     3866470 :         switch (nWhence)
     454             :         {
     455     3435170 :             case SEEK_SET:
     456     3435170 :                 nTargetPos = nOffsetIn;
     457     3435170 :                 break;
     458       83387 :             case SEEK_CUR:
     459       83387 :                 nTargetPos = m_nFilePos + nOffsetIn;
     460       83387 :                 break;
     461      347727 :             case SEEK_END:
     462             :             {
     463      347727 :                 const auto nNewPos = VSI_LSEEK64(fd, nOffsetIn, SEEK_END);
     464      347774 :                 if (nNewPos < 0)
     465             :                 {
     466           0 :                     bError = true;
     467           0 :                     return -1;
     468             :                 }
     469      347774 :                 nTargetPos = nNewPos;
     470      347774 :                 break;
     471             :             }
     472         187 :             default:
     473         187 :                 return -1;
     474             :         }
     475             : 
     476             :         // Compute absolute position of the current buffer
     477     3866330 :         const auto nBufStartOffset = m_nFilePos - m_nBufferCurPos;
     478     3866330 :         const auto nBufEndOffset = nBufStartOffset + m_nBufferSize;
     479             : 
     480     3866330 :         if (nTargetPos >= nBufStartOffset && nTargetPos < nBufEndOffset)
     481             :         {
     482             :             // Seek inside current buffer - adjust buffer pos only
     483     2796700 :             m_nBufferCurPos = static_cast<size_t>(nTargetPos - nBufStartOffset);
     484     2796700 :             m_nFilePos = nTargetPos;
     485             :         }
     486             :         else
     487             :         {
     488             :             // Outside buffer, do real seek and invalidate buffer
     489     1069630 :             const auto nNewPos = VSI_LSEEK64(fd, nTargetPos, SEEK_SET);
     490     1069740 :             if (nNewPos < 0)
     491             :             {
     492           1 :                 bError = true;
     493           1 :                 return -1;
     494             :             }
     495             : 
     496     1069740 :             m_nFilePos = nNewPos;
     497     1069740 :             m_nBufferCurPos = 0;
     498     1069740 :             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     4165280 :     return 0;
     506             : }
     507             : 
     508             : /************************************************************************/
     509             : /*                                Tell()                                */
     510             : /************************************************************************/
     511             : 
     512     3792920 : vsi_l_offset VSIUnixStdioHandle::Tell()
     513             : 
     514             : {
     515     3792920 :     return m_nFilePos;
     516             : }
     517             : 
     518             : /************************************************************************/
     519             : /*                               Flush()                                */
     520             : /************************************************************************/
     521             : 
     522      472870 : int VSIUnixStdioHandle::Flush()
     523             : 
     524             : {
     525             :     VSIDebug1("VSIUnixStdioHandle::Flush(%d)", fd);
     526             : 
     527      472870 :     if (m_bBufferDirty)
     528             :     {
     529      346942 :         m_bBufferDirty = false;
     530             : 
     531             :         // Sync kernel offset to our logical position before writing
     532      346942 :         if (VSI_LSEEK64(fd, m_nFilePos - m_nBufferSize, SEEK_SET) < 0)
     533             :         {
     534           0 :             return EOF;
     535             :         }
     536             : 
     537      346942 :         size_t nOff = 0;
     538      693884 :         while (m_nBufferSize > 0)
     539             :         {
     540      346942 :             errno = 0;
     541             :             const auto nWritten =
     542      346942 :                 write(fd, m_abyBuffer.data() + nOff, m_nBufferSize);
     543      346942 :             if (nWritten < 0)
     544             :             {
     545           0 :                 if (errno == EINTR)
     546           0 :                     continue;
     547           0 :                 return EOF;
     548             :             }
     549      346942 :             CPLAssert(static_cast<size_t>(nWritten) <= m_nBufferSize);
     550      346942 :             nOff += nWritten;
     551      346942 :             m_nBufferSize -= nWritten;
     552             :         }
     553             :     }
     554             : 
     555      472870 :     return 0;
     556             : }
     557             : 
     558             : /************************************************************************/
     559             : /*                                Read()                                */
     560             : /************************************************************************/
     561             : 
     562     9631760 : size_t VSIUnixStdioHandle::Read(void *pBuffer, size_t nBytes)
     563             : 
     564             : {
     565     9631760 :     if (nBytes == 0 || bAtEOF)
     566       41115 :         return 0;
     567     9590650 :     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     9590640 :     if (eLastOp == Operation::WRITE)
     582             :     {
     583        2854 :         if (m_bBufferDirty && Flush() < 0)
     584             :         {
     585           0 :             bError = true;
     586           0 :             return -1;
     587             :         }
     588        2854 :         if (VSI_LSEEK64(fd, m_nFilePos, SEEK_SET) < 0)
     589             :         {
     590           0 :             bError = true;
     591           0 :             return -1;
     592             :         }
     593        2854 :         m_nBufferCurPos = 0;
     594        2854 :         m_nBufferSize = 0;
     595             :     }
     596             : 
     597     9590640 :     const size_t nTotal = nBytes;
     598     9590640 :     size_t nBytesRead = 0;
     599     9590640 :     GByte *const pabyDest = static_cast<GByte *>(pBuffer);
     600             : 
     601    19212100 :     while (nBytesRead < nTotal)
     602             :     {
     603     9694800 :         size_t nAvailInBuffer = m_nBufferSize - m_nBufferCurPos;
     604             : 
     605             :         // If buffer is empty
     606     9694800 :         if (nAvailInBuffer == 0)
     607             :         {
     608      917589 :             size_t nRemaining = nTotal - nBytesRead;
     609      917589 :             if (nRemaining >= BUFFER_SIZE)
     610             :             {
     611             :                 // Bypass buffer if large request
     612       75303 :                 errno = 0;
     613             :                 const ssize_t nBytesReadThisTime =
     614       75303 :                     read(fd, pabyDest + nBytesRead, nRemaining);
     615       75302 :                 if (nBytesReadThisTime <= 0)
     616             :                 {
     617       14759 :                     if (nBytesReadThisTime == 0)
     618       14754 :                         bAtEOF = true;
     619           5 :                     else if (errno == EINTR)
     620           0 :                         continue;
     621             :                     else
     622           5 :                         bError = true;
     623       73273 :                     break;
     624             :                 }
     625       60543 :                 m_nFilePos += nBytesReadThisTime;
     626       60543 :                 nBytesRead += nBytesReadThisTime;
     627       60543 :                 m_nBufferCurPos = 0;
     628       60543 :                 m_nBufferSize = 0;
     629             :             }
     630             :             else
     631             :             {
     632             :                 // Small request: Refill internal buffer
     633      842286 :                 errno = 0;
     634             :                 const ssize_t nBytesReadThisTime =
     635      842286 :                     read(fd, m_abyBuffer.data(), BUFFER_SIZE);
     636      842087 :                 if (nBytesReadThisTime <= 0)
     637             :                 {
     638       58514 :                     if (nBytesReadThisTime == 0)
     639       57325 :                         bAtEOF = true;
     640        1189 :                     else if (errno == EINTR)
     641           0 :                         continue;
     642             :                     else
     643        1189 :                         bError = true;
     644       58514 :                     break;
     645             :                 }
     646      783573 :                 m_nBufferCurPos = 0;
     647      783573 :                 m_nBufferSize = nBytesReadThisTime;
     648      783573 :                 nAvailInBuffer = nBytesReadThisTime;
     649             :             }
     650             :         }
     651             : 
     652             :         // Copy from buffer to user
     653     9621330 :         const size_t nToCopy = std::min(nTotal - nBytesRead, nAvailInBuffer);
     654     9621300 :         memcpy(pabyDest + nBytesRead, m_abyBuffer.data() + m_nBufferCurPos,
     655             :                nToCopy);
     656     9621150 :         nBytesRead += nToCopy;
     657     9621150 :         m_nBufferCurPos += nToCopy;
     658     9621150 :         m_nFilePos += nToCopy;
     659             : 
     660     9621150 :         CPLAssert(m_nFilePos - m_nBufferCurPos + m_nBufferSize ==
     661             :                   static_cast<uint64_t>(VSI_LSEEK64(fd, 0, SEEK_CUR)));
     662             :     }
     663             : 
     664             : #ifdef VSI_DEBUG
     665             :     const int nError = errno;
     666             :     VSIDebug4("VSIUnixStdioHandle::Read(%d,%%ld) = %" PRId64, fd,
     667             :               static_cast<long>(nBytes), static_cast<int64_t>(nBytesRead));
     668             :     errno = nError;
     669             : #endif
     670             : 
     671     9590530 :     eLastOp = Operation::READ;
     672             : 
     673             : #ifdef VSI_COUNT_BYTES_READ
     674             :     nTotalBytesRead += nBytesRead;
     675             : #endif
     676             : 
     677     9590530 :     return nBytesRead;
     678             : }
     679             : 
     680             : /************************************************************************/
     681             : /*                               Write()                                */
     682             : /************************************************************************/
     683             : 
     684     2464650 : size_t VSIUnixStdioHandle::Write(const void *pBuffer, size_t nBytes)
     685             : 
     686             : {
     687     2464650 :     if (nBytes == 0)
     688       22958 :         return 0;
     689     2441690 :     if (eAccessMode == AccessMode::READ_ONLY)
     690             :     {
     691           2 :         bError = true;
     692           2 :         errno = EINVAL;
     693           2 :         return 0;
     694             :     }
     695             : 
     696             :     /* -------------------------------------------------------------------- */
     697             :     /*      If a fwrite() is followed by an fread(), the POSIX rules are    */
     698             :     /*      that some of the write may still be buffered and lost.  We      */
     699             :     /*      are required to do a seek between to force flushing.   So we    */
     700             :     /*      keep careful track of what happened last to know if we          */
     701             :     /*      skipped a flushing seek that we may need to do now.             */
     702             :     /* -------------------------------------------------------------------- */
     703     2441690 :     if (eLastOp == Operation::READ)
     704             :     {
     705       43535 :         if (VSI_LSEEK64(fd, m_nFilePos, SEEK_SET) < 0)
     706             :         {
     707           0 :             bError = true;
     708           0 :             return -1;
     709             :         }
     710       43535 :         m_nBufferCurPos = 0;
     711       43535 :         m_nBufferSize = 0;
     712             :     }
     713             : 
     714     2441690 :     const size_t nTotal = nBytes;
     715     2441690 :     size_t nBytesWritten = 0;
     716     2441690 :     const GByte *const pabySrc = static_cast<const GByte *>(pBuffer);
     717     4898050 :     while (nBytesWritten < nTotal)
     718             :     {
     719     2456350 :         const size_t nRemaining = nTotal - nBytesWritten;
     720             : 
     721             :         // Bypass buffer if request > BUFFER_SIZE
     722     2456350 :         if (m_nBufferSize == 0 && nRemaining >= BUFFER_SIZE)
     723             :         {
     724             :             const auto nWritten =
     725       10894 :                 write(fd, pabySrc + nBytesWritten, nRemaining);
     726       10894 :             if (nWritten < 0)
     727             :             {
     728           0 :                 bError = true;
     729           0 :                 break;
     730             :             }
     731       10894 :             CPLAssert(static_cast<size_t>(nWritten) <= nRemaining);
     732       10894 :             m_nFilePos += nWritten;
     733       10894 :             nBytesWritten += nWritten;
     734       10894 :             continue;
     735             :         }
     736             : 
     737             :         // Small request: Fill internal buffer
     738     2445460 :         const size_t nFreeSpaceInBuffer = BUFFER_SIZE - m_nBufferSize;
     739     2445460 :         const size_t nToCopy = std::min(nRemaining, nFreeSpaceInBuffer);
     740     2445460 :         memcpy(m_abyBuffer.data() + m_nBufferSize, pabySrc + nBytesWritten,
     741             :                nToCopy);
     742             : 
     743     2445460 :         nBytesWritten += nToCopy;
     744     2445460 :         m_nBufferSize += nToCopy;
     745     2445460 :         m_nFilePos += nToCopy;
     746     2445460 :         m_bBufferDirty = true;
     747             : 
     748     2445460 :         if (m_nBufferSize == BUFFER_SIZE && Flush() < 0)
     749             :         {
     750           0 :             break;
     751             :         }
     752             :     }
     753             : 
     754             : #ifdef VSI_DEBUG
     755             :     const int nError = errno;
     756             :     VSIDebug4("VSIUnixStdioHandle::Write(%d,%ld) = %" PRId64, fd,
     757             :               static_cast<long>(nBytes), static_cast<int64_t>(nBytesWritten));
     758             :     errno = nError;
     759             : #endif
     760             : 
     761     2441690 :     eLastOp = Operation::WRITE;
     762             : 
     763     2441690 :     return nBytesWritten;
     764             : }
     765             : 
     766             : /************************************************************************/
     767             : /*                              ClearErr()                              */
     768             : /************************************************************************/
     769             : 
     770        8808 : void VSIUnixStdioHandle::ClearErr()
     771             : 
     772             : {
     773        8808 :     bAtEOF = false;
     774        8808 :     bError = false;
     775        8808 : }
     776             : 
     777             : /************************************************************************/
     778             : /*                               Error()                                */
     779             : /************************************************************************/
     780             : 
     781       65093 : int VSIUnixStdioHandle::Error()
     782             : 
     783             : {
     784       65093 :     return bError ? TRUE : FALSE;
     785             : }
     786             : 
     787             : /************************************************************************/
     788             : /*                                Eof()                                 */
     789             : /************************************************************************/
     790             : 
     791      161580 : int VSIUnixStdioHandle::Eof()
     792             : 
     793             : {
     794      161580 :     return bAtEOF ? TRUE : FALSE;
     795             : }
     796             : 
     797             : /************************************************************************/
     798             : /*                              Truncate()                              */
     799             : /************************************************************************/
     800             : 
     801         617 : int VSIUnixStdioHandle::Truncate(vsi_l_offset nNewSize)
     802             : {
     803         617 :     if (Flush() != 0)
     804           0 :         return -1;
     805         617 :     return VSI_FTRUNCATE64(fd, nNewSize);
     806             : }
     807             : 
     808             : /************************************************************************/
     809             : /*                           GetRangeStatus()                           */
     810             : /************************************************************************/
     811             : 
     812             : #ifdef __linux
     813             : #if !defined(MISSING_LINUX_FS_H)
     814             : #include <linux/fs.h>  // FS_IOC_FIEMAP
     815             : #endif
     816             : #ifdef FS_IOC_FIEMAP
     817             : #include <linux/types.h>   // for types used in linux/fiemap.h
     818             : #include <linux/fiemap.h>  // struct fiemap
     819             : #endif
     820             : #include <sys/ioctl.h>
     821             : #include <errno.h>
     822             : #endif
     823             : 
     824         573 : VSIRangeStatus VSIUnixStdioHandle::GetRangeStatus(vsi_l_offset
     825             : #ifdef FS_IOC_FIEMAP
     826             :                                                       nOffset
     827             : #endif
     828             :                                                   ,
     829             :                                                   vsi_l_offset
     830             : #ifdef FS_IOC_FIEMAP
     831             :                                                       nLength
     832             : #endif
     833             : )
     834             : {
     835             : #ifdef FS_IOC_FIEMAP
     836             :     // fiemap IOCTL documented at
     837             :     // https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt
     838             : 
     839             :     // The fiemap struct contains a "variable length" array at its end
     840             :     // As we are interested in only one extent, we allocate the base size of
     841             :     // fiemap + one fiemap_extent.
     842             :     GByte abyBuffer[sizeof(struct fiemap) + sizeof(struct fiemap_extent)];
     843         573 :     struct fiemap *psExtentMap = reinterpret_cast<struct fiemap *>(&abyBuffer);
     844         573 :     memset(psExtentMap, 0,
     845             :            sizeof(struct fiemap) + sizeof(struct fiemap_extent));
     846         573 :     psExtentMap->fm_start = nOffset;
     847         573 :     psExtentMap->fm_length = nLength;
     848         573 :     psExtentMap->fm_extent_count = 1;
     849         573 :     int ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
     850         573 :     if (ret < 0)
     851           0 :         return VSI_RANGE_STATUS_UNKNOWN;
     852         573 :     if (psExtentMap->fm_mapped_extents == 0)
     853           2 :         return VSI_RANGE_STATUS_HOLE;
     854             :     // In case there is one extent with unknown status, retry after having
     855             :     // asked the kernel to sync the file.
     856         571 :     const fiemap_extent *pasExtent = &(psExtentMap->fm_extents[0]);
     857         571 :     if (psExtentMap->fm_mapped_extents == 1 &&
     858         571 :         (pasExtent[0].fe_flags & FIEMAP_EXTENT_UNKNOWN) != 0)
     859             :     {
     860          23 :         psExtentMap->fm_flags = FIEMAP_FLAG_SYNC;
     861          23 :         psExtentMap->fm_start = nOffset;
     862          23 :         psExtentMap->fm_length = nLength;
     863          23 :         psExtentMap->fm_extent_count = 1;
     864          23 :         ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
     865          23 :         if (ret < 0)
     866           0 :             return VSI_RANGE_STATUS_UNKNOWN;
     867          23 :         if (psExtentMap->fm_mapped_extents == 0)
     868           0 :             return VSI_RANGE_STATUS_HOLE;
     869             :     }
     870         571 :     return VSI_RANGE_STATUS_DATA;
     871             : #else
     872             :     static bool bMessageEmitted = false;
     873             :     if (!bMessageEmitted)
     874             :     {
     875             :         CPLDebug("VSI", "Sorry: GetExtentStatus() not implemented for "
     876             :                         "this operating system");
     877             :         bMessageEmitted = true;
     878             :     }
     879             :     return VSI_RANGE_STATUS_UNKNOWN;
     880             : #endif
     881             : }
     882             : 
     883             : /************************************************************************/
     884             : /*                              HasPRead()                              */
     885             : /************************************************************************/
     886             : 
     887             : #if defined(HAVE_PREAD64) || (defined(HAVE_PREAD_BSD) && SIZEOF_OFF_T == 8)
     888         731 : bool VSIUnixStdioHandle::HasPRead() const
     889             : {
     890         731 :     return true;
     891             : }
     892             : 
     893             : /************************************************************************/
     894             : /*                               PRead()                                */
     895             : /************************************************************************/
     896             : 
     897       24494 : size_t VSIUnixStdioHandle::PRead(void *pBuffer, size_t nSize,
     898             :                                  vsi_l_offset nOffset) const
     899             : {
     900             : #ifdef HAVE_PREAD64
     901       24494 :     return pread64(fd, pBuffer, nSize, nOffset);
     902             : #else
     903             :     return pread(fd, pBuffer, nSize, static_cast<off_t>(nOffset));
     904             : #endif
     905             : }
     906             : #endif
     907             : 
     908             : /************************************************************************/
     909             : /* ==================================================================== */
     910             : /*                       VSIUnixStdioFilesystemHandler                  */
     911             : /* ==================================================================== */
     912             : /************************************************************************/
     913             : 
     914             : #ifdef VSI_COUNT_BYTES_READ
     915             : /************************************************************************/
     916             : /*                   ~VSIUnixStdioFilesystemHandler()                   */
     917             : /************************************************************************/
     918             : 
     919             : VSIUnixStdioFilesystemHandler::~VSIUnixStdioFilesystemHandler()
     920             : {
     921             :     CPLDebug(
     922             :         "VSI",
     923             :         "~VSIUnixStdioFilesystemHandler() : nTotalBytesRead = " CPL_FRMT_GUIB,
     924             :         nTotalBytesRead);
     925             : 
     926             :     if (hMutex != nullptr)
     927             :         CPLDestroyMutex(hMutex);
     928             :     hMutex = nullptr;
     929             : }
     930             : #endif
     931             : 
     932             : /************************************************************************/
     933             : /*                                Open()                                */
     934             : /************************************************************************/
     935             : 
     936             : VSIVirtualHandleUniquePtr
     937      236137 : VSIUnixStdioFilesystemHandler::Open(const char *pszFilename,
     938             :                                     const char *pszAccess, bool bSetError,
     939             :                                     CSLConstList /* papszOptions */)
     940             : 
     941             : {
     942      236137 :     int flags = O_RDONLY;
     943             :     int fd;
     944             :     using AccessMode = VSIUnixStdioHandle::AccessMode;
     945      236137 :     AccessMode eAccessMode = AccessMode::NORMAL;
     946      236137 :     if (strchr(pszAccess, 'w'))
     947             :     {
     948       34120 :         if (strchr(pszAccess, '+'))
     949        6753 :             flags = O_CREAT | O_TRUNC | O_RDWR;
     950             :         else
     951             :         {
     952       27367 :             eAccessMode = AccessMode::WRITE_ONLY;
     953       27367 :             flags = O_CREAT | O_TRUNC | O_WRONLY;
     954             :         }
     955       34120 :         fd = VSI_OPEN64(pszFilename, flags, 0664);
     956             :     }
     957      202017 :     else if (strchr(pszAccess, 'a'))
     958             :     {
     959         172 :         if (strchr(pszAccess, '+'))
     960             :         {
     961         122 :             eAccessMode = AccessMode::APPEND_READ_WRITE;
     962         122 :             flags = O_CREAT | O_RDWR;
     963             :         }
     964             :         else
     965          50 :             flags = O_CREAT | O_WRONLY;
     966         172 :         fd = VSI_OPEN64(pszFilename, flags, 0664);
     967             :     }
     968             :     else
     969             :     {
     970      201845 :         if (strchr(pszAccess, '+'))
     971        7080 :             flags = O_RDWR;
     972             :         else
     973      194765 :             eAccessMode = AccessMode::READ_ONLY;
     974      201845 :         fd = VSI_OPEN64(pszFilename, flags);
     975             :     }
     976      235896 :     const int nError = errno;
     977             : 
     978             :     VSIDebug3("VSIUnixStdioFilesystemHandler::Open(\"%s\",\"%s\") = %d",
     979             :               pszFilename, pszAccess, fd);
     980             : 
     981      235896 :     if (fd < 0)
     982             :     {
     983       88709 :         if (bSetError)
     984             :         {
     985       17500 :             VSIError(VSIE_FileError, "%s: %s", pszFilename, strerror(nError));
     986             :         }
     987       88707 :         errno = nError;
     988       88707 :         return nullptr;
     989             :     }
     990             : 
     991      294538 :     auto poHandle = std::make_unique<VSIUnixStdioHandle>(this, fd, eAccessMode);
     992      147231 :     poHandle->m_osFilename = pszFilename;
     993             : 
     994      147321 :     errno = nError;
     995             : 
     996      147321 :     if (strchr(pszAccess, 'a'))
     997         172 :         poHandle->Seek(0, SEEK_END);
     998             : 
     999             :     /* -------------------------------------------------------------------- */
    1000             :     /*      If VSI_CACHE is set we want to use a cached reader instead      */
    1001             :     /*      of more direct io on the underlying file.                       */
    1002             :     /* -------------------------------------------------------------------- */
    1003      255289 :     if (eAccessMode == AccessMode::READ_ONLY &&
    1004      107882 :         CPLTestBool(CPLGetConfigOption("VSI_CACHE", "FALSE")))
    1005             :     {
    1006             :         return VSIVirtualHandleUniquePtr(
    1007           5 :             VSICreateCachedFile(poHandle.release()));
    1008             :     }
    1009             : 
    1010      147402 :     return VSIVirtualHandleUniquePtr(poHandle.release());
    1011             : }
    1012             : 
    1013             : /************************************************************************/
    1014             : /*                    CreateOnlyVisibleAtCloseTime()                    */
    1015             : /************************************************************************/
    1016             : 
    1017             : VSIVirtualHandleUniquePtr
    1018         229 : VSIUnixStdioFilesystemHandler::CreateOnlyVisibleAtCloseTime(
    1019             :     const char *pszFilename, bool bEmulationAllowed, CSLConstList papszOptions)
    1020             : {
    1021             : #ifdef __linux
    1022          53 :     static bool bIsLinkatSupported = []()
    1023             :     {
    1024             :         // Check that /proc is accessible, since we will need it to run linkat()
    1025             :         struct stat statbuf;
    1026          53 :         return stat("/proc/self/fd", &statbuf) == 0;
    1027         229 :     }();
    1028             : 
    1029             :     const int fd = bIsLinkatSupported
    1030         458 :                        ? VSI_OPEN64(CPLGetDirnameSafe(pszFilename).c_str(),
    1031             :                                     O_TMPFILE | O_RDWR, 0666)
    1032         229 :                        : -1;
    1033         229 :     if (fd < 0)
    1034             :     {
    1035         193 :         if (bIsLinkatSupported)
    1036             :         {
    1037         193 :             CPLDebugOnce("VSI",
    1038             :                          "open(O_TMPFILE | O_RDWR) failed with errno=%d (%s). "
    1039             :                          "Going through emulation",
    1040             :                          errno, VSIStrerror(errno));
    1041             :         }
    1042             :         return VSIFilesystemHandler::CreateOnlyVisibleAtCloseTime(
    1043         193 :             pszFilename, bEmulationAllowed, papszOptions);
    1044             :     }
    1045             : 
    1046             :     auto poHandle = std::make_unique<VSIUnixStdioHandle>(
    1047          72 :         this, fd, VSIUnixStdioHandle::AccessMode::NORMAL);
    1048          36 :     poHandle->m_osFilename = pszFilename;
    1049          36 :     poHandle->m_bUnlinkedFile = true;
    1050          36 :     return VSIVirtualHandleUniquePtr(poHandle.release());
    1051             : #else
    1052             :     if (!bEmulationAllowed)
    1053             :         return nullptr;
    1054             : 
    1055             :     std::string osTmpFilename = std::string(pszFilename).append("XXXXXX");
    1056             :     int fd = mkstemp(osTmpFilename.data());
    1057             :     if (fd < 0)
    1058             :     {
    1059             :         return VSIFilesystemHandler::CreateOnlyVisibleAtCloseTime(
    1060             :             pszFilename, bEmulationAllowed, papszOptions);
    1061             :     }
    1062             :     auto poHandle = std::make_unique<VSIUnixStdioHandle>(
    1063             :         this, fd, VSIUnixStdioHandle::AccessMode::NORMAL);
    1064             :     poHandle->m_osTmpFilename = std::move(osTmpFilename);
    1065             :     poHandle->m_osFilename = pszFilename;
    1066             :     return VSIVirtualHandleUniquePtr(poHandle.release());
    1067             : #endif
    1068             : }
    1069             : 
    1070             : /************************************************************************/
    1071             : /*                                Stat()                                */
    1072             : /************************************************************************/
    1073             : 
    1074      273480 : int VSIUnixStdioFilesystemHandler::Stat(const char *pszFilename,
    1075             :                                         VSIStatBufL *pStatBuf, int /* nFlags */)
    1076             : {
    1077      273480 :     return (VSI_STAT64(pszFilename, pStatBuf));
    1078             : }
    1079             : 
    1080             : /************************************************************************/
    1081             : /*                               Unlink()                               */
    1082             : /************************************************************************/
    1083             : 
    1084        5355 : int VSIUnixStdioFilesystemHandler::Unlink(const char *pszFilename)
    1085             : 
    1086             : {
    1087        5355 :     return unlink(pszFilename);
    1088             : }
    1089             : 
    1090             : /************************************************************************/
    1091             : /*                               Rename()                               */
    1092             : /************************************************************************/
    1093             : 
    1094        1143 : int VSIUnixStdioFilesystemHandler::Rename(const char *oldpath,
    1095             :                                           const char *newpath, GDALProgressFunc,
    1096             :                                           void *)
    1097             : 
    1098             : {
    1099        1143 :     return rename(oldpath, newpath);
    1100             : }
    1101             : 
    1102             : /************************************************************************/
    1103             : /*                               Mkdir()                                */
    1104             : /************************************************************************/
    1105             : 
    1106        2233 : int VSIUnixStdioFilesystemHandler::Mkdir(const char *pszPathname, long nMode)
    1107             : 
    1108             : {
    1109        2233 :     return mkdir(pszPathname, static_cast<int>(nMode));
    1110             : }
    1111             : 
    1112             : /************************************************************************/
    1113             : /*                               Rmdir()                                */
    1114             : /************************************************************************/
    1115             : 
    1116         107 : int VSIUnixStdioFilesystemHandler::Rmdir(const char *pszPathname)
    1117             : 
    1118             : {
    1119         107 :     return rmdir(pszPathname);
    1120             : }
    1121             : 
    1122             : /************************************************************************/
    1123             : /*                             ReadDirEx()                              */
    1124             : /************************************************************************/
    1125             : 
    1126       21847 : char **VSIUnixStdioFilesystemHandler::ReadDirEx(const char *pszPath,
    1127             :                                                 int nMaxFiles)
    1128             : 
    1129             : {
    1130       21847 :     if (strlen(pszPath) == 0)
    1131          17 :         pszPath = ".";
    1132             : 
    1133       43693 :     CPLStringList oDir;
    1134       21847 :     DIR *hDir = opendir(pszPath);
    1135       21847 :     if (hDir != nullptr)
    1136             :     {
    1137             :         // We want to avoid returning NULL for an empty list.
    1138       21777 :         oDir.Assign(static_cast<char **>(CPLCalloc(2, sizeof(char *))));
    1139             : 
    1140       21777 :         struct dirent *psDirEntry = nullptr;
    1141     2055070 :         while ((psDirEntry = readdir(hDir)) != nullptr)
    1142             :         {
    1143     2033310 :             oDir.AddString(psDirEntry->d_name);
    1144     2033300 :             if (nMaxFiles > 0 && oDir.Count() > nMaxFiles)
    1145           7 :                 break;
    1146             :         }
    1147             : 
    1148       21777 :         closedir(hDir);
    1149             :     }
    1150             :     else
    1151             :     {
    1152             :         // Should we generate an error?
    1153             :         // For now we'll just return NULL (at the end of the function).
    1154             :     }
    1155             : 
    1156       43693 :     return oDir.StealList();
    1157             : }
    1158             : 
    1159             : /************************************************************************/
    1160             : /*                          GetDiskFreeSpace()                          */
    1161             : /************************************************************************/
    1162             : 
    1163           1 : GIntBig VSIUnixStdioFilesystemHandler::GetDiskFreeSpace(const char *
    1164             : #ifdef HAVE_STATVFS
    1165             :                                                             pszDirname
    1166             : #endif
    1167             : )
    1168             : {
    1169           1 :     GIntBig nRet = -1;
    1170             : #ifdef HAVE_STATVFS
    1171             : 
    1172             : #ifdef HAVE_STATVFS64
    1173             :     struct statvfs64 buf;
    1174           1 :     if (statvfs64(pszDirname, &buf) == 0)
    1175             :     {
    1176           0 :         nRet = static_cast<GIntBig>(buf.f_frsize *
    1177           0 :                                     static_cast<GUIntBig>(buf.f_bavail));
    1178             :     }
    1179             : #else
    1180             :     struct statvfs buf;
    1181             :     if (statvfs(pszDirname, &buf) == 0)
    1182             :     {
    1183             :         nRet = static_cast<GIntBig>(buf.f_frsize *
    1184             :                                     static_cast<GUIntBig>(buf.f_bavail));
    1185             :     }
    1186             : #endif
    1187             : 
    1188             : #endif
    1189           1 :     return nRet;
    1190             : }
    1191             : 
    1192             : /************************************************************************/
    1193             : /*                        SupportsSparseFiles()                         */
    1194             : /************************************************************************/
    1195             : 
    1196             : #ifdef __linux
    1197             : #include <sys/vfs.h>
    1198             : #endif
    1199             : 
    1200           2 : int VSIUnixStdioFilesystemHandler::SupportsSparseFiles(const char *
    1201             : #ifdef __linux
    1202             :                                                            pszPath
    1203             : #endif
    1204             : )
    1205             : {
    1206             : #ifdef __linux
    1207             :     struct statfs sStatFS;
    1208           2 :     if (statfs(pszPath, &sStatFS) == 0)
    1209             :     {
    1210             :         // Add here any missing filesystem supporting sparse files.
    1211             :         // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
    1212           2 :         switch (static_cast<unsigned>(sStatFS.f_type))
    1213             :         {
    1214             :             // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
    1215           2 :             case 0xef53U:      // ext2, 3, 4
    1216             :             case 0x52654973U:  // reiser
    1217             :             case 0x58465342U:  // xfs
    1218             :             case 0x3153464aU:  // jfs
    1219             :             case 0x5346544eU:  // ntfs
    1220             :             case 0x9123683eU:  // brfs
    1221             :             // nfs: NFS < 4.2 supports creating sparse files (but reading them
    1222             :             // not efficiently).
    1223             :             case 0x6969U:
    1224             :             case 0x01021994U:  // tmpfs
    1225           2 :                 return TRUE;
    1226             : 
    1227           0 :             case 0x4d44U:  // msdos
    1228           0 :                 return FALSE;
    1229             : 
    1230           0 :             case 0x53464846U:  // Windows Subsystem for Linux fs
    1231             :             {
    1232             :                 static bool bUnknownFSEmitted = false;
    1233           0 :                 if (!bUnknownFSEmitted)
    1234             :                 {
    1235           0 :                     CPLDebug("VSI",
    1236             :                              "Windows Subsystem for Linux FS is at "
    1237             :                              "the time of writing not known to support sparse "
    1238             :                              "files");
    1239           0 :                     bUnknownFSEmitted = true;
    1240             :                 }
    1241           0 :                 return FALSE;
    1242             :             }
    1243             : 
    1244           0 :             default:
    1245             :             {
    1246             :                 static bool bUnknownFSEmitted = false;
    1247           0 :                 if (!bUnknownFSEmitted)
    1248             :                 {
    1249           0 :                     CPLDebug("VSI",
    1250             :                              "Filesystem with type %X unknown. "
    1251             :                              "Assuming it does not support sparse files",
    1252           0 :                              static_cast<int>(sStatFS.f_type));
    1253           0 :                     bUnknownFSEmitted = true;
    1254             :                 }
    1255           0 :                 return FALSE;
    1256             :             }
    1257             :         }
    1258             :     }
    1259           0 :     return FALSE;
    1260             : #else
    1261             :     static bool bMessageEmitted = false;
    1262             :     if (!bMessageEmitted)
    1263             :     {
    1264             :         CPLDebug("VSI", "Sorry: SupportsSparseFiles() not implemented "
    1265             :                         "for this operating system");
    1266             :         bMessageEmitted = true;
    1267             :     }
    1268             :     return FALSE;
    1269             : #endif
    1270             : }
    1271             : 
    1272             : /************************************************************************/
    1273             : /*                              IsLocal()                               */
    1274             : /************************************************************************/
    1275             : 
    1276        1687 : bool VSIUnixStdioFilesystemHandler::IsLocal(const char *
    1277             : #ifdef __linux
    1278             :                                                 pszPath
    1279             : #endif
    1280             : ) const
    1281             : {
    1282             : #ifdef __linux
    1283             :     struct statfs sStatFS;
    1284        1687 :     if (statfs(pszPath, &sStatFS) == 0)
    1285             :     {
    1286             :         // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
    1287         220 :         switch (static_cast<unsigned>(sStatFS.f_type))
    1288             :         {
    1289             :             // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
    1290           0 :             case 0x6969U:      // NFS
    1291             :             case 0x517bU:      // SMB
    1292             :             case 0xff534d42U:  // CIFS
    1293             :             case 0xfe534d42U:  // SMB2
    1294             :                 // (https://github.com/libuv/libuv/blob/97dcdb1926f6aca43171e1614338bcef067abd59/src/unix/fs.c#L960)
    1295           0 :                 return false;
    1296             :         }
    1297             :     }
    1298             : #else
    1299             :     static bool bMessageEmitted = false;
    1300             :     if (!bMessageEmitted)
    1301             :     {
    1302             :         CPLDebug("VSI", "Sorry: IsLocal() not implemented "
    1303             :                         "for this operating system");
    1304             :         bMessageEmitted = true;
    1305             :     }
    1306             : #endif
    1307        1687 :     return true;
    1308             : }
    1309             : 
    1310             : /************************************************************************/
    1311             : /*                      SupportsSequentialWrite()                       */
    1312             : /************************************************************************/
    1313             : 
    1314         169 : bool VSIUnixStdioFilesystemHandler::SupportsSequentialWrite(
    1315             :     const char *pszPath, bool /* bAllowLocalTempFile */)
    1316             : {
    1317             :     VSIStatBufL sStat;
    1318         169 :     if (VSIStatL(pszPath, &sStat) == 0)
    1319          62 :         return access(pszPath, W_OK) == 0;
    1320         107 :     return access(CPLGetDirnameSafe(pszPath).c_str(), W_OK) == 0;
    1321             : }
    1322             : 
    1323             : /************************************************************************/
    1324             : /*                        SupportsRandomWrite()                         */
    1325             : /************************************************************************/
    1326             : 
    1327         107 : bool VSIUnixStdioFilesystemHandler::SupportsRandomWrite(
    1328             :     const char *pszPath, bool /* bAllowLocalTempFile */)
    1329             : {
    1330         107 :     return SupportsSequentialWrite(pszPath, false);
    1331             : }
    1332             : 
    1333             : /************************************************************************/
    1334             : /*                           VSIDIRUnixStdio                            */
    1335             : /************************************************************************/
    1336             : 
    1337             : struct VSIDIRUnixStdio final : public VSIDIR
    1338             : {
    1339             :     struct DIRCloser
    1340             :     {
    1341        1121 :         void operator()(DIR *d)
    1342             :         {
    1343        1121 :             if (d)
    1344        1121 :                 closedir(d);
    1345        1121 :         }
    1346             :     };
    1347             : 
    1348             :     CPLString osRootPath{};
    1349             :     CPLString osBasePath{};
    1350             :     std::unique_ptr<DIR, DIRCloser> m_psDir{};
    1351             :     int nRecurseDepth = 0;
    1352             :     VSIDIREntry entry{};
    1353             :     std::vector<std::unique_ptr<VSIDIR>> aoStackSubDir{};
    1354             :     std::string m_osFilterPrefix{};
    1355             :     bool m_bNameAndTypeOnly = false;
    1356             : 
    1357             :     const VSIDIREntry *NextDirEntry() override;
    1358             : };
    1359             : 
    1360             : /************************************************************************/
    1361             : /*                          OpenDirInternal()                           */
    1362             : /************************************************************************/
    1363             : 
    1364             : /* static */
    1365        1146 : std::unique_ptr<VSIDIRUnixStdio> VSIUnixStdioFilesystemHandler::OpenDirInternal(
    1366             :     const char *pszPath, int nRecurseDepth, const char *const *papszOptions)
    1367             : {
    1368        2292 :     std::unique_ptr<DIR, VSIDIRUnixStdio::DIRCloser> psDir(opendir(pszPath));
    1369        1146 :     if (psDir == nullptr)
    1370             :     {
    1371          25 :         return nullptr;
    1372             :     }
    1373        2242 :     auto dir = std::make_unique<VSIDIRUnixStdio>();
    1374        1121 :     dir->osRootPath = pszPath;
    1375        1121 :     dir->nRecurseDepth = nRecurseDepth;
    1376        1121 :     dir->m_psDir = std::move(psDir);
    1377        1121 :     dir->m_osFilterPrefix = CSLFetchNameValueDef(papszOptions, "PREFIX", "");
    1378        1121 :     dir->m_bNameAndTypeOnly = CPLTestBool(
    1379             :         CSLFetchNameValueDef(papszOptions, "NAME_AND_TYPE_ONLY", "NO"));
    1380        1121 :     return dir;
    1381             : }
    1382             : 
    1383             : /************************************************************************/
    1384             : /*                              OpenDir()                               */
    1385             : /************************************************************************/
    1386             : 
    1387         393 : VSIDIR *VSIUnixStdioFilesystemHandler::OpenDir(const char *pszPath,
    1388             :                                                int nRecurseDepth,
    1389             :                                                const char *const *papszOptions)
    1390             : {
    1391         393 :     return OpenDirInternal(pszPath, nRecurseDepth, papszOptions).release();
    1392             : }
    1393             : 
    1394             : /************************************************************************/
    1395             : /*                            NextDirEntry()                            */
    1396             : /************************************************************************/
    1397             : 
    1398       26999 : const VSIDIREntry *VSIDIRUnixStdio::NextDirEntry()
    1399             : {
    1400       26999 : begin:
    1401       26999 :     if (VSI_ISDIR(entry.nMode) && nRecurseDepth != 0)
    1402             :     {
    1403        1506 :         CPLString osCurFile(osRootPath);
    1404         753 :         if (!osCurFile.empty())
    1405         753 :             osCurFile += '/';
    1406         753 :         osCurFile += entry.pszName;
    1407             :         auto subdir = VSIUnixStdioFilesystemHandler::OpenDirInternal(
    1408         753 :             osCurFile, nRecurseDepth - 1, nullptr);
    1409         753 :         if (subdir)
    1410             :         {
    1411         753 :             subdir->osRootPath = osRootPath;
    1412         753 :             subdir->osBasePath = entry.pszName;
    1413         753 :             subdir->m_osFilterPrefix = m_osFilterPrefix;
    1414         753 :             subdir->m_bNameAndTypeOnly = m_bNameAndTypeOnly;
    1415         753 :             aoStackSubDir.push_back(std::move(subdir));
    1416             :         }
    1417         753 :         entry.nMode = 0;
    1418             :     }
    1419             : 
    1420       27750 :     while (!aoStackSubDir.empty())
    1421             :     {
    1422       14219 :         auto l_entry = aoStackSubDir.back()->NextDirEntry();
    1423       14219 :         if (l_entry)
    1424             :         {
    1425       13468 :             return l_entry;
    1426             :         }
    1427         751 :         aoStackSubDir.pop_back();
    1428             :     }
    1429             : 
    1430       15760 :     while (const auto *psEntry = readdir(m_psDir.get()))
    1431             :     {
    1432             :         // Skip . and ..entries
    1433       14650 :         if (psEntry->d_name[0] == '.' &&
    1434        2242 :             (psEntry->d_name[1] == '\0' ||
    1435        1129 :              (psEntry->d_name[1] == '.' && psEntry->d_name[2] == '\0')))
    1436             :         {
    1437             :             // do nothing
    1438             :         }
    1439             :         else
    1440             :         {
    1441       12426 :             CPLFree(entry.pszName);
    1442       12426 :             CPLString osName(osBasePath);
    1443       12426 :             if (!osName.empty())
    1444        7943 :                 osName += '/';
    1445       12426 :             osName += psEntry->d_name;
    1446             : 
    1447       12426 :             entry.pszName = CPLStrdup(osName);
    1448       12426 :             entry.nMode = 0;
    1449       12426 :             entry.nSize = 0;
    1450       12426 :             entry.nMTime = 0;
    1451       12426 :             entry.bModeKnown = false;
    1452       12426 :             entry.bSizeKnown = false;
    1453       12426 :             entry.bMTimeKnown = false;
    1454             : 
    1455       12426 :             CPLString osCurFile(osRootPath);
    1456       12426 :             if (!osCurFile.empty())
    1457       12426 :                 osCurFile += '/';
    1458       12426 :             osCurFile += entry.pszName;
    1459             : 
    1460             : #if !defined(__sun) && !defined(__HAIKU__)
    1461       12426 :             if (psEntry->d_type == DT_REG)
    1462        9969 :                 entry.nMode = S_IFREG;
    1463        2457 :             else if (psEntry->d_type == DT_DIR)
    1464         893 :                 entry.nMode = S_IFDIR;
    1465        1564 :             else if (psEntry->d_type == DT_LNK)
    1466        1564 :                 entry.nMode = S_IFLNK;
    1467             : #endif
    1468             : 
    1469       19486 :             const auto StatFile = [&osCurFile, this]()
    1470             :             {
    1471             :                 VSIStatBufL sStatL;
    1472        9743 :                 if (VSIStatL(osCurFile, &sStatL) == 0)
    1473             :                 {
    1474        9743 :                     entry.nMode = sStatL.st_mode;
    1475        9743 :                     entry.nSize = sStatL.st_size;
    1476        9743 :                     entry.nMTime = sStatL.st_mtime;
    1477        9743 :                     entry.bModeKnown = true;
    1478        9743 :                     entry.bSizeKnown = true;
    1479        9743 :                     entry.bMTimeKnown = true;
    1480             :                 }
    1481        9743 :             };
    1482             : 
    1483       12438 :             if (!m_osFilterPrefix.empty() &&
    1484          12 :                 m_osFilterPrefix.size() > osName.size())
    1485             :             {
    1486           6 :                 if (STARTS_WITH(m_osFilterPrefix.c_str(), osName.c_str()) &&
    1487           2 :                     m_osFilterPrefix[osName.size()] == '/')
    1488             :                 {
    1489             : #if !defined(__sun) && !defined(__HAIKU__)
    1490           1 :                     if (psEntry->d_type == DT_UNKNOWN)
    1491             : #endif
    1492             :                     {
    1493           0 :                         StatFile();
    1494             :                     }
    1495           1 :                     if (VSI_ISDIR(entry.nMode))
    1496             :                     {
    1497           1 :                         goto begin;
    1498             :                     }
    1499             :                 }
    1500           3 :                 continue;
    1501             :             }
    1502       12430 :             if (!m_osFilterPrefix.empty() &&
    1503           8 :                 !STARTS_WITH(osName.c_str(), m_osFilterPrefix.c_str()))
    1504             :             {
    1505           2 :                 continue;
    1506             :             }
    1507             : 
    1508       12420 :             if (!m_bNameAndTypeOnly
    1509             : #if !defined(__sun) && !defined(__HAIKU__)
    1510        2677 :                 || psEntry->d_type == DT_UNKNOWN
    1511             : #endif
    1512             :             )
    1513             :             {
    1514        9743 :                 StatFile();
    1515             :             }
    1516             : 
    1517       12420 :             return &(entry);
    1518             :         }
    1519        2229 :     }
    1520             : 
    1521        1110 :     return nullptr;
    1522             : }
    1523             : 
    1524             : #ifdef VSI_COUNT_BYTES_READ
    1525             : /************************************************************************/
    1526             : /*                             AddToTotal()                             */
    1527             : /************************************************************************/
    1528             : 
    1529             : void VSIUnixStdioFilesystemHandler::AddToTotal(vsi_l_offset nBytes)
    1530             : {
    1531             :     CPLMutexHolder oHolder(&hMutex);
    1532             :     nTotalBytesRead += nBytes;
    1533             : }
    1534             : 
    1535             : #endif
    1536             : 
    1537             : /************************************************************************/
    1538             : /*                        GetCanonicalFilename()                        */
    1539             : /************************************************************************/
    1540             : 
    1541             : #ifdef HAS_CASE_INSENSITIVE_FILE_SYSTEM
    1542             : std::string VSIUnixStdioFilesystemHandler::GetCanonicalFilename(
    1543             :     const std::string &osFilename) const
    1544             : {
    1545             :     char szResolvedPath[PATH_MAX];
    1546             :     const char *pszFilename = osFilename.c_str();
    1547             :     if (realpath(pszFilename, szResolvedPath))
    1548             :     {
    1549             :         const char *pszFilenameLastPart = strrchr(pszFilename, '/');
    1550             :         const char *pszResolvedFilenameLastPart = strrchr(szResolvedPath, '/');
    1551             :         if (pszFilenameLastPart && pszResolvedFilenameLastPart &&
    1552             :             EQUAL(pszFilenameLastPart, pszResolvedFilenameLastPart))
    1553             :         {
    1554             :             std::string osRet;
    1555             :             osRet.assign(pszFilename, pszFilenameLastPart - pszFilename);
    1556             :             osRet += pszResolvedFilenameLastPart;
    1557             :             return osRet;
    1558             :         }
    1559             :         return szResolvedPath;
    1560             :     }
    1561             :     return osFilename;
    1562             : }
    1563             : #endif
    1564             : 
    1565             : /************************************************************************/
    1566             : /*                     VSIInstallLargeFileHandler()                     */
    1567             : /************************************************************************/
    1568             : 
    1569        2101 : void VSIInstallLargeFileHandler()
    1570             : 
    1571             : {
    1572        2101 :     VSIFileManager::InstallHandler(
    1573        4202 :         "", std::make_shared<VSIUnixStdioFilesystemHandler>());
    1574        2101 : }
    1575             : 
    1576             : #endif  // ndef WIN32
    1577             : 
    1578             : //! @endcond

Generated by: LCOV version 1.14