NAME
puffs —
Pass-to-Userspace Framework
File System development interface
LIBRARY
puffs Convenience Library (libpuffs, -lpuffs)
SYNOPSIS
#include <puffs.h>
struct puffs_usermount *
puffs_init(
struct puffs_ops *pops,
const char *mntfromname,
const char
*puffsname,
void *private,
uint32_t flags);
int
puffs_mount(
struct puffs_usermount *pu,
const char *dir,
int mntflags,
puffs_cookie_t root_cookie);
int
puffs_getselectable(
struct
puffs_usermount *pu);
int
puffs_setblockingmode(
struct
puffs_usermount *pu,
int
mode);
int
puffs_getstate(
struct
puffs_usermount *pu);
int
puffs_setstacksize(
struct
puffs_usermount *pu,
size_t
stacksize);
void
puffs_setroot(
struct
puffs_usermount *pu,
struct
puffs_node *node);
void
puffs_setrootinfo(
struct puffs_usermount
*pu,
enum vtype vt,
vsize_t
vsize,
dev_t rdev);
struct puffs_node *
puffs_getroot(
struct
puffs_usermount *pu);
void *
puffs_getspecific(
struct
puffs_usermount *pu);
void
puffs_setspecific(
struct
puffs_usermount *pu,
void
*private);
void
puffs_setmaxreqlen(
struct
puffs_usermount *pu,
size_t
maxreqlen);
size_t
puffs_getmaxreqlen(
struct
puffs_usermount *pu);
void
puffs_setfhsize(
struct
puffs_usermount *pu,
size_t
fhsize,
int flags);
void
puffs_setncookiehash(
struct
puffs_usermount *pu,
int
nhashes);
void
puffs_ml_loop_fn(
struct
puffs_usermount *pu);
void
puffs_ml_setloopfn(
struct
puffs_usermount *pu,
puffs_ml_loop_fn lfn);
void
puffs_ml_settimeout(
struct
puffs_usermount *pu,
struct
timespec *ts);
int
puffs_daemon(
struct
puffs_usermount *pu,
int
nochdir,
int
noclose);
int
puffs_mainloop(
struct
puffs_usermount *pu);
int
puffs_unmountonsignal(
int
sig,
bool ignoresig);
int
puffs_dispatch_create(
struct puffs_usermount
*pu,
struct puffs_framebuf *pb,
struct puffs_cc **pccp);
int
puffs_dispatch_exec(
struct
puffs_cc *pcc,
struct
puffs_framebuf **pbp);
DESCRIPTION
puffs provides a framework for creating file systems as
userspace servers. Operations are transported from the kernel virtual file
system layer to the concrete implementation behind
puffs,
where they are processed and results are sent back to the kernel.
It is possible to use
puffs in two different ways. Calling
puffs_mainloop() takes execution context away from the
caller and automatically handles all requests by using the callbacks. By using
puffs_framebuf(3) in
conjuction with
puffs_mainloop(), it is possible to handle
I/O to and from file descriptors. This is suited e.g. for distributed file
servers.
Library operation
Operations on the library always require a pointer to the opaque context
identifier,
struct puffs_usermount. It is obtained by
calling
puffs_init().
puffs operates using operation callbacks. They can be
initialized using the macro
PUFFSOP_SET(
pops,
fsname,
type,
opname), which will initialize the operation
puffs_type_opname() in
pops to
fsname_type_opname(). All operations are initialized to a
default state with the call
PUFFSOP_INIT(
pops). All of the VFS
routines are mandatory, but all of the node operations with the exception of
puffs_node_lookup() are optional. However, leaving
operations blank will naturally have an effect on the features available from
the file system implementation.
-
-
- puffs_init(pops,
mntfromname, puffsname,
private, flags)
- Initializes the library context. pops
specifies the callback operations vector.
mntfromname is device the file system is mounted
from. This can be for example a block device such as
/dev/wd0a or, if the file system is pseudo file system,
the puffs device name can be given by
_PATH_PUFFS
. This value is used for example in the
first column of the output of
mount(8) and
df(1).
puffsname is the file system type. It will always be
prepended with the string "puffs|". If possible, file server
binaries should be named using the format "mount_myfsnamehere"
and this value should equal "myfsnamehere". A file system
specific context pointer can optionally be given in
private. This can be retrieved by
puffs_getspecific(). Flags for puffs
can be given via flags. Currently the following
flags are supported:
-
-
PUFFS_KFLAG_NOCACHE_NAME
- Do not enter pathname components into the name cache.
This means that every time the kernel does a lookup for a
componentname, the file server will be consulted.
-
-
PUFFS_KFLAG_NOCACHE_PAGE
- Do not use the page cache. This means that all reads
and writes to regular file are propagated to the file server for
handling. This option makes a difference only for regular files.
-
-
PUFFS_KFLAG_NOCACHE
- An alias for both
PUFFS_KFLAG_NOCACHE_NAME
and
PUFFS_KFLAG_NOCACHE_PAGE
.
-
-
PUFFS_KFLAG_ALLOPS
- This flag requests that all operations are sent to
userspace. Normally the kernel shortcircuits unimplemented operations.
This flag is mostly useful for debugging purposes.
-
-
PUFFS_KFLAG_WTCACHE
- Set the file system cache behavior as write-through.
This means that all writes are immediately issued to the file server
instead of being flushed in file system sync. This is useful
especially for distributed file systems.
-
-
PUFFS_KFLAG_IAONDEMAND
- Issue inactive only on demand. If a file server defines
the inactive method, call it only if the file server has explicitly
requested that inactive be called for the node in question. Once
inactive has been called for a node, it will not be called again
unless the request to call inactive is reissued by the file server.
See puffs_setback() in
puffs_ops(3) for more
information.
-
-
PUFFS_KFLAG_LOOKUP_FULLPNBUF
- This flag affects only the parameter
pcn to puffs_node_lookup(). If
this flag is not given, only the next pathname component under lookup
is found from pcn->pcn_name. If this flag is
given, the full path the kernel was asked to resolve can be found from
there.
-
-
PUFFS_FLAG_BUILDPATH
- The framework will build a complete path name, which is
supplied with each operation and can be found from the
pcn_po_full.po_path field in a
struct puffs_cn. The option assumes that the
framework can map a cookie to a struct
puffs_node. See Cookies for
more information on cookie mapping. See
puffs_path(3) for
more information on library calls involving paths.
-
-
PUFFS_FLAG_HASHPATH
- Calculate a hash of the path into the path object field
po_hash. This hash value is used by
puffs_path_walkcmp() to avoid doing a full
comparison for every path equal in length to the one searched for.
Especially if the file system uses the abovementioned function, it is
a good idea to define this flag.
-
-
PUFFS_FLAG_PNCOOKIE
- Tell puffs that cookies map to struct
pnode. This is automagically set if
puffs_pn_new() is called.
-
-
PUFFS_KFLAG_CACHE_FS_TTL
- Enforce name and attribute caches based on file
system-supplied TTL. In lookup, create, mknod, mkdir, and symlink, the
file system must update the node attributes, their TTL, and the node
name TTL through puffs_newinfo_setva(),
puffs_newinfo_setvattl(), and
puffs_newinfo_setcnttl().
Additionally, puffs_node_getattr_ttl() and
puffs_node_setattr_ttl() will be called instead of
puffs_node_getattr() and
puffs_node_setattr().
-
-
PUFFS_KFLAG_CACHE_DOTDOT
- Never send lookups for “..” to the file
system. Parent vnodes are all kept active until their children are
reclaimed.
-
-
PUFFS_KFLAG_NOFLUSH_META
- Do not send metadata cache flushes for time and size to
the file system, which should take care of updating the values on its
own.
-
-
PUFFS_FLAG_OPDUMP
- This option makes the framework dump a textual
representation of each operation before executing it. It is useful for
debugging purposes.
The following functions can be used to query or modify the global state of the
file system. Note, that all calls are not available at all times.
-
-
- puffs_getselectable(pu)
- Returns a handle to do I/O multiplexing with:
select(2),
poll(2), and
kqueue(2) are all examples
of acceptable operations.
-
-
- puffs_setblockingmode(pu,
mode)
- Sets the file system upstream access to blocking or
non-blocking mode. Acceptable values for the argument are
PUFFSDEV_BLOCK
and
PUFFSDEV_NONBLOCK
.
This routine can be called only after calling
puffs_mount().
-
-
- puffs_getstate(pu)
- Returns the state of the file system. It is maintained by
the framework and is mostly useful for the framework itself. Possible
values are
PUFFS_STATE_BEFOREMOUNT
,
PUFFS_STATE_RUNNING
,
PUFFS_STATE_UNMOUNTING
and
PUFFS_STATE_UNMOUNTED
.
-
-
- puffs_setstacksize(pu,
stacksize)
- Sets the stack size used when running callbacks. The
default is
PUFFS_STACKSIZE_DEFAULT
bytes of stack
space per request. The minimum stacksize is architecture-dependent and can
be specified by using the opaque constant
PUFFS_STACKSIZE_MIN
.
-
-
- puffs_setroot(pu,
node)
- Sets the root node of mount pu to
node. Setting the root node is currently required
only if the path framework is used, see
puffs_path(3).
-
-
- puffs_setrootinfo(pu,
vt, vsize,
rdev)
- The default root node is a directory. In case the file
system wants something different, it can call this function and set the
type, size and possible device type to whatever it wants. This routine is
independent of puffs_setroot().
-
-
- puffs_getroot(pu)
- Returns the root node set earlier.
-
-
- puffs_getspecific(pu)
- Returns the private argument of
puffs_init().
-
-
- puffs_setspecific(pu,
private)
- Can be used to set the specific data after the call to
puffs_init().
-
-
- puffs_setmaxreqlen(pu,
maxreqlen)
- In case the file system desires a maximum buffer length
different from the default, the amount maxreqlen
will be requested from the kernel when the file system is mounted.
It is legal to call this function only between
puffs_init() and puffs_mount().
NOTE This does not currently work.
-
-
- puffs_getmaxreqlen(pu)
- Returns the maximum request length the kernel will need for
a single request.
NOTE This does not currently work.
-
-
- puffs_setfhsize(pu,
fhsize, flags)
- Sets the desired file handle size. This must be called if
the file system wishes to support NFS exporting file systems of the
fh*() family of function calls.
In case all nodes in the file system produce the same length file handle, it
must be supplied as fhsize. In this case, the file
system may ignore the length parameters in the file handle callback
routines, as the kernel will always pass the correct length buffer.
However, if the file handle size varies according to file, the argument
fhsize defines the maximum size of a file handle for
the file system. In this case the file system must take care of the handle
lengths by itself in the file handle callbacks, see
puffs_ops(3) for more
information. Also, the flag
PUFFS_FHFLAG_DYNAMIC
must be provided in the argument flags.
In case the file system wants to sanity check its file handle lengths for
the limits of NFS, it can supply
PUFFS_FHFLAG_NFSV2
and
PUFFS_FHFLAG_NFSV3
in the
flags parameter. It is especially important to note
that these are not directly the limits specified by the protocols, as the
kernel uses some bytes from the buffer space. In case the file handles are
too large, mount will return an error.
It is legal to call this function only between
puffs_init() and puffs_mount().
-
-
- puffs_setncookiehash(pu,
ncookiehash)
- The parameter ncookiehash controls
the amount of hash buckets the kernel has for reverse lookups from cookie
to vnode. Technically the default is enough, but a memory/time tradeoff
can be made by increasing this for file systems which know they will have
very many active files.
It is legal to call this function only between
puffs_init() and puffs_mount().
After the correct setup for the library has been established and the backend has
been initialized the file system is made operational by calling
puffs_mount(). After this function returns the file system
should start processing requests.
-
-
- puffs_mount(pu,
dir, mntflags,
root_cookie)
- pu is the library context pointer
from puffs_init(). The argument
dir signifies the mount point and
mntflags is the flagset given to
mount(2). The value
root_cookie will be used as the cookie for the file
system root node.
Using the built-in
eventloop
-
-
- puffs_ml_loop_fn(pu)
- Loop function signature.
-
-
- puffs_ml_setloopfn(pu,
lfn)
- Set loop function to lfn. This
function is called once each time the event loop loops. It is not a
well-defined interval, but it can be made fairly regular by setting the
loop timeout by puffs_ml_settimeout().
-
-
- puffs_ml_settimeout(pu,
ts)
- Sets the loop timeout to ts or
disables it if ts is
NULL
.
This can be used to roughly control how often the loop callback
lfn() is called
-
-
- puffs_daemon(pu,
nochdir, noclose)
- Detach from the console like
daemon(3). This call synchronizes
with puffs_mount() and the foreground process does not
exit before the file system mount call has returned from the kernel. Since
this routine internally calls fork, it has to be called
before puffs_mount().
-
-
- puffs_mainloop(pu,
flags)
- Handle all requests automatically until the file system is
unmounted. It returns 0 if the file system was successfully unmounted or
-1 if it was killed in action.
In case
puffs_framebuf(3)
has been initialized, I/O from the relevant descriptors is processed
automatically by the eventloop.
-
-
- puffs_unmountonsignal(signum,
ignoresig)
- Cause all file servers within the process to initiate
unmount upon receipt of signal signum. This works
only for servers which call puffs_mainloop() and must be
called before any server within the process enters the mainloop. The
process signal handler is still called before starting the unmount
procedure. The parameter ignoresig is provided as a
convenience and tells if to install a signal handler to ignore
sig so that the process will not e.g. terminate
based on the default action before the file system unmount can be
initiated.
-
-
- puffs_dispatch_create(pu,
pb, pccp)
-
- puffs_dispatch_exec(pcc,
pbp)
- In case the use of puffs_mainloop() is
not possible, requests may be dispatched manually. However, as this is
less efficient than using the mainloop, it should never be the first
preference.
Calling puffs_dispatch_create() creates a dispatch
request. The argument pb should contains a valid
request and upon success pccp will contain a valid
request context. This context is passed to
puffs_dispatch_exec() to execute the request. If the
request yielded before completing, the routine returns 0, otherwise 1.
When the routine completes, pcc is made invalid and
a pointer to the processed buffer is placed in pbp.
It is the responsibility of the caller to send the response (if necessary)
and destroy the buffer.
See puffs_cc(3) and
puffs_framebuf(3)
for further information.
Cookies
Every file (regular file, directory, device node, ...) instance is attached to
the kernel using a cookie. A cookie should uniquely map to a file during its
lifetime. If file instances are kept in memory, a simple strategy is to use
the virtual address of the structure describing the file. The cookie can be
recycled when
puffs_node_reclaim() is called for a node.
For some operations (such as building paths) the framework needs to map the
cookie to the framework-level structure describing a file,
struct puffs_node. It is advisable to simply use the
struct puffs_node address as a cookie and store file
system specific data in the private portion of
struct
puffs_node. The library assumes this by default. If it is not desirable,
the file system implementation can call
puffs_set_cookiemap() to provide an alternative
cookie-to-node mapping function.
SEE ALSO
mount(2),
puffs_cc(3),
puffs_cred(3),
puffs_flush(3),
puffs_framebuf(3),
puffs_node(3),
puffs_ops(3),
puffs_path(3),
refuse(3),
puffs(4)
Antti Kantee, puffs
- Pass-to-Userspace Framework File System, Proceedings
of AsiaBSDCon 2007, pp. 29-42,
March 2007.
Antti Kantee, Using
puffs for Implementing Client-Server Distributed File Systems,
Helsinki University of Technology, Tech
Report TKK-TKO-B157, September 2007.
Antti Kantee and
Alistair Crooks, ReFUSE: Userspace
FUSE Reimplementation Using puffs, EuroBSDCon 2007,
September 2007.
Antti Kantee, Send
and Receive of File System Protocols: Userspace Approach With puffs,
Proceedings of AsiaBSDCon 2008, pp.
55-70, March 2008.
HISTORY
An unsupported experimental version of
puffs first appeared in
NetBSD 4.0. A stable version appeared in
NetBSD 5.0.
AUTHORS
Antti Kantee
<
pooka@iki.fi>