summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qfile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/io/qfile.cpp')
-rw-r--r--src/corelib/io/qfile.cpp122
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);