QStandardPaths: Correct handling for XDG_RUNTIME_DIR

Always try to create the runtime directory and never change
the permissions of an existing directory. Conform to the
XDG Base Directory Specification:

"If, when attempting to write a file, the destination directory
is non-existent an attempt should be made to create it with
permission 0700. If the destination directory exists already
the permissions should not be changed."

Fixes: QTBUG-68338
Change-Id: Iaf854d69225fc46e43abae86232d749e5c247df0
Reviewed-by: David Faure <david.faure@kdab.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Samuli Piippo 2019-03-20 15:39:59 +02:00
parent 2e12825b0b
commit 55427858e3
2 changed files with 48 additions and 37 deletions

View File

@ -120,54 +120,59 @@ QString QStandardPaths::writableLocation(StandardLocation type)
} }
case RuntimeLocation: case RuntimeLocation:
{ {
const uint myUid = uint(geteuid());
// http://standards.freedesktop.org/basedir-spec/latest/ // http://standards.freedesktop.org/basedir-spec/latest/
const uint myUid = uint(geteuid());
// since the current user is the owner, set both xxxUser and xxxOwner
const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
| QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
QFileInfo fileInfo; QFileInfo fileInfo;
QString xdgRuntimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR")); QString xdgRuntimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR"));
if (xdgRuntimeDir.isEmpty()) { if (xdgRuntimeDir.isEmpty()) {
const QString userName = QFileSystemEngine::resolveUserName(myUid); const QString userName = QFileSystemEngine::resolveUserName(myUid);
xdgRuntimeDir = QDir::tempPath() + QLatin1String("/runtime-") + userName; xdgRuntimeDir = QDir::tempPath() + QLatin1String("/runtime-") + userName;
fileInfo.setFile(xdgRuntimeDir); fileInfo.setFile(xdgRuntimeDir);
if (!fileInfo.isDir()) {
if (!QDir().mkdir(xdgRuntimeDir)) {
qWarning("QStandardPaths: error creating runtime directory %s: %s", qPrintable(xdgRuntimeDir), qPrintable(qt_error_string(errno)));
return QString();
}
}
#ifndef Q_OS_WASM #ifndef Q_OS_WASM
qWarning("QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '%s'", qPrintable(xdgRuntimeDir)); qWarning("QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '%s'", qPrintable(xdgRuntimeDir));
#endif #endif
} else { } else {
fileInfo.setFile(xdgRuntimeDir); fileInfo.setFile(xdgRuntimeDir);
if (!fileInfo.exists()) { }
qWarning("QStandardPaths: XDG_RUNTIME_DIR points to non-existing path '%s', " if (fileInfo.exists()) {
"please create it with 0700 permissions.", qPrintable(xdgRuntimeDir));
return QString();
}
if (!fileInfo.isDir()) { if (!fileInfo.isDir()) {
qWarning("QStandardPaths: XDG_RUNTIME_DIR points to '%s' which is not a directory", qWarning("QStandardPaths: XDG_RUNTIME_DIR points to '%s' which is not a directory",
qPrintable(xdgRuntimeDir)); qPrintable(xdgRuntimeDir));
return QString(); return QString();
} }
} else {
QFileSystemEntry entry(xdgRuntimeDir);
if (!QFileSystemEngine::createDirectory(entry, false)) {
if (errno != EEXIST) {
qWarning("QStandardPaths: error creating runtime directory %s: %s",
qPrintable(xdgRuntimeDir), qPrintable(qt_error_string(errno)));
return QString();
}
} else {
QSystemError error;
if (!QFileSystemEngine::setPermissions(entry, wantedPerms, error)) {
qWarning("QStandardPaths: could not set correct permissions on runtime directory %s: %s",
qPrintable(xdgRuntimeDir), qPrintable(error.toString()));
return QString();
}
}
} }
// "The directory MUST be owned by the user" // "The directory MUST be owned by the user"
if (fileInfo.ownerId() != myUid) { if (fileInfo.ownerId() != myUid) {
qWarning("QStandardPaths: wrong ownership on runtime directory %s, %d instead of %d", qPrintable(xdgRuntimeDir), qWarning("QStandardPaths: wrong ownership on runtime directory %s, %d instead of %d",
fileInfo.ownerId(), myUid); qPrintable(xdgRuntimeDir), fileInfo.ownerId(), myUid);
return QString(); return QString();
} }
// "and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700." // "and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700."
// since the current user is the owner, set both xxxUser and xxxOwner
const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
| QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
if (fileInfo.permissions() != wantedPerms) { if (fileInfo.permissions() != wantedPerms) {
QFile file(xdgRuntimeDir); qWarning("QStandardPaths: wrong permissions on runtime directory %s, %x instead of %x",
if (!file.setPermissions(wantedPerms)) { qPrintable(xdgRuntimeDir), uint(fileInfo.permissions()), uint(wantedPerms));
qWarning("QStandardPaths: could not set correct permissions on runtime directory %s: %s", return QString();
qPrintable(xdgRuntimeDir), qPrintable(file.errorString()));
return QString();
}
} }
return xdgRuntimeDir; return xdgRuntimeDir;
} }
default: default:

