src/corelib/io/qfsfileengine.cpp
changeset 3 41300fa6a67c
parent 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
2:56cd8111b7f7 3:41300fa6a67c
    53 #endif
    53 #endif
    54 #if defined(Q_OS_UNIX)
    54 #if defined(Q_OS_UNIX)
    55 #include "private/qcore_unix_p.h"
    55 #include "private/qcore_unix_p.h"
    56 #endif
    56 #endif
    57 #include <stdio.h>
    57 #include <stdio.h>
       
    58 #include <stdlib.h>
    58 
    59 
    59 QT_BEGIN_NAMESPACE
    60 QT_BEGIN_NAMESPACE
    60 
    61 
    61 #ifdef Q_OS_WIN
    62 #ifdef Q_OS_WIN
    62 #  ifndef S_ISREG
    63 #  ifndef S_ISREG
    74 #  ifndef INVALID_FILE_ATTRIBUTES
    75 #  ifndef INVALID_FILE_ATTRIBUTES
    75 #    define INVALID_FILE_ATTRIBUTES (DWORD (-1))
    76 #    define INVALID_FILE_ATTRIBUTES (DWORD (-1))
    76 #  endif
    77 #  endif
    77 #endif
    78 #endif
    78 
    79 
    79 
       
    80 /*! \class QFSFileEngine
    80 /*! \class QFSFileEngine
    81     \brief The QFSFileEngine class implements Qt's default file engine.
    81     \brief The QFSFileEngine class implements Qt's default file engine.
    82     \since 4.1
    82     \since 4.1
    83 
    83 
    84     This class is part of the file engine framework in Qt. If you only want to
    84     This class is part of the file engine framework in Qt. If you only want to
   120     lastFlushFailed = false;
   120     lastFlushFailed = false;
   121     closeFileHandle = false;
   121     closeFileHandle = false;
   122 #ifdef Q_OS_WIN
   122 #ifdef Q_OS_WIN
   123     fileAttrib = INVALID_FILE_ATTRIBUTES;
   123     fileAttrib = INVALID_FILE_ATTRIBUTES;
   124     fileHandle = INVALID_HANDLE_VALUE;
   124     fileHandle = INVALID_HANDLE_VALUE;
       
   125     mapHandle = INVALID_HANDLE_VALUE;
   125     cachedFd = -1;
   126     cachedFd = -1;
   126 #endif
       
   127 #ifdef Q_USE_DEPRECATED_MAP_API
       
   128     fileMapHandle = INVALID_HANDLE_VALUE;
       
   129 #endif
   127 #endif
   130 }
   128 }
   131 
   129 
   132 /*!
   130 /*!
   133     \internal
   131     \internal
   137 */
   135 */
   138 QString QFSFileEnginePrivate::canonicalized(const QString &path)
   136 QString QFSFileEnginePrivate::canonicalized(const QString &path)
   139 {
   137 {
   140     if (path.isEmpty())
   138     if (path.isEmpty())
   141         return path;
   139         return path;
       
   140 
       
   141     // FIXME let's see if this stuff works, then we might be able to remove some of the other code.
       
   142 #if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN)
       
   143     if (path.size() == 1 && path.at(0) == QLatin1Char('/'))
       
   144         return path;
       
   145 #endif
       
   146     // Mac OS X 10.5.x doesn't support the realpath(X,0) extenstion we use here.
       
   147 #if defined(Q_OS_LINIX) || defined(Q_OS_SYMBIAN)
       
   148     char *ret = realpath(path.toLocal8Bit().constData(), (char*)0);
       
   149     if (ret) {
       
   150         QString canonicalPath = QDir::cleanPath(QString::fromLocal8Bit(ret));
       
   151         free(ret);
       
   152         return canonicalPath;
       
   153     }
       
   154 #endif
   142 
   155 
   143     QFileInfo fi;
   156     QFileInfo fi;
   144     const QChar slash(QLatin1Char('/'));
   157     const QChar slash(QLatin1Char('/'));
   145     QString tmpPath = path;
   158     QString tmpPath = path;
   146     int separatorPos = 0;
   159     int separatorPos = 0;
   158         separatorPos = tmpPath.indexOf(slash, separatorPos + 1);
   171         separatorPos = tmpPath.indexOf(slash, separatorPos + 1);
   159         QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(separatorPos);
   172         QString prefix = separatorPos == -1 ? tmpPath : tmpPath.left(separatorPos);
   160         if (
   173         if (
   161 #ifdef Q_OS_SYMBIAN
   174 #ifdef Q_OS_SYMBIAN
   162             // Symbian doesn't support directory symlinks, so do not check for link unless we
   175             // 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, 
   176             // 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
   177             // but also saves us from lot of unnecessary platform security check failures
   165             // when dealing with files under *:/private directories.
   178             // when dealing with files under *:/private directories.
   166             separatorPos == -1 &&
   179             separatorPos == -1 &&
   167 #endif            
   180 #endif
   168             !nonSymlinks.contains(prefix)) {
   181             !nonSymlinks.contains(prefix)) {
   169             fi.setFile(prefix);
   182             fi.setFile(prefix);
   170             if (fi.isSymLink()) {
   183             if (fi.isSymLink()) {
   171                 QString target = fi.symLinkTarget();
   184                 QString target = fi.symLinkTarget();
   172                 if (separatorPos != -1) {
   185                 if (separatorPos != -1) {
   317     // Seek to the end when in Append mode.
   330     // Seek to the end when in Append mode.
   318     if (openMode & QIODevice::Append) {
   331     if (openMode & QIODevice::Append) {
   319         int ret;
   332         int ret;
   320         do {
   333         do {
   321             ret = QT_FSEEK(fh, 0, SEEK_END);
   334             ret = QT_FSEEK(fh, 0, SEEK_END);
   322         } while (ret == -1 && errno == EINTR);
   335         } while (ret != 0 && errno == EINTR);
   323 
   336 
   324         if (ret == -1) {
   337         if (ret != 0) {
   325             q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
   338             q->setError(errno == EMFILE ? QFile::ResourceError : QFile::OpenError,
   326                         qt_error_string(int(errno)));
   339                         qt_error_string(int(errno)));
   327 
   340 
   328             this->openMode = QIODevice::NotOpen;
   341             this->openMode = QIODevice::NotOpen;
   329             this->fh = 0;
   342             this->fh = 0;
   564     // fwrite are undefined if not called either in sequence, or if preceded
   577     // fwrite are undefined if not called either in sequence, or if preceded
   565     // with a call to fflush().
   578     // with a call to fflush().
   566     if (lastIOCommand != QFSFileEnginePrivate::IOFlushCommand && !q->flush())
   579     if (lastIOCommand != QFSFileEnginePrivate::IOFlushCommand && !q->flush())
   567         return false;
   580         return false;
   568 
   581 
       
   582     if (pos < 0 || pos != qint64(QT_OFF_T(pos)))
       
   583         return false;
       
   584 
   569     if (fh) {
   585     if (fh) {
   570         // Buffered stdlib mode.
   586         // Buffered stdlib mode.
   571         int ret;
   587         int ret;
   572         do {
   588         do {
   573             ret = QT_FSEEK(fh, QT_OFF_T(pos), SEEK_SET);
   589             ret = QT_FSEEK(fh, QT_OFF_T(pos), SEEK_SET);
   574         } while (ret == -1 && errno == EINTR);
   590         } while (ret != 0 && errno == EINTR);
   575 
   591 
   576         if (ret == -1) {
   592         if (ret != 0) {
   577             q->setError(QFile::ReadError, qt_error_string(int(errno)));
   593             q->setError(QFile::ReadError, qt_error_string(int(errno)));
   578             return false;
   594             return false;
   579         }
   595         }
   580     } else {
   596     } else {
   581         // Unbuffered stdio mode.
   597         // Unbuffered stdio mode.
   582         if (QT_LSEEK(fd, pos, SEEK_SET) == -1) {
   598         if (QT_LSEEK(fd, QT_OFF_T(pos), SEEK_SET) == -1) {
   583             qWarning() << "QFile::at: Cannot set file position" << pos;
   599             qWarning() << "QFile::at: Cannot set file position" << pos;
   584             q->setError(QFile::PositionError, qt_error_string(errno));
   600             q->setError(QFile::PositionError, qt_error_string(errno));
   585             return false;
   601             return false;
   586         }
   602         }
   587     }
   603     }
   620 */
   636 */
   621 qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len)
   637 qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len)
   622 {
   638 {
   623     Q_Q(QFSFileEngine);
   639     Q_Q(QFSFileEngine);
   624 
   640 
   625     // Buffered stdlib mode.
   641     if (len < 0 || len != qint64(size_t(len))) {
       
   642         q->setError(QFile::ReadError, qt_error_string(EINVAL));
       
   643         return -1;
       
   644     }
       
   645 
       
   646     qint64 readBytes = 0;
       
   647     bool eof = false;
       
   648 
   626     if (fh) {
   649     if (fh) {
   627         qint64 readBytes = 0;
   650         // Buffered stdlib mode.
   628         qint64 read = 0;
   651 
   629         int retry = 0;
   652         size_t result;
   630 
   653         bool retry = true;
   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 {
   654         do {
   635             if (retry == 1)
   655             result = fread(data + readBytes, 1, size_t(len - readBytes), fh);
   636                 retry = 2;
   656             eof = feof(fh);
   637 
   657             if (retry && eof && result == 0) {
   638             bytesToRead = qMin<qint64>(4096, len - read);
   658                 // On Mac OS, this is needed, e.g., if a file was written to
   639             do {
   659                 // through another stream since our last read. See test
   640                 readBytes = fread(data + read, 1, size_t(bytesToRead), fh);
   660                 // tst_QFile::appendAndRead
   641             } while (readBytes == 0 && !feof(fh) && errno == EINTR);
   661                 QT_FSEEK(fh, QT_FTELL(fh), SEEK_SET); // re-sync stream.
   642 
   662                 retry = false;
   643             if (readBytes > 0) {
   663                 continue;
   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             }
   664             }
   650         } while (retry == 1 || (readBytes == bytesToRead && read < len));
   665             readBytes += result;
   651 
   666         } while (!eof && (result == 0 ? errno == EINTR : readBytes < len));
   652         // Return the number of bytes read, or if nothing was read, return -1
   667 
   653         // if an error occurred, or 0 if we detected EOF.
   668     } else if (fd != -1) {
   654         if (read == 0) {
   669         // Unbuffered stdio mode.
   655             q->setError(QFile::ReadError, qt_error_string(int(errno)));
   670 
   656             if (!feof(fh))
   671 #ifdef Q_OS_WIN
   657                 read = -1;
       
   658         }
       
   659         return read;
       
   660     }
       
   661 
       
   662     // Unbuffered stdio mode.
       
   663     qint64 ret = 0;
       
   664     if (len) {
       
   665         int result;
   672         int result;
   666         qint64 read = 0;
   673 #else
   667         errno = 0;
   674         ssize_t result;
   668 
   675 #endif
   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 {
   676         do {
   672             qint64 bytesToRead = qMin<qint64>(4096, len - read);
   677             result = QT_READ(fd, data + readBytes, size_t(len - readBytes));
   673             do {
   678         } while ((result == -1 && errno == EINTR)
   674                 result = QT_READ(fd, data + read, int(bytesToRead));
   679                 || (result > 0 && (readBytes += result) < len));
   675             } while (result == -1 && errno == EINTR);
   680 
   676             if (result > 0)
   681         eof = !(result == -1);
   677                 read += result;
   682     }
   678         } while (result > 0 && read < len);
   683 
   679 
   684     if (!eof && readBytes == 0) {
   680         // Return the number of bytes read, or if nothing was read, return -1
   685         readBytes = -1;
   681         // if an error occurred.
   686         q->setError(QFile::ReadError, qt_error_string(errno));
   682         if (read > 0) {
   687     }
   683             ret += read;
   688 
   684         } else if (read == 0 && result < 0) {
   689     return readBytes;
   685             ret = -1;
       
   686             q->setError(QFile::ReadError, qt_error_string(errno));
       
   687         }
       
   688     }
       
   689     return ret;
       
   690 }
   690 }
   691 
   691 
   692 /*!
   692 /*!
   693     \reimp
   693     \reimp
   694 */
   694 */
   764     \internal
   764     \internal
   765 */
   765 */
   766 qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len)
   766 qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len)
   767 {
   767 {
   768     Q_Q(QFSFileEngine);
   768     Q_Q(QFSFileEngine);
   769     qint64 result;
   769 
   770     qint64 written = 0;
   770     if (len < 0 || len != qint64(size_t(len))) {
   771 
   771         q->setError(QFile::WriteError, qt_error_string(EINVAL));
   772     do {
   772         return -1;
   773         // Write blocks of 4k to avoid platform limitations (Windows commonly
   773     }
   774         // bails out if you read or write too large blocks at once).
   774 
   775         qint64 bytesToWrite = qMin<qint64>(4096, len - written);
   775     qint64 writtenBytes = 0;
   776         if (fh) {
   776 
   777             do {
   777     if (fh) {
   778                 // Buffered stdlib mode.
   778         // Buffered stdlib mode.
   779                 result = qint64(fwrite(data + written, 1, size_t(bytesToWrite), fh));
   779 
   780             } while (result == 0 && errno == EINTR);
   780         size_t result;
   781             if (bytesToWrite > 0 && result == 0)
   781         do {
   782                 result = -1;
   782             result = fwrite(data + writtenBytes, 1, size_t(len - writtenBytes), fh);
   783         } else {
   783             writtenBytes += result;
   784             do {
   784         } while (result == 0 ? errno == EINTR : writtenBytes < len);
   785                 // Unbuffered stdio mode.
   785 
   786                 result = QT_WRITE(fd, data + written, bytesToWrite);
   786     } else if (fd != -1) {
   787             } while (result == -1 && errno == EINTR);
   787         // Unbuffered stdio mode.
   788         }
   788 
   789         if (result > 0)
   789 #ifdef Q_OS_WIN
   790             written += qint64(result);
   790         int result;
   791     } while (written < len && result > 0);
   791 #else
   792 
   792         ssize_t result;
   793     // If we read anything, return that with success. Otherwise, set an error,
   793 #endif
   794     // and return the last return value.
   794         do {
   795     if (result > 0)
   795             result = QT_WRITE(fd, data + writtenBytes, size_t(len - writtenBytes));
   796         return written;
   796         } while ((result == -1 && errno == EINTR)
   797     q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, qt_error_string(errno));
   797                 || (result > 0 && (writtenBytes += result) < len));
   798     return result;
   798     }
       
   799 
       
   800     if (len &&  writtenBytes == 0) {
       
   801         writtenBytes = -1;
       
   802         q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, qt_error_string(errno));
       
   803     }
       
   804 
       
   805     return writtenBytes;
   799 }
   806 }
   800 
   807 
   801 /*!
   808 /*!
   802     \internal
   809     \internal
   803 */
   810 */
   901 */
   908 */
   902 
   909 
   903 /*! \fn QString QFSFileEngine::currentPath(const QString &fileName)
   910 /*! \fn QString QFSFileEngine::currentPath(const QString &fileName)
   904   For Unix, returns the current working directory for the file
   911   For Unix, returns the current working directory for the file
   905   engine.
   912   engine.
   906   
   913 
   907   For Windows, returns the canonicalized form of the current path used
   914   For Windows, returns the canonicalized form of the current path used
   908   by the file engine for the drive specified by \a fileName.  On
   915   by the file engine for the drive specified by \a fileName.  On
   909   Windows, each drive has its own current directory, so a different
   916   Windows, each drive has its own current directory, so a different
   910   path is returned for file names that include different drive names
   917   path is returned for file names that include different drive names
   911   (e.g. A: or C:).
   918   (e.g. A: or C:).