dotnet / dotnet/runtime

[API Proposal]: APIs for checking Unix file access mode

Open
#120,608 3 comments 0 reactions 0 assignees View on GitHub
api-suggestion area-System.IO
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

### Background and motivation

I propose adding Unix-specific file permission checking APIs that use `faccessat(2)` instead of the less efficient `stat(2)`/`lstat(2)` approach used by current methods like `File.GetUnixFileMode()`.

#### Notes

Under the hood of this API we can call `faccessat`.

Currently, checking file permissions requires using `File.GetUnixFileMode()` which uses the `stat`/`lstat` system calls. These calls retrieve extensive file metadata when we only need permission information.

The `faccessat` system call is specifically designed for efficient permission checking and can provide significant performance improvements by avoiding unnecessary metadata retrieval.

This is particularly valuable for applications that frequently check file permissions (e.g. build systems, SDKs)

### API Proposal

```csharp
namespace System.IO;

[Flags]
public enum UnixFileAccess
{
None = 0,
Execute = 1,
Write = 2,
Read = 4,

// Common combinations
ReadWrite = Read | Write,
ReadExecute = Read | Execute,
ReadWriteExecute = Read | Write | Execute
}

public static class File
{
// New unified method for permission checking

[UnsupportedOSPlatform("windows")]
public static bool CheckUnixFileAccess(string path, UnixFileAccess modes, bool followSymlinks = true);

// Convenience methods for common cases

[UnsupportedOSPlatform("windows")]
public static bool CanUnixFileRead(string path, bool followSymlinks = true)
=> CheckAccess(path, UnixFileAccess.Read, followSymlinks);
[UnsupportedOSPlatform("windows")]
public static bool CanUnixFileWrite(string path, bool followSymlinks = true)
=> CheckAccess(path, UnixFileAccess.Write, followSymlinks);
[UnsupportedOSPlatform("windows")]
public static bool CanUnixFileExecute(string path, bool followSymlinks = true)
=> CheckAccess(path, UnixFileAccess.Execute, followSymlinks);
}
```

### API Usage

```csharp
// Check if dotnet executable is accessible
var dotnetPath = "/usr/bin/dotnet";

if (File.CanUnixExecute(dotnetPath)) // Returns true if executable
Console.WriteLine("dotnet is executable");

if (File.CanUnixExecute(dotnetPath, followSymlinks: false)) // Check symlink itself
Console.WriteLine("dotnet symlink is executable");

// Check multiple files for read+execute permissions
string[] files = { "/bin/bash", "/usr/bin/python", "/usr/local/bin/node" };
var accessibleFiles = files.Where(f =>
File.CheckUnixAccess(f, UnixFileAccess.ReadExecute));

// Check write permission on config file
if (File.CanUnixWrite("/etc/myapp/config.json"))
{
// Update configuration
}
```

### Alternative Designs

- Continue using `File.GetUnixFileMode()` - less efficient due to full metadata retrieval
- P/Invoke to faccessat directly - not type-safe and harder to maintain

### Risks

- Platform compatibility: `faccessat` availability across Unix variants
- Security: Some systems may have restrictions on `faccessat` usage

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.