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

Generated by: LCOV version 1.14