@@ -116,15 +116,15 @@ void FileSystem::setFileReadOnly(const QString &filename, bool readonly)
116116 return ;
117117 }
118118
119- // current read-only folder ACL needs to be removed from files also when making a folder read-write
120- // we currently have a too limited set of authorization for files when applying the restrictive ACL for folders on the child files
121- // the file's attributes can only be modified if there's no access denied ACE present
122- setAclPermission (filename, FileSystem::FolderPermissions::ReadWrite, false );
123119
124120 auto newFileAttributes = fileAttributes;
125121 if (readonly) {
122+ // replace any existing access denied ACE on this object with one that allows us to at least modify the file attributes
123+ setAclPermission (filename, FileSystem::FolderPermissions::ReadOnly, false );
126124 newFileAttributes = newFileAttributes | FILE_ATTRIBUTE_READONLY;
127125 } else {
126+ // remove the access denied ACE from this object in case we have a too restrictive setting that does not allow to modify file attributes
127+ setAclPermission (filename, FileSystem::FolderPermissions::ReadWrite, false );
128128 newFileAttributes = newFileAttributes & (~FILE_ATTRIBUTE_READONLY);
129129 }
130130
@@ -133,11 +133,6 @@ void FileSystem::setFileReadOnly(const QString &filename, bool readonly)
133133 qCWarning (lcFileSystem ()).nospace () << " SetFileAttributesW failed, action=" << (readonly ? " readonly" : " read write" ) << " filename=" << windowsFilename << " lastError=" << lastError << " errorMessage=" << Utility::formatWinError (lastError);
134134 }
135135
136- if (readonly) {
137- // re-add the previously removed readonly ACE
138- setAclPermission (filename, FileSystem::FolderPermissions::ReadWrite, true );
139- }
140-
141136 return ;
142137#endif
143138 QFile file (filename);
@@ -776,10 +771,11 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
776771 const auto safePathFileInfo = QFileInfo{path};
777772
778773 // CreateFileW is known to work with long paths in the \\?\ variant
779- constexpr DWORD desiredAccess = READ_CONTROL | WRITE_DAC;
774+ // MAXIMUM_ALLOWED will not propagate ACEs to children when setting the DACL
775+ constexpr DWORD desiredAccess = READ_CONTROL | WRITE_DAC | MAXIMUM_ALLOWED;
780776 constexpr DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
781777 constexpr DWORD creationDisposition = OPEN_EXISTING;
782- constexpr DWORD flagsAndAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_OPEN_NO_RECALL ;
778+ constexpr DWORD flagsAndAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT;
783779 fileHandle.reset (CreateFileW (rawPath, desiredAccess, shareMode, nullptr , creationDisposition, flagsAndAttributes, nullptr ));
784780
785781 if (fileHandle.get () == INVALID_HANDLE_VALUE) {
@@ -828,8 +824,8 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
828824
829825 if (permissions == FileSystem::FolderPermissions::ReadOnly) {
830826 // the access denied ACE needs to appear at the start of the ACL
831- if (!AddAccessDeniedAceEx (newDacl.get (), ACL_REVISION, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE ,
832- FILE_DELETE_CHILD | DELETE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA, sid.get ())) {
827+ if (!AddAccessDeniedAceEx (newDacl.get (), ACL_REVISION, NO_PROPAGATE_INHERIT_ACE ,
828+ FILE_DELETE_CHILD | DELETE | FILE_WRITE_DATA | FILE_WRITE_EA | FILE_APPEND_DATA, sid.get ())) {
833829 qCWarning (lcFileSystem).nospace () << " AddAccessDeniedAceEx failed, path=" << path << " errorMessage=" << Utility::formatWinError (GetLastError ());
834830 return false ;
835831 }
@@ -858,7 +854,6 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
858854 }
859855
860856 if (!AddAce (newDacl.get (), ACL_REVISION, newAceIndex, currentAce, currentAceHeader->AceSize )) {
861- const auto lastError = GetLastError ();
862857 qCWarning (lcFileSystem).nospace () << " AddAce failed,"
863858 << " path=" << path
864859 << " errorMessage=" << Utility::formatWinError (GetLastError ())
@@ -869,21 +864,20 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
869864 newAceIndex++;
870865 }
871866
867+ #if 0
868+ // TODO: figure out if this is necessary; before 4.0.2 this did not work at all
869+ // --> currentFolder used to be the parent directory of `path`, so this ended up to be
870+ // iterating over nonexisting entries inside `path`.
871+ // due to the early `return` in the loop this may have never reached the call to
872+ // what-used-to-be `SetFileSecurityW` (now `SetSecurityInfo`)
872873 if (safePathFileInfo.isDir() && applyAlsoToFiles) {
873874 const auto currentFolder = QDir{path};
874875 const auto childFiles = currentFolder.entryList(QDir::Filter::Files);
875876 for (const auto &oneEntry : childFiles) {
876877 const auto childFile = joinPath(path, oneEntry);
877-
878878 const auto rawChildFile = reinterpret_cast<const wchar_t *>(childFile.utf16());;
879- const auto attributes = GetFileAttributesW (rawChildFile);
880-
881- // testing if that could be a pure virtual placeholder file (i.e. CfApi file without data)
882- // we do not want to trigger implicit hydration ourself
883- if ((attributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0 ) {
884- continue ;
885- }
886879
880+ // apparently as long as we do not attempt to read the file contents we will not trigger an implicit hydration ourselves
887881 Utility::UniqueHandle childFileHandle(CreateFileW(rawChildFile, desiredAccess, shareMode, nullptr, creationDisposition, flagsAndAttributes, nullptr));
888882 if (childFileHandle.get() == INVALID_HANDLE_VALUE) {
889883 qCWarning(lcFileSystem).nospace() << "CreateFileW failed, path=" << childFile << " errorMessage=" << Utility::formatWinError(GetLastError());
@@ -896,6 +890,7 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
896890 }
897891 }
898892 }
893+ #endif
899894
900895 if (const auto lastError = SetSecurityInfo (fileHandle.get (), SE_FILE_OBJECT, PROTECTED_DACL_SECURITY_INFORMATION | securityInfo, nullptr , nullptr , newDacl.get (), nullptr ); lastError != ERROR_SUCCESS) {
901896 qCWarning (lcFileSystem).nospace () << " SetSecurityInfo failed, path=" << path << " errorMessage=" << Utility::formatWinError (lastError);
0 commit comments