diff --git a/src/corelib/io/qfilesystemengine_mac.mm b/src/corelib/io/qfilesystemengine_mac.mm index 4bbce9e5f6..258ae2e8e0 100644 --- a/src/corelib/io/qfilesystemengine_mac.mm +++ b/src/corelib/io/qfilesystemengine_mac.mm @@ -60,20 +60,24 @@ QT_BEGIN_NAMESPACE bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source, QFileSystemEntry &newLocation, QSystemError &error) { +#ifdef Q_OS_MACOS // desktop macOS has a trash can + QMacAutoReleasePool pool; + QFileInfo info(source.filePath()); - @autoreleasepool { - NSString *filepath = info.filePath().toNSString(); - NSURL *fileurl = [NSURL fileURLWithPath:filepath isDirectory:info.isDir()]; - NSURL *resultingUrl = nil; - NSError *nserror = nil; - NSFileManager *fm = [NSFileManager defaultManager]; - if ([fm trashItemAtURL:fileurl resultingItemURL:&resultingUrl error:&nserror] != YES) { - error = QSystemError(nserror.code, QSystemError::NativeError); - return false; - } - newLocation = QFileSystemEntry(QUrl::fromNSURL(resultingUrl).path()); + NSString *filepath = info.filePath().toNSString(); + NSURL *fileurl = [NSURL fileURLWithPath:filepath isDirectory:info.isDir()]; + NSURL *resultingUrl = nil; + NSError *nserror = nil; + NSFileManager *fm = [NSFileManager defaultManager]; + if ([fm trashItemAtURL:fileurl resultingItemURL:&resultingUrl error:&nserror] != YES) { + error = QSystemError(nserror.code, QSystemError::NativeError); + return false; } + newLocation = QFileSystemEntry(QUrl::fromNSURL(resultingUrl).path()); return true; +#else // watch, tv, iOS don't have a trash can + return false; +#endif } QT_END_NAMESPACE diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp index 16901be187..eed34086b4 100644 --- a/src/corelib/io/qfilesystemengine_unix.cpp +++ b/src/corelib/io/qfilesystemengine_unix.cpp @@ -1341,13 +1341,15 @@ bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source, QString infoFileName; int counter = 0; QFile infoFile; - do { - while (QFile::exists(trashDir.filePath(filesDir) + uniqueTrashedName)) { - ++counter; - uniqueTrashedName = QString(QLatin1String("/%1-%2")) + auto makeUniqueTrashedName = [trashedName, &counter]() -> QString { + ++counter; + return QString(QLatin1String("/%1-%2")) .arg(trashedName) .arg(counter, 4, 10, QLatin1Char('0')); - } + }; + do { + while (QFile::exists(trashDir.filePath(filesDir) + uniqueTrashedName)) + uniqueTrashedName = makeUniqueTrashedName(); /* "The $trash/info directory contains an "information file" for every file and directory in $trash/files. This file MUST have exactly the same name as the file or directory in @@ -1363,15 +1365,21 @@ bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source, infoFileName = trashDir.filePath(infoDir) + uniqueTrashedName + QLatin1String(".trashinfo"); infoFile.setFileName(infoFileName); - } while (!infoFile.open(QIODevice::NewOnly | QIODevice::WriteOnly | QIODevice::Text)); + if (!infoFile.open(QIODevice::NewOnly | QIODevice::WriteOnly | QIODevice::Text)) + uniqueTrashedName = makeUniqueTrashedName(); + } while (!infoFile.isOpen()); const QString targetPath = trashDir.filePath(filesDir) + uniqueTrashedName; const QFileSystemEntry target(targetPath); + /* + We might fail to rename if source and target are on different file systems. + In that case, we don't try further, i.e. copying and removing the original + is usually not what the user would expect to happen. + */ if (!renameFile(source, target, error)) { infoFile.close(); infoFile.remove(); - error = QSystemError(errno, QSystemError::StandardLibraryError); return false; } diff --git a/tests/auto/corelib/io/qfilesystemengine/qfilesystemengine.pro b/tests/auto/corelib/io/qfilesystemengine/qfilesystemengine.pro deleted file mode 100644 index ed59b48d5e..0000000000 --- a/tests/auto/corelib/io/qfilesystemengine/qfilesystemengine.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qfilesystemengine -QT = core-private testlib -SOURCES = tst_qfilesystemengine.cpp \ - $$QT_SOURCE_TREE/src/corelib/io/qfilesystementry.cpp -HEADERS = $$QT_SOURCE_TREE/src/corelib/io/qfilesystementry_p.h diff --git a/tests/auto/corelib/io/qfilesystemengine/tst_qfilesystemengine.cpp b/tests/auto/corelib/io/qfilesystemengine/tst_qfilesystemengine.cpp deleted file mode 100644 index b64852057a..0000000000 --- a/tests/auto/corelib/io/qfilesystemengine/tst_qfilesystemengine.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include - -#include -#include - -class tst_QFileSystemEngine : public QObject -{ - Q_OBJECT - -private slots: - void cleanupTestCase(); - void moveToTrash_data(); - void moveToTrash(); - -private: - QStringList createdEntries; -}; - -void tst_QFileSystemEngine::cleanupTestCase() -{ - for (QString entry : createdEntries) { - QFileInfo entryInfo(entry); - if (!entryInfo.exists()) - continue; - QDir entryDir(entry); - if (entryInfo.isDir()) { - if (!entryDir.removeRecursively()) - qWarning("Failed to remove trashed dir '%s'", entry.toLocal8Bit().constData()); - } else if (!QFile::remove(entry)) { - qWarning("Failed to remove trashed file '%s'", entry.toLocal8Bit().constData()); - } - } -} - -void tst_QFileSystemEngine::moveToTrash_data() -{ - QTest::addColumn("filePath"); - QTest::addColumn("create"); - QTest::addColumn("success"); - - { - QTemporaryFile tempFile; - tempFile.open(); - QTest::newRow("temporary file") - << tempFile.fileName() - << true << true; - } - { - QTemporaryDir tempDir; - tempDir.setAutoRemove(false); - QTest::newRow("temporary dir") - << tempDir.path() + QLatin1Char('/') - << true << true; - } - { - QTemporaryDir homeDir(QFileSystemEngine::homePath() + QLatin1String("/XXXXXX")); - homeDir.setAutoRemove(false); - QTemporaryFile homeFile(homeDir.path() - + QLatin1String("/tst_qfilesystemengine-XXXXXX")); - homeFile.open(); - QTest::newRow("home file") - << homeFile.fileName() - << true << true; - - QTest::newRow("home dir") - << homeDir.path() + QLatin1Char('/') - << true << true; - } - - QTest::newRow("unmovable") - << QFileSystemEngine::rootPath() - << false << false; - QTest::newRow("no such file") - << QString::fromLatin1("no/such/file") - << false << false; -} - -void tst_QFileSystemEngine::moveToTrash() -{ - QFETCH(QString, filePath); - QFETCH(bool, create); - QFETCH(bool, success); - -#if defined(Q_OS_WINRT) - QSKIP("WinRT does not have a trash", SkipAll); -#endif - - if (create && !QFileInfo::exists(filePath)) { - createdEntries << filePath; - if (filePath.endsWith(QLatin1Char('/'))) { - QDir temp(QFileSystemEngine::rootPath()); - temp.mkdir(filePath); - QFile file(filePath + QLatin1String("test")); - if (!file.open(QIODevice::WriteOnly)) - QSKIP("Couldn't create directory with file"); - } else { - QFile file(filePath); - if (!file.open(QIODevice::WriteOnly)) - QSKIP("Couldn't open file for writing"); - } - QVERIFY(QFileInfo::exists(filePath)); - } - - QFileSystemEntry entry(filePath); - QFileSystemEntry newLocation; - QSystemError error; - - bool existed = QFileInfo::exists(filePath); - bool result = QFileSystemEngine::moveFileToTrash(entry, newLocation, error); - QCOMPARE(result, success); - if (result) { - QCOMPARE(error.error(), 0); - QVERIFY(existed != QFileInfo::exists(filePath)); - const QString newPath = newLocation.filePath(); -#if defined(Q_OS_WIN) - // one of the Windows code paths doesn't provide the location of the object in the trash - if (newPath.isEmpty()) - QEXPECT_FAIL("", "Qt built without IFileOperations support on Windows!", Continue); -#endif - QVERIFY(!newPath.isEmpty()); - if (!newPath.isEmpty()) { - createdEntries << newPath; - QFileInfo trashInfo(newPath); - QVERIFY(trashInfo.exists()); -#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) - QString infoFile = trashInfo.absolutePath() + QLatin1String("/../info/") - + trashInfo.fileName() + QLatin1String(".trashinfo"); - createdEntries << infoFile; -#endif - } - } else { - QVERIFY(error.error() != 0); - QCOMPARE(existed, QFileInfo::exists(filePath)); - } -} - -QTEST_MAIN(tst_QFileSystemEngine) -#include