Systems Programming 09. Filesystem in USErspace (FUSE) Alexander Holupirek Database and Information Systems Group Department of Computer & Information Science University of Konstanz Summer Term 2008
Schedule for Today 2 Last lectures: File I/O Unbuffered I/O and control functions on file descriptors. Functions for operating on directories and for manipulating file attributes such as access modes and ownership. I/O on streams, i.e., standard I/O library (ISO C). Today: Filesystem in USErspace (FUSE) Short introduction to FUSE with practical examples. Stepwise discussion of project part III. Use relational storage as filesystem implementation.
FUSE stands for Filesystem in USErspace 3 FUSE stands for Filesystem in USErspace It provides a framework for building userspace filesystem servers, i.e., implement filesystems with userspace code. Userlevel FS have been around before (on Linux NFS was implemented that way for quite some time). The basic idea is to integrate information behind the filesystem namespace (GmailFS, FUSEPod...). From a programmer s perspective FUSE provides an library and defines a standard and a low-level interface to use it. A FUSE-supported system integrates kernel and userlevel components.
Ports of the Userspace Filesystem Interface 4 While the interface definition originated in LINUX 1, support has since been added to multiple different UNIX-type OSs: LINUX http://fuse.sourceforge.net/ NetBSD/puffs/reFUSE http://www.netbsd.org/docs/puffs/ FreeBSD/fuse4bsd http://fuse4bsd.creo.hu/ Mac OS X/MacFUSE http://code.google.com/p/macfuse/ OpenSolaris http://opensolaris.org/os/project/fuse/ 1 http://kerneltrap.org/node/4517, Miklos announces FUSE (LKML)
FUSE-based FS Implementations 5 sshfs - mount a SSH filesystem $ sshfs username@ hostname :/ path / to/ mount \ > ~/ mnt / ssh / -o uid =1000, gid =1000 $ fusermount -u ~/ mnt / ssh / NTFS-3G (http://www.ntfs-3g.org) Comprehensive list of FUSE-based FSs. http://fuse.sourceforge.net/wiki/index.php/filesystems ArchiveFSs, CompressedFSs, DatabaseFSs, EncryptedFSs, MediaFSs, HardwareFSs, MonitoringFSs, NetworkFSs, NonNativeFSs, UnionFSs, VersioningFSs
The Big Picture 6 How do userspace FSs operate? Attach an in-kernel filesystem (component/module) to the kernel s virtual filesystem layer. It prepares incoming requests for delivery to userspace. Sends the request to userspace. Waits for a response. Interprets the answers. Feeds the results back to the caller in the kernel.
The Big Picture (Illustrated) 7 Figure: Path of a filesystem call (e.g., stat) [FUSE project page]
Communication via Special Device 8 Special file descriptor /dev/fuse The FUSE kernel module and the FUSE library communicate via a special file descriptor which is obtained by opening /dev/fuse. This file can be opened multiple times, and the obtained file descriptor is passed to the mount syscall, to match up the descriptor with the mounted filesystem.
FUSE-based FS Implementation Overview 9 How do userspace FSs operate (implementation view)? A userlevel file server registers a number of callbacks with the userlevel library. It requests the kernel to mount the filesystem. Control is either passed to the library or kept with the caller. The library provides routines to decode filesystem requests from the kernel. The library calls back the appropriate registered functions. The library passes back the results to the kernel.
Restrictions and Possibilities 10 Incidental Remarks The kernel filesystem calling conventions dictate how to interface with the virtual filesystem layer. Other than that, myfuse is free to decide how to operate. myfuse is free to provide other interfaces to userspace. Applications and the rest of the kernel (outside the VFS module) cannot distinguish a filesystem implemented on top of FUSE from a filesystem implementation in the kernel.
Standard and Low Level Interface 11 For the filesystem callbacks, FUSE provides two different interfaces against which to write a filesystem: The standard interface based on pathnames. Operations resemble system calls. The low level interface. It resemble the kernel virtual filesystem interface closely. Requires the filesystem to manually handle all operation traffic between filesystem and kernel.
A. Kernel Module 2 FUSE Summary 12 The kernel module hooks into the VFS code and looks like a filesystem module. It implements a special-purpose device which can be opened by a userspace process. It spends its time accepting filesystem requests. Translates them into its own protocol. Sends them out via the device interface. Responses to requests come back from userspace via the FUSE device. They are translated back into the form expected by the kernel. 2 Jonathan Corbet summarizes FUSE as a three-part system http://lwn.net/articles/68104/
FUSE Summary (cont.) 13 B. FUSE library FUSE implements a library which manages communications with the kernel module. It accepts filesystem requests from the FUSE device. Translates them into a set of function calls which look similar (but not identical) to the kernel s VFS interface. These functions have names like open(), read(), write(), rename(), symlink(), etc.
FUSE Summary (cont.) 14 C. FUSE-based FS implementation The user-supplied component which actually implements the filesystem of interest. It fills a fuse operations structure with pointers to its functions to register for callbacks: static struct fuse_ operations myfs_ ops = {. getattr = myfs_getattr,. readdir = myfs_readdir,. open = myfs_open,. read = myfs_read, }; Those implement the required operations in whatever way makes sense.
Documentation and Further Information 15 Where to start and where to look? The interfaces are well documented (within the header files). Some example filesystem are provided with the FUSE code. Hello FUSE (standard interface) hello.c Hello FUSE LL (low-level interface) hello ll.c Hello FUSE standard interface example / FUSE : Filesystem in Userspace Copyright ( C) 2001-2007 Miklos Szeredi < miklos@szeredi. hu > This program can be distributed under the terms of the GNU GPL. See the file COPYING. gcc - Wall pkg - config fuse -- cflags -- libs hello. c -o hello /
Hello FUSE (hello.c) 16 # define FUSE_USE_VERSION 26 # include <fuse.h> # include <stdio.h> # include < string.h> # include <errno.h> # include <fcntl.h> static const char hello_str = " Hello World!\ n"; static const char hello_path = "/ hello "; static struct fuse_operations hello_oper = {. getattr = hello_getattr,. readdir = hello_readdir,. open = hello_open,. read = hello_read, }; int main ( int argc, char argv []) { return fuse_main ( argc, argv, & hello_oper, NULL ); }
Hello FUSE (hello.c, cont.) 17 static int hello_getattr ( const char path, struct stat stbuf ) { int res = 0; memset ( stbuf, 0, sizeof ( struct stat )); if ( strcmp (path, "/") == 0) { stbuf - > st_mode = S_IFDIR 0755; stbuf - > st_nlink = 2; } else if ( strcmp ( path, hello_path ) == 0) { stbuf - > st_mode = S_IFREG 0444; stbuf - > st_nlink = 1; stbuf - > st_size = strlen ( hello_str ); } else res = - ENOENT ; } return res ;
Hello FUSE (hello.c, cont.) 18 static int hello_readdir ( const char path, void buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info fi) { ( void ) offset ; ( void ) fi; if ( strcmp (path, "/")!= 0) return - ENOENT ; filler (buf, ".", NULL, 0); filler (buf, "..", NULL, 0); filler ( buf, hello_path + 1, NULL, 0); } return 0;
Hello FUSE (hello.c, cont.) 19 static int hello_open ( const char path, struct fuse_file_info fi) { if ( strcmp ( path, hello_path )!= 0) return - ENOENT ; if (( fi - > flags & 3)!= O_RDONLY ) return - EACCES ; } return 0;
Hello FUSE (hello.c, cont.) 20 static int hello_read ( const char path, char buf, size_t size, off_t offset, struct fuse_file_info fi) { size_t len ; ( void ) fi; if( strcmp ( path, hello_path )!= 0) return - ENOENT ; len = strlen ( hello_str ); if ( offset < len ) { if ( offset + size > len ) size = len - offset ; memcpy ( buf, hello_str + offset, size ); } else size = 0; } return size ;
Project Part Three: DBFS 21 Use relational storage as backend for a FUSE implementation We use the low level interface to connect DB and FS via pre and ino, respectively. The implementation is read-only. Mount the shredded file hierarchy (from part two) as FUSE. Optional Part: Prolong the file hierarchy on XML file s inherent structure. Whenever you encounter an XML file, shred it (project part one) into file hierarchy kind FXML. Fake stat information for ELEM etc. where appropriate. Content of TEXT nodes may appear as symbolic links.
Demonstration of Expected Result 22 A Stepwise Solution of Project Part Three Access to FUSE supported OS. Send me an e-mail for access to compute server (titan14). Take hello ll.c as template. Have a look at fuse lowlevel.h. Implement functions as demonstrated during lecture.
Low Level Functions 23 We will need to implement the following functions: static struct fuse_lowlevel_ops dbfs_ll_oper = {. lookup = dbfs_ll_lookup,. getattr = dbfs_ll_getattr,. readdir = dbfs_ll_readdir,. open = dbfs_ll_open,. read = dbfs_ll_read, }; lookup - Lookup a dir entry by name and get its attributes. getattr - Get file attributes readdir - Read directory open - Open a file read - Read data
Lookup a Directory Entry by Name 24 / fuse_lowlevel.h Look up a directory entry by name and get its attributes. Valid replies : fuse_reply_entry fuse_reply_err @param req request handle @param parent inode number of the parent directory @param name the name to look up / void ( lookup ) ( fuse_req_t req, fuse_ino_t parent, const char name );
Get File Attributes 25 / fuse_lowlevel.h Get file attributes Valid replies : fuse_reply_attr fuse_reply_err @param req request handle @param ino the inode number @param fi for future use, currently always NULL / void ( getattr ) ( fuse_req_t req, fuse_ino_t ino, struct fuse_file_info fi );
Read Directory 26 / fuse_lowlevel.h Read directory Send a buffer filled using fuse_add_direntry (), with size not exceeding the requested size. Send an empty buffer on end of stream. Valid replies : fuse_reply_buf fuse_reply_err @param req request handle @param ino the inode number @param size maximum number of bytes to send @param off offset to continue reading the directory stream @param fi file information / void ( readdir ) ( fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info fi );
Filling a Buffer in readdir 27 / fuse_lowlevel.h Add a directory entry to the buffer Buffer needs to be large enough to hold the entry. If it s not, then the entry is not filled in, but the size of the entry is still returned. The caller can check this by comparing the bufsize parameter with the returned entry size. If the entry size is larger than the buffer size, the operation failed. / size_t / return the space needed for the entry / fuse_add_direntry ( fuse_req_t req, / request handle / char buf, / point to add new entry / size_t bufsize,/ remaining size of buf / const char name, / name of the entry / const struct stat stbuf, / file atts / off_t off ); / offset of the next entry /
Open a File 28 / fuse_lowlevel.h Open a file Open flags ( with the exception of O_CREAT, O_EXCL, O_NOCTTY and O_TRUNC ) are available in fi - > flags.... Valid replies : fuse_reply_open fuse_reply_err @param req request handle @param ino the inode number @param fi file information / void ( open ) ( fuse_req_t req, fuse_ino_t ino, struct fuse_file_info fi );
Read data 29 / fuse_lowlevel.h Read data Read should send exactly the number of bytes requested except on EOF or error, otherwise the rest of the data will be substituted with zeroes.... Valid replies : fuse_reply_buf fuse_reply_err / void ( read ) ( fuse_req_t req, / request handle / fuse_ino_t ino, / inode number / size_t size, / number of bytes to read / off_t off, / offset to read from / struct fuse_file_info fi ); / file info /
Miscellaneous 30 Definitions / The node ID of the root inode / # define FUSE_ROOT_ID 1 / Inode number type / typedef unsigned long fuse_ino_t ; / Request pointer type / typedef struct fuse_req fuse_req_t ; Caveats Beware of different struct stat sizes. FUSE compiles with -D FILE OFFSET BITS=64 fts(3) does not!
Low Level Replies to Functions 31 The following replies are relevant to our functions: fuse reply err fuse reply attr fuse reply entry fuse reply buf fuse reply open
Reply with an Error Code or Success 32 / fuse_lowlevel.h Reply with an error code or success Possible requests : all except forget unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr, removexattr and setlk may send a zero code @param req request handle @param err the positive error value, or zero for success @return zero for success, - errno for failure to send reply / int fuse_reply_err ( fuse_req_t req, int err );
Reply with a Directory Entry 33 / fuse_lowlevel.h Reply with a directory entry Possible requests : lookup, mknod, mkdir, symlink, link @param req request handle @param e the entry parameters @return zero for success, - errno for failure to send reply / int fuse_reply_entry ( fuse_req_t req, const struct fuse_entry_param e); struct fuse entry param is also in fuse lowlevel.h.
Reply with Data 34 / fuse_lowlevel.h Reply with data Possible requests : read, readdir / int / zero for success, - errno for failure / fuse_reply_buf ( fuse_req_t req, / request handle / const char buf, / contains data / size_t size ); / data size in bytes /
Reply with Attributes 35 / fuse_lowlevel.h Reply with attributes Possible requests : getattr, setattr @param req request handle @param the attributes @param attr_timeout validity timeout ( in seconds ) for the attributes @return zero for success, - errno for failure to send reply / int fuse_reply_attr ( fuse_req_t req, const struct stat attr, double attr_timeout );
Reply with Open Parameters 36 / fuse_lowlevel.h Reply with open parameters currently the following members of fi are used : fh, direct_io, keep_cache Possible requests : open, opendir @param req request handle @param fi file information @return zero for success, - errno for failure to send reply / int fuse_reply_open ( fuse_req_t req, const struct fuse_file_info fi );