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