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