|  | #include "a.h" | 
|  |  | 
|  | int fusefd; | 
|  | int fuseeof; | 
|  | int fusebufsize; | 
|  | int fusemaxwrite; | 
|  | FuseMsg *fusemsglist; | 
|  | Lock fusemsglock; | 
|  |  | 
|  | int mountfuse(char *mtpt); | 
|  | void unmountfuse(char *mtpt); | 
|  |  | 
|  | FuseMsg* | 
|  | allocfusemsg(void) | 
|  | { | 
|  | FuseMsg *m; | 
|  | void *vbuf; | 
|  |  | 
|  | lock(&fusemsglock); | 
|  | if((m = fusemsglist) != nil){ | 
|  | fusemsglist = m->next; | 
|  | unlock(&fusemsglock); | 
|  | return m; | 
|  | } | 
|  | unlock(&fusemsglock); | 
|  | 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) | 
|  | { | 
|  | lock(&fusemsglock); | 
|  | m->next = fusemsglist; | 
|  | fusemsglist = m; | 
|  | unlock(&fusemsglock); | 
|  | } | 
|  |  | 
|  | FuseMsg* | 
|  | readfusemsg(void) | 
|  | { | 
|  | FuseMsg *m; | 
|  | int n, nn; | 
|  |  | 
|  | 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; | 
|  |  | 
|  | /* | 
|  | * FreeBSD FUSE sends a short length in the header | 
|  | * for FUSE_INIT even though the actual read length | 
|  | * is correct. | 
|  | */ | 
|  | if(n == sizeof(*m->hdr)+sizeof(struct fuse_init_in) | 
|  | && m->hdr->opcode == FUSE_INIT && m->hdr->len < n) | 
|  | m->hdr->len = 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, one string, and one binary blob */ | 
|  | if(m->hdr->len <= sizeof(struct fuse_setxattr_in)) | 
|  | goto bad; | 
|  | nn = ((struct fuse_setxattr_in*)m->tx)->size; | 
|  | if(m->hdr->len < sizeof(struct fuse_setxattr_in)+nn+1) | 
|  | goto bad; | 
|  | if(((char*)m->tx)[m->hdr->len-nn-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++; | 
|  | } | 
|  | writev(fusefd, vec, nvec); | 
|  | freefusemsg(m); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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); | 
|  | write(fusefd, &hdr, sizeof hdr); | 
|  | freefusemsg(m); | 
|  | } | 
|  |  | 
|  | void | 
|  | replyfuseerrstr(FuseMsg *m) | 
|  | { | 
|  | replyfuseerrno(m, errstr2errno()); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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; | 
|  | } | 
|  |  | 
|  | #if defined(__APPLE__) | 
|  | #include <sys/param.h> | 
|  | #include <sys/mount.h> | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * Mounts a fuse file system on mtpt and returns | 
|  | * a file descriptor for the corresponding fuse | 
|  | * message conversation. | 
|  | */ | 
|  | int | 
|  | mountfuse(char *mtpt) | 
|  | { | 
|  | #if defined(__linux__) | 
|  | 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]); | 
|  | return fd; | 
|  | #elif defined(__FreeBSD__) && !defined(__APPLE__) | 
|  | int pid, fd; | 
|  | char buf[20]; | 
|  |  | 
|  | if((fd = open("/dev/fuse", ORDWR)) < 0) | 
|  | return -1; | 
|  | snprint(buf, sizeof buf, "%d", fd); | 
|  |  | 
|  | pid = fork(); | 
|  | if(pid < 0) | 
|  | return -1; | 
|  | if(pid == 0){ | 
|  | execlp("mount_fusefs", "mount_fusefs", buf, mtpt, nil); | 
|  | fprint(2, "exec mount_fusefs: %r\n"); | 
|  | _exit(1); | 
|  | } | 
|  | return fd; | 
|  | #elif defined(__APPLE__) | 
|  | int i, pid, fd, r; | 
|  | char buf[20]; | 
|  | struct vfsconf vfs; | 
|  | char *f; | 
|  |  | 
|  | if(getvfsbyname("fusefs", &vfs) < 0){ | 
|  | if(access(f="/System/Library/Extensions/fusefs.kext" | 
|  | "/Contents/Resources/load_fusefs", 0) < 0 && | 
|  | access(f="/Library/Extensions/fusefs.kext" | 
|  | "/Contents/Resources/load_fusefs", 0) < 0 && | 
|  | access(f="/Library/Filesystems" | 
|  | "/fusefs.fs/Support/load_fusefs", 0) < 0 && | 
|  | access(f="/System/Library/Filesystems" | 
|  | "/fusefs.fs/Support/load_fusefs", 0) < 0){ | 
|  | werrstr("cannot find load_fusefs"); | 
|  | return -1; | 
|  | } | 
|  | if((r=system(f)) < 0){ | 
|  | werrstr("%s: %r", f); | 
|  | return -1; | 
|  | } | 
|  | if(r != 0){ | 
|  | werrstr("load_fusefs failed: exit %d", r); | 
|  | return -1; | 
|  | } | 
|  | if(getvfsbyname("fusefs", &vfs) < 0){ | 
|  | werrstr("getvfsbyname fusefs: %r"); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Look for available FUSE device. */ | 
|  | for(i=0;; i++){ | 
|  | snprint(buf, sizeof buf, "/dev/fuse%d", i); | 
|  | if(access(buf, 0) < 0){ | 
|  | werrstr("no available fuse devices"); | 
|  | return -1; | 
|  | } | 
|  | if((fd = open(buf, ORDWR)) >= 0) | 
|  | break; | 
|  | } | 
|  |  | 
|  | pid = fork(); | 
|  | if(pid < 0) | 
|  | return -1; | 
|  | if(pid == 0){ | 
|  | snprint(buf, sizeof buf, "%d", fd); | 
|  | putenv("MOUNT_FUSEFS_CALL_BY_LIB", ""); | 
|  | /* | 
|  | * Different versions of MacFUSE put the | 
|  | * mount_fusefs binary in different places. | 
|  | * Try all. | 
|  | */ | 
|  | /* Leopard location */ | 
|  | putenv("MOUNT_FUSEFS_DAEMON_PATH", | 
|  | "/Library/Filesystems/fusefs.fs/Support/mount_fusefs"); | 
|  | execl("/Library/Filesystems/fusefs.fs/Support/mount_fusefs", | 
|  | "mount_fusefs", buf, mtpt, nil); | 
|  |  | 
|  | /* possible Tiger locations */ | 
|  | execl("/System/Library/Filesystems/fusefs.fs/mount_fusefs", | 
|  | "mount_fusefs", buf, mtpt, nil); | 
|  | execl("/System/Library/Filesystems/fusefs.fs/Support/mount_fusefs", | 
|  | "mount_fusefs", buf, mtpt, nil); | 
|  | fprint(2, "exec mount_fusefs: %r\n"); | 
|  | _exit(1); | 
|  | } | 
|  | return fd; | 
|  |  | 
|  | #else | 
|  | werrstr("cannot mount fuse on this system"); | 
|  | return -1; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void | 
|  | waitfuse(void) | 
|  | { | 
|  | waitpid(); | 
|  | } | 
|  |  | 
|  | void | 
|  | unmountfuse(char *mtpt) | 
|  | { | 
|  | int pid; | 
|  |  | 
|  | pid = fork(); | 
|  | if(pid < 0) | 
|  | return; | 
|  | if(pid == 0){ | 
|  | #if defined(__linux__) | 
|  | execlp("fusermount", "fusermount", "-u", "-z", "--", mtpt, nil); | 
|  | fprint(2, "exec fusermount -u: %r\n"); | 
|  | #else | 
|  | execlp("umount", "umount", mtpt, nil); | 
|  | fprint(2, "exec umount: %r\n"); | 
|  | #endif | 
|  | _exit(1); | 
|  | } | 
|  | waitpid(); | 
|  | } |