Fuse Extension

Erick Gallesio
Université de Nice - Sophia Antipolis
930 route des Colles, BP 145
F-06903 Sophia Antipolis, Cedex
Useful Links:
STklos Home page
STklos Extensions

1 Introduction

This extension permits the implementation of virtual file systems in Scheme thanks to the FUSE library. Here is an excerpt of the FUSE site:

With FUSE it is possible to implement a fully functional filesystem in a userspace program. Features include:
  • Simple library API
  • Simple installation (no need to patch or recompile the kernel)
  • Secure implementation
  • Userspace - kernel interface is very efficient
  • Usable by non privileged users
  • Runs on Linux kernels 2.4.X and 2.6.X
  • Has proven very stable over time

2 Compiling Fuse library for Boehm GC

The Fuse package uses Posix threads. As a consequence, you need to compile the Fuse library in a special way (the fuse kernel module doesnt need to be recompiled). Since, the Boehm GC used for STklos GC needs to scan the stacks of each thread, you have to include the "gc.h" in each source file of the "lib" directory. A simple way to achieve this consists to add the line

#include <gc.h>
in the file "include/config.h".

Once the "Fuse" library is compiled and installed, the Garbage Collector is aware of all the threads of your program and everything should work. In this is not the case, you should see GC complaints about areas which are not freed in the thhread which allocate them.

3 Using the fuse extension

To use this extension you need to include the following form in your program:

(require "fuse")

This library provides only one entry point called "fuse-mount". This function takes a first parameter which is the list of the program arguments and key-list of functions used to implement the file system. The arguments recognized by the version 2.4.1 "fuse-mount" are given below:

    FUSE options:
	-d                     enable debug output (implies -f)
	-f                     foreground operation
	-s                     disable multi-threaded operation
	-r                     mount read only (equivalent to '-o ro')
	-o opt,[opt...]        mount options
	-h                     print help

    Mount options:
	default_permissions    enable permission checking
	allow_other            allow access to other users
	allow_root             allow access to root
	kernel_cache           cache files in kernel
	large_read             issue large read requests (2.4 only)
	direct_io              use direct I/O
	max_read=N             set maximum size of read requests
	hard_remove            immediate removal (don't hide files)
	debug                  enable debug output
	fsname=NAME            set filesystem name in mtab
	use_ino                let filesystem set inode numbers
	readdir_ino            try to fill in d_ino in readdir
	nonempty               allow mounts over non-empty file/dir
	umask=M                set file permissions (octal)
	uid=N                  set file owner
	gid=N                  set file group

The list of functions used to implement the file system is given in the next section

4 File system primitives

The following functions are available to implement a Scheme file system:

:getattrpathreturns a vector of 8 elements containing: mode bits, number of links, size, uid, gid, atime, mtime, ctime
:opendirpaththis is a hook for controlling directory access, returns 0 if no error
:readdirpathreturns a list of the files in the firectorry "path"
:releasedirpathThis is a hook called after readdir, returns 0 if no error
:mknodpath modecreates the file named "path" with given "mode"
:openpath mode fdopens file named "path" with given mode (O = RDONLY, 1 = WRONLY, 2 = RDWR). The value "fd" is an unique integer associated by the system to this file
:readfd size offsetreturns a string of "size" bytes starting at "offset" on "fd"
:writefd buffer size offsetwrites the first "size" characters of "buffer" at "offset" on "fd". The offset can be after the actual end of file
:releasefdThis function is called when there are no more references to the open file "fd". The return value of this function is ignored
:renamefrom torenames file "from" with name "to"
:unlinkpathremoves the file with given "path"
:linkold newcreates a link from file "old" to file "to"
:symlinkold newcreates a symbolic link from file "old" to file "to"
:readlinkpathreturns the file that the symbolic link "path" point to
:mkdirpathcreates directory "path"
:rmdirpathremoves directory "path"
:chmodpath modechange the mode of file "path" to "mode"
:chownpath uid gidchanges the owner of "path" to "uid" and "gid"
:utimepath atime mtimechanges the access and modification time of file "path" to "atime" and "mtime"
:truncatepath sizechanges the size of "path" to "size"
:flushpath fdflushes cached data on file "fd"
:fsyncpath datasync fdif the "datasync" parameter is non-zero, then only the user data should be flushed, not the meta data
:fsyncdirpath datasyncif the "datasync" parameter is non-zero, then only the user data should be flushed, not the meta data
:initThis is a hook called when the file system is mounted. It can return a value which will be used when the file systeme is unmounted
:destroydataThis is a hook called when the file system is unmounted. Its parameter is the return value of the "init" call.

5 The hellofs filesystem

The following example is a very simple (even simplistic) file system written in STklos. This is a file system which contains only a file named "hello". You cannot do a lot with this file system and most actions produce errors. To mount the file system you can for instance type:

$ hellofs -f ~/fuse

This will mount the hellofs on the (already existing and empty) "~/fuse" directory. To unmount this file system, you can do:

$ fusermount -u ~/fuse

A more complete and realistic example is provided in the "examples" directory.

(require "posix")
(require "fuse")

(define content "Hello, world!\n")

(define (main args)
  (fuse-mount args
     :getattr (lambda (path)
                 (let ((tm (current-time)))
                     ((equal? path "/")
                     (vector (+ posix/IFDIR #o755)   ;; mode
                             2                       ;; links
                             123                     ;; size (why not this one?)
                             (posix-user-id)         ;; uid
                             (posix-group-id)        ;; gid
                             tm tm tm))              ;; atime, mtime, ctime
                    ((equal? path "/hello")
                     (vector (+ posix/IFREG #o440)   ;; mode
                             1                       ;; links
                             (string-length content) ;; size
                             (posix-user-id)         ;; uid
                             (posix-group-id)        ;; gid
                             tm tm tm))              ;; atime, mtime, ctime
                     (else  (- posix/ENOENT)))))

     :readdir (lambda (path)
                 (if (equal? path "/")
                     '("." ".." "hello")
                     (- posix/ENOENT)))
     :open (lambda (path mode fd)
                ((not (equal? path "/hello"))
                 (- posix/ENOENT))
                ((not (equal? mode 0))
                 (- posix/EACCESS))
     :read (lambda (fd size offset)
              (let ((len (string-length content)))
                (if (< offset len)
                      (if (> (+ offset size) len)
                          (set! size (- len offset)))
                      (substring content offset size))

This Html page has been produced by Skribe.
Last update Sun Jan 15 11:25:40 2006