PUFFS(3)

NAME
libpuffs - Pass-to-Userspace Framework File System development interface

LIBRARY
libpuffs

SYNOPSIS
#include <puffs.h>
struct puffs_usermount *
puffs_mount(struct puffs_vfsops *pvfs, struct puffs_vnops *pvn, const char *dir, int mntflags, const char *puffsname, uint32_t pflags, size_t maxreqlen);
int
puffs_mainloop(struct puffs_usermount *pu, int flags);
int
puffs_oneop(struct puffs_usermount *pu, uint8_t *buf, size_t buflen);
int
puffs_getselectable(struct puffs_usermount *pu);
int
puffs_setblockingmode(struct puffs_usermount *pu, int mode);
int
puffs_vfs_mount(struct puffs_usermount *pu, void **rootcookie);
int
puffs_vfs_unmount(struct puffs_usermount *pu, int flags, pid_t pid);
int
puffs_vfs_statvfs(struct puffs_usermount *pu, struct statvfs *sbp, pid_t pid);
int
puffs_vfs_sync(struct puffs_usermount *pu, int waitfor, const struct puffs_cred *cred, pid_t pid);
int
puffs_vn_lookup(struct puffs_usermount *pu, void *opc, void **newnode, enum vtype *newtype, voff_t *newsize, dev_t *newrdev, const struct puffs_cn *pcn);
int
puffs_vn_getattr(struct puffs_usermount *pu, void *opc, struct vattr *va, const struct puffs_cred *pcr, pid_t pid);
int
puffs_vn_setattr(struct puffs_usermount *pu, void *opc, const struct vattr *va, const struct puffs_cred *pcr, pid_t pid);
int
puffs_vn_create(struct puffs_usermount *pu, void *opc, void **newnode, const struct puffs_cn *pcn, const struct vattr *va);
int
puffs_vn_remove(struct puffs_usermount *pu, void *opc, void *targ, const struct puffs_cn *pcn);
int
puffs_vn_mkdir(struct puffs_usermount *pu, void *opc, void **newnode, const struct puffs_cn *pcn, const struct vattr *va);
int
puffs_vn_rmdir(struct puffs_usermount *pu, void *opc, void *targ, const struct puffs_cn *pcn);
int
puffs_vn_readdir(struct puffs_usermount *pu, void *opc, struct dirent *dent, const struct puffs_cred *pcr, off_t *readoff, size_t *reslen);
int
puffs_vn_rename(struct puffs_usermount *pu, void *opc, void *src, const struct puffs_cn *pcn_src, void *targ_dir, void *targ, const struct puffs_cn *pcn_targ);
int
puffs_vn_link(struct puffs_usermount *pu, void *opc, void *targ, const struct puffs_cn *pcn);
int
puffs_vn_symlink(struct puffs_usermount *pu, void *opc, void **newnode, const struct puffs_cn *pcn_src, const struct vattr *va, const char *link_target);
int
puffs_vn_readlink(struct puffs_usermount *pu, void *opc, const struct puffs_cred *cred, char *link, size_t *linklen);
int
puffs_vn_mknod(struct puffs_usermount *pu, void *opc, void **newnode, const struct puffs_cn *pcn, const struct vattr *va);
int
puffs_vn_open(struct puffs_usermount *pu, void *opc, int flags, const struct puffs_cred *pcr, pid_t pid);
int
puffs_vn_close(struct puffs_usermount *pu, void *opc, int flags, const struct puffs_cred *pcr, pid_t pid);
int
puffs_vn_access(struct puffs_usermount *pu, void *opc, int mode, struct puffs_cred *pcr, pid_t pid);
int
puffs_vn_read(struct puffs_usermount *pu, void *opc, uint8_t *buf, off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag);
int
puffs_vn_write(struct puffs_usermount *pu, void *opc, uint8_t *buf, off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag);
int
puffs_vn_reclaim(struct puffs_usermount *pu, void *opc, pid_t pid);
int
puffs_vn_inactive(struct puffs_usermount *pu, void *opc, pid_t pid, int *refcount);

DESCRIPTION
IMPORTANT NOTE! This document describes interfaces which are not yet guaranteed to be stable. In case you update your system sources, please recompile everything and fix complation errors. If your sources are out-of-sync, incorrect operation may result. The interfaces in this document will most likely be hugely simplified in later versions or made transparent to the implementation.

