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