summaryrefslogtreecommitdiffstats
path: root/src/corelib/io
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2025-08-06 08:59:10 -0700
committerThiago Macieira <thiago.macieira@intel.com>2025-08-26 13:17:17 -0700
commitc86a33db043cb20ceea0ee752dec9548c3a85259 (patch)
tree890d623a0cac782b76f230555ddc72ad413527c7 /src/corelib/io
parent2e3afe3f02b2cdbee1615ad7ef47ef47a0506804 (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.cpp24
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)