From 3dae3edff5e77e5b82ef2cfaa8ef4148870a36b8 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Sat, 31 Mar 2018 15:08:06 +0200 Subject: [PATCH] QzTools: Add copyRecursively and removeRecursively --- autotests/autotests.h | 2 +- autotests/qztoolstest.cpp | 104 +++++++++++++++++++++++++++++ autotests/qztoolstest.h | 3 + src/lib/app/mainapplication.cpp | 2 +- src/lib/app/profilemanager.cpp | 2 +- src/lib/other/clearprivatedata.cpp | 8 +-- src/lib/tools/qztools.cpp | 74 ++++++++++++++------ src/lib/tools/qztools.h | 4 +- 8 files changed, 170 insertions(+), 29 deletions(-) diff --git a/autotests/autotests.h b/autotests/autotests.h index ca4d21a31..1b3ac1ffc 100644 --- a/autotests/autotests.h +++ b/autotests/autotests.h @@ -23,7 +23,7 @@ #define FALKONTEST_MAIN(Test) \ int main(int argc, char **argv) \ { \ - QzTools::removeDir(QDir::tempPath() + QSL("/Falkon-test")); \ + QzTools::removeRecursively(QDir::tempPath() + QSL("/Falkon-test")); \ MainApplication::setTestModeEnabled(true); \ MainApplication app(argc, argv); \ QTEST_DISABLE_KEYPAD_NAVIGATION; \ diff --git a/autotests/qztoolstest.cpp b/autotests/qztoolstest.cpp index 3a433b384..5d705f6c9 100644 --- a/autotests/qztoolstest.cpp +++ b/autotests/qztoolstest.cpp @@ -271,6 +271,110 @@ void QzToolsTest::ensureUniqueFilename() } } +static void createTestDirectoryStructure(const QString &path) +{ + QDir().mkdir(path); + QDir dir(path); + dir.mkdir("dir1"); + dir.mkdir("dir2"); + dir.mkdir("dir3"); + dir.cd("dir1"); + dir.mkdir("dir1_1"); + dir.mkdir("dir1_2"); + dir.mkdir("dir1_3"); + dir.cdUp(); + dir.cd("dir3"); + dir.mkdir("dir3_1"); + QFile file(path + "/dir1/dir1_2/file1.txt"); + file.open(QFile::WriteOnly); + file.write("test"); + file.close(); +} + +void QzToolsTest::copyRecursivelyTest() +{ + const QString testDir = createPath("copyRecursivelyTest"); + createTestDirectoryStructure(testDir); + + QVERIFY(!QFileInfo(testDir + "-copy").exists()); + + // Copy to non-existant target + QCOMPARE(QzTools::copyRecursively(testDir, testDir + "-copy"), true); + + QCOMPARE(QFileInfo(testDir + "-copy").isDir(), true); + QCOMPARE(QFileInfo(testDir + "-copy/dir1").isDir(), true); + QCOMPARE(QFileInfo(testDir + "-copy/dir2").isDir(), true); + QCOMPARE(QFileInfo(testDir + "-copy/dir3").isDir(), true); + QCOMPARE(QFileInfo(testDir + "-copy/dir1/dir1_1").isDir(), true); + QCOMPARE(QFileInfo(testDir + "-copy/dir1/dir1_2").isDir(), true); + QCOMPARE(QFileInfo(testDir + "-copy/dir1/dir1_3").isDir(), true); + QCOMPARE(QFileInfo(testDir + "-copy/dir3/dir3_1").isDir(), true); + QCOMPARE(QFileInfo(testDir + "-copy/dir1/dir1_2/file1.txt").isFile(), true); + + QFile file(testDir + "-copy/dir1/dir1_2/file1.txt"); + file.open(QFile::ReadOnly); + QCOMPARE(file.readAll(), QByteArray("test")); + + // Copy to target that already exists + QCOMPARE(QzTools::copyRecursively(testDir, testDir + "-copy"), false); + + // Cleanup + QCOMPARE(QzTools::removeRecursively(testDir), true); + QCOMPARE(QzTools::removeRecursively(testDir + "-copy"), true); +} + +void QzToolsTest::removeRecursivelyTest() +{ + const QString testDir = createPath("removeRecursivelyTest"); + createTestDirectoryStructure(testDir); + + QCOMPARE(QzTools::copyRecursively(testDir, testDir + "-copy"), true); + QCOMPARE(QzTools::removeRecursively(testDir + "-copy"), true); + QCOMPARE(QFileInfo(testDir + "-copy").exists(), false); + + // Remove non-existant path returns success + QCOMPARE(QzTools::removeRecursively(testDir + "-copy"), true); + + QCOMPARE(QzTools::copyRecursively(testDir, testDir + "-copy2"), true); + + QFile dir(testDir + "-copy2"); + dir.setPermissions(dir.permissions() & ~(QFile::WriteOwner | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther)); + + QCOMPARE(QzTools::removeRecursively(testDir + "-copy2"), false); + + dir.setPermissions(dir.permissions() | QFile::WriteOwner); + + QCOMPARE(QzTools::removeRecursively(testDir + "-copy2"), true); + + // Cleanup + QCOMPARE(QzTools::removeRecursively(testDir), true); +} + +void QzToolsTest::dontFollowSymlinksTest() +{ + const QString testDir = createPath("removeRecursivelyTest"); + createTestDirectoryStructure(testDir); + + QDir().mkpath(testDir + "/subdir"); + QFile::link(testDir, testDir + "/subdir/link"); + + QVERIFY(QzTools::removeRecursively(testDir + "/subdir")); + + QVERIFY(!QFile::exists(testDir + "/subdir")); + QVERIFY(QFile::exists(testDir)); + + QDir().mkpath(testDir + "/subdir/normalfolder"); + QFile::link("..", testDir + "/subdir/link"); + + QVERIFY(QzTools::copyRecursively(testDir + "/subdir", testDir + "/subdir2")); + + QCOMPARE(QFile::exists(testDir + "/subdir2/link"), true); + QCOMPARE(QFile::exists(testDir + "/subdir2/normalfolder"), true); + + // Cleanup + QCOMPARE(QzTools::removeRecursively(testDir), true); +} + QString QzToolsTest::createPath(const char *file) const { return m_tmpPath + QL1S("/") + file; diff --git a/autotests/qztoolstest.h b/autotests/qztoolstest.h index 479123f1f..92ecd76cb 100644 --- a/autotests/qztoolstest.h +++ b/autotests/qztoolstest.h @@ -41,6 +41,9 @@ private Q_SLOTS: void escapeSqlGlobString(); void ensureUniqueFilename(); + void copyRecursivelyTest(); + void removeRecursivelyTest(); + void dontFollowSymlinksTest(); private: QString createPath(const char *file) const; diff --git a/src/lib/app/mainapplication.cpp b/src/lib/app/mainapplication.cpp index 7ef2081d4..29094c569 100644 --- a/src/lib/app/mainapplication.cpp +++ b/src/lib/app/mainapplication.cpp @@ -787,7 +787,7 @@ void MainApplication::saveSettings() m_cookieJar->deleteAllCookies(); } if (deleteCache) { - QzTools::removeDir(mApp->webProfile()->cachePath()); + QzTools::removeRecursively(mApp->webProfile()->cachePath()); } m_searchEnginesManager->saveSettings(); diff --git a/src/lib/app/profilemanager.cpp b/src/lib/app/profilemanager.cpp index d0499e661..11b1f95cc 100644 --- a/src/lib/app/profilemanager.cpp +++ b/src/lib/app/profilemanager.cpp @@ -115,7 +115,7 @@ bool ProfileManager::removeProfile(const QString &profileName) return false; } - QzTools::removeDir(dir.absolutePath()); + QzTools::removeRecursively(dir.absolutePath()); return true; } diff --git a/src/lib/other/clearprivatedata.cpp b/src/lib/other/clearprivatedata.cpp index ce84be5bc..3df5020d6 100644 --- a/src/lib/other/clearprivatedata.cpp +++ b/src/lib/other/clearprivatedata.cpp @@ -67,22 +67,22 @@ void ClearPrivateData::clearLocalStorage() { const QString profile = DataPaths::currentProfilePath(); - QzTools::removeDir(profile + "/Local Storage"); + QzTools::removeRecursively(profile + "/Local Storage"); } void ClearPrivateData::clearWebDatabases() { const QString profile = DataPaths::currentProfilePath(); - QzTools::removeDir(profile + "/IndexedDB"); - QzTools::removeDir(profile + "/databases"); + QzTools::removeRecursively(profile + "/IndexedDB"); + QzTools::removeRecursively(profile + "/databases"); } void ClearPrivateData::clearCache() { const QString profile = DataPaths::currentProfilePath(); - QzTools::removeDir(profile + "/GPUCache"); + QzTools::removeRecursively(profile + "/GPUCache"); mApp->webProfile()->clearHttpCache(); } diff --git a/src/lib/tools/qztools.cpp b/src/lib/tools/qztools.cpp index f1884ad86..b71242baf 100644 --- a/src/lib/tools/qztools.cpp +++ b/src/lib/tools/qztools.cpp @@ -48,6 +48,8 @@ #ifdef Q_OS_WIN #include +#else +#include #endif #ifdef Q_OS_MACOS @@ -131,35 +133,67 @@ void QzTools::centerWidgetToParent(QWidget* w, QWidget* parent) w->move(p); } -bool QzTools::removeFile(const QString &fullFileName) +bool QzTools::removeRecursively(const QString &filePath) { - QFile f(fullFileName); - if (f.exists()) { - return f.remove(); + const QFileInfo fileInfo(filePath); + if (!fileInfo.exists() && !fileInfo.isSymLink()) { + return true; } - else { + if (fileInfo.isDir() && !fileInfo.isSymLink()) { + QDir dir(filePath); + dir = dir.canonicalPath(); + if (dir.isRoot() || dir.path() == QDir::home().canonicalPath()) { + qCritical() << "Attempt to remove root/home directory" << dir; + return false; + } + const QStringList fileNames = dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); + for (const QString &fileName : fileNames) { + if (!removeRecursively(filePath + QLatin1Char('/') + fileName)) { + return false; + } + } + if (!QDir::root().rmdir(dir.path())) { + return false; + } + } else if (!QFile::remove(filePath)) { return false; } + return true; } -void QzTools::removeDir(const QString &d) +bool QzTools::copyRecursively(const QString &sourcePath, const QString &targetPath) { - QDir dir(d); - if (dir.exists()) { - const QFileInfoList list = dir.entryInfoList(); - QFileInfo fi; - for (int l = 0; l < list.size(); l++) { - fi = list.at(l); - if (fi.isDir() && fi.fileName() != QLatin1String(".") && fi.fileName() != QLatin1String("..")) { - QzTools::removeDir(fi.absoluteFilePath()); - } - else if (fi.isFile()) { - QzTools::removeFile(fi.absoluteFilePath()); - } - + const QFileInfo srcFileInfo(sourcePath); + if (srcFileInfo.isDir() && !srcFileInfo.isSymLink()) { + QDir targetDir(targetPath); + targetDir.cdUp(); + if (!targetDir.mkdir(QFileInfo(targetPath).fileName())) { + return false; } - dir.rmdir(d); + const QStringList fileNames = QDir(sourcePath).entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); + for (const QString &fileName : fileNames) { + const QString newSourcePath = sourcePath + QL1C('/') + fileName; + const QString newTargetPath = targetPath + QL1C('/') + fileName; + if (!copyRecursively(newSourcePath, newTargetPath)) { + return false; + } + } +#ifndef Q_OS_WIN + } else if (srcFileInfo.isSymLink()) { + const QByteArray pathData = sourcePath.toLocal8Bit(); + char buf[1024]; + ssize_t len = readlink(pathData.constData(), buf, sizeof(buf) - 1); + if (len < 0) { + qWarning() << "Error getting symlink path" << pathData; + return false; + } + buf[len] = '\0'; + return QFile::link(QString::fromLocal8Bit(buf), targetPath); +#endif + } else if (!QFile::copy(sourcePath, targetPath)) { + return false; } + return true; } /* Finds same part of @one in @other from the beginning */ diff --git a/src/lib/tools/qztools.h b/src/lib/tools/qztools.h index 59de30428..47d4b6318 100644 --- a/src/lib/tools/qztools.h +++ b/src/lib/tools/qztools.h @@ -43,8 +43,8 @@ public: static void centerWidgetOnScreen(QWidget* w); static void centerWidgetToParent(QWidget* w, QWidget* parent); - static bool removeFile(const QString &fullFileName); - static void removeDir(const QString &d); + static bool removeRecursively(const QString &filePath); + static bool copyRecursively(const QString &sourcePath, const QString &targetPath); static QString samePartOfStrings(const QString &one, const QString &other); static QString urlEncodeQueryString(const QUrl &url);