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 : #include <fcntl.h>
55 : #include <sys/stat.h>
56 : #ifdef HAVE_STATVFS
57 : #include <sys/statvfs.h>
58 : #endif
59 : #include <sys/types.h>
60 : #if HAVE_UNISTD_H
61 : #include <unistd.h>
62 : #endif
63 : #ifdef HAVE_PREAD_BSD
64 : #include <sys/uio.h>
65 : #endif
66 :
67 : #if defined(__MACH__) && defined(__APPLE__)
68 : #define HAS_CASE_INSENSITIVE_FILE_SYSTEM
69 : #include <stdio.h>
70 : #include <stdlib.h>
71 : #include <limits.h>
72 : #endif
73 :
74 : #include <limits>
75 : #include <new>
76 :
77 : #include "cpl_config.h"
78 : #include "cpl_conv.h"
79 : #include "cpl_error.h"
80 : #include "cpl_multiproc.h"
81 : #include "cpl_string.h"
82 : #include "cpl_vsi_error.h"
83 :
84 : #if defined(UNIX_STDIO_64)
85 :
86 : #ifndef VSI_FTELL64
87 : #define VSI_FTELL64 ftell64
88 : #endif
89 : #ifndef VSI_FSEEK64
90 : #define VSI_FSEEK64 fseek64
91 : #endif
92 : #ifndef VSI_FOPEN64
93 : #define VSI_FOPEN64 fopen64
94 : #endif
95 : #ifndef VSI_STAT64
96 : #define VSI_STAT64 stat64
97 : #endif
98 : #ifndef VSI_STAT64_T
99 : #define VSI_STAT64_T stat64
100 : #endif
101 : #ifndef VSI_FTRUNCATE64
102 : #define VSI_FTRUNCATE64 ftruncate64
103 : #endif
104 :
105 : #else /* not UNIX_STDIO_64 */
106 :
107 : #ifndef VSI_FTELL64
108 : #define VSI_FTELL64 ftell
109 : #endif
110 : #ifndef VSI_FSEEK64
111 : #define VSI_FSEEK64 fseek
112 : #endif
113 : #ifndef VSI_FOPEN64
114 : #define VSI_FOPEN64 fopen
115 : #endif
116 : #ifndef VSI_STAT64
117 : #define VSI_STAT64 stat
118 : #endif
119 : #ifndef VSI_STAT64_T
120 : #define VSI_STAT64_T stat
121 : #endif
122 : #ifndef VSI_FTRUNCATE64
123 : #define VSI_FTRUNCATE64 ftruncate
124 : #endif
125 :
126 : #endif /* ndef UNIX_STDIO_64 */
127 :
128 : #ifndef BUILD_WITHOUT_64BIT_OFFSET
129 : // Ensure we have working 64 bit API
130 : static_assert(sizeof(VSI_FTELL64(stdout)) == sizeof(vsi_l_offset),
131 : "File API does not seem to support 64-bit offset. "
132 : "If you still want to build GDAL without > 4GB file support, "
133 : "add the -DBUILD_WITHOUT_64BIT_OFFSET define");
134 : static_assert(sizeof(VSIStatBufL::st_size) == sizeof(vsi_l_offset),
135 : "File API does not seem to support 64-bit file size. "
136 : "If you still want to build GDAL without > 4GB file support, "
137 : "add the -DBUILD_WITHOUT_64BIT_OFFSET define");
138 : #endif
139 :
140 : /************************************************************************/
141 : /* ==================================================================== */
142 : /* VSIUnixStdioFilesystemHandler */
143 : /* ==================================================================== */
144 : /************************************************************************/
145 :
146 : struct VSIDIRUnixStdio;
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 1694 : 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 :
167 : VSIVirtualHandle *
168 : CreateOnlyVisibleAtCloseTime(const char *pszFilename,
169 : bool bEmulationAllowed,
170 : CSLConstList papszOptions) override;
171 :
172 : int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
173 : int nFlags) override;
174 : int Unlink(const char *pszFilename) override;
175 : int Rename(const char *oldpath, const char *newpath, GDALProgressFunc,
176 : void *) override;
177 : int Mkdir(const char *pszDirname, long nMode) override;
178 : int Rmdir(const char *pszDirname) override;
179 : char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
180 : GIntBig GetDiskFreeSpace(const char *pszDirname) override;
181 : int SupportsSparseFiles(const char *pszPath) override;
182 :
183 : bool IsLocal(const char *pszPath) override;
184 : bool SupportsSequentialWrite(const char *pszPath,
185 : bool /* bAllowLocalTempFile */) override;
186 : bool SupportsRandomWrite(const char *pszPath,
187 : bool /* bAllowLocalTempFile */) override;
188 :
189 : VSIDIR *OpenDir(const char *pszPath, int nRecurseDepth,
190 : const char *const *papszOptions) override;
191 :
192 : static std::unique_ptr<VSIDIRUnixStdio>
193 : OpenDirInternal(const char *pszPath, int nRecurseDepth,
194 : const char *const *papszOptions);
195 :
196 : #ifdef HAS_CASE_INSENSITIVE_FILE_SYSTEM
197 : std::string
198 : GetCanonicalFilename(const std::string &osFilename) const override;
199 : #endif
200 :
201 : #ifdef VSI_COUNT_BYTES_READ
202 : void AddToTotal(vsi_l_offset nBytes);
203 : #endif
204 : };
205 :
206 : /************************************************************************/
207 : /* ==================================================================== */
208 : /* VSIUnixStdioHandle */
209 : /* ==================================================================== */
210 : /************************************************************************/
211 :
212 : class VSIUnixStdioHandle final : public VSIVirtualHandle
213 : {
214 : friend class VSIUnixStdioFilesystemHandler;
215 : CPL_DISALLOW_COPY_ASSIGN(VSIUnixStdioHandle)
216 :
217 : FILE *fp = nullptr;
218 : vsi_l_offset m_nOffset = 0;
219 : bool bReadOnly = true;
220 : bool bLastOpWrite = false;
221 : bool bLastOpRead = false;
222 : bool bAtEOF = false;
223 : bool bError = false;
224 : // In a+ mode, disable any optimization since the behavior of the file
225 : // pointer on Mac and other BSD system is to have a seek() to the end of
226 : // file and thus a call to our Seek(0, SEEK_SET) before a read will be a
227 : // no-op.
228 : bool bModeAppendReadWrite = false;
229 : #ifdef VSI_COUNT_BYTES_READ
230 : vsi_l_offset nTotalBytesRead = 0;
231 : VSIUnixStdioFilesystemHandler *poFS = nullptr;
232 : #endif
233 :
234 : bool m_bCancelCreation = false;
235 : std::string m_osFilenameToSetAtCloseTime{};
236 : #if !defined(__linux)
237 : std::string m_osTmpFilename{};
238 : #endif
239 :
240 : public:
241 : VSIUnixStdioHandle(VSIUnixStdioFilesystemHandler *poFSIn, FILE *fpIn,
242 : bool bReadOnlyIn, bool bModeAppendReadWriteIn);
243 : ~VSIUnixStdioHandle() override;
244 :
245 : int Seek(vsi_l_offset nOffsetIn, int nWhence) override;
246 : vsi_l_offset Tell() override;
247 : size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
248 : size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
249 : void ClearErr() override;
250 : int Eof() override;
251 : int Error() override;
252 : int Flush() override;
253 : int Close() override;
254 : int Truncate(vsi_l_offset nNewSize) override;
255 :
256 55 : void *GetNativeFileDescriptor() override
257 : {
258 55 : return reinterpret_cast<void *>(static_cast<uintptr_t>(fileno(fp)));
259 : }
260 :
261 : VSIRangeStatus GetRangeStatus(vsi_l_offset nOffset,
262 : vsi_l_offset nLength) override;
263 : #if defined(HAVE_PREAD64) || (defined(HAVE_PREAD_BSD) && SIZEOF_OFF_T == 8)
264 : bool HasPRead() const override;
265 : size_t PRead(void * /*pBuffer*/, size_t /* nSize */,
266 : vsi_l_offset /*nOffset*/) const override;
267 : #endif
268 :
269 443 : void CancelCreation() override
270 : {
271 443 : m_bCancelCreation = true;
272 443 : }
273 : };
274 :
275 : /************************************************************************/
276 : /* VSIUnixStdioHandle() */
277 : /************************************************************************/
278 :
279 124840 : VSIUnixStdioHandle::VSIUnixStdioHandle(
280 : #ifndef VSI_COUNT_BYTES_READ
281 : CPL_UNUSED
282 : #endif
283 : VSIUnixStdioFilesystemHandler *poFSIn,
284 124840 : FILE *fpIn, bool bReadOnlyIn, bool bModeAppendReadWriteIn)
285 : : fp(fpIn), bReadOnly(bReadOnlyIn),
286 124840 : bModeAppendReadWrite(bModeAppendReadWriteIn)
287 : #ifdef VSI_COUNT_BYTES_READ
288 : ,
289 : poFS(poFSIn)
290 : #endif
291 : {
292 124653 : }
293 :
294 : /************************************************************************/
295 : /* ~VSIUnixStdioHandle() */
296 : /************************************************************************/
297 :
298 248113 : VSIUnixStdioHandle::~VSIUnixStdioHandle()
299 : {
300 124102 : VSIUnixStdioHandle::Close();
301 248253 : }
302 :
303 : /************************************************************************/
304 : /* Close() */
305 : /************************************************************************/
306 :
307 252510 : int VSIUnixStdioHandle::Close()
308 :
309 : {
310 252510 : if (!fp)
311 127685 : return 0;
312 :
313 : VSIDebug1("VSIUnixStdioHandle::Close(%p)", fp);
314 :
315 : #ifdef VSI_COUNT_BYTES_READ
316 : poFS->AddToTotal(nTotalBytesRead);
317 : #endif
318 :
319 124825 : int ret = 0;
320 :
321 : #ifdef __linux
322 124825 : if (!m_bCancelCreation && !m_osFilenameToSetAtCloseTime.empty())
323 : {
324 1 : ret = fflush(fp);
325 1 : if (ret == 0)
326 : {
327 : // As advised by "man 2 open" if the caller doesn't have the
328 : // CAP_DAC_READ_SEARCH capability, which seems to be the default
329 :
330 : char szPath[32];
331 1 : const int fd = fileno(fp);
332 1 : snprintf(szPath, sizeof(szPath), "/proc/self/fd/%d", fd);
333 : ret =
334 1 : linkat(AT_FDCWD, szPath, AT_FDCWD,
335 : m_osFilenameToSetAtCloseTime.c_str(), AT_SYMLINK_FOLLOW);
336 1 : if (ret != 0)
337 0 : CPLDebug("CPL", "linkat() failed with errno=%d", errno);
338 : }
339 : }
340 : #endif
341 :
342 125420 : int ret2 = fclose(fp);
343 123990 : if (ret == 0 && ret2 != 0)
344 0 : ret = ret2;
345 :
346 : #if !defined(__linux)
347 : if (!m_osFilenameToSetAtCloseTime.empty())
348 : {
349 : if (m_bCancelCreation)
350 : {
351 : ret = unlink(m_osFilenameToSetAtCloseTime.c_str());
352 : }
353 : else
354 : {
355 : ret = rename(m_osTmpFilename.c_str(),
356 : m_osFilenameToSetAtCloseTime.c_str());
357 : }
358 : }
359 : #endif
360 :
361 123990 : fp = nullptr;
362 123990 : return ret;
363 : }
364 :
365 : /************************************************************************/
366 : /* Seek() */
367 : /************************************************************************/
368 :
369 3367760 : int VSIUnixStdioHandle::Seek(vsi_l_offset nOffsetIn, int nWhence)
370 : {
371 3367760 : bAtEOF = false;
372 :
373 : // Seeks that do nothing are still surprisingly expensive with MSVCRT.
374 : // try and short circuit if possible.
375 3367760 : if (!bModeAppendReadWrite && nWhence == SEEK_SET && nOffsetIn == m_nOffset)
376 713564 : return 0;
377 :
378 : // On a read-only file, we can avoid a lseek() system call to be issued
379 : // if the next position to seek to is within the buffered page.
380 2654200 : if (bReadOnly && nWhence == SEEK_SET)
381 : {
382 1855000 : const int l_PAGE_SIZE = 4096;
383 1855000 : if (nOffsetIn > m_nOffset && nOffsetIn < l_PAGE_SIZE + m_nOffset)
384 : {
385 64175 : const int nDiff = static_cast<int>(nOffsetIn - m_nOffset);
386 : // Do not zero-initialize the buffer. We don't read from it
387 : GByte abyTemp[l_PAGE_SIZE];
388 64175 : const int nRead = static_cast<int>(fread(abyTemp, 1, nDiff, fp));
389 64208 : if (nRead == nDiff)
390 : {
391 64163 : m_nOffset = nOffsetIn;
392 64163 : bLastOpWrite = false;
393 64163 : bLastOpRead = false;
394 64163 : return 0;
395 : }
396 : }
397 : }
398 :
399 : #if !defined(UNIX_STDIO_64) && SIZEOF_UNSIGNED_LONG == 4
400 : if (nOffsetIn > static_cast<vsi_l_offset>(std::numeric_limits<long>::max()))
401 : {
402 : CPLError(
403 : CE_Failure, CPLE_AppDefined,
404 : "Attempt at seeking beyond long extent. Lack of 64-bit file I/O");
405 : return -1;
406 : }
407 : #endif
408 :
409 2590070 : const int nResult = VSI_FSEEK64(fp, nOffsetIn, nWhence);
410 2589320 : const int nError = errno;
411 :
412 : #ifdef VSI_DEBUG
413 :
414 : if (nWhence == SEEK_SET)
415 : {
416 : VSIDebug3("VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB
417 : ",SEEK_SET) = %d",
418 : fp, nOffsetIn, nResult);
419 : }
420 : else if (nWhence == SEEK_END)
421 : {
422 : VSIDebug3("VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB
423 : ",SEEK_END) = %d",
424 : fp, nOffsetIn, nResult);
425 : }
426 : else if (nWhence == SEEK_CUR)
427 : {
428 : VSIDebug3("VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB
429 : ",SEEK_CUR) = %d",
430 : fp, nOffsetIn, nResult);
431 : }
432 : else
433 : {
434 : VSIDebug4("VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB
435 : ",%d-Unknown) = %d",
436 : fp, nOffsetIn, nWhence, nResult);
437 : }
438 :
439 : #endif
440 :
441 2589320 : if (nResult != -1)
442 : {
443 2589280 : if (nWhence == SEEK_SET)
444 : {
445 1992630 : m_nOffset = nOffsetIn;
446 : }
447 596651 : else if (nWhence == SEEK_END)
448 : {
449 516752 : m_nOffset = VSI_FTELL64(fp);
450 : }
451 79899 : else if (nWhence == SEEK_CUR)
452 : {
453 : if (nOffsetIn > INT_MAX)
454 : {
455 : // printf("likely negative offset intended\n");
456 : }
457 79804 : m_nOffset += nOffsetIn;
458 : }
459 : }
460 :
461 2589560 : bLastOpWrite = false;
462 2589560 : bLastOpRead = false;
463 :
464 2589560 : errno = nError;
465 2589560 : return nResult;
466 : }
467 :
468 : /************************************************************************/
469 : /* Tell() */
470 : /************************************************************************/
471 :
472 2322850 : vsi_l_offset VSIUnixStdioHandle::Tell()
473 :
474 : {
475 : #if 0
476 : const vsi_l_offset nOffset = VSI_FTELL64( fp );
477 : const int nError = errno;
478 :
479 : VSIDebug2( "VSIUnixStdioHandle::Tell(%p) = %ld",
480 : fp, static_cast<long>(nOffset) );
481 :
482 : errno = nError;
483 : #endif
484 :
485 2322850 : return m_nOffset;
486 : }
487 :
488 : /************************************************************************/
489 : /* Flush() */
490 : /************************************************************************/
491 :
492 36104 : int VSIUnixStdioHandle::Flush()
493 :
494 : {
495 : VSIDebug1("VSIUnixStdioHandle::Flush(%p)", fp);
496 :
497 36104 : return fflush(fp);
498 : }
499 :
500 : /************************************************************************/
501 : /* Read() */
502 : /************************************************************************/
503 :
504 7801470 : size_t VSIUnixStdioHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
505 :
506 : {
507 : /* -------------------------------------------------------------------- */
508 : /* If a fwrite() is followed by an fread(), the POSIX rules are */
509 : /* that some of the write may still be buffered and lost. We */
510 : /* are required to do a seek between to force flushing. So we */
511 : /* keep careful track of what happened last to know if we */
512 : /* skipped a flushing seek that we may need to do now. */
513 : /* -------------------------------------------------------------------- */
514 7801470 : if (!bModeAppendReadWrite && bLastOpWrite)
515 : {
516 2851 : if (VSI_FSEEK64(fp, m_nOffset, SEEK_SET) != 0)
517 : {
518 : VSIDebug1("Write calling seek failed. %d", m_nOffset);
519 : }
520 : }
521 :
522 : /* -------------------------------------------------------------------- */
523 : /* Perform the read. */
524 : /* -------------------------------------------------------------------- */
525 7801470 : const size_t nResult = fread(pBuffer, nSize, nCount, fp);
526 :
527 : #ifdef VSI_DEBUG
528 : const int nError = errno;
529 : VSIDebug4("VSIUnixStdioHandle::Read(%p,%ld,%ld) = %ld", fp,
530 : static_cast<long>(nSize), static_cast<long>(nCount),
531 : static_cast<long>(nResult));
532 : errno = nError;
533 : #endif
534 :
535 : /* -------------------------------------------------------------------- */
536 : /* Update current offset. */
537 : /* -------------------------------------------------------------------- */
538 :
539 : #ifdef VSI_COUNT_BYTES_READ
540 : nTotalBytesRead += nSize * nResult;
541 : #endif
542 :
543 7801210 : m_nOffset += nSize * nResult;
544 7801210 : bLastOpWrite = false;
545 7801210 : bLastOpRead = true;
546 :
547 7801210 : if (nResult != nCount)
548 : {
549 88283 : if (ferror(fp))
550 1037 : bError = true;
551 : else
552 : {
553 87243 : CPLAssert(feof(fp));
554 87243 : bAtEOF = true;
555 : }
556 :
557 88280 : errno = 0;
558 88280 : vsi_l_offset nNewOffset = VSI_FTELL64(fp);
559 88281 : if (errno == 0) // ftell() can fail if we are end of file with a pipe.
560 88280 : m_nOffset = nNewOffset;
561 : else
562 1 : CPLDebug("VSI", "%s", VSIStrerror(errno));
563 : }
564 :
565 7800960 : return nResult;
566 : }
567 :
568 : /************************************************************************/
569 : /* Write() */
570 : /************************************************************************/
571 :
572 2393240 : size_t VSIUnixStdioHandle::Write(const void *pBuffer, size_t nSize,
573 : size_t nCount)
574 :
575 : {
576 : /* -------------------------------------------------------------------- */
577 : /* If a fwrite() is followed by an fread(), the POSIX rules are */
578 : /* that some of the write may still be buffered and lost. We */
579 : /* are required to do a seek between to force flushing. So we */
580 : /* keep careful track of what happened last to know if we */
581 : /* skipped a flushing seek that we may need to do now. */
582 : /* -------------------------------------------------------------------- */
583 2393240 : if (!bModeAppendReadWrite && bLastOpRead)
584 : {
585 1850 : if (VSI_FSEEK64(fp, m_nOffset, SEEK_SET) != 0)
586 : {
587 : VSIDebug1("Write calling seek failed. %d", m_nOffset);
588 : }
589 : }
590 :
591 : /* -------------------------------------------------------------------- */
592 : /* Perform the write. */
593 : /* -------------------------------------------------------------------- */
594 2393240 : const size_t nResult = fwrite(pBuffer, nSize, nCount, fp);
595 :
596 : #if VSI_DEBUG
597 : const int nError = errno;
598 :
599 : VSIDebug4("VSIUnixStdioHandle::Write(%p,%ld,%ld) = %ld", fp,
600 : static_cast<long>(nSize), static_cast<long>(nCount),
601 : static_cast<long>(nResult));
602 :
603 : errno = nError;
604 : #endif
605 :
606 : /* -------------------------------------------------------------------- */
607 : /* Update current offset. */
608 : /* -------------------------------------------------------------------- */
609 2393240 : m_nOffset += nSize * nResult;
610 2393240 : bLastOpWrite = true;
611 2393240 : bLastOpRead = false;
612 :
613 2393240 : return nResult;
614 : }
615 :
616 : /************************************************************************/
617 : /* ClearErr() */
618 : /************************************************************************/
619 :
620 5431 : void VSIUnixStdioHandle::ClearErr()
621 :
622 : {
623 5431 : clearerr(fp);
624 5431 : bAtEOF = false;
625 5431 : bError = false;
626 5431 : }
627 :
628 : /************************************************************************/
629 : /* Error() */
630 : /************************************************************************/
631 :
632 58851 : int VSIUnixStdioHandle::Error()
633 :
634 : {
635 58851 : return bError ? TRUE : FALSE;
636 : }
637 :
638 : /************************************************************************/
639 : /* Eof() */
640 : /************************************************************************/
641 :
642 153882 : int VSIUnixStdioHandle::Eof()
643 :
644 : {
645 153882 : return bAtEOF ? TRUE : FALSE;
646 : }
647 :
648 : /************************************************************************/
649 : /* Truncate() */
650 : /************************************************************************/
651 :
652 546 : int VSIUnixStdioHandle::Truncate(vsi_l_offset nNewSize)
653 : {
654 546 : fflush(fp);
655 546 : return VSI_FTRUNCATE64(fileno(fp), nNewSize);
656 : }
657 :
658 : /************************************************************************/
659 : /* GetRangeStatus() */
660 : /************************************************************************/
661 :
662 : #ifdef __linux
663 : #if !defined(MISSING_LINUX_FS_H)
664 : #include <linux/fs.h> // FS_IOC_FIEMAP
665 : #endif
666 : #ifdef FS_IOC_FIEMAP
667 : #include <linux/types.h> // for types used in linux/fiemap.h
668 : #include <linux/fiemap.h> // struct fiemap
669 : #endif
670 : #include <sys/ioctl.h>
671 : #include <errno.h>
672 : #endif
673 :
674 506 : VSIRangeStatus VSIUnixStdioHandle::GetRangeStatus(vsi_l_offset
675 : #ifdef FS_IOC_FIEMAP
676 : nOffset
677 : #endif
678 : ,
679 : vsi_l_offset
680 : #ifdef FS_IOC_FIEMAP
681 : nLength
682 : #endif
683 : )
684 : {
685 : #ifdef FS_IOC_FIEMAP
686 : // fiemap IOCTL documented at
687 : // https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt
688 :
689 : // The fiemap struct contains a "variable length" array at its end
690 : // As we are interested in only one extent, we allocate the base size of
691 : // fiemap + one fiemap_extent.
692 : GByte abyBuffer[sizeof(struct fiemap) + sizeof(struct fiemap_extent)];
693 506 : int fd = fileno(fp);
694 506 : struct fiemap *psExtentMap = reinterpret_cast<struct fiemap *>(&abyBuffer);
695 506 : memset(psExtentMap, 0,
696 : sizeof(struct fiemap) + sizeof(struct fiemap_extent));
697 506 : psExtentMap->fm_start = nOffset;
698 506 : psExtentMap->fm_length = nLength;
699 506 : psExtentMap->fm_extent_count = 1;
700 506 : int ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
701 506 : if (ret < 0)
702 0 : return VSI_RANGE_STATUS_UNKNOWN;
703 506 : if (psExtentMap->fm_mapped_extents == 0)
704 2 : return VSI_RANGE_STATUS_HOLE;
705 : // In case there is one extent with unknown status, retry after having
706 : // asked the kernel to sync the file.
707 504 : const fiemap_extent *pasExtent = &(psExtentMap->fm_extents[0]);
708 504 : if (psExtentMap->fm_mapped_extents == 1 &&
709 504 : (pasExtent[0].fe_flags & FIEMAP_EXTENT_UNKNOWN) != 0)
710 : {
711 20 : psExtentMap->fm_flags = FIEMAP_FLAG_SYNC;
712 20 : psExtentMap->fm_start = nOffset;
713 20 : psExtentMap->fm_length = nLength;
714 20 : psExtentMap->fm_extent_count = 1;
715 20 : ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
716 20 : if (ret < 0)
717 0 : return VSI_RANGE_STATUS_UNKNOWN;
718 20 : if (psExtentMap->fm_mapped_extents == 0)
719 0 : return VSI_RANGE_STATUS_HOLE;
720 : }
721 504 : return VSI_RANGE_STATUS_DATA;
722 : #else
723 : static bool bMessageEmitted = false;
724 : if (!bMessageEmitted)
725 : {
726 : CPLDebug("VSI", "Sorry: GetExtentStatus() not implemented for "
727 : "this operating system");
728 : bMessageEmitted = true;
729 : }
730 : return VSI_RANGE_STATUS_UNKNOWN;
731 : #endif
732 : }
733 :
734 : /************************************************************************/
735 : /* HasPRead() */
736 : /************************************************************************/
737 :
738 : #if defined(HAVE_PREAD64) || (defined(HAVE_PREAD_BSD) && SIZEOF_OFF_T == 8)
739 724 : bool VSIUnixStdioHandle::HasPRead() const
740 : {
741 724 : return true;
742 : }
743 :
744 : /************************************************************************/
745 : /* PRead() */
746 : /************************************************************************/
747 :
748 24285 : size_t VSIUnixStdioHandle::PRead(void *pBuffer, size_t nSize,
749 : vsi_l_offset nOffset) const
750 : {
751 : #ifdef HAVE_PREAD64
752 24285 : return pread64(fileno(fp), pBuffer, nSize, nOffset);
753 : #else
754 : return pread(fileno(fp), pBuffer, nSize, static_cast<off_t>(nOffset));
755 : #endif
756 : }
757 : #endif
758 :
759 : /************************************************************************/
760 : /* ==================================================================== */
761 : /* VSIUnixStdioFilesystemHandler */
762 : /* ==================================================================== */
763 : /************************************************************************/
764 :
765 : #ifdef VSI_COUNT_BYTES_READ
766 : /************************************************************************/
767 : /* ~VSIUnixStdioFilesystemHandler() */
768 : /************************************************************************/
769 :
770 : VSIUnixStdioFilesystemHandler::~VSIUnixStdioFilesystemHandler()
771 : {
772 : CPLDebug(
773 : "VSI",
774 : "~VSIUnixStdioFilesystemHandler() : nTotalBytesRead = " CPL_FRMT_GUIB,
775 : nTotalBytesRead);
776 :
777 : if (hMutex != nullptr)
778 : CPLDestroyMutex(hMutex);
779 : hMutex = nullptr;
780 : }
781 : #endif
782 :
783 : /************************************************************************/
784 : /* Open() */
785 : /************************************************************************/
786 :
787 : VSIVirtualHandle *
788 185243 : VSIUnixStdioFilesystemHandler::Open(const char *pszFilename,
789 : const char *pszAccess, bool bSetError,
790 : CSLConstList /* papszOptions */)
791 :
792 : {
793 185243 : FILE *fp = VSI_FOPEN64(pszFilename, pszAccess);
794 184378 : const int nError = errno;
795 :
796 : VSIDebug3("VSIUnixStdioFilesystemHandler::Open(\"%s\",\"%s\") = %p",
797 : pszFilename, pszAccess, fp);
798 :
799 184378 : if (fp == nullptr)
800 : {
801 59905 : if (bSetError)
802 : {
803 13976 : VSIError(VSIE_FileError, "%s: %s", pszFilename, strerror(nError));
804 : }
805 60207 : errno = nError;
806 60207 : return nullptr;
807 : }
808 :
809 124473 : const bool bReadOnly =
810 124473 : strcmp(pszAccess, "rb") == 0 || strcmp(pszAccess, "r") == 0;
811 124473 : const bool bModeAppendReadWrite =
812 124473 : strcmp(pszAccess, "a+b") == 0 || strcmp(pszAccess, "a+") == 0;
813 : VSIUnixStdioHandle *poHandle = new (std::nothrow)
814 124473 : VSIUnixStdioHandle(this, fp, bReadOnly, bModeAppendReadWrite);
815 124565 : if (poHandle == nullptr)
816 : {
817 0 : fclose(fp);
818 0 : return nullptr;
819 : }
820 :
821 124565 : errno = nError;
822 :
823 : /* -------------------------------------------------------------------- */
824 : /* If VSI_CACHE is set we want to use a cached reader instead */
825 : /* of more direct io on the underlying file. */
826 : /* -------------------------------------------------------------------- */
827 124565 : if (bReadOnly && CPLTestBool(CPLGetConfigOption("VSI_CACHE", "FALSE")))
828 : {
829 5 : return VSICreateCachedFile(poHandle);
830 : }
831 :
832 124989 : return poHandle;
833 : }
834 :
835 : /************************************************************************/
836 : /* CreateOnlyVisibleAtCloseTime() */
837 : /************************************************************************/
838 :
839 91 : VSIVirtualHandle *VSIUnixStdioFilesystemHandler::CreateOnlyVisibleAtCloseTime(
840 : const char *pszFilename, bool bEmulationAllowed, CSLConstList papszOptions)
841 : {
842 : #ifdef __linux
843 20 : static bool bIsLinkatSupported = []()
844 : {
845 : // Check that /proc is accessible, since we will need it to run linkat()
846 : struct stat statbuf;
847 20 : return stat("/proc/self/fd", &statbuf) == 0;
848 91 : }();
849 :
850 : const int fd = bIsLinkatSupported
851 182 : ? open(CPLGetDirnameSafe(pszFilename).c_str(),
852 : O_TMPFILE | O_RDWR, 0666)
853 91 : : -1;
854 91 : if (fd < 0)
855 89 : return VSIFilesystemHandler::CreateOnlyVisibleAtCloseTime(
856 89 : pszFilename, bEmulationAllowed, papszOptions);
857 :
858 2 : FILE *fp = fdopen(fd, "wb+");
859 2 : if (!fp)
860 : {
861 0 : close(fd);
862 0 : return nullptr;
863 : }
864 :
865 : VSIUnixStdioHandle *poHandle = new (std::nothrow) VSIUnixStdioHandle(
866 2 : this, fp, /* bReadOnly = */ false, /* bModeAppendReadWrite = */ false);
867 2 : if (poHandle)
868 : {
869 2 : poHandle->m_osFilenameToSetAtCloseTime = pszFilename;
870 : }
871 2 : return poHandle;
872 : #else
873 : if (!bEmulationAllowed)
874 : return nullptr;
875 :
876 : std::string osTmpFilename = std::string(pszFilename).append("XXXXXX");
877 : int fd = mkstemp(osTmpFilename.data());
878 : if (fd < 0)
879 : {
880 : return VSIFilesystemHandler::CreateOnlyVisibleAtCloseTime(
881 : pszFilename, bEmulationAllowed, papszOptions);
882 : }
883 :
884 : FILE *fp = fdopen(fd, "wb+");
885 : if (!fp)
886 : {
887 : close(fd);
888 : return nullptr;
889 : }
890 :
891 : VSIUnixStdioHandle *poHandle = new (std::nothrow) VSIUnixStdioHandle(
892 : this, fp, /* bReadOnly = */ false, /* bModeAppendReadWrite = */ false);
893 : if (poHandle)
894 : {
895 : poHandle->m_osTmpFilename = std::move(osTmpFilename);
896 : poHandle->m_osFilenameToSetAtCloseTime = pszFilename;
897 : }
898 : return poHandle;
899 : #endif
900 : }
901 :
902 : /************************************************************************/
903 : /* Stat() */
904 : /************************************************************************/
905 :
906 231723 : int VSIUnixStdioFilesystemHandler::Stat(const char *pszFilename,
907 : VSIStatBufL *pStatBuf, int /* nFlags */)
908 : {
909 231723 : return (VSI_STAT64(pszFilename, pStatBuf));
910 : }
911 :
912 : /************************************************************************/
913 : /* Unlink() */
914 : /************************************************************************/
915 :
916 4648 : int VSIUnixStdioFilesystemHandler::Unlink(const char *pszFilename)
917 :
918 : {
919 4648 : return unlink(pszFilename);
920 : }
921 :
922 : /************************************************************************/
923 : /* Rename() */
924 : /************************************************************************/
925 :
926 812 : int VSIUnixStdioFilesystemHandler::Rename(const char *oldpath,
927 : const char *newpath, GDALProgressFunc,
928 : void *)
929 :
930 : {
931 812 : return rename(oldpath, newpath);
932 : }
933 :
934 : /************************************************************************/
935 : /* Mkdir() */
936 : /************************************************************************/
937 :
938 1230 : int VSIUnixStdioFilesystemHandler::Mkdir(const char *pszPathname, long nMode)
939 :
940 : {
941 1230 : return mkdir(pszPathname, static_cast<int>(nMode));
942 : }
943 :
944 : /************************************************************************/
945 : /* Rmdir() */
946 : /************************************************************************/
947 :
948 63 : int VSIUnixStdioFilesystemHandler::Rmdir(const char *pszPathname)
949 :
950 : {
951 63 : return rmdir(pszPathname);
952 : }
953 :
954 : /************************************************************************/
955 : /* ReadDirEx() */
956 : /************************************************************************/
957 :
958 22114 : char **VSIUnixStdioFilesystemHandler::ReadDirEx(const char *pszPath,
959 : int nMaxFiles)
960 :
961 : {
962 22114 : if (strlen(pszPath) == 0)
963 17 : pszPath = ".";
964 :
965 44226 : CPLStringList oDir;
966 22114 : DIR *hDir = opendir(pszPath);
967 22114 : if (hDir != nullptr)
968 : {
969 : // We want to avoid returning NULL for an empty list.
970 19811 : oDir.Assign(static_cast<char **>(CPLCalloc(2, sizeof(char *))));
971 :
972 19810 : struct dirent *psDirEntry = nullptr;
973 1819220 : while ((psDirEntry = readdir(hDir)) != nullptr)
974 : {
975 1799460 : oDir.AddString(psDirEntry->d_name);
976 1799410 : if (nMaxFiles > 0 && oDir.Count() > nMaxFiles)
977 5 : break;
978 : }
979 :
980 19818 : closedir(hDir);
981 : }
982 : else
983 : {
984 : // Should we generate an error?
985 : // For now we'll just return NULL (at the end of the function).
986 : }
987 :
988 44226 : return oDir.StealList();
989 : }
990 :
991 : /************************************************************************/
992 : /* GetDiskFreeSpace() */
993 : /************************************************************************/
994 :
995 3 : GIntBig VSIUnixStdioFilesystemHandler::GetDiskFreeSpace(const char *
996 : #ifdef HAVE_STATVFS
997 : pszDirname
998 : #endif
999 : )
1000 : {
1001 3 : GIntBig nRet = -1;
1002 : #ifdef HAVE_STATVFS
1003 :
1004 : #ifdef HAVE_STATVFS64
1005 : struct statvfs64 buf;
1006 3 : if (statvfs64(pszDirname, &buf) == 0)
1007 : {
1008 2 : nRet = static_cast<GIntBig>(buf.f_frsize *
1009 2 : static_cast<GUIntBig>(buf.f_bavail));
1010 : }
1011 : #else
1012 : struct statvfs buf;
1013 : if (statvfs(pszDirname, &buf) == 0)
1014 : {
1015 : nRet = static_cast<GIntBig>(buf.f_frsize *
1016 : static_cast<GUIntBig>(buf.f_bavail));
1017 : }
1018 : #endif
1019 :
1020 : #endif
1021 3 : return nRet;
1022 : }
1023 :
1024 : /************************************************************************/
1025 : /* SupportsSparseFiles() */
1026 : /************************************************************************/
1027 :
1028 : #ifdef __linux
1029 : #include <sys/vfs.h>
1030 : #endif
1031 :
1032 2 : int VSIUnixStdioFilesystemHandler::SupportsSparseFiles(const char *
1033 : #ifdef __linux
1034 : pszPath
1035 : #endif
1036 : )
1037 : {
1038 : #ifdef __linux
1039 : struct statfs sStatFS;
1040 2 : if (statfs(pszPath, &sStatFS) == 0)
1041 : {
1042 : // Add here any missing filesystem supporting sparse files.
1043 : // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
1044 2 : switch (static_cast<unsigned>(sStatFS.f_type))
1045 : {
1046 : // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
1047 2 : case 0xef53U: // ext2, 3, 4
1048 : case 0x52654973U: // reiser
1049 : case 0x58465342U: // xfs
1050 : case 0x3153464aU: // jfs
1051 : case 0x5346544eU: // ntfs
1052 : case 0x9123683eU: // brfs
1053 : // nfs: NFS < 4.2 supports creating sparse files (but reading them
1054 : // not efficiently).
1055 : case 0x6969U:
1056 : case 0x01021994U: // tmpfs
1057 2 : return TRUE;
1058 :
1059 0 : case 0x4d44U: // msdos
1060 0 : return FALSE;
1061 :
1062 0 : case 0x53464846U: // Windows Subsystem for Linux fs
1063 : {
1064 : static bool bUnknownFSEmitted = false;
1065 0 : if (!bUnknownFSEmitted)
1066 : {
1067 0 : CPLDebug("VSI",
1068 : "Windows Subsystem for Linux FS is at "
1069 : "the time of writing not known to support sparse "
1070 : "files");
1071 0 : bUnknownFSEmitted = true;
1072 : }
1073 0 : return FALSE;
1074 : }
1075 :
1076 0 : default:
1077 : {
1078 : static bool bUnknownFSEmitted = false;
1079 0 : if (!bUnknownFSEmitted)
1080 : {
1081 0 : CPLDebug("VSI",
1082 : "Filesystem with type %X unknown. "
1083 : "Assuming it does not support sparse files",
1084 0 : static_cast<int>(sStatFS.f_type));
1085 0 : bUnknownFSEmitted = true;
1086 : }
1087 0 : return FALSE;
1088 : }
1089 : }
1090 : }
1091 0 : return FALSE;
1092 : #else
1093 : static bool bMessageEmitted = false;
1094 : if (!bMessageEmitted)
1095 : {
1096 : CPLDebug("VSI", "Sorry: SupportsSparseFiles() not implemented "
1097 : "for this operating system");
1098 : bMessageEmitted = true;
1099 : }
1100 : return FALSE;
1101 : #endif
1102 : }
1103 :
1104 : /************************************************************************/
1105 : /* IsLocal() */
1106 : /************************************************************************/
1107 :
1108 150 : bool VSIUnixStdioFilesystemHandler::IsLocal(const char *
1109 : #ifdef __linux
1110 : pszPath
1111 : #endif
1112 : )
1113 : {
1114 : #ifdef __linux
1115 : struct statfs sStatFS;
1116 150 : if (statfs(pszPath, &sStatFS) == 0)
1117 : {
1118 : // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
1119 146 : switch (static_cast<unsigned>(sStatFS.f_type))
1120 : {
1121 : // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
1122 0 : case 0x6969U: // NFS
1123 : case 0x517bU: // SMB
1124 : case 0xff534d42U: // CIFS
1125 : case 0xfe534d42U: // SMB2
1126 : // (https://github.com/libuv/libuv/blob/97dcdb1926f6aca43171e1614338bcef067abd59/src/unix/fs.c#L960)
1127 0 : return false;
1128 : }
1129 : }
1130 : #else
1131 : static bool bMessageEmitted = false;
1132 : if (!bMessageEmitted)
1133 : {
1134 : CPLDebug("VSI", "Sorry: IsLocal() not implemented "
1135 : "for this operating system");
1136 : bMessageEmitted = true;
1137 : }
1138 : #endif
1139 150 : return true;
1140 : }
1141 :
1142 : /************************************************************************/
1143 : /* SupportsSequentialWrite() */
1144 : /************************************************************************/
1145 :
1146 130 : bool VSIUnixStdioFilesystemHandler::SupportsSequentialWrite(
1147 : const char *pszPath, bool /* bAllowLocalTempFile */)
1148 : {
1149 : VSIStatBufL sStat;
1150 130 : if (VSIStatL(pszPath, &sStat) == 0)
1151 62 : return access(pszPath, W_OK) == 0;
1152 68 : return access(CPLGetDirnameSafe(pszPath).c_str(), W_OK) == 0;
1153 : }
1154 :
1155 : /************************************************************************/
1156 : /* SupportsRandomWrite() */
1157 : /************************************************************************/
1158 :
1159 68 : bool VSIUnixStdioFilesystemHandler::SupportsRandomWrite(
1160 : const char *pszPath, bool /* bAllowLocalTempFile */)
1161 : {
1162 68 : return SupportsSequentialWrite(pszPath, false);
1163 : }
1164 :
1165 : /************************************************************************/
1166 : /* VSIDIRUnixStdio */
1167 : /************************************************************************/
1168 :
1169 : struct VSIDIRUnixStdio final : public VSIDIR
1170 : {
1171 : struct DIRCloser
1172 : {
1173 695 : void operator()(DIR *d)
1174 : {
1175 695 : if (d)
1176 695 : closedir(d);
1177 695 : }
1178 : };
1179 :
1180 : CPLString osRootPath{};
1181 : CPLString osBasePath{};
1182 : std::unique_ptr<DIR, DIRCloser> m_psDir{};
1183 : int nRecurseDepth = 0;
1184 : VSIDIREntry entry{};
1185 : std::vector<std::unique_ptr<VSIDIR>> aoStackSubDir{};
1186 : std::string m_osFilterPrefix{};
1187 : bool m_bNameAndTypeOnly = false;
1188 :
1189 : const VSIDIREntry *NextDirEntry() override;
1190 : };
1191 :
1192 : /************************************************************************/
1193 : /* OpenDirInternal() */
1194 : /************************************************************************/
1195 :
1196 : /* static */
1197 707 : std::unique_ptr<VSIDIRUnixStdio> VSIUnixStdioFilesystemHandler::OpenDirInternal(
1198 : const char *pszPath, int nRecurseDepth, const char *const *papszOptions)
1199 : {
1200 1414 : std::unique_ptr<DIR, VSIDIRUnixStdio::DIRCloser> psDir(opendir(pszPath));
1201 707 : if (psDir == nullptr)
1202 : {
1203 12 : return nullptr;
1204 : }
1205 1390 : auto dir = std::make_unique<VSIDIRUnixStdio>();
1206 695 : dir->osRootPath = pszPath;
1207 695 : dir->nRecurseDepth = nRecurseDepth;
1208 695 : dir->m_psDir = std::move(psDir);
1209 695 : dir->m_osFilterPrefix = CSLFetchNameValueDef(papszOptions, "PREFIX", "");
1210 695 : dir->m_bNameAndTypeOnly = CPLTestBool(
1211 : CSLFetchNameValueDef(papszOptions, "NAME_AND_TYPE_ONLY", "NO"));
1212 695 : return dir;
1213 : }
1214 :
1215 : /************************************************************************/
1216 : /* OpenDir() */
1217 : /************************************************************************/
1218 :
1219 185 : VSIDIR *VSIUnixStdioFilesystemHandler::OpenDir(const char *pszPath,
1220 : int nRecurseDepth,
1221 : const char *const *papszOptions)
1222 : {
1223 185 : return OpenDirInternal(pszPath, nRecurseDepth, papszOptions).release();
1224 : }
1225 :
1226 : /************************************************************************/
1227 : /* NextDirEntry() */
1228 : /************************************************************************/
1229 :
1230 19132 : const VSIDIREntry *VSIDIRUnixStdio::NextDirEntry()
1231 : {
1232 19132 : begin:
1233 19132 : if (VSI_ISDIR(entry.nMode) && nRecurseDepth != 0)
1234 : {
1235 1044 : CPLString osCurFile(osRootPath);
1236 522 : if (!osCurFile.empty())
1237 522 : osCurFile += '/';
1238 522 : osCurFile += entry.pszName;
1239 : auto subdir = VSIUnixStdioFilesystemHandler::OpenDirInternal(
1240 522 : osCurFile, nRecurseDepth - 1, nullptr);
1241 522 : if (subdir)
1242 : {
1243 522 : subdir->osRootPath = osRootPath;
1244 522 : subdir->osBasePath = entry.pszName;
1245 522 : subdir->m_osFilterPrefix = m_osFilterPrefix;
1246 522 : subdir->m_bNameAndTypeOnly = m_bNameAndTypeOnly;
1247 522 : aoStackSubDir.push_back(std::move(subdir));
1248 : }
1249 522 : entry.nMode = 0;
1250 : }
1251 :
1252 19654 : while (!aoStackSubDir.empty())
1253 : {
1254 9972 : auto l_entry = aoStackSubDir.back()->NextDirEntry();
1255 9972 : if (l_entry)
1256 : {
1257 9450 : return l_entry;
1258 : }
1259 522 : aoStackSubDir.pop_back();
1260 : }
1261 :
1262 11065 : while (const auto *psEntry = readdir(m_psDir.get()))
1263 : {
1264 : // Skip . and ..entries
1265 10377 : if (psEntry->d_name[0] == '.' &&
1266 1386 : (psEntry->d_name[1] == '\0' ||
1267 697 : (psEntry->d_name[1] == '.' && psEntry->d_name[2] == '\0')))
1268 : {
1269 : // do nothing
1270 : }
1271 : else
1272 : {
1273 8999 : CPLFree(entry.pszName);
1274 8999 : CPLString osName(osBasePath);
1275 8999 : if (!osName.empty())
1276 5603 : osName += '/';
1277 8999 : osName += psEntry->d_name;
1278 :
1279 8999 : entry.pszName = CPLStrdup(osName);
1280 8999 : entry.nMode = 0;
1281 8999 : entry.nSize = 0;
1282 8999 : entry.nMTime = 0;
1283 8999 : entry.bModeKnown = false;
1284 8999 : entry.bSizeKnown = false;
1285 8999 : entry.bMTimeKnown = false;
1286 :
1287 8999 : CPLString osCurFile(osRootPath);
1288 8999 : if (!osCurFile.empty())
1289 8999 : osCurFile += '/';
1290 8999 : osCurFile += entry.pszName;
1291 :
1292 : #if !defined(__sun) && !defined(__HAIKU__)
1293 8999 : if (psEntry->d_type == DT_REG)
1294 7376 : entry.nMode = S_IFREG;
1295 1623 : else if (psEntry->d_type == DT_DIR)
1296 567 : entry.nMode = S_IFDIR;
1297 1056 : else if (psEntry->d_type == DT_LNK)
1298 1056 : entry.nMode = S_IFLNK;
1299 : #endif
1300 :
1301 14110 : const auto StatFile = [&osCurFile, this]()
1302 : {
1303 : VSIStatBufL sStatL;
1304 7055 : if (VSIStatL(osCurFile, &sStatL) == 0)
1305 : {
1306 7055 : entry.nMode = sStatL.st_mode;
1307 7055 : entry.nSize = sStatL.st_size;
1308 7055 : entry.nMTime = sStatL.st_mtime;
1309 7055 : entry.bModeKnown = true;
1310 7055 : entry.bSizeKnown = true;
1311 7055 : entry.bMTimeKnown = true;
1312 : }
1313 7055 : };
1314 :
1315 9011 : if (!m_osFilterPrefix.empty() &&
1316 12 : m_osFilterPrefix.size() > osName.size())
1317 : {
1318 6 : if (STARTS_WITH(m_osFilterPrefix.c_str(), osName.c_str()) &&
1319 2 : m_osFilterPrefix[osName.size()] == '/')
1320 : {
1321 : #if !defined(__sun) && !defined(__HAIKU__)
1322 1 : if (psEntry->d_type == DT_UNKNOWN)
1323 : #endif
1324 : {
1325 0 : StatFile();
1326 : }
1327 1 : if (VSI_ISDIR(entry.nMode))
1328 : {
1329 1 : goto begin;
1330 : }
1331 : }
1332 3 : continue;
1333 : }
1334 9003 : if (!m_osFilterPrefix.empty() &&
1335 8 : !STARTS_WITH(osName.c_str(), m_osFilterPrefix.c_str()))
1336 : {
1337 2 : continue;
1338 : }
1339 :
1340 8993 : if (!m_bNameAndTypeOnly
1341 : #if !defined(__sun) && !defined(__HAIKU__)
1342 1938 : || psEntry->d_type == DT_UNKNOWN
1343 : #endif
1344 : )
1345 : {
1346 7055 : StatFile();
1347 : }
1348 :
1349 8993 : return &(entry);
1350 : }
1351 1383 : }
1352 :
1353 688 : return nullptr;
1354 : }
1355 :
1356 : #ifdef VSI_COUNT_BYTES_READ
1357 : /************************************************************************/
1358 : /* AddToTotal() */
1359 : /************************************************************************/
1360 :
1361 : void VSIUnixStdioFilesystemHandler::AddToTotal(vsi_l_offset nBytes)
1362 : {
1363 : CPLMutexHolder oHolder(&hMutex);
1364 : nTotalBytesRead += nBytes;
1365 : }
1366 :
1367 : #endif
1368 :
1369 : /************************************************************************/
1370 : /* GetCanonicalFilename() */
1371 : /************************************************************************/
1372 :
1373 : #ifdef HAS_CASE_INSENSITIVE_FILE_SYSTEM
1374 : std::string VSIUnixStdioFilesystemHandler::GetCanonicalFilename(
1375 : const std::string &osFilename) const
1376 : {
1377 : char szResolvedPath[PATH_MAX];
1378 : const char *pszFilename = osFilename.c_str();
1379 : if (realpath(pszFilename, szResolvedPath))
1380 : {
1381 : const char *pszFilenameLastPart = strrchr(pszFilename, '/');
1382 : const char *pszResolvedFilenameLastPart = strrchr(szResolvedPath, '/');
1383 : if (pszFilenameLastPart && pszResolvedFilenameLastPart &&
1384 : EQUAL(pszFilenameLastPart, pszResolvedFilenameLastPart))
1385 : {
1386 : std::string osRet;
1387 : osRet.assign(pszFilename, pszFilenameLastPart - pszFilename);
1388 : osRet += pszResolvedFilenameLastPart;
1389 : return osRet;
1390 : }
1391 : return szResolvedPath;
1392 : }
1393 : return osFilename;
1394 : }
1395 : #endif
1396 :
1397 : /************************************************************************/
1398 : /* VSIInstallLargeFileHandler() */
1399 : /************************************************************************/
1400 :
1401 1694 : void VSIInstallLargeFileHandler()
1402 :
1403 : {
1404 1694 : VSIFileManager::InstallHandler("", new VSIUnixStdioFilesystemHandler());
1405 1694 : }
1406 :
1407 : #endif // ndef WIN32
1408 :
1409 : //! @endcond
|