From a06fb55dcb0820e078588c6a6d96c74b712dd380 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 29 Jun 2017 20:13:44 -0700 Subject: [PATCH] QFile::rename: use QTemporaryFileName instead of QTemporaryFile This means the workaround for Linux's behavior now works even if QTemporaryFile is disabled. We also avoid the creation of an otherwise unused temporary file just so we can take its name. Tested with tests/manual/filetest. Strace shows: renameat2(AT_FDCWD, "a", AT_FDCWD, "/var/run/media/tjmaciei/B852-6088/a.EBG705", RENAME_NOREPLACE) = 0 renameat2(AT_FDCWD, "/var/run/media/tjmaciei/B852-6088/a.EBG705", AT_FDCWD, "A", RENAME_NOREPLACE) = 0 Before it was: open("/var/run/media/tjmaciei/B852-6088/a.VuL412", O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC, 0600) = 4 fcntl(4, F_SETFD, FD_CLOEXEC) = 0 lseek(4, 0, SEEK_SET) = 0 rename("a", "/var/run/media/tjmaciei/B852-6088/a.VuL412") = 0 stat("A", 0x7fff13260f00) = -1 ENOENT (No such file or directory) close(4) = 0 renameat2(AT_FDCWD, "/var/run/media/tjmaciei/B852-6088/a.VuL412", AT_FDCWD, "A", RENAME_NOREPLACE) = 0 (the absolute path comes from fd61059d359f0bee1c37d6bf08bf4b83381658ca and it seems it was needed for Symbian) Change-Id: I1eba2b016de74620bfc8fffd14ccc7c4ded009c8 Reviewed-by: Friedemann Kleint Reviewed-by: Lars Knoll Reviewed-by: Oswald Buddenhagen Reviewed-by: Thiago Macieira --- src/corelib/io/qfile.cpp | 57 +++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp index ce99ac96c0..c1d5abdf7b 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. @@ -49,6 +50,7 @@ #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 @@ -581,37 +583,44 @@ QFile::rename(const QString &newName) 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()->renameOverwrite(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();