#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 #include #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__) 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(); }