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