8.16.0.1
A Racket library for implementing Filesystems in Userspace (FUSE).
This package implements communication routines for interfacing with the Linux FUSE kernel driver.
Client programs can implement a userspace filesystem by providing a collection of functions that
implement filesystem operations. Currently, racket-fuse depends on the libfuse C library to mount
a FUSE filesystem. However, the communication protocol with the kernel and filesystem API does
not reuse libfuse functionality, and this dependency will be removed in a future release.
See Example for an example FUSE filesystem.
1 Creating a filesystem🔗ℹ
To mount a userspace filesystem, call mount-filesystem with a filesystem struct created
by make-filesystem. The mount-filesystem procedure will not return until the filesystem is
umounted.
To umount a userspace filesystem, invoke the program fusermount:
fusermount -u /path/to/filesystem/mount. In exceptional circumstances (i.e., when using a
buggy userspace filesystem), you may need to force the filesystem to unmount using the
umount utility.
Mounts the userspace filesystem filesystem at path with FUSE options options.
Does not return until the filesystem is unmounted. See fuse(8) for a list of permitted options.
(make-filesystem operation ...)
|
|
operation | | = | | #:init init | | | | | | #:destroy destroy | | | | | | #:lookup lookup | | | | | | #:forget forget | | | | | | #:getattr getattr | | | | | | #:setattr setattr | | | | | | #:readlink readlink | | | | | | #:mknod mknod | | | | | | #:mkdir mkdir | | | | | | #:unlink unlink | | | | | | #:rmdir rmdir | | | | | | #:symlink symlink | | | | | | #:rename rename | | | | | | #:link link | | | | | | #:access access | | | | | | #:open open | | | | | | #:create create | | | | | | #:read read | | | | | | #:write write | | | | | | #:flush flush | | | | | | #:release release | | | | | | #:fsync fsync | | | | | | #:opendir opendir | | | | | | #:readdir readdir | | | | | | #:releasedir releasedir | | | | | | #:fsyncdir fsyncdir | | | | | | #:statfs statfs | | | | | | #:setxattr setxattr | | | | | | #:getxattr getxattr | | | | | | #:listxattr listxattr | | | | | | #:removexattr removexattr | | | | | | #:getlk getlk | | | | | | #:setlk setlk | | | | | | #:bmap bmap | | | | | | #:fallocate fallocate |
|
Creates a
filesystem? struct for use with
mount-filesystem. Each operation specifies
a procedure that will be invoked when the corresponding filesystem operation is requested by the operating
system. The default implementation of each operation does nothing and replies with the error
'ENOSYS.
The specification for the procedure required for each operation is given below. Most hooks provide
two response procedures: one marked
#:reply to indicate success, and one marked
#:error
to indicate failure. The response specifications are given below, following descriptions of each operation.
A filesystem can defer responding to an operation by spawning a thread which will call
a response procedure when the operation completes. Each operation should respond only once by calling the
appropriate procedure.
Filesystem procedures can access information about the process making a request using
the parameters request-pid, request-uid, request-gid.
The following documentation was adapted from the reference low level FUSE API, available at
https://github.com/libfuse/libfuse/blob/master/include/fuse_lowlevel.h.
Called before any other filesystem operation. Use this to initialize any state required by the filesystem.
Return true on success or an errno symbol on error.
Called on filesystem exit.
Lookup the entry name in the directory with inode nodeid, returning its attributes.
Each invocation of a reply-entry reply-create response increments the lookup
count for the corresponding inode by one. Each time a reference to the inode is removed from the kernel’s
cache, the lookup count is decremented. Subsequently, forget on the FUSE filesystem is invoked
with nlookup set to the number of lookups to forget.
While an inode has a non-zero lookup count, the filesystem may receive requests to access the node (even following
calls to unlink, rmdir, etc.). A filesystem should retain the underlying resources associated with the inode until
the lookup count is zero (because a sufficient number of forget messages have been received).
If a file has been exported over NFS, the resources should be retained even longer. See the libfuse documentation
for more details.
Operation forget may not be invoked on all active indoes when the filesystem is unmounted.
Get the file attributes of nodeid. The info argument may in future contain
information provided by the filesystem when opening the node, but currently
is always #f.
Set the file attributes of nodeid. Each argument is
either #f if the corresponding attribute should not
be modified, or contains the new value for that attribute.
If setattr was invoked by the ftruncate()
system call (and the kernel is version 2.6.15 or later),
info will contain the value set by the filesystem’s open procedure.
Otherwise, it will be set to 0.
On success, the filesystem should respond with the updated file attributes.
Read the symbolic link at inode nodeid.
Create a regular file, character device, fifo, or socket in the
directory nodeid with name name. The desired filetype
is given by kind along with the desired mode and
current umask. If the filetype is a device type, the desired
device number is given by rdev.
Create a directory with name name in directory nodeid.
The requested file mode and current process umask are given by mode
and umask.
Remove the file with name name from directory nodeid.
If the file’s lookup count is non-zero, the file system should retain
any resources required to respond to operations on the file until the
lookup count reaches zero. (See forget).
Remove the directory with the name name from directory
nodeid. If the directory’s lookup count is non-zero, the file
system should retain any resources required to respond to operations on
the directory until the lookup count reaches zero. (See forget).
Create a symbolic link in directory nodeid with name name
and content link.
Move the directory entry name in nodeid to
newname in newparent. If an entry newname in
newparent already exists, it should be atomically replaced.
If the replaced entry’s lookup count is non-zero, the file
system should retain any resources required to respond to operations on
the entry until the lookup count reaches zero. (See forget).
Create a hard link to nodeid in the directory newparent
with name newname.
Check file access permissions. If the ’default_permissions’ mount option is
given, this method is not called. (See the
libfuse documentation
for important security information related to FUSE file access permissions).
Open the file at inode nodeid. Argument flags records the open flags
passed to the originating system call, except for 'O_CREAT, O_EXCL,
O_NOCTTY, and O_TRUNC, which are handled by FUSE by invoking
create instead of open. When responding to an open operation,
the filesystem can specify a value that will be passed to the filesystem as the #:info
associated with an operation on the opened file.
Create and open a file in the directory nodeid with name name.
Argument flags lists the flags passed to the originating open system call.
When responding to an create operation, the filesystem can specify a value
that will be passed to the filesystem as the #:info associated with an
operation on the created file.
Read
size bytes from the file at offset
offset. Read should respond
with exactly the number of bytes requested (except on EOF or error), unless the
'FOPEN_DIRECT_IO flag was included in the response when opening the file or
the filesystem was mounted using the option
"direct_io". If direct IO is being used,
the system call result for
read will reflect the number of bytes returned by
the filesystem’s response.
Unless the file was opened with the 'FOPEN_DIRECT_IO flag or the filesystem
was mounted with the option "direct_io", write the entire request data
to the file at offset offset. If direct_io is being used, the operation may
write fewer bytes and return the number of bytes written.
Called whenever the close system call is invoked on a file. May be invoked zero or
multiple times per open call depending on whether the file is closed and if the file
descriptor of an open file has been duplicated.
If the filesystem supports locking, it should remove all locks belonging to owner.
Invoked when there are no outstanding file descriptors or memory mappings for a file. Will be
called exactly once per open call.
Synchronize the contents of directory nodeid. If syncdata is #t,
synchronize only the contents of the directory, and not meta data.
Return filesystem statistics for the filesystem containing nodeid.
Set the extended attribute name on file nodeid to value.
Argument op is one of 'XATTR_DEFAULT, 'XATTR_CREATE, or 'XATTR_REPLACE.
If op is 'XATTR_DEFAULT, the extended attribute should be created if it does not exist
or, if it exists the value should be replaced. If op is 'XATTR_CREATE, the extended
attribute should be created if it does not exist, otherwise the operation should fail. If op is
'XATTR_REPLACE, the operation should fail if the extended attribute does not exists, otherwise
it should replce the current value.
Retreive the extended attribute with name name for file nodeid.
Retreive the names of all currently set extended attributes for nodeid.
Remove the extended attribute name for file nodeid.
Test for the existence of a POSIX lock on nodeid. According to libfuse,
if getlk and setlk are not implemented, the kernel will implement
file locking automatically, so these operations are only required in special cases
(for example, to enable network filesystems to correctly implement distributed locking).
Acquire, modify, or release a POSIX lock on nodeid. According to libfuse,
if getlk and setlk are not implemented, the kernel will implement
file locking automatically, so these operations are only required in special cases
(for example, to enable network filesystems to correctly implement distributed locking).
Return the device block index for the block with index index of file nodeid.
Only useful for block-device backed filesystems using where the filesystem was mounted with
the "blkdev" FUSE option.
Allocate the requested space within file nodeid. Argument mode describes the particular
fallocate operation requested. See fallocate(2) for more details.
Respond to an operation with an error.
Indicates that an operation succeeded without providing any additional information.
Respond to an operation with information about a filesystem entry.
If the file system is exported as a network file system, #:generation and #:inode pairs
must be unique over the lifetime of the filesystem. #:entry-valid and #:attr-valid indicate
how long the kernel may cache the entry and attribute information for the inode. #:rdev is the
device number of the device containing the inode. #:atime, #:mtime, and #:ctime
are the last access, last modification, and creation times of the entry. #:kind indicates the
type of filesystem object represented by the entry. #:perm, #:uid, and #:gid are
the is UNIX file mode, owner, and group of the entry. #:nlink reports the number of links to the entry
in the filesystem.
Respond to an operation with a file’s attributes. #:attr-valid indicates how long the kernel may cache the
attribute information for the inode. Argument #:rdev is the device number of the device containing the inode.
Arguments #:atime, #:mtime, and #:ctime are the last access, last modification, and creation times
of the entry. #:kind indicates the type of filesystem object represented by the entry. Arguments #:perm,
#:uid, and #:gid are the is UNIX file mode, owner, and group of the entry. Argument #:nlink
reports the number of links to the entry in the filesystem.
Respond to an operation with a byte array.
Respond to an open operation with a user defined value #:info and flags configuring
future operations on the file.
Respond to a
create operation with a user defined value
#:info and flags configuring
future operations on the file. In addition, provide information about the newly created entry
and its attributes (see
reply-entry/c for details).
Respond to a
write operation with the number of bytes written.
Respond to a statfs operation with information about a filesystem.
Respond to a listxattr operation with the list of names of currently set extended attributes.
Respond to a lock operation with information about a POSIX lock.
Respond to a bmap operation with the device block index of a block within a file.
4 Miscellaneous🔗ℹ
A time expressed as seconds (and nanoseconds) the since POSIX epoch, 1970-01-01 00:00:00 +0000 (UTC).
The process id of the process that triggered the current filesystem operation.
The effective user id of the process that triggered the current filesystem operation.
The effective group id of the process that triggered the current filesystem operation.
A flat-contract that requires the input to be an
exact-positive-integer?
that can be represented by a 32-bit unsigned integer.
A flat-contract that requires the input to be an
exact-positive-integer?
that can be represented by a 64-bit unsigned integer.
Checks if v is either a member of '(ETXTBSY ENOEXEC ENODEV ENOMEM EBADR EPROTONOSUPPORT ECHRNG ENOMEDIUM ENAVAIL EL2NSYNC ENOTSUP ETIME ENOTBLK EROFS ENOTDIR EISNAM ENETDOWN ERESTART ENOLINK EDOTDOT ESTALE EINPROGRESS EADV ENOTRECOVERABLE EBFONT ECONNREFUSED EPFNOSUPPORT EBUSY EUSERS ETOOMANYREFS EKEYREJECTED ENOTCONN EXFULL ENOTUNIQ EHWPOISON ENOANO ENOLCK ENETUNREACH EMSGSIZE ESRCH EMLINK EL2HLT EBADE ELIBSCN EBADF ECANCELED EREMOTE EILSEQ EDOM EFBIG ENOPROTOOPT EL3RST ECONNRESET EAGAIN EEXIST EFAULT ENFILE ELIBMAX EKEYEXPIRED EKEYREVOKED ENOPKG ENOSR EADDRNOTAVAIL EINVAL EINTR EISDIR EIO ENXIO EBADMSG EDESTADDRREQ EBADFD ENOCSI E2BIG EALREADY EBADSLT ECONNABORTED ELIBACC ENOMSG EHOSTDOWN ERFKILL ENOKEY ELNRNG ECHILD EUCLEAN ESRMNT EREMOTEIO ELIBBAD ETIMEDOUT ENONET ENOTTY ELOOP EPERM EPIPE ELIBEXEC EBADRQC EDEADLOCK ENOSPC EXDEV ENOTNAM EHOSTUNREACH ENOENT EREMCHG EMEDIUMTYPE ENETRESET EISCONN EDQUOT ECOMM EPROTOTYPE EOWNERDEAD ESTRPIPE EUNATCH EL3HLT EAFNOSUPPORT EMULTIHOP EMFILE EACCES ESPIPE ENOBUFS ENOTEMPTY ERANGE ESOCKTNOSUPPORT ENOSTR EADDRINUSE ENOTSOCK ENODATA ENOSYS EOVERFLOW ESHUTDOWN EIDRM ENAMETOOLONG EPROTO).
Checks if v is either a member of or a sublist of '(S_IFSOCK S_IFLNK S_IFREG S_IFBLK S_IFDIR S_IFCHR S_IFIFO S_ISUID S_ISGID S_ISVTX S_IRUSR S_IWUSR S_IXUSR S_IRGRP S_IWGRP S_IXGRP S_IROTH S_IWOTH S_IXOTH).
Checks if v is either a member of or a sublist of '(S_ISUID S_ISGID S_ISVTX S_IRUSR S_IWUSR S_IXUSR S_IRGRP S_IWGRP S_IXGRP S_IROTH S_IWOTH S_IXOTH).
Checks if v is either a member of '(S_IFSOCK S_IFLNK S_IFREG S_IFBLK S_IFDIR S_IFCHR S_IFIFO).
Checks if v is either a member of or a sublist of '(O_RDONLY O_WRONLY O_RDWR O_CLOEXEC O_CREAT O_DIRECTORY O_EXCL O_NOCTTY O_NOFOLLOW O_TMPFILE O_TRUNC O_APPEND O_NONBLOCK O_PATH O_DSYNC O_DIRECT O_LARGEFILE O_NOATIME).
Checks if v is either a member of '(XATTR_DEFAULT XATTR_CREATE XATTR_REPLACE).
Checks if v is either a member of or a sublist of '(LOCK_SH LOCK_EX LOCK_NB LOCK_UN LOCK_MAND LOCK_READ LOCK_WRITE LOCK_RW).
Checks if v is either a member of '(FALLOC_FL_KEEP_SIZE FALLOC_FL_PUNCH_HOLE FALLOC_FL_NO_HIDE_STALE FALLOC_FL_COLLAPSE_RANGE FALLOC_FL_ZERO_RANGE FALLOC_FL_INSERT_RANGE).
The module fuse/examples/hello implements an example FUSE
filesystem. It is reproduced below for convenience. To run the filesystem,
invoke racket -l fuse/examples/hello /path/to/mount/point.
(/path/to/mount/point must exist as an empty directory). You can
then explore the mounted filesystem at /path/to/mount/point. To unmount the
filesystem, invoke fusermount -u /path/to/mount/point.
"fuse/examples/hello"
#lang racket/base |
|
; This example was adapted from the hello filesystem example distributed with |
; libfuse (https://github.com/libfuse/libfuse/blob/master/example/hello_ll.c) |
; and the rust-fuse package (https://github.com/zargony/rust-fuse) |
|
(require racket/match |
racket/list |
fuse) |
|
(define (lookup #:nodeid nodeid #:name name #:reply reply-entry #:error error) |
(if (and (= nodeid 1) (equal? name (string->path "hello.txt"))) |
(reply-entry #:generation 0 #:entry-valid (timespec 1 0) #:attr-valid (timespec 1 0) |
#:inode 2 #:rdev 0 #:size 13 #:blocks 1 |
#:atime (timespec 1381237736 0) #:mtime (timespec 1381237736 0) |
#:ctime (timespec 1381237736 0) #:kind 'S_IFREG |
#:perm '(S_IRUSR S_IWUSR S_IRGRP S_IROTH) |
#:nlink 1 #:uid 1000 #:gid 1000) |
(error 'ENOENT))) |
|
(define (getattr #:nodeid nodeid #:info info #:reply reply-attr #:error error) |
(match nodeid |
[1 (reply-attr #:attr-valid (timespec 1 0) |
#:inode 1 #:rdev 0 #:size 0 #:blocks 0 |
#:atime (timespec 1381237736 0) #:mtime (timespec 1381237736 0) |
#:ctime (timespec 1381237736 0) #:kind 'S_IFDIR |
#:perm '(S_IRUSR S_IWUSR S_IXUSR S_IRGRP S_IXGRP S_IROTH S_IXOTH) |
#:nlink 1 #:uid 1000 #:gid 1000)] |
[2 (reply-attr #:attr-valid (timespec 1 0) |
#:inode 2 #:rdev 0 #:size 13 #:blocks 1 |
#:atime (timespec 1381237736 0) #:mtime (timespec 1381237736 0) |
#:ctime (timespec 1381237736 0) #:kind 'S_IFREG |
#:perm '(S_IRUSR S_IWUSR S_IRGRP S_IROTH) |
#:nlink 1 #:uid 1000 #:gid 10000)] |
[_ (error 'ENOENT)])) |
|
(define (readdir #:nodeid nodeid #:info info #:offset offset #:add reply-add #:reply reply-done #:error error) |
(if (= nodeid 1) |
(begin |
(when (= offset 0) |
(reply-add #:inode 1 #:offset 0 #:kind 'S_IFDIR #:name (string->path ".")) |
(reply-add #:inode 1 #:offset 1 #:kind 'S_IFDIR #:name (string->path "..")) |
(reply-add #:inode 1 #:offset 2 #:kind 'S_IFREG #:name (string->path "hello.txt"))) |
(reply-done)) |
(error 'ENOENT))) |
|
(define (open #:nodeid nodeid #:flags flags #:reply reply-open #:error error) |
(if (= nodeid 2) |
(reply-open #:info 0 #:flags empty) |
(error 'EISDIR))) |
|
(define (read #:nodeid nodeid #:info info #:offset offset #:size size #:lockowner lockowner #:reply reply-data #:error error) |
(reply-data (string->bytes/utf-8 "Hello world!\n"))) |
|
(define hellofs (make-filesystem #:lookup lookup #:getattr getattr #:readdir readdir #:open open #:read read)) |
|
(module+ main |
(unless (eq? (vector-length (current-command-line-arguments)) 1) |
(error "Must be invoked with an absolute path.")) |
(define path (vector-ref (current-command-line-arguments) 0)) |
(unless (directory-exists? path) |
(error "Mount directory does not exist.")) |
(mount-filesystem hellofs (string->path path) (list "default_permissions" "large_read" "direct_io" "hard_remove"))) |
|