((section 2 "Outdated egg!" (p "This is an egg for CHICKEN 4, the unsupported old release.  You're almost certainly looking for " (int-link "/eggref/5/fuse" "the CHICKEN 5 version of this egg") ", if it exists.") (p "If it does not exist, there may be equivalent functionality provided by another egg; have a look at the " (link "https://wiki.call-cc.org/chicken-projects/egg-index-5.html" "egg index") ". Otherwise, please consider porting this egg to the current version of CHICKEN.") (tags "egg")) (section 2 "fuse" (toc)) (section 2 "Description" (p "A " (link "https://fuse.sourceforge.net/" "FUSE") " interface.") (p "Installation requires the libfuse library and headers (API version 26) and a CHICKEN version 4.8.2 or newer.") (p "The source for this extension is available " (link "https://git.foldling.org/chicken-fuse.git" "here") ".") (p (b "This extension's interface is subject to change without notice or deprecation period.")) (section 3 "Requirements" (ul (li (int-link "concurrent-native-callbacks")) (li (int-link "foreigners")) (li (int-link "matchable")))) (section 3 "Platform Notes" (p (b "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.") (p "On OpenBSD, each filesystem's main loop is single-threaded and the " (b (tt "{ioctl:") "}") " callback is not available.")) (section 3 "Runtime Structure" (p "Each filesystem is executed in a separate native thread that communicates with the (single, shared) CHICKEN runtime via Unix pipe, per " (int-link "/eggref/4/concurrent-native-callbacks" "concurrent-native-callbacks") ". Multiple filesystems can be run at once from within a single process, but FUSE operations are synchronous across all filesystems so long-running callbacks should be avoided.") (p "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."))) (section 2 "API" (def (sig (record "filesystem" (id filesystem)) (procedure "(filesystem? object) -> boolean" (id filesystem?))) (p "A " (tt "filesystem") " is an opaque, " (tt "defstruct") "-style record type representing a set of FUSE filesystem operations.")) (def (sig (procedure "(make-filesystem #!key <operation> ...) -> filesystem" (id make-filesystem))) (p "Create a FUSE filesystem.") (p "The keyword arguments to " (tt "make-filesystem") " specify the resulting " (tt "filesystem") "'s callback procedures. Each " (tt "<operation>") " should be one the following:") (dl (dt (tt "access:")) (dd (tt "(procedure path mode) -> value")) (dt (tt "chmod:")) (dd (tt "(procedure path mode) -> value")) (dt (tt "chown:")) (dd (tt "(procedure uid gid) -> value")) (dt (tt "create:")) (dd (tt "(procedure path mode) -> (or handle false)")) (dt (tt "destroy:")) (dd (tt "(procedure) -> void")) (dt (tt "flush:")) (dd (tt "(procedure path) -> value")) (dt (tt "fsync:")) (dd (tt "(procedure path) -> value")) (dt (tt "getattr:")) (dd (tt "(procedure path) -> (or (vector mode nlink uid gid size atime ctime mtime) false)")) (dt (tt "init:")) (dd (tt "(procedure) -> void")) (dt (tt "ioctl:")) (dd (tt "(procedure path int pointer) -> value")) (dt (tt "link:")) (dd (tt "(procedure path path) -> value")) (dt (tt "mkdir:")) (dd (tt "(procedure path mode) -> value")) (dt (tt "mknod:")) (dd (tt "(procedure path mode) -> value")) (dt (tt "open:")) (dd (tt "(procedure path mode) -> (or handle false)")) (dt (tt "readdir:")) (dd (tt "(procedure path) -> (or (list path ...) value)")) (dt (tt "readlink:")) (dd (tt "(procedure path) -> (or path false)")) (dt (tt "read:")) (dd (tt "(procedure handle size offset) -> (or size string value)")) (dt (tt "release:")) (dd (tt "(procedure handle) -> value")) (dt (tt "rename:")) (dd (tt "(procedure path path) -> value")) (dt (tt "rmdir:")) (dd (tt "(procedure path) -> value")) (dt (tt "statfs:")) (dd (tt "(procedure path) -> (or (vector bsize blocks bfree bavail files ffree namemax) false)")) (dt (tt "symlink:")) (dd (tt "(procedure path path) -> value")) (dt (tt "truncate:")) (dd (tt "(procedure path) -> value")) (dt (tt "unlink:")) (dd (tt "(procedure path) -> value")) (dt (tt "utimens:")) (dd (tt "(procedure path atime mtime) -> value")) (dt (tt "write:")) (dd (tt "(procedure handle string offset) -> (or size string value)"))) (p (tt "offset") ", " (tt "size") ", " (tt "mode") ", " (tt "nlink") ", " (tt "uid") ", " (tt "gid") ", " (tt "size") ", " (tt "atime") ", " (tt "ctime") " and " (tt "mtime") " are numeric values with the obvious meanings. A " (tt "path") " is a pathname string. " (tt "bsize") ", " (tt "blocks") ", " (tt "bfree") ", " (tt "bavail") ", " (tt "files") ", " (tt "ffree") " and " (tt "namemax") " are positive numeric values corresponding to the " (tt "statvfs(2)") " struct members of the same names.") (p "A " (tt "value") " may be any Scheme object and indicates whether the filesystem operation was successful. When false, the filesystem will indicate a nonexistent file (" (tt "ENOENT") "); any other value indicates success. Callbacks should signal other types of failures by raising an appropriate " (tt "errno(3)") " value. For example, to signal insufficient permissions, an " (b (tt "access:")) " operation should " (tt "(raise errno/perm)") ".") (p "A " (tt "handle") " may be any Scheme object and represents a file handle. When returned as the result of an " (b (tt "open:")) " or " (b (tt "create:")) "callback, this value is provided to that file's subsequent " (b (tt "read:")) ", " (b (tt "write:")) " and " (b (tt "release:")) " operations. " (b (tt "release:")) " is guaranteed to be called once for every successful " (b (tt "open:")) " or " (b (tt "create:")) ", while " (b (tt "read:")) " and " (b (tt "write:")) " should be prepared to be called multiple times with diverse " (tt "offset") " values.")) (def (sig (procedure "(filesystem-start! path filesystem) -> (or false undefined)" (id filesystem-start!))) (p "Start " (tt "filesystem") " at the given " (tt "path") ".") (p (tt "path") " should be a pathname string indicating an empty directory.") (p "On successful startup, the filesystem is mounted, its " (b (tt "init:")) " callback is executed, any threads waiting for the filesystem to start are unblocked, and a non-false value is returned. On failure, " (tt "#f") " is returned immediately.") (p (tt "filesystem-start!") " does not wait for filesystem initialization before returning. To block until the filesystem becomes available, use " (tt "filesystem-wait!") ".") (p "The effective exception handler for the filesystem's operations at " (tt "path") " is that of the call to " (tt "filesystem-start!") "'s dynamic environment, and must " (i "always") " return with a suitable " (tt "errno(3)") " integer value. Failure to do so may result in orphaned mounts, infinite loops, and locusts.")) (def (sig (procedure "(filesystem-stop! path filesystem) -> undefined" (id filesystem-stop!))) (p "Stop " (tt "filesystem") " at the given " (tt "path") ".") (p (tt "path") " should be a pathname string and must exactly match the value provided to " (tt "filesystem-start!") " when the " (tt "filesystem") " was started (according to " (tt "string=?") ").") (p "If the given " (tt "filesystem") " isn't currently mounted at " (tt "path") ", this procedure is a noop. Otherwise, the filesystem is unmounted, any threads waiting for the filesystem to stop are unblocked, and its " (b (tt "destroy:")) " callback is executed.") (p (tt "filesystem-stop!") " does not wait for filesystem shutdown before returning. To block until the filesystem is fully stopped, use " (tt "filesystem-wait!") ".")) (def (sig (procedure "(filesystem-wait! path filesystem [status]) -> undefined" (id filesystem-wait!))) (p "Block until " (tt "filesystem") " is started or stopped at " (tt "path") ".") (p (tt "path") " should be a pathname string and must exactly match the value provided to " (tt "filesystem-start!") " when the " (tt "filesystem") " was started (according to " (tt "string=?") ").") (p "The optional " (tt "status") " argument should be a symbol indicating the filesystem state for which to wait, either " (tt "started") " or " (tt "stopped") ". If not specified, " (tt "stopped") " is used.")) (def (sig (procedure "(filesystem-running? path filesystem) -> boolean" (id filesystem-running?))) (p "Determine whether " (tt "filesystem") " is currently running at " (tt "path") ".") (p (tt "path") " should be a pathname string and must exactly match the value provided to " (tt "filesystem-start!") " when the " (tt "filesystem") " was started (according to " (tt "string=?") ").")) (def (sig (constant "file/fifo" (id file/fifo)) (constant "file/chr" (id file/chr)) (constant "file/blk" (id file/blk)) (constant "file/reg" (id file/reg)) (constant "file/dir" (id file/dir)) (constant "file/lnk" (id file/lnk)) (constant "file/sock" (id file/sock))) (p "These values correspond to the " (tt "S_IF*") " flags specified by " (tt "stat(2)") ". They're not FUSE-specific, but may be useful when defining " (b (tt "getattr:")) " callbacks."))) (section 2 "Examples" (p "Usage examples can be found in the project's " (link "https://git.foldling.org/chicken-fuse.git/src/examples/" "examples") " directory.")) (section 2 "Author" (p (int-link "/users/evan-hanson" "Evan Hanson")) (p "Credit to " (int-link "/users/ivan-raikov" "Ivan Raikov") " for initial work on libfuse bindings and inspiration for the keyword-based API.") (p "Credit to " (int-link "/users/jorg-wittenberger" "Jörg Wittenberger") " for lots of helpful bug hunting.")) (section 2 "License" (p "3-Clause BSD")))