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// Qt-Security score:critical reason:data-parser
5
6#include "qfsfileengine_p.h"
7#include "qfsfileengine_iterator_p.h"
8#include "qfilesystemengine_p.h"
9#include "qdatetime.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(QAbstractFileEngine *q)
81 : QAbstractFileEnginePrivate(q)
82{
83 init();
84}
85
86/*!
87 \internal
88*/
89void QFSFileEnginePrivate::init()
90{
91 is_sequential = 0;
92 tried_stat = 0;
93 need_lstat = 1;
94 is_link = 0;
95 openMode = QIODevice::NotOpen;
96 fd = -1;
97 fh = nullptr;
98 lastIOCommand = IOFlushCommand;
99 lastFlushFailed = false;
100 closeFileHandle = false;
101#ifdef Q_OS_WIN
102 fileAttrib = INVALID_FILE_ATTRIBUTES;
103 fileHandle = INVALID_HANDLE_VALUE;
104 mapHandle = NULL;
105 cachedFd = -1;
106#endif
107}
108
109/*!
110 Constructs a QFSFileEngine for the file name \a file.
111*/
112QFSFileEngine::QFSFileEngine(const QString &file)
113 : QAbstractFileEngine(*new QFSFileEnginePrivate(this))
114{
115 Q_D(QFSFileEngine);
116 d->fileEntry = QFileSystemEntry(file);
117}
118
119/*!
120 Constructs a QFSFileEngine.
121*/
122QFSFileEngine::QFSFileEngine() : QAbstractFileEngine(*new QFSFileEnginePrivate(this))
123{
124}
125
126/*!
127 \internal
128*/
129QFSFileEngine::QFSFileEngine(QFSFileEnginePrivate &dd)
130 : QAbstractFileEngine(dd)
131{
132}
133
134/*!
135 \internal
136*/
137ProcessOpenModeResult processOpenModeFlags(QIODevice::OpenMode openMode)
138{
139 ProcessOpenModeResult result;
140 result.ok = false;
141 if ((openMode & QFile::NewOnly) && (openMode & QFile::ExistingOnly)) {
142 qWarning(msg: "NewOnly and ExistingOnly are mutually exclusive");
143 result.error = "NewOnly and ExistingOnly are mutually exclusive"_L1;
144 return result;
145 }
146
147 if ((openMode & QFile::ExistingOnly) && !(openMode & (QFile::ReadOnly | QFile::WriteOnly))) {
148 qWarning(msg: "ExistingOnly must be specified alongside ReadOnly, WriteOnly, or ReadWrite");
149 result.error =
150 "ExistingOnly must be specified alongside ReadOnly, WriteOnly, or ReadWrite"_L1;
151 return result;
152 }
153
154 // Either Append or NewOnly implies WriteOnly
155 if (openMode & (QFile::Append | QFile::NewOnly))
156 openMode |= QFile::WriteOnly;
157
158 // WriteOnly implies Truncate when ReadOnly, Append, and NewOnly are not set.
159 if ((openMode & QFile::WriteOnly) && !(openMode & (QFile::ReadOnly | QFile::Append | QFile::NewOnly)))
160 openMode |= QFile::Truncate;
161
162 result.ok = true;
163 result.openMode = openMode;
164 return result;
165}
166
167/*!
168 Destructs the QFSFileEngine.
169*/
170QFSFileEngine::~QFSFileEngine()
171{
172 Q_D(QFSFileEngine);
173 if (d->closeFileHandle) {
174 if (d->fh) {
175 fclose(stream: d->fh);
176 } else if (d->fd != -1) {
177 QT_CLOSE(fd: d->fd);
178 }
179 }
180 d->unmapAll();
181}
182
183/*!
184 \reimp
185*/
186void QFSFileEngine::setFileName(const QString &file)
187{
188 Q_D(QFSFileEngine);
189 d->init();
190 d->fileEntry = QFileSystemEntry(file);
191}
192
193/*!
194 \reimp
195*/
196bool QFSFileEngine::open(QIODevice::OpenMode openMode,
197 std::optional<QFile::Permissions> permissions)
198{
199 Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
200 "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
201
202 Q_D(QFSFileEngine);
203 if (d->fileEntry.isEmpty()) {
204 qWarning(msg: "QFSFileEngine::open: No file name specified");
205 setError(error: QFile::OpenError, str: "No file name specified"_L1);
206 return false;
207 }
208
209 const ProcessOpenModeResult res = processOpenModeFlags(openMode);
210 if (!res.ok) {
211 setError(error: QFileDevice::OpenError, str: res.error);
212 return false;
213 }
214
215 d->openMode = res.openMode;
216 d->lastFlushFailed = false;
217 d->tried_stat = 0;
218 d->fh = nullptr;
219 d->fd = -1;
220
221 return d->nativeOpen(openMode: d->openMode, permissions);
222}
223
224/*!
225 Opens the file handle \a fh in \a openMode mode. Returns \c true on
226 success; otherwise returns \c false.
227*/
228bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh)
229{
230 return open(flags: openMode, fh, handleFlags: QFile::DontCloseHandle);
231}
232
233bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh, QFile::FileHandleFlags handleFlags)
234{
235 Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
236 "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
237
238 Q_D(QFSFileEngine);
239
240 const ProcessOpenModeResult res = processOpenModeFlags(openMode);
241 if (!res.ok) {
242 setError(error: QFileDevice::OpenError, str: res.error);
243 return false;
244 }
245
246 d->openMode = res.openMode;
247 d->lastFlushFailed = false;
248 d->closeFileHandle = handleFlags.testAnyFlag(flag: QFile::AutoCloseHandle);
249 d->fileEntry.clear();
250 d->tried_stat = 0;
251 d->fd = -1;
252
253 return d->openFh(flags: d->openMode, fh);
254}
255
256/*!
257 Opens the file handle \a fh using the open mode \a flags.
258*/
259bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh)
260{
261 Q_ASSERT_X(openMode & QIODevice::Unbuffered, "QFSFileEngine::open",
262 "QFSFileEngine no longer supports buffered mode; upper layer must buffer");
263
264 Q_Q(QFSFileEngine);
265 this->fh = fh;
266 fd = -1;
267
268 // Seek to the end when in Append mode.
269 if (openMode & QIODevice::Append) {
270 int ret;
271 do {
272 ret = QT_FSEEK(stream: fh, off: 0, SEEK_END);
273 } while (ret != 0 && errno == EINTR);
274
275 if (ret != 0) {
276 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
277 str: QSystemError::stdString(errno));
278
279 this->openMode = QIODevice::NotOpen;
280 this->fh = nullptr;
281
282 return false;
283 }
284 }
285
286 return true;
287}
288
289/*!
290 Opens the file descriptor \a fd in \a openMode mode. Returns \c true
291 on success; otherwise returns \c false.
292*/
293bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd)
294{
295 return open(flags: openMode, fd, handleFlags: QFile::DontCloseHandle);
296}
297
298bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd, QFile::FileHandleFlags handleFlags)
299{
300 Q_D(QFSFileEngine);
301
302 const ProcessOpenModeResult res = processOpenModeFlags(openMode);
303 if (!res.ok) {
304 setError(error: QFileDevice::OpenError, str: res.error);
305 return false;
306 }
307
308 d->openMode = res.openMode;
309 d->lastFlushFailed = false;
310 d->closeFileHandle = handleFlags.testAnyFlag(flag: QFile::AutoCloseHandle);
311 d->fileEntry.clear();
312 d->fh = nullptr;
313 d->fd = -1;
314 d->tried_stat = 0;
315
316 return d->openFd(flags: d->openMode, fd);
317}
318
319
320/*!
321 Opens the file descriptor \a fd to the file engine, using the open mode \a
322 flags.
323*/
324bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd)
325{
326 Q_Q(QFSFileEngine);
327 this->fd = fd;
328 fh = nullptr;
329
330 // Seek to the end when in Append mode.
331 if (openMode & QFile::Append) {
332 QT_OFF_T ret;
333 do {
334 ret = QT_LSEEK(fd: fd, offset: 0, SEEK_END);
335 } while (ret == -1 && errno == EINTR);
336
337 if (ret == -1) {
338 q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
339 str: QSystemError::stdString(errno));
340
341 this->openMode = QIODevice::NotOpen;
342 this->fd = -1;
343
344 return false;
345 }
346 }
347
348 return true;
349}
350
351/*!
352 \reimp
353*/
354bool QFSFileEngine::close()
355{
356 Q_D(QFSFileEngine);
357 d->openMode = QIODevice::NotOpen;
358 return d->nativeClose();
359}
360
361/*!
362 \internal
363*/
364bool QFSFileEnginePrivate::closeFdFh()
365{
366 Q_Q(QFSFileEngine);
367 if (fd == -1 && !fh)
368 return false;
369
370 // Flush the file if it's buffered, and if the last flush didn't fail.
371 bool flushed = !fh || (!lastFlushFailed && q->flush());
372 bool closed = true;
373 tried_stat = 0;
374
375 // Close the file if we created the handle.
376 if (closeFileHandle) {
377 int ret;
378
379 if (fh) {
380 // Close buffered file.
381 ret = fclose(stream: fh);
382 } else {
383 // Close unbuffered file.
384 ret = QT_CLOSE(fd);
385 }
386
387 // We must reset these guys regardless; calling close again after a
388 // failed close causes crashes on some systems.
389 fh = nullptr;
390 fd = -1;
391 closed = (ret == 0);
392 }
393
394 // Report errors.
395 if (!flushed || !closed) {
396 if (flushed) {
397 // If not flushed, we want the flush error to fall through.
398 q->setError(error: QFile::UnspecifiedError, str: QSystemError::stdString(errno));
399 }
400 return false;
401 }
402
403 return true;
404}
405
406/*!
407 \reimp
408*/
409bool QFSFileEngine::flush()
410{
411 Q_D(QFSFileEngine);
412 if ((d->openMode & QIODevice::WriteOnly) == 0) {
413 // Nothing in the write buffers, so flush succeeds in doing
414 // nothing.
415 return true;
416 }
417 return d->nativeFlush();
418}
419
420/*!
421 \reimp
422*/
423bool QFSFileEngine::syncToDisk()
424{
425 Q_D(QFSFileEngine);
426 if ((d->openMode & QIODevice::WriteOnly) == 0)
427 return true;
428 return d->nativeSyncToDisk();
429}
430
431/*!
432 \internal
433*/
434bool QFSFileEnginePrivate::flushFh()
435{
436 Q_Q(QFSFileEngine);
437
438 // Never try to flush again if the last flush failed. Otherwise you can
439 // get crashes on some systems (AIX).
440 if (lastFlushFailed)
441 return false;
442
443 int ret = fflush(stream: fh);
444
445 lastFlushFailed = (ret != 0);
446 lastIOCommand = QFSFileEnginePrivate::IOFlushCommand;
447
448 if (ret != 0) {
449 q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError,
450 str: QSystemError::stdString(errno));
451 return false;
452 }
453 return true;
454}
455
456/*!
457 \reimp
458*/
459qint64 QFSFileEngine::size() const
460{
461 Q_D(const QFSFileEngine);
462 return d->nativeSize();
463}
464
465/*!
466 \internal
467*/
468void QFSFileEnginePrivate::unmapAll()
469{
470 if (!maps.isEmpty()) {
471 const QList<uchar*> keys = maps.keys(); // Make a copy since unmap() modifies the map.
472 for (int i = 0; i < keys.size(); ++i)
473 unmap(ptr: keys.at(i));
474 }
475}
476
477#ifndef Q_OS_WIN
478/*!
479 \internal
480*/
481qint64 QFSFileEnginePrivate::sizeFdFh() const
482{
483 Q_Q(const QFSFileEngine);
484 const_cast<QFSFileEngine *>(q)->flush();
485
486 tried_stat = 0;
487 metaData.clearFlags(flags: QFileSystemMetaData::SizeAttribute);
488 if (!doStat(flags: QFileSystemMetaData::SizeAttribute))
489 return 0;
490 return metaData.size();
491}
492#endif
493
494/*!
495 \reimp
496*/
497qint64 QFSFileEngine::pos() const
498{
499 Q_D(const QFSFileEngine);
500 return d->nativePos();
501}
502
503/*!
504 \internal
505*/
506qint64 QFSFileEnginePrivate::posFdFh() const
507{
508 if (fh)
509 return qint64(QT_FTELL(stream: fh));
510 return QT_LSEEK(fd: fd, offset: 0, SEEK_CUR);
511}
512
513/*!
514 \reimp
515*/
516bool QFSFileEngine::seek(qint64 pos)
517{
518 Q_D(QFSFileEngine);
519 return d->nativeSeek(pos);
520}
521
522/*!
523 \reimp
524*/
525QDateTime QFSFileEngine::fileTime(QFile::FileTime time) const
526{
527 Q_D(const QFSFileEngine);
528
529 if (time == QFile::FileAccessTime) {
530 // always refresh for the access time
531 d->metaData.clearFlags(flags: QFileSystemMetaData::AccessTime);
532 }
533
534 if (d->doStat(flags: QFileSystemMetaData::Times))
535 return d->metaData.fileTime(time);
536
537 return QDateTime();
538}
539
540
541/*!
542 \internal
543*/
544bool QFSFileEnginePrivate::seekFdFh(qint64 pos)
545{
546 Q_Q(QFSFileEngine);
547
548 // On Windows' stdlib implementation, the results of calling fread and
549 // fwrite are undefined if not called either in sequence, or if preceded
550 // with a call to fflush().
551 if (lastIOCommand != QFSFileEnginePrivate::IOFlushCommand && !q->flush())
552 return false;
553
554 if (pos < 0 || pos != qint64(QT_OFF_T(pos)))
555 return false;
556
557 if (fh) {
558 // Buffered stdlib mode.
559 int ret;
560 do {
561 ret = QT_FSEEK(stream: fh, QT_OFF_T(pos), SEEK_SET);
562 } while (ret != 0 && errno == EINTR);
563
564 if (ret != 0) {
565 q->setError(error: QFile::ReadError, str: QSystemError::stdString(errno));
566 return false;
567 }
568 } else {
569 // Unbuffered stdio mode.
570 if (QT_LSEEK(fd: fd, QT_OFF_T(pos), SEEK_SET) == -1) {
571 q->setError(error: QFile::PositionError, str: QSystemError::stdString(errno));
572 qWarning(msg: "QFile::at: Cannot set file position %lld", pos);
573 return false;
574 }
575 }
576 return true;
577}
578
579/*!
580 \reimp
581*/
582int QFSFileEngine::handle() const
583{
584 Q_D(const QFSFileEngine);
585 return d->nativeHandle();
586}
587
588/*!
589 \reimp
590*/
591qint64 QFSFileEngine::read(char *data, qint64 maxlen)
592{
593 Q_D(QFSFileEngine);
594
595 // On Windows' stdlib implementation, the results of calling fread and
596 // fwrite are undefined if not called either in sequence, or if preceded
597 // with a call to fflush().
598 if (d->lastIOCommand != QFSFileEnginePrivate::IOReadCommand) {
599 flush();
600 d->lastIOCommand = QFSFileEnginePrivate::IOReadCommand;
601 }
602
603 return d->nativeRead(data, maxlen);
604}
605
606/*!
607 \internal
608*/
609qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len)
610{
611 Q_Q(QFSFileEngine);
612
613 if (len < 0 || len != qint64(size_t(len))) {
614 q->setError(error: QFile::ReadError, str: QSystemError::stdString(EINVAL));
615 return -1;
616 }
617
618 qint64 readBytes = 0;
619 bool eof = false;
620
621 if (fh) {
622 // Buffered stdlib mode.
623
624 size_t result;
625 do {
626 result = fread(ptr: data + readBytes, size: 1, n: size_t(len - readBytes), stream: fh);
627 eof = feof(stream: fh); // Doesn't change errno
628 if (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 break;
634 }
635 readBytes += result;
636 } while (!eof && (result == 0 ? errno == EINTR : readBytes < len));
637
638 } else if (fd != -1) {
639 // Unbuffered stdio mode.
640
641 SignedIOType result;
642 do {
643 // calculate the chunk size
644 // on Windows or 32-bit no-largefile Unix, we'll need to read in chunks
645 // we limit to the size of the signed type, otherwise we could get a negative number as a result
646 quint64 wantedBytes = quint64(len) - quint64(readBytes);
647 UnsignedIOType chunkSize = std::numeric_limits<SignedIOType>::max();
648 if (chunkSize > wantedBytes)
649 chunkSize = wantedBytes;
650 result = QT_READ(fd, data: data + readBytes, maxlen: chunkSize);
651 } while (result > 0 && (readBytes += result) < len);
652
653 // QT_READ (::read()) returns 0 to indicate end-of-file
654 eof = result == 0;
655 }
656
657 if (!eof && readBytes == 0) {
658 readBytes = -1;
659 q->setError(error: QFile::ReadError, str: QSystemError::stdString(errno));
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)) // Doesn't change errno
705 q->setError(error: QFile::ReadError, str: QSystemError::stdString(errno));
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,
783 str: QSystemError::stdString(errno));
784 } else {
785 // reset the cached size, if any
786 metaData.clearFlags(flags: QFileSystemMetaData::SizeAttribute);
787 }
788
789 return writtenBytes;
790}
791
792#ifndef QT_NO_FILESYSTEMITERATOR
793/*!
794 \internal
795*/
796QAbstractFileEngine::IteratorUniquePtr
797QFSFileEngine::beginEntryList(const QString &path, QDirListing::IteratorFlags filters,
798 const QStringList &filterNames)
799{
800 return std::make_unique<QFSFileEngineIterator>(args: path, args&: filters, args: filterNames);
801}
802#endif // QT_NO_FILESYSTEMITERATOR
803
804/*!
805 \reimp
806*/
807bool QFSFileEngine::isSequential() const
808{
809 Q_D(const QFSFileEngine);
810 if (d->is_sequential == 0)
811 d->is_sequential = d->nativeIsSequential() ? 1 : 2;
812 return d->is_sequential == 1;
813}
814
815/*!
816 \internal
817*/
818#ifdef Q_OS_UNIX
819bool QFSFileEnginePrivate::isSequentialFdFh() const
820{
821 if (doStat(flags: QFileSystemMetaData::SequentialType))
822 return metaData.isSequential();
823 return true;
824}
825#endif
826
827/*!
828 \reimp
829*/
830bool QFSFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
831{
832 Q_D(QFSFileEngine);
833 if (extension == AtEndExtension && d->fh && isSequential())
834 return feof(stream: d->fh);
835
836 if (extension == MapExtension) {
837 const MapExtensionOption *options = (const MapExtensionOption*)(option);
838 MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output);
839 returnValue->address = d->map(offset: options->offset, size: options->size, flags: options->flags);
840 return (returnValue->address != nullptr);
841 }
842 if (extension == UnMapExtension) {
843 const UnMapExtensionOption *options = (const UnMapExtensionOption*)option;
844 return d->unmap(ptr: options->address);
845 }
846
847 return false;
848}
849
850/*!
851 \reimp
852*/
853bool QFSFileEngine::supportsExtension(Extension extension) const
854{
855 Q_D(const QFSFileEngine);
856 if (extension == AtEndExtension && d->fh && isSequential())
857 return true;
858 if (extension == FastReadLineExtension && d->fh)
859 return true;
860 if (extension == FastReadLineExtension && d->fd != -1 && isSequential())
861 return true;
862 if (extension == UnMapExtension || extension == MapExtension)
863 return true;
864 return false;
865}
866
867/*! \fn QString QFSFileEngine::currentPath(const QString &fileName)
868 For Unix, returns the current working directory for the file
869 engine.
870
871 For Windows, returns the canonicalized form of the current path used
872 by the file engine for the drive specified by \a fileName. On
873 Windows, each drive has its own current directory, so a different
874 path is returned for file names that include different drive names
875 (e.g. A: or C:).
876
877 \sa setCurrentPath()
878*/
879
880/*! \fn QFileInfoList QFSFileEngine::drives()
881 For Windows, returns the list of drives in the file system as a list
882 of QFileInfo objects. On Unix, only the root path is returned.
883 On Windows, this function returns all drives (A:\, C:\, D:\, and so on).
884
885 For Unix, the list contains just the root path "/".
886*/
887
888/*! \fn QString QFSFileEngine::fileName(QAbstractFileEngine::FileName file) const
889 \reimp
890*/
891
892/*! \fn bool QFSFileEngine::setFileTime(const QDateTime &newDate, QFile::FileTime time)
893 \reimp
894*/
895
896/*! \fn bool QFSFileEngine::isRelativePath() const
897 \reimp
898*/
899
900/*! \fn bool QFSFileEngine::link(const QString &newName)
901
902 Creates a link from the file currently specified by fileName() to
903 \a newName. What a link is depends on the underlying filesystem
904 (be it a shortcut on Windows or a symbolic link on Unix). Returns
905 \c true if successful; otherwise returns \c false.
906
907 \note On Windows \a newName is expected to end with .lnk as the filename
908 extension.
909*/
910
911
912/*! \fn uint QFSFileEngine::ownerId(QAbstractFileEngine::FileOwner own) const
913 In Unix, if stat() is successful, the \c uid is returned if
914 \a own is the owner. Otherwise the \c gid is returned. If stat()
915 is unsuccessful, -2 is reuturned.
916
917 For Windows, -2 is always returned.
918*/
919
920/*! \fn QString QFSFileEngine::owner(QAbstractFileEngine::FileOwner own) const
921 \reimp
922*/
923
924/*!
925 For Windows or Apple platforms, copy the file to file \a copyName.
926
927 Not implemented for other Unix platforms.
928*/
929bool QFSFileEngine::copy(const QString &copyName)
930{
931 Q_D(QFSFileEngine);
932 QSystemError error;
933 bool ret = QFileSystemEngine::copyFile(source: d->fileEntry, target: QFileSystemEntry(copyName), error);
934 if (!ret)
935 setError(error: QFile::CopyError, str: error.toString());
936 return ret;
937}
938
939/*!
940 \reimp
941*/
942bool QFSFileEngine::remove()
943{
944 Q_D(QFSFileEngine);
945 QSystemError error;
946 bool ret = QFileSystemEngine::removeFile(entry: d->fileEntry, error);
947 if (!ret)
948 setError(error: QFile::RemoveError, str: error.toString());
949 else
950 d->metaData.clear();
951 return ret;
952}
953
954/*
955 An alternative to setFileName() when you have already constructed
956 a QFileSystemEntry.
957*/
958void QFSFileEngine::setFileEntry(QFileSystemEntry &&entry)
959{
960 Q_D(QFSFileEngine);
961 d->init();
962 d->fileEntry = std::move(entry);
963}
964
965bool QFSFileEngine::rename_helper(const QString &newName, RenameMode mode)
966{
967 Q_D(QFSFileEngine);
968
969 auto func = mode == Rename ? QFileSystemEngine::renameFile
970 : QFileSystemEngine::renameOverwriteFile;
971 QSystemError error;
972 auto newEntry = QFileSystemEntry(newName);
973 const bool ret = func(d->fileEntry, newEntry, error);
974 if (!ret) {
975 setError(error: QFile::RenameError, str: error.toString());
976 return false;
977 }
978 setFileEntry(std::move(newEntry));
979 return true;
980}
981
982/*!
983 \reimp
984*/
985bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories,
986 std::optional<QFile::Permissions> permissions) const
987{
988 return QFileSystemEngine::createDirectory(entry: QFileSystemEntry(name), createParents: createParentDirectories,
989 permissions);
990}
991
992/*!
993 \reimp
994*/
995bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
996{
997 return QFileSystemEngine::removeDirectory(entry: QFileSystemEntry(name), removeEmptyParents: recurseParentDirectories);
998}
999
1000
1001/*!
1002 Sets the current path (e.g., for QDir), to \a path. Returns \c true if the
1003 new path exists; otherwise this function does nothing, and returns \c false.
1004
1005 \sa currentPath()
1006*/
1007bool QFSFileEngine::setCurrentPath(const QString &path)
1008{
1009 return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path));
1010}
1011
1012/*!
1013 Returns whether the file system considers the file name to be
1014 case sensitive.
1015*/
1016bool QFSFileEngine::caseSensitive() const
1017{
1018 Q_D(const QFSFileEngine);
1019 return QFileSystemEngine::isCaseSensitive(entry: d->fileEntry, data&: d->metaData);
1020}
1021
1022/*! \fn bool QFSFileEngine::setPermissions(uint perms)
1023 \reimp
1024*/
1025
1026/*! \fn bool QFSFileEngine::setSize(qint64 size)
1027 \reimp
1028*/
1029
1030/*! \fn QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions(QAbstractFileEngine::FileFlags type) const
1031 \internal
1032*/
1033
1034QT_END_NAMESPACE
1035
1036#endif // QT_NO_FSFILEENGINE
1037

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