libpuffs 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 libpuffs, where they are processed and results are sent back to the kernel.

It is possible to use libpuffs in two different ways. Calling puffs_mainloop() takes execution context away from the caller and automatically handles all requests by using the callbacks. Alternatively, control can be kept with the caller and operations handled manually. In the latter case the function puffs_oneop() can be helpful.

  Library operation
The file system is mounted using puffs_vfs_mount(). The callbacks are passed as the fields in the structures pvfs and pvn. The ones intended to be used should initialized to point to the correct routines before calling and the rest should be set to zero. All of the VFS routines are mandatory, but all of the node operations with the exception of puffs_vfs_lookup() are optional. However, leaving operations blank will naturally have an effect on the features available from the file system implementation. The argument dir signifies the mount point, mntflags is the flagset given to mount(2), and puffsname is the name of the file system implementation. Flags for libpuffs can be given via pflags. Currently PUFFS_FLAG_OPDUMP is supported, this dumps each received operation to stdout before handling it. Finally, the maximum operation buffer length is requested by maxreqlen. The field pu_maxreqlen from the returned mount structure is the kernel sanity-checked value and should always be consulted after the mount call returns. Supplying 0 as this parameter will make the kernel choose the longest possible buffer length. In case of success, puffs_vfs_mount() returns the address of the user mount instance. Otherwise, NULL is returned and errno is set to specify the error.

To handle all requests automatically until the file system is unmounted, puffs_mainloop() should be used. It returns 0 if the file system was succesfully unmounted or -1 if it was killed in action. Unless flags is used to pass PUFFSLOOP_NODAEMON, puffs_mainloop() will also detach from the terminal.

To handle a single operation, puffs_oneop() can be used. The buffer for the request should be supplied by the caller and, if possible, should match the buffer length gotten from mount. In case the request was succefully handled (orthogonal to if the request itself was a success from the file system point of view), 0 is returned. Otherwise, -1 is returned and errno is set.

puffs_getselectable() can be used to query a handle to do I/O multiplexing with: select(2), poll(2), and kqueue(2) are all examples of acceptable operations.

The library can be set in blocking or non-blocking mode using puffs_setblockingmode(). Acceptable values for the argument are PUFFSDEV_BLOCK and PUFFSDEV_NONBLOCK.

  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_vn_reclaim() is called for a node.

  File system callbacks
The callbacks do all the actual work in implementing the file system. Currently they are fairly close to the vfs and vnode operations in the kernel but with simplified operation. This section describes the calls which relate to the file system itself.

All callbacks can be prototyped with the file system name and operation name using the macro PUFFSVFS_PROTOS(fsname).

puffs_vfs_mount() should handle all operations which are necessary to mount the file system, e.g. open backing storage, check magic numbers, open a network connection, authenticate, etc. It returns the file system root directory cookie in rootcookie.

puffs_vfs_statvfs() should fill in the following fields of sbp:

     * unsigned long   f_bsize;         file system block size
     * unsigned long   f_frsize;        fundamental file system block size
     * fsblkcnt_t      f_blocks;        number of blocks in file system,
     *                                            (in units of f_frsize)
     *
     * fsblkcnt_t      f_bfree;         free blocks avail in file system
     * fsblkcnt_t      f_bavail;        free blocks avail to non-root
     * fsblkcnt_t      f_bresvd;        blocks reserved for root
     *
     * fsfilcnt_t      f_files;         total file nodes in file system
     * fsfilcnt_t      f_ffree;         free file nodes in file system
     * fsfilcnt_t      f_favail;        free file nodes avail to non-root
     * fsfilcnt_t      f_fresvd;        file nodes reserved for root

The process requiring this information is given by pid.

The file system should be sychronized to storage when puffs_vfs_sync() is called. The waitfor parameter should handled similarly as inside the kernel.

The file system should be unmounted when puffs_vfs_unmount() is called. If the flag MNT_FORCE is not honored, the kernel will proceed to forcibly unmount the file system despite this.

  Node callbacks
These operations operate in the level of individual files. The file cookie is always provided as the second argument opc. If the operation is for a file, it will be the cookie of the file. The case the operation involves a directory (such as “create file in directory”), the cookie will be for the directory. Some operations take additional cookies to describe the rest of the operands. The return value 0 signals success, else an appropriate errno value should be returned. Please note that neither this list nor the descriptions are complete.

