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

Generated by: LCOV version 1.14