diff options
| author | Thiago Macieira <thiago.macieira@intel.com> | 2025-08-06 08:59:10 -0700 |
|---|---|---|
| committer | Thiago Macieira <thiago.macieira@intel.com> | 2025-08-26 13:17:17 -0700 |
| commit | c86a33db043cb20ceea0ee752dec9548c3a85259 (patch) | |
| tree | 890d623a0cac782b76f230555ddc72ad413527c7 /src/corelib/io | |
| parent | 2e3afe3f02b2cdbee1615ad7ef47ef47a0506804 (diff) | |
moveToTrash/XDG: allow $XDG_DATA_HOME/Trash to be a symlink
This is in the user's $HOME dir and thus entirely controlled by the
user, so we don't need to apply the strict protections that the XDG spec
requires of other dirs. The trash spec[1] being one long section without
structures made the reading ambiguous whether the checks should also be
applied to $HOME.
This commit also allows the "info" and "files" subdirs to be symlinks,
but I'm not testing that.
[ChangeLog][QtCore][QFile] Fixed a bug that caused moveToTrash() to
disallow trashing to trash bins using the XDG Trash Specification when
the trash path was a symlink.
[1] https://specifications.freedesktop.org/trash-spec/1.0/
Pick-to: 6.8 6.9 6.10
Fixes: QTBUG-138984
Change-Id: I4f05391082bc607389e0fffd06a9b72fdd098f22
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: David Faure <david.faure@kdab.com>
Diffstat (limited to 'src/corelib/io')
| -rw-r--r-- | src/corelib/io/qfilesystemengine_unix.cpp | 24 |
1 files changed, 13 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) |
