Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qstandardpaths_unix.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// Copyright (C) 2020 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qstandardpaths.h"
6#include <qdir.h>
7#include <qfile.h>
8#include <qhash.h>
9#include <qtextstream.h>
10#if QT_CONFIG(regularexpression)
11#include <qregularexpression.h>
12#endif
13#include <private/qfilesystemengine_p.h>
14#include <errno.h>
15#include <stdlib.h>
16
17#ifndef QT_BOOTSTRAPPED
18#include <qcoreapplication.h>
19#endif
20
21#ifndef QT_NO_STANDARDPATHS
22
24
25using namespace Qt::StringLiterals;
26
28{
29#ifndef QT_BOOTSTRAPPED
31 if (!org.isEmpty())
32 path += u'/' + org;
34 if (!appName.isEmpty())
35 path += u'/' + appName;
36#else
38#endif
39}
40
41#if QT_CONFIG(regularexpression)
43{
44 switch (type) {
46 return "DESKTOP"_L1;
48 return "DOCUMENTS"_L1;
50 return "PICTURES"_L1;
52 return "MUSIC"_L1;
54 return "VIDEOS"_L1;
56 return "DOWNLOAD"_L1;
58 return "PUBLICSHARE"_L1;
60 return "TEMPLATES"_L1;
61 default:
62 return {};
63 }
64}
65#endif
66
67static QByteArray unixPermissionsText(QFile::Permissions permissions)
68{
69 mode_t perms = 0;
70 if (permissions & QFile::ReadOwner)
71 perms |= S_IRUSR;
72 if (permissions & QFile::WriteOwner)
73 perms |= S_IWUSR;
74 if (permissions & QFile::ExeOwner)
75 perms |= S_IXUSR;
76 if (permissions & QFile::ReadGroup)
77 perms |= S_IRGRP;
78 if (permissions & QFile::WriteGroup)
79 perms |= S_IWGRP;
80 if (permissions & QFile::ExeGroup)
81 perms |= S_IXGRP;
82 if (permissions & QFile::ReadOther)
83 perms |= S_IROTH;
84 if (permissions & QFile::WriteOther)
85 perms |= S_IWOTH;
86 if (permissions & QFile::ExeOther)
87 perms |= S_IXOTH;
88 return '0' + QByteArray::number(perms, 8);
89}
90
91static bool checkXdgRuntimeDir(const QString &xdgRuntimeDir)
92{
93 auto describeMetaData = [](const QFileSystemMetaData &metaData) -> QByteArray {
94 if (!metaData.exists())
95 return "a broken symlink";
96
97 QByteArray description;
98 if (metaData.isLink())
99 description = "a symbolic link to ";
100
101 if (metaData.isFile())
102 description += "a regular file";
103 else if (metaData.isDirectory())
104 description += "a directory";
105 else if (metaData.isSequential())
106 description += "a character device, socket or FIFO";
107 else
108 description += "a block device";
109
110 description += " permissions " + unixPermissionsText(metaData.permissions());
111
112 return description
113 + " owned by UID " + QByteArray::number(metaData.userId())
114 + " GID " + QByteArray::number(metaData.groupId());
115 };
116
117 // http://standards.freedesktop.org/basedir-spec/latest/
118 const uint myUid = uint(geteuid());
119 const QFile::Permissions wantedPerms = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
120 const QFileSystemMetaData::MetaDataFlags statFlags = QFileSystemMetaData::PosixStatFlags
122 QFileSystemMetaData metaData;
123 QFileSystemEntry entry(xdgRuntimeDir);
124
125 // Check that the xdgRuntimeDir is a directory by attempting to create it.
126 // A stat() before mkdir() that concluded it doesn't exist is a meaningless
127 // result: we'd race against someone else attempting to create it.
128 // ### QFileSystemEngine::createDirectory cannot take the extra mode argument.
129 if (QT_MKDIR(entry.nativeFilePath(), 0700) == 0)
130 return true;
131 if (errno != EEXIST) {
132 qErrnoWarning("QStandardPaths: error creating runtime directory '%ls'",
133 qUtf16Printable(xdgRuntimeDir));
134 return false;
135 }
136
137 // We use LinkType to force an lstat(), but fillMetaData() still returns error
138 // on broken symlinks.
139 if (!QFileSystemEngine::fillMetaData(entry, metaData, statFlags) && !metaData.isLink()) {
140 qErrnoWarning("QStandardPaths: error obtaining permissions of runtime directory '%ls'",
141 qUtf16Printable(xdgRuntimeDir));
142 return false;
143 }
144
145 // Checks:
146 // - is a directory
147 // - is not a symlink (even is pointing to a directory)
148 if (metaData.isLink() || !metaData.isDirectory()) {
149 qWarning("QStandardPaths: runtime directory '%ls' is not a directory, but %s",
150 qUtf16Printable(xdgRuntimeDir), describeMetaData(metaData).constData());
151 return false;
152 }
153
154 // - "The directory MUST be owned by the user"
155 if (metaData.userId() != myUid) {
156 qWarning("QStandardPaths: runtime directory '%ls' is not owned by UID %d, but %s",
157 qUtf16Printable(xdgRuntimeDir), myUid, describeMetaData(metaData).constData());
158 return false;
159 }
160
161 // "and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700."
162 if (metaData.permissions() != wantedPerms) {
163 qWarning("QStandardPaths: wrong permissions on runtime directory %ls, %s instead of %s",
164 qUtf16Printable(xdgRuntimeDir),
166 unixPermissionsText(wantedPerms).constData());
167 return false;
168 }
169
170 return true;
171}
172
174{
175 switch (type) {
176 case HomeLocation:
177 return QDir::homePath();
178 case TempLocation:
179 return QDir::tempPath();
180 case CacheLocation:
182 {
183 QString xdgCacheHome;
184 if (isTestModeEnabled()) {
185 xdgCacheHome = QDir::homePath() + "/.qttest/cache"_L1;
186 } else {
187 // http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
188 xdgCacheHome = QFile::decodeName(qgetenv("XDG_CACHE_HOME"));
189 if (!xdgCacheHome.startsWith(u'/'))
190 xdgCacheHome.clear(); // spec says relative paths should be ignored
191
192 if (xdgCacheHome.isEmpty())
193 xdgCacheHome = QDir::homePath() + "/.cache"_L1;
194 }
196 appendOrganizationAndApp(xdgCacheHome);
197 return xdgCacheHome;
198 }
199 case AppDataLocation:
202 {
203 QString xdgDataHome;
204 if (isTestModeEnabled()) {
205 xdgDataHome = QDir::homePath() + "/.qttest/share"_L1;
206 } else {
207 xdgDataHome = QFile::decodeName(qgetenv("XDG_DATA_HOME"));
208 if (!xdgDataHome.startsWith(u'/'))
209 xdgDataHome.clear(); // spec says relative paths should be ignored
210
211 if (xdgDataHome.isEmpty())
212 xdgDataHome = QDir::homePath() + "/.local/share"_L1;
213 }
215 appendOrganizationAndApp(xdgDataHome);
216 return xdgDataHome;
217 }
218 case ConfigLocation:
221 {
222 QString xdgConfigHome;
223 if (isTestModeEnabled()) {
224 xdgConfigHome = QDir::homePath() + "/.qttest/config"_L1;
225 } else {
226 // http://standards.freedesktop.org/basedir-spec/latest/
227 xdgConfigHome = QFile::decodeName(qgetenv("XDG_CONFIG_HOME"));
228 if (!xdgConfigHome.startsWith(u'/'))
229 xdgConfigHome.clear(); // spec says relative paths should be ignored
230
231 if (xdgConfigHome.isEmpty())
232 xdgConfigHome = QDir::homePath() + "/.config"_L1;
233 }
234 if (type == AppConfigLocation)
235 appendOrganizationAndApp(xdgConfigHome);
236 return xdgConfigHome;
237 }
238 case RuntimeLocation:
239 {
240 QString xdgRuntimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR"));
241 if (!xdgRuntimeDir.startsWith(u'/'))
242 xdgRuntimeDir.clear(); // spec says relative paths should be ignored
243
244 bool fromEnv = !xdgRuntimeDir.isEmpty();
245 if (xdgRuntimeDir.isEmpty() || !checkXdgRuntimeDir(xdgRuntimeDir)) {
246 // environment variable not set or is set to something unsuitable
247 const uint myUid = uint(geteuid());
248 const QString userName = QFileSystemEngine::resolveUserName(myUid);
249 xdgRuntimeDir = QDir::tempPath() + "/runtime-"_L1 + userName;
250
251 if (!fromEnv) {
252#ifndef Q_OS_WASM
253 qWarning("QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '%ls'", qUtf16Printable(xdgRuntimeDir));
254#endif
255 }
256
257 if (!checkXdgRuntimeDir(xdgRuntimeDir))
258 xdgRuntimeDir.clear();
259 }
260
261 return xdgRuntimeDir;
262 }
263 default:
264 break;
265 }
266
267#if QT_CONFIG(regularexpression)
268 // http://www.freedesktop.org/wiki/Software/xdg-user-dirs
269 QString xdgConfigHome = QFile::decodeName(qgetenv("XDG_CONFIG_HOME"));
270 if (!xdgConfigHome.startsWith(u'/'))
271 xdgConfigHome.clear(); // spec says relative paths should be ignored
272
273 if (xdgConfigHome.isEmpty())
274 xdgConfigHome = QDir::homePath() + "/.config"_L1;
275 QFile file(xdgConfigHome + "/user-dirs.dirs"_L1);
276 const QLatin1StringView key = xdg_key_name(type);
277 if (!key.isEmpty() && !isTestModeEnabled() && file.open(QIODevice::ReadOnly)) {
279 // Only look for lines like: XDG_DESKTOP_DIR="$HOME/Desktop"
280 static const QRegularExpression exp(u"^XDG_(.*)_DIR=(.*)$"_s);
282 while (!stream.atEnd()) {
283 const QString &line = stream.readLine();
285 if (match.hasMatch() && match.capturedView(1) == key) {
286 QStringView value = match.capturedView(2);
287 if (value.size() > 2
288 && value.startsWith(u'\"')
289 && value.endsWith(u'\"'))
290 value = value.mid(1, value.size() - 2);
291 // value can start with $HOME
292 if (value.startsWith("$HOME"_L1))
294 else
295 result = value.toString();
296 if (result.size() > 1 && result.endsWith(u'/'))
297 result.chop(1);
298 }
299 }
300 if (!result.isNull())
301 return result;
302 }
303#endif // QT_CONFIG(regularexpression)
304
306 switch (type) {
307 case DesktopLocation:
308 path = QDir::homePath() + "/Desktop"_L1;
309 break;
311 path = QDir::homePath() + "/Documents"_L1;
312 break;
313 case PicturesLocation:
314 path = QDir::homePath() + "/Pictures"_L1;
315 break;
316
317 case FontsLocation:
319 break;
320
321 case MusicLocation:
322 path = QDir::homePath() + "/Music"_L1;
323 break;
324
325 case MoviesLocation:
326 path = QDir::homePath() + "/Videos"_L1;
327 break;
328 case DownloadLocation:
329 path = QDir::homePath() + "/Downloads"_L1;
330 break;
332 path = writableLocation(GenericDataLocation) + "/applications"_L1;
333 break;
334
336 path = QDir::homePath() + "/Public"_L1;
337 break;
338
340 path = QDir::homePath() + "/Templates"_L1;
341 break;
342
343 default:
344 break;
345 }
346
347 return path;
348}
349
350static QStringList dirsList(const QString &xdgEnvVar)
351{
352 QStringList dirs;
353 // http://standards.freedesktop.org/basedir-spec/latest/
354 // Normalize paths, skip relative paths (the spec says relative paths
355 // should be ignored)
356 for (const auto dir : qTokenize(xdgEnvVar, u':'))
357 if (dir.startsWith(u'/'))
358 dirs.push_back(QDir::cleanPath(dir.toString()));
359
360 // Remove duplicates from the list, there's no use for duplicated paths
361 // in XDG_* env vars - if whatever is being looked for is not found in
362 // the given directory the first time, it won't be there the second time.
363 // Plus duplicate paths causes problems for example for mimetypes,
364 // where duplicate paths here lead to duplicated mime types returned
365 // for a file, eg "text/plain,text/plain" instead of "text/plain"
366 dirs.removeDuplicates();
367
368 return dirs;
369}
370
372{
373 // http://standards.freedesktop.org/basedir-spec/latest/
374 QString xdgDataDirsEnv = QFile::decodeName(qgetenv("XDG_DATA_DIRS"));
375
376 QStringList dirs = dirsList(xdgDataDirsEnv);
377 if (dirs.isEmpty())
378 dirs = QStringList{u"/usr/local/share"_s, u"/usr/share"_s};
379
380 return dirs;
381}
382
384{
385 // http://standards.freedesktop.org/basedir-spec/latest/
386 const QString xdgConfigDirs = QFile::decodeName(qgetenv("XDG_CONFIG_DIRS"));
387
389 if (dirs.isEmpty())
390 dirs.push_back(u"/etc/xdg"_s);
391
392 return dirs;
393}
394
396{
397 QStringList dirs;
398 switch (type) {
399 case ConfigLocation:
401 dirs = xdgConfigDirs();
402 break;
404 dirs = xdgConfigDirs();
405 for (int i = 0; i < dirs.size(); ++i)
407 break;
409 dirs = xdgDataDirs();
410 break;
412 dirs = xdgDataDirs();
413 for (int i = 0; i < dirs.size(); ++i)
414 dirs[i].append("/applications"_L1);
415 break;
416 case AppDataLocation:
418 dirs = xdgDataDirs();
419 for (int i = 0; i < dirs.size(); ++i)
421 break;
422 case FontsLocation:
423 dirs += QDir::homePath() + "/.fonts"_L1;
424 dirs += xdgDataDirs();
425 for (int i = 1; i < dirs.size(); ++i)
426 dirs[i].append("/fonts"_L1);
427 break;
428 default:
429 break;
430 }
431 const QString localDir = writableLocation(type);
432 dirs.prepend(localDir);
433 return dirs;
434}
435
437
438#endif // QT_NO_STANDARDPATHS
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
QString organizationName
the name of the organization that wrote this application
QString applicationName
the name of this application
static QString tempPath()
Returns the absolute canonical path of the system's temporary directory.
Definition qdir.cpp:2130
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2395
static QString homePath()
Returns the absolute path of the user's home directory.
Definition qdir.cpp:2100
static bool fillMetaData(const QFileSystemEntry &entry, QFileSystemMetaData &data, QFileSystemMetaData::MetaDataFlags what)
static QString resolveUserName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
QFile::Permissions permissions() const
uint userId() const
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
static QString decodeName(const QByteArray &localFileName)
This does the reverse of QFile::encodeName() using localFileName.
Definition qfile.h:162
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
static bool isTestModeEnabled()
static QStringList standardLocations(StandardLocation type)
static QString writableLocation(StandardLocation type)
StandardLocation
This enum describes the different locations that can be queried using methods such as QStandardPaths:...
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6180
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
\inmodule QtCore
list append(new Employee("Blackpool", "Stephen"))
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
EGLStreamKHR stream
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:162
GLuint64 key
GLenum type
GLuint entry
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
static void appendOrganizationAndApp(QString &path)
static QByteArray unixPermissionsText(QFile::Permissions permissions)
static QStringList dirsList(const QString &xdgEnvVar)
static bool checkXdgRuntimeDir(const QString &xdgRuntimeDir)
static QStringList xdgConfigDirs()
static void appendOrganizationAndApp(QString &path)
static QStringList xdgDataDirs()
#define qUtf16Printable(string)
Definition qstring.h:1403
constexpr auto qTokenize(Haystack &&h, Needle &&n, Flags...flags) noexcept(QtPrivate::Tok::is_nothrow_constructible_from< Haystack, Needle >::value) -> decltype(QtPrivate::Tok::TokenizerResult< Haystack, Needle >{std::forward< Haystack >(h), std::forward< Needle >(n), flags...})
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
#define Q_UNUSED(x)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
unsigned int uint
Definition qtypes.h:29
QFile file
[0]
QString dir
[11]