t9pfuse - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 5551e51d2b9df4e289ea3cb1350db4d5f5444df2
 (DIR) parent 30f8beab329834c1968c391e35890e3cd9917895
 (HTM) Author: rsc <devnull@localhost>
       Date:   Sun, 23 Jul 2006 03:02:03 +0000
       
       9pfuse
       
       Diffstat:
         A man/man4/9pfuse.4                   |      62 +++++++++++++++++++++++++++++++
         A src/cmd/9pfuse/COPYRIGHT            |      17 +++++++++++++++++
         A src/cmd/9pfuse/a.h                  |      51 +++++++++++++++++++++++++++++++
         A src/cmd/9pfuse/fuse.c               |     772 +++++++++++++++++++++++++++++++
         A src/cmd/9pfuse/fuse_kernel.h        |     312 +++++++++++++++++++++++++++++++
         A src/cmd/9pfuse/main.c               |    1169 +++++++++++++++++++++++++++++++
         A src/cmd/9pfuse/mkfile               |      12 ++++++++++++
       
       7 files changed, 2395 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/man/man4/9pfuse.4 b/man/man4/9pfuse.4
       t@@ -0,0 +1,62 @@
       +.TH 9PFUSE 4
       +.SH NAME
       +9pfuse \- mount 9P service via FUSE
       +.SH SYNOPSIS
       +.B 9pfuse
       +[
       +.B -D
       +]
       +[
       +.B -a
       +.I t
       +]
       +[
       +.B -e
       +.I t
       +]
       +.I addr
       +.I mtpt
       +.SH DESCRIPTION
       +.I 9pfuse
       +mounts the 9P service running at 
       +.I addr
       +onto 
       +.I mtpt
       +using the FUSE user-level file system driver.
       +.PP
       +.I 9pfuse
       +sets up the initial mount and then forks itself
       +into the background, where it serves the FUSE
       +protocol, translating the requests into 9P.
       +.PP
       +The options are:
       +.TP
       +.B -D
       +Print each FUSE and 9P message to standard error.
       +.TP
       +.B -a\fI t
       +Set the kernel cache timeout for attribute information
       +to 
       +.I t
       +(default 1.0) seconds.
       +.TP
       +.B -e\fI t
       +Set the kernel cache timeout for directory entries to
       +.I t
       +(default 1.0) seconds.
       +.PD
       +.PP
       +The
       +.I fusermount
       +binary must exist in the current search path.
       +.PP
       +FUSE is available for Linux 2.4.21 and later, 
       +Linux 2.6, and FreeBSD 6.x and later.
       +.SH SEE ALSO
       +FUSE Homepage,
       +.HR http://fuse.sourceforge.net
       +.PP
       +FUSE for FreeBSD,
       +.HR http://fuse4bsd.creo.hu
       +.SH SOURCE
       +.B \*9/src/cmd/9pfuse
 (DIR) diff --git a/src/cmd/9pfuse/COPYRIGHT b/src/cmd/9pfuse/COPYRIGHT
       t@@ -0,0 +1,17 @@
       +The files in this directory are subject to the following license.
       +
       +The author of this software is Russ Cox.
       +
       +        Copyright (c) 2006 Russ Cox
       +
       +Permission to use, copy, modify, and distribute this software for any
       +purpose without fee is hereby granted, provided that this entire notice
       +is included in all copies of any software which is or includes a copy
       +or modification of this software and in all copies of the supporting
       +documentation for such software.
       +
       +THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
       +WARRANTY.  IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION OR WARRANTY
       +OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS
       +FITNESS FOR ANY PARTICULAR PURPOSE.
       +
 (DIR) diff --git a/src/cmd/9pfuse/a.h b/src/cmd/9pfuse/a.h
       t@@ -0,0 +1,51 @@
       +#include <u.h>
       +#include <errno.h>
       +#include <sys/uio.h>
       +#include <sys/socket.h>
       +#include <libc.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include <9pclient.h>
       +#include "fuse_kernel.h"
       +
       +/* Somehow the FUSE guys forgot to define this one! */
       +struct fuse_create_out {
       +        struct fuse_entry_out e;
       +        struct fuse_open_out o;
       +};
       +
       +typedef struct FuseMsg FuseMsg;
       +struct FuseMsg
       +{
       +        FuseMsg *next;
       +        uchar *buf;
       +        int nbuf;
       +        struct fuse_in_header *hdr;        /* = buf */
       +        void *tx;        /* = hdr+1 */
       +};
       +
       +extern int debug;
       +
       +extern int fusefd;
       +extern int fuseeof;
       +extern int fusebufsize;
       +extern int fusemaxwrite;
       +extern FuseMsg *fusemsglist;
       +extern char *fusemtpt;
       +
       +void                freefusemsg(FuseMsg *m);
       +int                fusefmt(Fmt*);
       +void                initfuse(char *mtpt);
       +FuseMsg*        readfusemsg(void);
       +void                replyfuse(FuseMsg *m, void *arg, int narg);
       +void                replyfuseerrno(FuseMsg *m, int e);
       +void                replyfuseerrstr(FuseMsg*);
       +void                request9p(Fcall *tx);
       +
       +void*                emalloc(size_t n);
       +void*                erealloc(void *p, size_t n);
       +char*                estrdup(char *p);
       +
       +int                errstr2errno(void);
       +void unmountatexit(void);
       +
 (DIR) diff --git a/src/cmd/9pfuse/fuse.c b/src/cmd/9pfuse/fuse.c
       t@@ -0,0 +1,772 @@
       +#include "a.h"
       +
       +int fusefd;
       +int fuseeof;
       +int fusebufsize;
       +int fusemaxwrite;
       +FuseMsg *fusemsglist;
       +
       +FuseMsg*
       +allocfusemsg(void)
       +{
       +        FuseMsg *m;
       +        void *vbuf;
       +        
       +        if((m = fusemsglist) != nil){
       +                fusemsglist = m->next;
       +                return m;
       +        }
       +        m = emalloc(sizeof(*m) + fusebufsize);
       +        vbuf = m+1;
       +        m->buf = vbuf;
       +        m->nbuf = 0;
       +        m->hdr = vbuf;
       +        m->tx = m->hdr+1;
       +        return m;
       +}
       +
       +void
       +freefusemsg(FuseMsg *m)
       +{
       +        m->next = fusemsglist;
       +        fusemsglist = m;
       +}
       +
       +FuseMsg*
       +readfusemsg(void)
       +{
       +        FuseMsg *m;
       +        int n;
       +        
       +        m = allocfusemsg();
       +        errno = 0;
       +        /*
       +         * The FUSE kernel device apparently guarantees
       +         * that this read will return exactly one message.
       +         * You get an error return if you ask for just the
       +         * length (first 4 bytes).
       +         * FUSE returns an ENODEV error, not EOF,
       +         * when the connection is unmounted.
       +         */
       +        if((n = read(fusefd, m->buf, fusebufsize)) < 0){
       +                if(errno != ENODEV)
       +                        sysfatal("readfusemsg: %r");
       +        }
       +        if(n <= 0){
       +                fuseeof = 1;
       +                freefusemsg(m);
       +                return nil;
       +        }
       +        m->nbuf = n;
       +        if(m->hdr->len != n)
       +                sysfatal("readfusemsg: got %d wanted %d",
       +                        n, m->hdr->len);
       +        m->hdr->len -= sizeof(m->hdr);
       +        
       +        /*
       +         * Paranoia.
       +         * Make sure lengths are long enough.
       +         * Make sure string arguments are NUL terminated.
       +         * (I don't trust the kernel module.)
       +         */
       +        switch(m->hdr->opcode){
       +        default:
       +                /*
       +                 * Could sysfatal here, but can also let message go
       +                 * and assume higher-level code will return an
       +                 * "I don't know what you mean" error and recover.
       +                 */
       +                break;
       +        case FUSE_LOOKUP:
       +        case FUSE_UNLINK:
       +        case FUSE_RMDIR:
       +        case FUSE_REMOVEXATTR:
       +                /* just a string */
       +                if(((char*)m->tx)[m->hdr->len-1] != 0)
       +                bad:
       +                        sysfatal("readfusemsg: bad message");
       +                break;
       +        case FUSE_FORGET:
       +                if(m->hdr->len < sizeof(struct fuse_forget_in))
       +                        goto bad;
       +                break;
       +        case FUSE_GETATTR:
       +                break;
       +        case FUSE_SETATTR:
       +                if(m->hdr->len < sizeof(struct fuse_setattr_in))
       +                        goto bad;
       +                break;
       +        case FUSE_READLINK:
       +                break;
       +        case FUSE_SYMLINK:
       +                /* two strings */
       +                if(((char*)m->tx)[m->hdr->len-1] != 0
       +                || memchr(m->tx, 0, m->hdr->len-1) == 0)
       +                        goto bad;
       +                break;        
       +        case FUSE_MKNOD:
       +                if(m->hdr->len <= sizeof(struct fuse_mknod_in)
       +                || ((char*)m->tx)[m->hdr->len-1] != 0)
       +                        goto bad;
       +                break;
       +        case FUSE_MKDIR:
       +                if(m->hdr->len <= sizeof(struct fuse_mkdir_in)
       +                || ((char*)m->tx)[m->hdr->len-1] != 0)
       +                        goto bad;
       +                break;
       +        case FUSE_RENAME:
       +                /* a struct and two strings */
       +                if(m->hdr->len <= sizeof(struct fuse_rename_in)
       +                || ((char*)m->tx)[m->hdr->len-1] != 0
       +                || memchr((uchar*)m->tx+sizeof(struct fuse_rename_in), 0, m->hdr->len-sizeof(struct fuse_rename_in)-1) == 0)
       +                        goto bad;
       +                break;
       +        case FUSE_LINK:
       +                if(m->hdr->len <= sizeof(struct fuse_link_in)
       +                || ((char*)m->tx)[m->hdr->len-1] != 0)
       +                        goto bad;
       +                break;
       +        case FUSE_OPEN:
       +        case FUSE_OPENDIR:
       +                if(m->hdr->len < sizeof(struct fuse_open_in))
       +                        goto bad;
       +                break;
       +        case FUSE_READ:
       +        case FUSE_READDIR:
       +                if(m->hdr->len < sizeof(struct fuse_read_in))
       +                        goto bad;
       +                break;
       +        case FUSE_WRITE:
       +                /* no strings, but check that write length is sane */
       +                if(m->hdr->len < sizeof(struct fuse_write_in)+((struct fuse_write_in*)m->tx)->size)
       +                        goto bad;
       +                break;
       +        case FUSE_STATFS:
       +                break;
       +        case FUSE_RELEASE:
       +        case FUSE_RELEASEDIR:
       +                if(m->hdr->len < sizeof(struct fuse_release_in))
       +                        goto bad;
       +                break;
       +        case FUSE_FSYNC:
       +        case FUSE_FSYNCDIR:
       +                if(m->hdr->len < sizeof(struct fuse_fsync_in))
       +                        goto bad;
       +                break;
       +        case FUSE_SETXATTR:
       +                /* struct and two strings */
       +                if(m->hdr->len <= sizeof(struct fuse_setxattr_in)
       +                || ((char*)m->tx)[m->hdr->len-1] != 0
       +                || memchr((uchar*)m->tx+sizeof(struct fuse_setxattr_in), 0, m->hdr->len-sizeof(struct fuse_setxattr_in)-1) == 0)
       +                        goto bad;
       +                break;
       +        case FUSE_GETXATTR:
       +                /* struct and one string */
       +                if(m->hdr->len <= sizeof(struct fuse_getxattr_in)
       +                || ((char*)m->tx)[m->hdr->len-1] != 0)
       +                        goto bad;
       +                break;
       +        case FUSE_LISTXATTR:
       +                if(m->hdr->len < sizeof(struct fuse_getxattr_in))
       +                        goto bad;
       +                break;
       +        case FUSE_FLUSH:
       +                if(m->hdr->len < sizeof(struct fuse_flush_in))
       +                        goto bad;
       +                break;
       +        case FUSE_INIT:
       +                if(m->hdr->len < sizeof(struct fuse_init_in))
       +                        goto bad;
       +                break;
       +        case FUSE_ACCESS:
       +                if(m->hdr->len < sizeof(struct fuse_access_in))
       +                        goto bad;
       +                break;
       +        case FUSE_CREATE:
       +                if(m->hdr->len <= sizeof(struct fuse_open_in)
       +                || ((char*)m->tx)[m->hdr->len-1] != 0)
       +                        goto bad;
       +                break;
       +        }
       +        if(debug)
       +                fprint(2, "FUSE -> %G\n", m->hdr, m->tx);
       +        return m;
       +}
       +
       +/*
       + * Reply to FUSE request m using additonal 
       + * argument buffer arg of size narg bytes.
       + * Perhaps should free the FuseMsg here?
       + */
       +void
       +replyfuse(FuseMsg *m, void *arg, int narg)
       +{
       +        struct iovec vec[2];
       +        struct fuse_out_header hdr;
       +        int nvec;
       +        
       +        hdr.len = sizeof hdr + narg;
       +        hdr.error = 0;
       +        hdr.unique = m->hdr->unique;
       +        if(debug)
       +                fprint(2, "FUSE <- %#G\n", m->hdr, &hdr, arg);
       +
       +        vec[0].iov_base = &hdr;
       +        vec[0].iov_len = sizeof hdr;
       +        nvec = 1;
       +        if(arg && narg){
       +                vec[1].iov_base = arg;
       +                vec[1].iov_len = narg;
       +                nvec++;
       +        }
       +        if(writev(fusefd, vec, nvec) < 0)
       +                sysfatal("replyfuse: %r");
       +}
       +
       +/*
       + * Reply to FUSE request m with errno e.
       + */
       +void
       +replyfuseerrno(FuseMsg *m, int e)
       +{
       +        struct fuse_out_header hdr;
       +        
       +        hdr.len = sizeof hdr;
       +        hdr.error = -e;        /* FUSE sends negative errnos. */
       +        hdr.unique = m->hdr->unique;
       +        if(debug)
       +                fprint(2, "FUSE <- %#G\n", m->hdr, &hdr, 0);
       +        if(write(fusefd, &hdr, sizeof hdr) < 0)
       +                sysfatal("replyfuseerror: %r");
       +}
       +
       +void
       +replyfuseerrstr(FuseMsg *m)
       +{
       +        replyfuseerrno(m, errstr2errno());
       +}
       +
       +/*
       + * Mounts a fuse file system on mtpt and returns
       + * a file descriptor for the corresponding fuse 
       + * message conversation.
       + */
       +int
       +mountfuse(char *mtpt)
       +{
       +        int p[2], pid, fd;
       +        char buf[20];
       +        
       +        if(socketpair(AF_UNIX, SOCK_STREAM, 0, p) < 0)
       +                return -1;
       +        pid = fork();
       +        if(pid < 0)
       +                return -1;
       +        if(pid == 0){
       +                close(p[1]);
       +                snprint(buf, sizeof buf, "%d", p[0]);
       +                putenv("_FUSE_COMMFD", buf);
       +                execlp("fusermount", "fusermount", "--", mtpt, nil);
       +                fprint(2, "exec fusermount: %r\n");
       +                _exit(1);
       +        }
       +        close(p[0]);
       +        fd = recvfd(p[1]);
       +        close(p[1]);
       +        waitpid();
       +        return fd;
       +}
       +
       +void
       +unmountfuse(char *mtpt)
       +{
       +        int pid;
       +        
       +        pid = fork();
       +        if(pid < 0)
       +                return;
       +        if(pid == 0){
       +                atexitdont(unmountatexit);
       +                execlp("fusermount", "fusermount", "-u", "-z", "--", mtpt, nil);
       +                fprint(2, "exec fusermount -u: %r\n");
       +                _exit(1);
       +        }
       +        waitpid();
       +}
       +
       +char *fusemtpt;
       +void
       +unmountatexit(void)
       +{
       +        if(fusemtpt)
       +                unmountfuse(fusemtpt);
       +}
       +
       +void
       +initfuse(char *mtpt)
       +{
       +        FuseMsg *m;
       +        struct fuse_init_in *tx;
       +        struct fuse_init_out rx;
       +
       +        fusemtpt = mtpt;
       +
       +        /*
       +         * The 4096 is for the message headers.
       +         * It's a lot, but it's what the FUSE libraries ask for.
       +         */
       +        fusemaxwrite = getpagesize();
       +        fusebufsize = 4096 + fusemaxwrite;
       +
       +        if((fusefd = mountfuse(mtpt)) < 0)
       +                sysfatal("mountfuse: %r");
       +
       +        if((m = readfusemsg()) == nil)
       +                sysfatal("readfusemsg: %r");
       +        if(m->hdr->opcode != FUSE_INIT)
       +                sysfatal("fuse: expected FUSE_INIT (26) got %d", m->hdr->opcode);
       +        tx = m->tx;
       +
       +        /*
       +         * Complain if the kernel is too new.
       +         * We could forge ahead, but at least the one time I tried,
       +         * the kernel rejected the newer version by making the 
       +         * writev fail in replyfuse, which is a much more confusing
       +         * error message.  In the future, might be nice to try to 
       +         * support older versions that differ only slightly.
       +         */
       +        if(tx->major < FUSE_KERNEL_VERSION 
       +        || (tx->major == FUSE_KERNEL_VERSION && tx->minor < FUSE_KERNEL_MINOR_VERSION))
       +                sysfatal("fuse: too kernel version %d.%d older than program version %d.%d",
       +                        tx->major, tx->minor, FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
       +
       +        memset(&rx, 0, sizeof rx);
       +        rx.major = FUSE_KERNEL_VERSION;
       +        rx.minor = FUSE_KERNEL_MINOR_VERSION;
       +        rx.max_write = fusemaxwrite;
       +        replyfuse(m, &rx, sizeof rx);
       +        freefusemsg(m);
       +}
       +
       +/*
       + * Print FUSE messages.  Assuming it is installed as %G,
       + * use %G with hdr, arg arguments to format a request,
       + * and %#G with reqhdr, hdr, arg arguments to format a response.
       + * The reqhdr is necessary in the %#G form because the
       + * response does not contain an opcode tag.
       + */
       +int
       +fusefmt(Fmt *fmt)
       +{
       +        struct fuse_in_header *hdr = va_arg(fmt->args, void*);
       +        if((fmt->flags&FmtSharp) == 0){  /* "%G", hdr, arg */
       +                void *a = va_arg(fmt->args, void*);
       +                fmtprint(fmt, "len %d unique %#llux uid %d gid %d pid %d ",
       +                        hdr->len, hdr->unique, hdr->uid, hdr->gid, hdr->pid);
       +
       +                switch(hdr->opcode){
       +                        default: {
       +                                fmtprint(fmt, "??? opcode %d", hdr->opcode);
       +                                break;
       +                        }
       +                        case FUSE_LOOKUP: {
       +                                fmtprint(fmt, "Lookup nodeid %#llux name %#q",
       +                                        hdr->nodeid, a);
       +                                break;
       +                        }
       +                        case FUSE_FORGET: {
       +                                struct fuse_forget_in *tx = a;
       +                                /* nlookup (a ref count) is a vlong! */
       +                                fmtprint(fmt, "Forget nodeid %#llux nlookup %lld",
       +                                        hdr->nodeid, tx->nlookup);
       +                                break;
       +                        }
       +                        case FUSE_GETATTR: {
       +                                fmtprint(fmt, "Getattr nodeid %#llux", hdr->nodeid);
       +                                break;
       +                        }
       +                        case FUSE_SETATTR: {
       +                                struct fuse_setattr_in *tx = a;
       +                                fmtprint(fmt, "Setattr nodeid %#llux", hdr->nodeid);
       +                                if(tx->valid&FATTR_FH)
       +                                        fmtprint(fmt, " fh %#llux", tx->fh);
       +                                if(tx->valid&FATTR_SIZE)
       +                                        fmtprint(fmt, " size %lld", tx->size);
       +                                if(tx->valid&FATTR_ATIME)
       +                                        fmtprint(fmt, " atime %.20g", tx->atime+tx->atimensec*1e-9);
       +                                if(tx->valid&FATTR_MTIME)
       +                                        fmtprint(fmt, " mtime %.20g", tx->mtime+tx->mtimensec*1e-9);
       +                                if(tx->valid&FATTR_MODE)
       +                                        fmtprint(fmt, " mode %#uo", tx->mode);
       +                                if(tx->valid&FATTR_UID)
       +                                        fmtprint(fmt, " uid %d", tx->uid);
       +                                if(tx->valid&FATTR_GID)
       +                                        fmtprint(fmt, " gid %d", tx->gid);
       +                                break;
       +                        }
       +                        case FUSE_READLINK: {
       +                                fmtprint(fmt, "Readlink nodeid %#llux", hdr->nodeid);
       +                                break;
       +                        }
       +                        case FUSE_SYMLINK: {
       +                                char *old, *new;
       +                                
       +                                old = a;
       +                                new = a + strlen(a) + 1;
       +                                fmtprint(fmt, "Symlink nodeid %#llux old %#q new %#q",
       +                                        hdr->nodeid, old, new);
       +                                break;
       +                        }
       +                        case FUSE_MKNOD: {
       +                                struct fuse_mknod_in *tx = a;
       +                                fmtprint(fmt, "Mknod nodeid %#llux mode %#uo rdev %#ux name %#q",
       +                                        hdr->nodeid, tx->mode, tx->rdev, tx+1);
       +                                break;
       +                        }
       +                        case FUSE_MKDIR: {
       +                                struct fuse_mkdir_in *tx = a;
       +                                fmtprint(fmt, "Mkdir nodeid %#llux mode %#uo name %#q",
       +                                        hdr->nodeid, tx->mode, tx+1);
       +                                break;
       +                        }
       +                        case FUSE_UNLINK: {
       +                                fmtprint(fmt, "Unlink nodeid %#llux name %#q",
       +                                        hdr->nodeid, a);
       +                                break;
       +                        }
       +                        case FUSE_RMDIR: {
       +                                fmtprint(fmt, "Rmdir nodeid %#llux name %#q",
       +                                        hdr->nodeid, a);
       +                                break;
       +                        }
       +                        case FUSE_RENAME: {
       +                                struct fuse_rename_in *tx = a;
       +                                char *old = (char*)(tx+1);
       +                                char *new = old + strlen(old) + 1;
       +                                fmtprint(fmt, "Rename nodeid %#llux old %#q newdir %#llux new %#q",
       +                                        hdr->nodeid, old, tx->newdir, new);
       +                                break;
       +                        }
       +                        case FUSE_LINK: {
       +                                struct fuse_link_in *tx = a;
       +                                fmtprint(fmt, "Link oldnodeid %#llux nodeid %#llux name %#q",
       +                                        tx->oldnodeid, hdr->nodeid, tx+1);
       +                                break;
       +                        }
       +                        case FUSE_OPEN: {
       +                                struct fuse_open_in *tx = a;
       +                                /* Should one or both of flags and mode be octal? */
       +                                fmtprint(fmt, "Open nodeid %#llux flags %#ux mode %#ux",
       +                                        hdr->nodeid, tx->flags, tx->mode);
       +                                break;
       +                        }
       +                        case FUSE_READ: {
       +                                struct fuse_read_in *tx = a;
       +                                fmtprint(fmt, "Read nodeid %#llux fh %#llux offset %lld size %ud",
       +                                        hdr->nodeid, tx->fh, tx->offset, tx->size);
       +                                break;
       +                        }
       +                        case FUSE_WRITE: {
       +                                struct fuse_write_in *tx = a;
       +                                fmtprint(fmt, "Write nodeid %#llux fh %#llux offset %lld size %ud flags %#ux",
       +                                        hdr->nodeid, tx->fh, tx->offset, tx->size, tx->write_flags);
       +                                break;
       +                        }
       +                        case FUSE_STATFS: {
       +                                fmtprint(fmt, "Statfs");
       +                                break;
       +                        }
       +                        case FUSE_RELEASE: {
       +                                struct fuse_release_in *tx = a;
       +                                fmtprint(fmt, "Release nodeid %#llux fh %#llux flags %#ux",
       +                                        hdr->nodeid, tx->fh, tx->flags); 
       +                                break;
       +                        }
       +                        case FUSE_FSYNC: {
       +                                struct fuse_fsync_in *tx = a;
       +                                fmtprint(fmt, "Fsync nodeid %#llux fh %#llux flags %#ux",
       +                                        hdr->nodeid, tx->fh, tx->fsync_flags);
       +                                break;
       +                        }
       +                        case FUSE_SETXATTR: {
       +                                struct fuse_setxattr_in *tx = a;
       +                                char *name = (char*)(tx+1);
       +                                char *value = name + strlen(name) + 1;
       +                                fmtprint(fmt, "Setxattr nodeid %#llux size %d flags %#ux name %#q value %#q",
       +                                        hdr->nodeid, tx->size, tx->flags, name, value);
       +                                break;
       +                        }
       +                        case FUSE_GETXATTR: {
       +                                struct fuse_getxattr_in *tx = a;
       +                                fmtprint(fmt, "Getxattr nodeid %#llux size %d name %#q",
       +                                        hdr->nodeid, tx->size, tx+1);
       +                                break;
       +                        }
       +                        case FUSE_LISTXATTR: {
       +                                struct fuse_getxattr_in *tx = a;
       +                                fmtprint(fmt, "Listxattr nodeid %#llux size %d",
       +                                        hdr->nodeid, tx->size);
       +                                break;
       +                        }
       +                        case FUSE_REMOVEXATTR: {
       +                                fmtprint(fmt, "Removexattr nodeid %#llux name %#q",
       +                                        hdr->nodeid, a);
       +                                break;
       +                        }
       +                        case FUSE_FLUSH: {
       +                                struct fuse_flush_in *tx = a;
       +                                fmtprint(fmt, "Flush nodeid %#llux fh %#llux flags %#ux",
       +                                        hdr->nodeid, tx->fh, tx->flush_flags);
       +                                break;
       +                        }
       +                        case FUSE_INIT: {
       +                                struct fuse_init_in *tx = a;
       +                                fmtprint(fmt, "Init major %d minor %d",
       +                                        tx->major, tx->minor);
       +                                break;
       +                        }
       +                        case FUSE_OPENDIR: {
       +                                struct fuse_open_in *tx = a;
       +                                fmtprint(fmt, "Opendir nodeid %#llux flags %#ux mode %#ux",
       +                                        hdr->nodeid, tx->flags, tx->mode);
       +                                break;
       +                        }
       +                        case FUSE_READDIR: {
       +                                struct fuse_read_in *tx = a;
       +                                fmtprint(fmt, "Readdir nodeid %#llux fh %#llux offset %lld size %ud",
       +                                        hdr->nodeid, tx->fh, tx->offset, tx->size);
       +                                break;
       +                        }
       +                        case FUSE_RELEASEDIR: {
       +                                struct fuse_release_in *tx = a;
       +                                fmtprint(fmt, "Releasedir nodeid %#llux fh %#llux flags %#ux",
       +                                        hdr->nodeid, tx->fh, tx->flags); 
       +                                break;
       +                        }
       +                        case FUSE_FSYNCDIR: {
       +                                struct fuse_fsync_in *tx = a;
       +                                fmtprint(fmt, "Fsyncdir nodeid %#llux fh %#llux flags %#ux",
       +                                        hdr->nodeid, tx->fh, tx->fsync_flags);
       +                                break;
       +                        }
       +                        case FUSE_ACCESS: {
       +                                struct fuse_access_in *tx  = a;
       +                                fmtprint(fmt, "Access nodeid %#llux mask %#ux",
       +                                        hdr->nodeid, tx->mask);
       +                                break;
       +                        }
       +                        case FUSE_CREATE: {
       +                                struct fuse_open_in *tx = a;
       +                                fmtprint(fmt, "Create nodeid %#llx flags %#ux mode %#ux name %#q",
       +                                        hdr->nodeid, tx->flags, tx->mode, tx+1);
       +                                break;
       +                        }
       +                }
       +        }else{  /* "%#G", reqhdr, hdr, arg - use reqhdr only for type */
       +                struct fuse_out_header *ohdr = va_arg(fmt->args, void*);
       +                void *a = va_arg(fmt->args, void*);
       +                int len = ohdr->len - sizeof *ohdr;
       +                fmtprint(fmt, "unique %#llux ", ohdr->unique);
       +                if(ohdr->error){
       +                        fmtprint(fmt, "error %d %s", ohdr->error, strerror(-ohdr->error));
       +                }else
       +                switch(hdr->opcode){
       +                        default: {
       +                                fmtprint(fmt, "??? opcode %d", hdr->opcode);
       +                                break;
       +                        }
       +                        case FUSE_LOOKUP: {
       +                                /*
       +                                 * For a negative entry, can send back ENOENT
       +                                 * or rx->ino == 0.  
       +                                 * In protocol version 7.4 and before, can only use
       +                                 * the ENOENT method.
       +                                 * Presumably the benefit of sending rx->ino == 0
       +                                 * is that you can specify the length of time to cache
       +                                 * the negative result.
       +                                 */
       +                                struct fuse_entry_out *rx;
       +                                fmtprint(fmt, "(Lookup) ");
       +                        fmt_entry_out:
       +                                rx = a;
       +                                fmtprint(fmt, "nodeid %#llux gen %#llux entry_valid %.20g attr_valid %.20g ",
       +                                        rx->nodeid, rx->generation,
       +                                        rx->entry_valid+rx->entry_valid_nsec*1e-9,
       +                                        rx->attr_valid+rx->attr_valid_nsec*1e-9);
       +                                fmtprint(fmt, " ino %#llux size %lld blocks %lld atime %.20g mtime %.20g ctime %.20g mode %#uo nlink %d uid %d gid %d rdev %#ux",
       +                                        rx->attr.ino, rx->attr.size, rx->attr.blocks,
       +                                        rx->attr.atime+rx->attr.atimensec*1e-9, 
       +                                        rx->attr.mtime+rx->attr.mtimensec*1e-9,
       +                                        rx->attr.ctime+rx->attr.ctimensec*1e-9,
       +                                        rx->attr.mode, rx->attr.nlink, rx->attr.uid,
       +                                        rx->attr.gid, rx->attr.rdev);
       +                                break;
       +                        }
       +                        case FUSE_FORGET: {
       +                                /* Can't happen! No reply. */
       +                                fmtprint(fmt, "(Forget) can't happen");
       +                                break;
       +                        }
       +                        case FUSE_GETATTR: {
       +                                struct fuse_attr_out *rx;
       +                                fmtprint(fmt, "(Getattr) ");
       +                        fmt_attr_out:
       +                                rx = a;
       +                                fmtprint(fmt, "attr_valid %.20g",
       +                                        rx->attr_valid+rx->attr_valid_nsec*1e-9);
       +                                fmtprint(fmt, " ino %#llux size %lld blocks %lld atime %.20g mtime %.20g ctime %.20g mode %#uo nlink %d uid %d gid %d rdev %#ux",
       +                                        rx->attr.ino, rx->attr.size, rx->attr.blocks,
       +                                        rx->attr.atime+rx->attr.atimensec*1e-9, 
       +                                        rx->attr.mtime+rx->attr.mtimensec*1e-9,
       +                                        rx->attr.ctime+rx->attr.ctimensec*1e-9,
       +                                        rx->attr.mode, rx->attr.nlink, rx->attr.uid,
       +                                        rx->attr.gid, rx->attr.rdev);
       +                                break;
       +                        }
       +                        case FUSE_SETATTR: {
       +                                fmtprint(fmt, "(Setattr) ");
       +                                goto fmt_attr_out;
       +                                break;
       +                        }
       +                        case FUSE_READLINK: {
       +                                fmtprint(fmt, "(Readlink) %#.*q",
       +                                        utfnlen(a, len), a);
       +                                break;
       +                        }
       +                        case FUSE_SYMLINK: {
       +                                fmtprint(fmt, "(Symlink) ");
       +                                goto fmt_entry_out;
       +                                break;
       +                        }
       +                        case FUSE_MKNOD: {
       +                                fmtprint(fmt, "(Mknod) ");
       +                                goto fmt_entry_out;
       +                                break;
       +                        }
       +                        case FUSE_MKDIR: {
       +                                fmtprint(fmt, "(Mkdir) ");
       +                                goto fmt_entry_out;
       +                                break;
       +                        }
       +                        case FUSE_UNLINK: {
       +                                fmtprint(fmt, "(Unlink)");
       +                                break;
       +                        }
       +                        case FUSE_RMDIR: {
       +                                fmtprint(fmt, "(Rmdir)");
       +                                break;
       +                        }
       +                        case FUSE_RENAME: {
       +                                fmtprint(fmt, "(Rename)");
       +                                break;
       +                        }
       +                        case FUSE_LINK: {
       +                                fmtprint(fmt, "(Link) ");
       +                                goto fmt_entry_out;
       +                                break;
       +                        }
       +                        case FUSE_OPEN: {
       +                                struct fuse_open_out *rx;
       +                                fmtprint(fmt, "(Open) ");
       +                        fmt_open_out:
       +                                rx = a;
       +                                fmtprint(fmt, "fh %#llux flags %#ux", rx->fh, rx->open_flags);
       +                                break;
       +                        }
       +                        case FUSE_READ: {
       +                                fmtprint(fmt, "(Read) size %d", len);
       +                                break;
       +                        }
       +                        case FUSE_WRITE: {
       +                                struct fuse_write_out *rx = a;
       +                                fmtprint(fmt, "(Write) size %d", rx->size);
       +                                break;
       +                        }
       +                        case FUSE_STATFS: {
       +                                /*
       +                                 * Before protocol version 7.4, only first 48 bytes are used.
       +                                 */
       +                                struct fuse_statfs_out *rx = a;
       +                                fmtprint(fmt, "(Statfs) blocks %lld bfree %lld bavail %lld files %lld ffree %lld bsize %ud namelen %ud frsize %ud",
       +                                        rx->st.blocks, rx->st.bfree, rx->st.bavail,
       +                                        rx->st.files, rx->st.ffree, rx->st.bsize,
       +                                        rx->st.namelen, rx->st.frsize);
       +                                break;
       +                        }
       +                        case FUSE_RELEASE: {
       +                                fmtprint(fmt, "(Release)");
       +                                break;
       +                        }
       +                        case FUSE_FSYNC: {
       +                                fmtprint(fmt, "(Fsync)");
       +                                break;
       +                        }
       +                        case FUSE_SETXATTR: {
       +                                fmtprint(fmt, "(Serxattr)");
       +                                break;
       +                        }
       +                        case FUSE_GETXATTR: {
       +                                fmtprint(fmt, "(Getxattr) size %d", len);
       +                                break;
       +                        }
       +                        case FUSE_LISTXATTR: {
       +                                fmtprint(fmt, "(Lisrxattr) size %d", len);
       +                                break;
       +                        }
       +                        case FUSE_REMOVEXATTR: {
       +                                fmtprint(fmt, "(Removexattr)");
       +                                break;
       +                        }
       +                        case FUSE_FLUSH: {
       +                                fmtprint(fmt, "(Flush)");
       +                                break;
       +                        }
       +                        case FUSE_INIT: {
       +                                struct fuse_init_out *rx = a;
       +                                fmtprint(fmt, "(Init) major %d minor %d max_write %d",
       +                                        rx->major, rx->minor, rx->max_write);
       +                                break;
       +                        }
       +                        case FUSE_OPENDIR: {
       +                                fmtprint(fmt, "(Opendir) ");
       +                                goto fmt_open_out;
       +                                break;
       +                        }
       +                        case FUSE_READDIR: {
       +                                fmtprint(fmt, "(Readdir) size %d", len);
       +                                break;
       +                        }
       +                        case FUSE_RELEASEDIR: {
       +                                fmtprint(fmt, "(Releasedir)");
       +                                break;
       +                        }
       +                        case FUSE_FSYNCDIR: {
       +                                fmtprint(fmt, "(Fsyncdir)");
       +                                break;
       +                        }
       +                        case FUSE_ACCESS: {
       +                                fmtprint(fmt, "(Access)");
       +                                break;
       +                        }
       +                        case FUSE_CREATE: {
       +                                struct fuse_create_out *rx = a;
       +                                fmtprint(fmt, "(Create) ");
       +                                fmtprint(fmt, "nodeid %#llux gen %#llux entry_valid %.20g attr_valid %.20g ",
       +                                        rx->e.nodeid, rx->e.generation,
       +                                        rx->e.entry_valid+rx->e.entry_valid_nsec*1e-9,
       +                                        rx->e.attr_valid+rx->e.attr_valid_nsec*1e-9);
       +                                fmtprint(fmt, " ino %#llux size %lld blocks %lld atime %.20g mtime %.20g ctime %.20g mode %#uo nlink %d uid %d gid %d rdev %#ux",
       +                                        rx->e.attr.ino, rx->e.attr.size, rx->e.attr.blocks,
       +                                        rx->e.attr.atime+rx->e.attr.atimensec*1e-9, 
       +                                        rx->e.attr.mtime+rx->e.attr.mtimensec*1e-9,
       +                                        rx->e.attr.ctime+rx->e.attr.ctimensec*1e-9,
       +                                        rx->e.attr.mode, rx->e.attr.nlink, rx->e.attr.uid,
       +                                        rx->e.attr.gid, rx->e.attr.rdev);
       +                                fmtprint(fmt, " fh %#llux flags %#ux", rx->o.fh, rx->o.open_flags);
       +                                break;
       +                        }
       +                }
       +        }
       +        return 0;
       +}
       +
 (DIR) diff --git a/src/cmd/9pfuse/fuse_kernel.h b/src/cmd/9pfuse/fuse_kernel.h
       t@@ -0,0 +1,312 @@
       +/* This file defines the kernel interface of FUSE */
       +
       +/*
       +    This -- and only this -- header file may also be distributed under
       +    the terms of the BSD Licence as follows:
       +
       +    Copyright (C) 2001-2006 Miklos Szeredi. All rights reserved.
       +
       +    Redistribution and use in source and binary forms, with or without
       +    modification, are permitted provided that the following conditions
       +    are met:
       +    1. Redistributions of source code must retain the above copyright
       +       notice, this list of conditions and the following disclaimer.
       +    2. Redistributions in binary form must reproduce the above copyright
       +       notice, this list of conditions and the following disclaimer in the
       +       documentation and/or other materials provided with the distribution.
       +
       +    THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
       +    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
       +    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
       +    ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
       +    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
       +    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
       +    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
       +    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
       +    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
       +    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
       +    SUCH DAMAGE.
       +*/
       +
       +/* RSC changed these lines */
       +#include <inttypes.h>
       +#define __u64 uint64_t
       +#define __u32 uint32_t
       +#define __s32 int32_t
       +
       +/** Version number of this interface */
       +#define FUSE_KERNEL_VERSION 7
       +
       +/** Minor version number of this interface */
       +#define FUSE_KERNEL_MINOR_VERSION 5
       +
       +/** The node ID of the root inode */
       +#define FUSE_ROOT_ID 1
       +
       +/** The major number of the fuse character device */
       +#define FUSE_MAJOR 10
       +
       +/** The minor number of the fuse character device */
       +#define FUSE_MINOR 229
       +
       +/* Make sure all structures are padded to 64bit boundary, so 32bit
       +   userspace works under 64bit kernels */
       +
       +struct fuse_attr {
       +        __u64        ino;
       +        __u64        size;
       +        __u64        blocks;
       +        __u64        atime;
       +        __u64        mtime;
       +        __u64        ctime;
       +        __u32        atimensec;
       +        __u32        mtimensec;
       +        __u32        ctimensec;
       +        __u32        mode;
       +        __u32        nlink;
       +        __u32        uid;
       +        __u32        gid;
       +        __u32        rdev;
       +};
       +
       +struct fuse_kstatfs {
       +        __u64        blocks;
       +        __u64        bfree;
       +        __u64        bavail;
       +        __u64        files;
       +        __u64        ffree;
       +        __u32        bsize;
       +        __u32        namelen;
       +        __u32        frsize;
       +        __u32        padding;
       +        __u32        spare[6];
       +};
       +
       +#define FATTR_MODE        (1 << 0)
       +#define FATTR_UID        (1 << 1)
       +#define FATTR_GID        (1 << 2)
       +#define FATTR_SIZE        (1 << 3)
       +#define FATTR_ATIME        (1 << 4)
       +#define FATTR_MTIME        (1 << 5)
       +#define FATTR_FH        (1 << 6)
       +
       +/**
       + * Flags returned by the OPEN request
       + *
       + * FOPEN_DIRECT_IO: bypass page cache for this open file
       + * FOPEN_KEEP_CACHE: don't invalidate the data cache on open
       + */
       +#define FOPEN_DIRECT_IO                (1 << 0)
       +#define FOPEN_KEEP_CACHE        (1 << 1)
       +
       +enum fuse_opcode {
       +        FUSE_LOOKUP           = 1,
       +        FUSE_FORGET           = 2,  /* no reply */
       +        FUSE_GETATTR           = 3,
       +        FUSE_SETATTR           = 4,
       +        FUSE_READLINK           = 5,
       +        FUSE_SYMLINK           = 6,
       +        FUSE_MKNOD           = 8,
       +        FUSE_MKDIR           = 9,
       +        FUSE_UNLINK           = 10,
       +        FUSE_RMDIR           = 11,
       +        FUSE_RENAME           = 12,
       +        FUSE_LINK           = 13,
       +        FUSE_OPEN           = 14,
       +        FUSE_READ           = 15,
       +        FUSE_WRITE           = 16,
       +        FUSE_STATFS           = 17,
       +        FUSE_RELEASE       = 18,
       +        FUSE_FSYNC         = 20,
       +        FUSE_SETXATTR      = 21,
       +        FUSE_GETXATTR      = 22,
       +        FUSE_LISTXATTR     = 23,
       +        FUSE_REMOVEXATTR   = 24,
       +        FUSE_FLUSH         = 25,
       +        FUSE_INIT          = 26,
       +        FUSE_OPENDIR       = 27,
       +        FUSE_READDIR       = 28,
       +        FUSE_RELEASEDIR    = 29,
       +        FUSE_FSYNCDIR      = 30,
       +        FUSE_ACCESS        = 34,
       +        FUSE_CREATE        = 35
       +};
       +
       +/* The read buffer is required to be at least 8k, but may be much larger */
       +#define FUSE_MIN_READ_BUFFER 8192
       +
       +struct fuse_entry_out {
       +        __u64        nodeid;                /* Inode ID */
       +        __u64        generation;        /* Inode generation: nodeid:gen must
       +                                   be unique for the fs's lifetime */
       +        __u64        entry_valid;        /* Cache timeout for the name */
       +        __u64        attr_valid;        /* Cache timeout for the attributes */
       +        __u32        entry_valid_nsec;
       +        __u32        attr_valid_nsec;
       +        struct fuse_attr attr;
       +};
       +
       +struct fuse_forget_in {
       +        __u64        nlookup;
       +};
       +
       +struct fuse_attr_out {
       +        __u64        attr_valid;        /* Cache timeout for the attributes */
       +        __u32        attr_valid_nsec;
       +        __u32        dummy;
       +        struct fuse_attr attr;
       +};
       +
       +struct fuse_mknod_in {
       +        __u32        mode;
       +        __u32        rdev;
       +};
       +
       +struct fuse_mkdir_in {
       +        __u32        mode;
       +        __u32        padding;
       +};
       +
       +struct fuse_rename_in {
       +        __u64        newdir;
       +};
       +
       +struct fuse_link_in {
       +        __u64        oldnodeid;
       +};
       +
       +struct fuse_setattr_in {
       +        __u32        valid;
       +        __u32        padding;
       +        __u64        fh;
       +        __u64        size;
       +        __u64        unused1;
       +        __u64        atime;
       +        __u64        mtime;
       +        __u64        unused2;
       +        __u32        atimensec;
       +        __u32        mtimensec;
       +        __u32        unused3;
       +        __u32        mode;
       +        __u32        unused4;
       +        __u32        uid;
       +        __u32        gid;
       +        __u32        unused5;
       +};
       +
       +struct fuse_open_in {
       +        __u32        flags;
       +        __u32        mode;
       +};
       +
       +struct fuse_open_out {
       +        __u64        fh;
       +        __u32        open_flags;
       +        __u32        padding;
       +};
       +
       +struct fuse_release_in {
       +        __u64        fh;
       +        __u32        flags;
       +        __u32        padding;
       +};
       +
       +struct fuse_flush_in {
       +        __u64        fh;
       +        __u32        flush_flags;
       +        __u32        padding;
       +};
       +
       +struct fuse_read_in {
       +        __u64        fh;
       +        __u64        offset;
       +        __u32        size;
       +        __u32        padding;
       +};
       +
       +struct fuse_write_in {
       +        __u64        fh;
       +        __u64        offset;
       +        __u32        size;
       +        __u32        write_flags;
       +};
       +
       +struct fuse_write_out {
       +        __u32        size;
       +        __u32        padding;
       +};
       +
       +#define FUSE_COMPAT_STATFS_SIZE 48
       +
       +struct fuse_statfs_out {
       +        struct fuse_kstatfs st;
       +};
       +
       +struct fuse_fsync_in {
       +        __u64        fh;
       +        __u32        fsync_flags;
       +        __u32        padding;
       +};
       +
       +struct fuse_setxattr_in {
       +        __u32        size;
       +        __u32        flags;
       +};
       +
       +struct fuse_getxattr_in {
       +        __u32        size;
       +        __u32        padding;
       +};
       +
       +struct fuse_getxattr_out {
       +        __u32        size;
       +        __u32        padding;
       +};
       +
       +struct fuse_access_in {
       +        __u32        mask;
       +        __u32        padding;
       +};
       +
       +struct fuse_init_in {
       +        __u32        major;
       +        __u32        minor;
       +};
       +
       +struct fuse_init_out {
       +        __u32        major;
       +        __u32        minor;
       +        __u32        unused[3];
       +        __u32        max_write;
       +};
       +
       +struct fuse_in_header {
       +        __u32        len;
       +        __u32        opcode;
       +        __u64        unique;
       +        __u64        nodeid;
       +        __u32        uid;
       +        __u32        gid;
       +        __u32        pid;
       +        __u32        padding;
       +};
       +
       +struct fuse_out_header {
       +        __u32        len;
       +        __s32        error;
       +        __u64        unique;
       +};
       +
       +/* RSC changed name[0] to name[1] for old C compilers */
       +struct fuse_dirent {
       +        __u64        ino;
       +        __u64        off;
       +        __u32        namelen;
       +        __u32        type;
       +        char name[1];
       +};
       +
       +#define FUSE_NAME_OFFSET ((unsigned) ((struct fuse_dirent *) 0)->name)
       +#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1))
       +#define FUSE_DIRENT_SIZE(d) \
       +        FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
 (DIR) diff --git a/src/cmd/9pfuse/main.c b/src/cmd/9pfuse/main.c
       t@@ -0,0 +1,1169 @@
       +/*
       + * 9P to FUSE translator.  Acts as FUSE server, 9P client.
       + * Mounts 9P servers via FUSE kernel module.
       + *
       + * There are four procs in this threaded program
       + * (ignoring the one that runs main and then exits).
       + * The first proc reads FUSE requests from /dev/fuse.
       + * It sends the requests over a channel to a second proc,
       + * which serves the requests.  Each request runs in a
       + * thread in that second proc.  Those threads do write
       + * FUSE replies, which in theory might block, but in practice don't.
       + * The 9P interactions are handled by lib9pclient, which
       + * allocates two more procs, one for reading and one for
       + * writing the 9P connection.  Thus the many threads in the
       + * request proc can do 9P interactions without blocking.
       + *
       + * TODO: graceful shutdown.
       + */
       + 
       +#define _GNU_SOURCE 1        /* for O_DIRECTORY */
       +#include "a.h"
       +
       +int debug;
       +char *argv0;
       +void fusedispatch(void*);
       +Channel *fusechan;
       +
       +enum
       +{
       +        STACK = 8192
       +};
       +
       +/*
       + * The number of seconds that the kernel can cache
       + * returned file attributes.  FUSE's default is 1.0.
       + * I haven't experimented with using 0.
       + */
       +double attrtimeout = 1.0;
       +
       +/*
       + * The number of seconds that the kernel can cache
       + * the returned entry nodeids returned by lookup.
       + * I haven't experimented with other values.
       + */
       +double entrytimeout = 1.0;
       +
       +CFsys *fsys;
       +CFid *fsysroot;
       +void init9p(char*);
       +
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: 9pfuse [-D] [-a attrtimeout] address mtpt\n");
       +        exit(1);
       +}
       +
       +void fusereader(void*);
       +
       +void
       +threadmain(int argc, char **argv)
       +{
       +        ARGBEGIN{
       +        case 'D':
       +                chatty9pclient++;
       +                debug++;
       +                break;
       +        case 'a':
       +                attrtimeout = atof(EARGF(usage()));
       +                break;
       +        default:
       +                usage();
       +        }ARGEND
       +
       +        if(argc != 2)
       +                usage();
       +
       +        quotefmtinstall();
       +        fmtinstall('F', fcallfmt);
       +        fmtinstall('M', dirmodefmt);
       +        fmtinstall('G', fusefmt);
       +
       +        init9p(argv[0]);
       +        initfuse(argv[1]);
       +
       +        fusechan = chancreate(sizeof(void*), 0);
       +        proccreate(fusedispatch, nil, STACK);
       +        sendp(fusechan, nil);        /* sync */
       +
       +        proccreate(fusereader, nil, STACK);
       +        threadexits(0);
       +}
       +
       +void
       +fusereader(void *v)
       +{
       +        FuseMsg *m;
       +
       +        while((m = readfusemsg()) != nil)
       +                sendp(fusechan, m);
       +
       +        fusemtpt = nil;        /* no need to unmount */
       +        threadexitsall(0);
       +}
       +
       +void
       +init9p(char *addr)
       +{
       +        int fd;
       +
       +        if((fd = dial(netmkaddr(addr, "tcp", "564"), nil, nil, nil)) < 0)
       +                sysfatal("dial %s: %r", addr);
       +        if((fsys = fsmount(fd, "")) == nil)
       +                sysfatal("fsmount: %r");
       +        fsysroot = fsroot(fsys);
       +}
       +
       +int
       +errstr2errno(void)
       +{
       +        /* TODO: a better job */
       +        return EPERM;
       +}
       +
       +/*
       + * FUSE uses nodeids to refer to active "struct inodes"
       + * (9P's unopened fids).  FUSE uses fhs to refer to active
       + * "struct fuse_files" (9P's opened fids).  The choice of 
       + * numbers is up to us except that nodeid 1 is the root directory.
       + * We use the same number space for both and call the 
       + * bookkeeping structure a FuseFid.
       + *
       + * FUSE requires nodeids to have associated generation 
       + * numbers.  If we reuse a nodeid, we have to bump the 
       + * generation number to guarantee that the nodeid,gen
       + * combination is never reused.
       + * 
       + * There are also inode numbers returned in directory reads
       + * and file attributes, but these do NOT need to match the nodeids.
       + * We use a combination of qid.path and qid.type as the inode
       + * number.
       + */
       +/*
       + * TO DO: reference count the fids.
       + */
       +typedef struct Fusefid Fusefid;
       +struct Fusefid
       +{
       +        Fusefid *next;
       +        CFid *fid;
       +        int ref;
       +        int id;
       +        int gen;
       +        int isnodeid;
       +        
       +        /* directory read state */
       +        Dir *d0;
       +        Dir *d;
       +        int nd;
       +        int off;
       +};
       +
       +Fusefid **fusefid;
       +int nfusefid;
       +Fusefid *freefusefidlist;
       +
       +Fusefid*
       +allocfusefid(void)
       +{
       +        Fusefid *f;
       +        
       +        if((f = freefusefidlist) == nil){
       +                f = emalloc(sizeof *f);
       +                fusefid = erealloc(fusefid, (nfusefid+1)*sizeof *fusefid);
       +                f->id = nfusefid;
       +fprint(2, "allocfusefid %d %p\n", f->id, f);
       +                fusefid[f->id] = f;
       +                nfusefid++;
       +        }else
       +                freefusefidlist = f->next;
       +        f->next = nil;
       +        f->ref = 1;
       +        f->isnodeid = -1;
       +        return f;
       +}
       +
       +void
       +freefusefid(Fusefid *f)
       +{
       +        if(--f->ref > 0)
       +                return;
       +        assert(f->ref == 0);
       +        if(f->fid)
       +                fsclose(f->fid);
       +        if(f->d0)
       +                free(f->d0);
       +        f->off = 0;
       +        f->d0 = nil;
       +        f->fid = nil;
       +        f->d = nil;
       +        f->nd = 0;
       +        f->next = freefusefidlist;
       +        f->isnodeid = -1;
       +        freefusefidlist = f;
       +}
       +
       +uvlong
       +_alloc(CFid *fid, int isnodeid)
       +{
       +        Fusefid *ff;
       +        
       +        ff = allocfusefid();
       +        ff->fid = fid;
       +        ff->isnodeid = isnodeid;
       +        ff->gen++;
       +        return ff->id+2; /* skip 0 and 1 */
       +}
       +
       +uvlong
       +allocfh(CFid *fid)
       +{
       +        return _alloc(fid, 0);
       +}
       +uvlong
       +allocnodeid(CFid *fid)
       +{
       +        return _alloc(fid, 1);
       +}
       +
       +Fusefid*
       +lookupfusefid(uvlong id, int isnodeid)
       +{
       +        Fusefid *ff;
       +        if(id < 2 || id >= nfusefid+2)
       +                return nil;
       +        ff = fusefid[(int)id-2];
       +        if(ff->isnodeid != isnodeid)
       +                return nil;
       +        return ff;
       +}
       +
       +CFid*
       +_lookupcfid(uvlong id, int isnodeid)
       +{
       +        Fusefid *ff;
       +        
       +        if((ff = lookupfusefid(id, isnodeid)) == nil)
       +                return nil;
       +        return ff->fid;
       +}
       +
       +CFid*
       +fh2fid(uvlong fh)
       +{
       +        return _lookupcfid(fh, 0);
       +}
       +
       +CFid*
       +nodeid2fid(uvlong nodeid)
       +{
       +        if(nodeid == 1)
       +                return fsysroot;
       +        return _lookupcfid(nodeid, 1);
       +}
       +
       +uvlong
       +qid2inode(Qid q)
       +{
       +        return q.path | ((uvlong)q.type<<56);
       +}
       +
       +void
       +dir2attr(Dir *d, struct fuse_attr *attr)
       +{
       +        attr->ino = qid2inode(d->qid);
       +        attr->size = d->length;
       +        attr->blocks = (d->length+8191)/8192;
       +        attr->atime = d->atime;
       +        attr->mtime = d->mtime;
       +        attr->ctime = d->mtime;        /* not right */
       +        attr->atimensec = 0;
       +        attr->mtimensec = 0;
       +        attr->ctimensec = 0;
       +        attr->mode = d->mode&0777;
       +        if(d->mode&DMDIR)
       +                attr->mode |= S_IFDIR;
       +        else
       +                attr->mode |= S_IFREG;
       +        attr->nlink = 1;        /* works for directories! - see FUSE FAQ */
       +        attr->uid = getuid();
       +        attr->gid = getgid();
       +        attr->rdev = 0;
       +}
       +
       +void
       +f2timeout(double f, __u64 *s, __u32 *ns)
       +{
       +        *s = f;
       +        *ns = (f - (int)f)*1e9;
       +}
       +
       +void
       +dir2attrout(Dir *d, struct fuse_attr_out *out)
       +{
       +        f2timeout(attrtimeout, &out->attr_valid, &out->attr_valid_nsec);
       +        dir2attr(d, &out->attr);
       +}
       +
       +/*
       + * Lookup.  Walk to the name given as the argument.
       + * The response is a fuse_entry_out giving full stat info.
       + */
       +void
       +fuselookup(FuseMsg *m)
       +{
       +        char *name;
       +        Fusefid *ff;
       +        CFid *fid, *newfid;
       +        Dir *d;
       +        struct fuse_entry_out out;
       +        
       +        name = m->tx;
       +        if((fid = nodeid2fid(m->hdr->nodeid)) == nil){
       +                replyfuseerrno(m, ESTALE);
       +                return;
       +        }
       +        if(strchr(name, '/')){
       +                replyfuseerrno(m, ENOENT);
       +                return;
       +        }
       +        if((newfid = fswalk(fid, name)) == nil){
       +                replyfuseerrstr(m);
       +                return;
       +        }
       +        if((d = fsdirfstat(newfid)) == nil){
       +                fsclose(newfid);
       +                replyfuseerrstr(m);
       +                return;
       +        }
       +        out.nodeid = allocnodeid(newfid);
       +        ff = lookupfusefid(out.nodeid, 1);
       +        out.generation = ff->gen;
       +        f2timeout(attrtimeout, &out.attr_valid, &out.attr_valid_nsec);
       +        f2timeout(entrytimeout, &out.entry_valid, &out.entry_valid_nsec);
       +        dir2attr(d, &out.attr);
       +        free(d);
       +        replyfuse(m, &out, sizeof out);
       +}
       +
       +/*
       + * Forget.  Reference-counted clunk for nodeids.
       + * Does not send a reply.
       + * Each lookup response gives the kernel an additional reference 
       + * to the returned nodeid.  Forget says "drop this many references
       + * to this nodeid".  Our fuselookup, when presented with the same query,
       + * does not return the same results (it allocates a new nodeid for each
       + * call), but if that ever changes, fuseforget already handles the ref
       + * counts properly.
       + */
       +void
       +fuseforget(FuseMsg *m)
       +{
       +        struct fuse_forget_in *in;
       +        Fusefid *ff;
       +
       +        in = m->tx;
       +        if((ff = lookupfusefid(m->hdr->nodeid, 1)) == nil)
       +                return;
       +        if(ff->ref > in->nlookup){
       +                ff->ref -= in->nlookup;
       +                return;
       +        }
       +        if(ff->ref < in->nlookup)
       +                fprint(2, "bad count in forget\n");
       +        ff->ref = 1;
       +        freefusefid(ff);
       +}
       +
       +/*
       + * Getattr.
       + * Replies with a fuse_attr_out structure giving the
       + * attr for the requested nodeid in out.attr.
       + * Out.attr_valid and out.attr_valid_nsec give 
       + * the amount of time that the attributes can
       + * be cached.
       + *
       + * Empirically, though, if I run ls -ld on the root
       + * twice back to back, I still get two getattrs,
       + * even with a one second attribute timeout!
       + */
       +void
       +fusegetattr(FuseMsg *m)
       +{
       +        CFid *fid;
       +        struct fuse_attr_out out;
       +        Dir *d;
       +
       +        if((fid = nodeid2fid(m->hdr->nodeid)) == nil){
       +                replyfuseerrno(m, ESTALE);
       +                return;
       +        }
       +        if((d = fsdirfstat(fid)) == nil){
       +                replyfuseerrstr(m);
       +                return;
       +        }
       +        memset(&out, 0, sizeof out);
       +        dir2attrout(d, &out);
       +        free(d);
       +        replyfuse(m, &out, sizeof out);
       +}
       +
       +/*
       + * Setattr.
       + * FUSE treats the many Unix attribute setting routines
       + * more or less like 9P does, with a single message.
       + */
       +void
       +fusesetattr(FuseMsg *m)
       +{
       +        CFid *fid, *nfid;
       +        Dir d, *dd;
       +        struct fuse_setattr_in *in;
       +        struct fuse_attr_out out;
       +
       +        in = m->tx;
       +        if(in->valid&FATTR_FH){
       +                if((fid = fh2fid(in->fh)) == nil){
       +                        replyfuseerrno(m, ESTALE);
       +                        return;
       +                }
       +        }else{
       +                if((fid = nodeid2fid(m->hdr->nodeid)) == nil){
       +                        replyfuseerrno(m, ESTALE);
       +                        return;
       +                }
       +                /*
       +                 * Special case: Linux issues a size change to
       +                 * truncate a file before opening it OTRUNC.
       +                 * Synthetic file servers (e.g., plumber) honor 
       +                 * open(OTRUNC) but not wstat.
       +                 */
       +                if(in->valid == FATTR_SIZE && in->size == 0){
       +                        if((nfid = fswalk(fid, nil)) == nil){
       +                                replyfuseerrstr(m);
       +                                return;
       +                        }
       +                        if(fsfopen(nfid, OWRITE|OTRUNC) < 0){
       +                                replyfuseerrstr(m);
       +                                fsclose(nfid);
       +                        }
       +                        fsclose(nfid);
       +                        goto stat;
       +                }
       +        }
       +
       +        nulldir(&d);
       +        if(in->valid&FATTR_SIZE)
       +                d.length = in->size;
       +        if(in->valid&FATTR_ATIME)
       +                d.atime = in->atime;
       +        if(in->valid&FATTR_MTIME)
       +                d.mtime = in->mtime;
       +        if(in->valid&FATTR_MODE)
       +                d.mode = in->mode;
       +        if((in->valid&FATTR_UID) || (in->valid&FATTR_GID)){
       +                /*
       +                 * I can't be bothered with these yet.
       +                 */
       +                replyfuseerrno(m, EPERM);
       +                return;
       +        }
       +        if(fsdirfwstat(fid, &d) < 0){
       +                replyfuseerrstr(m);
       +                return;
       +        }
       +stat:
       +        if((dd = fsdirfstat(fid)) == nil){
       +                replyfuseerrstr(m);
       +                return;
       +        }
       +        memset(&out, 0, sizeof out);
       +        dir2attrout(dd, &out);
       +        free(dd);
       +        replyfuse(m, &out, sizeof out);
       +}
       +
       +CFid*
       +_fuseopenfid(uvlong nodeid, int isdir, int openmode, int *err)
       +{
       +        CFid *fid, *newfid;
       +
       +        if((fid = nodeid2fid(nodeid)) == nil){
       +                *err = ESTALE;
       +                return nil;
       +        }
       +        if(isdir && !(fsqid(fid).type&QTDIR)){
       +                *err = ENOTDIR;
       +                return nil;
       +        }
       +        if(openmode != OREAD && fsqid(fid).type&QTDIR){
       +                *err = EISDIR;
       +                return nil;
       +        }
       +
       +        /* Clone fid to get one we can open. */
       +        newfid = fswalk(fid, nil);
       +        if(newfid == nil){
       +                *err = errstr2errno();
       +        //        fsclose(fid);
       +                return nil;
       +        }
       +        // fsputfid(fid);
       +                
       +        if(fsfopen(newfid, openmode) < 0){
       +                *err = errstr2errno();
       +                fsclose(newfid);
       +                return nil;
       +        }
       +
       +        return newfid;
       +}
       +
       +/*
       + * Open & Opendir.
       + * Argument is a struct fuse_open_in.
       + * The mode field is ignored (presumably permission bits)
       + * and flags is the open mode.
       + * Replies with a struct fuse_open_out.
       + */
       +void
       +_fuseopen(FuseMsg *m, int isdir)
       +{
       +        struct fuse_open_in *in;
       +        struct fuse_open_out out;
       +        CFid *fid;
       +        int openmode, flags, err;
       +
       +        /* TODO: better job translating openmode - see lib9 open */
       +        in = m->tx;
       +        flags = in->flags;
       +        openmode = flags&3;
       +        flags &= ~3;
       +        flags &= ~(O_DIRECTORY|O_NONBLOCK|O_LARGEFILE);
       +        if(flags){
       +                fprint(2, "unexpected open flags %#uo", (uint)in->flags);
       +                replyfuseerrno(m, EACCES);
       +                return;
       +        }
       +        if((fid = _fuseopenfid(m->hdr->nodeid, isdir, openmode, &err)) == nil){
       +                replyfuseerrno(m, err);
       +                return;
       +        }
       +        out.fh = allocfh(fid);
       +        out.open_flags = FOPEN_DIRECT_IO;        /* no page cache */        
       +        replyfuse(m, &out, sizeof out);
       +}
       +
       +void
       +fuseopen(FuseMsg *m)
       +{
       +        _fuseopen(m, 0);
       +}
       +
       +void
       +fuseopendir(FuseMsg *m)
       +{
       +        _fuseopen(m, 1);
       +}
       +
       +/*
       + * Create & Mkdir.
       + */
       +CFid*
       +_fusecreate(uvlong nodeid, char *name, int perm, int ismkdir, int omode, struct fuse_entry_out *out, int *err)
       +{
       +        CFid *fid, *newfid, *newfid2;
       +        Dir *d;
       +        Fusefid *ff;
       +
       +        if((fid = nodeid2fid(nodeid)) == nil){
       +                *err = ESTALE;
       +                return nil;
       +        }
       +        perm &= 0777;
       +        if(ismkdir)
       +                perm |= DMDIR;
       +        if(ismkdir && omode != OREAD){
       +                *err = EPERM;
       +                return nil;
       +        }
       +        if((newfid = fswalk(fid, nil)) == nil){
       +                *err = errstr2errno();
       +                return nil;
       +        }
       +        if(fsfcreate(newfid, name, perm, omode) < 0){
       +                *err = errstr2errno();
       +                fsclose(newfid);
       +                return nil;
       +        }
       +        if((d = fsdirfstat(newfid)) == nil){
       +                *err = errstr2errno();
       +                fsfremove(newfid);
       +                return nil;
       +        }
       +        /*
       +         * This fid is no good, because it's open.
       +         * We need an unopened fid.  Sigh.
       +         */
       +        if((newfid2 = fswalk(fid, name)) == nil){
       +                *err = errstr2errno();
       +                free(d);
       +                fsfremove(newfid);
       +                return nil;
       +        }
       +        out->nodeid = allocnodeid(newfid2);
       +        ff = lookupfusefid(out->nodeid, 1);
       +        out->generation = ff->gen;
       +        f2timeout(attrtimeout, &out->attr_valid, &out->attr_valid_nsec);
       +        f2timeout(entrytimeout, &out->entry_valid, &out->entry_valid_nsec);
       +        dir2attr(d, &out->attr);
       +        free(d);
       +        return newfid;
       +}
       +
       +void
       +fusemkdir(FuseMsg *m)
       +{
       +        struct fuse_mkdir_in *in;
       +        struct fuse_entry_out out;
       +        CFid *fid;
       +        int err;
       +        char *name;
       +        
       +        in = m->tx;
       +        name = (char*)(in+1);
       +        if((fid = _fusecreate(m->hdr->nodeid, name, in->mode, 1, OREAD, &out, &err)) == nil){
       +                replyfuseerrno(m, err);
       +                return;
       +        }
       +        /* Toss the open fid. */
       +        fsclose(fid);
       +        replyfuse(m, &out, sizeof out);
       +}
       +
       +void
       +fusecreate(FuseMsg *m)
       +{
       +        struct fuse_open_in *in;
       +        struct fuse_create_out out;
       +        CFid *fid;
       +        int err, openmode, flags;
       +        char *name;
       +        
       +        in = m->tx;
       +        flags = in->flags;
       +        openmode = in->flags&3;
       +        flags &= ~(O_DIRECTORY|O_NONBLOCK|O_LARGEFILE);
       +        if(flags){
       +                fprint(2, "bad mode %#uo\n", in->flags);
       +                replyfuseerrno(m, EACCES);
       +                return;
       +        }
       +        name = (char*)(in+1);
       +        if((fid = _fusecreate(m->hdr->nodeid, name, in->mode, 0, openmode, &out.e, &err)) == nil){
       +                replyfuseerrno(m, err);
       +                return;
       +        }
       +        out.o.fh = allocfh(fid);
       +        out.o.open_flags = FOPEN_DIRECT_IO;        /* no page cache */
       +        replyfuse(m, &out, sizeof out);
       +}
       +
       +/*
       + * Access.  
       + * Lib9pclient implements this just as Plan 9 does,
       + * by opening the file (or not) and then closing it.
       + */
       +void
       +fuseaccess(FuseMsg *m)
       +{
       +        struct fuse_access_in *in;
       +        CFid *fid;
       +        int err;
       +        static int a2o[] = {
       +                0,
       +                OEXEC,
       +                OWRITE,
       +                ORDWR,
       +                OREAD,
       +                OEXEC,
       +                ORDWR,
       +                ORDWR
       +        };
       +        
       +        in = m->tx;
       +        if(in->mask >= nelem(a2o)){
       +                replyfuseerrno(m, EINVAL);
       +                return;
       +        }
       +        if((fid = _fuseopenfid(m->hdr->nodeid, 0, a2o[in->mask], &err)) == nil){
       +                replyfuseerrno(m, err);
       +                return;
       +        }
       +        fsclose(fid);
       +        replyfuse(m, nil, 0);
       +}
       +
       +/*
       + * Release.
       + * Equivalent of clunk for file handles.
       + * in->flags is the open mode used in Open or Opendir.
       + */
       +void
       +fuserelease(FuseMsg *m)
       +{
       +        struct fuse_release_in *in;
       +        Fusefid *ff;
       +        
       +        in = m->tx;
       +        if((ff = lookupfusefid(in->fh, 0)) != nil)
       +                freefusefid(ff);
       +        else
       +                fprint(2, "fuserelease: fh not found\n");
       +        replyfuse(m, nil, 0);
       +}
       +
       +void
       +fusereleasedir(FuseMsg *m)
       +{
       +        fuserelease(m);
       +}
       +
       +/*
       + * Read.
       + * Read from file handle in->fh at offset in->offset for size in->size.
       + * We truncate size to maxwrite just to keep the buffer reasonable.
       + */
       +void
       +fuseread(FuseMsg *m)
       +{
       +        int n;
       +        uchar *buf;
       +        CFid *fid;
       +        struct fuse_read_in *in;
       +
       +        in = m->tx;
       +        if((fid = fh2fid(in->fh)) == nil){
       +                replyfuseerrno(m, ESTALE);
       +                return;
       +        }
       +        n = in->size;
       +        if(n > fusemaxwrite)
       +                n = fusemaxwrite;
       +        buf = emalloc(n);
       +        n = fsread(fid, buf, n);
       +        if(n < 0){
       +                free(buf);
       +                replyfuseerrstr(m);
       +        }
       +        replyfuse(m, buf, n);
       +        free(buf);
       +}
       +
       +/* 
       + * Readdir.
       + * Read from file handle in->fh at offset in->offset for size in->size.
       + * We truncate size to maxwrite just to keep the buffer reasonable.
       + * We assume 9P directory read semantics: a read at offset 0 rewinds
       + * and a read at any other offset starts where we left off.
       + * If it became necessary, we could implement a crude seek
       + * or cache the entire list of directory entries.
       + * Directory entries read from 9P but not yet handed to FUSE
       + * are stored in m->d,nd,d0.
       + */
       +int canpack(Dir*, uvlong, uchar**, uchar*);
       +void
       +fusereaddir(FuseMsg *m)
       +{
       +        struct fuse_read_in *in;
       +        uchar *buf, *p, *ep;
       +        int n;
       +        Fusefid *ff;
       +        
       +        in = m->tx;
       +        if((ff = lookupfusefid(in->fh, 0)) == nil){
       +                replyfuseerrno(m, ESTALE);
       +                return;
       +        }        
       +        if(in->offset == 0){
       +                fsseek(ff->fid, 0, 0);
       +                free(ff->d0);
       +                ff->d0 = nil;
       +                ff->d = nil;
       +                ff->nd = 0;
       +        }
       +        n = in->size;
       +        if(n > fusemaxwrite)
       +                n = fusemaxwrite;
       +        buf = emalloc(n);
       +        p = buf;
       +        ep = buf + n;
       +        for(;;){
       +                if(ff->nd == 0){
       +                        free(ff->d0);
       +                        ff->d0 = nil;
       +                        ff->d = nil;
       +                        if((ff->nd = fsdirread(ff->fid, &ff->d0)) < 0){
       +                                replyfuseerrstr(m);
       +                                return;
       +                        }
       +                        if(ff->nd == 0)
       +                                break;
       +                        ff->d = ff->d0;
       +                }
       +                while(ff->nd > 0 && canpack(ff->d, ff->off, &p, ep)){
       +                        ff->off++;
       +                        ff->d++;
       +                        ff->nd--;
       +                }
       +        }                                
       +        replyfuse(m, buf, p - buf);
       +        free(buf);
       +}
       +
       +int
       +canpack(Dir *d, uvlong off, uchar **pp, uchar *ep)
       +{
       +        uchar *p;
       +        struct fuse_dirent *de;
       +        int pad, size;
       +        
       +        p = *pp;
       +        size = FUSE_NAME_OFFSET + strlen(d->name);
       +        pad = 0;
       +        if(size%8)
       +                pad = 8 - size%8;
       +        if(size+pad > ep - p)
       +                return 0;
       +        de = (struct fuse_dirent*)p;
       +        de->ino = qid2inode(d->qid);
       +        de->off = off;
       +        de->namelen = strlen(d->name);
       +        memmove(de->name, d->name, de->namelen);
       +        if(pad > 0)
       +                memset(de->name+de->namelen, 0, pad);
       +        *pp = p+size+pad;
       +        return 1;
       +}
       +
       +/*
       + * Write.
       + * Write from file handle in->fh at offset in->offset for size in->size.
       + * Don't know what in->write_flags means.
       + * 
       + * Apparently implementations are allowed to buffer these writes
       + * and wait until Flush is sent, but FUSE docs say flush may be
       + * called zero, one, or even more times per close.  So better do the
       + * actual writing here.  Also, errors that happen during Flush just
       + * show up in the close() return status, which no one checks anyway.
       + */
       +void
       +fusewrite(FuseMsg *m)
       +{
       +        struct fuse_write_in *in;
       +        struct fuse_write_out out;
       +        void *a;
       +        CFid *fid;
       +        int n;
       +        
       +        in = m->tx;
       +        a = in+1;
       +        if((fid = fh2fid(in->fh)) == nil){
       +                replyfuseerrno(m, ESTALE);
       +                return;
       +        }
       +        if(in->size > fusemaxwrite){
       +                replyfuseerrno(m, EINVAL);
       +                return;
       +        }
       +        n = fswrite(fid, a, in->size);
       +        if(n < 0){
       +                replyfuseerrstr(m);
       +                return;
       +        }
       +        out.size = n;
       +        replyfuse(m, &out, sizeof out);
       +}
       +
       +/*
       + * Flush.  Supposed to flush any buffered writes.  Don't use this.
       + * 
       + * Flush is a total crock.  It gets called on close() of a file descriptor
       + * associated with this open file.  Some open files have multiple file
       + * descriptors and thus multiple closes of those file descriptors.
       + * In those cases, Flush is called multiple times.  Some open files
       + * have file descriptors that are closed on process exit instead of
       + * closed explicitly.  For those files, Flush is never called.
       + * Even more amusing, Flush gets called before close() of read-only
       + * file descriptors too!
       + * 
       + * This is just a bad idea.
       + */
       +void
       +fuseflush(FuseMsg *m)
       +{
       +        replyfuse(m, nil, 0);
       +}
       +
       +/*
       + * Unlink & Rmdir.
       + */
       +void
       +_fuseremove(FuseMsg *m, int isdir)
       +{
       +        char *name;
       +        CFid *fid, *newfid;
       +        
       +        name = m->tx;
       +        if((fid = nodeid2fid(m->hdr->nodeid)) == nil){
       +                replyfuseerrno(m, ESTALE);
       +                return;
       +        }
       +        if(strchr(name, '/')){
       +                replyfuseerrno(m, ENOENT);
       +                return;
       +        }
       +        if((newfid = fswalk(fid, name)) == nil){
       +                replyfuseerrstr(m);
       +                return;
       +        }
       +        if(isdir && !(fsqid(newfid).type&QTDIR)){
       +                replyfuseerrno(m, ENOTDIR);
       +                fsclose(newfid);
       +                return;
       +        }
       +        if(!isdir && (fsqid(newfid).type&QTDIR)){
       +                replyfuseerrno(m, EISDIR);
       +                fsclose(newfid);
       +                return;
       +        }
       +        if(fsfremove(newfid) < 0){
       +                replyfuseerrstr(m);
       +                return;
       +        }
       +        replyfuse(m, nil, 0);
       +}
       +
       +void
       +fuseunlink(FuseMsg *m)
       +{
       +        _fuseremove(m, 0);
       +}
       +
       +void
       +fusermdir(FuseMsg *m)
       +{
       +        _fuseremove(m, 1);
       +}
       +
       +/*
       + * Rename.
       + *
       + * FUSE sends the nodeid for the source and destination
       + * directory and then the before and after names as strings.
       + * 9P can only do the rename if the source and destination
       + * are the same.  If the same nodeid is used for source and
       + * destination, we're fine, but if FUSE gives us different nodeids
       + * that happen to correspond to the same directory, we have
       + * no way of figuring that out.  Let's hope it doesn't happen too often.
       + */
       +void
       +fuserename(FuseMsg *m)
       +{
       +        struct fuse_rename_in *in;
       +        char *before, *after;
       +        CFid *fid, *newfid;
       +        Dir d;
       +        
       +        in = m->tx;
       +        if(in->newdir != m->hdr->nodeid){
       +                replyfuseerrno(m, EXDEV);
       +                return;
       +        }
       +        before = (char*)(in+1);
       +        after = before + strlen(before) + 1;
       +        if((fid = nodeid2fid(m->hdr->nodeid)) == nil){
       +                replyfuseerrno(m, ESTALE);
       +                return;
       +        }
       +        if(strchr(before, '/') || strchr(after, '/')){
       +                replyfuseerrno(m, ENOENT);
       +                return;
       +        }
       +        if((newfid = fswalk(fid, before)) == nil){
       +                replyfuseerrstr(m);
       +                return;
       +        }
       +        nulldir(&d);
       +        d.name = after;
       +        if(fsdirfwstat(newfid, &d) < 0){
       +                replyfuseerrstr(m);
       +                fsclose(newfid);
       +                return;
       +        }
       +        fsclose(newfid);
       +        replyfuse(m, nil, 0);
       +}
       +
       +/*
       + * Fsync.  Commit file info to stable storage.
       + * Not sure what in->fsync_flags are.
       + */
       +void
       +fusefsync(FuseMsg *m)
       +{
       +        struct fuse_fsync_in *in;
       +        CFid *fid;
       +        Dir d;
       +        
       +        in = m->tx;
       +        if((fid = fh2fid(in->fh)) == nil){
       +                replyfuseerrno(m, ESTALE);
       +                return;
       +        }
       +        nulldir(&d);
       +        if(fsdirfwstat(fid, &d) < 0){
       +                replyfuseerrstr(m);
       +                return;
       +        }
       +        replyfuse(m, nil, 0);
       +}
       +
       +/*
       + * Fsyncdir.  Commit dir info to stable storage?
       + */
       +void
       +fusefsyncdir(FuseMsg *m)
       +{
       +        fusefsync(m);
       +}
       +
       +/*
       + * Statfs.  Send back information about file system.
       + * Not really worth implementing, except that if we
       + * reply with ENOSYS, programs like df print messages like
       + *   df: `/tmp/z': Function not implemented
       + * and that gets annoying.  Returning all zeros excludes
       + * us from df without appearing to cause any problems.
       + */
       +void
       +fusestatfs(FuseMsg *m)
       +{
       +        struct fuse_statfs_out out;
       +        
       +        memset(&out, 0, sizeof out);
       +        replyfuse(m, &out, sizeof out);
       +}
       +
       +void (*fusehandlers[100])(FuseMsg*);
       +
       +struct {
       +        int op;
       +        void (*fn)(FuseMsg*);
       +} fuselist[] = {
       +        { FUSE_LOOKUP,                fuselookup },
       +        { FUSE_FORGET,                fuseforget },
       +        { FUSE_GETATTR,                fusegetattr },
       +        { FUSE_SETATTR,                fusesetattr },
       +        /*
       +         * FUSE_READLINK, FUSE_SYMLINK, FUSE_MKNOD are unimplemented.
       +         */
       +        { FUSE_MKDIR,                fusemkdir },
       +        { FUSE_UNLINK,                fuseunlink },
       +        { FUSE_RMDIR,                fusermdir },
       +        { FUSE_RENAME,                fuserename },
       +        /*
       +         * FUSE_LINK is unimplemented.
       +         */
       +        { FUSE_OPEN,                fuseopen },
       +        { FUSE_READ,                fuseread },
       +        { FUSE_WRITE,                fusewrite },
       +        { FUSE_STATFS,                fusestatfs },
       +        { FUSE_RELEASE,                fuserelease },
       +        { FUSE_FSYNC,                fusefsync },
       +        /*
       +         * FUSE_SETXATTR, FUSE_GETXATTR, FUSE_LISTXATTR, and
       +         * FUSE_REMOVEXATTR are unimplemented. 
       +         * FUSE will stop sending these requests after getting
       +         * an -ENOSYS reply (see dispatch below).
       +         */
       +        { FUSE_FLUSH,                fuseflush },
       +        /*
       +         * FUSE_INIT is handled in initfuse and should not be seen again.
       +         */
       +        { FUSE_OPENDIR,                fuseopendir },
       +        { FUSE_READDIR,                fusereaddir },
       +        { FUSE_RELEASEDIR,        fusereleasedir },
       +        { FUSE_FSYNCDIR,        fusefsyncdir },
       +        { FUSE_ACCESS,                fuseaccess },
       +        { FUSE_CREATE,                fusecreate },
       +};
       +
       +void
       +fusethread(void *v)
       +{
       +        FuseMsg *m;
       +
       +        m = v;
       +        if((uint)m->hdr->opcode >= nelem(fusehandlers) 
       +        || !fusehandlers[m->hdr->opcode]){
       +                replyfuseerrno(m, ENOSYS);
       +                return;
       +        }
       +        fusehandlers[m->hdr->opcode](m);
       +}
       +
       +void
       +fusedispatch(void *v)
       +{
       +        int i;
       +        FuseMsg *m;
       +
       +        eofkill9pclient = 1;        /* threadexitsall on 9P eof */
       +        atexit(unmountatexit);
       +
       +        recvp(fusechan);        /* sync */
       +
       +        for(i=0; i<nelem(fuselist); i++){
       +                if(fuselist[i].op >= nelem(fusehandlers))
       +                        sysfatal("make fusehandlers bigger op=%d", fuselist[i].op);
       +                fusehandlers[fuselist[i].op] = fuselist[i].fn;
       +        }
       +
       +        while((m = recvp(fusechan)) != nil)
       +                threadcreate(fusethread, m, STACK);
       +}
       +
       +void*
       +emalloc(uint n)
       +{
       +        void *p;
       +
       +        p = malloc(n);
       +        if(p == nil)
       +                sysfatal("malloc(%d): %r", n);
       +        memset(p, 0, n);
       +        return p;
       +}
       +
       +void*
       +erealloc(void *p, uint n)
       +{
       +        p = realloc(p, n);
       +        if(p == nil)
       +                sysfatal("realloc(..., %d): %r", n);
       +        return p;
       +}
       +
       +char*
       +estrdup(char *p)
       +{
       +        char *pp;
       +        pp = strdup(p);
       +        if(pp == nil)
       +                sysfatal("strdup(%.20s): %r", p);
       +        return pp;
       +}
       +
       +
 (DIR) diff --git a/src/cmd/9pfuse/mkfile b/src/cmd/9pfuse/mkfile
       t@@ -0,0 +1,12 @@
       +<$PLAN9/src/mkhdr
       +
       +TARG=9pfuse
       +
       +OFILES=\
       +        fuse.$O\
       +        main.$O\
       +
       +HFILES=a.h
       +
       +<$PLAN9/src/mkone
       +