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  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************
30  *
31  * NB: Note that in wrappers we are always saving the error state (errno
32  * variable) to avoid side effects during debug prints or other possible
33  * standard function calls (error states will be overwritten after such
34  * a call).
35  *
36  ****************************************************************************/
37 
38 //! @cond Doxygen_Suppress
39 
40 //#define VSI_COUNT_BYTES_READ
41 
42 // Some unusual filesystems do not work if _FORTIFY_SOURCE in GCC or
43 // clang is used within this source file, especially if techniques
44 // like those in vsipreload are used.  Fortify source interacts poorly with
45 // filesystems that use fread for forward seeks.  This leads to SIGSEGV within
46 // fread calls.
47 //
48 // See this for hardening background info: https://wiki.debian.org/Hardening
49 #undef _FORTIFY_SOURCE
50 
51 // 64 bit IO is only available on 32-bit android since API 24 / Android 7.0
52 // See https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md
53 #if defined(__ANDROID_API__) && __ANDROID_API__ >= 24
54 #define _FILE_OFFSET_BITS 64
55 #endif
56 
57 #include "cpl_port.h"
58 
59 #if !defined(WIN32)
60 
61 #include "cpl_vsi.h"
62 #include "cpl_vsi_virtual.h"
63 
64 #include <cstddef>
65 #include <cstdio>
66 #include <cstring>
67 #include <dirent.h>
68 #include <errno.h>
69 #if HAVE_FCNTL_H
70 #  include <fcntl.h>
71 #endif
72 #include <sys/stat.h>
73 #ifdef HAVE_STATVFS
74 #include <sys/statvfs.h>
75 #endif
76 #include <sys/types.h>
77 #if HAVE_UNISTD_H
78 #include <unistd.h>
79 #endif
80 
81 #include <limits>
82 #include <new>
83 
84 #include "cpl_config.h"
85 #include "cpl_conv.h"
86 #include "cpl_error.h"
87 #include "cpl_multiproc.h"
88 #include "cpl_string.h"
89 #include "cpl_vsi_error.h"
90 
91 CPL_CVSID("$Id: cpl_vsil_unix_stdio_64.cpp e7e12cb098f856435ea3dc6f43839809682ca37a 2021-09-19 19:43:21 +0200 Even Rouault $")
92 
93 #if defined(UNIX_STDIO_64)
94 
95 #ifndef VSI_FTELL64
96 #define VSI_FTELL64 ftell64
97 #endif
98 #ifndef VSI_FSEEK64
99 #define VSI_FSEEK64 fseek64
100 #endif
101 #ifndef VSI_FOPEN64
102 #define VSI_FOPEN64 fopen64
103 #endif
104 #ifndef VSI_STAT64
105 #define VSI_STAT64 stat64
106 #endif
107 #ifndef VSI_STAT64_T
108 #define VSI_STAT64_T stat64
109 #endif
110 #ifndef VSI_FTRUNCATE64
111 #define VSI_FTRUNCATE64 ftruncate64
112 #endif
113 
114 #else /* not UNIX_STDIO_64 */
115 
116 #ifndef VSI_FTELL64
117 #define VSI_FTELL64 ftell
118 #endif
119 #ifndef VSI_FSEEK64
120 #define VSI_FSEEK64 fseek
121 #endif
122 #ifndef VSI_FOPEN64
123 #define VSI_FOPEN64 fopen
124 #endif
125 #ifndef VSI_STAT64
126 #define VSI_STAT64 stat
127 #endif
128 #ifndef VSI_STAT64_T
129 #define VSI_STAT64_T stat
130 #endif
131 #ifndef VSI_FTRUNCATE64
132 #define VSI_FTRUNCATE64 ftruncate
133 #endif
134 
135 #endif /* ndef UNIX_STDIO_64 */
136 
137 /************************************************************************/
138 /* ==================================================================== */
139 /*                       VSIUnixStdioFilesystemHandler                  */
140 /* ==================================================================== */
141 /************************************************************************/
142 
143 class VSIUnixStdioFilesystemHandler final : public VSIFilesystemHandler
144 {
145     CPL_DISALLOW_COPY_ASSIGN(VSIUnixStdioFilesystemHandler)
146 
147 #ifdef VSI_COUNT_BYTES_READ
148     vsi_l_offset  nTotalBytesRead  = 0;
149     CPLMutex     *hMutex = nullptr;
150 #endif
151 
152 public:
153     VSIUnixStdioFilesystemHandler() = default;
154 #ifdef VSI_COUNT_BYTES_READ
155     ~VSIUnixStdioFilesystemHandler() override;
156 #endif
157 
158     VSIVirtualHandle *Open( const char *pszFilename,
159                             const char *pszAccess,
160                             bool bSetError,
161                             CSLConstList /* papszOptions */ ) override;
162     int Stat( const char *pszFilename, VSIStatBufL *pStatBuf,
163               int nFlags ) override;
164     int Unlink( const char *pszFilename ) override;
165     int Rename( const char *oldpath, const char *newpath ) override;
166     int Mkdir( const char *pszDirname, long nMode ) override;
167     int Rmdir( const char *pszDirname ) override;
168     char **ReadDirEx( const char *pszDirname, int nMaxFiles ) override;
169     GIntBig GetDiskFreeSpace( const char* pszDirname ) override;
170     int SupportsSparseFiles( const char* pszPath ) override;
171 
172 #ifdef VSI_COUNT_BYTES_READ
173     void             AddToTotal(vsi_l_offset nBytes);
174 #endif
175 };
176 
177 /************************************************************************/
178 /* ==================================================================== */
179 /*                        VSIUnixStdioHandle                            */
180 /* ==================================================================== */
181 /************************************************************************/
182 
183 class VSIUnixStdioHandle final : public VSIVirtualHandle
184 {
185     CPL_DISALLOW_COPY_ASSIGN(VSIUnixStdioHandle)
186 
187     FILE          *fp = nullptr;
188     vsi_l_offset  m_nOffset = 0;
189     bool          bReadOnly = true;
190     bool          bLastOpWrite = false;
191     bool          bLastOpRead = false;
192     bool          bAtEOF = false;
193     // In a+ mode, disable any optimization since the behavior of the file
194     // pointer on Mac and other BSD system is to have a seek() to the end of
195     // file and thus a call to our Seek(0, SEEK_SET) before a read will be a
196     // no-op.
197     bool          bModeAppendReadWrite = false;
198 #ifdef VSI_COUNT_BYTES_READ
199     vsi_l_offset  nTotalBytesRead = 0;
200     VSIUnixStdioFilesystemHandler *poFS = nullptr;
201 #endif
202   public:
203     VSIUnixStdioHandle( VSIUnixStdioFilesystemHandler *poFSIn,
204                         FILE* fpIn, bool bReadOnlyIn,
205                         bool bModeAppendReadWriteIn );
206 
207     int Seek( vsi_l_offset nOffsetIn, int nWhence ) override;
208     vsi_l_offset Tell() override;
209     size_t Read( void *pBuffer, size_t nSize, size_t nMemb ) override;
210     size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ) override;
211     int Eof() override;
212     int Flush() override;
213     int Close() override;
214     int Truncate( vsi_l_offset nNewSize ) override;
GetNativeFileDescriptor()215     void *GetNativeFileDescriptor() override {
216         return reinterpret_cast<void *>(static_cast<size_t>(fileno(fp))); }
217     VSIRangeStatus GetRangeStatus( vsi_l_offset nOffset,
218                                    vsi_l_offset nLength ) override;
219 };
220 
221 /************************************************************************/
222 /*                       VSIUnixStdioHandle()                           */
223 /************************************************************************/
224 
VSIUnixStdioHandle(CPL_UNUSED VSIUnixStdioFilesystemHandler * poFSIn,FILE * fpIn,bool bReadOnlyIn,bool bModeAppendReadWriteIn)225 VSIUnixStdioHandle::VSIUnixStdioHandle(
226 #ifndef VSI_COUNT_BYTES_READ
227 CPL_UNUSED
228 #endif
229                                        VSIUnixStdioFilesystemHandler *poFSIn,
230                                        FILE* fpIn, bool bReadOnlyIn,
231                                        bool bModeAppendReadWriteIn) :
232     fp(fpIn),
233     bReadOnly(bReadOnlyIn),
234     bModeAppendReadWrite(bModeAppendReadWriteIn)
235 #ifdef VSI_COUNT_BYTES_READ
236     ,
237     poFS(poFSIn)
238 #endif
239 {}
240 
241 /************************************************************************/
242 /*                               Close()                                */
243 /************************************************************************/
244 
Close()245 int VSIUnixStdioHandle::Close()
246 
247 {
248     VSIDebug1( "VSIUnixStdioHandle::Close(%p)", fp );
249 
250 #ifdef VSI_COUNT_BYTES_READ
251     poFS->AddToTotal(nTotalBytesRead);
252 #endif
253 
254     return fclose( fp );
255 }
256 
257 /************************************************************************/
258 /*                                Seek()                                */
259 /************************************************************************/
260 
Seek(vsi_l_offset nOffsetIn,int nWhence)261 int VSIUnixStdioHandle::Seek( vsi_l_offset nOffsetIn, int nWhence )
262 {
263     bAtEOF = false;
264 
265     // Seeks that do nothing are still surprisingly expensive with MSVCRT.
266     // try and short circuit if possible.
267     if( !bModeAppendReadWrite && nWhence == SEEK_SET && nOffsetIn == m_nOffset )
268         return 0;
269 
270     // On a read-only file, we can avoid a lseek() system call to be issued
271     // if the next position to seek to is within the buffered page.
272     if( bReadOnly && nWhence == SEEK_SET )
273     {
274         const int l_PAGE_SIZE = 4096;
275         if( nOffsetIn > m_nOffset && nOffsetIn < l_PAGE_SIZE + m_nOffset )
276         {
277             const int nDiff = static_cast<int>(nOffsetIn - m_nOffset);
278             // Do not zero-initialize the buffer. We don't read from it
279             GByte abyTemp[l_PAGE_SIZE];
280             const int nRead = static_cast<int>(fread(abyTemp, 1, nDiff, fp));
281             if( nRead == nDiff )
282             {
283                 m_nOffset = nOffsetIn;
284                 bLastOpWrite = false;
285                 bLastOpRead = false;
286                 return 0;
287             }
288         }
289     }
290 
291 #if !defined(UNIX_STDIO_64) && SIZEOF_UNSIGNED_LONG == 4
292     if( nOffsetIn > static_cast<vsi_l_offset>(std::numeric_limits<long>::max()) )
293     {
294         CPLError(CE_Failure, CPLE_AppDefined,
295                  "Attempt at seeking beyond long extent. Lack of 64-bit file I/O");
296         return -1;
297     }
298 #endif
299 
300     const int nResult = VSI_FSEEK64( fp, nOffsetIn, nWhence );
301     const int nError = errno;
302 
303 #ifdef VSI_DEBUG
304 
305     if( nWhence == SEEK_SET )
306     {
307         VSIDebug3( "VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB
308                    ",SEEK_SET) = %d",
309                    fp, nOffsetIn, nResult );
310     }
311     else if( nWhence == SEEK_END )
312     {
313         VSIDebug3( "VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB
314                    ",SEEK_END) = %d",
315                    fp, nOffsetIn, nResult );
316     }
317     else if( nWhence == SEEK_CUR )
318     {
319         VSIDebug3( "VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB
320                    ",SEEK_CUR) = %d",
321                    fp, nOffsetIn, nResult );
322     }
323     else
324     {
325         VSIDebug4( "VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB
326                    ",%d-Unknown) = %d",
327                    fp, nOffsetIn, nWhence, nResult );
328     }
329 
330 #endif
331 
332     if( nResult != -1 )
333     {
334         if( nWhence == SEEK_SET )
335         {
336             m_nOffset = nOffsetIn;
337         }
338         else if( nWhence == SEEK_END )
339         {
340             m_nOffset = VSI_FTELL64( fp );
341         }
342         else if( nWhence == SEEK_CUR )
343         {
344             if( nOffsetIn > INT_MAX )
345             {
346                 //printf("likely negative offset intended\n");
347             }
348             m_nOffset += nOffsetIn;
349         }
350     }
351 
352     bLastOpWrite = false;
353     bLastOpRead = false;
354 
355     errno = nError;
356     return nResult;
357 }
358 
359 /************************************************************************/
360 /*                                Tell()                                */
361 /************************************************************************/
362 
Tell()363 vsi_l_offset VSIUnixStdioHandle::Tell()
364 
365 {
366 #if 0
367     const vsi_l_offset nOffset = VSI_FTELL64( fp );
368     const int nError = errno;
369 
370     VSIDebug2( "VSIUnixStdioHandle::Tell(%p) = %ld",
371                fp, static_cast<long>(nOffset) );
372 
373     errno = nError;
374 #endif
375 
376     return m_nOffset;
377 }
378 
379 /************************************************************************/
380 /*                               Flush()                                */
381 /************************************************************************/
382 
Flush()383 int VSIUnixStdioHandle::Flush()
384 
385 {
386     VSIDebug1( "VSIUnixStdioHandle::Flush(%p)", fp );
387 
388     return fflush( fp );
389 }
390 
391 /************************************************************************/
392 /*                                Read()                                */
393 /************************************************************************/
394 
Read(void * pBuffer,size_t nSize,size_t nCount)395 size_t VSIUnixStdioHandle::Read( void * pBuffer, size_t nSize, size_t nCount )
396 
397 {
398 /* -------------------------------------------------------------------- */
399 /*      If a fwrite() is followed by an fread(), the POSIX rules are    */
400 /*      that some of the write may still be buffered and lost.  We      */
401 /*      are required to do a seek between to force flushing.   So we    */
402 /*      keep careful track of what happened last to know if we          */
403 /*      skipped a flushing seek that we may need to do now.             */
404 /* -------------------------------------------------------------------- */
405     if( !bModeAppendReadWrite && bLastOpWrite )
406     {
407         if( VSI_FSEEK64( fp, m_nOffset, SEEK_SET ) != 0 )
408         {
409             VSIDebug1("Write calling seek failed. %d", m_nOffset);
410         }
411     }
412 
413 /* -------------------------------------------------------------------- */
414 /*      Perform the read.                                               */
415 /* -------------------------------------------------------------------- */
416     const size_t nResult = fread( pBuffer, nSize, nCount, fp );
417 
418 #ifdef VSI_DEBUG
419     const int nError = errno;
420     VSIDebug4( "VSIUnixStdioHandle::Read(%p,%ld,%ld) = %ld",
421                fp, static_cast<long>(nSize), static_cast<long>(nCount),
422                static_cast<long>(nResult) );
423     errno = nError;
424 #endif
425 
426 /* -------------------------------------------------------------------- */
427 /*      Update current offset.                                          */
428 /* -------------------------------------------------------------------- */
429 
430 #ifdef VSI_COUNT_BYTES_READ
431     nTotalBytesRead += nSize * nResult;
432 #endif
433 
434     m_nOffset += nSize * nResult;
435     bLastOpWrite = false;
436     bLastOpRead = true;
437 
438     if( nResult != nCount )
439     {
440         errno = 0;
441         vsi_l_offset nNewOffset = VSI_FTELL64( fp );
442         if( errno == 0 ) // ftell() can fail if we are end of file with a pipe.
443             m_nOffset = nNewOffset;
444         else
445             CPLDebug("VSI", "%s", VSIStrerror(errno));
446         bAtEOF = CPL_TO_BOOL(feof(fp));
447     }
448 
449     return nResult;
450 }
451 
452 /************************************************************************/
453 /*                               Write()                                */
454 /************************************************************************/
455 
Write(const void * pBuffer,size_t nSize,size_t nCount)456 size_t VSIUnixStdioHandle::Write( const void * pBuffer, size_t nSize,
457                                   size_t nCount )
458 
459 {
460 /* -------------------------------------------------------------------- */
461 /*      If a fwrite() is followed by an fread(), the POSIX rules are    */
462 /*      that some of the write may still be buffered and lost.  We      */
463 /*      are required to do a seek between to force flushing.   So we    */
464 /*      keep careful track of what happened last to know if we          */
465 /*      skipped a flushing seek that we may need to do now.             */
466 /* -------------------------------------------------------------------- */
467     if( !bModeAppendReadWrite && bLastOpRead )
468     {
469         if( VSI_FSEEK64( fp, m_nOffset, SEEK_SET ) != 0 )
470         {
471             VSIDebug1("Write calling seek failed. %d", m_nOffset);
472         }
473     }
474 
475 /* -------------------------------------------------------------------- */
476 /*      Perform the write.                                              */
477 /* -------------------------------------------------------------------- */
478     const size_t nResult = fwrite( pBuffer, nSize, nCount, fp );
479 
480 #if VSI_DEBUG
481     const int nError = errno;
482 
483     VSIDebug4( "VSIUnixStdioHandle::Write(%p,%ld,%ld) = %ld",
484                fp, static_cast<long>(nSize), static_cast<long>(nCount),
485                static_cast<long>(nResult) );
486 
487     errno = nError;
488 #endif
489 
490 /* -------------------------------------------------------------------- */
491 /*      Update current offset.                                          */
492 /* -------------------------------------------------------------------- */
493     m_nOffset += nSize * nResult;
494     bLastOpWrite = true;
495     bLastOpRead = false;
496 
497     return nResult;
498 }
499 
500 /************************************************************************/
501 /*                                Eof()                                 */
502 /************************************************************************/
503 
Eof()504 int VSIUnixStdioHandle::Eof()
505 
506 {
507     return bAtEOF ? TRUE : FALSE;
508 }
509 
510 /************************************************************************/
511 /*                             Truncate()                               */
512 /************************************************************************/
513 
Truncate(vsi_l_offset nNewSize)514 int VSIUnixStdioHandle::Truncate( vsi_l_offset nNewSize )
515 {
516     fflush(fp);
517     return VSI_FTRUNCATE64( fileno(fp), nNewSize );
518 }
519 
520 /************************************************************************/
521 /*                          GetRangeStatus()                            */
522 /************************************************************************/
523 
524 #ifdef __linux
525 #include <linux/fs.h>  // FS_IOC_FIEMAP
526 #ifdef FS_IOC_FIEMAP
527 #include <linux/types.h>   // for types used in linux/fiemap.h
528 #include <linux/fiemap.h>  // struct fiemap
529 #endif
530 #include <sys/ioctl.h>
531 #include <errno.h>
532 #endif
533 
GetRangeStatus(vsi_l_offset nOffset,vsi_l_offset nLength)534 VSIRangeStatus VSIUnixStdioHandle::GetRangeStatus( vsi_l_offset
535 #ifdef FS_IOC_FIEMAP
536                                                                 nOffset
537 #endif
538                                                      , vsi_l_offset
539 #ifdef FS_IOC_FIEMAP
540                                                                 nLength
541 #endif
542                                                     )
543 {
544 #ifdef FS_IOC_FIEMAP
545     // fiemap IOCTL documented at
546     // https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt
547 
548     // The fiemap struct contains a "variable length" array at its end
549     // As we are interested in only one extent, we allocate the base size of
550     // fiemap + one fiemap_extent.
551     GByte abyBuffer[sizeof(struct fiemap) + sizeof(struct fiemap_extent)];
552     int fd = fileno(fp);
553     struct fiemap *psExtentMap = reinterpret_cast<struct fiemap *>(&abyBuffer);
554     memset(psExtentMap,
555            0,
556            sizeof(struct fiemap) + sizeof(struct fiemap_extent));
557     psExtentMap->fm_start = nOffset;
558     psExtentMap->fm_length = nLength;
559     psExtentMap->fm_extent_count = 1;
560     int ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
561     if( ret < 0 )
562         return VSI_RANGE_STATUS_UNKNOWN;
563     if( psExtentMap->fm_mapped_extents == 0 )
564         return VSI_RANGE_STATUS_HOLE;
565     // In case there is one extent with unknown status, retry after having
566     // asked the kernel to sync the file.
567     const fiemap_extent* pasExtent = &(psExtentMap->fm_extents[0]);
568     if( psExtentMap->fm_mapped_extents == 1 &&
569         (pasExtent[0].fe_flags & FIEMAP_EXTENT_UNKNOWN) != 0 )
570     {
571         psExtentMap->fm_flags = FIEMAP_FLAG_SYNC;
572         psExtentMap->fm_start = nOffset;
573         psExtentMap->fm_length = nLength;
574         psExtentMap->fm_extent_count = 1;
575         ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
576         if( ret < 0 )
577             return VSI_RANGE_STATUS_UNKNOWN;
578         if( psExtentMap->fm_mapped_extents == 0 )
579             return VSI_RANGE_STATUS_HOLE;
580     }
581     return VSI_RANGE_STATUS_DATA;
582 #else
583     static bool bMessageEmitted = false;
584     if( !bMessageEmitted )
585     {
586         CPLDebug("VSI",
587                  "Sorry: GetExtentStatus() not implemented for "
588                  "this operating system");
589         bMessageEmitted = true;
590     }
591     return VSI_RANGE_STATUS_UNKNOWN;
592 #endif
593 }
594 
595 /************************************************************************/
596 /* ==================================================================== */
597 /*                       VSIUnixStdioFilesystemHandler                  */
598 /* ==================================================================== */
599 /************************************************************************/
600 
601 #ifdef VSI_COUNT_BYTES_READ
602 /************************************************************************/
603 /*                     ~VSIUnixStdioFilesystemHandler()                 */
604 /************************************************************************/
605 
~VSIUnixStdioFilesystemHandler()606 VSIUnixStdioFilesystemHandler::~VSIUnixStdioFilesystemHandler()
607 {
608     CPLDebug( "VSI",
609               "~VSIUnixStdioFilesystemHandler() : nTotalBytesRead = "
610               CPL_FRMT_GUIB,
611               nTotalBytesRead );
612 
613     if( hMutex != nullptr )
614         CPLDestroyMutex( hMutex );
615     hMutex = nullptr;
616 }
617 #endif
618 
619 /************************************************************************/
620 /*                                Open()                                */
621 /************************************************************************/
622 
623 VSIVirtualHandle *
Open(const char * pszFilename,const char * pszAccess,bool bSetError,CSLConstList)624 VSIUnixStdioFilesystemHandler::Open( const char *pszFilename,
625                                      const char *pszAccess,
626                                      bool bSetError,
627                                      CSLConstList /* papszOptions */ )
628 
629 {
630     FILE *fp = VSI_FOPEN64( pszFilename, pszAccess );
631     const int nError = errno;
632 
633     VSIDebug3( "VSIUnixStdioFilesystemHandler::Open(\"%s\",\"%s\") = %p",
634                pszFilename, pszAccess, fp );
635 
636     if( fp == nullptr )
637     {
638         if( bSetError )
639         {
640             VSIError(VSIE_FileError, "%s: %s", pszFilename, strerror(nError));
641         }
642         errno = nError;
643         return nullptr;
644     }
645 
646     const bool bReadOnly =
647         strcmp(pszAccess, "rb") == 0 || strcmp(pszAccess, "r") == 0;
648     const bool bModeAppendReadWrite =
649         strcmp(pszAccess, "a+b") == 0 || strcmp(pszAccess, "a+") == 0;
650     VSIUnixStdioHandle *poHandle =
651         new(std::nothrow) VSIUnixStdioHandle( this, fp, bReadOnly,
652                                               bModeAppendReadWrite );
653     if( poHandle == nullptr )
654     {
655         fclose(fp);
656         return nullptr;
657     }
658 
659     errno = nError;
660 
661 /* -------------------------------------------------------------------- */
662 /*      If VSI_CACHE is set we want to use a cached reader instead      */
663 /*      of more direct io on the underlying file.                       */
664 /* -------------------------------------------------------------------- */
665     if( bReadOnly &&
666         CPLTestBool( CPLGetConfigOption( "VSI_CACHE", "FALSE" ) ) )
667     {
668         return VSICreateCachedFile( poHandle );
669     }
670 
671     return poHandle;
672 }
673 
674 /************************************************************************/
675 /*                                Stat()                                */
676 /************************************************************************/
677 
Stat(const char * pszFilename,VSIStatBufL * pStatBuf,int)678 int VSIUnixStdioFilesystemHandler::Stat( const char * pszFilename,
679                                          VSIStatBufL * pStatBuf,
680                                          int /* nFlags */ )
681 {
682     return( VSI_STAT64( pszFilename, pStatBuf ) );
683 }
684 
685 /************************************************************************/
686 /*                               Unlink()                               */
687 /************************************************************************/
688 
Unlink(const char * pszFilename)689 int VSIUnixStdioFilesystemHandler::Unlink( const char * pszFilename )
690 
691 {
692     return unlink( pszFilename );
693 }
694 
695 /************************************************************************/
696 /*                               Rename()                               */
697 /************************************************************************/
698 
Rename(const char * oldpath,const char * newpath)699 int VSIUnixStdioFilesystemHandler::Rename( const char *oldpath,
700                                            const char *newpath )
701 
702 {
703     return rename( oldpath, newpath );
704 }
705 
706 /************************************************************************/
707 /*                               Mkdir()                                */
708 /************************************************************************/
709 
Mkdir(const char * pszPathname,long nMode)710 int VSIUnixStdioFilesystemHandler::Mkdir( const char * pszPathname,
711                                           long nMode )
712 
713 {
714     return mkdir( pszPathname, static_cast<int>(nMode) );
715 }
716 
717 /************************************************************************/
718 /*                               Rmdir()                                */
719 /************************************************************************/
720 
Rmdir(const char * pszPathname)721 int VSIUnixStdioFilesystemHandler::Rmdir( const char * pszPathname )
722 
723 {
724     return rmdir( pszPathname );
725 }
726 
727 /************************************************************************/
728 /*                              ReadDirEx()                             */
729 /************************************************************************/
730 
ReadDirEx(const char * pszPath,int nMaxFiles)731 char **VSIUnixStdioFilesystemHandler::ReadDirEx( const char *pszPath,
732                                                  int nMaxFiles )
733 
734 {
735     if( strlen(pszPath) == 0 )
736         pszPath = ".";
737 
738     CPLStringList oDir;
739     DIR *hDir = opendir(pszPath);
740     if( hDir != nullptr )
741     {
742         // We want to avoid returning NULL for an empty list.
743         oDir.Assign(static_cast<char**>(CPLCalloc(2, sizeof(char*))));
744 
745         struct dirent *psDirEntry = nullptr;
746         while( (psDirEntry = readdir(hDir)) != nullptr )
747         {
748             oDir.AddString( psDirEntry->d_name );
749             if( nMaxFiles > 0 && oDir.Count() > nMaxFiles )
750                 break;
751         }
752 
753         closedir( hDir );
754     }
755     else
756     {
757         // Should we generate an error?
758         // For now we'll just return NULL (at the end of the function).
759     }
760 
761     return oDir.StealList();
762 }
763 
764 /************************************************************************/
765 /*                        GetDiskFreeSpace()                            */
766 /************************************************************************/
767 
GetDiskFreeSpace(const char * pszDirname)768 GIntBig VSIUnixStdioFilesystemHandler::GetDiskFreeSpace( const char*
769 #ifdef HAVE_STATVFS
770                                                          pszDirname
771 #endif
772                                                        )
773 {
774     GIntBig nRet = -1;
775 #ifdef HAVE_STATVFS
776 
777 #ifdef HAVE_STATVFS64
778     struct statvfs64 buf;
779     if( statvfs64(pszDirname, &buf) == 0 )
780     {
781         nRet = static_cast<GIntBig>(buf.f_frsize *
782                                     static_cast<GUIntBig>(buf.f_bavail));
783     }
784 #else
785     struct statvfs buf;
786     if( statvfs(pszDirname, &buf) == 0 )
787     {
788         nRet = static_cast<GIntBig>(buf.f_frsize *
789                                     static_cast<GUIntBig>(buf.f_bavail));
790     }
791 #endif
792 
793 #endif
794     return nRet;
795 }
796 
797 /************************************************************************/
798 /*                      SupportsSparseFiles()                           */
799 /************************************************************************/
800 
801 #ifdef __linux
802 #include <sys/vfs.h>
803 #endif
804 
SupportsSparseFiles(const char * pszPath)805 int VSIUnixStdioFilesystemHandler::SupportsSparseFiles( const char*
806 #ifdef __linux
807                                                         pszPath
808 #endif
809                                                         )
810 {
811 #ifdef __linux
812     struct statfs sStatFS;
813     if( statfs( pszPath, &sStatFS ) == 0 )
814     {
815         // Add here any missing filesystem supporting sparse files.
816         // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
817         switch( static_cast<unsigned>(sStatFS.f_type) )
818         {
819             // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
820             case 0xef53U:  // ext2, 3, 4
821             case 0x52654973U:  // reiser
822             case 0x58465342U:  // xfs
823             case 0x3153464aU:  // jfs
824             case 0x5346544eU:  // ntfs
825             case 0x9123683eU:  // brfs
826             // nfs: NFS < 4.2 supports creating sparse files (but reading them
827             // not efficiently).
828             case 0x6969U:
829             case 0x01021994U:  // tmpfs
830                 return TRUE;
831 
832             case 0x4d44U: // msdos
833                 return FALSE;
834 
835             case 0x53464846U:  // Windows Subsystem for Linux fs
836             {
837                 static bool bUnknownFSEmitted = false;
838                 if( !bUnknownFSEmitted )
839                 {
840                     CPLDebug("VSI", "Windows Subsystem for Linux FS is at "
841                              "the time of writing not known to support sparse "
842                              "files");
843                     bUnknownFSEmitted = true;
844                 }
845                 return FALSE;
846             }
847 
848             default:
849             {
850                 static bool bUnknownFSEmitted = false;
851                 if( !bUnknownFSEmitted )
852                 {
853                     CPLDebug("VSI", "Filesystem with type %X unknown. "
854                              "Assuming it does not support sparse files",
855                              static_cast<int>(sStatFS.f_type) );
856                     bUnknownFSEmitted = true;
857                 }
858                 return FALSE;
859             }
860         }
861     }
862     return FALSE;
863 #else
864     static bool bMessageEmitted = false;
865     if( !bMessageEmitted )
866     {
867         CPLDebug("VSI",
868                  "Sorry: SupportsSparseFiles() not implemented "
869                  "for this operating system");
870         bMessageEmitted = true;
871     }
872     return FALSE;
873 #endif
874 }
875 
876 #ifdef VSI_COUNT_BYTES_READ
877 /************************************************************************/
878 /*                            AddToTotal()                              */
879 /************************************************************************/
880 
AddToTotal(vsi_l_offset nBytes)881 void VSIUnixStdioFilesystemHandler::AddToTotal( vsi_l_offset nBytes )
882 {
883     CPLMutexHolder oHolder(&hMutex);
884     nTotalBytesRead += nBytes;
885 }
886 
887 #endif
888 
889 /************************************************************************/
890 /*                     VSIInstallLargeFileHandler()                     */
891 /************************************************************************/
892 
VSIInstallLargeFileHandler()893 void VSIInstallLargeFileHandler()
894 
895 {
896     VSIFileManager::InstallHandler( "", new VSIUnixStdioFilesystemHandler() );
897 }
898 
899 #endif  // ndef WIN32
900 
901 //! @endcond
902