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 
61 QT_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
83 typedef int SignedIOType;
84 typedef unsigned int UnsignedIOType;
85 #else
86 typedef ssize_t SignedIOType;
87 typedef size_t UnsignedIOType;
88 Q_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
QFSFileEnginePrivate()114 QFSFileEnginePrivate::QFSFileEnginePrivate() : QAbstractFileEnginePrivate()
115 {
116     init();
117 }
118 
119 /*!
120     \internal
121 */
init()122 void 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 */
QFSFileEngine(const QString & file)145 QFSFileEngine::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 */
QFSFileEngine()155 QFSFileEngine::QFSFileEngine() : QAbstractFileEngine(*new QFSFileEnginePrivate)
156 {
157 }
158 
159 /*!
160     \internal
161 */
QFSFileEngine(QFSFileEnginePrivate & dd)162 QFSFileEngine::QFSFileEngine(QFSFileEnginePrivate &dd)
163     : QAbstractFileEngine(dd)
164 {
165 }
166 
167 /*!
168     \internal
169 */
processOpenModeFlags(QIODevice::OpenMode openMode)170 ProcessOpenModeResult processOpenModeFlags(QIODevice::OpenMode openMode)
171 {
172     ProcessOpenModeResult result;
173     result.ok = false;
174     if ((openMode & QFile::NewOnly) && (openMode & QFile::ExistingOnly)) {
175         qWarning("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("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 */
~QFSFileEngine()203 QFSFileEngine::~QFSFileEngine()
204 {
205     Q_D(QFSFileEngine);
206     if (d->closeFileHandle) {
207         if (d->fh) {
208             fclose(d->fh);
209         } else if (d->fd != -1) {
210             QT_CLOSE(d->fd);
211         }
212     }
213     d->unmapAll();
214 }
215 
216 /*!
217     \reimp
218 */
setFileName(const QString & file)219 void QFSFileEngine::setFileName(const QString &file)
220 {
221     Q_D(QFSFileEngine);
222     d->init();
223     d->fileEntry = QFileSystemEntry(file);
224 }
225 
226 /*!
227     \reimp
228 */
open(QIODevice::OpenMode openMode)229 bool 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("QFSFileEngine::open: No file name specified");
237         setError(QFile::OpenError, QLatin1String("No file name specified"));
238         return false;
239     }
240 
241     const ProcessOpenModeResult res = processOpenModeFlags(openMode);
242     if (!res.ok) {
243         setError(QFileDevice::OpenError, 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(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 */
open(QIODevice::OpenMode openMode,FILE * fh)260 bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh)
261 {
262     return open(openMode, fh, QFile::DontCloseHandle);
263 }
264 
open(QIODevice::OpenMode openMode,FILE * fh,QFile::FileHandleFlags handleFlags)265 bool 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(QFileDevice::OpenError, 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(d->openMode, fh);
286 }
287 
288 /*!
289     Opens the file handle \a fh using the open mode \a flags.
290 */
openFh(QIODevice::OpenMode openMode,FILE * fh)291 bool 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(fh, 0, SEEK_END);
305         } while (ret != 0 && errno == EINTR);
306 
307         if (ret != 0) {
308             q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
309                         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 */
open(QIODevice::OpenMode openMode,int fd)325 bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd)
326 {
327     return open(openMode, fd, QFile::DontCloseHandle);
328 }
329 
open(QIODevice::OpenMode openMode,int fd,QFile::FileHandleFlags handleFlags)330 bool 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(QFileDevice::OpenError, 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(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 */
openFd(QIODevice::OpenMode openMode,int fd)356 bool 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, 0, SEEK_END);
367         } while (ret == -1 && errno == EINTR);
368 
369         if (ret == -1) {
370             q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
371                         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 */
close()386 bool QFSFileEngine::close()
387 {
388     Q_D(QFSFileEngine);
389     d->openMode = QIODevice::NotOpen;
390     return d->nativeClose();
391 }
392 
393 /*!
394     \internal
395 */
closeFdFh()396 bool 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(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(QFile::UnspecifiedError, QSystemError::stdString());
431         }
432         return false;
433     }
434 
435     return true;
436 }
437 
438 /*!
439     \reimp
440 */
flush()441 bool 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 */
syncToDisk()455 bool 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 */
flushFh()466 bool 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(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                     QSystemError::stdString());
483         return false;
484     }
485     return true;
486 }
487 
488 /*!
489     \reimp
490 */
size() const491 qint64 QFSFileEngine::size() const
492 {
493     Q_D(const QFSFileEngine);
494     return d->nativeSize();
495 }
496 
497 /*!
498     \internal
499 */
unmapAll()500 void 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(keys.at(i));
506     }
507 }
508 
509 #ifndef Q_OS_WIN
510 /*!
511     \internal
512 */
sizeFdFh() const513 qint64 QFSFileEnginePrivate::sizeFdFh() const
514 {
515     Q_Q(const QFSFileEngine);
516     const_cast<QFSFileEngine *>(q)->flush();
517 
518     tried_stat = 0;
519     metaData.clearFlags(QFileSystemMetaData::SizeAttribute);
520     if (!doStat(QFileSystemMetaData::SizeAttribute))
521         return 0;
522     return metaData.size();
523 }
524 #endif
525 
526 /*!
527     \reimp
528 */
pos() const529 qint64 QFSFileEngine::pos() const
530 {
531     Q_D(const QFSFileEngine);
532     return d->nativePos();
533 }
534 
535 /*!
536     \internal
537 */
posFdFh() const538 qint64 QFSFileEnginePrivate::posFdFh() const
539 {
540     if (fh)
541         return qint64(QT_FTELL(fh));
542     return QT_LSEEK(fd, 0, SEEK_CUR);
543 }
544 
545 /*!
546     \reimp
547 */
seek(qint64 pos)548 bool QFSFileEngine::seek(qint64 pos)
549 {
550     Q_D(QFSFileEngine);
551     return d->nativeSeek(pos);
552 }
553 
554 /*!
555     \reimp
556 */
fileTime(FileTime time) const557 QDateTime 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(QFileSystemMetaData::AccessTime);
564     }
565 
566     if (d->doStat(QFileSystemMetaData::Times))
567         return d->metaData.fileTime(time);
568 
569     return QDateTime();
570 }
571 
572 
573 /*!
574     \internal
575 */
seekFdFh(qint64 pos)576 bool 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(fh, QT_OFF_T(pos), SEEK_SET);
594         } while (ret != 0 && errno == EINTR);
595 
596         if (ret != 0) {
597             q->setError(QFile::ReadError, QSystemError::stdString());
598             return false;
599         }
600     } else {
601         // Unbuffered stdio mode.
602         if (QT_LSEEK(fd, QT_OFF_T(pos), SEEK_SET) == -1) {
603             qWarning("QFile::at: Cannot set file position %lld", pos);
604             q->setError(QFile::PositionError, QSystemError::stdString());
605             return false;
606         }
607     }
608     return true;
609 }
610 
611 /*!
612     \reimp
613 */
handle() const614 int QFSFileEngine::handle() const
615 {
616     Q_D(const QFSFileEngine);
617     return d->nativeHandle();
618 }
619 
620 /*!
621     \reimp
622 */
read(char * data,qint64 maxlen)623 qint64 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 */
readFdFh(char * data,qint64 len)641 qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len)
642 {
643     Q_Q(QFSFileEngine);
644 
645     if (len < 0 || len != qint64(size_t(len))) {
646         q->setError(QFile::ReadError, 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(data + readBytes, 1, size_t(len - readBytes), fh);
660             eof = feof(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(fh, QT_FTELL(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 + readBytes, 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(QFile::ReadError, QSystemError::stdString());
693     }
694 
695     return readBytes;
696 }
697 
698 /*!
699     \reimp
700 */
readLine(char * data,qint64 maxlen)701 qint64 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 */
readLineFdFh(char * data,qint64 maxlen)719 qint64 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(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(data, int(maxlen + 1), fh)) {
737         if (!feof(fh))
738             q->setError(QFile::ReadError, 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(fh) - oldPos;
748     return lineLength > 0 ? lineLength : qstrlen(data);
749 }
750 
751 /*!
752     \reimp
753 */
write(const char * data,qint64 len)754 qint64 QFSFileEngine::write(const char *data, qint64 len)
755 {
756     Q_D(QFSFileEngine);
757     d->metaData.clearFlags(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 */
writeFdFh(const char * data,qint64 len)773 qint64 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(QFile::WriteError, 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(data + writtenBytes, 1, size_t(len - writtenBytes), 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 + writtenBytes, 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, QSystemError::stdString());
816     } else {
817         // reset the cached size, if any
818         metaData.clearFlags(QFileSystemMetaData::SizeAttribute);
819     }
820 
821     return writtenBytes;
822 }
823 
824 #ifndef QT_NO_FILESYSTEMITERATOR
825 /*!
826     \internal
827 */
beginEntryList(QDir::Filters filters,const QStringList & filterNames)828 QAbstractFileEngine::Iterator *QFSFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames)
829 {
830     return new QFSFileEngineIterator(filters, filterNames);
831 }
832 
833 /*!
834     \internal
835 */
endEntryList()836 QAbstractFileEngine::Iterator *QFSFileEngine::endEntryList()
837 {
838     return nullptr;
839 }
840 #endif // QT_NO_FILESYSTEMITERATOR
841 
842 /*!
843     \internal
844 */
entryList(QDir::Filters filters,const QStringList & filterNames) const845 QStringList QFSFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
846 {
847     return QAbstractFileEngine::entryList(filters, filterNames);
848 }
849 
850 /*!
851     \reimp
852 */
isSequential() const853 bool 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
isSequentialFdFh() const865 bool QFSFileEnginePrivate::isSequentialFdFh() const
866 {
867     if (doStat(QFileSystemMetaData::SequentialType))
868         return metaData.isSequential();
869     return true;
870 }
871 #endif
872 
873 /*!
874     \reimp
875 */
extension(Extension extension,const ExtensionOption * option,ExtensionReturn * output)876 bool QFSFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
877 {
878     Q_D(QFSFileEngine);
879     if (extension == AtEndExtension && d->fh && isSequential())
880         return feof(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(options->offset, options->size, options->flags);
886         return (returnValue->address != nullptr);
887     }
888     if (extension == UnMapExtension) {
889         const UnMapExtensionOption *options = (const UnMapExtensionOption*)option;
890         return d->unmap(options->address);
891     }
892 
893     return false;
894 }
895 
896 /*!
897     \reimp
898 */
supportsExtension(Extension extension) const899 bool 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 */
homePath()951 QString QFSFileEngine::homePath()
952 {
953     return QFileSystemEngine::homePath();
954 }
955 
956 /*!
957   Returns the root path.
958 
959   \sa homePath()
960 */
rootPath()961 QString 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 */
tempPath()970 QString 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 */
copy(const QString & copyName)1005 bool QFSFileEngine::copy(const QString &copyName)
1006 {
1007     Q_D(QFSFileEngine);
1008     QSystemError error;
1009     bool ret = QFileSystemEngine::copyFile(d->fileEntry, QFileSystemEntry(copyName), error);
1010     if (!ret)
1011         setError(QFile::CopyError, error.toString());
1012     return ret;
1013 }
1014 
1015 /*!
1016   \reimp
1017 */
remove()1018 bool QFSFileEngine::remove()
1019 {
1020     Q_D(QFSFileEngine);
1021     QSystemError error;
1022     bool ret = QFileSystemEngine::removeFile(d->fileEntry, error);
1023     d->metaData.clear();
1024     if (!ret)
1025         setError(QFile::RemoveError, error.toString());
1026     return ret;
1027 }
1028 
1029 /*!
1030   \reimp
1031 */
rename(const QString & newName)1032 bool QFSFileEngine::rename(const QString &newName)
1033 {
1034     Q_D(QFSFileEngine);
1035     QSystemError error;
1036     bool ret = QFileSystemEngine::renameFile(d->fileEntry, QFileSystemEntry(newName), error);
1037     if (!ret)
1038         setError(QFile::RenameError, error.toString());
1039     return ret;
1040 }
1041 /*!
1042   \reimp
1043 */
renameOverwrite(const QString & newName)1044 bool QFSFileEngine::renameOverwrite(const QString &newName)
1045 {
1046     Q_D(QFSFileEngine);
1047     QSystemError error;
1048     bool ret = QFileSystemEngine::renameOverwriteFile(d->fileEntry, QFileSystemEntry(newName), error);
1049     if (!ret)
1050         setError(QFile::RenameError, error.toString());
1051     return ret;
1052 }
1053 
1054 /*!
1055   \reimp
1056 */
mkdir(const QString & name,bool createParentDirectories) const1057 bool QFSFileEngine::mkdir(const QString &name, bool createParentDirectories) const
1058 {
1059     return QFileSystemEngine::createDirectory(QFileSystemEntry(name), createParentDirectories);
1060 }
1061 
1062 /*!
1063   \reimp
1064 */
rmdir(const QString & name,bool recurseParentDirectories) const1065 bool QFSFileEngine::rmdir(const QString &name, bool recurseParentDirectories) const
1066 {
1067     return QFileSystemEngine::removeDirectory(QFileSystemEntry(name), 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 */
setCurrentPath(const QString & path)1077 bool 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 
1094 QT_END_NAMESPACE
1095 
1096 #endif // QT_NO_FSFILEENGINE
1097