sal/osl/w32/file_dirvol.cxx |   90 ++++++++++++++++++++++++++------------------
 1 file changed, 55 insertions(+), 35 deletions(-)

New commits:
commit 75d572302767eac4596d56b0ac848b39825b8013
Author:     Balazs Varga <[email protected]>
AuthorDate: Sun Feb 22 21:47:19 2026 +0100
Commit:     Balazs Varga <[email protected]>
CommitDate: Mon Feb 23 17:23:50 2026 +0100

    tdf#170297: skip AccessCheck for remote drives in osl_getFileStatus
    
    The tdf#150118 fix used AccessCheck to check write permissions when
    FILE_ATTRIBUTE_READONLY is not set. However, AccessCheck uses a local
    impersonation token which does not reflect the network credentials
    used by the SMB redirector, causing files on remote drives to be
    incorrectly marked as read-only.
    
    Skip the AccessCheck logic for remote drives by detecting UNC paths
    (\server\share\...) and mapped network drives (via GetDriveTypeW
    returning DRIVE_REMOTE).
    
    regression after: 1b5d039d8b5861b5ea21ef00ff521c6e2565105f
    
    Change-Id: I7f1514db89655e8607f64da3d62e8e29c23105f4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199997
    Tested-by: Jenkins
    Reviewed-by: Balazs Varga <[email protected]>
    Tested-by: Balazs Varga <[email protected]>

diff --git a/sal/osl/w32/file_dirvol.cxx b/sal/osl/w32/file_dirvol.cxx
index 748a431dd4f9..a01f95555e94 100644
--- a/sal/osl/w32/file_dirvol.cxx
+++ b/sal/osl/w32/file_dirvol.cxx
@@ -1528,54 +1528,74 @@ oslFileError SAL_CALL osl_getFileStatus(
         pStatus->uAttributes &= ~sal_uInt64(FILE_ATTRIBUTE_READONLY);
 
     // tdf#150118: if there is no Read Only attribute set, lets check if the 
user has write access on the file
+    // tdf#170297: skip AccessCheck for remote drives - it uses a local 
impersonation token which
+    // does not reflect the network credentials used by the SMB redirector, 
producing unreliable
+    // results that cause remote files to be incorrectly marked as read-only
     if ( (pStatus->uAttributes & (FILE_ATTRIBUTE_READONLY | 
FILE_ATTRIBUTE_DIRECTORY)) == 0 )
     {
-        HANDLE hProcessToken = nullptr;
-        OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY, 
&hProcessToken);
+        bool bIsRemote = false;
+        if (sFullPath.startsWith("\\") && !sFullPath.startsWith("\\?\"))
+        {
+            // UNC path (\server\share\...) is always remote
+            bIsRemote = true;
+        }
+        else if (sFullPath.getLength() >= 2 && rtl::isAsciiAlpha(sFullPath[0])
+                 && sFullPath[1] == ':')
+        {
+            // Drive letter path: check if mapped to a remote drive
+            WCHAR rootPath[4] = { static_cast<WCHAR>(sFullPath[0]), L':', 
L'\', 0 };
+            bIsRemote = (GetDriveTypeW(rootPath) == DRIVE_REMOTE);
+        }
 
-        HANDLE hImpersonationToken = nullptr;
-        DuplicateToken(hProcessToken, SecurityImpersonation, 
&hImpersonationToken);
+        if (!bIsRemote)
+        {
+            HANDLE hProcessToken = nullptr;
+            OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | 
TOKEN_QUERY, &hProcessToken);
 
-        PSECURITY_DESCRIPTOR pSD = nullptr;
-        // 
https://learn.microsoft.com/en-us/windows/win32/api/aclapi/nf-aclapi-getnamedsecurityinfow
-        DWORD aResult = GetNamedSecurityInfoW(
-            o3tl::toW(sFullPath.getStr()), SE_FILE_OBJECT,
-            OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | 
DACL_SECURITY_INFORMATION,
-            nullptr, nullptr, nullptr, nullptr, &pSD);
+            HANDLE hImpersonationToken = nullptr;
+            DuplicateToken(hProcessToken, SecurityImpersonation, 
&hImpersonationToken);
 
-        if (aResult == ERROR_SUCCESS)
-        {
-            GENERIC_MAPPING mapping
-                = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, 
FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS };
-            DWORD grantedAccess = 0;
-            BOOL accessStatus = TRUE;
-            PRIVILEGE_SET privSet;
-            DWORD privSetSize = sizeof(privSet);
-
-            // 
https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-accesscheck
-            BOOL bResult = AccessCheck(pSD, hImpersonationToken, 
FILE_GENERIC_WRITE, &mapping,
-                                       &privSet, &privSetSize, &grantedAccess, 
&accessStatus);
-
-            if (bResult)
+            PSECURITY_DESCRIPTOR pSD = nullptr;
+            // 
https://learn.microsoft.com/en-us/windows/win32/api/aclapi/nf-aclapi-getnamedsecurityinfow
+            DWORD aResult = GetNamedSecurityInfoW(
+                o3tl::toW(sFullPath.getStr()), SE_FILE_OBJECT,
+                OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | 
DACL_SECURITY_INFORMATION,
+                nullptr, nullptr, nullptr, nullptr, &pSD);
+
+            if (aResult == ERROR_SUCCESS)
             {
-                if (!accessStatus)
+                GENERIC_MAPPING mapping
+                    = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, 
FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS };
+                DWORD grantedAccess = 0;
+                BOOL accessStatus = TRUE;
+                PRIVILEGE_SET privSet;
+                DWORD privSetSize = sizeof(privSet);
+
+                // 
https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-accesscheck
+                BOOL bResult = AccessCheck(pSD, hImpersonationToken, 
FILE_GENERIC_WRITE, &mapping,
+                                           &privSet, &privSetSize, 
&grantedAccess, &accessStatus);
+
+                if (bResult)
                 {
-                    // User does NOT have write access: set Read Only attribute
-                    pStatus->uAttributes |= 
sal_uInt64(FILE_ATTRIBUTE_READONLY);
+                    if (!accessStatus)
+                    {
+                        // User does NOT have write access: set Read Only 
attribute
+                        pStatus->uAttributes |= 
sal_uInt64(FILE_ATTRIBUTE_READONLY);
+                    }
                 }
+                else
+                {
+                    SAL_WARN("sal.osl", "AccessCheck API failed with: " << 
GetLastError());
+                }
+                LocalFree(pSD); // free memory
             }
             else
             {
-                SAL_WARN("sal.osl", "AccessCheck API failed with: " << 
GetLastError());
+                SAL_WARN("sal.osl", "GetNamedSecurityInfoW API failed with: " 
<< aResult);
             }
-            LocalFree(pSD); // free memory
-        }
-        else
-        {
-            SAL_WARN("sal.osl", "GetNamedSecurityInfoW API failed with: " << 
aResult);
+            CloseHandle(hImpersonationToken); // free memory
+            CloseHandle(hProcessToken); // free memory
         }
-        CloseHandle(hImpersonationToken); // free memory
-        CloseHandle(hProcessToken); // free memory
     }
 
     pStatus->uValidFields |= osl_FileStatus_Mask_Attributes;

Reply via email to