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