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: 293 319 91.8 %
Date: 2025-08-19 18:03:11 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 with fseek64()
       5             :  *           and ftell64() such as IRIX.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  **********************************************************************
       9             :  * Copyright (c) 2001, Frank Warmerdam
      10             :  * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************
      14             :  *
      15             :  * NB: Note that in wrappers we are always saving the error state (errno
      16             :  * variable) to avoid side effects during debug prints or other possible
      17             :  * standard function calls (error states will be overwritten after such
      18             :  * a call).
      19             :  *
      20             :  ****************************************************************************/
      21             : 
      22             : //! @cond Doxygen_Suppress
      23             : 
      24             : // #define VSI_COUNT_BYTES_READ
      25             : 
      26             : // Some unusual filesystems do not work if _FORTIFY_SOURCE in GCC or
      27             : // clang is used within this source file, especially if techniques
      28             : // like those in vsipreload are used.  Fortify source interacts poorly with
      29             : // filesystems that use fread for forward seeks.  This leads to SIGSEGV within
      30             : // fread calls.
      31             : //
      32             : // See this for hardening background info: https://wiki.debian.org/Hardening
      33             : #undef _FORTIFY_SOURCE
      34             : 
      35             : // 64 bit IO is only available on 32-bit android since API 24 / Android 7.0
      36             : // See
      37             : // https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md
      38             : #if defined(__ANDROID_API__) && __ANDROID_API__ >= 24
      39             : #define _FILE_OFFSET_BITS 64
      40             : #endif
      41             : 
      42             : #include "cpl_port.h"
      43             : 
      44             : #if !defined(_WIN32)
      45             : 
      46             : #include "cpl_vsi.h"
      47             : #include "cpl_vsi_virtual.h"
      48             : 
      49             : #include <cstddef>
      50             : #include <cstdio>
      51             : #include <cstring>
      52             : #include <dirent.h>
      53             : #include <errno.h>
      54             : #include <fcntl.h>
      55             : #include <sys/stat.h>
      56             : #ifdef HAVE_STATVFS
      57             : #include <sys/statvfs.h>
      58             : #endif
      59             : #include <sys/types.h>
      60             : #if HAVE_UNISTD_H
      61             : #include <unistd.h>
      62             : #endif
      63             : #ifdef HAVE_PREAD_BSD
      64             : #include <sys/uio.h>
      65             : #endif
      66             : 
      67             : #if defined(__MACH__) && defined(__APPLE__)
      68             : #define HAS_CASE_INSENSITIVE_FILE_SYSTEM
      69             : #include <stdio.h>
      70             : #include <stdlib.h>
      71             : #include <limits.h>
      72             : #endif
      73             : 
      74             : #include <limits>
      75             : #include <new>
      76             : 
      77             : #include "cpl_config.h"
      78             : #include "cpl_conv.h"
      79             : #include "cpl_error.h"
      80             : #include "cpl_multiproc.h"
      81             : #include "cpl_string.h"
      82             : #include "cpl_vsi_error.h"
      83             : 
      84             : #if defined(UNIX_STDIO_64)
      85             : 
      86             : #ifndef VSI_FTELL64
      87             : #define VSI_FTELL64 ftell64
      88             : #endif
      89             : #ifndef VSI_FSEEK64
      90             : #define VSI_FSEEK64 fseek64
      91             : #endif
      92             : #ifndef VSI_FOPEN64
      93             : #define VSI_FOPEN64 fopen64
      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_FTELL64
     108             : #define VSI_FTELL64 ftell
     109             : #endif
     110             : #ifndef VSI_FSEEK64
     111             : #define VSI_FSEEK64 fseek
     112             : #endif
     113             : #ifndef VSI_FOPEN64
     114             : #define VSI_FOPEN64 fopen
     115             : #endif
     116             : #ifndef VSI_STAT64
     117             : #define VSI_STAT64 stat
     118             : #endif
     119             : #ifndef VSI_STAT64_T
     120             : #define VSI_STAT64_T stat
     121             : #endif
     122             : #ifndef VSI_FTRUNCATE64
     123             : #define VSI_FTRUNCATE64 ftruncate
     124             : #endif
     125             : 
     126             : #endif /* ndef UNIX_STDIO_64 */
     127             : 
     128             : #ifndef BUILD_WITHOUT_64BIT_OFFSET
     129             : // Ensure we have working 64 bit API
     130             : static_assert(sizeof(VSI_FTELL64(stdout)) == sizeof(vsi_l_offset),
     131             :               "File API does not seem to support 64-bit offset. "
     132             :               "If you still want to build GDAL without > 4GB file support, "
     133             :               "add the -DBUILD_WITHOUT_64BIT_OFFSET define");
     134             : static_assert(sizeof(VSIStatBufL::st_size) == sizeof(vsi_l_offset),
     135             :               "File API does not seem to support 64-bit file size. "
     136             :               "If you still want to build GDAL without > 4GB file support, "
     137             :               "add the -DBUILD_WITHOUT_64BIT_OFFSET define");
     138             : #endif
     139             : 
     140             : /************************************************************************/
     141             : /* ==================================================================== */
     142             : /*                       VSIUnixStdioFilesystemHandler                  */
     143             : /* ==================================================================== */
     144             : /************************************************************************/
     145             : 
     146             : struct VSIDIRUnixStdio;
     147             : 
     148             : class VSIUnixStdioFilesystemHandler final : public VSIFilesystemHandler
     149             : {
     150             :     CPL_DISALLOW_COPY_ASSIGN(VSIUnixStdioFilesystemHandler)
     151             : 
     152             : #ifdef VSI_COUNT_BYTES_READ
     153             :     vsi_l_offset nTotalBytesRead = 0;
     154             :     CPLMutex *hMutex = nullptr;
     155             : #endif
     156             : 
     157             :   public:
     158        1694 :     VSIUnixStdioFilesystemHandler() = default;
     159             : #ifdef VSI_COUNT_BYTES_READ
     160             :     ~VSIUnixStdioFilesystemHandler() override;
     161             : #endif
     162             : 
     163             :     VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
     164             :                            bool bSetError,
     165             :                            CSLConstList /* papszOptions */) override;
     166             : 
     167             :     VSIVirtualHandle *
     168             :     CreateOnlyVisibleAtCloseTime(const char *pszFilename,
     169             :                                  bool bEmulationAllowed,
     170             :                                  CSLConstList papszOptions) override;
     171             : 
     172             :     int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
     173             :              int nFlags) override;
     174             :     int Unlink(const char *pszFilename) override;
     175             :     int Rename(const char *oldpath, const char *newpath, GDALProgressFunc,
     176             :                void *) override;
     177             :     int Mkdir(const char *pszDirname, long nMode) override;
     178             :     int Rmdir(const char *pszDirname) override;
     179             :     char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
     180             :     GIntBig GetDiskFreeSpace(const char *pszDirname) override;
     181             :     int SupportsSparseFiles(const char *pszPath) override;
     182             : 
     183             :     bool IsLocal(const char *pszPath) override;
     184             :     bool SupportsSequentialWrite(const char *pszPath,
     185             :                                  bool /* bAllowLocalTempFile */) override;
     186             :     bool SupportsRandomWrite(const char *pszPath,
     187             :                              bool /* bAllowLocalTempFile */) override;
     188             : 
     189             :     VSIDIR *OpenDir(const char *pszPath, int nRecurseDepth,
     190             :                     const char *const *papszOptions) override;
     191             : 
     192             :     static std::unique_ptr<VSIDIRUnixStdio>
     193             :     OpenDirInternal(const char *pszPath, int nRecurseDepth,
     194             :                     const char *const *papszOptions);
     195             : 
     196             : #ifdef HAS_CASE_INSENSITIVE_FILE_SYSTEM
     197             :     std::string
     198             :     GetCanonicalFilename(const std::string &osFilename) const override;
     199             : #endif
     200             : 
     201             : #ifdef VSI_COUNT_BYTES_READ
     202             :     void AddToTotal(vsi_l_offset nBytes);
     203             : #endif
     204             : };
     205             : 
     206             : /************************************************************************/
     207             : /* ==================================================================== */
     208             : /*                        VSIUnixStdioHandle                            */
     209             : /* ==================================================================== */
     210             : /************************************************************************/
     211             : 
     212             : class VSIUnixStdioHandle final : public VSIVirtualHandle
     213             : {
     214             :     friend class VSIUnixStdioFilesystemHandler;
     215             :     CPL_DISALLOW_COPY_ASSIGN(VSIUnixStdioHandle)
     216             : 
     217             :     FILE *fp = nullptr;
     218             :     vsi_l_offset m_nOffset = 0;
     219             :     bool bReadOnly = true;
     220             :     bool bLastOpWrite = false;
     221             :     bool bLastOpRead = false;
     222             :     bool bAtEOF = false;
     223             :     bool bError = false;
     224             :     // In a+ mode, disable any optimization since the behavior of the file
     225             :     // pointer on Mac and other BSD system is to have a seek() to the end of
     226             :     // file and thus a call to our Seek(0, SEEK_SET) before a read will be a
     227             :     // no-op.
     228             :     bool bModeAppendReadWrite = false;
     229             : #ifdef VSI_COUNT_BYTES_READ
     230             :     vsi_l_offset nTotalBytesRead = 0;
     231             :     VSIUnixStdioFilesystemHandler *poFS = nullptr;
     232             : #endif
     233             : 
     234             :     bool m_bCancelCreation = false;
     235             :     std::string m_osFilenameToSetAtCloseTime{};
     236             : #if !defined(__linux)
     237             :     std::string m_osTmpFilename{};
     238             : #endif
     239             : 
     240             :   public:
     241             :     VSIUnixStdioHandle(VSIUnixStdioFilesystemHandler *poFSIn, FILE *fpIn,
     242             :                        bool bReadOnlyIn, bool bModeAppendReadWriteIn);
     243             :     ~VSIUnixStdioHandle() override;
     244             : 
     245             :     int Seek(vsi_l_offset nOffsetIn, int nWhence) override;
     246             :     vsi_l_offset Tell() override;
     247             :     size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
     248             :     size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
     249             :     void ClearErr() override;
     250             :     int Eof() override;
     251             :     int Error() override;
     252             :     int Flush() override;
     253             :     int Close() override;
     254             :     int Truncate(vsi_l_offset nNewSize) override;
     255             : 
     256          55 :     void *GetNativeFileDescriptor() override
     257             :     {
     258          55 :         return reinterpret_cast<void *>(static_cast<uintptr_t>(fileno(fp)));
     259             :     }
     260             : 
     261             :     VSIRangeStatus GetRangeStatus(vsi_l_offset nOffset,
     262             :                                   vsi_l_offset nLength) override;
     263             : #if defined(HAVE_PREAD64) || (defined(HAVE_PREAD_BSD) && SIZEOF_OFF_T == 8)
     264             :     bool HasPRead() const override;
     265             :     size_t PRead(void * /*pBuffer*/, size_t /* nSize */,
     266             :                  vsi_l_offset /*nOffset*/) const override;
     267             : #endif
     268             : 
     269         443 :     void CancelCreation() override
     270             :     {
     271         443 :         m_bCancelCreation = true;
     272         443 :     }
     273             : };
     274             : 
     275             : /************************************************************************/
     276             : /*                       VSIUnixStdioHandle()                           */
     277             : /************************************************************************/
     278             : 
     279      124840 : VSIUnixStdioHandle::VSIUnixStdioHandle(
     280             : #ifndef VSI_COUNT_BYTES_READ
     281             :     CPL_UNUSED
     282             : #endif
     283             :         VSIUnixStdioFilesystemHandler *poFSIn,
     284      124840 :     FILE *fpIn, bool bReadOnlyIn, bool bModeAppendReadWriteIn)
     285             :     : fp(fpIn), bReadOnly(bReadOnlyIn),
     286      124840 :       bModeAppendReadWrite(bModeAppendReadWriteIn)
     287             : #ifdef VSI_COUNT_BYTES_READ
     288             :       ,
     289             :       poFS(poFSIn)
     290             : #endif
     291             : {
     292      124653 : }
     293             : 
     294             : /************************************************************************/
     295             : /*                         ~VSIUnixStdioHandle()                        */
     296             : /************************************************************************/
     297             : 
     298      248113 : VSIUnixStdioHandle::~VSIUnixStdioHandle()
     299             : {
     300      124102 :     VSIUnixStdioHandle::Close();
     301      248253 : }
     302             : 
     303             : /************************************************************************/
     304             : /*                               Close()                                */
     305             : /************************************************************************/
     306             : 
     307      252510 : int VSIUnixStdioHandle::Close()
     308             : 
     309             : {
     310      252510 :     if (!fp)
     311      127685 :         return 0;
     312             : 
     313             :     VSIDebug1("VSIUnixStdioHandle::Close(%p)", fp);
     314             : 
     315             : #ifdef VSI_COUNT_BYTES_READ
     316             :     poFS->AddToTotal(nTotalBytesRead);
     317             : #endif
     318             : 
     319      124825 :     int ret = 0;
     320             : 
     321             : #ifdef __linux
     322      124825 :     if (!m_bCancelCreation && !m_osFilenameToSetAtCloseTime.empty())
     323             :     {
     324           1 :         ret = fflush(fp);
     325           1 :         if (ret == 0)
     326             :         {
     327             :             // As advised by "man 2 open" if the caller doesn't have the
     328             :             // CAP_DAC_READ_SEARCH capability, which seems to be the default
     329             : 
     330             :             char szPath[32];
     331           1 :             const int fd = fileno(fp);
     332           1 :             snprintf(szPath, sizeof(szPath), "/proc/self/fd/%d", fd);
     333             :             ret =
     334           1 :                 linkat(AT_FDCWD, szPath, AT_FDCWD,
     335             :                        m_osFilenameToSetAtCloseTime.c_str(), AT_SYMLINK_FOLLOW);
     336           1 :             if (ret != 0)
     337           0 :                 CPLDebug("CPL", "linkat() failed with errno=%d", errno);
     338             :         }
     339             :     }
     340             : #endif
     341             : 
     342      125420 :     int ret2 = fclose(fp);
     343      123990 :     if (ret == 0 && ret2 != 0)
     344           0 :         ret = ret2;
     345             : 
     346             : #if !defined(__linux)
     347             :     if (!m_osFilenameToSetAtCloseTime.empty())
     348             :     {
     349             :         if (m_bCancelCreation)
     350             :         {
     351             :             ret = unlink(m_osFilenameToSetAtCloseTime.c_str());
     352             :         }
     353             :         else
     354             :         {
     355             :             ret = rename(m_osTmpFilename.c_str(),
     356             :                          m_osFilenameToSetAtCloseTime.c_str());
     357             :         }
     358             :     }
     359             : #endif
     360             : 
     361      123990 :     fp = nullptr;
     362      123990 :     return ret;
     363             : }
     364             : 
     365             : /************************************************************************/
     366             : /*                                Seek()                                */
     367             : /************************************************************************/
     368             : 
     369     3367760 : int VSIUnixStdioHandle::Seek(vsi_l_offset nOffsetIn, int nWhence)
     370             : {
     371     3367760 :     bAtEOF = false;
     372             : 
     373             :     // Seeks that do nothing are still surprisingly expensive with MSVCRT.
     374             :     // try and short circuit if possible.
     375     3367760 :     if (!bModeAppendReadWrite && nWhence == SEEK_SET && nOffsetIn == m_nOffset)
     376      713564 :         return 0;
     377             : 
     378             :     // On a read-only file, we can avoid a lseek() system call to be issued
     379             :     // if the next position to seek to is within the buffered page.
     380     2654200 :     if (bReadOnly && nWhence == SEEK_SET)
     381             :     {
     382     1855000 :         const int l_PAGE_SIZE = 4096;
     383     1855000 :         if (nOffsetIn > m_nOffset && nOffsetIn < l_PAGE_SIZE + m_nOffset)
     384             :         {
     385       64175 :             const int nDiff = static_cast<int>(nOffsetIn - m_nOffset);
     386             :             // Do not zero-initialize the buffer. We don't read from it
     387             :             GByte abyTemp[l_PAGE_SIZE];
     388       64175 :             const int nRead = static_cast<int>(fread(abyTemp, 1, nDiff, fp));
     389       64208 :             if (nRead == nDiff)
     390             :             {
     391       64163 :                 m_nOffset = nOffsetIn;
     392       64163 :                 bLastOpWrite = false;
     393       64163 :                 bLastOpRead = false;
     394       64163 :                 return 0;
     395             :             }
     396             :         }
     397             :     }
     398             : 
     399             : #if !defined(UNIX_STDIO_64) && SIZEOF_UNSIGNED_LONG == 4
     400             :     if (nOffsetIn > static_cast<vsi_l_offset>(std::numeric_limits<long>::max()))
     401             :     {
     402             :         CPLError(
     403             :             CE_Failure, CPLE_AppDefined,
     404             :             "Attempt at seeking beyond long extent. Lack of 64-bit file I/O");
     405             :         return -1;
     406             :     }
     407             : #endif
     408             : 
     409     2590070 :     const int nResult = VSI_FSEEK64(fp, nOffsetIn, nWhence);
     410     2589320 :     const int nError = errno;
     411             : 
     412             : #ifdef VSI_DEBUG
     413             : 
     414             :     if (nWhence == SEEK_SET)
     415             :     {
     416             :         VSIDebug3("VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB
     417             :                   ",SEEK_SET) = %d",
     418             :                   fp, nOffsetIn, nResult);
     419             :     }
     420             :     else if (nWhence == SEEK_END)
     421             :     {
     422             :         VSIDebug3("VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB
     423             :                   ",SEEK_END) = %d",
     424             :                   fp, nOffsetIn, nResult);
     425             :     }
     426             :     else if (nWhence == SEEK_CUR)
     427             :     {
     428             :         VSIDebug3("VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB
     429             :                   ",SEEK_CUR) = %d",
     430             :                   fp, nOffsetIn, nResult);
     431             :     }
     432             :     else
     433             :     {
     434             :         VSIDebug4("VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB
     435             :                   ",%d-Unknown) = %d",
     436             :                   fp, nOffsetIn, nWhence, nResult);
     437             :     }
     438             : 
     439             : #endif
     440             : 
     441     2589320 :     if (nResult != -1)
     442             :     {
     443     2589280 :         if (nWhence == SEEK_SET)
     444             :         {
     445     1992630 :             m_nOffset = nOffsetIn;
     446             :         }
     447      596651 :         else if (nWhence == SEEK_END)
     448             :         {
     449      516752 :             m_nOffset = VSI_FTELL64(fp);
     450             :         }
     451       79899 :         else if (nWhence == SEEK_CUR)
     452             :         {
     453             :             if (nOffsetIn > INT_MAX)
     454             :             {
     455             :                 // printf("likely negative offset intended\n");
     456             :             }
     457       79804 :             m_nOffset += nOffsetIn;
     458             :         }
     459             :     }
     460             : 
     461     2589560 :     bLastOpWrite = false;
     462     2589560 :     bLastOpRead = false;
     463             : 
     464     2589560 :     errno = nError;
     465     2589560 :     return nResult;
     466             : }
     467             : 
     468             : /************************************************************************/
     469             : /*                                Tell()                                */
     470             : /************************************************************************/
     471             : 
     472     2322850 : vsi_l_offset VSIUnixStdioHandle::Tell()
     473             : 
     474             : {
     475             : #if 0
     476             :     const vsi_l_offset nOffset = VSI_FTELL64( fp );
     477             :     const int nError = errno;
     478             : 
     479             :     VSIDebug2( "VSIUnixStdioHandle::Tell(%p) = %ld",
     480             :                fp, static_cast<long>(nOffset) );
     481             : 
     482             :     errno = nError;
     483             : #endif
     484             : 
     485     2322850 :     return m_nOffset;
     486             : }
     487             : 
     488             : /************************************************************************/
     489             : /*                               Flush()                                */
     490             : /************************************************************************/
     491             : 
     492       36104 : int VSIUnixStdioHandle::Flush()
     493             : 
     494             : {
     495             :     VSIDebug1("VSIUnixStdioHandle::Flush(%p)", fp);
     496             : 
     497       36104 :     return fflush(fp);
     498             : }
     499             : 
     500             : /************************************************************************/
     501             : /*                                Read()                                */
     502             : /************************************************************************/
     503             : 
     504     7801470 : size_t VSIUnixStdioHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
     505             : 
     506             : {
     507             :     /* -------------------------------------------------------------------- */
     508             :     /*      If a fwrite() is followed by an fread(), the POSIX rules are    */
     509             :     /*      that some of the write may still be buffered and lost.  We      */
     510             :     /*      are required to do a seek between to force flushing.   So we    */
     511             :     /*      keep careful track of what happened last to know if we          */
     512             :     /*      skipped a flushing seek that we may need to do now.             */
     513             :     /* -------------------------------------------------------------------- */
     514     7801470 :     if (!bModeAppendReadWrite && bLastOpWrite)
     515             :     {
     516        2851 :         if (VSI_FSEEK64(fp, m_nOffset, SEEK_SET) != 0)
     517             :         {
     518             :             VSIDebug1("Write calling seek failed. %d", m_nOffset);
     519             :         }
     520             :     }
     521             : 
     522             :     /* -------------------------------------------------------------------- */
     523             :     /*      Perform the read.                                               */
     524             :     /* -------------------------------------------------------------------- */
     525     7801470 :     const size_t nResult = fread(pBuffer, nSize, nCount, fp);
     526             : 
     527             : #ifdef VSI_DEBUG
     528             :     const int nError = errno;
     529             :     VSIDebug4("VSIUnixStdioHandle::Read(%p,%ld,%ld) = %ld", fp,
     530             :               static_cast<long>(nSize), static_cast<long>(nCount),
     531             :               static_cast<long>(nResult));
     532             :     errno = nError;
     533             : #endif
     534             : 
     535             :     /* -------------------------------------------------------------------- */
     536             :     /*      Update current offset.                                          */
     537             :     /* -------------------------------------------------------------------- */
     538             : 
     539             : #ifdef VSI_COUNT_BYTES_READ
     540             :     nTotalBytesRead += nSize * nResult;
     541             : #endif
     542             : 
     543     7801210 :     m_nOffset += nSize * nResult;
     544     7801210 :     bLastOpWrite = false;
     545     7801210 :     bLastOpRead = true;
     546             : 
     547     7801210 :     if (nResult != nCount)
     548             :     {
     549       88283 :         if (ferror(fp))
     550        1037 :             bError = true;
     551             :         else
     552             :         {
     553       87243 :             CPLAssert(feof(fp));
     554       87243 :             bAtEOF = true;
     555             :         }
     556             : 
     557       88280 :         errno = 0;
     558       88280 :         vsi_l_offset nNewOffset = VSI_FTELL64(fp);
     559       88281 :         if (errno == 0)  // ftell() can fail if we are end of file with a pipe.
     560       88280 :             m_nOffset = nNewOffset;
     561             :         else
     562           1 :             CPLDebug("VSI", "%s", VSIStrerror(errno));
     563             :     }
     564             : 
     565     7800960 :     return nResult;
     566             : }
     567             : 
     568             : /************************************************************************/
     569             : /*                               Write()                                */
     570             : /************************************************************************/
     571             : 
     572     2393240 : size_t VSIUnixStdioHandle::Write(const void *pBuffer, size_t nSize,
     573             :                                  size_t nCount)
     574             : 
     575             : {
     576             :     /* -------------------------------------------------------------------- */
     577             :     /*      If a fwrite() is followed by an fread(), the POSIX rules are    */
     578             :     /*      that some of the write may still be buffered and lost.  We      */
     579             :     /*      are required to do a seek between to force flushing.   So we    */
     580             :     /*      keep careful track of what happened last to know if we          */
     581             :     /*      skipped a flushing seek that we may need to do now.             */
     582             :     /* -------------------------------------------------------------------- */
     583     2393240 :     if (!bModeAppendReadWrite && bLastOpRead)
     584             :     {
     585        1850 :         if (VSI_FSEEK64(fp, m_nOffset, SEEK_SET) != 0)
     586             :         {
     587             :             VSIDebug1("Write calling seek failed. %d", m_nOffset);
     588             :         }
     589             :     }
     590             : 
     591             :     /* -------------------------------------------------------------------- */
     592             :     /*      Perform the write.                                              */
     593             :     /* -------------------------------------------------------------------- */
     594     2393240 :     const size_t nResult = fwrite(pBuffer, nSize, nCount, fp);
     595             : 
     596             : #if VSI_DEBUG
     597             :     const int nError = errno;
     598             : 
     599             :     VSIDebug4("VSIUnixStdioHandle::Write(%p,%ld,%ld) = %ld", fp,
     600             :               static_cast<long>(nSize), static_cast<long>(nCount),
     601             :               static_cast<long>(nResult));
     602             : 
     603             :     errno = nError;
     604             : #endif
     605             : 
     606             :     /* -------------------------------------------------------------------- */
     607             :     /*      Update current offset.                                          */
     608             :     /* -------------------------------------------------------------------- */
     609     2393240 :     m_nOffset += nSize * nResult;
     610     2393240 :     bLastOpWrite = true;
     611     2393240 :     bLastOpRead = false;
     612             : 
     613     2393240 :     return nResult;
     614             : }
     615             : 
     616             : /************************************************************************/
     617             : /*                             ClearErr()                               */
     618             : /************************************************************************/
     619             : 
     620        5431 : void VSIUnixStdioHandle::ClearErr()
     621             : 
     622             : {
     623        5431 :     clearerr(fp);
     624        5431 :     bAtEOF = false;
     625        5431 :     bError = false;
     626        5431 : }
     627             : 
     628             : /************************************************************************/
     629             : /*                              Error()                                 */
     630             : /************************************************************************/
     631             : 
     632       58851 : int VSIUnixStdioHandle::Error()
     633             : 
     634             : {
     635       58851 :     return bError ? TRUE : FALSE;
     636             : }
     637             : 
     638             : /************************************************************************/
     639             : /*                                Eof()                                 */
     640             : /************************************************************************/
     641             : 
     642      153882 : int VSIUnixStdioHandle::Eof()
     643             : 
     644             : {
     645      153882 :     return bAtEOF ? TRUE : FALSE;
     646             : }
     647             : 
     648             : /************************************************************************/
     649             : /*                             Truncate()                               */
     650             : /************************************************************************/
     651             : 
     652         546 : int VSIUnixStdioHandle::Truncate(vsi_l_offset nNewSize)
     653             : {
     654         546 :     fflush(fp);
     655         546 :     return VSI_FTRUNCATE64(fileno(fp), nNewSize);
     656             : }
     657             : 
     658             : /************************************************************************/
     659             : /*                          GetRangeStatus()                            */
     660             : /************************************************************************/
     661             : 
     662             : #ifdef __linux
     663             : #if !defined(MISSING_LINUX_FS_H)
     664             : #include <linux/fs.h>  // FS_IOC_FIEMAP
     665             : #endif
     666             : #ifdef FS_IOC_FIEMAP
     667             : #include <linux/types.h>   // for types used in linux/fiemap.h
     668             : #include <linux/fiemap.h>  // struct fiemap
     669             : #endif
     670             : #include <sys/ioctl.h>
     671             : #include <errno.h>
     672             : #endif
     673             : 
     674         506 : VSIRangeStatus VSIUnixStdioHandle::GetRangeStatus(vsi_l_offset
     675             : #ifdef FS_IOC_FIEMAP
     676             :                                                       nOffset
     677             : #endif
     678             :                                                   ,
     679             :                                                   vsi_l_offset
     680             : #ifdef FS_IOC_FIEMAP
     681             :                                                       nLength
     682             : #endif
     683             : )
     684             : {
     685             : #ifdef FS_IOC_FIEMAP
     686             :     // fiemap IOCTL documented at
     687             :     // https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt
     688             : 
     689             :     // The fiemap struct contains a "variable length" array at its end
     690             :     // As we are interested in only one extent, we allocate the base size of
     691             :     // fiemap + one fiemap_extent.
     692             :     GByte abyBuffer[sizeof(struct fiemap) + sizeof(struct fiemap_extent)];
     693         506 :     int fd = fileno(fp);
     694         506 :     struct fiemap *psExtentMap = reinterpret_cast<struct fiemap *>(&abyBuffer);
     695         506 :     memset(psExtentMap, 0,
     696             :            sizeof(struct fiemap) + sizeof(struct fiemap_extent));
     697         506 :     psExtentMap->fm_start = nOffset;
     698         506 :     psExtentMap->fm_length = nLength;
     699         506 :     psExtentMap->fm_extent_count = 1;
     700         506 :     int ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
     701         506 :     if (ret < 0)
     702           0 :         return VSI_RANGE_STATUS_UNKNOWN;
     703         506 :     if (psExtentMap->fm_mapped_extents == 0)
     704           2 :         return VSI_RANGE_STATUS_HOLE;
     705             :     // In case there is one extent with unknown status, retry after having
     706             :     // asked the kernel to sync the file.
     707         504 :     const fiemap_extent *pasExtent = &(psExtentMap->fm_extents[0]);
     708         504 :     if (psExtentMap->fm_mapped_extents == 1 &&
     709         504 :         (pasExtent[0].fe_flags & FIEMAP_EXTENT_UNKNOWN) != 0)
     710             :     {
     711          20 :         psExtentMap->fm_flags = FIEMAP_FLAG_SYNC;
     712          20 :         psExtentMap->fm_start = nOffset;
     713          20 :         psExtentMap->fm_length = nLength;
     714          20 :         psExtentMap->fm_extent_count = 1;
     715          20 :         ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
     716          20 :         if (ret < 0)
     717           0 :             return VSI_RANGE_STATUS_UNKNOWN;
     718          20 :         if (psExtentMap->fm_mapped_extents == 0)
     719           0 :             return VSI_RANGE_STATUS_HOLE;
     720             :     }
     721         504 :     return VSI_RANGE_STATUS_DATA;
     722             : #else
     723             :     static bool bMessageEmitted = false;
     724             :     if (!bMessageEmitted)
     725             :     {
     726             :         CPLDebug("VSI", "Sorry: GetExtentStatus() not implemented for "
     727             :                         "this operating system");
     728             :         bMessageEmitted = true;
     729             :     }
     730             :     return VSI_RANGE_STATUS_UNKNOWN;
     731             : #endif
     732             : }
     733             : 
     734             : /************************************************************************/
     735             : /*                             HasPRead()                               */
     736             : /************************************************************************/
     737             : 
     738             : #if defined(HAVE_PREAD64) || (defined(HAVE_PREAD_BSD) && SIZEOF_OFF_T == 8)
     739         724 : bool VSIUnixStdioHandle::HasPRead() const
     740             : {
     741         724 :     return true;
     742             : }
     743             : 
     744             : /************************************************************************/
     745             : /*                              PRead()                                 */
     746             : /************************************************************************/
     747             : 
     748       24285 : size_t VSIUnixStdioHandle::PRead(void *pBuffer, size_t nSize,
     749             :                                  vsi_l_offset nOffset) const
     750             : {
     751             : #ifdef HAVE_PREAD64
     752       24285 :     return pread64(fileno(fp), pBuffer, nSize, nOffset);
     753             : #else
     754             :     return pread(fileno(fp), pBuffer, nSize, static_cast<off_t>(nOffset));
     755             : #endif
     756             : }
     757             : #endif
     758             : 
     759             : /************************************************************************/
     760             : /* ==================================================================== */
     761             : /*                       VSIUnixStdioFilesystemHandler                  */
     762             : /* ==================================================================== */
     763             : /************************************************************************/
     764             : 
     765             : #ifdef VSI_COUNT_BYTES_READ
     766             : /************************************************************************/
     767             : /*                     ~VSIUnixStdioFilesystemHandler()                 */
     768             : /************************************************************************/
     769             : 
     770             : VSIUnixStdioFilesystemHandler::~VSIUnixStdioFilesystemHandler()
     771             : {
     772             :     CPLDebug(
     773             :         "VSI",
     774             :         "~VSIUnixStdioFilesystemHandler() : nTotalBytesRead = " CPL_FRMT_GUIB,
     775             :         nTotalBytesRead);
     776             : 
     777             :     if (hMutex != nullptr)
     778             :         CPLDestroyMutex(hMutex);
     779             :     hMutex = nullptr;
     780             : }
     781             : #endif
     782             : 
     783             : /************************************************************************/
     784             : /*                                Open()                                */
     785             : /************************************************************************/
     786             : 
     787             : VSIVirtualHandle *
     788      185243 : VSIUnixStdioFilesystemHandler::Open(const char *pszFilename,
     789             :                                     const char *pszAccess, bool bSetError,
     790             :                                     CSLConstList /* papszOptions */)
     791             : 
     792             : {
     793      185243 :     FILE *fp = VSI_FOPEN64(pszFilename, pszAccess);
     794      184378 :     const int nError = errno;
     795             : 
     796             :     VSIDebug3("VSIUnixStdioFilesystemHandler::Open(\"%s\",\"%s\") = %p",
     797             :               pszFilename, pszAccess, fp);
     798             : 
     799      184378 :     if (fp == nullptr)
     800             :     {
     801       59905 :         if (bSetError)
     802             :         {
     803       13976 :             VSIError(VSIE_FileError, "%s: %s", pszFilename, strerror(nError));
     804             :         }
     805       60207 :         errno = nError;
     806       60207 :         return nullptr;
     807             :     }
     808             : 
     809      124473 :     const bool bReadOnly =
     810      124473 :         strcmp(pszAccess, "rb") == 0 || strcmp(pszAccess, "r") == 0;
     811      124473 :     const bool bModeAppendReadWrite =
     812      124473 :         strcmp(pszAccess, "a+b") == 0 || strcmp(pszAccess, "a+") == 0;
     813             :     VSIUnixStdioHandle *poHandle = new (std::nothrow)
     814      124473 :         VSIUnixStdioHandle(this, fp, bReadOnly, bModeAppendReadWrite);
     815      124565 :     if (poHandle == nullptr)
     816             :     {
     817           0 :         fclose(fp);
     818           0 :         return nullptr;
     819             :     }
     820             : 
     821      124565 :     errno = nError;
     822             : 
     823             :     /* -------------------------------------------------------------------- */
     824             :     /*      If VSI_CACHE is set we want to use a cached reader instead      */
     825             :     /*      of more direct io on the underlying file.                       */
     826             :     /* -------------------------------------------------------------------- */
     827      124565 :     if (bReadOnly && CPLTestBool(CPLGetConfigOption("VSI_CACHE", "FALSE")))
     828             :     {
     829           5 :         return VSICreateCachedFile(poHandle);
     830             :     }
     831             : 
     832      124989 :     return poHandle;
     833             : }
     834             : 
     835             : /************************************************************************/
     836             : /*                      CreateOnlyVisibleAtCloseTime()                  */
     837             : /************************************************************************/
     838             : 
     839          91 : VSIVirtualHandle *VSIUnixStdioFilesystemHandler::CreateOnlyVisibleAtCloseTime(
     840             :     const char *pszFilename, bool bEmulationAllowed, CSLConstList papszOptions)
     841             : {
     842             : #ifdef __linux
     843          20 :     static bool bIsLinkatSupported = []()
     844             :     {
     845             :         // Check that /proc is accessible, since we will need it to run linkat()
     846             :         struct stat statbuf;
     847          20 :         return stat("/proc/self/fd", &statbuf) == 0;
     848          91 :     }();
     849             : 
     850             :     const int fd = bIsLinkatSupported
     851         182 :                        ? open(CPLGetDirnameSafe(pszFilename).c_str(),
     852             :                               O_TMPFILE | O_RDWR, 0666)
     853          91 :                        : -1;
     854          91 :     if (fd < 0)
     855          89 :         return VSIFilesystemHandler::CreateOnlyVisibleAtCloseTime(
     856          89 :             pszFilename, bEmulationAllowed, papszOptions);
     857             : 
     858           2 :     FILE *fp = fdopen(fd, "wb+");
     859           2 :     if (!fp)
     860             :     {
     861           0 :         close(fd);
     862           0 :         return nullptr;
     863             :     }
     864             : 
     865             :     VSIUnixStdioHandle *poHandle = new (std::nothrow) VSIUnixStdioHandle(
     866           2 :         this, fp, /* bReadOnly = */ false, /* bModeAppendReadWrite = */ false);
     867           2 :     if (poHandle)
     868             :     {
     869           2 :         poHandle->m_osFilenameToSetAtCloseTime = pszFilename;
     870             :     }
     871           2 :     return poHandle;
     872             : #else
     873             :     if (!bEmulationAllowed)
     874             :         return nullptr;
     875             : 
     876             :     std::string osTmpFilename = std::string(pszFilename).append("XXXXXX");
     877             :     int fd = mkstemp(osTmpFilename.data());
     878             :     if (fd < 0)
     879             :     {
     880             :         return VSIFilesystemHandler::CreateOnlyVisibleAtCloseTime(
     881             :             pszFilename, bEmulationAllowed, papszOptions);
     882             :     }
     883             : 
     884             :     FILE *fp = fdopen(fd, "wb+");
     885             :     if (!fp)
     886             :     {
     887             :         close(fd);
     888             :         return nullptr;
     889             :     }
     890             : 
     891             :     VSIUnixStdioHandle *poHandle = new (std::nothrow) VSIUnixStdioHandle(
     892             :         this, fp, /* bReadOnly = */ false, /* bModeAppendReadWrite = */ false);
     893             :     if (poHandle)
     894             :     {
     895             :         poHandle->m_osTmpFilename = std::move(osTmpFilename);
     896             :         poHandle->m_osFilenameToSetAtCloseTime = pszFilename;
     897             :     }
     898             :     return poHandle;
     899             : #endif
     900             : }
     901             : 
     902             : /************************************************************************/
     903             : /*                                Stat()                                */
     904             : /************************************************************************/
     905             : 
     906      231723 : int VSIUnixStdioFilesystemHandler::Stat(const char *pszFilename,
     907             :                                         VSIStatBufL *pStatBuf, int /* nFlags */)
     908             : {
     909      231723 :     return (VSI_STAT64(pszFilename, pStatBuf));
     910             : }
     911             : 
     912             : /************************************************************************/
     913             : /*                               Unlink()                               */
     914             : /************************************************************************/
     915             : 
     916        4648 : int VSIUnixStdioFilesystemHandler::Unlink(const char *pszFilename)
     917             : 
     918             : {
     919        4648 :     return unlink(pszFilename);
     920             : }
     921             : 
     922             : /************************************************************************/
     923             : /*                               Rename()                               */
     924             : /************************************************************************/
     925             : 
     926         812 : int VSIUnixStdioFilesystemHandler::Rename(const char *oldpath,
     927             :                                           const char *newpath, GDALProgressFunc,
     928             :                                           void *)
     929             : 
     930             : {
     931         812 :     return rename(oldpath, newpath);
     932             : }
     933             : 
     934             : /************************************************************************/
     935             : /*                               Mkdir()                                */
     936             : /************************************************************************/
     937             : 
     938        1230 : int VSIUnixStdioFilesystemHandler::Mkdir(const char *pszPathname, long nMode)
     939             : 
     940             : {
     941        1230 :     return mkdir(pszPathname, static_cast<int>(nMode));
     942             : }
     943             : 
     944             : /************************************************************************/
     945             : /*                               Rmdir()                                */
     946             : /************************************************************************/
     947             : 
     948          63 : int VSIUnixStdioFilesystemHandler::Rmdir(const char *pszPathname)
     949             : 
     950             : {
     951          63 :     return rmdir(pszPathname);
     952             : }
     953             : 
     954             : /************************************************************************/
     955             : /*                              ReadDirEx()                             */
     956             : /************************************************************************/
     957             : 
     958       22114 : char **VSIUnixStdioFilesystemHandler::ReadDirEx(const char *pszPath,
     959             :                                                 int nMaxFiles)
     960             : 
     961             : {
     962       22114 :     if (strlen(pszPath) == 0)
     963          17 :         pszPath = ".";
     964             : 
     965       44226 :     CPLStringList oDir;
     966       22114 :     DIR *hDir = opendir(pszPath);
     967       22114 :     if (hDir != nullptr)
     968             :     {
     969             :         // We want to avoid returning NULL for an empty list.
     970       19811 :         oDir.Assign(static_cast<char **>(CPLCalloc(2, sizeof(char *))));
     971             : 
     972       19810 :         struct dirent *psDirEntry = nullptr;
     973     1819220 :         while ((psDirEntry = readdir(hDir)) != nullptr)
     974             :         {
     975     1799460 :             oDir.AddString(psDirEntry->d_name);
     976     1799410 :             if (nMaxFiles > 0 && oDir.Count() > nMaxFiles)
     977           5 :                 break;
     978             :         }
     979             : 
     980       19818 :         closedir(hDir);
     981             :     }
     982             :     else
     983             :     {
     984             :         // Should we generate an error?
     985             :         // For now we'll just return NULL (at the end of the function).
     986             :     }
     987             : 
     988       44226 :     return oDir.StealList();
     989             : }
     990             : 
     991             : /************************************************************************/
     992             : /*                        GetDiskFreeSpace()                            */
     993             : /************************************************************************/
     994             : 
     995           3 : GIntBig VSIUnixStdioFilesystemHandler::GetDiskFreeSpace(const char *
     996             : #ifdef HAVE_STATVFS
     997             :                                                             pszDirname
     998             : #endif
     999             : )
    1000             : {
    1001           3 :     GIntBig nRet = -1;
    1002             : #ifdef HAVE_STATVFS
    1003             : 
    1004             : #ifdef HAVE_STATVFS64
    1005             :     struct statvfs64 buf;
    1006           3 :     if (statvfs64(pszDirname, &buf) == 0)
    1007             :     {
    1008           2 :         nRet = static_cast<GIntBig>(buf.f_frsize *
    1009           2 :                                     static_cast<GUIntBig>(buf.f_bavail));
    1010             :     }
    1011             : #else
    1012             :     struct statvfs buf;
    1013             :     if (statvfs(pszDirname, &buf) == 0)
    1014             :     {
    1015             :         nRet = static_cast<GIntBig>(buf.f_frsize *
    1016             :                                     static_cast<GUIntBig>(buf.f_bavail));
    1017             :     }
    1018             : #endif
    1019             : 
    1020             : #endif
    1021           3 :     return nRet;
    1022             : }
    1023             : 
    1024             : /************************************************************************/
    1025             : /*                      SupportsSparseFiles()                           */
    1026             : /************************************************************************/
    1027             : 
    1028             : #ifdef __linux
    1029             : #include <sys/vfs.h>
    1030             : #endif
    1031             : 
    1032           2 : int VSIUnixStdioFilesystemHandler::SupportsSparseFiles(const char *
    1033             : #ifdef __linux
    1034             :                                                            pszPath
    1035             : #endif
    1036             : )
    1037             : {
    1038             : #ifdef __linux
    1039             :     struct statfs sStatFS;
    1040           2 :     if (statfs(pszPath, &sStatFS) == 0)
    1041             :     {
    1042             :         // Add here any missing filesystem supporting sparse files.
    1043             :         // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
    1044           2 :         switch (static_cast<unsigned>(sStatFS.f_type))
    1045             :         {
    1046             :             // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
    1047           2 :             case 0xef53U:      // ext2, 3, 4
    1048             :             case 0x52654973U:  // reiser
    1049             :             case 0x58465342U:  // xfs
    1050             :             case 0x3153464aU:  // jfs
    1051             :             case 0x5346544eU:  // ntfs
    1052             :             case 0x9123683eU:  // brfs
    1053             :             // nfs: NFS < 4.2 supports creating sparse files (but reading them
    1054             :             // not efficiently).
    1055             :             case 0x6969U:
    1056             :             case 0x01021994U:  // tmpfs
    1057           2 :                 return TRUE;
    1058             : 
    1059           0 :             case 0x4d44U:  // msdos
    1060           0 :                 return FALSE;
    1061             : 
    1062           0 :             case 0x53464846U:  // Windows Subsystem for Linux fs
    1063             :             {
    1064             :                 static bool bUnknownFSEmitted = false;
    1065           0 :                 if (!bUnknownFSEmitted)
    1066             :                 {
    1067           0 :                     CPLDebug("VSI",
    1068             :                              "Windows Subsystem for Linux FS is at "
    1069             :                              "the time of writing not known to support sparse "
    1070             :                              "files");
    1071           0 :                     bUnknownFSEmitted = true;
    1072             :                 }
    1073           0 :                 return FALSE;
    1074             :             }
    1075             : 
    1076           0 :             default:
    1077             :             {
    1078             :                 static bool bUnknownFSEmitted = false;
    1079           0 :                 if (!bUnknownFSEmitted)
    1080             :                 {
    1081           0 :                     CPLDebug("VSI",
    1082             :                              "Filesystem with type %X unknown. "
    1083             :                              "Assuming it does not support sparse files",
    1084           0 :                              static_cast<int>(sStatFS.f_type));
    1085           0 :                     bUnknownFSEmitted = true;
    1086             :                 }
    1087           0 :                 return FALSE;
    1088             :             }
    1089             :         }
    1090             :     }
    1091           0 :     return FALSE;
    1092             : #else
    1093             :     static bool bMessageEmitted = false;
    1094             :     if (!bMessageEmitted)
    1095             :     {
    1096             :         CPLDebug("VSI", "Sorry: SupportsSparseFiles() not implemented "
    1097             :                         "for this operating system");
    1098             :         bMessageEmitted = true;
    1099             :     }
    1100             :     return FALSE;
    1101             : #endif
    1102             : }
    1103             : 
    1104             : /************************************************************************/
    1105             : /*                          IsLocal()                                   */
    1106             : /************************************************************************/
    1107             : 
    1108         150 : bool VSIUnixStdioFilesystemHandler::IsLocal(const char *
    1109             : #ifdef __linux
    1110             :                                                 pszPath
    1111             : #endif
    1112             : )
    1113             : {
    1114             : #ifdef __linux
    1115             :     struct statfs sStatFS;
    1116         150 :     if (statfs(pszPath, &sStatFS) == 0)
    1117             :     {
    1118             :         // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
    1119         146 :         switch (static_cast<unsigned>(sStatFS.f_type))
    1120             :         {
    1121             :             // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
    1122           0 :             case 0x6969U:      // NFS
    1123             :             case 0x517bU:      // SMB
    1124             :             case 0xff534d42U:  // CIFS
    1125             :             case 0xfe534d42U:  // SMB2
    1126             :                 // (https://github.com/libuv/libuv/blob/97dcdb1926f6aca43171e1614338bcef067abd59/src/unix/fs.c#L960)
    1127           0 :                 return false;
    1128             :         }
    1129             :     }
    1130             : #else
    1131             :     static bool bMessageEmitted = false;
    1132             :     if (!bMessageEmitted)
    1133             :     {
    1134             :         CPLDebug("VSI", "Sorry: IsLocal() not implemented "
    1135             :                         "for this operating system");
    1136             :         bMessageEmitted = true;
    1137             :     }
    1138             : #endif
    1139         150 :     return true;
    1140             : }
    1141             : 
    1142             : /************************************************************************/
    1143             : /*                    SupportsSequentialWrite()                         */
    1144             : /************************************************************************/
    1145             : 
    1146         130 : bool VSIUnixStdioFilesystemHandler::SupportsSequentialWrite(
    1147             :     const char *pszPath, bool /* bAllowLocalTempFile */)
    1148             : {
    1149             :     VSIStatBufL sStat;
    1150         130 :     if (VSIStatL(pszPath, &sStat) == 0)
    1151          62 :         return access(pszPath, W_OK) == 0;
    1152          68 :     return access(CPLGetDirnameSafe(pszPath).c_str(), W_OK) == 0;
    1153             : }
    1154             : 
    1155             : /************************************************************************/
    1156             : /*                     SupportsRandomWrite()                            */
    1157             : /************************************************************************/
    1158             : 
    1159          68 : bool VSIUnixStdioFilesystemHandler::SupportsRandomWrite(
    1160             :     const char *pszPath, bool /* bAllowLocalTempFile */)
    1161             : {
    1162          68 :     return SupportsSequentialWrite(pszPath, false);
    1163             : }
    1164             : 
    1165             : /************************************************************************/
    1166             : /*                            VSIDIRUnixStdio                           */
    1167             : /************************************************************************/
    1168             : 
    1169             : struct VSIDIRUnixStdio final : public VSIDIR
    1170             : {
    1171             :     struct DIRCloser
    1172             :     {
    1173         695 :         void operator()(DIR *d)
    1174             :         {
    1175         695 :             if (d)
    1176         695 :                 closedir(d);
    1177         695 :         }
    1178             :     };
    1179             : 
    1180             :     CPLString osRootPath{};
    1181             :     CPLString osBasePath{};
    1182             :     std::unique_ptr<DIR, DIRCloser> m_psDir{};
    1183             :     int nRecurseDepth = 0;
    1184             :     VSIDIREntry entry{};
    1185             :     std::vector<std::unique_ptr<VSIDIR>> aoStackSubDir{};
    1186             :     std::string m_osFilterPrefix{};
    1187             :     bool m_bNameAndTypeOnly = false;
    1188             : 
    1189             :     const VSIDIREntry *NextDirEntry() override;
    1190             : };
    1191             : 
    1192             : /************************************************************************/
    1193             : /*                        OpenDirInternal()                             */
    1194             : /************************************************************************/
    1195             : 
    1196             : /* static */
    1197         707 : std::unique_ptr<VSIDIRUnixStdio> VSIUnixStdioFilesystemHandler::OpenDirInternal(
    1198             :     const char *pszPath, int nRecurseDepth, const char *const *papszOptions)
    1199             : {
    1200        1414 :     std::unique_ptr<DIR, VSIDIRUnixStdio::DIRCloser> psDir(opendir(pszPath));
    1201         707 :     if (psDir == nullptr)
    1202             :     {
    1203          12 :         return nullptr;
    1204             :     }
    1205        1390 :     auto dir = std::make_unique<VSIDIRUnixStdio>();
    1206         695 :     dir->osRootPath = pszPath;
    1207         695 :     dir->nRecurseDepth = nRecurseDepth;
    1208         695 :     dir->m_psDir = std::move(psDir);
    1209         695 :     dir->m_osFilterPrefix = CSLFetchNameValueDef(papszOptions, "PREFIX", "");
    1210         695 :     dir->m_bNameAndTypeOnly = CPLTestBool(
    1211             :         CSLFetchNameValueDef(papszOptions, "NAME_AND_TYPE_ONLY", "NO"));
    1212         695 :     return dir;
    1213             : }
    1214             : 
    1215             : /************************************************************************/
    1216             : /*                            OpenDir()                                 */
    1217             : /************************************************************************/
    1218             : 
    1219         185 : VSIDIR *VSIUnixStdioFilesystemHandler::OpenDir(const char *pszPath,
    1220             :                                                int nRecurseDepth,
    1221             :                                                const char *const *papszOptions)
    1222             : {
    1223         185 :     return OpenDirInternal(pszPath, nRecurseDepth, papszOptions).release();
    1224             : }
    1225             : 
    1226             : /************************************************************************/
    1227             : /*                           NextDirEntry()                             */
    1228             : /************************************************************************/
    1229             : 
    1230       19132 : const VSIDIREntry *VSIDIRUnixStdio::NextDirEntry()
    1231             : {
    1232       19132 : begin:
    1233       19132 :     if (VSI_ISDIR(entry.nMode) && nRecurseDepth != 0)
    1234             :     {
    1235        1044 :         CPLString osCurFile(osRootPath);
    1236         522 :         if (!osCurFile.empty())
    1237         522 :             osCurFile += '/';
    1238         522 :         osCurFile += entry.pszName;
    1239             :         auto subdir = VSIUnixStdioFilesystemHandler::OpenDirInternal(
    1240         522 :             osCurFile, nRecurseDepth - 1, nullptr);
    1241         522 :         if (subdir)
    1242             :         {
    1243         522 :             subdir->osRootPath = osRootPath;
    1244         522 :             subdir->osBasePath = entry.pszName;
    1245         522 :             subdir->m_osFilterPrefix = m_osFilterPrefix;
    1246         522 :             subdir->m_bNameAndTypeOnly = m_bNameAndTypeOnly;
    1247         522 :             aoStackSubDir.push_back(std::move(subdir));
    1248             :         }
    1249         522 :         entry.nMode = 0;
    1250             :     }
    1251             : 
    1252       19654 :     while (!aoStackSubDir.empty())
    1253             :     {
    1254        9972 :         auto l_entry = aoStackSubDir.back()->NextDirEntry();
    1255        9972 :         if (l_entry)
    1256             :         {
    1257        9450 :             return l_entry;
    1258             :         }
    1259         522 :         aoStackSubDir.pop_back();
    1260             :     }
    1261             : 
    1262       11065 :     while (const auto *psEntry = readdir(m_psDir.get()))
    1263             :     {
    1264             :         // Skip . and ..entries
    1265       10377 :         if (psEntry->d_name[0] == '.' &&
    1266        1386 :             (psEntry->d_name[1] == '\0' ||
    1267         697 :              (psEntry->d_name[1] == '.' && psEntry->d_name[2] == '\0')))
    1268             :         {
    1269             :             // do nothing
    1270             :         }
    1271             :         else
    1272             :         {
    1273        8999 :             CPLFree(entry.pszName);
    1274        8999 :             CPLString osName(osBasePath);
    1275        8999 :             if (!osName.empty())
    1276        5603 :                 osName += '/';
    1277        8999 :             osName += psEntry->d_name;
    1278             : 
    1279        8999 :             entry.pszName = CPLStrdup(osName);
    1280        8999 :             entry.nMode = 0;
    1281        8999 :             entry.nSize = 0;
    1282        8999 :             entry.nMTime = 0;
    1283        8999 :             entry.bModeKnown = false;
    1284        8999 :             entry.bSizeKnown = false;
    1285        8999 :             entry.bMTimeKnown = false;
    1286             : 
    1287        8999 :             CPLString osCurFile(osRootPath);
    1288        8999 :             if (!osCurFile.empty())
    1289        8999 :                 osCurFile += '/';
    1290        8999 :             osCurFile += entry.pszName;
    1291             : 
    1292             : #if !defined(__sun) && !defined(__HAIKU__)
    1293        8999 :             if (psEntry->d_type == DT_REG)
    1294        7376 :                 entry.nMode = S_IFREG;
    1295        1623 :             else if (psEntry->d_type == DT_DIR)
    1296         567 :                 entry.nMode = S_IFDIR;
    1297        1056 :             else if (psEntry->d_type == DT_LNK)
    1298        1056 :                 entry.nMode = S_IFLNK;
    1299             : #endif
    1300             : 
    1301       14110 :             const auto StatFile = [&osCurFile, this]()
    1302             :             {
    1303             :                 VSIStatBufL sStatL;
    1304        7055 :                 if (VSIStatL(osCurFile, &sStatL) == 0)
    1305             :                 {
    1306        7055 :                     entry.nMode = sStatL.st_mode;
    1307        7055 :                     entry.nSize = sStatL.st_size;
    1308        7055 :                     entry.nMTime = sStatL.st_mtime;
    1309        7055 :                     entry.bModeKnown = true;
    1310        7055 :                     entry.bSizeKnown = true;
    1311        7055 :                     entry.bMTimeKnown = true;
    1312             :                 }
    1313        7055 :             };
    1314             : 
    1315        9011 :             if (!m_osFilterPrefix.empty() &&
    1316          12 :                 m_osFilterPrefix.size() > osName.size())
    1317             :             {
    1318           6 :                 if (STARTS_WITH(m_osFilterPrefix.c_str(), osName.c_str()) &&
    1319           2 :                     m_osFilterPrefix[osName.size()] == '/')
    1320             :                 {
    1321             : #if !defined(__sun) && !defined(__HAIKU__)
    1322           1 :                     if (psEntry->d_type == DT_UNKNOWN)
    1323             : #endif
    1324             :                     {
    1325           0 :                         StatFile();
    1326             :                     }
    1327           1 :                     if (VSI_ISDIR(entry.nMode))
    1328             :                     {
    1329           1 :                         goto begin;
    1330             :                     }
    1331             :                 }
    1332           3 :                 continue;
    1333             :             }
    1334        9003 :             if (!m_osFilterPrefix.empty() &&
    1335           8 :                 !STARTS_WITH(osName.c_str(), m_osFilterPrefix.c_str()))
    1336             :             {
    1337           2 :                 continue;
    1338             :             }
    1339             : 
    1340        8993 :             if (!m_bNameAndTypeOnly
    1341             : #if !defined(__sun) && !defined(__HAIKU__)
    1342        1938 :                 || psEntry->d_type == DT_UNKNOWN
    1343             : #endif
    1344             :             )
    1345             :             {
    1346        7055 :                 StatFile();
    1347             :             }
    1348             : 
    1349        8993 :             return &(entry);
    1350             :         }
    1351        1383 :     }
    1352             : 
    1353         688 :     return nullptr;
    1354             : }
    1355             : 
    1356             : #ifdef VSI_COUNT_BYTES_READ
    1357             : /************************************************************************/
    1358             : /*                            AddToTotal()                              */
    1359             : /************************************************************************/
    1360             : 
    1361             : void VSIUnixStdioFilesystemHandler::AddToTotal(vsi_l_offset nBytes)
    1362             : {
    1363             :     CPLMutexHolder oHolder(&hMutex);
    1364             :     nTotalBytesRead += nBytes;
    1365             : }
    1366             : 
    1367             : #endif
    1368             : 
    1369             : /************************************************************************/
    1370             : /*                      GetCanonicalFilename()                          */
    1371             : /************************************************************************/
    1372             : 
    1373             : #ifdef HAS_CASE_INSENSITIVE_FILE_SYSTEM
    1374             : std::string VSIUnixStdioFilesystemHandler::GetCanonicalFilename(
    1375             :     const std::string &osFilename) const
    1376             : {
    1377             :     char szResolvedPath[PATH_MAX];
    1378             :     const char *pszFilename = osFilename.c_str();
    1379             :     if (realpath(pszFilename, szResolvedPath))
    1380             :     {
    1381             :         const char *pszFilenameLastPart = strrchr(pszFilename, '/');
    1382             :         const char *pszResolvedFilenameLastPart = strrchr(szResolvedPath, '/');
    1383             :         if (pszFilenameLastPart && pszResolvedFilenameLastPart &&
    1384             :             EQUAL(pszFilenameLastPart, pszResolvedFilenameLastPart))
    1385             :         {
    1386             :             std::string osRet;
    1387             :             osRet.assign(pszFilename, pszFilenameLastPart - pszFilename);
    1388             :             osRet += pszResolvedFilenameLastPart;
    1389             :             return osRet;
    1390             :         }
    1391             :         return szResolvedPath;
    1392             :     }
    1393             :     return osFilename;
    1394             : }
    1395             : #endif
    1396             : 
    1397             : /************************************************************************/
    1398             : /*                     VSIInstallLargeFileHandler()                     */
    1399             : /************************************************************************/
    1400             : 
    1401        1694 : void VSIInstallLargeFileHandler()
    1402             : 
    1403             : {
    1404        1694 :     VSIFileManager::InstallHandler("", new VSIUnixStdioFilesystemHandler());
    1405        1694 : }
    1406             : 
    1407             : #endif  // ndef WIN32
    1408             : 
    1409             : //! @endcond

Generated by: LCOV version 1.14