dots

Personal dotfiles
git clone git://git.gormless.xyz/dots.git
Log | Files | Refs

fuse.c (25106B)


      1 #include "a.h"
      2 
      3 int fusefd;
      4 int fuseeof;
      5 int fusebufsize;
      6 int fusemaxwrite;
      7 FuseMsg *fusemsglist;
      8 Lock fusemsglock;
      9 
     10 int mountfuse(char *mtpt);
     11 void unmountfuse(char *mtpt);
     12 
     13 FuseMsg*
     14 allocfusemsg(void)
     15 {
     16 	FuseMsg *m;
     17 	void *vbuf;
     18 
     19 	lock(&fusemsglock);
     20 	if((m = fusemsglist) != nil){
     21 		fusemsglist = m->next;
     22 		unlock(&fusemsglock);
     23 		return m;
     24 	}
     25 	unlock(&fusemsglock);
     26 	m = emalloc(sizeof(*m) + fusebufsize);
     27 	vbuf = m+1;
     28 	m->buf = vbuf;
     29 	m->nbuf = 0;
     30 	m->hdr = vbuf;
     31 	m->tx = m->hdr+1;
     32 	return m;
     33 }
     34 
     35 void
     36 freefusemsg(FuseMsg *m)
     37 {
     38 	lock(&fusemsglock);
     39 	m->next = fusemsglist;
     40 	fusemsglist = m;
     41 	unlock(&fusemsglock);
     42 }
     43 
     44 FuseMsg*
     45 readfusemsg(void)
     46 {
     47 	FuseMsg *m;
     48 	int n, nn;
     49 
     50 	m = allocfusemsg();
     51 	/*
     52 	 * The FUSE kernel device apparently guarantees
     53 	 * that this read will return exactly one message.
     54 	 * You get an error return if you ask for just the
     55 	 * length (first 4 bytes).
     56 	 * FUSE returns an ENODEV error, not EOF,
     57 	 * when the connection is unmounted.
     58 	 */
     59 	do{
     60 		errno = 0;
     61 		n = read(fusefd, m->buf, fusebufsize);
     62 	}while(n < 0 && errno == EINTR);
     63 	if(n < 0){
     64 		if(errno != ENODEV)
     65 			sysfatal("readfusemsg: %r");
     66 	}
     67 	if(n <= 0){
     68 		fuseeof = 1;
     69 		freefusemsg(m);
     70 		return nil;
     71 	}
     72 	m->nbuf = n;
     73 
     74 	/*
     75 	 * FreeBSD FUSE sends a short length in the header
     76 	 * for FUSE_INIT even though the actual read length
     77 	 * is correct.
     78 	 */
     79 	if(n == sizeof(*m->hdr)+sizeof(struct fuse_init_in)
     80 	&& m->hdr->opcode == FUSE_INIT && m->hdr->len < n)
     81 		m->hdr->len = n;
     82 
     83 	if(m->hdr->len != n)
     84 		sysfatal("readfusemsg: got %d wanted %d",
     85 			n, m->hdr->len);
     86 	m->hdr->len -= sizeof(*m->hdr);
     87 
     88 	/*
     89 	 * Paranoia.
     90 	 * Make sure lengths are long enough.
     91 	 * Make sure string arguments are NUL terminated.
     92 	 * (I don't trust the kernel module.)
     93 	 */
     94 	switch(m->hdr->opcode){
     95 	default:
     96 		/*
     97 		 * Could sysfatal here, but can also let message go
     98 		 * and assume higher-level code will return an
     99 		 * "I don't know what you mean" error and recover.
    100 		 */
    101 		break;
    102 	case FUSE_LOOKUP:
    103 	case FUSE_UNLINK:
    104 	case FUSE_RMDIR:
    105 	case FUSE_REMOVEXATTR:
    106 		/* just a string */
    107 		if(((char*)m->tx)[m->hdr->len-1] != 0)
    108 		bad:
    109 			sysfatal("readfusemsg: bad message");
    110 		break;
    111 	case FUSE_FORGET:
    112 		if(m->hdr->len < sizeof(struct fuse_forget_in))
    113 			goto bad;
    114 		break;
    115 	case FUSE_GETATTR:
    116 		break;
    117 	case FUSE_SETATTR:
    118 		if(m->hdr->len < sizeof(struct fuse_setattr_in))
    119 			goto bad;
    120 		break;
    121 	case FUSE_READLINK:
    122 		break;
    123 	case FUSE_SYMLINK:
    124 		/* two strings */
    125 		if(((char*)m->tx)[m->hdr->len-1] != 0
    126 		|| memchr(m->tx, 0, m->hdr->len-1) == 0)
    127 			goto bad;
    128 		break;
    129 	case FUSE_MKNOD:
    130 		if(m->hdr->len <= sizeof(struct fuse_mknod_in)
    131 		|| ((char*)m->tx)[m->hdr->len-1] != 0)
    132 			goto bad;
    133 		break;
    134 	case FUSE_MKDIR:
    135 		if(m->hdr->len <= sizeof(struct fuse_mkdir_in)
    136 		|| ((char*)m->tx)[m->hdr->len-1] != 0)
    137 			goto bad;
    138 		break;
    139 	case FUSE_RENAME:
    140 		/* a struct and two strings */
    141 		if(m->hdr->len <= sizeof(struct fuse_rename_in)
    142 		|| ((char*)m->tx)[m->hdr->len-1] != 0
    143 		|| memchr((uchar*)m->tx+sizeof(struct fuse_rename_in), 0, m->hdr->len-sizeof(struct fuse_rename_in)-1) == 0)
    144 			goto bad;
    145 		break;
    146 	case FUSE_LINK:
    147 		if(m->hdr->len <= sizeof(struct fuse_link_in)
    148 		|| ((char*)m->tx)[m->hdr->len-1] != 0)
    149 			goto bad;
    150 		break;
    151 	case FUSE_OPEN:
    152 	case FUSE_OPENDIR:
    153 		if(m->hdr->len < sizeof(struct fuse_open_in))
    154 			goto bad;
    155 		break;
    156 	case FUSE_READ:
    157 	case FUSE_READDIR:
    158 		if(m->hdr->len < sizeof(struct fuse_read_in))
    159 			goto bad;
    160 		break;
    161 	case FUSE_WRITE:
    162 		/* no strings, but check that write length is sane */
    163 		if(m->hdr->len < sizeof(struct fuse_write_in)+((struct fuse_write_in*)m->tx)->size)
    164 			goto bad;
    165 		break;
    166 	case FUSE_STATFS:
    167 		break;
    168 	case FUSE_RELEASE:
    169 	case FUSE_RELEASEDIR:
    170 		if(m->hdr->len < sizeof(struct fuse_release_in))
    171 			goto bad;
    172 		break;
    173 	case FUSE_FSYNC:
    174 	case FUSE_FSYNCDIR:
    175 		if(m->hdr->len < sizeof(struct fuse_fsync_in))
    176 			goto bad;
    177 		break;
    178 	case FUSE_SETXATTR:
    179 		/* struct, one string, and one binary blob */
    180 		if(m->hdr->len <= sizeof(struct fuse_setxattr_in))
    181 			goto bad;
    182 		nn = ((struct fuse_setxattr_in*)m->tx)->size;
    183 		if(m->hdr->len < sizeof(struct fuse_setxattr_in)+nn+1)
    184 			goto bad;
    185 		if(((char*)m->tx)[m->hdr->len-nn-1] != 0)
    186 			goto bad;
    187 		break;
    188 	case FUSE_GETXATTR:
    189 		/* struct and one string */
    190 		if(m->hdr->len <= sizeof(struct fuse_getxattr_in)
    191 		|| ((char*)m->tx)[m->hdr->len-1] != 0)
    192 			goto bad;
    193 		break;
    194 	case FUSE_LISTXATTR:
    195 		if(m->hdr->len < sizeof(struct fuse_getxattr_in))
    196 			goto bad;
    197 		break;
    198 	case FUSE_FLUSH:
    199 		if(m->hdr->len < sizeof(struct fuse_flush_in))
    200 			goto bad;
    201 		break;
    202 	case FUSE_INIT:
    203 		if(m->hdr->len < sizeof(struct fuse_init_in))
    204 			goto bad;
    205 		break;
    206 	case FUSE_ACCESS:
    207 		if(m->hdr->len < sizeof(struct fuse_access_in))
    208 			goto bad;
    209 		break;
    210 	case FUSE_CREATE:
    211 		if(m->hdr->len <= sizeof(struct fuse_open_in)
    212 		|| ((char*)m->tx)[m->hdr->len-1] != 0)
    213 			goto bad;
    214 		break;
    215 	}
    216 	if(debug)
    217 		fprint(2, "FUSE -> %G\n", m->hdr, m->tx);
    218 	return m;
    219 }
    220 
    221 /*
    222  * Reply to FUSE request m using additonal
    223  * argument buffer arg of size narg bytes.
    224  * Perhaps should free the FuseMsg here?
    225  */
    226 void
    227 replyfuse(FuseMsg *m, void *arg, int narg)
    228 {
    229 	struct iovec vec[2];
    230 	struct fuse_out_header hdr;
    231 	int nvec;
    232 
    233 	hdr.len = sizeof hdr + narg;
    234 	hdr.error = 0;
    235 	hdr.unique = m->hdr->unique;
    236 	if(debug)
    237 		fprint(2, "FUSE <- %#G\n", m->hdr, &hdr, arg);
    238 
    239 	vec[0].iov_base = &hdr;
    240 	vec[0].iov_len = sizeof hdr;
    241 	nvec = 1;
    242 	if(arg && narg){
    243 		vec[1].iov_base = arg;
    244 		vec[1].iov_len = narg;
    245 		nvec++;
    246 	}
    247 	writev(fusefd, vec, nvec);
    248 	freefusemsg(m);
    249 }
    250 
    251 /*
    252  * Reply to FUSE request m with errno e.
    253  */
    254 void
    255 replyfuseerrno(FuseMsg *m, int e)
    256 {
    257 	struct fuse_out_header hdr;
    258 
    259 	hdr.len = sizeof hdr;
    260 	hdr.error = -e;	/* FUSE sends negative errnos. */
    261 	hdr.unique = m->hdr->unique;
    262 	if(debug)
    263 		fprint(2, "FUSE <- %#G\n", m->hdr, &hdr, 0);
    264 	write(fusefd, &hdr, sizeof hdr);
    265 	freefusemsg(m);
    266 }
    267 
    268 void
    269 replyfuseerrstr(FuseMsg *m)
    270 {
    271 	replyfuseerrno(m, errstr2errno());
    272 }
    273 
    274 char *fusemtpt;
    275 void
    276 unmountatexit(void)
    277 {
    278 	if(fusemtpt)
    279 		unmountfuse(fusemtpt);
    280 }
    281 
    282 void
    283 initfuse(char *mtpt)
    284 {
    285 	FuseMsg *m;
    286 	struct fuse_init_in *tx;
    287 	struct fuse_init_out rx;
    288 
    289 	fusemtpt = mtpt;
    290 
    291 	/*
    292 	 * The 4096 is for the message headers.
    293 	 * It's a lot, but it's what the FUSE libraries ask for.
    294 	 */
    295 	fusemaxwrite = getpagesize();
    296 	fusebufsize = 4096 + fusemaxwrite;
    297 
    298 	if((fusefd = mountfuse(mtpt)) < 0)
    299 		sysfatal("mountfuse: %r");
    300 
    301 	if((m = readfusemsg()) == nil)
    302 		sysfatal("readfusemsg: %r");
    303 	if(m->hdr->opcode != FUSE_INIT)
    304 		sysfatal("fuse: expected FUSE_INIT (26) got %d", m->hdr->opcode);
    305 	tx = m->tx;
    306 
    307 	/*
    308 	 * Complain if the kernel is too new.
    309 	 * We could forge ahead, but at least the one time I tried,
    310 	 * the kernel rejected the newer version by making the
    311 	 * writev fail in replyfuse, which is a much more confusing
    312 	 * error message.  In the future, might be nice to try to
    313 	 * support older versions that differ only slightly.
    314 	 */
    315 	if(tx->major < FUSE_KERNEL_VERSION
    316 	|| (tx->major == FUSE_KERNEL_VERSION && tx->minor < FUSE_KERNEL_MINOR_VERSION))
    317 		sysfatal("fuse: too kernel version %d.%d older than program version %d.%d",
    318 			tx->major, tx->minor, FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
    319 
    320 	memset(&rx, 0, sizeof rx);
    321 	rx.major = FUSE_KERNEL_VERSION;
    322 	rx.minor = FUSE_KERNEL_MINOR_VERSION;
    323 	rx.max_write = fusemaxwrite;
    324 	replyfuse(m, &rx, sizeof rx);
    325 }
    326 
    327 /*
    328  * Print FUSE messages.  Assuming it is installed as %G,
    329  * use %G with hdr, arg arguments to format a request,
    330  * and %#G with reqhdr, hdr, arg arguments to format a response.
    331  * The reqhdr is necessary in the %#G form because the
    332  * response does not contain an opcode tag.
    333  */
    334 int
    335 fusefmt(Fmt *fmt)
    336 {
    337 	struct fuse_in_header *hdr = va_arg(fmt->args, void*);
    338 	if((fmt->flags&FmtSharp) == 0){  /* "%G", hdr, arg */
    339 		void *a = va_arg(fmt->args, void*);
    340 		fmtprint(fmt, "len %d unique %#llux uid %d gid %d pid %d ",
    341 			hdr->len, hdr->unique, hdr->uid, hdr->gid, hdr->pid);
    342 
    343 		switch(hdr->opcode){
    344 			default: {
    345 				fmtprint(fmt, "??? opcode %d", hdr->opcode);
    346 				break;
    347 			}
    348 			case FUSE_LOOKUP: {
    349 				fmtprint(fmt, "Lookup nodeid %#llux name %#q",
    350 					hdr->nodeid, a);
    351 				break;
    352 			}
    353 			case FUSE_FORGET: {
    354 				struct fuse_forget_in *tx = a;
    355 				/* nlookup (a ref count) is a vlong! */
    356 				fmtprint(fmt, "Forget nodeid %#llux nlookup %lld",
    357 					hdr->nodeid, tx->nlookup);
    358 				break;
    359 			}
    360 			case FUSE_GETATTR: {
    361 				fmtprint(fmt, "Getattr nodeid %#llux", hdr->nodeid);
    362 				break;
    363 			}
    364 			case FUSE_SETATTR: {
    365 				struct fuse_setattr_in *tx = a;
    366 				fmtprint(fmt, "Setattr nodeid %#llux", hdr->nodeid);
    367 				if(tx->valid&FATTR_FH)
    368 					fmtprint(fmt, " fh %#llux", tx->fh);
    369 				if(tx->valid&FATTR_SIZE)
    370 					fmtprint(fmt, " size %lld", tx->size);
    371 				if(tx->valid&FATTR_ATIME)
    372 					fmtprint(fmt, " atime %.20g", tx->atime+tx->atimensec*1e-9);
    373 				if(tx->valid&FATTR_MTIME)
    374 					fmtprint(fmt, " mtime %.20g", tx->mtime+tx->mtimensec*1e-9);
    375 				if(tx->valid&FATTR_MODE)
    376 					fmtprint(fmt, " mode %#uo", tx->mode);
    377 				if(tx->valid&FATTR_UID)
    378 					fmtprint(fmt, " uid %d", tx->uid);
    379 				if(tx->valid&FATTR_GID)
    380 					fmtprint(fmt, " gid %d", tx->gid);
    381 				break;
    382 			}
    383 			case FUSE_READLINK: {
    384 				fmtprint(fmt, "Readlink nodeid %#llux", hdr->nodeid);
    385 				break;
    386 			}
    387 			case FUSE_SYMLINK: {
    388 				char *old, *new;
    389 
    390 				old = a;
    391 				new = a + strlen(a) + 1;
    392 				fmtprint(fmt, "Symlink nodeid %#llux old %#q new %#q",
    393 					hdr->nodeid, old, new);
    394 				break;
    395 			}
    396 			case FUSE_MKNOD: {
    397 				struct fuse_mknod_in *tx = a;
    398 				fmtprint(fmt, "Mknod nodeid %#llux mode %#uo rdev %#ux name %#q",
    399 					hdr->nodeid, tx->mode, tx->rdev, tx+1);
    400 				break;
    401 			}
    402 			case FUSE_MKDIR: {
    403 				struct fuse_mkdir_in *tx = a;
    404 				fmtprint(fmt, "Mkdir nodeid %#llux mode %#uo name %#q",
    405 					hdr->nodeid, tx->mode, tx+1);
    406 				break;
    407 			}
    408 			case FUSE_UNLINK: {
    409 				fmtprint(fmt, "Unlink nodeid %#llux name %#q",
    410 					hdr->nodeid, a);
    411 				break;
    412 			}
    413 			case FUSE_RMDIR: {
    414 				fmtprint(fmt, "Rmdir nodeid %#llux name %#q",
    415 					hdr->nodeid, a);
    416 				break;
    417 			}
    418 			case FUSE_RENAME: {
    419 				struct fuse_rename_in *tx = a;
    420 				char *old = (char*)(tx+1);
    421 				char *new = old + strlen(old) + 1;
    422 				fmtprint(fmt, "Rename nodeid %#llux old %#q newdir %#llux new %#q",
    423 					hdr->nodeid, old, tx->newdir, new);
    424 				break;
    425 			}
    426 			case FUSE_LINK: {
    427 				struct fuse_link_in *tx = a;
    428 				fmtprint(fmt, "Link oldnodeid %#llux nodeid %#llux name %#q",
    429 					tx->oldnodeid, hdr->nodeid, tx+1);
    430 				break;
    431 			}
    432 			case FUSE_OPEN: {
    433 				struct fuse_open_in *tx = a;
    434 				/* Should one or both of flags and mode be octal? */
    435 				fmtprint(fmt, "Open nodeid %#llux flags %#ux mode %#ux",
    436 					hdr->nodeid, tx->flags, tx->mode);
    437 				break;
    438 			}
    439 			case FUSE_READ: {
    440 				struct fuse_read_in *tx = a;
    441 				fmtprint(fmt, "Read nodeid %#llux fh %#llux offset %lld size %ud",
    442 					hdr->nodeid, tx->fh, tx->offset, tx->size);
    443 				break;
    444 			}
    445 			case FUSE_WRITE: {
    446 				struct fuse_write_in *tx = a;
    447 				fmtprint(fmt, "Write nodeid %#llux fh %#llux offset %lld size %ud flags %#ux",
    448 					hdr->nodeid, tx->fh, tx->offset, tx->size, tx->write_flags);
    449 				break;
    450 			}
    451 			case FUSE_STATFS: {
    452 				fmtprint(fmt, "Statfs");
    453 				break;
    454 			}
    455 			case FUSE_RELEASE: {
    456 				struct fuse_release_in *tx = a;
    457 				fmtprint(fmt, "Release nodeid %#llux fh %#llux flags %#ux",
    458 					hdr->nodeid, tx->fh, tx->flags);
    459 				break;
    460 			}
    461 			case FUSE_FSYNC: {
    462 				struct fuse_fsync_in *tx = a;
    463 				fmtprint(fmt, "Fsync nodeid %#llux fh %#llux flags %#ux",
    464 					hdr->nodeid, tx->fh, tx->fsync_flags);
    465 				break;
    466 			}
    467 			case FUSE_SETXATTR: {
    468 				struct fuse_setxattr_in *tx = a;
    469 				char *name = (char*)(tx+1);
    470 				char *value = name + strlen(name) + 1;
    471 				fmtprint(fmt, "Setxattr nodeid %#llux size %d flags %#ux name %#q value %#q",
    472 					hdr->nodeid, tx->size, tx->flags, name, value);
    473 				break;
    474 			}
    475 			case FUSE_GETXATTR: {
    476 				struct fuse_getxattr_in *tx = a;
    477 				fmtprint(fmt, "Getxattr nodeid %#llux size %d name %#q",
    478 					hdr->nodeid, tx->size, tx+1);
    479 				break;
    480 			}
    481 			case FUSE_LISTXATTR: {
    482 				struct fuse_getxattr_in *tx = a;
    483 				fmtprint(fmt, "Listxattr nodeid %#llux size %d",
    484 					hdr->nodeid, tx->size);
    485 				break;
    486 			}
    487 			case FUSE_REMOVEXATTR: {
    488 				fmtprint(fmt, "Removexattr nodeid %#llux name %#q",
    489 					hdr->nodeid, a);
    490 				break;
    491 			}
    492 			case FUSE_FLUSH: {
    493 				struct fuse_flush_in *tx = a;
    494 				fmtprint(fmt, "Flush nodeid %#llux fh %#llux flags %#ux",
    495 					hdr->nodeid, tx->fh, tx->flush_flags);
    496 				break;
    497 			}
    498 			case FUSE_INIT: {
    499 				struct fuse_init_in *tx = a;
    500 				fmtprint(fmt, "Init major %d minor %d",
    501 					tx->major, tx->minor);
    502 				break;
    503 			}
    504 			case FUSE_OPENDIR: {
    505 				struct fuse_open_in *tx = a;
    506 				fmtprint(fmt, "Opendir nodeid %#llux flags %#ux mode %#ux",
    507 					hdr->nodeid, tx->flags, tx->mode);
    508 				break;
    509 			}
    510 			case FUSE_READDIR: {
    511 				struct fuse_read_in *tx = a;
    512 				fmtprint(fmt, "Readdir nodeid %#llux fh %#llux offset %lld size %ud",
    513 					hdr->nodeid, tx->fh, tx->offset, tx->size);
    514 				break;
    515 			}
    516 			case FUSE_RELEASEDIR: {
    517 				struct fuse_release_in *tx = a;
    518 				fmtprint(fmt, "Releasedir nodeid %#llux fh %#llux flags %#ux",
    519 					hdr->nodeid, tx->fh, tx->flags);
    520 				break;
    521 			}
    522 			case FUSE_FSYNCDIR: {
    523 				struct fuse_fsync_in *tx = a;
    524 				fmtprint(fmt, "Fsyncdir nodeid %#llux fh %#llux flags %#ux",
    525 					hdr->nodeid, tx->fh, tx->fsync_flags);
    526 				break;
    527 			}
    528 			case FUSE_ACCESS: {
    529 				struct fuse_access_in *tx  = a;
    530 				fmtprint(fmt, "Access nodeid %#llux mask %#ux",
    531 					hdr->nodeid, tx->mask);
    532 				break;
    533 			}
    534 			case FUSE_CREATE: {
    535 				struct fuse_open_in *tx = a;
    536 				fmtprint(fmt, "Create nodeid %#llx flags %#ux mode %#ux name %#q",
    537 					hdr->nodeid, tx->flags, tx->mode, tx+1);
    538 				break;
    539 			}
    540 		}
    541 	}else{  /* "%#G", reqhdr, hdr, arg - use reqhdr only for type */
    542 		struct fuse_out_header *ohdr = va_arg(fmt->args, void*);
    543 		void *a = va_arg(fmt->args, void*);
    544 		int len = ohdr->len - sizeof *ohdr;
    545 		fmtprint(fmt, "unique %#llux ", ohdr->unique);
    546 		if(ohdr->error){
    547 			fmtprint(fmt, "error %d %s", ohdr->error, strerror(-ohdr->error));
    548 		}else
    549 		switch(hdr->opcode){
    550 			default: {
    551 				fmtprint(fmt, "??? opcode %d", hdr->opcode);
    552 				break;
    553 			}
    554 			case FUSE_LOOKUP: {
    555 				/*
    556 				 * For a negative entry, can send back ENOENT
    557 				 * or rx->ino == 0.
    558 				 * In protocol version 7.4 and before, can only use
    559 				 * the ENOENT method.
    560 				 * Presumably the benefit of sending rx->ino == 0
    561 				 * is that you can specify the length of time to cache
    562 				 * the negative result.
    563 				 */
    564 				struct fuse_entry_out *rx;
    565 				fmtprint(fmt, "(Lookup) ");
    566 			fmt_entry_out:
    567 				rx = a;
    568 				fmtprint(fmt, "nodeid %#llux gen %#llux entry_valid %.20g attr_valid %.20g ",
    569 					rx->nodeid, rx->generation,
    570 					rx->entry_valid+rx->entry_valid_nsec*1e-9,
    571 					rx->attr_valid+rx->attr_valid_nsec*1e-9);
    572 				fmtprint(fmt, " ino %#llux size %lld blocks %lld atime %.20g mtime %.20g ctime %.20g mode %#uo nlink %d uid %d gid %d rdev %#ux",
    573 					rx->attr.ino, rx->attr.size, rx->attr.blocks,
    574 					rx->attr.atime+rx->attr.atimensec*1e-9,
    575 					rx->attr.mtime+rx->attr.mtimensec*1e-9,
    576 					rx->attr.ctime+rx->attr.ctimensec*1e-9,
    577 					rx->attr.mode, rx->attr.nlink, rx->attr.uid,
    578 					rx->attr.gid, rx->attr.rdev);
    579 				break;
    580 			}
    581 			case FUSE_FORGET: {
    582 				/* Can't happen! No reply. */
    583 				fmtprint(fmt, "(Forget) can't happen");
    584 				break;
    585 			}
    586 			case FUSE_GETATTR: {
    587 				struct fuse_attr_out *rx;
    588 				fmtprint(fmt, "(Getattr) ");
    589 			fmt_attr_out:
    590 				rx = a;
    591 				fmtprint(fmt, "attr_valid %.20g",
    592 					rx->attr_valid+rx->attr_valid_nsec*1e-9);
    593 				fmtprint(fmt, " ino %#llux size %lld blocks %lld atime %.20g mtime %.20g ctime %.20g mode %#uo nlink %d uid %d gid %d rdev %#ux",
    594 					rx->attr.ino, rx->attr.size, rx->attr.blocks,
    595 					rx->attr.atime+rx->attr.atimensec*1e-9,
    596 					rx->attr.mtime+rx->attr.mtimensec*1e-9,
    597 					rx->attr.ctime+rx->attr.ctimensec*1e-9,
    598 					rx->attr.mode, rx->attr.nlink, rx->attr.uid,
    599 					rx->attr.gid, rx->attr.rdev);
    600 				break;
    601 			}
    602 			case FUSE_SETATTR: {
    603 				fmtprint(fmt, "(Setattr) ");
    604 				goto fmt_attr_out;
    605 				break;
    606 			}
    607 			case FUSE_READLINK: {
    608 				fmtprint(fmt, "(Readlink) %#.*q",
    609 					utfnlen(a, len), a);
    610 				break;
    611 			}
    612 			case FUSE_SYMLINK: {
    613 				fmtprint(fmt, "(Symlink) ");
    614 				goto fmt_entry_out;
    615 				break;
    616 			}
    617 			case FUSE_MKNOD: {
    618 				fmtprint(fmt, "(Mknod) ");
    619 				goto fmt_entry_out;
    620 				break;
    621 			}
    622 			case FUSE_MKDIR: {
    623 				fmtprint(fmt, "(Mkdir) ");
    624 				goto fmt_entry_out;
    625 				break;
    626 			}
    627 			case FUSE_UNLINK: {
    628 				fmtprint(fmt, "(Unlink)");
    629 				break;
    630 			}
    631 			case FUSE_RMDIR: {
    632 				fmtprint(fmt, "(Rmdir)");
    633 				break;
    634 			}
    635 			case FUSE_RENAME: {
    636 				fmtprint(fmt, "(Rename)");
    637 				break;
    638 			}
    639 			case FUSE_LINK: {
    640 				fmtprint(fmt, "(Link) ");
    641 				goto fmt_entry_out;
    642 				break;
    643 			}
    644 			case FUSE_OPEN: {
    645 				struct fuse_open_out *rx;
    646 				fmtprint(fmt, "(Open) ");
    647 			fmt_open_out:
    648 				rx = a;
    649 				fmtprint(fmt, "fh %#llux flags %#ux", rx->fh, rx->open_flags);
    650 				break;
    651 			}
    652 			case FUSE_READ: {
    653 				fmtprint(fmt, "(Read) size %d", len);
    654 				break;
    655 			}
    656 			case FUSE_WRITE: {
    657 				struct fuse_write_out *rx = a;
    658 				fmtprint(fmt, "(Write) size %d", rx->size);
    659 				break;
    660 			}
    661 			case FUSE_STATFS: {
    662 				/*
    663 				 * Before protocol version 7.4, only first 48 bytes are used.
    664 				 */
    665 				struct fuse_statfs_out *rx = a;
    666 				fmtprint(fmt, "(Statfs) blocks %lld bfree %lld bavail %lld files %lld ffree %lld bsize %ud namelen %ud frsize %ud",
    667 					rx->st.blocks, rx->st.bfree, rx->st.bavail,
    668 					rx->st.files, rx->st.ffree, rx->st.bsize,
    669 					rx->st.namelen, rx->st.frsize);
    670 				break;
    671 			}
    672 			case FUSE_RELEASE: {
    673 				fmtprint(fmt, "(Release)");
    674 				break;
    675 			}
    676 			case FUSE_FSYNC: {
    677 				fmtprint(fmt, "(Fsync)");
    678 				break;
    679 			}
    680 			case FUSE_SETXATTR: {
    681 				fmtprint(fmt, "(Setxattr)");
    682 				break;
    683 			}
    684 			case FUSE_GETXATTR: {
    685 				fmtprint(fmt, "(Getxattr) size %d", len);
    686 				break;
    687 			}
    688 			case FUSE_LISTXATTR: {
    689 				fmtprint(fmt, "(Lisrxattr) size %d", len);
    690 				break;
    691 			}
    692 			case FUSE_REMOVEXATTR: {
    693 				fmtprint(fmt, "(Removexattr)");
    694 				break;
    695 			}
    696 			case FUSE_FLUSH: {
    697 				fmtprint(fmt, "(Flush)");
    698 				break;
    699 			}
    700 			case FUSE_INIT: {
    701 				struct fuse_init_out *rx = a;
    702 				fmtprint(fmt, "(Init) major %d minor %d max_write %d",
    703 					rx->major, rx->minor, rx->max_write);
    704 				break;
    705 			}
    706 			case FUSE_OPENDIR: {
    707 				fmtprint(fmt, "(Opendir) ");
    708 				goto fmt_open_out;
    709 				break;
    710 			}
    711 			case FUSE_READDIR: {
    712 				fmtprint(fmt, "(Readdir) size %d", len);
    713 				break;
    714 			}
    715 			case FUSE_RELEASEDIR: {
    716 				fmtprint(fmt, "(Releasedir)");
    717 				break;
    718 			}
    719 			case FUSE_FSYNCDIR: {
    720 				fmtprint(fmt, "(Fsyncdir)");
    721 				break;
    722 			}
    723 			case FUSE_ACCESS: {
    724 				fmtprint(fmt, "(Access)");
    725 				break;
    726 			}
    727 			case FUSE_CREATE: {
    728 				struct fuse_create_out *rx = a;
    729 				fmtprint(fmt, "(Create) ");
    730 				fmtprint(fmt, "nodeid %#llux gen %#llux entry_valid %.20g attr_valid %.20g ",
    731 					rx->e.nodeid, rx->e.generation,
    732 					rx->e.entry_valid+rx->e.entry_valid_nsec*1e-9,
    733 					rx->e.attr_valid+rx->e.attr_valid_nsec*1e-9);
    734 				fmtprint(fmt, " ino %#llux size %lld blocks %lld atime %.20g mtime %.20g ctime %.20g mode %#uo nlink %d uid %d gid %d rdev %#ux",
    735 					rx->e.attr.ino, rx->e.attr.size, rx->e.attr.blocks,
    736 					rx->e.attr.atime+rx->e.attr.atimensec*1e-9,
    737 					rx->e.attr.mtime+rx->e.attr.mtimensec*1e-9,
    738 					rx->e.attr.ctime+rx->e.attr.ctimensec*1e-9,
    739 					rx->e.attr.mode, rx->e.attr.nlink, rx->e.attr.uid,
    740 					rx->e.attr.gid, rx->e.attr.rdev);
    741 				fmtprint(fmt, " fh %#llux flags %#ux", rx->o.fh, rx->o.open_flags);
    742 				break;
    743 			}
    744 		}
    745 	}
    746 	return 0;
    747 }
    748 
    749 #if defined(__APPLE__)
    750 #include <sys/param.h>
    751 #include <sys/mount.h>
    752 #endif
    753 
    754 /*
    755  * Mounts a fuse file system on mtpt and returns
    756  * a file descriptor for the corresponding fuse
    757  * message conversation.
    758  */
    759 int
    760 mountfuse(char *mtpt)
    761 {
    762 #if defined(__linux__)
    763 	int p[2], pid, fd;
    764 	char buf[20];
    765 
    766 	if(socketpair(AF_UNIX, SOCK_STREAM, 0, p) < 0)
    767 		return -1;
    768 	pid = fork();
    769 	if(pid < 0)
    770 		return -1;
    771 	if(pid == 0){
    772 		close(p[1]);
    773 		snprint(buf, sizeof buf, "%d", p[0]);
    774 		putenv("_FUSE_COMMFD", buf);
    775 		execlp("fusermount", "fusermount", "--", mtpt, nil);
    776 		fprint(2, "exec fusermount: %r\n");
    777 		_exit(1);
    778 	}
    779 	close(p[0]);
    780 	fd = recvfd(p[1]);
    781 	close(p[1]);
    782 	return fd;
    783 #elif defined(__FreeBSD__) && !defined(__APPLE__)
    784 	int pid, fd;
    785 	char buf[20];
    786 
    787 	if((fd = open("/dev/fuse", ORDWR)) < 0)
    788 		return -1;
    789 	snprint(buf, sizeof buf, "%d", fd);
    790 
    791 	pid = fork();
    792 	if(pid < 0)
    793 		return -1;
    794 	if(pid == 0){
    795 		execlp("mount_fusefs", "mount_fusefs", buf, mtpt, nil);
    796 		fprint(2, "exec mount_fusefs: %r\n");
    797 		_exit(1);
    798 	}
    799 	return fd;
    800 #elif defined(__APPLE__)
    801 	int i, pid, fd, r, p[2];
    802 	char buf[20];
    803 	struct vfsconf vfs;
    804 	char *f, *v;
    805 
    806 	if(getvfsbyname(v="osxfusefs", &vfs) < 0 &&
    807 	   getvfsbyname(v="macfuse", &vfs) < 0 &&
    808 	   getvfsbyname(v="osxfuse", &vfs) < 0 &&
    809 	   getvfsbyname(v="fusefs", &vfs) < 0){
    810 		if(access((v="osxfusefs", f="/Library/Filesystems/osxfusefs.fs"
    811 			"/Support/load_osxfusefs"), 0) < 0 &&
    812 		   access((v="macfuse", f="/Library/Filesystems/macfuse.fs"
    813 			"/Contents/Resources/load_macfuse"), 0) < 0 &&
    814 		   access((v="osxfuse", f="/Library/Filesystems/osxfuse.fs"
    815 			"/Contents/Resources/load_osxfuse"), 0) < 0 &&
    816 		   access((v="osxfuse", f="/opt/local/Library/Filesystems/osxfuse.fs"
    817 			"/Contents/Resources/load_osxfuse"), 0) < 0 &&
    818 		   access((v="fusefs", f="/System/Library/Extensions/fusefs.kext"
    819 			"/Contents/Resources/load_fusefs"), 0) < 0 &&
    820 		   access(f="/Library/Extensions/fusefs.kext"
    821 		   	"/Contents/Resources/load_fusefs", 0) < 0 &&
    822 		   access(f="/Library/Filesystems"
    823 				  "/fusefs.fs/Support/load_fusefs", 0) < 0 &&
    824 		   access(f="/System/Library/Filesystems"
    825 		         "/fusefs.fs/Support/load_fusefs", 0) < 0){
    826 		   	werrstr("cannot find load_fusefs");
    827 		   	return -1;
    828 		}
    829 		if((r=system(f)) < 0){
    830 			werrstr("%s: %r", f);
    831 			return -1;
    832 		}
    833 		if(r != 0){
    834 			werrstr("load_fusefs failed: exit %d", r);
    835 			return -1;
    836 		}
    837 		if(getvfsbyname(v, &vfs) < 0){
    838 			werrstr("getvfsbyname %s: %r", v);
    839 			return -1;
    840 		}
    841 	}
    842 
    843 	/* MacFUSE >=4 dropped support for passing fd */
    844 	if (strcmp(v, "macfuse") == 0) {
    845 		if(socketpair(AF_UNIX, SOCK_STREAM, 0, p) < 0)
    846 			return -1;
    847 		pid = fork();
    848 		if(pid < 0)
    849 			return -1;
    850 		if(pid == 0){
    851 			close(p[1]);
    852 			snprint(buf, sizeof buf, "%d", p[0]);
    853 			putenv("_FUSE_COMMFD", buf);
    854 			putenv("_FUSE_COMMVERS", "2");
    855 			putenv("_FUSE_CALL_BY_LIB", "1");
    856 			putenv("_FUSE_DAEMON_PATH",
    857 				"/Library/Filesystems/macfuse.fs/Contents/Resources/mount_macfus");
    858 			execl("/Library/Filesystems/macfuse.fs/Contents/Resources/mount_macfuse",
    859 				"mount_macfuse", mtpt, nil);
    860 			fprint(2, "exec mount_macfuse: %r\n");
    861 			_exit(1);
    862 		}
    863 		close(p[0]);
    864 		fd = recvfd(p[1]);
    865 		close(p[1]);
    866 		return fd;
    867 	}
    868 
    869 	/* Look for available FUSE device. */
    870 	/*
    871 	 * We need to truncate `fs` from the end of the vfs name if
    872 	 * it's present
    873 	 */
    874 	int len;
    875 	if (strcmp(v, "osxfuse") == 0) {
    876 		len = strlen(v);
    877 	} else {
    878 		len = strlen(v)-2;
    879 	}
    880 	for(i=0;; i++){
    881 		snprint(buf, sizeof buf, "/dev/%.*s%d", len, v, i);
    882 		if(access(buf, 0) < 0){
    883 			werrstr("no available fuse devices");
    884 			return -1;
    885 		}
    886 		if((fd = open(buf, ORDWR)) >= 0)
    887 			break;
    888 	}
    889 
    890 	pid = fork();
    891 	if(pid < 0)
    892 		return -1;
    893 	if(pid == 0){
    894 		snprint(buf, sizeof buf, "%d", fd);
    895 		/* OSXFUSE >=3.3 changed the name of the environment variable, set both */
    896 		putenv("MOUNT_FUSEFS_CALL_BY_LIB", "");
    897 		putenv("MOUNT_OSXFUSE_CALL_BY_LIB", "");
    898 		/*
    899 		 * Different versions of OSXFUSE and MacFUSE put the
    900 		 * mount_fusefs binary in different places.  Try all.
    901 		 */
    902 		/*  OSXFUSE >=3.3  greater location */
    903 		putenv("MOUNT_OSXFUSE_DAEMON_PATH",
    904 			   "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse");
    905 		execl("/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse",
    906 			  "mount_osxfuse", buf, mtpt, nil);
    907 
    908 		/* OSXFUSE >=3.3 from macports */
    909 		putenv("MOUNT_OSXFUSE_DAEMON_PATH",
    910 			"/opt/local/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse");
    911 		execl("/opt/local/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse",
    912 			"mount_osxfuse", buf, mtpt, nil);
    913 
    914 		/* Lion OSXFUSE location */
    915 		putenv("MOUNT_FUSEFS_DAEMON_PATH",
    916 			   "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs");
    917 		execl("/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs",
    918 			  "mount_osxfusefs", buf, mtpt, nil);
    919 
    920 		/* Leopard location */
    921 		putenv("MOUNT_FUSEFS_DAEMON_PATH",
    922 			   "/Library/Filesystems/fusefs.fs/Support/mount_fusefs");
    923 		execl("/Library/Filesystems/fusefs.fs/Support/mount_fusefs",
    924 			  "mount_fusefs", buf, mtpt, nil);
    925 
    926 		/* possible Tiger locations */
    927 		execl("/System/Library/Filesystems/fusefs.fs/mount_fusefs",
    928 			"mount_fusefs", buf, mtpt, nil);
    929 		execl("/System/Library/Filesystems/fusefs.fs/Support/mount_fusefs",
    930 			"mount_fusefs", buf, mtpt, nil);
    931 		fprint(2, "exec mount_fusefs: %r\n");
    932 		_exit(1);
    933 	}
    934 	return fd;
    935 
    936 #else
    937 	werrstr("cannot mount fuse on this system");
    938 	return -1;
    939 #endif
    940 }
    941 
    942 void
    943 waitfuse(void)
    944 {
    945 	waitpid();
    946 }
    947 
    948 void
    949 unmountfuse(char *mtpt)
    950 {
    951 	int pid;
    952 
    953 	pid = fork();
    954 	if(pid < 0)
    955 		return;
    956 	if(pid == 0){
    957 #if defined(__linux__)
    958 		execlp("fusermount", "fusermount", "-u", "-z", "--", mtpt, nil);
    959 		fprint(2, "exec fusermount -u: %r\n");
    960 #else
    961 		execlp("umount", "umount", mtpt, nil);
    962 		fprint(2, "exec umount: %r\n");
    963 #endif
    964 		_exit(1);
    965 	}
    966 	waitpid();
    967 }