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

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