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