diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2017-06-29 14:03:26 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2017-08-04 17:29:52 +0000 |
commit | 363dc9146e53e24172bb9c0ae68100a8543bd9ae (patch) | |
tree | 49374e22f5986e9a99e46e0f73a187f55ef67bc9 /src/corelib/io/qfile.cpp | |
parent | 74197140be68fd7fe54c3a346a0e769928d7a3ea (diff) |
QFSFileEngine: make rename() on Unix not overwrite
The rename(2) system call overwrites, so instead of using it, we try to
use the link/unlink pair. This works for regular cases, but can fail if
trying to change case in case-insensitive filesystems, if we're
operating on a non-Unix filesystem (FAT) or, on Linux, if the file
doesn't belong to the calling user (BSDs permit this). For those cases,
we fall back to rename(2).
That means there's a race condition if a new file is created there. But
we at least reduce the likelihood of that happening for regular files.
Change-Id: I1eba2b016de74620bfc8fffd14ccb38fd929e5aa
Reviewed-by: David Faure <david.faure@kdab.com>
Diffstat (limited to 'src/corelib/io/qfile.cpp')
-rw-r--r-- | src/corelib/io/qfile.cpp | 12 |
1 files changed, 6 insertions, 6 deletions
diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp index 2cfb718932..ce99ac96c0 100644 --- a/src/corelib/io/qfile.cpp +++ b/src/corelib/io/qfile.cpp @@ -566,18 +566,18 @@ 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; } @@ -593,7 +593,7 @@ QFile::rename(const QString &newName) return false; } tempFile.close(); - if (!d->engine()->rename(tempFile.fileName())) { + if (!d->engine()->renameOverwrite(tempFile.fileName())) { d->setError(QFile::RenameError, tr("Error while renaming.")); return false; } @@ -616,7 +616,7 @@ QFile::rename(const QString &newName) 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); |