fuse

  1. fuse
    1. Description
      1. Warning
      2. Requirements
      3. Platform Notes
      4. Runtime Structure
    2. API
    3. Author
    4. License

Description

A FUSE interface.

Installation requires the libfuse library and headers (API version 26) and a CHICKEN version 4.8.2 or newer.

The source for this extension is available at Bitbucket.

Warning

This extension is not yet stable. It may be broken in ways both subtle and not so subtle, and its interface is subject to change without warning.

Requirements

Platform Notes

This extension is only officially supported on Linux and OpenBSD. It has also been installed successfully on FreeBSD and Mac OS X, but tested far less thoroughly on those platforms.

On OpenBSD, each filesystem's main loop is single-threaded, the ioctl: callback is not available, and stopping a filesystem via filesystem-stop! is significantly slower than stopping it via umount(2).

Runtime Structure

Each filesystem is executed in a separate native thread that communicates with the (single, shared) CHICKEN runtime via Unix pipe, per concurrent-native-callbacks. More than one filesystem can be run at once, but FUSE operations are synchronous across all filesystems so long-running callbacks should be avoided.

More importantly, care must be taken not to deadlock the Scheme runtime by requesting a filesystem operation from within CHICKEN that itself requires a response from CHICKEN, for example by accessing a file that's part of a running filesystem. The easiest way to avoid this situation is to run each filesystem in a more-or-less dedicated OS-level process whose sole responsibility is to service FUSE requests.

API

[record] filesystem
[procedure] (filesystem? object) -> boolean

A filesystem is an opaque, defstruct-style record type representing a set of FUSE filesystem operations.

[procedure] (make-filesystem #!key <operation> ...) -> filesystem

Create a FUSE filesystem.

The keyword arguments to make-filesystem specify the resulting filesystem's callback procedures. Each <operation> should be one the following:

access:
(procedure path mode) -> value
chmod:
(procedure path mode) -> value
chown:
(procedure uid gid) -> value
create:
(procedure path mode) -> (or handle #f)
destroy:
(procedure) -> void
flush:
(procedure path) -> value
fsync:
(procedure path) -> value
getattr:
(procedure path) -> (or (vector mode nlink uid gid size atime ctime mtime) #f)
init:
(procedure) -> void
ioctl:
(procedure path int pointer) -> value
link:
(procedure path path) -> value
mkdir:
(procedure path mode) -> value
mknod:
(procedure path mode) -> value
open:
(procedure path mode) -> (or handle #f)
readdir:
(procedure path) -> (or (list path ...) value)
readlink:
(procedure path) -> (or path #f)
read:
(procedure handle size offset) -> (or size string value)
release:
(procedure handle) -> value
rename:
(procedure path path) -> value
rmdir:
(procedure path) -> value
statfs:
(procedure path) -> (or (vector bsize blocks bfree bavail files ffree namemax) #f)
symlink:
(procedure path path) -> value
truncate:
(procedure path) -> value
unlink:
(procedure path) -> value
utimens:
(procedure path atime mtime) -> value
write:
(procedure handle string offset) -> (or size string value)

offset, size, mode, nlink, uid, gid, size, atime, ctime and mtime are numeric values with the obvious meanings. A path is a pathname string. bsize, blocks, bfree, bavail, files, ffree and namemax are positive numeric values corresponding to the statvfs(2) struct members of the same names.

A value may be any Scheme object and indicates whether the filesystem operation was successful. When #f, the filesystem will indicate a nonexistent file (ENOENT); any other value indicates success. Callbacks should signal other types of failures by raising an appropriate errno(3) value. For example, to signal insufficient permissions, an access: operation should (raise errno/perm).

A handle may be any Scheme object and represents a file handle. When returned as the result of an open: or create:callback, this value is provided to that file's subsequent read:, write: and release: operations. Note that this object is evicted into static memory (via object-evict) until just before release:, so it is more efficient (as well as memory-safe) to use simple values as file handles; the same caveats that apply to object-evict apply here. release: is guaranteed to be called once for every successful open:, while read: and write: should be prepared to be called multiple times with diverse offset values.

[procedure] (filesystem-start! path filesystem) -> (or #f undefined)

Start filesystem at the given path.

path should be a pathname string indicating an empty directory.

On successful startup, the filesystem is mounted, its init: callback is executed, any threads waiting for the filesystem to start are unblocked, and a non-#f value is returned. On failure, #f is returned immediately.

filesystem-start! does not wait for filesystem initialization before returning. To block until the filesystem is becomes available, use filesystem-wait!.

The effective exception handler for the filesystem's operations at path is that of the call to filesystem-start!'s dynamic environment, and must always return with a suitable errno(3) integer value. Failure to do so may result in orphaned mounts, infinite loops, and locusts.

[procedure] (filesystem-stop! path filesystem) -> undefined

Stop filesystem at the given path.

path should be a pathname string and must exactly match the value provided to filesystem-start! when the filesystem was started (according to string=?).

If the given filesystem isn't currently mounted at path, this procedure is a noop. Otherwise, the filesystem is unmounted, any threads waiting for the filesystem to stop are unblocked, and its destroy: callback is executed.

filesystem-stop! does not wait for filesystem shutdown before returning. To block until the filesystem is fully stopped, use filesystem-wait!.

[procedure] (filesystem-wait! path filesystem [status]) -> undefined

Block until filesystem is started or stopped at path.

path should be a pathname string and must exactly match the value provided to filesystem-start! when the filesystem was started (according to string=?).

The optional status argument should be a symbol indicating the filesystem state for which to wait, either started or stopped. By default, (eq? status stopped).

[procedure] (filesystem-running? path filesystem) -> boolean

Determine whether filesystem is currently running at path.

path should be a pathname string and must exactly match the value provided to filesystem-start! when the filesystem was started (according to string=?).

[constant] file/fifo
[constant] file/chr
[constant] file/blk
[constant] file/reg
[constant] file/dir
[constant] file/lnk
[constant] file/sock

These values correspond to the S_IF* flags specified by stat(2). They're not FUSE-specific, but may be useful when defining getattr: callbacks.

Author

Evan Hanson

Credit to Ivan Raikov for initial work on libfuse bindings and inspiration for the keyword-based API.

Credit to Jörg Wittenberger for lots of helpful bug hunting.

License

Copyright (c) 2013-2014, 3-Clause BSD.