Fuse Extension

Erick Gallesio
Université de Nice - Sophia Antipolis
930 route des Colles, BP 145
F-06903 Sophia Antipolis, Cedex
France
Version:
0.90
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 Using the fuse extension

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

(begin (require "stklos-fuse") (import "stklos-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.6.5 "fuse-mount" are given below:

usage: <filesystem> mountpoint `(options)

general options:
    -o opt,`(opt...)        mount options
    -h   --help            print help
    -V   --version         print version

FUSE options:
    -d   -o debug          enable debug output (implies -f)
    -f                     foreground operation
    -s                     disable multi-threaded operation

    -o allow_other         allow access to other users
    -o allow_root          allow access to root
    -o nonempty            allow mounts over non-empty file/dir
    -o default_permissions enable permission checking by kernel
    -o fsname=NAME         set filesystem name
    -o large_read          issue large read requests (2.4 only)
    -o max_read=N          set maximum size of read requests

    -o hard_remove         immediate removal (don't hide files)
    -o use_ino             let filesystem set inode numbers
    -o readdir_ino         try to fill in d_ino in readdir
    -o direct_io           use direct I/O
    -o kernel_cache        cache files in kernel
    -o `(no)auto_cache      enable caching based on modification times
    -o umask=M             set file permissions (octal)
    -o uid=N               set file owner
    -o gid=N               set file group
    -o entry_timeout=T     cache timeout for names (1.0s)
    -o negative_timeout=T  cache timeout for deleted names (0.0s)
    -o attr_timeout=T      cache timeout for attributes (1.0s)
    -o ac_attr_timeout=T   auto cache timeout for attributes (attr_timeout)
    -o intr                allow requests to be interrupted
    -o intr_signal=NUM     signal to send on interrupt (10)

    -o max_write=N         set maximum size of write requests
    -o max_readahead=N     set maximum readahead
    -o async_read          perform reads asynchronously (default)
    -o sync_read           perform reads synchronously

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

3 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.

4 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 "stklos-fuse")

(define-module HELLOFS
  (import stklos-posix stklos-fuse)
  
(define content "Hello, world!\n")

(define (hellofs-main args)
  (fuse-mount args
     :getattr (lambda (path)
                 (let ((tm (current-seconds)))
                   (cond
                     ((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)
              (cond
                ((not (equal? path "/hello"))
                 (- posix/enoent))
                ((not (equal? mode 0))
                 (- posix/eaccess))
                (else
                 0)))
     :read (lambda (fd size offset)
              (let ((len (string-length content)))
                (if (< offset len)
                    (begin
                      (if (> (+ offset size) len)
                          (set! size (- len offset)))
                      (substring content offset size))
                    0)))))

) ;; End of module HELLOFS

;; ======================================================================
;;         HELLOFS ...
;; ======================================================================
(define main (in-module HELLOFS hellofs-main))

This Html page has been produced by Skribe.
Last update Tue Jun 12 19:15:19 2007