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

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