From f638c9fa317eeb2085249238d95996e783a1c891 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 24 Jan 2025 14:18:04 +0100 Subject: [PATCH] Use `std::filesystem::rename` on non-Windows systems To prevent `QFile::rename` doing normalization changes to the file name. --- src/common/filesystembase.cpp | 23 +++++----------------- test/testfilesystem.cpp | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/common/filesystembase.cpp b/src/common/filesystembase.cpp index 835c37c7fbc..193b44d9c82 100644 --- a/src/common/filesystembase.cpp +++ b/src/common/filesystembase.cpp @@ -209,28 +209,15 @@ bool FileSystem::uncheckedRenameReplace(const QString &originFileName, QString *errorString) { Q_ASSERT(errorString); + #ifndef Q_OS_WIN - bool success; - QFile orig(originFileName); - // We want a rename that also overwrites. QFile::rename does not overwrite. - // Qt 5.1 has QSaveFile::renameOverwrite we could use. - // ### FIXME - success = true; - bool destExists = fileExists(destinationFileName); - if (destExists && !QFile::remove(destinationFileName)) { - *errorString = orig.errorString(); - qCWarning(lcFileSystem) << "Target file could not be removed."; - success = false; - } - if (success) { - success = orig.rename(destinationFileName); - } - if (!success) { - *errorString = orig.errorString(); + std::error_code err; + std::filesystem::rename(originFileName.toStdString(), destinationFileName.toStdString(), err); + if (err) { + *errorString = QString::fromStdString(err.message()); qCWarning(lcFileSystem) << "Renaming temp file to final failed: " << *errorString; return false; } - #else //Q_OS_WIN // You can not overwrite a read-only file on windows. diff --git a/test/testfilesystem.cpp b/test/testfilesystem.cpp index 097bcb4e1a9..e2fb5da7659 100644 --- a/test/testfilesystem.cpp +++ b/test/testfilesystem.cpp @@ -168,6 +168,43 @@ private Q_SLOTS: } csync_vio_local_closedir(dh); } + + void testRename_data() + { + QTest::addColumn("name"); + + const unsigned char a_umlaut_composed_bytes[] = {0xc3, 0xa4, 0x00}; + const QString a_umlaut_composed = QString::fromUtf8(reinterpret_cast(a_umlaut_composed_bytes)); + const QString a_umlaut_decomposed = a_umlaut_composed.normalized(QString::NormalizationForm_D); + + QTest::newRow("a-umlaut composed") << a_umlaut_composed; + QTest::newRow("a-umlaut decomposed") << a_umlaut_decomposed; + } + + // This is not a full test, it is meant to verify that no nomalization changes are done. + void testRename() + { + QFETCH(QString, name); + + auto tmp = OCC::TestUtils::createTempDir(); + QFile f(tmp.path() + u"/abc"); + QVERIFY(f.open(QFile::WriteOnly)); + QByteArray data("abc"); + QCOMPARE(f.write(data), data.size()); + f.close(); + + QString err; + QVERIFY(OCC::FileSystem::uncheckedRenameReplace(f.fileName(), tmp.path() + u'/' + name, &err)); + + csync_file_stat_t buf; + auto dh = csync_vio_local_opendir(tmp.path()); + QVERIFY(dh); + while (auto fs = csync_vio_local_readdir(dh, nullptr)) { + QCOMPARE(fs->path, name); + QCOMPARE(fs->type, ItemTypeFile); + } + csync_vio_local_closedir(dh); + } }; QTEST_GUILESS_MAIN(TestFileSystem)