From 922ccb27fba27328741aafdf6bd7a4258d193e88 Mon Sep 17 00:00:00 2001 From: Méven Car Date: Thu, 18 Jun 2026 09:08:39 +0000 Subject: viewproperties: detect read-only directories on Windows On Windows QFileInfo::isWritable() returns true for a directory that carries the read-only attribute, because the attribute does not prevent creating child files. It does prevent writing the directory's own extended attribute, the Alternate Data Stream where the view properties are stored, so the save fails with access denied and the settings are lost. Detect the read-only attribute with GetFileAttributesW and store the view properties in the destination directory instead, the same way a directory without write permission is handled on other platforms. The read-only directory test checks that the properties land in the destination directory. --- src/tests/viewpropertiestest.cpp | 33 +++++++++++---------------------- src/views/viewproperties.cpp | 13 ++++++++++++- 2 files changed, 23 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/tests/viewpropertiestest.cpp b/src/tests/viewpropertiestest.cpp index 1d9e886e1..73a26f289 100644 --- a/src/tests/viewpropertiestest.cpp +++ b/src/tests/viewpropertiestest.cpp @@ -109,45 +109,34 @@ void ViewPropertiesTest::testReadOnlyDirectory() QVERIFY(QFile(localFolder).setPermissions(QFileDevice::ReadOwner)); QScopedPointer props(new ViewProperties(testDirUrl)); +#ifdef Q_OS_WIN + const QString destinationDir = props->destinationDir(QStringLiteral("local")) + QDir::separator() + QString(localFolder).remove(QLatin1Char(':')); +#else + const QString destinationDir = props->destinationDir(QStringLiteral("local")) + localFolder; +#endif + QVERIFY(props->isAutoSaveEnabled()); props->setSortRole("someNewSortRole"); props.reset(); -#ifdef Q_OS_WIN - // On Windows the read-only attribute is not honored on directories: files and - // Alternate Data Streams can still be created inside them. The properties are - // therefore stored locally rather than redirected to the destination directory. - KFileMetaData::UserMetaData metadata(localFolder); - if (metadata.isSupported()) { - QVERIFY(metadata.hasAttribute("kde.fm.viewproperties#1")); - } else { - QVERIFY(QFile::exists(dotDirectoryFile)); - } -#else - // On a directory without write permission the properties are redirected to the - // destination directory, leaving the local directory untouched. - const QString destinationDir = props->destinationDir(QStringLiteral("local")) + localFolder; QVERIFY(QDir(destinationDir).exists()); QVERIFY(!QFile::exists(dotDirectoryFile)); KFileMetaData::UserMetaData metadata(localFolder); - QVERIFY(metadata.attribute(QStringLiteral("kde.fm.viewproperties#1")).isEmpty()); -#endif + const QString viewProperties = metadata.attribute(QStringLiteral("kde.fm.viewproperties#1")); + QVERIFY(viewProperties.isEmpty()); - // The stored value round-trips regardless of where it was written. props.reset(new ViewProperties(testDirUrl)); QVERIFY(props->isAutoSaveEnabled()); QCOMPARE(props->sortRole(), "someNewSortRole"); props.reset(); -#ifndef Q_OS_WIN - KFileMetaData::UserMetaData destMetadata(destinationDir); - if (destMetadata.isSupported()) { - QVERIFY(destMetadata.hasAttribute("kde.fm.viewproperties#1")); + metadata = KFileMetaData::UserMetaData(destinationDir); + if (metadata.isSupported()) { + QVERIFY(metadata.hasAttribute("kde.fm.viewproperties#1")); } else { QVERIFY(QFile::exists(destinationDir + "/.directory")); } -#endif // un-restrict write permissions QFile(localFolder).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner); diff --git a/src/views/viewproperties.cpp b/src/views/viewproperties.cpp index 94262e651..f461ebb89 100644 --- a/src/views/viewproperties.cpp +++ b/src/views/viewproperties.cpp @@ -14,6 +14,10 @@ #include #include +#ifdef Q_OS_WIN +#include +#endif + #include #include @@ -186,7 +190,14 @@ ViewProperties::ViewProperties(const QUrl &url) if (!useDestinationDir) { const QFileInfo dirInfo(m_filePath); const QFileInfo fileInfo(m_filePath + QDir::separator() + ViewPropertiesFileName); - useDestinationDir = !dirInfo.isWritable() || (dirInfo.size() > 0 && fileInfo.exists() && !(fileInfo.isReadable() && fileInfo.isWritable())); + bool dirWritable = dirInfo.isWritable(); +#ifdef Q_OS_WIN + if (dirWritable) { + const DWORD attrs = GetFileAttributesW(reinterpret_cast(m_filePath.utf16())); + dirWritable = (attrs == INVALID_FILE_ATTRIBUTES) || !(attrs & FILE_ATTRIBUTE_READONLY); + } +#endif + useDestinationDir = !dirWritable || (dirInfo.size() > 0 && fileInfo.exists() && !(fileInfo.isReadable() && fileInfo.isWritable())); } if (useDestinationDir) { -- cgit v1.3.1