1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qfsfileengine_p.h"
6#include "qfsfileengine_iterator_p.h"
7#include "qfilesystemengine_p.h"
8#include "qdatetime.h"
9#include "qset.h"
10#include <QtCore/qdebug.h>
11
12#ifndef QT_NO_FSFILEENGINE
13
14#include <errno.h>
15#if defined(Q_OS_UNIX)
16#include "private/qcore_unix_p.h"
17#endif
18#include <stdio.h>
19#include <stdlib.h>
20#if defined(Q_OS_DARWIN)
21# include <private/qcore_mac_p.h>
22#endif
23
24QT_BEGIN_NAMESPACE
25
26using namespace Qt::StringLiterals;
27
28#ifdef Q_OS_WIN
29# ifndef S_ISREG
30# define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
31# endif
32# ifndef S_ISCHR
33# define S_ISCHR(x) (((x) & S_IFMT) == S_IFCHR)
34# endif
35# ifndef S_ISFIFO
36# define S_ISFIFO(x) false
37# endif
38# ifndef S_ISSOCK
39# define S_ISSOCK(x) false
40# endif
41# ifndef INVALID_FILE_ATTRIBUTES
42# define INVALID_FILE_ATTRIBUTES (DWORD (-1))
43# endif
44#endif
45
46#ifdef Q_OS_WIN
47// on Windows, read() and write() use int and unsigned int
48typedef int SignedIOType;
49typedef unsigned int UnsignedIOType;
50#else
51typedef ssize_t SignedIOType;
52typedef size_t UnsignedIOType;
53static_assert(sizeof(SignedIOType) == sizeof(UnsignedIOType),
54 "Unsupported: read/write return a type with different size as the len parameter");
55#endif
56
57/*! \class QFSFileEngine
58 \inmodule QtCore
59 \brief The QFSFileEngine class implements Qt's default file engine.
60 \since 4.1
61 \internal
62
63 This class is part of the file engine framework in Qt. If you only want to
64 access files or directories, use QFile, QFileInfo or QDir instead.
65
66 QFSFileEngine is the default file engine for accessing regular files. It
67 is provided for convenience; by subclassing this class, you can alter its
68 behavior slightly, without having to write a complete QAbstractFileEngine
69 subclass. To install your custom file engine, you must also subclass
70 QAbstractFileEngineHandler and create an instance of your handler.
71
72 It can also be useful to create a QFSFileEngine object directly if you
73 need to use the local file system inside QAbstractFileEngine::create(), in
74 order to avoid recursion (as higher-level classes tend to call
75 QAbstractFileEngine::create()).
76*/
77
78//**************** QFSFileEnginePrivate
79QFSFileEnginePrivate::QFSFileEnginePrivate() : QAbstractFileEnginePrivate()
80{
81 init();
82}
83
84/*!
85 \internal
86*/
87void QFSFileEnginePrivate::init()
88{
89 is_sequential = 0;
90 tried_stat = 0;
91 need_lstat = 1;
92 is_link = 0;
93 openMode = QIODevice::NotOpen;
94 fd = -1;
95 fh = nullptr;
96 lastIOCommand = IOFlushCommand;
97 lastFlushFailed = false;
98 closeFileHandle = false;
99#ifdef Q_OS_WIN
100 fileAttrib = INVALID_FILE_ATTRIBUTES;
101 fileHandle = INVALID_HANDLE_VALUE;
102 mapHandle = NULL;
103 cachedFd = -1;
104#endif
105}
106
107/*!
108 Constructs a QFSFileEngine for the file name \a file.
109*/
110QFSFileEngine::QFSFileEngine(const QString &file)
111 : QAbstractFileEngine(*new QFSFileEnginePrivate)
112{
113 Q_D(QFSFileEngine);
114 d->fileEntry = QFileSystemEntry(file);
115}
116
117/*!
118 Constructs a QFSFileEngine.
119*/
120QFSFileEngine::QFSFileEngine() : QAbstractFileEngine(*new QFSFileEnginePrivate)
121{
122}
123
124/*!
125 \internal
126*/
127QFSFileEngine::QFSFileEngine(QFSFileEnginePrivate &dd)
128 : QAbstractFileEngine(dd)
129{
130}
131
132/*!
133 \internal
134*/
135ProcessOpenModeResult processOpenModeFlags(QIODevice::OpenMode openMode)
136{
137 ProcessOpenModeResult result;
138 result.ok = false;
139 if ((openMode & QFile::NewOnly) && (openMode & QFile::ExistingOnly)) {
140 qWarning(msg: "NewOnly and ExistingOnly are mutually exclusive");
141 result.error = "NewOnly and ExistingOnly are mutually exclusive"_L1;
142 return result;
143 }
144
145 if ((openMode & QFile::ExistingOnly) && !(openMode & (QFile::ReadOnly | QFile::WriteOnly))) {
146 qWarning(msg: "ExistingOnly must be specified alongside ReadOnly, WriteOnly, or ReadWrite");
147 result.error =
148 "ExistingOnly must be specified alongside ReadOnly, WriteOnly, or ReadWrite"_L1;
149 return result;
150 }
151
152 // Either Append or NewOnly implies WriteOnly
153 if (openMode & (QFile::Append | QFile::NewOnly))
154 openMode |= QFile::WriteOnly;
155
156 // WriteOnly implies Truncate when ReadOnly, Append, and NewOnly are not set.
157 if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append | QFile::NewOnly)))
158 openMode |= QFile::Truncate;
159
160 result.ok = true;
161 result.openMode = openMode;
162 return result;
163}
164
165/*!
166 Destructs the QFSFileEngine.
167*/
168QFSFileEngine::~QFSFileEngine()
169{
170 Q_D(QFSFileEngine);
171 if (d->closeFileHandle) {
172 if (d->fh) {
173 fclose(stream: d->fh);
174 } else if (d->fd != -1) {
175 QT_CLOSE(fd: d->fd);
176 }
177 }
178 d->unmapAll();
179}
180
181/*!
182 \reimp
183*/
184void QFSFileEngine::setFileName(const QString &file)
185{
186 Q_D(QFSFileEngine);
187 d->init();
188 d->fileEntry = QFileSystemEntry(file);
189}
190
191/*!
192 \reimp
193*/
194bool QFSFileEngine::open(QIODevice::OpenMode openMode,
195 std::optional<QFile::Permissions> permissions)
196{
197 Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
198 "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
199
200 Q_D(QFSFileEngine);
201 if (d->fileEntry.isEmpty()) {
202 qWarning(msg: "QFSFileEngine::open: No file name specified");
203 setError(error: QFile::OpenError, str: "No file name specified"_L1);
204 return false;
205 }
206
207 const ProcessOpenModeResult res = processOpenModeFlags(openMode);
208 if (!res.ok) {
209 setError(error: QFileDevice::OpenError, str: res.error);
210 return false;
211 }
212
213 d->openMode = res.openMode;
214 d->lastFlushFailed = false;
215 d->tried_stat = 0;
216 d->fh = nullptr;
217 d->fd = -1;
218
219 return d->nativeOpen(openMode: d->openMode, permissions);
220}
221
222/*!
223 Opens the file handle \a fh in \a openMode mode. Returns \c true on
224 success; otherwise returns \c false.
225*/
226bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh)
227{
228 return open(flags: openMode, fh, handleFlags: QFile::DontCloseHandle);
229}
230
231bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh, QFile::FileHandleFlags handleFlags)
232{
233 Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
234 "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
235
236 Q_D(QFSFileEngine);
237
238 const ProcessOpenModeResult res = processOpenModeFlags(openMode);
239 if (!res.ok) {
240 setError(error: QFileDevice::OpenError, str: res.error);
241 return false;
242 }
243
244 d->openMode = res.openMode;
245 d->lastFlushFailed = false;
246 d->closeFileHandle = handleFlags.testAnyFlag(flag: QFile::AutoCloseHandle);
247 d->fileEntry.clear();
248 d->tried_stat = 0;
249 d->fd = -1;
250
251 return d->openFh(flags: d->openMode, fh);
252}
253
254/*!
255 Opens the file handle \a fh using the open mode \a flags.
256*/
257bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh)
258{
259 Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
260 "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
261
262 Q_Q(QFSFileEngine);
263 this->fh = fh;
264 fd = -1;
265
266 // Seek to the end when in Append mode.
267 if (openMode & QIODevice::Append) {
268 int ret;
269 do {
270 ret = QT_FSEEK(stream: fh, off: 0, SEEK_END);
271 } while (ret != 0 && errno == EINTR);
272
273 if (ret != 0) {
274 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
275 str: QSystemError::stdString(errno));
276
277 this->openMode = QIODevice::NotOpen;
278 this->fh = nullptr;
279
280 return false;
281 }
282 }
283
284 return true;
285}
286
287/*!
288 Opens the file descriptor \a fd in \a openMode mode. Returns \c true
289 on success; otherwise returns \c false.
290*/
291bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd)
292{
293 return open(flags: openMode, fd, handleFlags: QFile::DontCloseHandle);
294}
295
296bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd, QFile::FileHandleFlags handleFlags)
297{
298 Q_D(QFSFileEngine);
299
300 const ProcessOpenModeResult res = processOpenModeFlags(openMode);
301 if (!res.ok) {
302 setError(error: QFileDevice::OpenError, str: res.error);
303 return false;
304 }
305
306 d->openMode = res.openMode;
307 d->lastFlushFailed = false;
308 d->closeFileHandle = handleFlags.testAnyFlag(flag: QFile::AutoCloseHandle);
309 d->fileEntry.clear();
310 d->fh = nullptr;
311 d->fd = -1;
312 d->tried_stat = 0;
313
314 return d->openFd(flags: d->openMode, fd);
315}
316
317
318/*!
319 Opens the file descriptor \a fd to the file engine, using the open mode \a
320 flags.
321*/
322bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd)
323{
324 Q_Q(QFSFileEngine);
325 this->fd = fd;
326 fh = nullptr;
327
328 // Seek to the end when in Append mode.
329 if (openMode & QFile::Append) {
330 QT_OFF_T ret;
331 do {
332 ret = QT_LSEEK(fd: fd, offset: 0, SEEK_END);
333 } while (ret == -1 && errno == EINTR);
334
335 if (ret == -1) {
336 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
337 str: QSystemError::stdString(errno));
338
339 this->openMode = QIODevice::NotOpen;
340 this->fd = -1;
341
342 return false;
343 }
344 }
345
346 return true;
347}
348
349/*!
350 \reimp
351*/
352bool QFSFileEngine::close()
353{
354 Q_D(QFSFileEngine);
355 d->openMode = QIODevice::NotOpen;
356 return d->nativeClose();
357}
358
359/*!
360 \internal
361*/
362bool QFSFileEnginePrivate::closeFdFh()
363{
364 Q_Q(QFSFileEngine);
365 if (fd == -1 && !fh)
366 return false;
367
368 // Flush the file if it's buffered, and if the last flush didn't fail.
369 bool flushed = !fh || (!lastFlushFailed && q->flush());
370 bool closed = true;
371 tried_stat = 0;
372
373 // Close the file if we created the handle.
374 if (closeFileHandle) {
375 int ret;
376
377 if (fh) {
378 // Close buffered file.
379 ret = fclose(stream: fh);
380 } else {
381 // Close unbuffered file.
382 ret = QT_CLOSE(fd);
383 }
384
385 // We must reset these guys regardless; calling close again after a
386 // failed close causes crashes on some systems.
387 fh = nullptr;
388 fd = -1;
389 closed = (ret == 0);
390 }
391
392 // Report errors.
393 if (!flushed || !closed) {
394 if (flushed) {
395 // If not flushed, we want the flush error to fall through.
396 q->setError(error: QFile::UnspecifiedError, str: QSystemError::stdString(errno));
397 }
398 return false;
399 }
400
401 return true;
402}
403
404/*!
405 \reimp
406*/
407bool QFSFileEngine::flush()
408{
409 Q_D(QFSFileEngine);
410 if ((d->openMode & QIODevice::WriteOnly) == 0) {
411 // Nothing in the write buffers, so flush succeeds in doing
412 // nothing.
413 return true;
414 }
415 return d->nativeFlush();
416}
417
418/*!
419 \reimp
420*/
421bool QFSFileEngine::syncToDisk()
422{
423 Q_D(QFSFileEngine);
424 if ((d->openMode & QIODevice::WriteOnly) == 0)
425 return true;
426 return d->nativeSyncToDisk();
427}
428
429/*!
430 \internal
431*/
432bool QFSFileEnginePrivate::flushFh()
433{
434 Q_Q(QFSFileEngine);
435
436 // Never try to flush again if the last flush failed. Otherwise you can
437 // get crashes on some systems (AIX).
438 if (lastFlushFailed)
439 return false;
440
441 int ret = fflush(stream: fh);
442
443 lastFlushFailed = (ret != 0);
444 lastIOCommand = QFSFileEnginePrivate::IOFlushCommand;
445
446 if (ret != 0) {
447 q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError,
448 str: QSystemError::stdString(errno));
449 return false;
450 }
451 return true;
452}
453
454/*!
455 \reimp
456*/
457qint64 QFSFileEngine::size() const
458{
459 Q_D(const QFSFileEngine);
460 return d->nativeSize();
461}
462
463/*!
464 \internal
465*/
466void QFSFileEnginePrivate::unmapAll()
467{
468 if (!maps.isEmpty()) {
469 const QList<uchar*> keys = maps.keys(); // Make a copy since unmap() modifies the map.
470 for (int i = 0; i < keys.size(); ++i)
471 unmap(ptr: keys.at(i));
472 }
473}
474
475#ifndef Q_OS_WIN
476/*!
477 \internal
478*/
479qint64 QFSFileEnginePrivate::sizeFdFh() const
480{
481 Q_Q(const QFSFileEngine);
482 const_cast<QFSFileEngine *>(q)->flush();
483
484 tried_stat = 0;
485 metaData.clearFlags(flags: QFileSystemMetaData::SizeAttribute);
486 if (!doStat(flags: QFileSystemMetaData::SizeAttribute))
487 return 0;
488 return metaData.size();
489}
490#endif
491
492/*!
493 \reimp
494*/
495qint64 QFSFileEngine::pos() const
496{
497 Q_D(const QFSFileEngine);
498 return d->nativePos();
499}
500
501/*!
502 \internal
503*/
504qint64 QFSFileEnginePrivate::posFdFh() const
505{
506 if (fh)
507 return qint64(QT_FTELL(stream: fh));
508 return QT_LSEEK(fd: fd, offset: 0, SEEK_CUR);
509}
510
511/*!
512 \reimp
513*/
514bool QFSFileEngine::seek(qint64 pos)
515{
516 Q_D(QFSFileEngine);
517 return d->nativeSeek(pos);
518}
519
520/*!
521 \reimp
522*/
523QDateTime QFSFileEngine::fileTime(QFile::FileTime time) const
524{
525 Q_D(const QFSFileEngine);
526
527 if (time == QFile::FileAccessTime) {
528 // always refresh for the access time
529 d->metaData.clearFlags(flags: QFileSystemMetaData::AccessTime);
530 }
531
532 if (d->doStat(flags: QFileSystemMetaData::Times))
533 return d->metaData.fileTime(time);
534
535 return QDateTime();
536}
537
538
539/*!
540 \internal
541*/
542bool QFSFileEnginePrivate::seekFdFh(qint64 pos)
543{
544 Q_Q(QFSFileEngine);
545
546 // On Windows' stdlib implementation, the results of calling fread and
547 // fwrite are undefined if not called either in sequence, or if preceded
548 // with a call to fflush().
549 if (lastIOCommand != QFSFileEnginePrivate::IOFlushCommand && !q->flush())
550 return false;
551
552 if (pos < 0 || pos != qint64(QT_OFF_T(pos)))
553 return false;
554
555 if (fh) {
556 // Buffered stdlib mode.
557 int ret;
558 do {
559 ret = QT_FSEEK(stream: fh, QT_OFF_T(pos), SEEK_SET);
560 } while (ret != 0 && errno == EINTR);
561
562 if (ret != 0) {
563 q->setError(error: QFile::ReadError, str: QSystemError::stdString(errno));
564 return false;
565 }
566 } else {
567 // Unbuffered stdio mode.
568 if (QT_LSEEK(fd: fd, QT_OFF_T(pos), SEEK_SET) == -1) {
569 q->setError(error: QFile::PositionError, str: QSystemError::stdString(errno));
570 qWarning(msg: "QFile::at: Cannot set file position %lld", pos);
571 return false;
572 }
573 }
574 return true;
575}
576
577/*!
578 \reimp
579*/
580int QFSFileEngine::handle() const
581{
582 Q_D(const QFSFileEngine);
583 return d->nativeHandle();
584}
585
586/*!
587 \reimp
588*/
589qint64 QFSFileEngine::read(char *data, qint64 maxlen)
590{
591 Q_D(QFSFileEngine);
592
593 // On Windows' stdlib implementation, the results of calling fread and
594 // fwrite are undefined if not called either in sequence, or if preceded
595 // with a call to fflush().
596 if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) {
597 flush();
598 d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand;
599 }
600
601 return d->nativeRead(data, maxlen);
602}
603
604/*!
605 \internal
606*/
607qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len)
608{
609 Q_Q(QFSFileEngine);
610
611 if (len < 0 || len != qint64(size_t(len))) {
612 q->setError(error: QFile::ReadError, str: QSystemError::stdString(EINVAL));
613 return -1;
614 }
615
616 qint64 readBytes = 0;
617 bool eof = false;
618
619 if (fh) {
620 // Buffered stdlib mode.
621
622 size_t result;
623 do {
624 result = fread(ptr: data + readBytes, size: 1, n: size_t(len - readBytes), stream: fh);
625 eof = feof(stream: fh); // Doesn't change errno
626 if (eof && result == 0) {
627 // On OS X, this is needed, e.g., if a file was written to
628 // through another stream since our last read. See test
629 // tst_QFile::appendAndRead
630 QT_FSEEK(stream: fh, QT_FTELL(stream: fh), SEEK_SET); // re-sync stream.
631 break;
632 }
633 readBytes += result;
634 } while (!eof && (result == 0 ? errno == EINTR : readBytes < len));
635
636 } else if (fd != -1) {
637 // Unbuffered stdio mode.
638
639 SignedIOType result;
640 do {
641 // calculate the chunk size
642 // on Windows or 32-bit no-largefile Unix, we'll need to read in chunks
643 // we limit to the size of the signed type, otherwise we could get a negative number as a result
644 quint64 wantedBytes = quint64(len) - quint64(readBytes);
645 UnsignedIOType chunkSize = std::numeric_limits<SignedIOType>::max();
646 if (chunkSize > wantedBytes)
647 chunkSize = wantedBytes;
648 result = QT_READ(fd, data: data + readBytes, maxlen: chunkSize);
649 } while (result > 0 && (readBytes += result) < len);
650
651 // QT_READ (::read()) returns 0 to indicate end-of-file
652 eof = result == 0;
653 }
654
655 if (!eof && readBytes == 0) {
656 readBytes = -1;
657 q->setError(error: QFile::ReadError, str: QSystemError::stdString(errno));
658 }
659
660 return readBytes;
661}
662
663/*!
664 \reimp
665*/
666qint64 QFSFileEngine::readLine(char *data, qint64 maxlen)
667{
668 Q_D(QFSFileEngine);
669
670 // On Windows' stdlib implementation, the results of calling fread and
671 // fwrite are undefined if not called either in sequence, or if preceded
672 // with a call to fflush().
673 if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) {
674 flush();
675 d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand;
676 }
677
678 return d->nativeReadLine(data, maxlen);
679}
680
681/*!
682 \internal
683*/
684qint64 QFSFileEnginePrivate::readLineFdFh(char *data, qint64 maxlen)
685{
686 Q_Q(QFSFileEngine);
687 if (!fh)
688 return q->QAbstractFileEngine::readLine(data, maxlen);
689
690 QT_OFF_T oldPos = 0;
691#ifdef Q_OS_WIN
692 bool seq = q->isSequential();
693 if (!seq)
694#endif
695 oldPos = QT_FTELL(stream: fh);
696
697 // QIODevice::readLine() passes maxlen - 1 to QFile::readLineData()
698 // because it has made space for the '\0' at the end of data. But fgets
699 // does the same, so we'd get two '\0' at the end - passing maxlen + 1
700 // solves this.
701 if (!fgets(s: data, n: int(maxlen + 1), stream: fh)) {
702 if (!feof(stream: fh)) // Doesn't change errno
703 q->setError(error: QFile::ReadError, str: QSystemError::stdString(errno));
704 return -1; // error
705 }
706
707#ifdef Q_OS_WIN
708 if (seq)
709 return qstrlen(data);
710#endif
711
712 qint64 lineLength = QT_FTELL(stream: fh) - oldPos;
713 return lineLength > 0 ? lineLength : qstrlen(str: data);
714}
715
716/*!
717 \reimp
718*/
719qint64 QFSFileEngine::write(const char *data, qint64 len)
720{
721 Q_D(QFSFileEngine);
722 d->metaData.clearFlags(flags: QFileSystemMetaData::Times);
723
724 // On Windows' stdlib implementation, the results of calling fread and
725 // fwrite are undefined if not called either in sequence, or if preceded
726 // with a call to fflush().
727 if (d->lastIOCommand != QFSFileEnginePrivate::IOWriteCommand) {
728 flush();
729 d->lastIOCommand = QFSFileEnginePrivate::IOWriteCommand;
730 }
731
732 return d->nativeWrite(data, len);
733}
734
735/*!
736 \internal
737*/
738qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len)
739{
740 Q_Q(QFSFileEngine);
741
742 if (len < 0 || len != qint64(size_t(len))) {
743 q->setError(error: QFile::WriteError, str: QSystemError::stdString(EINVAL));
744 return -1;
745 }
746
747 qint64 writtenBytes = 0;
748
749 if (len) { // avoid passing nullptr to fwrite() or QT_WRITE() (UB)
750
751 if (fh) {
752 // Buffered stdlib mode.
753
754 size_t result;
755 do {
756 result = fwrite(ptr: data + writtenBytes, size: 1, n: size_t(len - writtenBytes), s: fh);
757 writtenBytes += result;
758 } while (result == 0 ? errno == EINTR : writtenBytes < len);
759
760 } else if (fd != -1) {
761 // Unbuffered stdio mode.
762
763 SignedIOType result;
764 do {
765 // calculate the chunk size
766 // on Windows or 32-bit no-largefile Unix, we'll need to read in chunks
767 // we limit to the size of the signed type, otherwise we could get a negative number as a result
768 quint64 wantedBytes = quint64(len) - quint64(writtenBytes);
769 UnsignedIOType chunkSize = std::numeric_limits<SignedIOType>::max();
770 if (chunkSize > wantedBytes)
771 chunkSize = wantedBytes;
772 result = QT_WRITE(fd, data: data + writtenBytes, len: chunkSize);
773 } while (result > 0 && (writtenBytes += result) < len);
774 }
775
776 }
777
778 if (len && writtenBytes == 0) {
779 writtenBytes = -1;
780 q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError,
781 str: QSystemError::stdString(errno));
782 } else {
783 // reset the cached size, if any
784 metaData.clearFlags(flags: QFileSystemMetaData::SizeAttribute);
785 }
786
787 return writtenBytes;
788}
789
790#ifndef QT_NO_FILESYSTEMITERATOR
791/*!
792 \internal
793*/
794QAbstractFileEngine::IteratorUniquePtr
795QFSFileEngine::beginEntryList(const QString &path, QDirListing::IteratorFlags filters,
796 const QStringList &filterNames)
797{
798 return std::make_unique<QFSFileEngineIterator>(args: path, args&: filters, args: filterNames);
799}
800#endif // QT_NO_FILESYSTEMITERATOR
801
802/*!
803 \reimp
804*/
805bool QFSFileEngine::isSequential() const
806{
807 Q_D(const QFSFileEngine);
808 if (d->is_sequential == 0)
809 d->is_sequential = d->nativeIsSequential() ? 1 : 2;
810 return d->is_sequential == 1;
811}
812
813/*!
814 \internal
815*/
816#ifdef Q_OS_UNIX
817bool QFSFileEnginePrivate::isSequentialFdFh() const
818{
819 if (doStat(flags: QFileSystemMetaData::SequentialType))
820 return metaData.isSequential();
821 return true;
822}
823#endif
824
825/*!
826 \reimp
827*/
828bool QFSFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
829{
830 Q_D(QFSFileEngine);
831 if (extension == AtEndExtension && d->fh && isSequential())
832 return feof(stream: d->fh);
833
834 if (extension == MapExtension) {
835 const MapExtensionOption *options = (const MapExtensionOption*)(option);
836 MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output);
837 returnValue->address = d->map(offset: options->offset, size: options->size, flags: options->flags);
838 return (returnValue->address != nullptr);
839 }
840 if (extension == UnMapExtension) {
841 const UnMapExtensionOption *options = (const UnMapExtensionOption*)option;
842 return d->unmap(ptr: options->address);
843 }
844
845 return false;
846}
847
848/*!
849 \reimp
850*/
851bool QFSFileEngine::supportsExtension(Extension extension) const
852{
853 Q_D(const QFSFileEngine);
854 if (extension == AtEndExtension && d->fh && isSequential())
855 return true;
856 if (extension == FastReadLineExtension && d->fh)
857 return true;
858 if (extension == FastReadLineExtension && d->fd != -1 && isSequential())
859 return true;
860 if (extension == UnMapExtension || extension == MapExtension)
861 return true;
862 return false;
863}
864
865/*! \fn QString QFSFileEngine::currentPath(const QString &fileName)
866 For Unix, returns the current working directory for the file
867 engine.
868
869 For Windows, returns the canonicalized form of the current path used
870 by the file engine for the drive specified by \a fileName. On
871 Windows, each drive has its own current directory, so a different
872 path is returned for file names that include different drive names
873 (e.g. A: or C:).
874
875 \sa setCurrentPath()
876*/
877
878/*! \fn QFileInfoList QFSFileEngine::drives()
879 For Windows, returns the list of drives in the file system as a list
880 of QFileInfo objects. On Unix, only the root path is returned.
881 On Windows, this function returns all drives (A:\, C:\, D:\, and so on).
882
883 For Unix, the list contains just the root path "/".
884*/
885
886/*! \fn QString QFSFileEngine::fileName(QAbstractFileEngine::FileName file) const
887 \reimp
888*/
889
890/*! \fn bool QFSFileEngine::setFileTime(const QDateTime &newDate, QFile::FileTime time)
891 \reimp
892*/
893
894/*!
895 Returns the home path of the current user.
896
897 \sa rootPath()
898*/
899QString QFSFileEngine::homePath()
900{
901 return QFileSystemEngine::homePath();
902}
903
904/*!
905 Returns the root path.
906
907 \sa homePath()
908*/
909QString QFSFileEngine::rootPath()
910{
911 return QFileSystemEngine::rootPath();
912}
913
914/*!
915 Returns the temporary path (i.e., a path in which it is safe
916 to store temporary files).
917*/
918QString QFSFileEngine::tempPath()
919{
920 return QFileSystemEngine::tempPath();
921}
922
923/*! \fn bool QFSFileEngine::isRelativePath() const
924 \reimp
925*/
926
927/*! \fn bool QFSFileEngine::link(const QString &newName)
928
929 Creates a link from the file currently specified by fileName() to
930 \a newName. What a link is depends on the underlying filesystem
931 (be it a shortcut on Windows or a symbolic link on Unix). Returns
932 \c true if successful; otherwise returns \c false.
933
934 \note On Windows \a newName is expected to end with .lnk as the filename
935 extension.
936*/
937
938
939/*! \fn uint QFSFileEngine::ownerId(QAbstractFileEngine::FileOwner own) const
940 In Unix, if stat() is successful, the \c uid is returned if
941 \a own is the owner. Otherwise the \c gid is returned. If stat()
942 is unsuccessful, -2 is reuturned.
943
944 For Windows, -2 is always returned.
945*/
946
947/*! \fn QString QFSFileEngine::owner(QAbstractFileEngine::FileOwner own) const
948 \reimp
949*/
950
951/*!
952 For Windows or Apple platforms, copy the file to file \a copyName.
953
954 Not implemented for other Unix platforms.
955*/
956bool QFSFileEngine::copy(const QString &copyName)
957{
958 Q_D(QFSFileEngine);
959 QSystemError error;
960 bool ret = QFileSystemEngine::copyFile(source: d->fileEntry, target: QFileSystemEntry(copyName), error);
961 if (!ret)
962 setError(error: QFile::CopyError, str: error.toString());
963 return ret;
964}
965
966/*!
967 \reimp
968*/
969bool QFSFileEngine::remove()
970{
971 Q_D(QFSFileEngine);
972 QSystemError error;
973 bool ret = QFileSystemEngine::removeFile(entry: d->fileEntry, error);
974 d->metaData.clear();
975 if (!ret)
976 setError(error: QFile::RemoveError, str: error.toString());
977 return ret;
978}
979
980/*
981 An alternative to setFileName() when you have already constructed
982 a QFileSystemEntry.
983*/
984void QFSFileEngine::setFileEntry(QFileSystemEntry &&entry)
985{
986 Q_D(QFSFileEngine);
987 d->init();
988 d->fileEntry = std::move(entry);
989}
990
991bool QFSFileEngine::rename_helper(const QString &newName, RenameMode mode)
992{
993 Q_D(QFSFileEngine);
994
995 auto func = mode == Rename ? QFileSystemEngine::renameFile
996 : QFileSystemEngine::renameOverwriteFile;
997 QSystemError error;
998 auto newEntry = QFileSystemEntry(newName);
999 const bool ret = func(d->fileEntry, newEntry, error);
1000 if (!ret) {
1001 setError(error: QFile::RenameError, str: error.toString());
1002 return false;
1003 }
1004 setFileEntry(std::move(newEntry));
1005 return true;
1006}
1007
1008/*!
1009 \reimp
1010*/
1011bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories,
1012 std::optional<QFile::Permissions> permissions) const
1013{
1014 return QFileSystemEngine::createDirectory(entry: QFileSystemEntry(name), createParents: createParentDirectories,
1015 permissions);
1016}
1017
1018/*!
1019 \reimp
1020*/
1021bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
1022{
1023 return QFileSystemEngine::removeDirectory(entry: QFileSystemEntry(name), removeEmptyParents: recurseParentDirectories);
1024}
1025
1026
1027/*!
1028 Sets the current path (e.g., for QDir), to \a path. Returns \c true if the
1029 new path exists; otherwise this function does nothing, and returns \c false.
1030
1031 \sa currentPath()
1032*/
1033bool QFSFileEngine::setCurrentPath(const QString &path)
1034{
1035 return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path));
1036}
1037
1038/*!
1039 Returns whether the file system considers the file name to be
1040 case sensitive.
1041*/
1042bool QFSFileEngine::caseSensitive() const
1043{
1044 Q_D(const QFSFileEngine);
1045 return QFileSystemEngine::isCaseSensitive(entry: d->fileEntry, data&: d->metaData);
1046}
1047
1048/*! \fn bool QFSFileEngine::setPermissions(uint perms)
1049 \reimp
1050*/
1051
1052/*! \fn bool QFSFileEngine::setSize(qint64 size)
1053 \reimp
1054*/
1055
1056/*! \fn QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions(QAbstractFileEngine::FileFlags type) const
1057 \internal
1058*/
1059
1060QT_END_NAMESPACE
1061
1062#endif // QT_NO_FSFILEENGINE
1063

source code of qtbase/src/corelib/io/qfsfileengine.cpp