Blob


1 #include "a.h"
3 int fusefd;
4 int fuseeof;
5 int fusebufsize;
6 int fusemaxwrite;
7 FuseMsg *fusemsglist;
8 Lock fusemsglock;
10 int mountfuse(char *mtpt);
11 void unmountfuse(char *mtpt);
13 FuseMsg*
14 allocfusemsg(void)
15 {
16 FuseMsg *m;
17 void *vbuf;
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 }
35 void
36 freefusemsg(FuseMsg *m)
37 {
38 lock(&fusemsglock);
39 m->next = fusemsglist;
40 fusemsglist = m;
41 unlock(&fusemsglock);
42 }
44 FuseMsg*
45 readfusemsg(void)
46 {
47 FuseMsg *m;
48 int n, nn;
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;
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;
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);
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;
216 if(debug)
217 fprint(2, "FUSE -> %G\n", m->hdr, m->tx);
218 return m;
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)
229 struct iovec vec[2];
230 struct fuse_out_header hdr;
231 int nvec;
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);
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++;
247 writev(fusefd, vec, nvec);
248 freefusemsg(m);
251 /*
252 * Reply to FUSE request m with errno e.
253 */
254 void
255 replyfuseerrno(FuseMsg *m, int e)
257 struct fuse_out_header hdr;
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);
268 void
269 replyfuseerrstr(FuseMsg *m)
271 replyfuseerrno(m, errstr2errno());
274 char *fusemtpt;
275 void
276 unmountatexit(void)
278 if(fusemtpt)
279 unmountfuse(fusemtpt);
282 void
283 initfuse(char *mtpt)
285 FuseMsg *m;
286 struct fuse_init_in *tx;
287 struct fuse_init_out rx;
289 fusemtpt = mtpt;
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;
298 if((fusefd = mountfuse(mtpt)) < 0)
299 sysfatal("mountfuse: %r");
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;
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);
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);
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)
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);
343 switch(hdr->opcode){
344 default: {
345 fmtprint(fmt, "??? opcode %d", hdr->opcode);
346 break;
348 case FUSE_LOOKUP: {
349 fmtprint(fmt, "Lookup nodeid %#llux name %#q",
350 hdr->nodeid, a);
351 break;
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;
360 case FUSE_GETATTR: {
361 fmtprint(fmt, "Getattr nodeid %#llux", hdr->nodeid);
362 break;
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;
383 case FUSE_READLINK: {
384 fmtprint(fmt, "Readlink nodeid %#llux", hdr->nodeid);
385 break;
387 case FUSE_SYMLINK: {
388 char *old, *new;
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;
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;
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;
408 case FUSE_UNLINK: {
409 fmtprint(fmt, "Unlink nodeid %#llux name %#q",
410 hdr->nodeid, a);
411 break;
413 case FUSE_RMDIR: {
414 fmtprint(fmt, "Rmdir nodeid %#llux name %#q",
415 hdr->nodeid, a);
416 break;
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;
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;
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;
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;
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;
451 case FUSE_STATFS: {
452 fmtprint(fmt, "Statfs");
453 break;
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;
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;
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;
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;
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;
487 case FUSE_REMOVEXATTR: {
488 fmtprint(fmt, "Removexattr nodeid %#llux name %#q",
489 hdr->nodeid, a);
490 break;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
581 case FUSE_FORGET: {
582 /* Can't happen! No reply. */
583 fmtprint(fmt, "(Forget) can't happen");
584 break;
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;
602 case FUSE_SETATTR: {
603 fmtprint(fmt, "(Setattr) ");
604 goto fmt_attr_out;
605 break;
607 case FUSE_READLINK: {
608 fmtprint(fmt, "(Readlink) %#.*q",
609 utfnlen(a, len), a);
610 break;
612 case FUSE_SYMLINK: {
613 fmtprint(fmt, "(Symlink) ");
614 goto fmt_entry_out;
615 break;
617 case FUSE_MKNOD: {
618 fmtprint(fmt, "(Mknod) ");
619 goto fmt_entry_out;
620 break;
622 case FUSE_MKDIR: {
623 fmtprint(fmt, "(Mkdir) ");
624 goto fmt_entry_out;
625 break;
627 case FUSE_UNLINK: {
628 fmtprint(fmt, "(Unlink)");
629 break;
631 case FUSE_RMDIR: {
632 fmtprint(fmt, "(Rmdir)");
633 break;
635 case FUSE_RENAME: {
636 fmtprint(fmt, "(Rename)");
637 break;
639 case FUSE_LINK: {
640 fmtprint(fmt, "(Link) ");
641 goto fmt_entry_out;
642 break;
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;
652 case FUSE_READ: {
653 fmtprint(fmt, "(Read) size %d", len);
654 break;
656 case FUSE_WRITE: {
657 struct fuse_write_out *rx = a;
658 fmtprint(fmt, "(Write) size %d", rx->size);
659 break;
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;
672 case FUSE_RELEASE: {
673 fmtprint(fmt, "(Release)");
674 break;
676 case FUSE_FSYNC: {
677 fmtprint(fmt, "(Fsync)");
678 break;
680 case FUSE_SETXATTR: {
681 fmtprint(fmt, "(Setxattr)");
682 break;
684 case FUSE_GETXATTR: {
685 fmtprint(fmt, "(Getxattr) size %d", len);
686 break;
688 case FUSE_LISTXATTR: {
689 fmtprint(fmt, "(Lisrxattr) size %d", len);
690 break;
692 case FUSE_REMOVEXATTR: {
693 fmtprint(fmt, "(Removexattr)");
694 break;
696 case FUSE_FLUSH: {
697 fmtprint(fmt, "(Flush)");
698 break;
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;
706 case FUSE_OPENDIR: {
707 fmtprint(fmt, "(Opendir) ");
708 goto fmt_open_out;
709 break;
711 case FUSE_READDIR: {
712 fmtprint(fmt, "(Readdir) size %d", len);
713 break;
715 case FUSE_RELEASEDIR: {
716 fmtprint(fmt, "(Releasedir)");
717 break;
719 case FUSE_FSYNCDIR: {
720 fmtprint(fmt, "(Fsyncdir)");
721 break;
723 case FUSE_ACCESS: {
724 fmtprint(fmt, "(Access)");
725 break;
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;
746 return 0;
749 #if defined(__APPLE__)
750 #include <sys/param.h>
751 #include <sys/mount.h>
752 #endif
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)
762 #if defined(__linux__)
763 int p[2], pid, fd;
764 char buf[20];
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);
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];
787 if((fd = open("/dev/fuse", ORDWR)) < 0)
788 return -1;
789 snprint(buf, sizeof buf, "%d", fd);
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);
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;
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;
829 if((r=system(f)) < 0){
830 werrstr("%s: %r", f);
831 return -1;
833 if(r != 0){
834 werrstr("load_fusefs failed: exit %d", r);
835 return -1;
837 if(getvfsbyname(v, &vfs) < 0){
838 werrstr("getvfsbyname %s: %r", v);
839 return -1;
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);
863 close(p[0]);
864 fd = recvfd(p[1]);
865 close(p[1]);
866 return fd;
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;
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;
886 if((fd = open(buf, ORDWR)) >= 0)
887 break;
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);
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);
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);
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);
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);
934 return fd;
936 #else
937 werrstr("cannot mount fuse on this system");
938 return -1;
939 #endif
942 void
943 waitfuse(void)
945 waitpid();
948 void
949 unmountfuse(char *mtpt)
951 int pid;
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);
966 waitpid();