View File

@ -465,16 +465,6 @@ void tst_qstandardpaths::testRuntimeDirectory()
#ifdef Q_XDG_PLATFORM #ifdef Q_XDG_PLATFORM
const QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); const QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
QVERIFY(!runtimeDir.isEmpty()); QVERIFY(!runtimeDir.isEmpty());
// Check that it can automatically fix permissions
QFile file(runtimeDir);
const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser;
const QFile::Permissions additionalPerms = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
QCOMPARE(file.permissions(), wantedPerms | additionalPerms);
QVERIFY(file.setPermissions(wantedPerms | QFile::ExeGroup));
const QString runtimeDirAgain = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
QCOMPARE(runtimeDirAgain, runtimeDir);
QCOMPARE(QFile(runtimeDirAgain).permissions(), wantedPerms | additionalPerms);
#endif #endif
} }
@ -516,11 +506,27 @@ void tst_qstandardpaths::testCustomRuntimeDirectory()
const QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); const QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
QVERIFY2(runtimeDir.isEmpty(), qPrintable(runtimeDir)); QVERIFY2(runtimeDir.isEmpty(), qPrintable(runtimeDir));
// When $XDG_RUNTIME_DIR points to a non-existing directory, QStandardPaths should warn (QTBUG-48771) // When $XDG_RUNTIME_DIR points to a directory with wrong permissions, QStandardPaths should warn
qputenv("XDG_RUNTIME_DIR", "does_not_exist"); const QByteArray wrongPermissionFileName = "wrong_permissions";
QTest::ignoreMessage(QtWarningMsg, "QStandardPaths: XDG_RUNTIME_DIR points to non-existing path 'does_not_exist', please create it with 0700 permissions."); QDir::current().mkdir(wrongPermissionFileName);
QFile wrongPermissionFile(wrongPermissionFileName);
const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser;
QVERIFY(wrongPermissionFile.setPermissions(wantedPerms | QFile::ExeGroup));
qputenv("XDG_RUNTIME_DIR", wrongPermissionFileName);
QTest::ignoreMessage(QtWarningMsg,
qPrintable(QString::fromLatin1("QStandardPaths: wrong permissions on runtime directory " + wrongPermissionFileName + ", 7710 instead of 7700")));
const QString wrongPermissionRuntimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
QVERIFY(wrongPermissionRuntimeDir.isEmpty());
QDir::current().rmdir(wrongPermissionFileName);
// When $XDG_RUNTIME_DIR points to a non-existing directory, QStandardPaths should create it first
const QByteArray nonExistingDir = "does_not_exist";
qputenv("XDG_RUNTIME_DIR", nonExistingDir);
const QString nonExistingRuntimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); const QString nonExistingRuntimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
QVERIFY2(nonExistingRuntimeDir.isEmpty(), qPrintable(nonExistingRuntimeDir)); QVERIFY2(!nonExistingRuntimeDir.compare(nonExistingDir), qPrintable(nonExistingRuntimeDir));
QVERIFY(QDir::current().exists(nonExistingRuntimeDir));
QDir::current().rmdir(nonExistingRuntimeDir);
// When $XDG_RUNTIME_DIR points to a file, QStandardPaths should warn // When $XDG_RUNTIME_DIR points to a file, QStandardPaths should warn
const QString file = QFINDTESTDATA("tst_qstandardpaths.cpp"); const QString file = QFINDTESTDATA("tst_qstandardpaths.cpp");