diff options
Diffstat (limited to 'src/corelib/io/qfile.cpp')
-rw-r--r-- | src/corelib/io/qfile.cpp | 122 |
1 files changed, 70 insertions, 52 deletions
diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp index 64d34aec59..bac995ff25 100644 --- a/src/corelib/io/qfile.cpp +++ b/src/corelib/io/qfile.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -42,12 +43,14 @@ #include "qfile.h" #include "qfsfileengine_p.h" #include "qtemporaryfile.h" +#include "qtemporaryfile_p.h" #include "qlist.h" #include "qfileinfo.h" #include "private/qiodevice_p.h" #include "private/qfile_p.h" #include "private/qfilesystemengine_p.h" #include "private/qsystemerror_p.h" +#include "private/qtemporaryfile_p.h" #if defined(QT_BUILD_CORE_LIB) # include "qcoreapplication.h" #endif @@ -420,7 +423,7 @@ QFile::exists() const Q_D(const QFile); // 0x1000000 = QAbstractFileEngine::Refresh, forcing an update return (d->engine()->fileFlags(QAbstractFileEngine::FlagsMask - | QAbstractFileEngine::FileFlag(0x1000000)) & QAbstractFileEngine::ExistsFlag); + | QAbstractFileEngine::Refresh) & QAbstractFileEngine::ExistsFlag); } /*! @@ -553,7 +556,9 @@ bool QFile::rename(const QString &newName) { Q_D(QFile); - if (d->fileName.isEmpty()) { + + // if this is a QTemporaryFile, the virtual fileName() call here may do something + if (fileName().isEmpty()) { qWarning("QFile::rename: Empty or null file name"); return false; } @@ -565,57 +570,64 @@ QFile::rename(const QString &newName) d->setError(QFile::RenameError, tr("Source file does not exist.")); return false; } + // If the file exists and it is a case-changing rename ("foo" -> "Foo"), // compare Ids to make sure it really is a different file. // Note: this does not take file engines into account. + bool changingCase = false; QByteArray targetId = QFileSystemEngine::id(QFileSystemEntry(newName)); if (!targetId.isNull()) { QByteArray fileId = d->fileEngine ? d->fileEngine->id() : QFileSystemEngine::id(QFileSystemEntry(d->fileName)); - if (fileId != targetId || d->fileName.compare(newName, Qt::CaseInsensitive)) { - // ### Race condition. If a file is moved in after this, it /will/ be - // overwritten. On Unix, the proper solution is to use hardlinks: - // return ::link(old, new) && ::remove(old); + changingCase = (fileId == targetId && d->fileName.compare(newName, Qt::CaseInsensitive) == 0); + if (!changingCase) { d->setError(QFile::RenameError, tr("Destination file exists")); return false; } -#ifndef QT_NO_TEMPORARYFILE - // This #ifndef disables the workaround it encloses. Therefore, this configuration is not recommended. + #ifdef Q_OS_LINUX // rename() on Linux simply does nothing when renaming "foo" to "Foo" on a case-insensitive // FS, such as FAT32. Move the file away and rename in 2 steps to work around. - QTemporaryFile tempFile(d->fileName + QLatin1String(".XXXXXX")); - tempFile.setAutoRemove(false); - if (!tempFile.open(QIODevice::ReadWrite)) { - d->setError(QFile::RenameError, tempFile.errorString()); - return false; - } - tempFile.close(); - if (!d->engine()->rename(tempFile.fileName())) { - d->setError(QFile::RenameError, tr("Error while renaming.")); - return false; - } - if (tempFile.rename(newName)) { - d->fileEngine->setFileName(newName); - d->fileName = newName; - return true; - } - d->setError(QFile::RenameError, tempFile.errorString()); - // We need to restore the original file. - if (!tempFile.rename(d->fileName)) { - d->setError(QFile::RenameError, errorString() + QLatin1Char('\n') + QTemporaryFileName tfn(d->fileName); + QFileSystemEntry src(d->fileName); + QSystemError error; + for (int attempt = 0; attempt < 16; ++attempt) { + QFileSystemEntry tmp(tfn.generateNext(), QFileSystemEntry::FromNativePath()); + + // rename to temporary name + if (!QFileSystemEngine::renameFile(src, tmp, error)) + continue; + + // rename to final name + if (QFileSystemEngine::renameFile(tmp, QFileSystemEntry(newName), error)) { + d->fileEngine->setFileName(newName); + d->fileName = newName; + return true; + } + + // We need to restore the original file. + QSystemError error2; + if (QFileSystemEngine::renameFile(tmp, src, error2)) + break; // report the original error, below + + // report both errors + d->setError(QFile::RenameError, + tr("Error while renaming: %1").arg(error.toString()) + + QLatin1Char('\n') + tr("Unable to restore from %1: %2"). - arg(QDir::toNativeSeparators(tempFile.fileName()), tempFile.errorString())); + arg(QDir::toNativeSeparators(tmp.filePath()), error2.toString())); + return false; } + d->setError(QFile::RenameError, + tr("Error while renaming: %1").arg(error.toString())); return false; #endif // Q_OS_LINUX -#endif // QT_NO_TEMPORARYFILE } unsetError(); close(); if(error() == QFile::NoError) { - if (d->engine()->rename(newName)) { + if (changingCase ? d->engine()->renameOverwrite(newName) : d->engine()->rename(newName)) { unsetError(); // engine was able to handle the new name so we just reset it d->fileEngine->setFileName(newName); @@ -707,7 +719,7 @@ bool QFile::link(const QString &linkName) { Q_D(QFile); - if (d->fileName.isEmpty()) { + if (fileName().isEmpty()) { qWarning("QFile::link: Empty or null file name"); return false; } @@ -753,7 +765,7 @@ bool QFile::copy(const QString &newName) { Q_D(QFile); - if (d->fileName.isEmpty()) { + if (fileName().isEmpty()) { qWarning("QFile::copy: Empty or null file name"); return false; } @@ -794,25 +806,27 @@ QFile::copy(const QString &newName) close(); d->setError(QFile::CopyError, tr("Cannot open for output")); } else { - char block[4096]; - qint64 totalRead = 0; - while(!atEnd()) { - qint64 in = read(block, sizeof(block)); - if (in <= 0) - break; - totalRead += in; - if(in != out.write(block, in)) { - close(); - d->setError(QFile::CopyError, tr("Failure to write block")); - error = true; - break; + if (!d->engine()->cloneTo(out.d_func()->engine())) { + char block[4096]; + qint64 totalRead = 0; + while (!atEnd()) { + qint64 in = read(block, sizeof(block)); + if (in <= 0) + break; + totalRead += in; + if (in != out.write(block, in)) { + close(); + d->setError(QFile::CopyError, tr("Failure to write block")); + error = true; + break; + } } - } - if (totalRead != size()) { - // Unable to read from the source. The error string is - // already set from read(). - error = true; + if (totalRead != size()) { + // Unable to read from the source. The error string is + // already set from read(). + error = true; + } } if (!error && !out.rename(newName)) { error = true; @@ -957,7 +971,9 @@ bool QFile::open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags) qWarning("QFile::open: File access not specified"); return false; } - if (d->openExternalFile(mode, fh, handleFlags)) { + + // QIODevice provides the buffering, so request unbuffered file engines + if (d->openExternalFile(mode | Unbuffered, fh, handleFlags)) { QIODevice::open(mode); if (!(mode & Append) && !isSequential()) { qint64 pos = (qint64)QT_FTELL(fh); @@ -1013,7 +1029,9 @@ bool QFile::open(int fd, OpenMode mode, FileHandleFlags handleFlags) qWarning("QFile::open: File access not specified"); return false; } - if (d->openExternalFile(mode, fd, handleFlags)) { + + // QIODevice provides the buffering, so request unbuffered file engines + if (d->openExternalFile(mode | Unbuffered, fd, handleFlags)) { QIODevice::open(mode); if (!(mode & Append) && !isSequential()) { qint64 pos = (qint64)QT_LSEEK(fd, QT_OFF_T(0), SEEK_CUR); |