Line data Source code
1 : /**********************************************************************
2 : *
3 : * Project: CPL - Common Portability Library
4 : * Purpose: Implement VSI large file api for Unix platforms
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : **********************************************************************
8 : * Copyright (c) 2001, Frank Warmerdam
9 : * Copyright (c) 2010-2026, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************
13 : *
14 : * NB: Note that in wrappers we are always saving the error state (errno
15 : * variable) to avoid side effects during debug prints or other possible
16 : * standard function calls (error states will be overwritten after such
17 : * a call).
18 : *
19 : ****************************************************************************/
20 :
21 : //! @cond Doxygen_Suppress
22 :
23 : #if !defined(_WIN32)
24 :
25 : // #define VSI_COUNT_BYTES_READ
26 : // #define VSI_DEBUG
27 :
28 : // Some unusual filesystems do not work if _FORTIFY_SOURCE in GCC or
29 : // clang is used within this source file, especially if techniques
30 : // like those in vsipreload are used. Fortify source interacts poorly with
31 : // filesystems that use fread for forward seeks. This leads to SIGSEGV within
32 : // fread calls.
33 : //
34 : // See this for hardening background info: https://wiki.debian.org/Hardening
35 : #undef _FORTIFY_SOURCE
36 :
37 : // 64 bit IO is only available on 32-bit android since API 24 / Android 7.0
38 : // See
39 : // https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md
40 : #if !defined(__ANDROID_API__) || __ANDROID_API__ >= 24
41 : #define _FILE_OFFSET_BITS 64
42 : #endif
43 :
44 : #include "cpl_port.h"
45 :
46 : #include "cpl_vsi.h"
47 : #include "cpl_vsi_virtual.h"
48 :
49 : #include <cinttypes>
50 : #include <cstddef>
51 : #include <cstdio>
52 : #include <cstring>
53 : #include <dirent.h>
54 : #include <errno.h>
55 : #include <fcntl.h>
56 : #include <sys/stat.h>
57 : #ifdef HAVE_STATVFS
58 : #include <sys/statvfs.h>
59 : #endif
60 : #include <sys/types.h>
61 : #if HAVE_UNISTD_H
62 : #include <unistd.h>
63 : #endif
64 : #ifdef HAVE_PREAD_BSD
65 : #include <sys/uio.h>
66 : #endif
67 :
68 : #if defined(__MACH__) && defined(__APPLE__)
69 : #define HAS_CASE_INSENSITIVE_FILE_SYSTEM
70 : #include <stdio.h>
71 : #include <stdlib.h>
72 : #include <limits.h>
73 : #endif
74 :
75 : #include <algorithm>
76 : #include <array>
77 : #include <limits>
78 : #include <new>
79 :
80 : #include "cpl_config.h"
81 : #include "cpl_conv.h"
82 : #include "cpl_error.h"
83 : #include "cpl_multiproc.h"
84 : #include "cpl_string.h"
85 : #include "cpl_vsi_error.h"
86 :
87 : #if defined(UNIX_STDIO_64)
88 :
89 : #ifndef VSI_OPEN64
90 : #define VSI_OPEN64 open64
91 : #endif
92 : #ifndef VSI_LSEEK64
93 : #define VSI_LSEEK64 lseek64
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_OPEN64
108 : #define VSI_OPEN64 open
109 : #endif
110 : #ifndef VSI_LSEEK64
111 : #define VSI_LSEEK64 lseek
112 : #endif
113 : #ifndef VSI_STAT64
114 : #define VSI_STAT64 stat
115 : #endif
116 : #ifndef VSI_STAT64_T
117 : #define VSI_STAT64_T stat
118 : #endif
119 : #ifndef VSI_FTRUNCATE64
120 : #define VSI_FTRUNCATE64 ftruncate
121 : #endif
122 :
123 : #endif /* ndef UNIX_STDIO_64 */
124 :
125 : #ifndef BUILD_WITHOUT_64BIT_OFFSET
126 : // Ensure we have working 64 bit API
127 : static_assert(sizeof(VSI_LSEEK64(0, 0, SEEK_CUR)) == sizeof(vsi_l_offset),
128 : "File API does not seem to support 64-bit offset. "
129 : "If you still want to build GDAL without > 4GB file support, "
130 : "add the -DBUILD_WITHOUT_64BIT_OFFSET define");
131 : static_assert(sizeof(VSIStatBufL::st_size) == sizeof(vsi_l_offset),
132 : "File API does not seem to support 64-bit file size. "
133 : "If you still want to build GDAL without > 4GB file support, "
134 : "add the -DBUILD_WITHOUT_64BIT_OFFSET define");
135 : #endif
136 :
137 : /************************************************************************/
138 : /* ==================================================================== */
139 : /* VSIUnixStdioFilesystemHandler */
140 : /* ==================================================================== */
141 : /************************************************************************/
142 :
143 : struct VSIDIRUnixStdio;
144 :
145 : class VSIUnixStdioFilesystemHandler final : public VSIFilesystemHandler
146 : {
147 : CPL_DISALLOW_COPY_ASSIGN(VSIUnixStdioFilesystemHandler)
148 :
149 : #ifdef VSI_COUNT_BYTES_READ
150 : vsi_l_offset nTotalBytesRead = 0;
151 : CPLMutex *hMutex = nullptr;
152 : #endif
153 :
154 : public:
155 2101 : VSIUnixStdioFilesystemHandler() = default;
156 : #ifdef VSI_COUNT_BYTES_READ
157 : ~VSIUnixStdioFilesystemHandler() override;
158 : #endif
159 :
160 : VSIVirtualHandleUniquePtr Open(const char *pszFilename,
161 : const char *pszAccess, bool bSetError,
162 : CSLConstList /* papszOptions */) override;
163 :
164 : VSIVirtualHandleUniquePtr
165 : CreateOnlyVisibleAtCloseTime(const char *pszFilename,
166 : bool bEmulationAllowed,
167 : CSLConstList papszOptions) override;
168 :
169 : int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
170 : int nFlags) override;
171 : int Unlink(const char *pszFilename) override;
172 : int Rename(const char *oldpath, const char *newpath, GDALProgressFunc,
173 : void *) override;
174 : int Mkdir(const char *pszDirname, long nMode) override;
175 : int Rmdir(const char *pszDirname) override;
176 : char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
177 : GIntBig GetDiskFreeSpace(const char *pszDirname) override;
178 : int SupportsSparseFiles(const char *pszPath) override;
179 :
180 : bool IsLocal(const char *pszPath) const override;
181 : bool SupportsSequentialWrite(const char *pszPath,
182 : bool /* bAllowLocalTempFile */) override;
183 : bool SupportsRandomWrite(const char *pszPath,
184 : bool /* bAllowLocalTempFile */) override;
185 :
186 : VSIDIR *OpenDir(const char *pszPath, int nRecurseDepth,
187 : const char *const *papszOptions) override;
188 :
189 : static std::unique_ptr<VSIDIRUnixStdio>
190 : OpenDirInternal(const char *pszPath, int nRecurseDepth,
191 : const char *const *papszOptions);
192 :
193 : #ifdef HAS_CASE_INSENSITIVE_FILE_SYSTEM
194 : std::string
195 : GetCanonicalFilename(const std::string &osFilename) const override;
196 : #endif
197 :
198 : #ifdef VSI_COUNT_BYTES_READ
199 : void AddToTotal(vsi_l_offset nBytes);
200 : #endif
201 : };
202 :
203 : /************************************************************************/
204 : /* ==================================================================== */
205 : /* VSIUnixStdioHandle */
206 : /* ==================================================================== */
207 : /************************************************************************/
208 :
209 : class VSIUnixStdioHandle final : public VSIVirtualHandle
210 : {
211 : public:
212 : enum class AccessMode : unsigned char
213 : {
214 : NORMAL,
215 : READ_ONLY,
216 : WRITE_ONLY,
217 : // In a+ mode, disable any optimization since the behavior of the file
218 : // pointer on Mac and other BSD system is to have a seek() to the end of
219 : // file and thus a call to our Seek(0, SEEK_SET) before a read will be a
220 : // no-op.
221 : APPEND_READ_WRITE,
222 : };
223 :
224 : private:
225 : friend class VSIUnixStdioFilesystemHandler;
226 : CPL_DISALLOW_COPY_ASSIGN(VSIUnixStdioHandle)
227 :
228 : int fd = -1;
229 : vsi_l_offset m_nFilePos = 0; // Logical/user position
230 : static constexpr size_t BUFFER_SIZE = 4096;
231 : std::array<GByte, BUFFER_SIZE> m_abyBuffer{};
232 : // For read operations, current position within m_abyBuffer
233 : // This implies that m_nFilePos - m_nBufferCurPos + m_nBufferSize is the
234 : //current real/kernel file position.
235 : size_t m_nBufferCurPos = 0; // Current position within m_abyBuffer
236 : // Number of valid bytes in m_abyBuffer (both for read and write buffering)
237 : size_t m_nBufferSize = 0;
238 : bool m_bBufferDirty = false;
239 : const AccessMode eAccessMode;
240 : enum class Operation : unsigned char
241 : {
242 : NONE,
243 : READ,
244 : WRITE
245 : };
246 : Operation eLastOp = Operation::NONE;
247 : bool bAtEOF = false;
248 : bool bError = false;
249 : #ifdef VSI_COUNT_BYTES_READ
250 : vsi_l_offset nTotalBytesRead = 0;
251 : VSIUnixStdioFilesystemHandler *poFS = nullptr;
252 : #endif
253 :
254 : std::string m_osFilename{};
255 : #if defined(__linux)
256 : bool m_bUnlinkedFile = false;
257 : bool m_bCancelCreation = false;
258 : #else
259 : std::string m_osTmpFilename{};
260 : #endif
261 :
262 : public:
263 : VSIUnixStdioHandle(VSIUnixStdioFilesystemHandler *poFSIn, int fdIn,
264 : AccessMode eAccessModeIn);
265 : ~VSIUnixStdioHandle() override;
266 :
267 : int Seek(vsi_l_offset nOffsetIn, int nWhence) override;
268 : vsi_l_offset Tell() override;
269 : size_t Read(void *pBuffer, size_t nBytes) override;
270 : size_t Write(const void *pBuffer, size_t nBytes) override;
271 : void ClearErr() override;
272 : int Eof() override;
273 : int Error() override;
274 : int Flush() override;
275 : int Close() override;
276 : int Truncate(vsi_l_offset nNewSize) override;
277 :
278 55 : void *GetNativeFileDescriptor() override
279 : {
280 55 : return reinterpret_cast<void *>(static_cast<uintptr_t>(fd));
281 : }
282 :
283 : VSIRangeStatus GetRangeStatus(vsi_l_offset nOffset,
284 : vsi_l_offset nLength) override;
285 : #if defined(HAVE_PREAD64) || (defined(HAVE_PREAD_BSD) && SIZEOF_OFF_T == 8)
286 : bool HasPRead() const override;
287 : size_t PRead(void * /*pBuffer*/, size_t /* nSize */,
288 : vsi_l_offset /*nOffset*/) const override;
289 : #endif
290 :
291 : void CancelCreation() override;
292 : };
293 :
294 : /************************************************************************/
295 : /* VSIUnixStdioHandle() */
296 : /************************************************************************/
297 :
298 147411 : VSIUnixStdioHandle::VSIUnixStdioHandle(
299 : #ifndef VSI_COUNT_BYTES_READ
300 : CPL_UNUSED
301 : #endif
302 : VSIUnixStdioFilesystemHandler *poFSIn,
303 147411 : int fdIn, AccessMode eAccessModeIn)
304 147411 : : fd(fdIn), eAccessMode(eAccessModeIn)
305 : #ifdef VSI_COUNT_BYTES_READ
306 : ,
307 : poFS(poFSIn)
308 : #endif
309 : {
310 147366 : }
311 :
312 : /************************************************************************/
313 : /* ~VSIUnixStdioHandle() */
314 : /************************************************************************/
315 :
316 293892 : VSIUnixStdioHandle::~VSIUnixStdioHandle()
317 : {
318 146876 : VSIUnixStdioHandle::Close();
319 294006 : }
320 :
321 : /************************************************************************/
322 : /* Close() */
323 : /************************************************************************/
324 :
325 297824 : int VSIUnixStdioHandle::Close()
326 :
327 : {
328 297824 : if (fd < 0)
329 150585 : return 0;
330 :
331 : VSIDebug1("VSIUnixStdioHandle::Close(%d)", fd);
332 :
333 : #ifdef VSI_COUNT_BYTES_READ
334 : poFS->AddToTotal(nTotalBytesRead);
335 : #endif
336 :
337 147239 : int ret = VSIUnixStdioHandle::Flush();
338 :
339 : #ifdef __linux
340 293067 : if (ret == 0 && !m_bCancelCreation && !m_osFilename.empty() &&
341 146008 : m_bUnlinkedFile)
342 : {
343 3 : ret = fsync(fd);
344 3 : if (ret == 0)
345 : {
346 : // As advised by "man 2 open" if the caller doesn't have the
347 : // CAP_DAC_READ_SEARCH capability, which seems to be the default
348 :
349 : char szPath[32];
350 3 : snprintf(szPath, sizeof(szPath), "/proc/self/fd/%d", fd);
351 3 : ret = linkat(AT_FDCWD, szPath, AT_FDCWD, m_osFilename.c_str(),
352 : AT_SYMLINK_FOLLOW);
353 3 : if (ret != 0)
354 0 : CPLDebug("CPL", "linkat() failed with errno=%d", errno);
355 : }
356 : }
357 : #endif
358 :
359 147479 : int ret2 = close(fd);
360 146791 : if (ret == 0 && ret2 != 0)
361 0 : ret = ret2;
362 :
363 : #if !defined(__linux)
364 : if (!m_osTmpFilename.empty() && !m_osFilename.empty())
365 : {
366 : ret = rename(m_osTmpFilename.c_str(), m_osFilename.c_str());
367 : }
368 : #endif
369 :
370 146791 : fd = -1;
371 146791 : return ret;
372 : }
373 :
374 : /************************************************************************/
375 : /* CancelCreation() */
376 : /************************************************************************/
377 :
378 550 : void VSIUnixStdioHandle::CancelCreation()
379 : {
380 : #if defined(__linux)
381 550 : if (!m_osFilename.empty() && !m_bUnlinkedFile)
382 : {
383 482 : unlink(m_osFilename.c_str());
384 482 : m_osFilename.clear();
385 : }
386 68 : else if (m_bUnlinkedFile)
387 65 : m_bCancelCreation = true;
388 : #else
389 : if (!m_osTmpFilename.empty())
390 : {
391 : unlink(m_osTmpFilename.c_str());
392 : m_osTmpFilename.clear();
393 : }
394 : else if (!m_osFilename.empty())
395 : {
396 : unlink(m_osFilename.c_str());
397 : m_osFilename.clear();
398 : }
399 : #endif
400 550 : }
401 :
402 : /************************************************************************/
403 : /* Seek() */
404 : /************************************************************************/
405 :
406 4925220 : int VSIUnixStdioHandle::Seek(vsi_l_offset nOffsetIn, int nWhence)
407 : {
408 4925220 : bAtEOF = false;
409 4925220 : bError = false;
410 :
411 : // Seeks that do nothing are still surprisingly expensive with MSVCRT.
412 : // try and short circuit if possible.
413 4925220 : if (eAccessMode != AccessMode::APPEND_READ_WRITE && nWhence == SEEK_SET &&
414 4291250 : nOffsetIn == m_nFilePos)
415 759915 : return 0;
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 4165300 : if (eLastOp == Operation::WRITE)
428 : {
429 298837 : eLastOp = Operation::NONE;
430 :
431 298837 : if (m_bBufferDirty && Flush() < 0)
432 0 : return -1;
433 :
434 298837 : const auto nNewPos = VSI_LSEEK64(fd, nOffsetIn, nWhence);
435 298837 : if (nNewPos < 0)
436 : {
437 0 : bError = true;
438 0 : return -1;
439 : }
440 :
441 298837 : m_nFilePos = nNewPos;
442 298837 : m_nBufferCurPos = 0;
443 298837 : m_nBufferSize = 0;
444 : }
445 :
446 : else
447 : {
448 3866470 : eLastOp = Operation::READ;
449 :
450 : vsi_l_offset nTargetPos;
451 :
452 : // Compute absolute target position
453 3866470 : switch (nWhence)
454 : {
455 3435170 : case SEEK_SET:
456 3435170 : nTargetPos = nOffsetIn;
457 3435170 : break;
458 83387 : case SEEK_CUR:
459 83387 : nTargetPos = m_nFilePos + nOffsetIn;
460 83387 : break;
461 347727 : case SEEK_END:
462 : {
463 347727 : const auto nNewPos = VSI_LSEEK64(fd, nOffsetIn, SEEK_END);
464 347774 : if (nNewPos < 0)
465 : {
466 0 : bError = true;
467 0 : return -1;
468 : }
469 347774 : nTargetPos = nNewPos;
470 347774 : break;
471 : }
472 187 : default:
473 187 : return -1;
474 : }
475 :
476 : // Compute absolute position of the current buffer
477 3866330 : const auto nBufStartOffset = m_nFilePos - m_nBufferCurPos;
478 3866330 : const auto nBufEndOffset = nBufStartOffset + m_nBufferSize;
479 :
480 3866330 : if (nTargetPos >= nBufStartOffset && nTargetPos < nBufEndOffset)
481 : {
482 : // Seek inside current buffer - adjust buffer pos only
483 2796700 : m_nBufferCurPos = static_cast<size_t>(nTargetPos - nBufStartOffset);
484 2796700 : m_nFilePos = nTargetPos;
485 : }
486 : else
487 : {
488 : // Outside buffer, do real seek and invalidate buffer
489 1069630 : const auto nNewPos = VSI_LSEEK64(fd, nTargetPos, SEEK_SET);
490 1069740 : if (nNewPos < 0)
491 : {
492 1 : bError = true;
493 1 : return -1;
494 : }
495 :
496 1069740 : m_nFilePos = nNewPos;
497 1069740 : m_nBufferCurPos = 0;
498 1069740 : m_nBufferSize = 0;
499 : }
500 : }
501 :
502 : VSIDebug4("VSIUnixStdioHandle::Seek(%d," CPL_FRMT_GUIB ", %d) = %" PRIu64,
503 : fd, nOffsetIn, nWhence, static_cast<uint64_t>(m_nFilePos));
504 :
505 4165280 : return 0;
506 : }
507 :
508 : /************************************************************************/
509 : /* Tell() */
510 : /************************************************************************/
511 :
512 3792920 : vsi_l_offset VSIUnixStdioHandle::Tell()
513 :
514 : {
515 3792920 : return m_nFilePos;
516 : }
517 :
518 : /************************************************************************/
519 : /* Flush() */
520 : /************************************************************************/
521 :
522 472870 : int VSIUnixStdioHandle::Flush()
523 :
524 : {
525 : VSIDebug1("VSIUnixStdioHandle::Flush(%d)", fd);
526 :
527 472870 : if (m_bBufferDirty)
528 : {
529 346942 : m_bBufferDirty = false;
530 :
531 : // Sync kernel offset to our logical position before writing
532 346942 : if (VSI_LSEEK64(fd, m_nFilePos - m_nBufferSize, SEEK_SET) < 0)
533 : {
534 0 : return EOF;
535 : }
536 :
537 346942 : size_t nOff = 0;
538 693884 : while (m_nBufferSize > 0)
539 : {
540 346942 : errno = 0;
541 : const auto nWritten =
542 346942 : write(fd, m_abyBuffer.data() + nOff, m_nBufferSize);
543 346942 : if (nWritten < 0)
544 : {
545 0 : if (errno == EINTR)
546 0 : continue;
547 0 : return EOF;
548 : }
549 346942 : CPLAssert(static_cast<size_t>(nWritten) <= m_nBufferSize);
550 346942 : nOff += nWritten;
551 346942 : m_nBufferSize -= nWritten;
552 : }
553 : }
554 :
555 472870 : return 0;
556 : }
557 :
558 : /************************************************************************/
559 : /* Read() */
560 : /************************************************************************/
561 :
562 9631760 : size_t VSIUnixStdioHandle::Read(void *pBuffer, size_t nBytes)
563 :
564 : {
565 9631760 : if (nBytes == 0 || bAtEOF)
566 41115 : return 0;
567 9590650 : if (eAccessMode == AccessMode::WRITE_ONLY)
568 : {
569 1 : bError = true;
570 1 : errno = EINVAL;
571 1 : return 0;
572 : }
573 :
574 : /* -------------------------------------------------------------------- */
575 : /* If a fwrite() is followed by an fread(), the POSIX rules are */
576 : /* that some of the write may still be buffered and lost. We */
577 : /* are required to do a seek between to force flushing. So we */
578 : /* keep careful track of what happened last to know if we */
579 : /* skipped a flushing seek that we may need to do now. */
580 : /* -------------------------------------------------------------------- */
581 9590640 : if (eLastOp == Operation::WRITE)
582 : {
583 2854 : if (m_bBufferDirty && Flush() < 0)
584 : {
585 0 : bError = true;
586 0 : return -1;
587 : }
588 2854 : if (VSI_LSEEK64(fd, m_nFilePos, SEEK_SET) < 0)
589 : {
590 0 : bError = true;
591 0 : return -1;
592 : }
593 2854 : m_nBufferCurPos = 0;
594 2854 : m_nBufferSize = 0;
595 : }
596 :
597 9590640 : const size_t nTotal = nBytes;
598 9590640 : size_t nBytesRead = 0;
599 9590640 : GByte *const pabyDest = static_cast<GByte *>(pBuffer);
600 :
601 19212100 : while (nBytesRead < nTotal)
602 : {
603 9694800 : size_t nAvailInBuffer = m_nBufferSize - m_nBufferCurPos;
604 :
605 : // If buffer is empty
606 9694800 : if (nAvailInBuffer == 0)
607 : {
608 917589 : size_t nRemaining = nTotal - nBytesRead;
609 917589 : if (nRemaining >= BUFFER_SIZE)
610 : {
611 : // Bypass buffer if large request
612 75303 : errno = 0;
613 : const ssize_t nBytesReadThisTime =
614 75303 : read(fd, pabyDest + nBytesRead, nRemaining);
615 75302 : if (nBytesReadThisTime <= 0)
616 : {
617 14759 : if (nBytesReadThisTime == 0)
618 14754 : bAtEOF = true;
619 5 : else if (errno == EINTR)
620 0 : continue;
621 : else
622 5 : bError = true;
623 73273 : break;
624 : }
625 60543 : m_nFilePos += nBytesReadThisTime;
626 60543 : nBytesRead += nBytesReadThisTime;
627 60543 : m_nBufferCurPos = 0;
628 60543 : m_nBufferSize = 0;
629 : }
630 : else
631 : {
632 : // Small request: Refill internal buffer
633 842286 : errno = 0;
634 : const ssize_t nBytesReadThisTime =
635 842286 : read(fd, m_abyBuffer.data(), BUFFER_SIZE);
636 842087 : if (nBytesReadThisTime <= 0)
637 : {
638 58514 : if (nBytesReadThisTime == 0)
639 57325 : bAtEOF = true;
640 1189 : else if (errno == EINTR)
641 0 : continue;
642 : else
643 1189 : bError = true;
644 58514 : break;
645 : }
646 783573 : m_nBufferCurPos = 0;
647 783573 : m_nBufferSize = nBytesReadThisTime;
648 783573 : nAvailInBuffer = nBytesReadThisTime;
649 : }
650 : }
651 :
652 : // Copy from buffer to user
653 9621330 : const size_t nToCopy = std::min(nTotal - nBytesRead, nAvailInBuffer);
654 9621300 : memcpy(pabyDest + nBytesRead, m_abyBuffer.data() + m_nBufferCurPos,
655 : nToCopy);
656 9621150 : nBytesRead += nToCopy;
657 9621150 : m_nBufferCurPos += nToCopy;
658 9621150 : m_nFilePos += nToCopy;
659 :
660 9621150 : CPLAssert(m_nFilePos - m_nBufferCurPos + m_nBufferSize ==
661 : static_cast<uint64_t>(VSI_LSEEK64(fd, 0, SEEK_CUR)));
662 : }
663 :
664 : #ifdef VSI_DEBUG
665 : const int nError = errno;
666 : VSIDebug4("VSIUnixStdioHandle::Read(%d,%%ld) = %" PRId64, fd,
667 : static_cast<long>(nBytes), static_cast<int64_t>(nBytesRead));
668 : errno = nError;
669 : #endif
670 :
671 9590530 : eLastOp = Operation::READ;
672 :
673 : #ifdef VSI_COUNT_BYTES_READ
674 : nTotalBytesRead += nBytesRead;
675 : #endif
676 :
677 9590530 : return nBytesRead;
678 : }
679 :
680 : /************************************************************************/
681 : /* Write() */
682 : /************************************************************************/
683 :
684 2464650 : size_t VSIUnixStdioHandle::Write(const void *pBuffer, size_t nBytes)
685 :
686 : {
687 2464650 : if (nBytes == 0)
688 22958 : return 0;
689 2441690 : if (eAccessMode == AccessMode::READ_ONLY)
690 : {
691 2 : bError = true;
692 2 : errno = EINVAL;
693 2 : return 0;
694 : }
695 :
696 : /* -------------------------------------------------------------------- */
697 : /* If a fwrite() is followed by an fread(), the POSIX rules are */
698 : /* that some of the write may still be buffered and lost. We */
699 : /* are required to do a seek between to force flushing. So we */
700 : /* keep careful track of what happened last to know if we */
701 : /* skipped a flushing seek that we may need to do now. */
702 : /* -------------------------------------------------------------------- */
703 2441690 : if (eLastOp == Operation::READ)
704 : {
705 43535 : if (VSI_LSEEK64(fd, m_nFilePos, SEEK_SET) < 0)
706 : {
707 0 : bError = true;
708 0 : return -1;
709 : }
710 43535 : m_nBufferCurPos = 0;
711 43535 : m_nBufferSize = 0;
712 : }
713 :
714 2441690 : const size_t nTotal = nBytes;
715 2441690 : size_t nBytesWritten = 0;
716 2441690 : const GByte *const pabySrc = static_cast<const GByte *>(pBuffer);
717 4898050 : while (nBytesWritten < nTotal)
718 : {
719 2456350 : const size_t nRemaining = nTotal - nBytesWritten;
720 :
721 : // Bypass buffer if request > BUFFER_SIZE
722 2456350 : if (m_nBufferSize == 0 && nRemaining >= BUFFER_SIZE)
723 : {
724 : const auto nWritten =
725 10894 : write(fd, pabySrc + nBytesWritten, nRemaining);
726 10894 : if (nWritten < 0)
727 : {
728 0 : bError = true;
729 0 : break;
730 : }
731 10894 : CPLAssert(static_cast<size_t>(nWritten) <= nRemaining);
732 10894 : m_nFilePos += nWritten;
733 10894 : nBytesWritten += nWritten;
734 10894 : continue;
735 : }
736 :
737 : // Small request: Fill internal buffer
738 2445460 : const size_t nFreeSpaceInBuffer = BUFFER_SIZE - m_nBufferSize;
739 2445460 : const size_t nToCopy = std::min(nRemaining, nFreeSpaceInBuffer);
740 2445460 : memcpy(m_abyBuffer.data() + m_nBufferSize, pabySrc + nBytesWritten,
741 : nToCopy);
742 :
743 2445460 : nBytesWritten += nToCopy;
744 2445460 : m_nBufferSize += nToCopy;
745 2445460 : m_nFilePos += nToCopy;
746 2445460 : m_bBufferDirty = true;
747 :
748 2445460 : if (m_nBufferSize == BUFFER_SIZE && Flush() < 0)
749 : {
750 0 : break;
751 : }
752 : }
753 :
754 : #ifdef VSI_DEBUG
755 : const int nError = errno;
756 : VSIDebug4("VSIUnixStdioHandle::Write(%d,%ld) = %" PRId64, fd,
757 : static_cast<long>(nBytes), static_cast<int64_t>(nBytesWritten));
758 : errno = nError;
759 : #endif
760 :
761 2441690 : eLastOp = Operation::WRITE;
762 :
763 2441690 : return nBytesWritten;
764 : }
765 :
766 : /************************************************************************/
767 : /* ClearErr() */
768 : /************************************************************************/
769 :
770 8808 : void VSIUnixStdioHandle::ClearErr()
771 :
772 : {
773 8808 : bAtEOF = false;
774 8808 : bError = false;
775 8808 : }
776 :
777 : /************************************************************************/
778 : /* Error() */
779 : /************************************************************************/
780 :
781 65093 : int VSIUnixStdioHandle::Error()
782 :
783 : {
784 65093 : return bError ? TRUE : FALSE;
785 : }
786 :
787 : /************************************************************************/
788 : /* Eof() */
789 : /************************************************************************/
790 :
791 161580 : int VSIUnixStdioHandle::Eof()
792 :
793 : {
794 161580 : return bAtEOF ? TRUE : FALSE;
795 : }
796 :
797 : /************************************************************************/
798 : /* Truncate() */
799 : /************************************************************************/
800 :
801 617 : int VSIUnixStdioHandle::Truncate(vsi_l_offset nNewSize)
802 : {
803 617 : if (Flush() != 0)
804 0 : return -1;
805 617 : return VSI_FTRUNCATE64(fd, nNewSize);
806 : }
807 :
808 : /************************************************************************/
809 : /* GetRangeStatus() */
810 : /************************************************************************/
811 :
812 : #ifdef __linux
813 : #if !defined(MISSING_LINUX_FS_H)
814 : #include <linux/fs.h> // FS_IOC_FIEMAP
815 : #endif
816 : #ifdef FS_IOC_FIEMAP
817 : #include <linux/types.h> // for types used in linux/fiemap.h
818 : #include <linux/fiemap.h> // struct fiemap
819 : #endif
820 : #include <sys/ioctl.h>
821 : #include <errno.h>
822 : #endif
823 :
824 573 : VSIRangeStatus VSIUnixStdioHandle::GetRangeStatus(vsi_l_offset
825 : #ifdef FS_IOC_FIEMAP
826 : nOffset
827 : #endif
828 : ,
829 : vsi_l_offset
830 : #ifdef FS_IOC_FIEMAP
831 : nLength
832 : #endif
833 : )
834 : {
835 : #ifdef FS_IOC_FIEMAP
836 : // fiemap IOCTL documented at
837 : // https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt
838 :
839 : // The fiemap struct contains a "variable length" array at its end
840 : // As we are interested in only one extent, we allocate the base size of
841 : // fiemap + one fiemap_extent.
842 : GByte abyBuffer[sizeof(struct fiemap) + sizeof(struct fiemap_extent)];
843 573 : struct fiemap *psExtentMap = reinterpret_cast<struct fiemap *>(&abyBuffer);
844 573 : memset(psExtentMap, 0,
845 : sizeof(struct fiemap) + sizeof(struct fiemap_extent));
846 573 : psExtentMap->fm_start = nOffset;
847 573 : psExtentMap->fm_length = nLength;
848 573 : psExtentMap->fm_extent_count = 1;
849 573 : int ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
850 573 : if (ret < 0)
851 0 : return VSI_RANGE_STATUS_UNKNOWN;
852 573 : if (psExtentMap->fm_mapped_extents == 0)
853 2 : return VSI_RANGE_STATUS_HOLE;
854 : // In case there is one extent with unknown status, retry after having
855 : // asked the kernel to sync the file.
856 571 : const fiemap_extent *pasExtent = &(psExtentMap->fm_extents[0]);
857 571 : if (psExtentMap->fm_mapped_extents == 1 &&
858 571 : (pasExtent[0].fe_flags & FIEMAP_EXTENT_UNKNOWN) != 0)
859 : {
860 23 : psExtentMap->fm_flags = FIEMAP_FLAG_SYNC;
861 23 : psExtentMap->fm_start = nOffset;
862 23 : psExtentMap->fm_length = nLength;
863 23 : psExtentMap->fm_extent_count = 1;
864 23 : ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
865 23 : if (ret < 0)
866 0 : return VSI_RANGE_STATUS_UNKNOWN;
867 23 : if (psExtentMap->fm_mapped_extents == 0)
868 0 : return VSI_RANGE_STATUS_HOLE;
869 : }
870 571 : return VSI_RANGE_STATUS_DATA;
871 : #else
872 : static bool bMessageEmitted = false;
873 : if (!bMessageEmitted)
874 : {
875 : CPLDebug("VSI", "Sorry: GetExtentStatus() not implemented for "
876 : "this operating system");
877 : bMessageEmitted = true;
878 : }
879 : return VSI_RANGE_STATUS_UNKNOWN;
880 : #endif
881 : }
882 :
883 : /************************************************************************/
884 : /* HasPRead() */
885 : /************************************************************************/
886 :
887 : #if defined(HAVE_PREAD64) || (defined(HAVE_PREAD_BSD) && SIZEOF_OFF_T == 8)
888 731 : bool VSIUnixStdioHandle::HasPRead() const
889 : {
890 731 : return true;
891 : }
892 :
893 : /************************************************************************/
894 : /* PRead() */
895 : /************************************************************************/
896 :
897 24494 : size_t VSIUnixStdioHandle::PRead(void *pBuffer, size_t nSize,
898 : vsi_l_offset nOffset) const
899 : {
900 : #ifdef HAVE_PREAD64
901 24494 : return pread64(fd, pBuffer, nSize, nOffset);
902 : #else
903 : return pread(fd, pBuffer, nSize, static_cast<off_t>(nOffset));
904 : #endif
905 : }
906 : #endif
907 :
908 : /************************************************************************/
909 : /* ==================================================================== */
910 : /* VSIUnixStdioFilesystemHandler */
911 : /* ==================================================================== */
912 : /************************************************************************/
913 :
914 : #ifdef VSI_COUNT_BYTES_READ
915 : /************************************************************************/
916 : /* ~VSIUnixStdioFilesystemHandler() */
917 : /************************************************************************/
918 :
919 : VSIUnixStdioFilesystemHandler::~VSIUnixStdioFilesystemHandler()
920 : {
921 : CPLDebug(
922 : "VSI",
923 : "~VSIUnixStdioFilesystemHandler() : nTotalBytesRead = " CPL_FRMT_GUIB,
924 : nTotalBytesRead);
925 :
926 : if (hMutex != nullptr)
927 : CPLDestroyMutex(hMutex);
928 : hMutex = nullptr;
929 : }
930 : #endif
931 :
932 : /************************************************************************/
933 : /* Open() */
934 : /************************************************************************/
935 :
936 : VSIVirtualHandleUniquePtr
937 236137 : VSIUnixStdioFilesystemHandler::Open(const char *pszFilename,
938 : const char *pszAccess, bool bSetError,
939 : CSLConstList /* papszOptions */)
940 :
941 : {
942 236137 : int flags = O_RDONLY;
943 : int fd;
944 : using AccessMode = VSIUnixStdioHandle::AccessMode;
945 236137 : AccessMode eAccessMode = AccessMode::NORMAL;
946 236137 : if (strchr(pszAccess, 'w'))
947 : {
948 34120 : if (strchr(pszAccess, '+'))
949 6753 : flags = O_CREAT | O_TRUNC | O_RDWR;
950 : else
951 : {
952 27367 : eAccessMode = AccessMode::WRITE_ONLY;
953 27367 : flags = O_CREAT | O_TRUNC | O_WRONLY;
954 : }
955 34120 : fd = VSI_OPEN64(pszFilename, flags, 0664);
956 : }
957 202017 : else if (strchr(pszAccess, 'a'))
958 : {
959 172 : if (strchr(pszAccess, '+'))
960 : {
961 122 : eAccessMode = AccessMode::APPEND_READ_WRITE;
962 122 : flags = O_CREAT | O_RDWR;
963 : }
964 : else
965 50 : flags = O_CREAT | O_WRONLY;
966 172 : fd = VSI_OPEN64(pszFilename, flags, 0664);
967 : }
968 : else
969 : {
970 201845 : if (strchr(pszAccess, '+'))
971 7080 : flags = O_RDWR;
972 : else
973 194765 : eAccessMode = AccessMode::READ_ONLY;
974 201845 : fd = VSI_OPEN64(pszFilename, flags);
975 : }
976 235896 : const int nError = errno;
977 :
978 : VSIDebug3("VSIUnixStdioFilesystemHandler::Open(\"%s\",\"%s\") = %d",
979 : pszFilename, pszAccess, fd);
980 :
981 235896 : if (fd < 0)
982 : {
983 88709 : if (bSetError)
984 : {
985 17500 : VSIError(VSIE_FileError, "%s: %s", pszFilename, strerror(nError));
986 : }
987 88707 : errno = nError;
988 88707 : return nullptr;
989 : }
990 :
991 294538 : auto poHandle = std::make_unique<VSIUnixStdioHandle>(this, fd, eAccessMode);
992 147231 : poHandle->m_osFilename = pszFilename;
993 :
994 147321 : errno = nError;
995 :
996 147321 : if (strchr(pszAccess, 'a'))
997 172 : poHandle->Seek(0, SEEK_END);
998 :
999 : /* -------------------------------------------------------------------- */
1000 : /* If VSI_CACHE is set we want to use a cached reader instead */
1001 : /* of more direct io on the underlying file. */
1002 : /* -------------------------------------------------------------------- */
1003 255289 : if (eAccessMode == AccessMode::READ_ONLY &&
1004 107882 : CPLTestBool(CPLGetConfigOption("VSI_CACHE", "FALSE")))
1005 : {
1006 : return VSIVirtualHandleUniquePtr(
1007 5 : VSICreateCachedFile(poHandle.release()));
1008 : }
1009 :
1010 147402 : return VSIVirtualHandleUniquePtr(poHandle.release());
1011 : }
1012 :
1013 : /************************************************************************/
1014 : /* CreateOnlyVisibleAtCloseTime() */
1015 : /************************************************************************/
1016 :
1017 : VSIVirtualHandleUniquePtr
1018 229 : VSIUnixStdioFilesystemHandler::CreateOnlyVisibleAtCloseTime(
1019 : const char *pszFilename, bool bEmulationAllowed, CSLConstList papszOptions)
1020 : {
1021 : #ifdef __linux
1022 53 : static bool bIsLinkatSupported = []()
1023 : {
1024 : // Check that /proc is accessible, since we will need it to run linkat()
1025 : struct stat statbuf;
1026 53 : return stat("/proc/self/fd", &statbuf) == 0;
1027 229 : }();
1028 :
1029 : const int fd = bIsLinkatSupported
1030 458 : ? VSI_OPEN64(CPLGetDirnameSafe(pszFilename).c_str(),
1031 : O_TMPFILE | O_RDWR, 0666)
1032 229 : : -1;
1033 229 : if (fd < 0)
1034 : {
1035 193 : if (bIsLinkatSupported)
1036 : {
1037 193 : CPLDebugOnce("VSI",
1038 : "open(O_TMPFILE | O_RDWR) failed with errno=%d (%s). "
1039 : "Going through emulation",
1040 : errno, VSIStrerror(errno));
1041 : }
1042 : return VSIFilesystemHandler::CreateOnlyVisibleAtCloseTime(
1043 193 : pszFilename, bEmulationAllowed, papszOptions);
1044 : }
1045 :
1046 : auto poHandle = std::make_unique<VSIUnixStdioHandle>(
1047 72 : this, fd, VSIUnixStdioHandle::AccessMode::NORMAL);
1048 36 : poHandle->m_osFilename = pszFilename;
1049 36 : poHandle->m_bUnlinkedFile = true;
1050 36 : return VSIVirtualHandleUniquePtr(poHandle.release());
1051 : #else
1052 : if (!bEmulationAllowed)
1053 : return nullptr;
1054 :
1055 : std::string osTmpFilename = std::string(pszFilename).append("XXXXXX");
1056 : int fd = mkstemp(osTmpFilename.data());
1057 : if (fd < 0)
1058 : {
1059 : return VSIFilesystemHandler::CreateOnlyVisibleAtCloseTime(
1060 : pszFilename, bEmulationAllowed, papszOptions);
1061 : }
1062 : auto poHandle = std::make_unique<VSIUnixStdioHandle>(
1063 : this, fd, VSIUnixStdioHandle::AccessMode::NORMAL);
1064 : poHandle->m_osTmpFilename = std::move(osTmpFilename);
1065 : poHandle->m_osFilename = pszFilename;
1066 : return VSIVirtualHandleUniquePtr(poHandle.release());
1067 : #endif
1068 : }
1069 :
1070 : /************************************************************************/
1071 : /* Stat() */
1072 : /************************************************************************/
1073 :
1074 273480 : int VSIUnixStdioFilesystemHandler::Stat(const char *pszFilename,
1075 : VSIStatBufL *pStatBuf, int /* nFlags */)
1076 : {
1077 273480 : return (VSI_STAT64(pszFilename, pStatBuf));
1078 : }
1079 :
1080 : /************************************************************************/
1081 : /* Unlink() */
1082 : /************************************************************************/
1083 :
1084 5355 : int VSIUnixStdioFilesystemHandler::Unlink(const char *pszFilename)
1085 :
1086 : {
1087 5355 : return unlink(pszFilename);
1088 : }
1089 :
1090 : /************************************************************************/
1091 : /* Rename() */
1092 : /************************************************************************/
1093 :
1094 1143 : int VSIUnixStdioFilesystemHandler::Rename(const char *oldpath,
1095 : const char *newpath, GDALProgressFunc,
1096 : void *)
1097 :
1098 : {
1099 1143 : return rename(oldpath, newpath);
1100 : }
1101 :
1102 : /************************************************************************/
1103 : /* Mkdir() */
1104 : /************************************************************************/
1105 :
1106 2233 : int VSIUnixStdioFilesystemHandler::Mkdir(const char *pszPathname, long nMode)
1107 :
1108 : {
1109 2233 : return mkdir(pszPathname, static_cast<int>(nMode));
1110 : }
1111 :
1112 : /************************************************************************/
1113 : /* Rmdir() */
1114 : /************************************************************************/
1115 :
1116 107 : int VSIUnixStdioFilesystemHandler::Rmdir(const char *pszPathname)
1117 :
1118 : {
1119 107 : return rmdir(pszPathname);
1120 : }
1121 :
1122 : /************************************************************************/
1123 : /* ReadDirEx() */
1124 : /************************************************************************/
1125 :
1126 21847 : char **VSIUnixStdioFilesystemHandler::ReadDirEx(const char *pszPath,
1127 : int nMaxFiles)
1128 :
1129 : {
1130 21847 : if (strlen(pszPath) == 0)
1131 17 : pszPath = ".";
1132 :
1133 43693 : CPLStringList oDir;
1134 21847 : DIR *hDir = opendir(pszPath);
1135 21847 : if (hDir != nullptr)
1136 : {
1137 : // We want to avoid returning NULL for an empty list.
1138 21777 : oDir.Assign(static_cast<char **>(CPLCalloc(2, sizeof(char *))));
1139 :
1140 21777 : struct dirent *psDirEntry = nullptr;
1141 2055070 : while ((psDirEntry = readdir(hDir)) != nullptr)
1142 : {
1143 2033310 : oDir.AddString(psDirEntry->d_name);
1144 2033300 : if (nMaxFiles > 0 && oDir.Count() > nMaxFiles)
1145 7 : break;
1146 : }
1147 :
1148 21777 : closedir(hDir);
1149 : }
1150 : else
1151 : {
1152 : // Should we generate an error?
1153 : // For now we'll just return NULL (at the end of the function).
1154 : }
1155 :
1156 43693 : return oDir.StealList();
1157 : }
1158 :
1159 : /************************************************************************/
1160 : /* GetDiskFreeSpace() */
1161 : /************************************************************************/
1162 :
1163 1 : GIntBig VSIUnixStdioFilesystemHandler::GetDiskFreeSpace(const char *
1164 : #ifdef HAVE_STATVFS
1165 : pszDirname
1166 : #endif
1167 : )
1168 : {
1169 1 : GIntBig nRet = -1;
1170 : #ifdef HAVE_STATVFS
1171 :
1172 : #ifdef HAVE_STATVFS64
1173 : struct statvfs64 buf;
1174 1 : if (statvfs64(pszDirname, &buf) == 0)
1175 : {
1176 0 : nRet = static_cast<GIntBig>(buf.f_frsize *
1177 0 : static_cast<GUIntBig>(buf.f_bavail));
1178 : }
1179 : #else
1180 : struct statvfs buf;
1181 : if (statvfs(pszDirname, &buf) == 0)
1182 : {
1183 : nRet = static_cast<GIntBig>(buf.f_frsize *
1184 : static_cast<GUIntBig>(buf.f_bavail));
1185 : }
1186 : #endif
1187 :
1188 : #endif
1189 1 : return nRet;
1190 : }
1191 :
1192 : /************************************************************************/
1193 : /* SupportsSparseFiles() */
1194 : /************************************************************************/
1195 :
1196 : #ifdef __linux
1197 : #include <sys/vfs.h>
1198 : #endif
1199 :
1200 2 : int VSIUnixStdioFilesystemHandler::SupportsSparseFiles(const char *
1201 : #ifdef __linux
1202 : pszPath
1203 : #endif
1204 : )
1205 : {
1206 : #ifdef __linux
1207 : struct statfs sStatFS;
1208 2 : if (statfs(pszPath, &sStatFS) == 0)
1209 : {
1210 : // Add here any missing filesystem supporting sparse files.
1211 : // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
1212 2 : switch (static_cast<unsigned>(sStatFS.f_type))
1213 : {
1214 : // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
1215 2 : case 0xef53U: // ext2, 3, 4
1216 : case 0x52654973U: // reiser
1217 : case 0x58465342U: // xfs
1218 : case 0x3153464aU: // jfs
1219 : case 0x5346544eU: // ntfs
1220 : case 0x9123683eU: // brfs
1221 : // nfs: NFS < 4.2 supports creating sparse files (but reading them
1222 : // not efficiently).
1223 : case 0x6969U:
1224 : case 0x01021994U: // tmpfs
1225 2 : return TRUE;
1226 :
1227 0 : case 0x4d44U: // msdos
1228 0 : return FALSE;
1229 :
1230 0 : case 0x53464846U: // Windows Subsystem for Linux fs
1231 : {
1232 : static bool bUnknownFSEmitted = false;
1233 0 : if (!bUnknownFSEmitted)
1234 : {
1235 0 : CPLDebug("VSI",
1236 : "Windows Subsystem for Linux FS is at "
1237 : "the time of writing not known to support sparse "
1238 : "files");
1239 0 : bUnknownFSEmitted = true;
1240 : }
1241 0 : return FALSE;
1242 : }
1243 :
1244 0 : default:
1245 : {
1246 : static bool bUnknownFSEmitted = false;
1247 0 : if (!bUnknownFSEmitted)
1248 : {
1249 0 : CPLDebug("VSI",
1250 : "Filesystem with type %X unknown. "
1251 : "Assuming it does not support sparse files",
1252 0 : static_cast<int>(sStatFS.f_type));
1253 0 : bUnknownFSEmitted = true;
1254 : }
1255 0 : return FALSE;
1256 : }
1257 : }
1258 : }
1259 0 : return FALSE;
1260 : #else
1261 : static bool bMessageEmitted = false;
1262 : if (!bMessageEmitted)
1263 : {
1264 : CPLDebug("VSI", "Sorry: SupportsSparseFiles() not implemented "
1265 : "for this operating system");
1266 : bMessageEmitted = true;
1267 : }
1268 : return FALSE;
1269 : #endif
1270 : }
1271 :
1272 : /************************************************************************/
1273 : /* IsLocal() */
1274 : /************************************************************************/
1275 :
1276 1687 : bool VSIUnixStdioFilesystemHandler::IsLocal(const char *
1277 : #ifdef __linux
1278 : pszPath
1279 : #endif
1280 : ) const
1281 : {
1282 : #ifdef __linux
1283 : struct statfs sStatFS;
1284 1687 : if (statfs(pszPath, &sStatFS) == 0)
1285 : {
1286 : // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
1287 220 : switch (static_cast<unsigned>(sStatFS.f_type))
1288 : {
1289 : // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
1290 0 : case 0x6969U: // NFS
1291 : case 0x517bU: // SMB
1292 : case 0xff534d42U: // CIFS
1293 : case 0xfe534d42U: // SMB2
1294 : // (https://github.com/libuv/libuv/blob/97dcdb1926f6aca43171e1614338bcef067abd59/src/unix/fs.c#L960)
1295 0 : return false;
1296 : }
1297 : }
1298 : #else
1299 : static bool bMessageEmitted = false;
1300 : if (!bMessageEmitted)
1301 : {
1302 : CPLDebug("VSI", "Sorry: IsLocal() not implemented "
1303 : "for this operating system");
1304 : bMessageEmitted = true;
1305 : }
1306 : #endif
1307 1687 : return true;
1308 : }
1309 :
1310 : /************************************************************************/
1311 : /* SupportsSequentialWrite() */
1312 : /************************************************************************/
1313 :
1314 169 : bool VSIUnixStdioFilesystemHandler::SupportsSequentialWrite(
1315 : const char *pszPath, bool /* bAllowLocalTempFile */)
1316 : {
1317 : VSIStatBufL sStat;
1318 169 : if (VSIStatL(pszPath, &sStat) == 0)
1319 62 : return access(pszPath, W_OK) == 0;
1320 107 : return access(CPLGetDirnameSafe(pszPath).c_str(), W_OK) == 0;
1321 : }
1322 :
1323 : /************************************************************************/
1324 : /* SupportsRandomWrite() */
1325 : /************************************************************************/
1326 :
1327 107 : bool VSIUnixStdioFilesystemHandler::SupportsRandomWrite(
1328 : const char *pszPath, bool /* bAllowLocalTempFile */)
1329 : {
1330 107 : return SupportsSequentialWrite(pszPath, false);
1331 : }
1332 :
1333 : /************************************************************************/
1334 : /* VSIDIRUnixStdio */
1335 : /************************************************************************/
1336 :
1337 : struct VSIDIRUnixStdio final : public VSIDIR
1338 : {
1339 : struct DIRCloser
1340 : {
1341 1121 : void operator()(DIR *d)
1342 : {
1343 1121 : if (d)
1344 1121 : closedir(d);
1345 1121 : }
1346 : };
1347 :
1348 : CPLString osRootPath{};
1349 : CPLString osBasePath{};
1350 : std::unique_ptr<DIR, DIRCloser> m_psDir{};
1351 : int nRecurseDepth = 0;
1352 : VSIDIREntry entry{};
1353 : std::vector<std::unique_ptr<VSIDIR>> aoStackSubDir{};
1354 : std::string m_osFilterPrefix{};
1355 : bool m_bNameAndTypeOnly = false;
1356 :
1357 : const VSIDIREntry *NextDirEntry() override;
1358 : };
1359 :
1360 : /************************************************************************/
1361 : /* OpenDirInternal() */
1362 : /************************************************************************/
1363 :
1364 : /* static */
1365 1146 : std::unique_ptr<VSIDIRUnixStdio> VSIUnixStdioFilesystemHandler::OpenDirInternal(
1366 : const char *pszPath, int nRecurseDepth, const char *const *papszOptions)
1367 : {
1368 2292 : std::unique_ptr<DIR, VSIDIRUnixStdio::DIRCloser> psDir(opendir(pszPath));
1369 1146 : if (psDir == nullptr)
1370 : {
1371 25 : return nullptr;
1372 : }
1373 2242 : auto dir = std::make_unique<VSIDIRUnixStdio>();
1374 1121 : dir->osRootPath = pszPath;
1375 1121 : dir->nRecurseDepth = nRecurseDepth;
1376 1121 : dir->m_psDir = std::move(psDir);
1377 1121 : dir->m_osFilterPrefix = CSLFetchNameValueDef(papszOptions, "PREFIX", "");
1378 1121 : dir->m_bNameAndTypeOnly = CPLTestBool(
1379 : CSLFetchNameValueDef(papszOptions, "NAME_AND_TYPE_ONLY", "NO"));
1380 1121 : return dir;
1381 : }
1382 :
1383 : /************************************************************************/
1384 : /* OpenDir() */
1385 : /************************************************************************/
1386 :
1387 393 : VSIDIR *VSIUnixStdioFilesystemHandler::OpenDir(const char *pszPath,
1388 : int nRecurseDepth,
1389 : const char *const *papszOptions)
1390 : {
1391 393 : return OpenDirInternal(pszPath, nRecurseDepth, papszOptions).release();
1392 : }
1393 :
1394 : /************************************************************************/
1395 : /* NextDirEntry() */
1396 : /************************************************************************/
1397 :
1398 26999 : const VSIDIREntry *VSIDIRUnixStdio::NextDirEntry()
1399 : {
1400 26999 : begin:
1401 26999 : if (VSI_ISDIR(entry.nMode) && nRecurseDepth != 0)
1402 : {
1403 1506 : CPLString osCurFile(osRootPath);
1404 753 : if (!osCurFile.empty())
1405 753 : osCurFile += '/';
1406 753 : osCurFile += entry.pszName;
1407 : auto subdir = VSIUnixStdioFilesystemHandler::OpenDirInternal(
1408 753 : osCurFile, nRecurseDepth - 1, nullptr);
1409 753 : if (subdir)
1410 : {
1411 753 : subdir->osRootPath = osRootPath;
1412 753 : subdir->osBasePath = entry.pszName;
1413 753 : subdir->m_osFilterPrefix = m_osFilterPrefix;
1414 753 : subdir->m_bNameAndTypeOnly = m_bNameAndTypeOnly;
1415 753 : aoStackSubDir.push_back(std::move(subdir));
1416 : }
1417 753 : entry.nMode = 0;
1418 : }
1419 :
1420 27750 : while (!aoStackSubDir.empty())
1421 : {
1422 14219 : auto l_entry = aoStackSubDir.back()->NextDirEntry();
1423 14219 : if (l_entry)
1424 : {
1425 13468 : return l_entry;
1426 : }
1427 751 : aoStackSubDir.pop_back();
1428 : }
1429 :
1430 15760 : while (const auto *psEntry = readdir(m_psDir.get()))
1431 : {
1432 : // Skip . and ..entries
1433 14650 : if (psEntry->d_name[0] == '.' &&
1434 2242 : (psEntry->d_name[1] == '\0' ||
1435 1129 : (psEntry->d_name[1] == '.' && psEntry->d_name[2] == '\0')))
1436 : {
1437 : // do nothing
1438 : }
1439 : else
1440 : {
1441 12426 : CPLFree(entry.pszName);
1442 12426 : CPLString osName(osBasePath);
1443 12426 : if (!osName.empty())
1444 7943 : osName += '/';
1445 12426 : osName += psEntry->d_name;
1446 :
1447 12426 : entry.pszName = CPLStrdup(osName);
1448 12426 : entry.nMode = 0;
1449 12426 : entry.nSize = 0;
1450 12426 : entry.nMTime = 0;
1451 12426 : entry.bModeKnown = false;
1452 12426 : entry.bSizeKnown = false;
1453 12426 : entry.bMTimeKnown = false;
1454 :
1455 12426 : CPLString osCurFile(osRootPath);
1456 12426 : if (!osCurFile.empty())
1457 12426 : osCurFile += '/';
1458 12426 : osCurFile += entry.pszName;
1459 :
1460 : #if !defined(__sun) && !defined(__HAIKU__)
1461 12426 : if (psEntry->d_type == DT_REG)
1462 9969 : entry.nMode = S_IFREG;
1463 2457 : else if (psEntry->d_type == DT_DIR)
1464 893 : entry.nMode = S_IFDIR;
1465 1564 : else if (psEntry->d_type == DT_LNK)
1466 1564 : entry.nMode = S_IFLNK;
1467 : #endif
1468 :
1469 19486 : const auto StatFile = [&osCurFile, this]()
1470 : {
1471 : VSIStatBufL sStatL;
1472 9743 : if (VSIStatL(osCurFile, &sStatL) == 0)
1473 : {
1474 9743 : entry.nMode = sStatL.st_mode;
1475 9743 : entry.nSize = sStatL.st_size;
1476 9743 : entry.nMTime = sStatL.st_mtime;
1477 9743 : entry.bModeKnown = true;
1478 9743 : entry.bSizeKnown = true;
1479 9743 : entry.bMTimeKnown = true;
1480 : }
1481 9743 : };
1482 :
1483 12438 : if (!m_osFilterPrefix.empty() &&
1484 12 : m_osFilterPrefix.size() > osName.size())
1485 : {
1486 6 : if (STARTS_WITH(m_osFilterPrefix.c_str(), osName.c_str()) &&
1487 2 : m_osFilterPrefix[osName.size()] == '/')
1488 : {
1489 : #if !defined(__sun) && !defined(__HAIKU__)
1490 1 : if (psEntry->d_type == DT_UNKNOWN)
1491 : #endif
1492 : {
1493 0 : StatFile();
1494 : }
1495 1 : if (VSI_ISDIR(entry.nMode))
1496 : {
1497 1 : goto begin;
1498 : }
1499 : }
1500 3 : continue;
1501 : }
1502 12430 : if (!m_osFilterPrefix.empty() &&
1503 8 : !STARTS_WITH(osName.c_str(), m_osFilterPrefix.c_str()))
1504 : {
1505 2 : continue;
1506 : }
1507 :
1508 12420 : if (!m_bNameAndTypeOnly
1509 : #if !defined(__sun) && !defined(__HAIKU__)
1510 2677 : || psEntry->d_type == DT_UNKNOWN
1511 : #endif
1512 : )
1513 : {
1514 9743 : StatFile();
1515 : }
1516 :
1517 12420 : return &(entry);
1518 : }
1519 2229 : }
1520 :
1521 1110 : return nullptr;
1522 : }
1523 :
1524 : #ifdef VSI_COUNT_BYTES_READ
1525 : /************************************************************************/
1526 : /* AddToTotal() */
1527 : /************************************************************************/
1528 :
1529 : void VSIUnixStdioFilesystemHandler::AddToTotal(vsi_l_offset nBytes)
1530 : {
1531 : CPLMutexHolder oHolder(&hMutex);
1532 : nTotalBytesRead += nBytes;
1533 : }
1534 :
1535 : #endif
1536 :
1537 : /************************************************************************/
1538 : /* GetCanonicalFilename() */
1539 : /************************************************************************/
1540 :
1541 : #ifdef HAS_CASE_INSENSITIVE_FILE_SYSTEM
1542 : std::string VSIUnixStdioFilesystemHandler::GetCanonicalFilename(
1543 : const std::string &osFilename) const
1544 : {
1545 : char szResolvedPath[PATH_MAX];
1546 : const char *pszFilename = osFilename.c_str();
1547 : if (realpath(pszFilename, szResolvedPath))
1548 : {
1549 : const char *pszFilenameLastPart = strrchr(pszFilename, '/');
1550 : const char *pszResolvedFilenameLastPart = strrchr(szResolvedPath, '/');
1551 : if (pszFilenameLastPart && pszResolvedFilenameLastPart &&
1552 : EQUAL(pszFilenameLastPart, pszResolvedFilenameLastPart))
1553 : {
1554 : std::string osRet;
1555 : osRet.assign(pszFilename, pszFilenameLastPart - pszFilename);
1556 : osRet += pszResolvedFilenameLastPart;
1557 : return osRet;
1558 : }
1559 : return szResolvedPath;
1560 : }
1561 : return osFilename;
1562 : }
1563 : #endif
1564 :
1565 : /************************************************************************/
1566 : /* VSIInstallLargeFileHandler() */
1567 : /************************************************************************/
1568 :
1569 2101 : void VSIInstallLargeFileHandler()
1570 :
1571 : {
1572 2101 : VSIFileManager::InstallHandler(
1573 4202 : "", std::make_shared<VSIUnixStdioFilesystemHandler>());
1574 2101 : }
1575 :
1576 : #endif // ndef WIN32
1577 :
1578 : //! @endcond
|