Understanding Windows File Permissions vs. Read-Only Attributes: A Developer’s Guide to Access Control


3 views

As a Unix developer transitioning to Windows, I initially struggled with the interaction between NTFS permissions and file attributes. While Unix uses a straightforward permission bitmask, Windows separates security descriptors (ACLs) from file attributes like "Read-only" - creating potential conflicts.

NTFS Permissions (set via Security tab):

icacls example.txt /grant Administrators:(F)  # Full control

File Attributes (set via Properties or command line):

attrib +R example.txt  # Sets read-only flag
attrib -R example.txt  # Removes read-only flag

Even with Full Control NTFS permissions, the read-only attribute acts as a hard restriction. This isn't a bug - it's by design. The attribute system predates NTFS and maintains backward compatibility.

Workaround options:

# Option 1: Temporarily override attribute in code
$file = [System.IO.File]::Open("example.txt", 
    [System.IO.FileMode]::Open, 
    [System.IO.FileAccess]::ReadWrite,
    [System.IO.FileShare]::ReadWrite)

# Option 2: Force modification via PowerShell
(Get-Content example.txt) -replace "old","new" | Set-Content example.txt -Force

When writing file operations in Windows:

  • Always check both permissions AND attributes
  • Handle AccessDenied exceptions appropriately
  • Consider using FileSystemRights.Modify instead of FullControl

Example C# check:

FileSecurity security = File.GetAccessControl(path);
AuthorizationRuleCollection rules = security.GetAccessRules(true, true, typeof(NTAccount));

bool hasWriteAccess = rules
    .Cast()
    .Any(r => (r.FileSystemRights & FileSystemRights.WriteData) > 0 
           && r.AccessControlType == AccessControlType.Allow
           && r.IdentityReference.Value.Contains("Administrators"));

bool isReadOnly = (File.GetAttributes(path) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;

The separation allows:

  • Application-level protection (attributes)
  • System-level security (ACLs)
  • Backward compatibility with legacy systems

For deployment scripts, always combine both approaches:

icacls %DEPLOY_DIR% /grant %USERNAME%:(OI)(CI)F
attrib -R %DEPLOY_DIR%\*.config

Coming from a Unix background, you might find Windows' dual-layer security model confusing. While Unix/Linux systems primarily rely on permission bits (rwx), Windows implements two distinct mechanisms:

  1. NTFS permissions (security descriptor)
  2. File attributes (including read-only flag)

The Windows security model evaluates these checks in sequence:

1. File attributes (read-only flag) → 2. Share permissions → 3. NTFS permissions

Even with Full Control NTFS permissions, the read-only attribute acts as a hard barrier. This is different from Unix where the write permission bit alone determines writability.

Consider this PowerShell scenario:

# Create test file with Full Control for Administrators
$file = "C:\test\demo.txt"
New-Item -Path $file -Force
icacls $file /grant "Administrators:(F)"

# Set read-only attribute
Set-ItemProperty -Path $file -Name IsReadOnly -Value $true

# Attempt to write (will fail)
try {
    [System.IO.File]::WriteAllText($file, "Test content")
    Write-Host "Write succeeded"
} catch {
    Write-Host "Write failed: $_"
}

The read-only attribute serves several purposes in Windows:

  • Application compatibility (legacy Windows programs)
  • Protection against accidental modification
  • Flag for system-critical files

If you need programmatic control while respecting the attribute system:

Method 1: Temporary attribute modification

# C# example
string filePath = @"C:\test\demo.txt";
var attributes = File.GetAttributes(filePath);

try {
    File.SetAttributes(filePath, attributes & ~FileAttributes.ReadOnly);
    File.WriteAllText(filePath, "New content");
} finally {
    File.SetAttributes(filePath, attributes);
}

Method 2: Low-level file access

// C++ example using CreateFile with FILE_FLAG_OPEN_REPARSE_POINT
HANDLE hFile = CreateFile(
    L"C:\\test\\demo.txt",
    GENERIC_WRITE,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_OPEN_REPARSE_POINT,
    NULL);

if (hFile != INVALID_HANDLE_VALUE) {
    WriteFile(hFile, ...);
    CloseHandle(hFile);
}

When designing applications that modify files:

  • Always check both attributes and effective permissions
  • Consider using Windows API functions like GetEffectiveRightsFromAcl
  • Handle UAC elevation scenarios properly

You can modify system behavior via registry (not recommended for production):

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer]
"HonorAdminReadOnlyAttribute"=dword:00000000