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 1789 : 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 133195 : VSIUnixStdioHandle::VSIUnixStdioHandle(
299 : #ifndef VSI_COUNT_BYTES_READ
300 : CPL_UNUSED
301 : #endif
302 : VSIUnixStdioFilesystemHandler *poFSIn,
303 133195 : int fdIn, AccessMode eAccessModeIn)
304 133195 : : fd(fdIn), eAccessMode(eAccessModeIn)
305 : #ifdef VSI_COUNT_BYTES_READ
306 : ,
307 : poFS(poFSIn)
308 : #endif
309 : {
310 133083 : }
311 :
312 : /************************************************************************/
313 : /* ~VSIUnixStdioHandle() */
314 : /************************************************************************/
315 :
316 265431 : VSIUnixStdioHandle::~VSIUnixStdioHandle()
317 : {
318 132376 : VSIUnixStdioHandle::Close();
319 265662 : }
320 :
321 : /************************************************************************/
322 : /* Close() */
323 : /************************************************************************/
324 :
325 269814 : int VSIUnixStdioHandle::Close()
326 :
327 : {
328 269814 : if (fd < 0)
329 136389 : return 0;
330 :
331 : VSIDebug1("VSIUnixStdioHandle::Close(%d)", fd);
332 :
333 : #ifdef VSI_COUNT_BYTES_READ
334 : poFS->AddToTotal(nTotalBytesRead);
335 : #endif
336 :
337 133425 : int ret = VSIUnixStdioHandle::Flush();
338 :
339 : #ifdef __linux
340 265092 : if (ret == 0 && !m_bCancelCreation && !m_osFilename.empty() &&
341 132224 : 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 133287 : int ret2 = close(fd);
360 132811 : 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 132811 : fd = -1;
371 132811 : return ret;
372 : }
373 :
374 : /************************************************************************/
375 : /* CancelCreation() */
376 : /************************************************************************/
377 :
378 519 : void VSIUnixStdioHandle::CancelCreation()
379 : {
380 : #if defined(__linux)
381 519 : if (!m_osFilename.empty() && !m_bUnlinkedFile)
382 : {
383 451 : unlink(m_osFilename.c_str());
384 451 : 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 519 : }
401 :
402 : /************************************************************************/
403 : /* Seek() */
404 : /************************************************************************/
405 :
406 4182420 : int VSIUnixStdioHandle::Seek(vsi_l_offset nOffsetIn, int nWhence)
407 : {
408 4182420 : bAtEOF = false;
409 4182420 : bError = false;
410 :
411 : // Seeks that do nothing are still surprisingly expensive with MSVCRT.
412 : // try and short circuit if possible.
413 4182420 : if (eAccessMode != AccessMode::APPEND_READ_WRITE && nWhence == SEEK_SET &&
414 3580400 : nOffsetIn == m_nFilePos)
415 740656 : 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 3441770 : if (eLastOp == Operation::WRITE)
428 : {
429 287125 : eLastOp = Operation::NONE;
430 :
431 287125 : if (m_bBufferDirty && Flush() < 0)
432 0 : return -1;
433 :
434 287125 : const auto nNewPos = VSI_LSEEK64(fd, nOffsetIn, nWhence);
435 287125 : if (nNewPos < 0)
436 : {
437 0 : bError = true;
438 0 : return -1;
439 : }
440 :
441 287125 : m_nFilePos = nNewPos;
442 287125 : m_nBufferCurPos = 0;
443 287125 : m_nBufferSize = 0;
444 : }
445 :
446 : else
447 : {
448 3154640 : eLastOp = Operation::READ;
449 :
450 : vsi_l_offset nTargetPos;
451 :
452 : // Compute absolute target position
453 3154640 : switch (nWhence)
454 : {
455 2747700 : case SEEK_SET:
456 2747700 : nTargetPos = nOffsetIn;
457 2747700 : break;
458 81490 : case SEEK_CUR:
459 81490 : nTargetPos = m_nFilePos + nOffsetIn;
460 81490 : break;
461 325172 : case SEEK_END:
462 : {
463 325172 : const auto nNewPos = VSI_LSEEK64(fd, nOffsetIn, SEEK_END);
464 325075 : if (nNewPos < 0)
465 : {
466 0 : bError = true;
467 0 : return -1;
468 : }
469 325075 : nTargetPos = nNewPos;
470 325075 : break;
471 : }
472 284 : default:
473 284 : return -1;
474 : }
475 :
476 : // Compute absolute position of the current buffer
477 3154260 : const auto nBufStartOffset = m_nFilePos - m_nBufferCurPos;
478 3154260 : const auto nBufEndOffset = nBufStartOffset + m_nBufferSize;
479 :
480 3154260 : if (nTargetPos >= nBufStartOffset && nTargetPos < nBufEndOffset)
481 : {
482 : // Seek inside current buffer - adjust buffer pos only
483 2139440 : m_nBufferCurPos = static_cast<size_t>(nTargetPos - nBufStartOffset);
484 2139440 : m_nFilePos = nTargetPos;
485 : }
486 : else
487 : {
488 : // Outside buffer, do real seek and invalidate buffer
489 1014820 : const auto nNewPos = VSI_LSEEK64(fd, nTargetPos, SEEK_SET);
490 1014930 : if (nNewPos < 0)
491 : {
492 1 : bError = true;
493 1 : return -1;
494 : }
495 :
496 1014930 : m_nFilePos = nNewPos;
497 1014930 : m_nBufferCurPos = 0;
498 1014930 : 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 3441500 : return 0;
506 : }
507 :
508 : /************************************************************************/
509 : /* Tell() */
510 : /************************************************************************/
511 :
512 3106110 : vsi_l_offset VSIUnixStdioHandle::Tell()
513 :
514 : {
515 3106110 : return m_nFilePos;
516 : }
517 :
518 : /************************************************************************/
519 : /* Flush() */
520 : /************************************************************************/
521 :
522 447800 : int VSIUnixStdioHandle::Flush()
523 :
524 : {
525 : VSIDebug1("VSIUnixStdioHandle::Flush(%d)", fd);
526 :
527 447800 : if (m_bBufferDirty)
528 : {
529 334058 : m_bBufferDirty = false;
530 :
531 : // Sync kernel offset to our logical position before writing
532 334058 : if (VSI_LSEEK64(fd, m_nFilePos - m_nBufferSize, SEEK_SET) < 0)
533 : {
534 0 : return EOF;
535 : }
536 :
537 334058 : size_t nOff = 0;
538 668116 : while (m_nBufferSize > 0)
539 : {
540 334058 : errno = 0;
541 : const auto nWritten =
542 334058 : write(fd, m_abyBuffer.data() + nOff, m_nBufferSize);
543 334058 : if (nWritten < 0)
544 : {
545 0 : if (errno == EINTR)
546 0 : continue;
547 0 : return EOF;
548 : }
549 334058 : CPLAssert(static_cast<size_t>(nWritten) <= m_nBufferSize);
550 334058 : nOff += nWritten;
551 334058 : m_nBufferSize -= nWritten;
552 : }
553 : }
554 :
555 447800 : return 0;
556 : }
557 :
558 : /************************************************************************/
559 : /* Read() */
560 : /************************************************************************/
561 :
562 8773660 : size_t VSIUnixStdioHandle::Read(void *pBuffer, size_t nBytes)
563 :
564 : {
565 8773660 : if (nBytes == 0 || bAtEOF)
566 38581 : return 0;
567 8735070 : 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 8735070 : if (eLastOp == Operation::WRITE)
582 : {
583 2843 : if (m_bBufferDirty && Flush() < 0)
584 : {
585 0 : bError = true;
586 0 : return -1;
587 : }
588 2843 : if (VSI_LSEEK64(fd, m_nFilePos, SEEK_SET) < 0)
589 : {
590 0 : bError = true;
591 0 : return -1;
592 : }
593 2843 : m_nBufferCurPos = 0;
594 2843 : m_nBufferSize = 0;
595 : }
596 :
597 8735070 : const size_t nTotal = nBytes;
598 8735070 : size_t nBytesRead = 0;
599 8735070 : GByte *const pabyDest = static_cast<GByte *>(pBuffer);
600 :
601 17499800 : while (nBytesRead < nTotal)
602 : {
603 8826870 : size_t nAvailInBuffer = m_nBufferSize - m_nBufferCurPos;
604 :
605 : // If buffer is empty
606 8826870 : if (nAvailInBuffer == 0)
607 : {
608 867742 : size_t nRemaining = nTotal - nBytesRead;
609 867742 : if (nRemaining >= BUFFER_SIZE)
610 : {
611 : // Bypass buffer if large request
612 71244 : errno = 0;
613 : const ssize_t nBytesReadThisTime =
614 71244 : read(fd, pabyDest + nBytesRead, nRemaining);
615 71247 : if (nBytesReadThisTime <= 0)
616 : {
617 14190 : if (nBytesReadThisTime == 0)
618 14185 : bAtEOF = true;
619 5 : else if (errno == EINTR)
620 0 : continue;
621 : else
622 5 : bError = true;
623 62159 : break;
624 : }
625 57057 : m_nFilePos += nBytesReadThisTime;
626 57057 : nBytesRead += nBytesReadThisTime;
627 : }
628 : else
629 : {
630 : // Small request: Refill internal buffer
631 796498 : errno = 0;
632 : const ssize_t nBytesReadThisTime =
633 796498 : read(fd, m_abyBuffer.data(), BUFFER_SIZE);
634 796291 : if (nBytesReadThisTime <= 0)
635 : {
636 47969 : if (nBytesReadThisTime == 0)
637 46897 : bAtEOF = true;
638 1072 : else if (errno == EINTR)
639 0 : continue;
640 : else
641 1072 : bError = true;
642 47969 : break;
643 : }
644 748322 : m_nBufferCurPos = 0;
645 748322 : m_nBufferSize = nBytesReadThisTime;
646 748322 : nAvailInBuffer = nBytesReadThisTime;
647 : }
648 : }
649 :
650 : // Copy from buffer to user
651 8764510 : const size_t nToCopy = std::min(nTotal - nBytesRead, nAvailInBuffer);
652 8764810 : memcpy(pabyDest + nBytesRead, m_abyBuffer.data() + m_nBufferCurPos,
653 : nToCopy);
654 8764680 : nBytesRead += nToCopy;
655 8764680 : m_nBufferCurPos += nToCopy;
656 8764680 : 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 8735040 : eLastOp = Operation::READ;
667 :
668 : #ifdef VSI_COUNT_BYTES_READ
669 : nTotalBytesRead += nBytesRead;
670 : #endif
671 :
672 8735040 : return nBytesRead;
673 : }
674 :
675 : /************************************************************************/
676 : /* Write() */
677 : /************************************************************************/
678 :
679 2401320 : size_t VSIUnixStdioHandle::Write(const void *pBuffer, size_t nBytes)
680 :
681 : {
682 2401320 : if (nBytes == 0)
683 22605 : return 0;
684 2378720 : 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 2378720 : if (eLastOp == Operation::READ)
699 : {
700 39776 : if (VSI_LSEEK64(fd, m_nFilePos, SEEK_SET) < 0)
701 : {
702 0 : bError = true;
703 0 : return -1;
704 : }
705 39776 : m_nBufferCurPos = 0;
706 39776 : m_nBufferSize = 0;
707 : }
708 :
709 2378720 : const size_t nTotal = nBytes;
710 2378720 : size_t nBytesWritten = 0;
711 2378720 : const GByte *const pabySrc = static_cast<const GByte *>(pBuffer);
712 4771970 : while (nBytesWritten < nTotal)
713 : {
714 2393250 : const size_t nRemaining = nTotal - nBytesWritten;
715 :
716 : // Bypass buffer if request > BUFFER_SIZE
717 2393250 : if (m_nBufferSize == 0 && nRemaining >= BUFFER_SIZE)
718 : {
719 : const auto nWritten =
720 10743 : write(fd, pabySrc + nBytesWritten, nRemaining);
721 10743 : if (nWritten < 0)
722 : {
723 0 : bError = true;
724 0 : break;
725 : }
726 10743 : CPLAssert(static_cast<size_t>(nWritten) <= nRemaining);
727 10743 : m_nFilePos += nWritten;
728 10743 : nBytesWritten += nWritten;
729 10743 : continue;
730 : }
731 :
732 : // Small request: Fill internal buffer
733 2382510 : const size_t nFreeSpaceInBuffer = BUFFER_SIZE - m_nBufferSize;
734 2382510 : const size_t nToCopy = std::min(nRemaining, nFreeSpaceInBuffer);
735 2382510 : memcpy(m_abyBuffer.data() + m_nBufferSize, pabySrc + nBytesWritten,
736 : nToCopy);
737 :
738 2382510 : nBytesWritten += nToCopy;
739 2382510 : m_nBufferSize += nToCopy;
740 2382510 : m_nFilePos += nToCopy;
741 2382510 : m_bBufferDirty = true;
742 :
743 2382510 : 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 2378720 : eLastOp = Operation::WRITE;
757 :
758 2378720 : return nBytesWritten;
759 : }
760 :
761 : /************************************************************************/
762 : /* ClearErr() */
763 : /************************************************************************/
764 :
765 8099 : void VSIUnixStdioHandle::ClearErr()
766 :
767 : {
768 8099 : bAtEOF = false;
769 8099 : bError = false;
770 8099 : }
771 :
772 : /************************************************************************/
773 : /* Error() */
774 : /************************************************************************/
775 :
776 62190 : int VSIUnixStdioHandle::Error()
777 :
778 : {
779 62190 : return bError ? TRUE : FALSE;
780 : }
781 :
782 : /************************************************************************/
783 : /* Eof() */
784 : /************************************************************************/
785 :
786 157834 : int VSIUnixStdioHandle::Eof()
787 :
788 : {
789 157834 : 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 544 : 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 544 : struct fiemap *psExtentMap = reinterpret_cast<struct fiemap *>(&abyBuffer);
839 544 : memset(psExtentMap, 0,
840 : sizeof(struct fiemap) + sizeof(struct fiemap_extent));
841 544 : psExtentMap->fm_start = nOffset;
842 544 : psExtentMap->fm_length = nLength;
843 544 : psExtentMap->fm_extent_count = 1;
844 544 : int ret = ioctl(fd, FS_IOC_FIEMAP, psExtentMap);
845 544 : if (ret < 0)
846 0 : return VSI_RANGE_STATUS_UNKNOWN;
847 544 : 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 542 : const fiemap_extent *pasExtent = &(psExtentMap->fm_extents[0]);
852 542 : if (psExtentMap->fm_mapped_extents == 1 &&
853 542 : (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 542 : 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 24481 : size_t VSIUnixStdioHandle::PRead(void *pBuffer, size_t nSize,
893 : vsi_l_offset nOffset) const
894 : {
895 : #ifdef HAVE_PREAD64
896 24481 : 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 213884 : VSIUnixStdioFilesystemHandler::Open(const char *pszFilename,
933 : const char *pszAccess, bool bSetError,
934 : CSLConstList /* papszOptions */)
935 :
936 : {
937 213884 : int flags = O_RDONLY;
938 : int fd;
939 : using AccessMode = VSIUnixStdioHandle::AccessMode;
940 213884 : AccessMode eAccessMode = AccessMode::NORMAL;
941 213884 : if (strchr(pszAccess, 'w'))
942 : {
943 33086 : if (strchr(pszAccess, '+'))
944 6369 : flags = O_CREAT | O_TRUNC | O_RDWR;
945 : else
946 : {
947 26717 : eAccessMode = AccessMode::WRITE_ONLY;
948 26717 : flags = O_CREAT | O_TRUNC | O_WRONLY;
949 : }
950 33086 : fd = VSI_OPEN64(pszFilename, flags, 0664);
951 : }
952 180798 : 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 180628 : if (strchr(pszAccess, '+'))
966 7774 : flags = O_RDWR;
967 : else
968 172854 : eAccessMode = AccessMode::READ_ONLY;
969 180628 : fd = VSI_OPEN64(pszFilename, flags);
970 : }
971 213338 : const int nError = errno;
972 :
973 : VSIDebug3("VSIUnixStdioFilesystemHandler::Open(\"%s\",\"%s\") = %d",
974 : pszFilename, pszAccess, fd);
975 :
976 213338 : if (fd < 0)
977 : {
978 80625 : if (bSetError)
979 : {
980 17398 : VSIError(VSIE_FileError, "%s: %s", pszFilename, strerror(nError));
981 : }
982 80625 : errno = nError;
983 80625 : return nullptr;
984 : }
985 :
986 265470 : auto poHandle = std::make_unique<VSIUnixStdioHandle>(this, fd, eAccessMode);
987 132687 : poHandle->m_osFilename = pszFilename;
988 :
989 132904 : errno = nError;
990 :
991 132904 : 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 227033 : if (eAccessMode == AccessMode::READ_ONLY &&
999 93862 : CPLTestBool(CPLGetConfigOption("VSI_CACHE", "FALSE")))
1000 : {
1001 : return VSIVirtualHandleUniquePtr(
1002 5 : VSICreateCachedFile(poHandle.release()));
1003 : }
1004 :
1005 133166 : 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 242179 : int VSIUnixStdioFilesystemHandler::Stat(const char *pszFilename,
1070 : VSIStatBufL *pStatBuf, int /* nFlags */)
1071 : {
1072 242179 : return (VSI_STAT64(pszFilename, pStatBuf));
1073 : }
1074 :
1075 : /************************************************************************/
1076 : /* Unlink() */
1077 : /************************************************************************/
1078 :
1079 5188 : int VSIUnixStdioFilesystemHandler::Unlink(const char *pszFilename)
1080 :
1081 : {
1082 5188 : 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 1604 : int VSIUnixStdioFilesystemHandler::Mkdir(const char *pszPathname, long nMode)
1102 :
1103 : {
1104 1604 : 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 21875 : char **VSIUnixStdioFilesystemHandler::ReadDirEx(const char *pszPath,
1122 : int nMaxFiles)
1123 :
1124 : {
1125 21875 : if (strlen(pszPath) == 0)
1126 17 : pszPath = ".";
1127 :
1128 43750 : CPLStringList oDir;
1129 21875 : DIR *hDir = opendir(pszPath);
1130 21875 : if (hDir != nullptr)
1131 : {
1132 : // We want to avoid returning NULL for an empty list.
1133 21812 : oDir.Assign(static_cast<char **>(CPLCalloc(2, sizeof(char *))));
1134 :
1135 21812 : struct dirent *psDirEntry = nullptr;
1136 2002150 : while ((psDirEntry = readdir(hDir)) != nullptr)
1137 : {
1138 1980370 : oDir.AddString(psDirEntry->d_name);
1139 1980330 : if (nMaxFiles > 0 && oDir.Count() > nMaxFiles)
1140 7 : break;
1141 : }
1142 :
1143 21815 : 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 43750 : 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 1476 : bool VSIUnixStdioFilesystemHandler::IsLocal(const char *
1272 : #ifdef __linux
1273 : pszPath
1274 : #endif
1275 : ) const
1276 : {
1277 : #ifdef __linux
1278 : struct statfs sStatFS;
1279 1476 : if (statfs(pszPath, &sStatFS) == 0)
1280 : {
1281 : // See http://en.wikipedia.org/wiki/Comparison_of_file_systems
1282 168 : 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 1476 : 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 886 : void operator()(DIR *d)
1337 : {
1338 886 : if (d)
1339 886 : closedir(d);
1340 886 : }
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 898 : std::unique_ptr<VSIDIRUnixStdio> VSIUnixStdioFilesystemHandler::OpenDirInternal(
1361 : const char *pszPath, int nRecurseDepth, const char *const *papszOptions)
1362 : {
1363 1796 : std::unique_ptr<DIR, VSIDIRUnixStdio::DIRCloser> psDir(opendir(pszPath));
1364 898 : if (psDir == nullptr)
1365 : {
1366 12 : return nullptr;
1367 : }
1368 1772 : auto dir = std::make_unique<VSIDIRUnixStdio>();
1369 886 : dir->osRootPath = pszPath;
1370 886 : dir->nRecurseDepth = nRecurseDepth;
1371 886 : dir->m_psDir = std::move(psDir);
1372 886 : dir->m_osFilterPrefix = CSLFetchNameValueDef(papszOptions, "PREFIX", "");
1373 886 : dir->m_bNameAndTypeOnly = CPLTestBool(
1374 : CSLFetchNameValueDef(papszOptions, "NAME_AND_TYPE_ONLY", "NO"));
1375 886 : return dir;
1376 : }
1377 :
1378 : /************************************************************************/
1379 : /* OpenDir() */
1380 : /************************************************************************/
1381 :
1382 254 : VSIDIR *VSIUnixStdioFilesystemHandler::OpenDir(const char *pszPath,
1383 : int nRecurseDepth,
1384 : const char *const *papszOptions)
1385 : {
1386 254 : return OpenDirInternal(pszPath, nRecurseDepth, papszOptions).release();
1387 : }
1388 :
1389 : /************************************************************************/
1390 : /* NextDirEntry() */
1391 : /************************************************************************/
1392 :
1393 24878 : const VSIDIREntry *VSIDIRUnixStdio::NextDirEntry()
1394 : {
1395 24878 : begin:
1396 24878 : if (VSI_ISDIR(entry.nMode) && nRecurseDepth != 0)
1397 : {
1398 1288 : CPLString osCurFile(osRootPath);
1399 644 : if (!osCurFile.empty())
1400 644 : osCurFile += '/';
1401 644 : osCurFile += entry.pszName;
1402 : auto subdir = VSIUnixStdioFilesystemHandler::OpenDirInternal(
1403 644 : osCurFile, nRecurseDepth - 1, nullptr);
1404 644 : if (subdir)
1405 : {
1406 644 : subdir->osRootPath = osRootPath;
1407 644 : subdir->osBasePath = entry.pszName;
1408 644 : subdir->m_osFilterPrefix = m_osFilterPrefix;
1409 644 : subdir->m_bNameAndTypeOnly = m_bNameAndTypeOnly;
1410 644 : aoStackSubDir.push_back(std::move(subdir));
1411 : }
1412 644 : entry.nMode = 0;
1413 : }
1414 :
1415 25522 : while (!aoStackSubDir.empty())
1416 : {
1417 13187 : auto l_entry = aoStackSubDir.back()->NextDirEntry();
1418 13187 : if (l_entry)
1419 : {
1420 12543 : return l_entry;
1421 : }
1422 644 : aoStackSubDir.pop_back();
1423 : }
1424 :
1425 14103 : while (const auto *psEntry = readdir(m_psDir.get()))
1426 : {
1427 : // Skip . and ..entries
1428 13224 : if (psEntry->d_name[0] == '.' &&
1429 1771 : (psEntry->d_name[1] == '\0' ||
1430 891 : (psEntry->d_name[1] == '.' && psEntry->d_name[2] == '\0')))
1431 : {
1432 : // do nothing
1433 : }
1434 : else
1435 : {
1436 11461 : CPLFree(entry.pszName);
1437 11461 : CPLString osName(osBasePath);
1438 11461 : if (!osName.empty())
1439 7320 : osName += '/';
1440 11461 : osName += psEntry->d_name;
1441 :
1442 11461 : entry.pszName = CPLStrdup(osName);
1443 11461 : entry.nMode = 0;
1444 11461 : entry.nSize = 0;
1445 11461 : entry.nMTime = 0;
1446 11461 : entry.bModeKnown = false;
1447 11461 : entry.bSizeKnown = false;
1448 11461 : entry.bMTimeKnown = false;
1449 :
1450 11461 : CPLString osCurFile(osRootPath);
1451 11461 : if (!osCurFile.empty())
1452 11461 : osCurFile += '/';
1453 11461 : osCurFile += entry.pszName;
1454 :
1455 : #if !defined(__sun) && !defined(__HAIKU__)
1456 11461 : if (psEntry->d_type == DT_REG)
1457 9337 : entry.nMode = S_IFREG;
1458 2124 : else if (psEntry->d_type == DT_DIR)
1459 727 : entry.nMode = S_IFDIR;
1460 1397 : else if (psEntry->d_type == DT_LNK)
1461 1397 : entry.nMode = S_IFLNK;
1462 : #endif
1463 :
1464 17904 : const auto StatFile = [&osCurFile, this]()
1465 : {
1466 : VSIStatBufL sStatL;
1467 8952 : if (VSIStatL(osCurFile, &sStatL) == 0)
1468 : {
1469 8952 : entry.nMode = sStatL.st_mode;
1470 8952 : entry.nSize = sStatL.st_size;
1471 8952 : entry.nMTime = sStatL.st_mtime;
1472 8952 : entry.bModeKnown = true;
1473 8952 : entry.bSizeKnown = true;
1474 8952 : entry.bMTimeKnown = true;
1475 : }
1476 8952 : };
1477 :
1478 11473 : 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 11465 : if (!m_osFilterPrefix.empty() &&
1498 8 : !STARTS_WITH(osName.c_str(), m_osFilterPrefix.c_str()))
1499 : {
1500 2 : continue;
1501 : }
1502 :
1503 11455 : if (!m_bNameAndTypeOnly
1504 : #if !defined(__sun) && !defined(__HAIKU__)
1505 2503 : || psEntry->d_type == DT_UNKNOWN
1506 : #endif
1507 : )
1508 : {
1509 8952 : StatFile();
1510 : }
1511 :
1512 11455 : return &(entry);
1513 : }
1514 1768 : }
1515 :
1516 879 : 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 1789 : void VSIInstallLargeFileHandler()
1565 :
1566 : {
1567 1789 : VSIFileManager::InstallHandler(
1568 3578 : "", std::make_shared<VSIUnixStdioFilesystemHandler>());
1569 1789 : }
1570 :
1571 : #endif // ndef WIN32
1572 :
1573 : //! @endcond
|