40

I am trying to learn how to set file permissions on Linux/Unix with .NET Core. I already found a question on here that points me in the direction of System.IO.FileSystem, but I can't seem to find any documentation on how to use it.

In a nutshell, I'd like to chmod a file 644 from a .NET Core application that only runs on Linux, but am at a loss on how to proceed.

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Steve
  • 403
  • 1
  • 4
  • 4
  • 2
    Microsoft uses their file permission model on Windows and translates it to Linux/UNIX. So the call to `chmod` is internal, https://github.com/dotnet/corefx/blob/bffef76f6af208e2042a2f27bc081ee908bb390b/src/Common/src/Interop/Unix/System.Native/Interop.ChMod.cs and is used only in https://github.com/dotnet/corefx/blob/801dde95a5eac06140d0ac633ac3f9bfdd25aca5/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs So in your case, you must translate 644 to the corresponding Windows file permissions and then use the Windows way to manipulate the file. – Lex Li Jul 17 '17 at 02:28
  • I've solved this issue calling `/bin/chmod` directly using `System.Diagnostics` API – gsscoder Oct 30 '22 at 14:01

4 Answers4

47

At the moment, there is no built in API in .NET Core for this. However, the .NET Core team is working to make Mono.Posix available on .NET Core. This exposes API to do this kind of operation in managed code. See https://github.com/dotnet/corefx/issues/15289 and https://github.com/dotnet/corefx/issues/3186. You can try an early version of this API here: https://www.nuget.org/packages/Mono.Posix.NETStandard/1.0.0-beta1

    var unixFileInfo = new Mono.Unix.UnixFileInfo("test.txt");
    // set file permission to 644
    unixFileInfo.FileAccessPermissions =
        FileAccessPermissions.UserRead | FileAccessPermissions.UserWrite
        | FileAccessPermissions.GroupRead
        | FileAccessPermissions.OtherRead;

If you don't want to use Mono.Posix, you can implement this same functionality by invoking native code. Using P/Invoke, you can call the chmod function from libc. See man 2 chmod for more details on the native API.

using System;
using System.IO;
using System.Runtime.InteropServices;
using static System.Console;

class Program
{
    [DllImport("libc", SetLastError = true)]
    private static extern int chmod(string pathname, int mode);

    // user permissions
    const int S_IRUSR = 0x100;
    const int S_IWUSR = 0x80;
    const int S_IXUSR = 0x40;

    // group permission
    const int S_IRGRP = 0x20;
    const int S_IWGRP = 0x10;
    const int S_IXGRP = 0x8;

    // other permissions
    const int S_IROTH = 0x4;
    const int S_IWOTH = 0x2;
    const int S_IXOTH = 0x1;

    static void Main(string[] args)
    {
        WriteLine("Setting permissions to 0755 on test.sh");
        const int _0755 =
            S_IRUSR | S_IXUSR | S_IWUSR
            | S_IRGRP | S_IXGRP
            | S_IROTH | S_IXOTH;
        WriteLine("Result = " + chmod(Path.GetFullPath("test.sh"), (int)_0755));

        WriteLine("Setting permissions to 0644 on sample.txt");
        const int _0644 =
            S_IRUSR | S_IWUSR
            | S_IRGRP
            | S_IROTH;
        WriteLine("Result = " + chmod(Path.GetFullPath("sample.txt"), _0644));

        WriteLine("Setting permissions to 0600 on secret.txt");
        const int _0600 = S_IRUSR | S_IWUSR;
        WriteLine("Result = " + chmod(Path.GetFullPath("secret.txt"), _0600));
    }
}
natemcmaster
  • 25,673
  • 6
  • 78
  • 100
  • 2
    Note - the NuGet package is out of beta: https://www.nuget.org/packages/Mono.Posix.NETStandard/ – RobSiklos Sep 12 '19 at 18:38
  • 1
    I found that I had to issue `unixFileInfo.Refresh()` to immediately commit the changes to the file system. Otherwise the changes would manifest some unspecified time later. Curious if anyone else has seen that? – Andrew Apr 10 '20 at 00:00
  • Are you running the native code sample using .NetCore? – JohnB Nov 22 '20 at 00:15
16

I solved this problem by just starting a new process and executing bash chmod commands.

Example:

public static void Exec(string cmd)
{
    var escapedArgs = cmd.Replace("\"", "\\\"");
        
    using var process = new Process
    {
        StartInfo = new ProcessStartInfo
        {
            RedirectStandardOutput = true,
            UseShellExecute = false,
            CreateNoWindow = true,
            WindowStyle = ProcessWindowStyle.Hidden,
            FileName = "/bin/bash",
            Arguments = $"-c \"{escapedArgs}\""
        }
    };

    process.Start();
    process.WaitForExit();
}

and then:

Exec("chmod 644 /path/to/file.txt");

You can also use this Exec method to run any other type of bash commands.

Tono Nam
  • 34,064
  • 78
  • 298
  • 470
kspearrin
  • 10,238
  • 9
  • 53
  • 82
11

I know this question is a bit old, but for .NET 7 and above, there's an easier API.

File.SetUnixFileMode(path, UnixFileMode.UserRead | UnixFileMode.UserWrite);
Sudara
  • 4,769
  • 3
  • 34
  • 39
3

Here is a simple chmod function that you can use in c# with no depencencies.

// Returns true if success and false otherwise
// permissions can be an int or a string. For example it can also be +x, -x etc..
bool Chmod(string filePath, string permissions = "700", bool recursive = false)
{
        string cmd;
        if (recursive)
            cmd = $"chmod -R {permissions} {filePath}";
        else
            cmd = $"chmod {permissions} {filePath}";

        try
        {
            using (Process proc = Process.Start("/bin/bash", $"-c \"{cmd}\""))
            {
                proc.WaitForExit();
                return proc.ExitCode == 0;
            }
        }
        catch
        {
            return false;
        }
}
Tono Nam
  • 34,064
  • 78
  • 298
  • 470
  • The original post is from a couple of years ago, so checking in to see if there are other alternatives available to "bash/chmod" and Mono.Postix package. – JohnB Nov 17 '20 at 15:20