From 1ec2f5df26d843bd431b1d202382d626dfac3184 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 8 Jan 2025 15:06:44 +0100 Subject: [PATCH] QTemporaryFile: add private API for an overwriting rename() In many situations, it is desireable that QTemporaryFile::rename() overwrites an existing file, such as when emulating QSaveFile because one would like more control over the temp file (such as passing it to a different process). The public API of QTemporaryFile did not allow it, making the typical Unix mktemp + mv algorithm to avoid symlink attacks unimplementable with public Qt API. QSaveFile is not a 100% replacement, because it always follows symlinks and places the temporary file beside the physical target file, keeping the symlink intact, instead of placing the tmp file beside the symlink and clobbering it, if the target happens to be one. Extract Method QTemporaryFilePrivate::rename() and add a bool overwrite parameter that causes the existing QTemporaryFileEngine::renameOverwrite() to be called in lieu of QTemporaryFileEngine::rename(). The non-atomic path via QFile::rename() is not affected. This makes the functionality available as private API, in case we need it in older branches. Task-number: QTBUG-132646 Pick-to: 6.8 6.5 5.15 Change-Id: I0d5ca96c0a1a557854582e1a565a3db0d8af2a95 Reviewed-by: Thiago Macieira (cherry picked from commit a01b14f1de59d378088a949bb21f3536832634c8) Reviewed-by: Qt Cherry-pick Bot --- src/corelib/io/qtemporaryfile.cpp | 26 ++++++++++++++++---------- src/corelib/io/qtemporaryfile_p.h | 2 ++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp index 0d69d3835a0..c1f01267949 100644 --- a/src/corelib/io/qtemporaryfile.cpp +++ b/src/corelib/io/qtemporaryfile.cpp @@ -886,21 +886,27 @@ void QTemporaryFile::setFileTemplate(const QString &name) bool QTemporaryFile::rename(const QString &newName) { Q_D(QTemporaryFile); - auto tef = static_cast(d->fileEngine.get()); - if (!tef || !tef->isReallyOpen() || !tef->filePathWasTemplate) - return QFile::rename(newName); + return d->rename(newName, false); +} - unsetError(); - close(); - if (error() == QFile::NoError) { - if (tef->rename(newName)) { - unsetError(); +bool QTemporaryFilePrivate::rename(const QString &newName, bool overwrite) +{ + Q_Q(QTemporaryFile); + auto tef = static_cast(fileEngine.get()); + if (!tef || !tef->isReallyOpen() || !tef->filePathWasTemplate) + return q->QFile::rename(newName); + + q->unsetError(); + q->close(); + if (q->error() == QFile::NoError) { + if (overwrite ? tef->renameOverwrite(newName) : tef->rename(newName)) { + q->unsetError(); // engine was able to handle the new name so we just reset it - d->fileName = newName; + fileName = newName; return true; } - d->setError(QFile::RenameError, tef->errorString()); + setError(QFile::RenameError, tef->errorString()); } return false; } diff --git a/src/corelib/io/qtemporaryfile_p.h b/src/corelib/io/qtemporaryfile_p.h index d160afe41ed..d79ffc266b9 100644 --- a/src/corelib/io/qtemporaryfile_p.h +++ b/src/corelib/io/qtemporaryfile_p.h @@ -56,6 +56,8 @@ public: explicit QTemporaryFilePrivate(const QString &templateNameIn); ~QTemporaryFilePrivate(); + bool rename(const QString &newName, bool overwrite); + QAbstractFileEngine *engine() const override; void resetFileEngine() const; void materializeUnnamedFile();