3

I'm creating a cross-platform software and I want to know if there is any (easy) way to read/write Unix (Mac OSX/Linux) extended file attributes in C#. I've just read about xattr namespaces, but I haven't found any information about C# implementation or bindings of this feature.

P.S. The only thing I found so far is python-xattr library, but I don't want to use it because:

  • I don't want to obligate the users to install Python (there is already Mono/.NET dependency to deal with)
  • By using Python I will have a performance decrease (C# is compiled, while Python is interpreted)
  • I don't want to rely/depend on external tools (if it's possible), because it's not safe
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Salaros
  • 1,444
  • 1
  • 14
  • 34
  • Quite often, relying on external libraries is much safer than writing your own in the sense that it will most likely have less bugs. Or did you mean something else by “safe”? – svick Sep 26 '11 at 13:56
  • @svick I think what he meant by safe is managed. C# is a managed language and python is not. – thekip Sep 26 '11 at 14:37
  • @thekip is right, I just want to have a fully managed code and possibly homogeneous develop environment. I'm already using C# as glue :D for HTTP5+jQuery+JSON app (running in the xulrunner), native C# code, SQLite/MySQL wrappers-entity providers etc By adding some other (unmanaged) languages and tools certainly I won't make my app more stable. – Salaros Oct 06 '11 at 20:31

3 Answers3

1

You should use the syscalls in Mono.Unix.Native.Syscall.*attr*.
The nuget assembly is called Mono.Posix.NETStandard

Note that the name of the name-value pair is prefixed with a namespace.
According to man 7 xattr

Currently, the security, system, trusted, and user extended attribute classes are defined as described below. Additional classes may be added in the future.

public static void Test()
{
    string path = "/root/Desktop/CppSharp.txt";

    // Mono.Unix.Native.Syscall.getxattr()
    // Mono.Unix.Native.Syscall.fgetxattr()
    // Mono.Unix.Native.Syscall.lgetxattr()

    // Mono.Unix.Native.Syscall.setxattr()
    // Mono.Unix.Native.Syscall.fsetxattr()
    // Mono.Unix.Native.Syscall.lsetxattr

    // Mono.Unix.Native.Syscall.llistxattr()
    // Mono.Unix.Native.Syscall.flistxattr()
    // Mono.Unix.Native.Syscall.llistxattr()

    // Mono.Unix.Native.Syscall.removexattr()
    // Mono.Unix.Native.Syscall.fremovexattr()
    // Mono.Unix.Native.Syscall.lremovexattr()

    if (System.IO.File.Exists(path))
        System.Console.WriteLine("path exists");
    else
        System.Console.WriteLine("path doesn't exists");

    System.Text.Encoding enc = new System.Text.UTF8Encoding(false);
    string[] values = null;

    int setXattrSucceeded = Mono.Unix.Native.Syscall.setxattr(path, "user.foobar",
        enc.GetBytes("Hello World äöüÄÖÜ"), Mono.Unix.Native.XattrFlags.XATTR_CREATE);

    if (setXattrSucceeded == -1)
    {
        Mono.Unix.Native.Errno er = Mono.Unix.Native.Stdlib.GetLastError();
        string message = Mono.Unix.Native.Stdlib.strerror(er);
        // https://stackoverflow.com/questions/12662765/how-can-i-get-error-message-for-errno-value-c-language
        System.Console.WriteLine(message);
    } // End if (setXattrSucceeded == -1)

    byte[] data = null;
    long szLen = Mono.Unix.Native.Syscall.getxattr(path, "user.foobar", out data);

    string value = enc.GetString(data);
    System.Console.WriteLine(value);

    Mono.Unix.Native.Syscall.listxattr(path, System.Text.Encoding.UTF8, out values);
    System.Console.WriteLine(values);

    // https://man7.org/linux/man-pages/man2/getxattr.2.html
} // End Sub TestExtendedAttributes 
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
0

I think Mono.Unix.Native.Syscall.setxattr would be a better solution, which is in Mono.Posix module.

more
  • 1
0

The final solution might be the following, tell me what do you think?

FileAttrsManager is an abstract class, used to create 2 derived classes:

  • FileAttrsManagerDos: manages advanced attributes using DSOFile.dll*
  • FileAttrsManagerUnix: manages advanced attributes using IronPython* and python-xattr**

[ * ] http:\\www.microsoft.com/download/en/details.aspx?displaylang=en&id=8422
[ ** ] http:\\ironpython.codeplex.com
[ ** * ] http:\\pypi.python.org/pypi/xatt

Extended attributes operation (such as SetPropery(string key, object value) and GetProperty(string key) etc) will be managed in a static class (FileAttrsProvider) that initializes a FileAttrsManager object to one of two derived types i.e.:

public static class FileAttrProvider
{

    private static FileAttrReader _reader = null;

    public static void Initialize()
    {
        switch (Environment.OSVersion.Platform)
        {
            case PlatformID.MacOSX:
            case PlatformID.Unix:
                _reader = new FileAttrReaderUnix();
                break;
            case PlatformID.Win32NT:
                _reader = new FileAttrReaderDos();
                break;
        }
    }
}

While the derived type depends on the environment, the original type is used in order to ensure an automatic dispatch of all the methods call on _reader object).

Phill
  • 18,398
  • 7
  • 62
  • 102
Salaros
  • 1,444
  • 1
  • 14
  • 34
  • Don't do this with a static class, use ABSTRACT classes for this ! – Stefan Steiger Jul 13 '20 at 04:36
  • @StefanSteiger look at the date, it's 2011! Now we'd use DI to inject a correct version of implementation etc. Anyway read the answer carefully please, it USES abstract classes, static class is just a facade. Again, today, after 9 years, I'd do it differently. – Salaros Jul 15 '20 at 11:12