Announcing Mono.Fuse - Jonathan Pryor's web log
« Definitions Matter | Main | Mono.Fuse, Take 2.1! »
Announcing Mono.Fuse
Mono.Fuse is a binding for the FUSE library, permitting user-space file systems to be written in C#.
Why?
I read Robert Love's announcement of beaglefs, a FUSE program that exposes Beagle searches as a filesystem. My first thought: Why wasn't that done in C# (considering that the rest of Beagle is C#)?
What about SULF?
Stackable User-Level Filesystem, or SULF, is a pre-existing FUSE binding in C#, started by Valient Gough in 2004.
Mono.Fuse has no relation to SULF, for three reasons:
- It goes to great efforts to avoid a Mono.Posix.dll dependency, duplicating Mono.Unix.Native.Stat (Fuse.Stat), Mono.Unix.Native.Statvfs (Fuse.StatFS), and many methods from Mono.Unix.Native.Syscall (Fuse.Wrapper).
- I don't like the SULF API. (Not that I spent a great deal of time looking at it, but what I did see I didn't like.)
- SULF wraps the FUSE kernel-level interface, while Mono.Fuse wraps the higher level libfuse C interface.
I find (1) the most appalling, if only because I'm the Mono.Posix maintainer and I'd like to see my work actually used. :-)
Once I started writing Mono.Fuse, I discovered a good reason to avoid Mono.Posix: it's currently impossible to use the native MonoPosixHelper shared library from outside of Mono. I figured this would be a good opportunity to rectify that, making it easier for additional libraries to build upon the Mono.Posix infrastructure.
Implementation
Mono.Fuse requires patches to the mcs and mono modules, changes which need to be proposed and discussed.
mono
The biggest problem with the mono module is that no headers are installed, making it difficult to make use of libMonoPosixHelper.so.
Changes:
- Modify configure to generate a mono-config.h file, installed as $libdir/mono/include/mono-config.h. (Basic idea "borrowed" from GLib's $libdir/glib-2.0/include/glibconfig.h).
- Add a mono-posix-helper.pc file.
- Install the files $includedir/mono/posix/helper.h and $includedir/mono/posix/map.h.
map.h is the current map.h file generated by make-map.exe, with some major additions (detailed in the mcs section).
helper.h is the main include file, which includes map.h and declares all types/functions which cannot be generated by make-map.exe.
mono-config.h is necessary because it needs to contain platform-specific macros. In particular, Linux needs:
int Mono_Posix_ToStatvfs (struct statvfs *to, struct Mono_Posix_Statvfs *to);
while OS X and *BSD need:
int Mono_Posix_ToStatvfs (struct statfs *to, struct Mono_Posix_Statvfs *to);
Note struct statvfs vs. struct statfs. The mono/posix/helper.h header needs to "paper over" the difference, and thus needs to know which type the platform prefers. helper.h thus looks like:
#ifdef MONO_HAVE_STATVFS struct statvfs; int Mono_Posix_ToStatvfs (struct statvfs *from, struct Mono_Posix_Statvfs *to); #endif #ifdef MONO_HAVE_STATFS struct statfs; int Mono_Posix_ToStatvfs (struct statfs *from, struct Mono_Posix_Statvfs *to); #endif
One of MONO_HAVE_STATVFS or MONO_HAVE_STATFS would be defined in mono-config.h.
mcs
There are two major changes:
- The addition of one public attribute to the API:
// targets Class, Delegate, Enum, Field, Struct class Mono.Unix.Native.MapAttribute { public MapAttribute (); public MapAttribute (string nativeType); public string NativeType {get;} public string NativeSymbolPrefix {get; set;} }
- A major revamp to make-map.exe.
The MapAttribute attribute is public so that make-map.exe can use a publically exposed API for code generation purposes which can be used by other libraries (Mono.Fuse makes use of these changes).
make-map.exe can also generate structure declarations and delegate declarations in addition to P/Invoke function declarations, allowing for a better, automated interface between C and C#.
Previously, [Map] could only be used on enumerations.
Now, [Map] can be used on classes, structures, and delegates, to create a C declaration of the C# type, suitable for P/Invoke purposes, e.g. the C# code:
[Map] struct Stat {public FilePermissions mode;}
would generate the C declaration
struct Namespace_Stat {unsigned int mode;};
The MapAttribute.NativeType property is used to specify that type conversion functions should be generated, thus:
[Map ("struct stat")] struct Stat {public FilePermissions mode;}
would generate
struct Namespace_Stat {unsigned int mode;}; int Namespace_ToStat (struct stat *from, struct Namespace_Stat *to); int Namespace_FromStat (struct Namespace_Stat *from, struct stat *to);
along with the actual implementations of Namespace_ToStat() and Namespace_FromStat().
The MapAttribute.NativeSymbolPrefix property is used to specify the C "namespace" to use:
[Map (NativeSymbolPrefix="Foo")] struct Stat {FilePermissiond mode;}
generates
struct Foo_Stat {unsigned int mode;};
This prefix is also used for the conversion functions.
(You may be wondering why NativeSymbolPrefix exists at all. This is for reasonable symbol versioning -- make-map.exe currently has a "hack" in place to rename Mono.Unix(.Native) to Mono_Posix, a hack I'd like to remove, and NativeSymbolPrefix allows the Mono.Unix.Native types to have a Mono_Posix C namespace in a reasonably general manner.)
The previously internal Mono.Unix.HeaderAttribute has been removed. The HeaderAttribute.Includes and HeaderAttribute.Defines properties have been replaced with make-map.exe command-line arguments. In particular, HeaderAttribute.Includes has been replaced with --autoconf-header, --impl-header, --impl-macro, --public-header, and --public-macro (the first three modify the generated .c file, while the latter two modify the generated .h file).
Finally, make-map.exe has been renamed and moved from mcs/class/Mono.Posix/Mono.Unix.Native/make-map.exe to mcs/tools/create-native-map/create-native-map.exe.
HOWTO
- Go to http://www.jprl.com/Projects/mono-fuse for the patches and source download.
- Apply mcs.patch to a mcs checkout, rebuild, and install.
- Apply mono.patch to a mono checkout, rebuild, and install.
- Build mono-fuse-0.1.0.tar.gz in "the standard manner" (./configure ; make ; make install).
Questions
- Is it OK for create-native-map.exe to be a .NET 2.0 app?
- How should we cope with unstable APIs which make use of native
code? The
Application Deployment Guidelines don't address
this issue, nor the related issue of what should be done with
64-bit binaries. In particular, for an AMD64 machine, which
directory layout should be used for an assembly + native lib
combo?
- What Mono seems to do, with the GAC in $prefix/lib no matter
what the architecture:
/usr/lib/mono-fuse/Mono.Fuse.dll /usr/lib64/libMonoFuseHelper.so
- Be consistent, and toss everything in @libdir@:
/usr/lib64/mono-fuse/Mono.Fuse.dll /usr/lib64/libMonoFuseHelper.so
- What Mono seems to do, with the GAC in $prefix/lib no matter
what the architecture: