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.9 6.8 6.5 5.15
Change-Id: I0d5ca96c0a1a557854582e1a565a3db0d8af2a95
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Marc Mutz 2025-01-08 15:06:44 +01:00
parent 2dfbfd5f9a
commit a01b14f1de
2 changed files with 18 additions and 10 deletions

View File

@ -886,21 +886,27 @@ void QTemporaryFile::setFileTemplate(const QString &name)
bool QTemporaryFile::rename(const QString &newName)
{
Q_D(QTemporaryFile);
auto tef = static_cast<QTemporaryFileEngine *>(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<QTemporaryFileEngine *>(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;
}

View File

@ -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();