Tales from a Core File

Search
Close this search box.

Category: Filesystems

One of the things I’ve worked with a decent amount is prototyping filesystems in FUSE. For the uninitated, FUSE allows you to implement the traditional VFS API in userspace. Of course, running your filesystem in userspace comes at an obvious performance cost. Serializing to and from userland is not the cheapest operation. However, a lot of filesystems that are not native to a platform are being written in FUSE. For example, the stock Ubuntu ships with NTFS-3G, an NTFS implementation in FUSE. Similarly, if you want ext-2 support on OS X, there is a FUSE filesystem that supports this.

However, those are some of the more mundane uses of FUSE. Rather, one could look
at FUSE as a way to about and reimplement a lot of the ideas from Plan 9 from Bell Labs. Plan 9 really added a lot of interesting things to the filesystem namesapce. In particular, take a look at exportfs, ftpfs, /proc, etc. The limit was very broad, the goal was to try and provide a file interface to a lot of different things. While in some cases, this may have not been the right way to go, it provides a fascinating look into where the original Unix guys thought to go next. You should definitely read up more about Plan 9 here.

While obviously, the means of implementation is vastly different, what you can do with fuse, is give anything really a file interface or filesystem that makes sense. Perhaps one of the more useful FUSE-based filesystems is sshfs, which is similar to the ftpfs presented above. This allows you to effectively mount a system that you have ssh access to and brose it as per any other filesystem. There are a lot of interesting things you could present as a file system interface; however, there is another thing we can do that is more interesting.

Now, while there are plethora of such systems and other things you can represent, there is a more interesting thing you can do. Rather than extending the namespace, change what happens when you access some portion of the namespace. This can be done thanks to a new round of system calls that were defined and published as part of a 2008 standard from the Open Group. These calls contain functions like openat, unlinkat, mkdirat, fstatat, etc. The main difference between these functions and their non-at forefathers are that they all take a file descriptor and a path relative to that file descriptor to determine what to modify.

With these, what you can do is open a file descriptor to the mount point you want to mount over, before you call into fuse. This when fuse mounts over that part of the filesystem namespace, you can still access it. This means that you now hvae the ability to interpose on all of the various system calls that are coming into this filesystem and present all the data that the underlying filesystems have always had. Your main method might look something like this:

static int rfd;

int
main(int argc, char *argv[])
{
        /* Throw in some sanity checking, expect mountpoint as last arg */
        rfd = open(argv[argc-1], O_RDONLY);
        if (rfd < 0) {
                fprintf(stderr, "Unable to open mountpoint - %s\n",
                    strerror(errno));
                return (1);
        }

        return (fuse_main(argc, argv, &fuseops, NULL));
}

This opens up a lot of possibilities for what we can do with our filesystem. Of course, not quite all of it makes sense to be done in this form. That is something that we’ll bring up and talk about in more detail later on.

Recent Posts

September 27, 2019
September 6, 2019
October 1, 2014

Archives