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