summaryrefslogtreecommitdiffstats
path: root/src/cmd/9pfuse/fuse.c
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2006-07-23 03:02:03 +0000
committerrsc <devnull@localhost>2006-07-23 03:02:03 +0000
commit5551e51d2b9df4e289ea3cb1350db4d5f5444df2 (patch)
tree9374211c6c3d9d18765ccfe4c5895b086a9e6928 /src/cmd/9pfuse/fuse.c
parent30f8beab329834c1968c391e35890e3cd9917895 (diff)
downloadplan9port-5551e51d2b9df4e289ea3cb1350db4d5f5444df2.tar.gz
plan9port-5551e51d2b9df4e289ea3cb1350db4d5f5444df2.zip
9pfuse
Diffstat (limited to 'src/cmd/9pfuse/fuse.c')
-rw-r--r--src/cmd/9pfuse/fuse.c772
1 files changed, 772 insertions, 0 deletions
diff --git a/src/cmd/9pfuse/fuse.c b/src/cmd/9pfuse/fuse.c
new file mode 100644
index 00000000..0e61f72f
--- /dev/null
+++ b/src/cmd/9pfuse/fuse.c
@@ -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;
+}
+