[[tags: egg]]
== fuse
[[toc:]]
=== Description
A [[http://fuse.sourceforge.net/|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
[[https://bitbucket.org/evhan/chicken-fuse|Bitbucket]].
==== Warning
'''This extension is not yet stable.''' Its interface is subject to
change, and I'd appreciate feedback and suggestions regarding the API.
==== Requirements
* [[/egg/concurrent-native-callbacks|concurrent-native-callbacks]]
* [[/egg/foreigners|foreigners]]
* [[/egg/matchable|matchable]]
==== Platform Notes
'''This extension is only officially supported on Linux.''' It has also
been installed successfully on OpenBSD, FreeBSD and Mac OS X, but tested
far less thoroughly on those platforms.
==== Architecture
Each filesystem is executed in a separate native thread that
communicates with the (single, shared) CHICKEN runtime via Unix pipe,
per ([[/egg/concurrent-native-callbacks|concurrent-native-callbacks]]).
More than one filesystem can be run at a time, 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 will
itself require a response from CHICKEN, for example by accessing a file
that's part of a running FUSE filesystem. The easiest way to do this is
to run each filesystem in a dedicated OS-level process whose sole
responsibility is to service FUSE requests.
On OpenBSD, each filesystem's FUSE loop is single-threaded.
=== API
filesystem
(filesystem? object) -> boolean
A {{filesystem}} is an opaque, {{defstruct}}-style record type
representing a set of FUSE filesystem operations.
(make-filesystem #!key ...) -> filesystem
Create a FUSE filesystem.
The keyword arguments to {{make-filesystem}} specify the resulting
{{filesystem}}'s callback procedures. Each {{}} 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}}
; {{getattr:}}: {{(procedure path) -> (or (vector mode nlink uid gid size atime ctime mtime) #f)}}
; {{init:}}: {{(procedure) -> void}}
; {{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 resulting {{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.
(filesystem-start! path filesystem) -> 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 on the filesystem (via
{{filesystem-wait!}}) 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.
(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, it is unmounted, its '''{{destroy:}}'''
callback is executed, and any threads waiting on the filesystem (via
{{filesystem-wait!}}) are unblocked.
(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)}}.
(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=?}}).
file/fifo
file/chr
file/blk
file/reg
file/dir
file/lnk
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
[[/users/evan-hanson|Evan Hanson]]
Credit to [[/users/ivan-raikov|Ivan Raikov]] for initial work on libfuse
bindings and inspiration for the keyword-based API.
Credit to [[/users/jorg-wittenberger|Jörg Wittenberger]] for lots of
helpful bug hunting.
=== License
Copyright (c) 2013, 3-Clause BSD.