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: 297 322 92.2 %
Date: 2025-09-10 17:48:50 Functions: 39 39 100.0 %

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

Generated by: LCOV version 1.14