diff options
| -rw-r--r-- | src/corelib/io/qfilesystemengine_unix.cpp | 24 | ||||
| -rw-r--r-- | tests/auto/corelib/io/qfile/tst_qfile.cpp | 48 |
2 files changed, 61 insertions, 11 deletions
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp index a5e2f47b485..a54c96a711d 100644 --- a/src/corelib/io/qfilesystemengine_unix.cpp +++ b/src/corelib/io/qfilesystemengine_unix.cpp @@ -1439,17 +1439,17 @@ struct FreeDesktopTrashOperation } // opens a directory and returns the file descriptor - static int openDirFd(int dfd, const char *path, int mode = 0) + static int openDirFd(int dfd, const char *path, int mode) { - mode |= QT_OPEN_RDONLY | O_NOFOLLOW | O_DIRECTORY; + mode |= QT_OPEN_RDONLY | O_DIRECTORY; return qt_safe_openat(dfd, path, mode); } // opens an XDG Trash directory that is a subdirectory of dfd, creating if necessary - static int openOrCreateDir(int dfd, const char *path) + static int openOrCreateDir(int dfd, const char *path, int openmode = 0) { // try to open it as a dir, first - int fd = openDirFd(dfd, path); + int fd = openDirFd(dfd, path, openmode); if (fd >= 0 || errno != ENOENT) return fd; @@ -1458,19 +1458,19 @@ struct FreeDesktopTrashOperation return -1; // try to open it again - return openDirFd(dfd, path); + return openDirFd(dfd, path, openmode); } // opens or makes the XDG Trash hierarchy on parentfd (may be -1) called targetDir bool getTrashDir(int parentfd, QString targetDir, const QFileSystemEntry &source, - QSystemError &error) + int openmode, QSystemError &error) { if (parentfd == AT_FDCWD) trashPath = targetDir; QByteArray nativePath = QFile::encodeName(targetDir); // open the directory - int trashfd = openOrCreateDir(parentfd, nativePath); + int trashfd = openOrCreateDir(parentfd, nativePath, openmode); if (trashfd < 0 && errno != ENOENT) { error = QSystemError(errno, QSystemError::StandardLibraryError); return false; @@ -1535,7 +1535,7 @@ struct FreeDesktopTrashOperation QFileSystemEntry dotTrashDir(sourceStorage.rootPath() + dotTrash); // we MUST check that the sticky bit is set, and that it is not a symlink - int genericTrashFd = openDirFd(AT_FDCWD, dotTrashDir.nativeFilePath()); + int genericTrashFd = openDirFd(AT_FDCWD, dotTrashDir.nativeFilePath(), O_NOFOLLOW); QT_STATBUF st = {}; if (genericTrashFd < 0 && errno != ENOENT && errno != EACCES) { // O_DIRECTORY + O_NOFOLLOW produces ENOTDIR on Linux @@ -1563,7 +1563,7 @@ struct FreeDesktopTrashOperation the implementation MUST immediately create it, without any warnings or delays for the user." */ - if (getTrashDir(genericTrashFd, userID, source, error)) { + if (getTrashDir(genericTrashFd, userID, source, O_NOFOLLOW, error)) { // recreate the resulting path trashPath = dotTrashDir.filePath() + u'/' + userID; } @@ -1579,7 +1579,8 @@ struct FreeDesktopTrashOperation immediately create it, without any warnings or delays for the user." */ if (!isTrashDirOpen()) - getTrashDir(AT_FDCWD, sourceStorage.rootPath() + dotTrash + u'-' + userID, source, error); + getTrashDir(AT_FDCWD, sourceStorage.rootPath() + dotTrash + u'-' + userID, source, + O_NOFOLLOW, error); if (isTrashDirOpen()) { volumePrefixLength = sourceStorage.rootPath().size(); @@ -1594,7 +1595,8 @@ struct FreeDesktopTrashOperation bool openHomeTrashLocation(const QFileSystemEntry &source, QSystemError &error) { QString topDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); - return getTrashDir(AT_FDCWD, topDir + "/Trash"_L1, source, error); + int openmode = 0; // do allow following symlinks + return getTrashDir(AT_FDCWD, topDir + "/Trash"_L1, source, openmode, error); } bool findTrashFor(const QFileSystemEntry &source, QSystemError &error) diff --git a/tests/auto/corelib/io/qfile/tst_qfile.cpp b/tests/auto/corelib/io/qfile/tst_qfile.cpp index 843b1294501..882fce81db5 100644 --- a/tests/auto/corelib/io/qfile/tst_qfile.cpp +++ b/tests/auto/corelib/io/qfile/tst_qfile.cpp @@ -301,6 +301,7 @@ private slots: void moveToTrashSymlinkToFile(); void moveToTrashSymlinkToDirectory_data(); void moveToTrashSymlinkToDirectory(); + void moveToTrashXdgHomeTrashIsSymlink(); void moveToTrashXdgSafety(); void stdfilesystem(); @@ -4409,6 +4410,53 @@ void tst_QFile::moveToTrashSymlinkToDirectory() cleanLink.dismiss(); } +void tst_QFile::moveToTrashXdgHomeTrashIsSymlink() +{ + if (!QFile::supportsMoveToTrash()) + QSKIP("This platform doesn't implement a trash bin"); + +#if defined(Q_OS_WIN) || defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID) || defined(Q_OS_WEBOS) + QSKIP("This test is specific to XDG Unix systems"); +#else + if (!QStandardPaths::isTestModeEnabled()) + QFAIL("Constructor should have enabled test mode"); + + QString xdgDataHome = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); + Q_ASSERT(xdgDataHome.contains("qttest/")); + QString xdgHomeTrash = xdgDataHome + "/Trash"_L1; + QString tempPattern = xdgDataHome + "/tst_qfile_moveToTrashXdgHomeTrashIsSymlink.XXXXXX"; + auto removeTrashAsSymlink = [&xdgHomeTrash] { + QFile::remove(xdgHomeTrash); + }; + + // create a file for us to trash + QTemporaryFile fileToTrash(tempPattern); + QVERIFY2(fileToTrash.open(), qPrintable(fileToTrash.errorString())); + + // obliterate the test-mode home trash and create a symlink in its place + if (QFileInfo fi(xdgHomeTrash); fi.isSymLink() || !fi.isDir()) + removeTrashAsSymlink(); + else + QDir(xdgHomeTrash).removeRecursively(); + + QTemporaryDir otherTrash(tempPattern); + QVERIFY2(otherTrash.isValid(), qPrintable(otherTrash.errorString())); + if (QFile src(otherTrash.path()); true) + QVERIFY2(src.link(xdgHomeTrash), qPrintable(src.errorString())); + auto deleteSymlink = qScopeGuard(removeTrashAsSymlink); + + // we should be able to trash an open file in XDG platforms (test above) + + QFile f(fileToTrash.fileName()); + QVERIFY2(f.moveToTrash(), qPrintable(f.errorString())); + QVERIFY(f.exists()); + + QVERIFY(!QFileInfo(fileToTrash.fileName()).exists()); + QVERIFY(QFile(otherTrash.filePath("files")).exists()); + QVERIFY(QFile(otherTrash.filePath("info")).exists()); +#endif +} + void tst_QFile::moveToTrashXdgSafety() { if (!QFile::supportsMoveToTrash()) |
