golffs - Implement a FUSE filesystem

3

1

Implement a FUSE filesystem in minimum bytes.

Your program must do three things:

  • Mounting the virtual filesystem to a directory
  • Handling requests to enumerate files in that directory (showing one file)
  • Handling requests to read the file in that directory

Rules:

  • ls (a system program, you don't need to implement it) must show hello.txt file (other unrelated files can also be visible, as well as errors may be printed). hello.txt must be exact, 68 65 6c 6c 6f 2e 74 78 74 in hex).
  • cat (a system program) pointed to the file should output Hello, world. (punctuation, case and end of lines may differ. A bunch of zero bytes can also slip unnoticed). It can also be cat m/hello.txt; echo if there is no trailing newline.
  • It must be actual FUSE filesystem, not just a regular file created by your program. For example, freezing your program should freeze the hello.txt file (cat and ls would hang). Creating a socket instead of mounting FUSE filesystem isn't an allowed workaround. Your mounted filesystem should be visible in /proc/mounts (or platform analogue).
  • For 200 bytes penalty, you may depend on libfuse or use it's header files (or depend on the appropriate FUSE binding in some other language)
  • Detaching is optional
  • You program may be platform-specific. Somebody else on that platform should check and write a comment that it works (or not)
  • Your program can't be a kernel module (fuse.ko is the involved kernel module on Linux and is considered a part of environtment, not your program)
  • Your program can access /dev/fuse (or platform analogue) directly. It may require root access (or changing permissions) for it if needed. It can also rely on fusermount for the mounting step (like the example below). Whether using fusermount for mounting carry penalty or not I have not decided yet (because of I don't know implementation details enough): if the rest feels like talking to kernel directly then it should not be a problem.
  • Additional compilation and invocation parameters are added to the score. A directory to be mounted may be specified as a free (non-penalty) parameter or may be just hardcoded (like m)

Example (directly based on official hello.c):

#define FUSE_USE_VERSION 26

#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

static const char *hello_str = "Hello, world\n";
static const char *hello_path = "/hello.txt";

static int hello_getattr(const char *path, struct stat *stbuf)
{
    int res = 0;

    memset(stbuf, 0, sizeof(struct stat));
    if (strcmp(path, "/") == 0) {
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 2;
    } else if (strcmp(path, hello_path) == 0) {
        stbuf->st_mode = S_IFREG | 0444;
        stbuf->st_nlink = 1;
        stbuf->st_size = strlen(hello_str);
    }
    return res;
}

static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
             off_t offset, struct fuse_file_info *fi)
{
    filler(buf, hello_path + 1, NULL, 0);
    return 0;
}

static int hello_open(const char *path, struct fuse_file_info *fi)
{
    return 0;
}

static int hello_read(const char *path, char *buf, size_t size, off_t offset,
              struct fuse_file_info *fi)
{
    size_t len;
    (void) fi;
    if(strcmp(path, hello_path) != 0)
        return -ENOENT;

    len = strlen(hello_str);
    if (offset < len) {
        if (offset + size > len)
            size = len - offset;
        memcpy(buf, hello_str + offset, size);
    } else
        size = 0;

    return size;
}

static struct fuse_operations hello_oper = {
    .getattr    = hello_getattr,
    .readdir    = hello_readdir,
    .open        = hello_open,
    .read        = hello_read,
};

int main(int argc, char *argv[])
{
    return fuse_main(argc, argv, &hello_oper, NULL);
}
$ gcc -D_FILE_OFFSET_BITS=64 golffs.c -lfuse -o golffs
$ ./golffs  m     # uses fusermount under the hood
$ ls m
hello.txt
$ cat m/hello.txt 
Hello, world
$ fusermount -u m

Score of the example: 1974 + 29 (compilation parameters) + 0 (invocation parameters) + 200 (penalty) = 2203

Vi.

Posted 2015-11-29T19:49:53.487

Reputation: 2 644

Question was closed 2015-11-30T05:09:43.617

1"hello.txt must be exactly this hex" seems to collide with the next line "cat should output Hello, world. **punctuation, EOL and case may differ**" – cat – 2015-11-29T20:56:23.733

1The first is about the virtual file name, the second is about virtual file content. The former is exact, the latter should match /^hello,?\s?world[.!;?]?\r?\n$?/im (for example). – Vi. – 2015-11-29T20:58:00.210

1Ohh, okay. I like this challenge because it gives real, non-golfing languages a chance; it will be a while before I have something that works. – cat – 2015-11-29T20:59:53.717

Wait, why are cat and ls builtins for the filesystem? shouldn't they be standalone binaries? – cat – 2015-11-29T21:00:55.910

1cat and ls are assumed to be system binaries. They are used to test the filesystem. They are clarified because of for poorly behaving filesystems, using other programs (like dd and find) can lead to different results (due to different syscalls or reaction to them). – Vi. – 2015-11-29T21:02:16.187

1So we get a 200 byte penalty if we use the actual library, which is a kernel module (because in order for a filesystem to show up in /proc/mounts or have udev even sneeze at it, it needs to have some sort of kernel bus, specifically a module) but we also aren't allowed to make a kernel module, so we have to use the existing library ( on *ix, at least ) which means for Mac and GNU/Linux the 200 byte penalty is unavoidable. – cat – 2015-11-29T21:11:04.063

1The fuse.ko kernel module is considered part of the system (like kernel or C library). The libfuse.so library is considered part of your program. To avoid penalty, you should implement it on low level (on the level the libfuse.so itself is implemented), i.e. open /dev/fuse and use it. fusermount program may be used to get access to /dev/fuse without requiring administrator access (assuming it does not take part in filesystem activity after starting up). – Vi. – 2015-11-29T21:14:47.487

Answers

3

Perl, 1348 998 846 748 714 683 642 575 520 + strlen("perl -mFuse") + 200 penalty for Fuse.pm = 731 bytes

Was interesting diversion from usual golf challenges. Started life as a version of https://metacpan.org/source/DPATES/Fuse-0.16/examples/example.pl before getting stripped down.

%k=('hello.txt'=>{});sub f{$_=shift;s,^/,,;return$_}Fuse::main(mountpoint=>$ARGV[0],getattr=>sub{$c=f(shift);$c=~s,^/,,;$c=~s/^$/\./;return(exists($k{$c})?0:-2,0,$c eq'.'?16877:33261,1,0,0,0,12,0,0,0,1024,0)},getdir=>sub{return(keys%k),0},open=>sub{return(0,[rand()])},statfs=>sub{return0},read=>sub{$c=f(shift);($x,$z,$h)=@_;return-2 unless exists($k{$c});if(!exists($k{$c})){$u=fuse_get_context();return sprintf("pid=0x%08x uid=0x%08x gid=0x%08x\n",@$u{'pid','uid','gid'})}return$z==12?0:substr("Hello, world",$z,$x)})

Long lines, but if presented via fold, appears as below:

# fold foo.pl
%k=('hello.txt'=>{});sub f{$_=shift;s,^/,,;return$_}Fuse::main(mountpoint=>$ARGV
[0],getattr=>sub{$c=f(shift);$c=~s,^/,,;$c=~s/^$/\./;return(exists($k{$c})?0:-2,
0,$c eq'.'?16877:33261,1,0,0,0,12,0,0,0,1024,0)},getdir=>sub{return(keys%k),0},o
pen=>sub{return(0,[rand()])},statfs=>sub{return0},read=>sub{$c=f(shift);($x,$z,$
h)=@_;return-2 unless exists($k{$c});if(!exists($k{$c})){$u=fuse_get_context();r
eturn sprintf("pid=0x%08x uid=0x%08x gid=0x%08x\n",@$u{'pid','uid','gid'})}retur
n$z==12?0:substr("Hello, world",$z,$x)})
#

Example use:

# perl -mFuse foo.pl m &
[1] 6410
# ls -ld m
drwxr-xr-x 1 root root 0 Jan  1  1970 m
# cd m
# ls -l
total 0
-rwxr-xr-x 1 root root 12 Jan  1  1970 hello.txt
# cat hello.txt
Hello, world#

steve

Posted 2015-11-29T19:49:53.487

Reputation: 2 276

1Works (although there is lot of room for golfing). Shebang (#!/usr/bin/perl) can be moved to invocation perl foo.pl m. – Vi. – 2015-11-29T23:36:41.653

1

For more interesting diversions, see my other challenges.

– Vi. – 2015-11-29T23:44:23.803

Updated source also works, score = 575 + strlen("perl -mFuse") + 200 = 786. – Vi. – 2015-12-01T02:07:43.107

1Are you aware that you don't really need to provide . and .. entries? – Vi. – 2015-12-01T02:12:50.647

Good point, shaved a few more bytes – steve – 2015-12-02T22:21:20.850