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 1793 : 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 137754 : VSIUnixStdioHandle::VSIUnixStdioHandle(
299 : #ifndef VSI_COUNT_BYTES_READ
300 : CPL_UNUSED
301 : #endif
302 : VSIUnixStdioFilesystemHandler *poFSIn,
303 137754 : int fdIn, AccessMode eAccessModeIn)
304 137754 : : fd(fdIn), eAccessMode(eAccessModeIn)
305 : #ifdef VSI_COUNT_BYTES_READ
306 : ,
307 : poFS(poFSIn)
308 : #endif
309 : {
310 137665 : }
311 :
312 : /************************************************************************/
313 : /* ~VSIUnixStdioHandle() */
314 : /************************************************************************/
315 :
316 272873 : VSIUnixStdioHandle::~VSIUnixStdioHandle()
317 : {
318 136424 : VSIUnixStdioHandle::Close();
319 273017 : }
320 :
321 : /************************************************************************/
322 : /* Close() */
323 : /************************************************************************/
324 :
325 277197 : int VSIUnixStdioHandle::Close()
326 :
327 : {
328 277197 : if (fd < 0)
329 140140 : return 0;
330 :
331 : VSIDebug1("VSIUnixStdioHandle::Close(%d)", fd);
332 :
333 : #ifdef VSI_COUNT_BYTES_READ
334 : poFS->AddToTotal(nTotalBytesRead);
335 : #endif
336 :
337 137057 : int ret = VSIUnixStdioHandle::Flush();
338 :
339 : #ifdef __linux
340 272693 : if (ret == 0 && !m_bCancelCreation && !m_osFilename.empty() &&
341 135688 : m_bUnlinkedFile)
342 : {
343 1 : ret = fsync(fd);
344 1 : 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 1 : snprintf(szPath, sizeof(szPath), "/proc/self/fd/%d", fd);
351 1 : ret = linkat(AT_FDCWD, szPath, AT_FDCWD, m_osFilename.c_str(),
352 : AT_SYMLINK_FOLLOW);
353 1 : if (ret != 0)
354 0 : CPLDebug("CPL", "linkat() failed with errno=%d", errno);
355 : }
356 : }
357 : #endif
358 :
359 137780 : int ret2 = close(fd);
360 137264 : 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 137264 : fd = -1;
371 137264 : return ret;
372 : }
373 :
374 : /************************************************************************/
375 : /* CancelCreation() */
376 : /************************************************************************/
377 :
378 531 : void VSIUnixStdioHandle::CancelCreation()
379 : {
380 : #if defined(__linux)
381 531 : if (!m_osFilename.empty() && !m_bUnlinkedFile)
382 : {
383 463 : unlink(m_osFilename.c_str());
384 463 : 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 531 : }
401 :
402 : /************************************************************************/
403 : /* Seek() */
404 : /************************************************************************/
405 :
406 4845880 : int VSIUnixStdioHandle::Seek(vsi_l_offset nOffsetIn, int nWhence)
407 : {
408 4845880 : bAtEOF = false;
409 4845880 : bError = false;
410 :
411 : // Seeks that do nothing are still surprisingly expensive with MSVCRT.
412 : // try and short circuit if possible.
413 4845880 : if (eAccessMode != AccessMode::APPEND_READ_WRITE && nWhence == SEEK_SET &&
414 4220830 : nOffsetIn == m_nFilePos)
415 748673 : 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 4097200 : if (eLastOp == Operation::WRITE)
428 : {
429 298410 : eLastOp = Operation::NONE;
430 :
431 298410 : if (m_bBufferDirty && Flush() < 0)
432 0 : return -1;
433 :
434 298410 : const auto nNewPos = VSI_LSEEK64(fd, nOffsetIn, nWhence);
435 298410 : if (nNewPos < 0)
436 : {
437 0 : bError = true;
438 0 : return -1;
439 : }
440 :
441 298410 : m_nFilePos = nNewPos;
442 298410 : m_nBufferCurPos = 0;
443 298410 : m_nBufferSize = 0;
444 : }
445 :
446 : else
447 : {
448 3798790 : eLastOp = Operation::READ;
449 :
450 : vsi_l_offset nTargetPos;
451 :
452 : // Compute absolute target position
453 3798790 : switch (nWhence)
454 : {
455 3376090 : case SEEK_SET:
456 3376090 : nTargetPos = nOffsetIn;
457 3376090 : break;
458 81601 : case SEEK_CUR:
459 81601 : nTargetPos = m_nFilePos + nOffsetIn;
460 81601 : break;
461 339684 : case SEEK_END:
462 : {
463 339684 : const auto nNewPos = VSI_LSEEK64(fd, nOffsetIn, SEEK_END);
464 340415 : if (nNewPos < 0)
465 : {
466 0 : bError = true;
467 0 : return -1;
468 : }
469 340415 : nTargetPos = nNewPos;
470 340415 : break;
471 : }
472 1415 : default:
473 1415 : return -1;
474 : }
475 :
476 : // Compute absolute position of the current buffer
477 3798110 : const auto nBufStartOffset = m_nFilePos - m_nBufferCurPos;
478 3798110 : const auto nBufEndOffset = nBufStartOffset + m_nBufferSize;
479 :
480 3798110 : if (nTargetPos >= nBufStartOffset && nTargetPos < nBufEndOffset)
481 : {
482 : // Seek inside current buffer - adjust buffer pos only
483 2741900 : m_nBufferCurPos = static_cast<size_t>(nTargetPos - nBufStartOffset);
484 2741900 : m_nFilePos = nTargetPos;
485 : }
486 : else
487 : {
488 : // Outside buffer, do real seek and invalidate buffer
489 1056210 : const auto nNewPos = VSI_LSEEK64(fd, nTargetPos, SEEK_SET);
490 1056280 : if (nNewPos < 0)
491 : {
492 1 : bError = true;
493 1 : return -1;
494 : }
495 :
496 1056280 : m_nFilePos = nNewPos;
497 1056280 : m_nBufferCurPos = 0;
498 1056280 : 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 4096590 : return 0;
506 : }
507 :
508 : /************************************************************************/
509 : /* Tell() */
510 : /************************************************************************/
511 :
512 3735180 : vsi_l_offset VSIUnixStdioHandle::Tell()
513 :
514 : {
515 3735180 : return m_nFilePos;
516 : }
517 :
518 : /************************************************************************/
519 : /* Flush() */
520 : /************************************************************************/
521 :
522 463048 : int VSIUnixStdioHandle::Flush()
523 :
524 : {
525 : VSIDebug1("VSIUnixStdioHandle::Flush(%d)", fd);
526 :
527 463048 : if (m_bBufferDirty)
528 : {
529 346131 : m_bBufferDirty = false;
530 :
531 : // Sync kernel offset to our logical position before writing
532 346131 : if (VSI_LSEEK64(fd, m_nFilePos - m_nBufferSize, SEEK_SET) < 0)
533 : {
534 0 : return EOF;
535 : }
536 :
537 346131 : size_t nOff = 0;
538 692262 : while (m_nBufferSize > 0)
539 : {
540 346131 : errno = 0;
541 : const auto nWritten =
542 346131 : write(fd, m_abyBuffer.data() + nOff, m_nBufferSize);
543 346131 : if (nWritten < 0)
544 : {
545 0 : if (errno == EINTR)
546 0 : continue;
547 0 : return EOF;
548 : }
549 346131 : CPLAssert(static_cast<size_t>(nWritten) <= m_nBufferSize);
550 346131 : nOff += nWritten;
551 346131 : m_nBufferSize -= nWritten;
552 : }
553 : }
554 :
555 463048 : return 0;
556 : }
557 :
558 : /************************************************************************/
559 : /* Read() */
560 : /************************************************************************/
561 :
562 9516740 : size_t VSIUnixStdioHandle::Read(void *pBuffer, size_t nBytes)
563 :
564 : {
565 9516740 : if (nBytes == 0 || bAtEOF)
566 42064 : return 0;
567 9474680 : 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 9474680 : if (eLastOp == Operation::WRITE)
582 : {
583 2862 : if (m_bBufferDirty && Flush() < 0)
584 : {
585 0 : bError = true;
586 0 : return -1;
587 : }
588 2862 : if (VSI_LSEEK64(fd, m_nFilePos, SEEK_SET) < 0)
589 : {
590 0 : bError = true;
591 0 : return -1;
592 : }
593 2862 : m_nBufferCurPos = 0;
594 2862 : m_nBufferSize = 0;
595 : }
596 :
597 9474680 : const size_t nTotal = nBytes;
598 9474680 : size_t nBytesRead = 0;
599 9474680 : GByte *const pabyDest = static_cast<GByte *>(pBuffer);
600 :
601 18980700 : while (nBytesRead < nTotal)
602 : {
603 9578180 : size_t nAvailInBuffer = m_nBufferSize - m_nBufferCurPos;
604 :
605 : // If buffer is empty
606 9578180 : if (nAvailInBuffer == 0)
607 : {
608 905093 : size_t nRemaining = nTotal - nBytesRead;
609 905093 : if (nRemaining >= BUFFER_SIZE)
610 : {
611 : // Bypass buffer if large request
612 72955 : errno = 0;
613 : const ssize_t nBytesReadThisTime =
614 72955 : read(fd, pabyDest + nBytesRead, nRemaining);
615 72955 : if (nBytesReadThisTime <= 0)
616 : {
617 14314 : if (nBytesReadThisTime == 0)
618 14309 : bAtEOF = true;
619 5 : else if (errno == EINTR)
620 0 : continue;
621 : else
622 5 : bError = true;
623 71546 : break;
624 : }
625 58641 : m_nFilePos += nBytesReadThisTime;
626 58641 : nBytesRead += nBytesReadThisTime;
627 : }
628 : else
629 : {
630 : // Small request: Refill internal buffer
631 832138 : errno = 0;
632 : const ssize_t nBytesReadThisTime =
633 832138 : read(fd, m_abyBuffer.data(), BUFFER_SIZE);
634 831692 : if (nBytesReadThisTime <= 0)
635 : {
636 57232 : if (nBytesReadThisTime == 0)
637 56115 : bAtEOF = true;
638 1117 : else if (errno == EINTR)
639 0 : continue;
640 : else
641 1117 : bError = true;
642 57232 : break;
643 : }
644 774460 : m_nBufferCurPos = 0;
645 774460 : m_nBufferSize = nBytesReadThisTime;
646 774460 : nAvailInBuffer = nBytesReadThisTime;
647 : }
648 : }
649 :
650 : // Copy from buffer to user
651 9506190 : const size_t nToCopy = std::min(nTotal - nBytesRead, nAvailInBuffer);
652 9506140 : memcpy(pabyDest + nBytesRead, m_abyBuffer.data() + m_nBufferCurPos,
653 : nToCopy);
654 9506050 : nBytesRead += nToCopy;
655 9506050 : m_nBufferCurPos += nToCopy;
656 9506050 : m_nFilePos += nToCopy;
657 : }
658 :
659 : #ifdef VSI_DEBUG
660 : const int nError = errno;
661 : VSIDebug4("VSIUnixStdioHandle::Read(%d,%%ld) = %" PRId64, fd,
662 : static_cast<long>(nBytes), static_cast<int64_t>(nBytesRead));
663 : errno = nError;
664 : #endif
665 :
666 9474100 : eLastOp = Operation::READ;
667 :
668 : #ifdef VSI_COUNT_BYTES_READ
669 : nTotalBytesRead += nBytesRead;
670 : #endif
671 :
672 9474100 : return nBytesRead;
673 : }
674 :
675 : /************************************************************************/
676 : /* Write() */
677 : /************************************************************************/
678 :
679 2453300 : size_t VSIUnixStdioHandle::Write(const void *pBuffer, size_t nBytes)
680 :
681 : {
682 2453300 : if (nBytes == 0)
683 22958 : return 0;
684 2430340 : if (eAccessMode == AccessMode::READ_ONLY)
685 : {
686 2 : bError = true;
687 2 : errno = EINVAL;
688 2 : return 0;
689 : }
690 :
691 : /* -------------------------------------------------------------------- */
692 : /* If a fwrite() is followed by an fread(), the POSIX rules are */
693 : /* that some of the write may still be buffered and lost. We */
694 : /* are required to do a seek between to force flushing. So we */
695 : /* keep careful track of what happened last to know if we */
696 : /* skipped a flushing seek that we may need to do now. */
697 : /* -------------------------------------------------------------------- */
698 2430340 : if (eLastOp == Operation::READ)
699 : {
700 43331 : if (VSI_LSEEK64(fd, m_nFilePos, SEEK_SET) < 0)
701 : {
702 0 : bError = true;
703 0 : return -1;
704 : }
705 43331 : m_nBufferCurPos = 0;
706 43331 : m_nBufferSize = 0;
707 : }
708 :
709 2430340 : const size_t nTotal = nBytes;
710 2430340 : size_t nBytesWritten = 0;
711 2430340 : const GByte *const pabySrc = static_cast<const GByte *>(pBuffer);
712 4875300 : while (nBytesWritten < nTotal)
713 : {
714 2444960 : const size_t nRemaining = nTotal - nBytesWritten;
715 :
716 : // Bypass buffer if request > BUFFER_SIZE
717 2444960 : if (m_nBufferSize == 0 && nRemaining >= BUFFER_SIZE)
718 : {
719 : const auto nWritten =
720 10813 : write(fd, pabySrc + nBytesWritten, nRemaining);
721 10813 : if (nWritten < 0)
722 : {
723 0 : bError = true;
724 0 : break;
725 : }
726 10813 : CPLAssert(static_cast<size_t>(nWritten) <= nRemaining);
727 10813 : m_nFilePos += nWritten;
728 10813 : nBytesWritten += nWritten;
729 10813 : continue;
730 : }
731 :
732 : // Small request: Fill internal buffer
733 2434150 : const size_t nFreeSpaceInBuffer = BUFFER_SIZE - m_nBufferSize;
734 2434150 : const size_t nToCopy = std::min(nRemaining, nFreeSpaceInBuffer);
735 2434150 : memcpy(m_abyBuffer.data() + m_nBufferSize, pabySrc + nBytesWritten,
736 : nToCopy);
737 :
738 2434150 : nBytesWritten += nToCopy;
739 2434150 : m_nBufferSize += nToCopy;
740 2434150 : m_nFilePos += nToCopy;
741 2434150 : m_bBufferDirty = true;
742 :
743 2434150 : if (m_nBufferSize == BUFFER_SIZE && Flush() < 0)
744 : {
745 0 : break;
746 : }
747 : }
748 :
749 : #ifdef VSI_DEBUG
750 : const int nError = errno;
751 : VSIDebug4("VSIUnixStdioHandle::Write(%d,%ld) = %" PRId64, fd,
752 : static_cast<long>(nBytes), static_cast<int64_t>(nBytesWritten));
753 : errno = nError;
754 : #endif
755 :
756 2430340 : eLastOp = Operation::WRITE;
757 :
758 2430340 : return nBytesWritten;
759 : }
760 :
761 : /************************************************************************/
762 : /* ClearErr() */
763 : /************************************************************************/
764 :
765 8117 : void VSIUnixStdioHandle::ClearErr()
766 :
767 : {
768 8117 : bAtEOF = false;
769 8117 : bError = false;
770 8117 : }
771 :
772 : /************************************************************************/
773 : /* Error() */
774 : /************************************************************************/
775 :
776 62376 : int VSIUnixStdioHandle::Error()
777 :
778 : {
779 62376 : return bError ? TRUE : FALSE;
780 : }
781 :
782 : /************************************************************************/
783 : /* Eof() */
784 : /************************************************************************/
785 :
786 158039 : int VSIUnixStdioHandle::Eof()
787 :
788 : {
789 158039 : return bAtEOF ? TRUE : FALSE;
790 : }
791 :
792 : /************************************************************************/
793 : /* Truncate() */
794 : /************************************************************************/
795 :
796 614 : int VSIUnixStdioHandle::Truncate(vsi_l_offset nNewSize)
797 : {
798 614 : if (Flush() != 0)
799 0 : return -1;
800 614 : return VSI_FTRUNCATE64(fd, nNewSize);
801 : }
802 :
803 : /************************************************************************/
804 : /* GetRangeStatus() */
805 : /************************************************************************/
806 :
807 : #ifdef __linux
808 : #if !defined(MISSING_LINUX_FS_H)
809 : #include <linux/fs.h> // FS_IOC_FIEMAP
810 : #endif
811 : #ifdef FS_IOC_FIEMAP
812 : #include <linux/types.h> // for types used in linux/fiemap.h
813 : #include <linux/fiemap.h> // struct fiemap
814 : #endif
815 : #include <sys/ioctl.h>
816 : #include <errno.h>
817 : #endif
818 :
819 552 : VSIRangeStatus VSIUnixStdioHandle::GetRangeStatus(vsi_l_offset
820 : #ifdef FS_IOC_FIEMAP
821 : nOffset
822 : #endif
823 : ,
824 : vsi_l_offset
825 : #ifdef FS_IOC_FIEMAP
826 : nLength
827 : #endif
828 : )
829 : {
830 : #ifdef FS_IOC_FIEMAP
831 : // fiemap IOCTL documented at
832 : // https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt
833 :
834 : // The fiemap struct contains a "variable length" array at its end
835 : // As we are interested in only one extent, we allocate the base size of
836 : // fiemap + one fiemap_extent.
837 : GByte abyBuffer[sizeof(struct fiemap) + sizeof(struct fiemap_extent)];
838 552 : struct fiemap *psExtentMap = reinterpret_cast<struct fiemap *>(&abyBuffer);
839 552 : memset(psExtentMap, 0,
840 : sizeof(struct fiemap) + sizeof(struct fiemap_extent));
841 552 : psExtentMap->fm_start = nOffset;
842 552 : psExtentMap->fm_length = nLength;
843 552 : psExtentMap->fm_extent_count = 1;
844 552 : int ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
845 552 : if (ret < 0)
846 0 : return VSI_RANGE_STATUS_UNKNOWN;
847 552 : if (psExtentMap->fm_mapped_extents == 0)
848 2 : return VSI_RANGE_STATUS_HOLE;
849 : // In case there is one extent with unknown status, retry after having
850 : // asked the kernel to sync the file.
851 550 : const fiemap_extent *pasExtent = &(psExtentMap->fm_extents[0]);
852 550 : if (psExtentMap->fm_mapped_extents == 1 &&
853 550 : (pasExtent[0].fe_flags & FIEMAP_EXTENT_UNKNOWN) != 0)
854 : {
855 20 : psExtentMap->fm_flags = FIEMAP_FLAG_SYNC;
856 20 : psExtentMap->fm_start = nOffset;
857 20 : psExtentMap->fm_length = nLength;
858 20 : psExtentMap->fm_extent_count = 1;
859 20 : ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
860 20 : if (ret < 0)
861 0 : return VSI_RANGE_STATUS_UNKNOWN;
862 20 : if (psExtentMap->fm_mapped_extents == 0)
863 0 : return VSI_RANGE_STATUS_HOLE;
864 : }
865 550 : return VSI_RANGE_STATUS_DATA;
866 : #else
867 : static bool bMessageEmitted = false;
868 : if (!bMessageEmitted)
869 : {
870 : CPLDebug("VSI", "Sorry: GetExtentStatus() not implemented for "
871 : "this operating system");
872 : bMessageEmitted = true;
873 : }
874 : return VSI_RANGE_STATUS_UNKNOWN;
875 : #endif
876 : }
877 :
878 : /************************************************************************/
879 : /* HasPRead() */
880 : /************************************************************************/
881 :
882 : #if defined(HAVE_PREAD64) || (defined(HAVE_PREAD_BSD) && SIZEOF_OFF_T == 8)
883 730 : bool VSIUnixStdioHandle::HasPRead() const
884 : {
885 730 : return true;
886 : }
887 :
888 : /************************************************************************/
889 : /* PRead() */
890 : /************************************************************************/
891 :
892 24500 : size_t VSIUnixStdioHandle::PRead(void *pBuffer, size_t nSize,
893 : vsi_l_offset nOffset) const
894 : {
895 : #ifdef HAVE_PREAD64
896 24500 : return pread64(fd, pBuffer, nSize, nOffset);
897 : #else
898 : return pread(fd, pBuffer, nSize, static_cast<off_t>(nOffset));
899 : #endif
900 : }
901 : #endif
902 :
903 : /************************************************************************/
904 : /* ==================================================================== */
905 : /* VSIUnixStdioFilesystemHandler */
906 : /* ==================================================================== */
907 : /************************************************************************/
908 :
909 : #ifdef VSI_COUNT_BYTES_READ
910 : /************************************************************************/
911 : /* ~VSIUnixStdioFilesystemHandler() */
912 : /************************************************************************/
913 :
914 : VSIUnixStdioFilesystemHandler::~VSIUnixStdioFilesystemHandler()
915 : {
916 : CPLDebug(
917 : "VSI",
918 : "~VSIUnixStdioFilesystemHandler() : nTotalBytesRead = " CPL_FRMT_GUIB,
919 : nTotalBytesRead);
920 :
921 : if (hMutex != nullptr)
922 : CPLDestroyMutex(hMutex);
923 : hMutex = nullptr;
924 : }
925 : #endif
926 :
927 : /************************************************************************/
928 : /* Open() */
929 : /************************************************************************/
930 :
931 : VSIVirtualHandleUniquePtr
932 219856 : VSIUnixStdioFilesystemHandler::Open(const char *pszFilename,
933 : const char *pszAccess, bool bSetError,
934 : CSLConstList /* papszOptions */)
935 :
936 : {
937 219856 : int flags = O_RDONLY;
938 : int fd;
939 : using AccessMode = VSIUnixStdioHandle::AccessMode;
940 219856 : AccessMode eAccessMode = AccessMode::NORMAL;
941 219856 : if (strchr(pszAccess, 'w'))
942 : {
943 33744 : if (strchr(pszAccess, '+'))
944 6453 : flags = O_CREAT | O_TRUNC | O_RDWR;
945 : else
946 : {
947 27291 : eAccessMode = AccessMode::WRITE_ONLY;
948 27291 : flags = O_CREAT | O_TRUNC | O_WRONLY;
949 : }
950 33744 : fd = VSI_OPEN64(pszFilename, flags, 0664);
951 : }
952 186112 : else if (strchr(pszAccess, 'a'))
953 : {
954 170 : if (strchr(pszAccess, '+'))
955 : {
956 120 : eAccessMode = AccessMode::APPEND_READ_WRITE;
957 120 : flags = O_CREAT | O_RDWR;
958 : }
959 : else
960 50 : flags = O_CREAT | O_WRONLY;
961 170 : fd = VSI_OPEN64(pszFilename, flags, 0664);
962 : }
963 : else
964 : {
965 185942 : if (strchr(pszAccess, '+'))
966 7837 : flags = O_RDWR;
967 : else
968 178105 : eAccessMode = AccessMode::READ_ONLY;
969 185942 : fd = VSI_OPEN64(pszFilename, flags);
970 : }
971 218423 : const int nError = errno;
972 :
973 : VSIDebug3("VSIUnixStdioFilesystemHandler::Open(\"%s\",\"%s\") = %d",
974 : pszFilename, pszAccess, fd);
975 :
976 218423 : if (fd < 0)
977 : {
978 81913 : if (bSetError)
979 : {
980 17475 : VSIError(VSIE_FileError, "%s: %s", pszFilename, strerror(nError));
981 : }
982 81913 : errno = nError;
983 81913 : return nullptr;
984 : }
985 :
986 273599 : auto poHandle = std::make_unique<VSIUnixStdioHandle>(this, fd, eAccessMode);
987 137195 : poHandle->m_osFilename = pszFilename;
988 :
989 137358 : errno = nError;
990 :
991 137358 : if (strchr(pszAccess, 'a'))
992 170 : poHandle->Seek(0, SEEK_END);
993 :
994 : /* -------------------------------------------------------------------- */
995 : /* If VSI_CACHE is set we want to use a cached reader instead */
996 : /* of more direct io on the underlying file. */
997 : /* -------------------------------------------------------------------- */
998 235201 : if (eAccessMode == AccessMode::READ_ONLY &&
999 97328 : CPLTestBool(CPLGetConfigOption("VSI_CACHE", "FALSE")))
1000 : {
1001 : return VSIVirtualHandleUniquePtr(
1002 5 : VSICreateCachedFile(poHandle.release()));
1003 : }
1004 :
1005 137868 : return VSIVirtualHandleUniquePtr(poHandle.release());
1006 : }
1007 :
1008 : /************************************************************************/
1009 : /* CreateOnlyVisibleAtCloseTime() */
1010 : /************************************************************************/
1011 :
1012 : VSIVirtualHandleUniquePtr
1013 168 : VSIUnixStdioFilesystemHandler::CreateOnlyVisibleAtCloseTime(
1014 : const char *pszFilename, bool bEmulationAllowed, CSLConstList papszOptions)
1015 : {
1016 : #ifdef __linux
1017 36 : static bool bIsLinkatSupported = []()
1018 : {
1019 : // Check that /proc is accessible, since we will need it to run linkat()
1020 : struct stat statbuf;
1021 36 : return stat("/proc/self/fd", &statbuf) == 0;
1022 168 : }();
1023 :
1024 : const int fd = bIsLinkatSupported
1025 336 : ? VSI_OPEN64(CPLGetDirnameSafe(pszFilename).c_str(),
1026 : O_TMPFILE | O_RDWR, 0666)
1027 168 : : -1;
1028 168 : if (fd < 0)
1029 : {
1030 134 : if (bIsLinkatSupported)
1031 : {
1032 134 : CPLDebugOnce("VSI",
1033 : "open(O_TMPFILE | O_RDWR) failed with errno=%d (%s). "
1034 : "Going through emulation",
1035 : errno, VSIStrerror(errno));
1036 : }
1037 : return VSIFilesystemHandler::CreateOnlyVisibleAtCloseTime(
1038 134 : pszFilename, bEmulationAllowed, papszOptions);
1039 : }
1040 :
1041 : auto poHandle = std::make_unique<VSIUnixStdioHandle>(
1042 68 : this, fd, VSIUnixStdioHandle::AccessMode::NORMAL);
1043 34 : poHandle->m_osFilename = pszFilename;
1044 34 : poHandle->m_bUnlinkedFile = true;
1045 34 : return VSIVirtualHandleUniquePtr(poHandle.release());
1046 : #else
1047 : if (!bEmulationAllowed)
1048 : return nullptr;
1049 :
1050 : std::string osTmpFilename = std::string(pszFilename).append("XXXXXX");
1051 : int fd = mkstemp(osTmpFilename.data());
1052 : if (fd < 0)
1053 : {
1054 : return VSIFilesystemHandler::CreateOnlyVisibleAtCloseTime(
1055 : pszFilename, bEmulationAllowed, papszOptions);
1056 : }
1057 : auto poHandle = std::make_unique<VSIUnixStdioHandle>(
1058 : this, fd, VSIUnixStdioHandle::AccessMode::NORMAL);
1059 : poHandle->m_osTmpFilename = std::move(osTmpFilename);
1060 : poHandle->m_osFilename = pszFilename;
1061 : return VSIVirtualHandleUniquePtr(poHandle.release());
1062 : #endif
1063 : }
1064 :
1065 : /************************************************************************/
1066 : /* Stat() */
1067 : /************************************************************************/
1068 :
1069 255912 : int VSIUnixStdioFilesystemHandler::Stat(const char *pszFilename,
1070 : VSIStatBufL *pStatBuf, int /* nFlags */)
1071 : {
1072 255912 : return (VSI_STAT64(pszFilename, pStatBuf));
1073 : }
1074 :
1075 : /************************************************************************/
1076 : /* Unlink() */
1077 : /************************************************************************/
1078 :
1079 5231 : int VSIUnixStdioFilesystemHandler::Unlink(const char *pszFilename)
1080 :
1081 : {
1082 5231 : return unlink(pszFilename);
1083 : }
1084 :
1085 : /************************************************************************/
1086 : /* Rename() */
1087 : /************************************************************************/
1088 :
1089 992 : int VSIUnixStdioFilesystemHandler::Rename(const char *oldpath,
1090 : const char *newpath, GDALProgressFunc,
1091 : void *)
1092 :
1093 : {
1094 992 : return rename(oldpath, newpath);
1095 : }
1096 :
1097 : /************************************************************************/
1098 : /* Mkdir() */
1099 : /************************************************************************/
1100 :
1101 1727 : int VSIUnixStdioFilesystemHandler::Mkdir(const char *pszPathname, long nMode)
1102 :
1103 : {
1104 1727 : return mkdir(pszPathname, static_cast<int>(nMode));
1105 : }
1106 :
1107 : /************************************************************************/
1108 : /* Rmdir() */
1109 : /************************************************************************/
1110 :
1111 63 : int VSIUnixStdioFilesystemHandler::Rmdir(const char *pszPathname)
1112 :
1113 : {
1114 63 : return rmdir(pszPathname);
1115 : }
1116 :
1117 : /************************************************************************/
1118 : /* ReadDirEx() */
1119 : /************************************************************************/
1120 :
1121 22038 : char **VSIUnixStdioFilesystemHandler::ReadDirEx(const char *pszPath,
1122 : int nMaxFiles)
1123 :
1124 : {
1125 22038 : if (strlen(pszPath) == 0)
1126 17 : pszPath = ".";
1127 :
1128 44076 : CPLStringList oDir;
1129 22038 : DIR *hDir = opendir(pszPath);
1130 22038 : if (hDir != nullptr)
1131 : {
1132 : // We want to avoid returning NULL for an empty list.
1133 21975 : oDir.Assign(static_cast<char **>(CPLCalloc(2, sizeof(char *))));
1134 :
1135 21975 : struct dirent *psDirEntry = nullptr;
1136 2012630 : while ((psDirEntry = readdir(hDir)) != nullptr)
1137 : {
1138 1990660 : oDir.AddString(psDirEntry->d_name);
1139 1990660 : if (nMaxFiles > 0 && oDir.Count() > nMaxFiles)
1140 7 : break;
1141 : }
1142 :
1143 21975 : closedir(hDir);
1144 : }
1145 : else
1146 : {
1147 : // Should we generate an error?
1148 : // For now we'll just return NULL (at the end of the function).
1149 : }
1150 :
1151 44076 : return oDir.StealList();
1152 : }
1153 :
1154 : /************************************************************************/
1155 : /* GetDiskFreeSpace() */
1156 : /************************************************************************/
1157 :
1158 3 : GIntBig VSIUnixStdioFilesystemHandler::GetDiskFreeSpace(const char *
1159 : #ifdef HAVE_STATVFS
1160 : pszDirname
1161 : #endif
1162 : )
1163 : {
1164 3 : GIntBig nRet = -1;
1165 : #ifdef HAVE_STATVFS
1166 :
1167 : #ifdef HAVE_STATVFS64
1168 : struct statvfs64 buf;
1169 3 : if (statvfs64(pszDirname, &buf) == 0)
1170 : {
1171 2 : nRet = static_cast<GIntBig>(buf.f_frsize *
1172 2 : static_cast<GUIntBig>(buf.f_bavail));
1173 : }
1174 : #else
1175 : struct statvfs buf;
1176 : if (statvfs(pszDirname, &buf) == 0)
1177 : {
1178 : nRet = static_cast<GIntBig>(buf.f_frsize *
1179 : static_cast<GUIntBig>(buf.f_bavail));
1180 : }
1181 : #endif
1182 :
1183 : #endif
1184 3 : return nRet;
1185 : }
1186 :
1187 : /************************************************************************/
1188 : /* SupportsSparseFiles() */
1189 : /************************************************************************/
1190 :
1191 : #ifdef __linux
1192 : #include <sys/vfs.h>
1193 : #endif
1194 :
1195 2 : int VSIUnixStdioFilesystemHandler::SupportsSparseFiles(const char *
1196 : #ifdef __linux
1197 : pszPath
1198 : #endif
1199 : )
1200 : {
1201 : #ifdef __linux
1202 : struct statfs sStatFS;
1203 2 : if (statfs(pszPath, &sStatFS) == 0)
1204 : {
1205 : // Add here any missing filesystem supporting sparse files.
1206 : // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
1207 2 : switch (static_cast<unsigned>(sStatFS.f_type))
1208 : {
1209 : // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
1210 2 : case 0xef53U: // ext2, 3, 4
1211 : case 0x52654973U: // reiser
1212 : case 0x58465342U: // xfs
1213 : case 0x3153464aU: // jfs
1214 : case 0x5346544eU: // ntfs
1215 : case 0x9123683eU: // brfs
1216 : // nfs: NFS < 4.2 supports creating sparse files (but reading them
1217 : // not efficiently).
1218 : case 0x6969U:
1219 : case 0x01021994U: // tmpfs
1220 2 : return TRUE;
1221 :
1222 0 : case 0x4d44U: // msdos
1223 0 : return FALSE;
1224 :
1225 0 : case 0x53464846U: // Windows Subsystem for Linux fs
1226 : {
1227 : static bool bUnknownFSEmitted = false;
1228 0 : if (!bUnknownFSEmitted)
1229 : {
1230 0 : CPLDebug("VSI",
1231 : "Windows Subsystem for Linux FS is at "
1232 : "the time of writing not known to support sparse "
1233 : "files");
1234 0 : bUnknownFSEmitted = true;
1235 : }
1236 0 : return FALSE;
1237 : }
1238 :
1239 0 : default:
1240 : {
1241 : static bool bUnknownFSEmitted = false;
1242 0 : if (!bUnknownFSEmitted)
1243 : {
1244 0 : CPLDebug("VSI",
1245 : "Filesystem with type %X unknown. "
1246 : "Assuming it does not support sparse files",
1247 0 : static_cast<int>(sStatFS.f_type));
1248 0 : bUnknownFSEmitted = true;
1249 : }
1250 0 : return FALSE;
1251 : }
1252 : }
1253 : }
1254 0 : return FALSE;
1255 : #else
1256 : static bool bMessageEmitted = false;
1257 : if (!bMessageEmitted)
1258 : {
1259 : CPLDebug("VSI", "Sorry: SupportsSparseFiles() not implemented "
1260 : "for this operating system");
1261 : bMessageEmitted = true;
1262 : }
1263 : return FALSE;
1264 : #endif
1265 : }
1266 :
1267 : /************************************************************************/
1268 : /* IsLocal() */
1269 : /************************************************************************/
1270 :
1271 1553 : bool VSIUnixStdioFilesystemHandler::IsLocal(const char *
1272 : #ifdef __linux
1273 : pszPath
1274 : #endif
1275 : ) const
1276 : {
1277 : #ifdef __linux
1278 : struct statfs sStatFS;
1279 1553 : if (statfs(pszPath, &sStatFS) == 0)
1280 : {
1281 : // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
1282 180 : switch (static_cast<unsigned>(sStatFS.f_type))
1283 : {
1284 : // Codes from http://man7.org/linux/man-pages/man2/statfs.2.html
1285 0 : case 0x6969U: // NFS
1286 : case 0x517bU: // SMB
1287 : case 0xff534d42U: // CIFS
1288 : case 0xfe534d42U: // SMB2
1289 : // (https://github.com/libuv/libuv/blob/97dcdb1926f6aca43171e1614338bcef067abd59/src/unix/fs.c#L960)
1290 0 : return false;
1291 : }
1292 : }
1293 : #else
1294 : static bool bMessageEmitted = false;
1295 : if (!bMessageEmitted)
1296 : {
1297 : CPLDebug("VSI", "Sorry: IsLocal() not implemented "
1298 : "for this operating system");
1299 : bMessageEmitted = true;
1300 : }
1301 : #endif
1302 1553 : return true;
1303 : }
1304 :
1305 : /************************************************************************/
1306 : /* SupportsSequentialWrite() */
1307 : /************************************************************************/
1308 :
1309 166 : bool VSIUnixStdioFilesystemHandler::SupportsSequentialWrite(
1310 : const char *pszPath, bool /* bAllowLocalTempFile */)
1311 : {
1312 : VSIStatBufL sStat;
1313 166 : if (VSIStatL(pszPath, &sStat) == 0)
1314 62 : return access(pszPath, W_OK) == 0;
1315 104 : return access(CPLGetDirnameSafe(pszPath).c_str(), W_OK) == 0;
1316 : }
1317 :
1318 : /************************************************************************/
1319 : /* SupportsRandomWrite() */
1320 : /************************************************************************/
1321 :
1322 104 : bool VSIUnixStdioFilesystemHandler::SupportsRandomWrite(
1323 : const char *pszPath, bool /* bAllowLocalTempFile */)
1324 : {
1325 104 : return SupportsSequentialWrite(pszPath, false);
1326 : }
1327 :
1328 : /************************************************************************/
1329 : /* VSIDIRUnixStdio */
1330 : /************************************************************************/
1331 :
1332 : struct VSIDIRUnixStdio final : public VSIDIR
1333 : {
1334 : struct DIRCloser
1335 : {
1336 916 : void operator()(DIR *d)
1337 : {
1338 916 : if (d)
1339 916 : closedir(d);
1340 916 : }
1341 : };
1342 :
1343 : CPLString osRootPath{};
1344 : CPLString osBasePath{};
1345 : std::unique_ptr<DIR, DIRCloser> m_psDir{};
1346 : int nRecurseDepth = 0;
1347 : VSIDIREntry entry{};
1348 : std::vector<std::unique_ptr<VSIDIR>> aoStackSubDir{};
1349 : std::string m_osFilterPrefix{};
1350 : bool m_bNameAndTypeOnly = false;
1351 :
1352 : const VSIDIREntry *NextDirEntry() override;
1353 : };
1354 :
1355 : /************************************************************************/
1356 : /* OpenDirInternal() */
1357 : /************************************************************************/
1358 :
1359 : /* static */
1360 929 : std::unique_ptr<VSIDIRUnixStdio> VSIUnixStdioFilesystemHandler::OpenDirInternal(
1361 : const char *pszPath, int nRecurseDepth, const char *const *papszOptions)
1362 : {
1363 1858 : std::unique_ptr<DIR, VSIDIRUnixStdio::DIRCloser> psDir(opendir(pszPath));
1364 929 : if (psDir == nullptr)
1365 : {
1366 13 : return nullptr;
1367 : }
1368 1832 : auto dir = std::make_unique<VSIDIRUnixStdio>();
1369 916 : dir->osRootPath = pszPath;
1370 916 : dir->nRecurseDepth = nRecurseDepth;
1371 916 : dir->m_psDir = std::move(psDir);
1372 916 : dir->m_osFilterPrefix = CSLFetchNameValueDef(papszOptions, "PREFIX", "");
1373 916 : dir->m_bNameAndTypeOnly = CPLTestBool(
1374 : CSLFetchNameValueDef(papszOptions, "NAME_AND_TYPE_ONLY", "NO"));
1375 916 : return dir;
1376 : }
1377 :
1378 : /************************************************************************/
1379 : /* OpenDir() */
1380 : /************************************************************************/
1381 :
1382 271 : VSIDIR *VSIUnixStdioFilesystemHandler::OpenDir(const char *pszPath,
1383 : int nRecurseDepth,
1384 : const char *const *papszOptions)
1385 : {
1386 271 : return OpenDirInternal(pszPath, nRecurseDepth, papszOptions).release();
1387 : }
1388 :
1389 : /************************************************************************/
1390 : /* NextDirEntry() */
1391 : /************************************************************************/
1392 :
1393 24990 : const VSIDIREntry *VSIDIRUnixStdio::NextDirEntry()
1394 : {
1395 24990 : begin:
1396 24990 : if (VSI_ISDIR(entry.nMode) && nRecurseDepth != 0)
1397 : {
1398 1316 : CPLString osCurFile(osRootPath);
1399 658 : if (!osCurFile.empty())
1400 658 : osCurFile += '/';
1401 658 : osCurFile += entry.pszName;
1402 : auto subdir = VSIUnixStdioFilesystemHandler::OpenDirInternal(
1403 658 : osCurFile, nRecurseDepth - 1, nullptr);
1404 658 : if (subdir)
1405 : {
1406 658 : subdir->osRootPath = osRootPath;
1407 658 : subdir->osBasePath = entry.pszName;
1408 658 : subdir->m_osFilterPrefix = m_osFilterPrefix;
1409 658 : subdir->m_bNameAndTypeOnly = m_bNameAndTypeOnly;
1410 658 : aoStackSubDir.push_back(std::move(subdir));
1411 : }
1412 658 : entry.nMode = 0;
1413 : }
1414 :
1415 25648 : while (!aoStackSubDir.empty())
1416 : {
1417 13228 : auto l_entry = aoStackSubDir.back()->NextDirEntry();
1418 13228 : if (l_entry)
1419 : {
1420 12570 : return l_entry;
1421 : }
1422 658 : aoStackSubDir.pop_back();
1423 : }
1424 :
1425 14244 : while (const auto *psEntry = readdir(m_psDir.get()))
1426 : {
1427 : // Skip . and ..entries
1428 13337 : if (psEntry->d_name[0] == '.' &&
1429 1830 : (psEntry->d_name[1] == '\0' ||
1430 922 : (psEntry->d_name[1] == '.' && psEntry->d_name[2] == '\0')))
1431 : {
1432 : // do nothing
1433 : }
1434 : else
1435 : {
1436 11518 : CPLFree(entry.pszName);
1437 11518 : CPLString osName(osBasePath);
1438 11518 : if (!osName.empty())
1439 7343 : osName += '/';
1440 11518 : osName += psEntry->d_name;
1441 :
1442 11518 : entry.pszName = CPLStrdup(osName);
1443 11518 : entry.nMode = 0;
1444 11518 : entry.nSize = 0;
1445 11518 : entry.nMTime = 0;
1446 11518 : entry.bModeKnown = false;
1447 11518 : entry.bSizeKnown = false;
1448 11518 : entry.bMTimeKnown = false;
1449 :
1450 11518 : CPLString osCurFile(osRootPath);
1451 11518 : if (!osCurFile.empty())
1452 11518 : osCurFile += '/';
1453 11518 : osCurFile += entry.pszName;
1454 :
1455 : #if !defined(__sun) && !defined(__HAIKU__)
1456 11518 : if (psEntry->d_type == DT_REG)
1457 9376 : entry.nMode = S_IFREG;
1458 2142 : else if (psEntry->d_type == DT_DIR)
1459 745 : entry.nMode = S_IFDIR;
1460 1397 : else if (psEntry->d_type == DT_LNK)
1461 1397 : entry.nMode = S_IFLNK;
1462 : #endif
1463 :
1464 18004 : const auto StatFile = [&osCurFile, this]()
1465 : {
1466 : VSIStatBufL sStatL;
1467 9002 : if (VSIStatL(osCurFile, &sStatL) == 0)
1468 : {
1469 9002 : entry.nMode = sStatL.st_mode;
1470 9002 : entry.nSize = sStatL.st_size;
1471 9002 : entry.nMTime = sStatL.st_mtime;
1472 9002 : entry.bModeKnown = true;
1473 9002 : entry.bSizeKnown = true;
1474 9002 : entry.bMTimeKnown = true;
1475 : }
1476 9002 : };
1477 :
1478 11530 : if (!m_osFilterPrefix.empty() &&
1479 12 : m_osFilterPrefix.size() > osName.size())
1480 : {
1481 6 : if (STARTS_WITH(m_osFilterPrefix.c_str(), osName.c_str()) &&
1482 2 : m_osFilterPrefix[osName.size()] == '/')
1483 : {
1484 : #if !defined(__sun) && !defined(__HAIKU__)
1485 1 : if (psEntry->d_type == DT_UNKNOWN)
1486 : #endif
1487 : {
1488 0 : StatFile();
1489 : }
1490 1 : if (VSI_ISDIR(entry.nMode))
1491 : {
1492 1 : goto begin;
1493 : }
1494 : }
1495 3 : continue;
1496 : }
1497 11522 : if (!m_osFilterPrefix.empty() &&
1498 8 : !STARTS_WITH(osName.c_str(), m_osFilterPrefix.c_str()))
1499 : {
1500 2 : continue;
1501 : }
1502 :
1503 11512 : if (!m_bNameAndTypeOnly
1504 : #if !defined(__sun) && !defined(__HAIKU__)
1505 2510 : || psEntry->d_type == DT_UNKNOWN
1506 : #endif
1507 : )
1508 : {
1509 9002 : StatFile();
1510 : }
1511 :
1512 11512 : return &(entry);
1513 : }
1514 1824 : }
1515 :
1516 907 : return nullptr;
1517 : }
1518 :
1519 : #ifdef VSI_COUNT_BYTES_READ
1520 : /************************************************************************/
1521 : /* AddToTotal() */
1522 : /************************************************************************/
1523 :
1524 : void VSIUnixStdioFilesystemHandler::AddToTotal(vsi_l_offset nBytes)
1525 : {
1526 : CPLMutexHolder oHolder(&hMutex);
1527 : nTotalBytesRead += nBytes;
1528 : }
1529 :
1530 : #endif
1531 :
1532 : /************************************************************************/
1533 : /* GetCanonicalFilename() */
1534 : /************************************************************************/
1535 :
1536 : #ifdef HAS_CASE_INSENSITIVE_FILE_SYSTEM
1537 : std::string VSIUnixStdioFilesystemHandler::GetCanonicalFilename(
1538 : const std::string &osFilename) const
1539 : {
1540 : char szResolvedPath[PATH_MAX];
1541 : const char *pszFilename = osFilename.c_str();
1542 : if (realpath(pszFilename, szResolvedPath))
1543 : {
1544 : const char *pszFilenameLastPart = strrchr(pszFilename, '/');
1545 : const char *pszResolvedFilenameLastPart = strrchr(szResolvedPath, '/');
1546 : if (pszFilenameLastPart && pszResolvedFilenameLastPart &&
1547 : EQUAL(pszFilenameLastPart, pszResolvedFilenameLastPart))
1548 : {
1549 : std::string osRet;
1550 : osRet.assign(pszFilename, pszFilenameLastPart - pszFilename);
1551 : osRet += pszResolvedFilenameLastPart;
1552 : return osRet;
1553 : }
1554 : return szResolvedPath;
1555 : }
1556 : return osFilename;
1557 : }
1558 : #endif
1559 :
1560 : /************************************************************************/
1561 : /* VSIInstallLargeFileHandler() */
1562 : /************************************************************************/
1563 :
1564 1793 : void VSIInstallLargeFileHandler()
1565 :
1566 : {
1567 1793 : VSIFileManager::InstallHandler(
1568 3586 : "", std::make_shared<VSIUnixStdioFilesystemHandler>());
1569 1793 : }
1570 :
1571 : #endif // ndef WIN32
1572 :
1573 : //! @endcond
|