The callbacks can be prototyped according to file system name by using the macro PUFFSVN_PROTOS(fsname).

The puffs_vn_lookup() function is used to locate nodes. The implementation should match the name in pcn against the existing entries in the directory provided by the cookie. If found, the cookie for the located node should be returned in newnode. Additionally, the type and size (latter applicable to regular files only) should be returned in newtype and newsize, respectively. If the located entry is a block device or character device file, the dev_t for the entry should be returned in newrdev. Otherwise, 0 signals a found node and a nonzero value signals an errno. As a special case, ENOENT signals success for cases where the lookup operation is PUFFSLOOKUP_CREATE or PUFFSLOOKUP_RENAME. Failure in these cases can be signalled by returning another appropriate error code, for example EACCESS.

puffs_vn_getattr() fills out a struct vattr pointed to by va.

puffs_vn_setattr() sets the attributes in va. Instead of setting everything according to that file, only fields which are not marked VNOVAL should be set.

A file node is created in the directory specified by the cookie when puffs_vn_create() is called. The attributes are specified by va and the cookie for the newly created node should be returned in newnode. Similarly, puffs_vn_mkdir() creates a directory.

puffs_vn_remove() removes the file targ from the directory indicated by the cookie. Similarly, puffs_vn_rmdir() removes a directory. The name of the directory entry to remove is described by pcn.

To read directory entries, puffs_vn_readdir() is called. It should store directories as struct dirents in the space pointed to by dent. The amount of space available is given by reslen and before returning it should be set to the amount of space remaining in the buffer. The argument offset is used to specify the offset to the directory. Its intepretation is up to the file systme and it should be set to signal the continuation point when there is no more room for the next entry in dent. It is most performant to return the maximal amount of directory entries each call. In case the directory was exhausted, the parameters should not be modified to signal end-of-directory.

A node rename is done by calling puffs_vn_rename(). If the destination file cookie is non-null, it must be removed and the new entry overwritten atomically. The directory entry names to be used are described by the struct puffs_cn's (cf. create and remove).

A hard link is created by puffs_vn_link(). In practice this means adding a directory entry described by pcn to the cookied directory and the entry pointing to the target node.

A symbolic link in turn is created by puffs_vn_symlink(). It is similar to creating a regular file, except that link_target specifies the target of the link which should be set for the link.

To read the target of a symbolic link, puffs_vn_readlink is called. The path in the link target should be copied to link and the length without the terminating nul set in linklen.

A device node is created using puffs_vn_mknod(). The only difference to creating a normal file is that the attribute struct contains the device identifier in va-va_rdev.

Files are opened with a call to puffs_vn_open(). Most of the time this can be left unimplemented, unless special resource allocation is required.

puffs_vn_close() releases all the resources allocated by puffs_vn_open().

To check if access of type mode to a file is allowed, puffs_vn_access() is called. This controls file access, not e.g. puffs_vn_open().

puffs_vn_read() reads the contents of a file. It will gather the data from offset in the file and read the number resid octets. The buffer is guaranteed to have this much space. The amount of data requested by resid should be read, except in the case of eof-of-file or an error. The parameter resid should be set to indicate the amount of request NOT completed. In the normal case this should be 0.

puffs_vn_write() writes data to a file at offset extending the file if necessary. The number of octets written is indicated by resid; everything must be written or an error will be generated. The parameter must be set to indicate the amount of data NOT written. In case the flag PUFFS_IO_APPEND is specified, the data should be appended to the end of the file.

puffs_vn_reclaim() signals that the cookie will no longer be referenced without a further call to puffs_vn_lookup(). This information can be used to free resources and specifically release a file for which no directory entries remain.

puffs_vn_inactive() signals that the kernel has released its last reference to the node. However, the cookie must still remain valid until puffs_vn_reclaim() is called. The file system should return its internal reference count on the file (usually number of links to the file) in refcount. If this is zero, the kernel will call reclaim immediately.

SEE ALSO
puffs(4)

HISTORY
An unsupported experimental version of libpuffs first appeared in NetBSD 4.0.

AUTHORS
Antti Kantee <pooka@iki.fi>

BUGS
struct puffs_node is of questionable content. Especially the use of pn_va should be avoided.

Operations which require arbitrary blocking periods are not well handled by the library in this version.