From 55427858e3f9d98dc7a75638c54a4fffde6be73a Mon Sep 17 00:00:00 2001 From: Samuli Piippo Date: Wed, 20 Mar 2019 15:39:59 +0200 Subject: 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 Reviewed-by: Thiago Macieira --- src/corelib/io/qstandardpaths_unix.cpp | 51 ++++++++++++---------- .../io/qstandardpaths/tst_qstandardpaths.cpp | 34 +++++++++------ 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/corelib/io/qstandardpaths_unix.cpp b/src/corelib/io/qstandardpaths_unix.cpp index 3d4a349c8c..cc656954d9 100644 --- a/src/corelib/io/qstandardpaths_unix.cpp +++ b/src/corelib/io/qstandardpaths_unix.cpp @@ -120,54 +120,59 @@ QString QStandardPaths::writableLocation(StandardLocation type) } case RuntimeLocation: { - const uint myUid = uint(geteuid()); // 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; QString xdgRuntimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR")); if (xdgRuntimeDir.isEmpty()) { const QString userName = QFileSystemEngine::resolveUserName(myUid); xdgRuntimeDir = QDir::tempPath() + QLatin1String("/runtime-") + userName; 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 qWarning("QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '%s'", qPrintable(xdgRuntimeDir)); #endif } else { fileInfo.setFile(xdgRuntimeDir); - if (!fileInfo.exists()) { - qWarning("QStandardPaths: XDG_RUNTIME_DIR points to non-existing path '%s', " - "please create it with 0700 permissions.", qPrintable(xdgRuntimeDir)); - return QString(); - } + } + if (fileInfo.exists()) { if (!fileInfo.isDir()) { qWarning("QStandardPaths: XDG_RUNTIME_DIR points to '%s' which is not a directory", qPrintable(xdgRuntimeDir)); 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" if (fileInfo.ownerId() != myUid) { - qWarning("QStandardPaths: wrong ownership on runtime directory %s, %d instead of %d", qPrintable(xdgRuntimeDir), - fileInfo.ownerId(), myUid); + qWarning("QStandardPaths: wrong ownership on runtime directory %s, %d instead of %d", + qPrintable(xdgRuntimeDir), fileInfo.ownerId(), myUid); return QString(); } // "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) { - QFile file(xdgRuntimeDir); - if (!file.setPermissions(wantedPerms)) { - qWarning("QStandardPaths: could not set correct permissions on runtime directory %s: %s", - qPrintable(xdgRuntimeDir), qPrintable(file.errorString())); - return QString(); - } + qWarning("QStandardPaths: wrong permissions on runtime directory %s, %x instead of %x", + qPrintable(xdgRuntimeDir), uint(fileInfo.permissions()), uint(wantedPerms)); + return QString(); } + return xdgRuntimeDir; } default: diff --git a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp index 1379c788d1..40023d7fea 100644 --- a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp +++ b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp @@ -465,16 +465,6 @@ void tst_qstandardpaths::testRuntimeDirectory() #ifdef Q_XDG_PLATFORM const QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); 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 } @@ -516,11 +506,27 @@ void tst_qstandardpaths::testCustomRuntimeDirectory() const QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); QVERIFY2(runtimeDir.isEmpty(), qPrintable(runtimeDir)); - // When $XDG_RUNTIME_DIR points to a non-existing directory, QStandardPaths should warn (QTBUG-48771) - qputenv("XDG_RUNTIME_DIR", "does_not_exist"); - QTest::ignoreMessage(QtWarningMsg, "QStandardPaths: XDG_RUNTIME_DIR points to non-existing path 'does_not_exist', please create it with 0700 permissions."); + // When $XDG_RUNTIME_DIR points to a directory with wrong permissions, QStandardPaths should warn + const QByteArray wrongPermissionFileName = "wrong_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); - 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 const QString file = QFINDTESTDATA("tst_qstandardpaths.cpp"); -- cgit v1.2.3