msg.c revision 1.26 1 /* $NetBSD: msg.c,v 1.26 2021/08/08 20:54:49 nia Exp $ */
2
3 /*-
4 * Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <err.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <sysexits.h>
35 #include <syslog.h>
36 #include <paths.h>
37 #include <puffs.h>
38 #include <limits.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <sys/un.h>
42 #include <machine/vmparam.h>
43
44 #include "perfused.h"
45
46 static int xchg_pb_inloop(struct puffs_usermount *a, struct puffs_framebuf *,
47 int, enum perfuse_xchg_pb_reply);
48 static int xchg_pb_early(struct puffs_usermount *a, struct puffs_framebuf *,
49 int, enum perfuse_xchg_pb_reply);
50
51 int
52 perfused_open_sock(void)
53 {
54 int s;
55 struct sockaddr_un sun;
56 const struct sockaddr *sa;
57 uint32_t opt;
58 int sock_type = SOCK_SEQPACKET;
59
60 (void)unlink(_PATH_FUSE);
61
62 /*
63 * Try SOCK_SEQPACKET and fallback to SOCK_DGRAM
64 * if unavaible
65 */
66 if ((s = socket(PF_LOCAL, SOCK_SEQPACKET, 0)) == -1) {
67 warnx("SEQPACKET local sockets unavailable, using less "
68 "reliable DGRAM sockets. Expect file operation hangs.");
69
70 sock_type = SOCK_DGRAM;
71 if ((s = socket(PF_LOCAL, SOCK_DGRAM, 0)) == -1)
72 err(EX_OSERR, "socket failed");
73 }
74
75 sa = (const struct sockaddr *)(void *)&sun;
76 sun.sun_len = sizeof(sun);
77 sun.sun_family = AF_LOCAL;
78 (void)strcpy(sun.sun_path, _PATH_FUSE);
79
80 /*
81 * Set a buffer length large enough so that a few FUSE packets
82 * will fit.
83 */
84 opt = perfuse_bufvar_from_env("PERFUSE_BUFSIZE", 16 * FUSE_BUFSIZE);
85 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) != 0)
86 DWARN("%s: setsockopt SO_SNDBUF = %d failed", __func__, opt);
87
88 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) != 0)
89 DWARN("%s: setsockopt SO_RCVBUF = %d failed", __func__, opt);
90
91 /*
92 * Request peer credentials
93 */
94 opt = 1;
95 if (setsockopt(s, SOL_LOCAL, LOCAL_CREDS, &opt, sizeof(opt)) != 0)
96 DWARN("%s: setsockopt LOCAL_CREDS failed", __func__);
97
98 if (bind(s, sa, (socklen_t )sun.sun_len) == -1)
99 err(EX_OSERR, "cannot open \"%s\" socket", _PATH_FUSE);
100
101 if (sock_type == SOCK_DGRAM) {
102 if (connect(s, sa, (socklen_t )sun.sun_len) == -1)
103 err(EX_OSERR, "cannot open \"%s\" socket", _PATH_FUSE);
104 }
105
106 return s;
107 }
108
109
110 void *
111 perfused_recv_early(int fd, struct sockcred *sockcred, size_t sockcred_len)
112 {
113 struct fuse_out_header foh;
114 size_t len;
115 char *buf;
116 struct msghdr msg;
117 char cmsg_buf[sizeof(struct cmsghdr) + SOCKCREDSIZE(NGROUPS_MAX)];
118 struct cmsghdr *cmsg = (struct cmsghdr *)(void *)&cmsg_buf;
119 struct sockcred *sc = (struct sockcred *)(void *)(cmsg + 1);
120 struct iovec iov;
121
122 len = sizeof(foh);
123
124 /*
125 * We use the complicated recvmsg because we want peer creds.
126 */
127 iov.iov_base = &foh;
128 iov.iov_len = len;
129 msg.msg_name = NULL;
130 msg.msg_namelen = 0;
131 msg.msg_iov = &iov;
132 msg.msg_iovlen = 1;
133 msg.msg_control = cmsg;
134 msg.msg_controllen = sizeof(cmsg_buf);
135 msg.msg_flags = 0;
136
137 if (recvmsg(fd, &msg, MSG_NOSIGNAL|MSG_PEEK) != (ssize_t)len) {
138 DWARN("short recv (header)");
139 return NULL;
140 }
141
142 if (cmsg->cmsg_type != SCM_CREDS) {
143 DWARNX("No SCM_CREDS");
144 return NULL;
145 }
146
147 if (sockcred != NULL)
148 (void)memcpy(sockcred, sc,
149 MIN(cmsg->cmsg_len - sizeof(*cmsg), sockcred_len));
150
151
152 len = foh.len;
153 if ((buf = malloc(len)) == NULL)
154 err(EX_OSERR, "malloc(%zd) failed", len);
155
156 if (recv(fd, buf, len, MSG_NOSIGNAL) != (ssize_t)len) {
157 DWARN("short recv (frame)");
158 return NULL;
159 }
160
161 return buf;
162 }
163
164
165 perfuse_msg_t *
166 perfused_new_pb (struct puffs_usermount *pu, puffs_cookie_t opc, int opcode,
167 size_t payload_len, const struct puffs_cred *cred)
168 {
169 struct puffs_framebuf *pb;
170 struct fuse_in_header *fih;
171 struct puffs_cc *pcc;
172 uint64_t nodeid;
173 void *data;
174 size_t len;
175
176 if ((pb = puffs_framebuf_make()) == NULL)
177 DERR(EX_OSERR, "puffs_framebuf_make failed");
178
179 len = payload_len + sizeof(*fih);
180 if (opc != 0)
181 nodeid = perfuse_get_nodeid(pu, opc);
182 else
183 nodeid = PERFUSE_UNKNOWN_NODEID;
184
185 if (puffs_framebuf_reserve_space(pb, len) != 0)
186 DERR(EX_OSERR, "puffs_framebuf_reserve_space failed");
187
188 if (puffs_framebuf_getwindow(pb, 0, &data, &len) != 0)
189 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
190 if (len != payload_len + sizeof(*fih))
191 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short len");
192
193 (void)memset(data, 0, len);
194 fih = (struct fuse_in_header *)data;
195 fih->len = (uint32_t)len;
196 fih->opcode = opcode;
197 fih->unique = perfuse_next_unique(pu);
198 fih->nodeid = nodeid;
199 fih->pid = 0;
200
201 /*
202 * NULL creds is taken as FUSE root. This currently happens for:
203 * - mount root cred assumed
204 * - umount root cred assumed
205 * - inactive kernel cred used
206 * - statvfs root cred assumed
207 * - poll checks have been done at open() time
208 * - addvlock checks have been done at open() time
209 */
210 if ((cred != NULL) && !puffs_cred_isjuggernaut(cred)) {
211 if (puffs_cred_getuid(cred, &fih->uid) != 0)
212 DERRX(EX_SOFTWARE, "puffs_cred_getuid failed");
213 if (puffs_cred_getgid(cred, &fih->gid) != 0)
214 DERRX(EX_SOFTWARE, "puffs_cred_getgid failed");
215 } else {
216 fih->uid = (uid_t)0;
217 fih->gid = (gid_t)0;
218 }
219
220 if ((pcc = puffs_cc_getcc(pu)) != NULL)
221 (void)puffs_cc_getcaller(pcc, (pid_t *)&fih->pid, NULL);
222
223 return (perfuse_msg_t *)(void *)pb;
224 }
225
226 /*
227 * framebuf send/receive primitives based on pcc are
228 * not available until puffs mainloop is entered.
229 * This xchg_pb_inloop() variant allow early communication.
230 */
231 static int
232 xchg_pb_early(struct puffs_usermount *pu, struct puffs_framebuf *pb, int fd,
233 enum perfuse_xchg_pb_reply reply)
234 {
235 int done;
236 int error;
237
238 done = 0;
239 while (done == 0) {
240 if ((error = perfused_writeframe(pu, pb, fd, &done)) != 0)
241 return error;
242 }
243
244 if (reply == no_reply) {
245 puffs_framebuf_destroy(pb);
246 return 0;
247 } else {
248 puffs_framebuf_recycle(pb);
249 }
250
251 done = 0;
252 while (done == 0) {
253 if ((error = perfused_readframe(pu, pb, fd, &done)) != 0)
254 return error;
255 }
256
257 return 0;
258 }
259
260 static int
261 xchg_pb_inloop(struct puffs_usermount *pu, struct puffs_framebuf *pb, int fd,
262 enum perfuse_xchg_pb_reply reply)
263 {
264 struct puffs_cc *pcc;
265 int error;
266
267 if (reply == no_reply) {
268 error = puffs_framev_enqueue_justsend(pu, fd, pb, 0, 0);
269 } else {
270 pcc = puffs_cc_getcc(pu);
271 error = puffs_framev_enqueue_cc(pcc, fd, pb, 0);
272 }
273
274 return error;
275 }
276
277 int
278 perfused_xchg_pb(struct puffs_usermount *pu, perfuse_msg_t *pm,
279 size_t expected_len, enum perfuse_xchg_pb_reply reply)
280 {
281 struct puffs_framebuf *pb = (struct puffs_framebuf *)(void *)pm;
282 int fd;
283 int error;
284 struct fuse_out_header *foh;
285 #ifdef PERFUSE_DEBUG
286 struct fuse_in_header *fih;
287 uint64_t nodeid;
288 int opcode;
289 uint64_t unique_in;
290 uint64_t unique_out;
291
292 fih = perfused_get_inhdr(pm);
293 unique_in = fih->unique;
294 nodeid = fih->nodeid;
295 opcode = fih->opcode;
296
297 if (perfuse_diagflags & PDF_FUSE)
298 DPRINTF("> unique = %"PRId64", nodeid = %"PRId64", "
299 "opcode = %s (%d)\n",
300 unique_in, nodeid, perfuse_opname(opcode), opcode);
301
302 if (perfuse_diagflags & PDF_DUMP)
303 perfused_hexdump((char *)fih, fih->len);
304
305 #endif /* PERFUSE_DEBUG */
306
307 fd = (int)(intptr_t)perfuse_getspecific(pu);
308
309 if (perfuse_inloop(pu))
310 error = xchg_pb_inloop(pu, pb, fd, reply);
311 else
312 error = xchg_pb_early(pu, pb, fd, reply);
313
314 if (error)
315 DERR(EX_SOFTWARE, "xchg_pb failed");
316
317 if (reply == no_reply)
318 return 0;
319
320 foh = perfused_get_outhdr((perfuse_msg_t *)(void *)pb);
321 #ifdef PERFUSE_DEBUG
322 unique_out = foh->unique;
323
324 if (perfuse_diagflags & PDF_FUSE)
325 DPRINTF("< unique = %"PRId64", nodeid = %"PRId64", "
326 "opcode = %s (%d), "
327 "error = %d\n", unique_out, nodeid,
328 perfuse_opname(opcode), opcode, foh->error);
329
330 if (perfuse_diagflags & PDF_DUMP)
331 perfused_hexdump((char *)foh, foh->len);
332
333 if (unique_in != unique_out) {
334 printf("%s: packet mismatch unique %"PRId64" vs %"PRId64"\n",
335 __func__, unique_in, unique_out);
336 abort();
337 errx(EX_SOFTWARE, "%s: packet mismatch unique "
338 "%"PRId64" vs %"PRId64"\n",
339 __func__, unique_in, unique_out);
340 }
341 #endif /* PERFUSE_DEBUG */
342
343 if ((expected_len != PERFUSE_UNSPEC_REPLY_LEN) &&
344 (foh->len - sizeof(*foh) < expected_len) &&
345 (foh->error == 0)) {
346 DERRX(EX_PROTOCOL,
347 "Unexpected short reply: received %zd bytes, expected %zd",
348 foh->len - sizeof(*foh), expected_len);
349 }
350
351 if ((expected_len != 0) &&
352 (foh->len - sizeof(*foh) > expected_len))
353 DWARNX("Unexpected long reply");
354
355 /*
356 * Negative Linux errno...
357 */
358 if (foh->error <= 0) {
359 foh->error = -foh->error;
360 } else {
361 DWARNX("FUSE resturns positive errno %d", foh->error);
362 foh->error = 0;
363 }
364
365 return foh->error;
366 }
367
368
369 struct fuse_in_header *
370 perfused_get_inhdr(perfuse_msg_t *pm)
371 {
372 struct puffs_framebuf *pb;
373 struct fuse_in_header *fih;
374 void *hdr;
375 size_t len;
376
377 pb = (struct puffs_framebuf *)(void *)pm;
378 len = sizeof(*fih);
379 if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
380 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
381 if (len != sizeof(*fih))
382 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
383
384 fih = (struct fuse_in_header *)hdr;
385
386 return fih;
387 }
388
389 struct fuse_out_header *
390 perfused_get_outhdr(perfuse_msg_t *pm)
391 {
392 struct puffs_framebuf *pb;
393 struct fuse_out_header *foh;
394 void *hdr;
395 size_t len;
396
397 pb = (struct puffs_framebuf *)(void *)pm;
398 len = sizeof(*foh);
399 if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
400 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
401 if (len != sizeof(*foh))
402 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
403
404 foh = (struct fuse_out_header *)hdr;
405
406 return foh;
407 }
408
409 char *
410 perfused_get_inpayload(perfuse_msg_t *pm)
411 {
412 struct puffs_framebuf *pb;
413 struct fuse_in_header *fih;
414 void *hdr;
415 void *payload;
416 size_t len;
417
418 pb = (struct puffs_framebuf *)(void *)pm;
419 len = sizeof(*fih);
420 if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
421 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
422 if (len != sizeof(*fih))
423 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
424
425 fih = (struct fuse_in_header *)hdr;
426
427 len = fih->len - sizeof(*fih);
428 if (puffs_framebuf_getwindow(pb, sizeof(*fih), &payload, &len) != 0)
429 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
430 if (len != fih->len - sizeof(*fih))
431 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
432
433 return (char *)payload;
434 }
435
436 char *
437 perfused_get_outpayload(perfuse_msg_t *pm)
438 {
439 struct puffs_framebuf *pb;
440 struct fuse_out_header *foh;
441 void *hdr;
442 void *payload;
443 size_t len;
444
445 pb = (struct puffs_framebuf *)(void *)pm;
446 len = sizeof(*foh);
447 if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
448 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
449 if (len != sizeof(*foh))
450 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
451
452 foh = (struct fuse_out_header *)hdr;
453
454 len = foh->len - sizeof(*foh);
455 if (puffs_framebuf_getwindow(pb, sizeof(*foh), &payload, &len) != 0)
456 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
457 if (len != foh->len - sizeof(*foh))
458 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
459
460 return (char *)payload;
461 }
462
463 #define PUFFS_FRAMEBUF_GETWINDOW(pb, offset, data, len) \
464 do { \
465 int pfg_error; \
466 size_t pfg_len = *(len); \
467 \
468 pfg_error = puffs_framebuf_getwindow(pb, offset, data, len); \
469 if (pfg_error != 0) \
470 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");\
471 \
472 if (*(len) != pfg_len) \
473 DERRX(EX_SOFTWARE, "puffs_framebuf_getwindow size"); \
474 } while (0 /* CONSTCOND */);
475
476 /* ARGSUSED0 */
477 int
478 perfused_readframe(struct puffs_usermount *pu, struct puffs_framebuf *pufbuf,
479 int fd, int *done)
480 {
481 struct fuse_out_header foh;
482 size_t len;
483 ssize_t readen;
484 void *data;
485
486 /*
487 * Read the header
488 */
489 len = sizeof(foh);
490 PUFFS_FRAMEBUF_GETWINDOW(pufbuf, 0, &data, &len);
491
492 switch (readen = recv(fd, data, len, MSG_NOSIGNAL|MSG_PEEK)) {
493 case 0:
494 DPRINTF("Filesystem exit\n");
495 /* NOTREACHED */
496 exit(0);
497 break;
498 case -1:
499 if (errno == EAGAIN)
500 return 0;
501 DWARN("%s: recv returned -1", __func__);
502 return errno;
503 /* NOTREACHED */
504 break;
505 default:
506 break;
507 }
508
509 #ifdef PERFUSE_DEBUG
510 if (readen != (ssize_t)len)
511 DERRX(EX_SOFTWARE, "%s: short recv %zd/%zd",
512 __func__, readen, len);
513 #endif
514
515 /*
516 * We have a header, get remaing length to read
517 */
518 if (puffs_framebuf_getdata_atoff(pufbuf, 0, &foh, sizeof(foh)) != 0)
519 DERR(EX_SOFTWARE, "puffs_framebuf_getdata_atoff failed");
520
521 len = foh.len;
522
523 #ifdef PERFUSE_DEBUG
524 if (len > (size_t)FUSE_BUFSIZE)
525 DERRX(EX_SOFTWARE, "%s: foh.len = %zu", __func__, len);
526 #endif
527
528 /*
529 * This is time to reserve space.
530 */
531 if (puffs_framebuf_reserve_space(pufbuf, len) == -1)
532 DERR(EX_OSERR, "puffs_framebuf_reserve_space failed");
533
534 /*
535 * And read the remaining data
536 */
537 PUFFS_FRAMEBUF_GETWINDOW(pufbuf, 0, &data, &len);
538
539 switch (readen = recv(fd, data, len, MSG_NOSIGNAL)) {
540 case 0:
541 DWARNX("%s: recv returned 0", __func__);
542 perfused_panic();
543 case -1:
544 if (errno == EAGAIN)
545 return 0;
546 DWARN("%s: recv returned -1", __func__);
547 return errno;
548 default:
549 break;
550 }
551
552 #ifdef PERFUSE_DEBUG
553 if (readen != (ssize_t)len)
554 DERRX(EX_SOFTWARE, "%s: short recv %zd/%zd",
555 __func__, readen, len);
556 #endif
557
558 *done = 1;
559 return 0;
560 }
561
562 /* ARGSUSED0 */
563 int
564 perfused_writeframe(struct puffs_usermount *pu, struct puffs_framebuf *pufbuf,
565 int fd, int *done)
566 {
567 size_t len;
568 ssize_t written;
569 void *data;
570
571 len = puffs_framebuf_tellsize(pufbuf);
572 PUFFS_FRAMEBUF_GETWINDOW(pufbuf, 0, &data, &len);
573
574 switch (written = send(fd, data, len, MSG_NOSIGNAL)) {
575 case 0:
576 #ifdef PERFUSE_DEBUG
577 DERRX(EX_SOFTWARE, "%s: send returned 0", __func__);
578 #else
579 return ECONNRESET;
580 #endif
581 /* NOTREACHED */
582 break;
583 case -1:
584 DWARN("%s: send returned -1, errno = %d", __func__, errno);
585 switch(errno) {
586 case EAGAIN:
587 case ENOBUFS:
588 case EMSGSIZE:
589 return 0;
590 break;
591 case EPIPE:
592 perfused_panic();
593 /* NOTREACHED */
594 break;
595 default:
596 return errno;
597 break;
598 }
599 /* NOTREACHED */
600 break;
601 default:
602 break;
603 }
604
605 #ifdef PERFUSE_DEBUG
606 if (written != (ssize_t)len)
607 DERRX(EX_SOFTWARE, "%s: short send %zd/%zd",
608 __func__, written, len);
609 #endif
610
611 *done = 1;
612 return 0;
613 }
614
615 /* ARGSUSED0 */
616 int
617 perfused_cmpframe(struct puffs_usermount *pu, struct puffs_framebuf *pb1,
618 struct puffs_framebuf *pb2, int *match)
619 {
620 struct fuse_in_header *fih;
621 struct fuse_out_header *foh;
622 uint64_t unique_in;
623 uint64_t unique_out;
624 size_t len;
625
626 len = sizeof(*fih);
627 PUFFS_FRAMEBUF_GETWINDOW(pb1, 0, (void **)(void *)&fih, &len);
628 unique_in = fih->unique;
629
630 len = sizeof(*foh);
631 PUFFS_FRAMEBUF_GETWINDOW(pb2, 0, (void **)(void *)&foh, &len);
632 unique_out = foh->unique;
633
634 return unique_in != unique_out;
635 }
636
637 /* ARGSUSED0 */
638 void
639 perfused_gotframe(struct puffs_usermount *pu, struct puffs_framebuf *pb)
640 {
641 struct fuse_out_header *foh;
642 size_t len;
643
644 len = sizeof(*foh);
645 PUFFS_FRAMEBUF_GETWINDOW(pb, 0, (void **)(void *)&foh, &len);
646
647 DWARNX("Unexpected frame: unique = %"PRId64", error = %d",
648 foh->unique, foh->error);
649 #ifdef PERFUSE_DEBUG
650 perfused_hexdump((char *)(void *)foh, foh->len);
651 #endif
652
653 return;
654 }
655
656 void
657 perfused_fdnotify(struct puffs_usermount *pu, int fd, int what)
658 {
659 if (fd != (int)(intptr_t)perfuse_getspecific(pu))
660 DERRX(EX_SOFTWARE, "%s: unexpected notification for fd = %d",
661 __func__, fd);
662
663 if ((what != PUFFS_FBIO_READ) && (what != PUFFS_FBIO_WRITE))
664 DERRX(EX_SOFTWARE, "%s: unexpected notification what = 0x%x",
665 __func__, what);
666
667 if (perfuse_unmount(pu) != 0)
668 DWARN("unmount() failed");
669
670 if (shutdown(fd, SHUT_RDWR) != 0)
671 DWARN("shutdown() failed");
672
673 if (perfuse_diagflags & PDF_MISC)
674 DPRINTF("Exit");
675
676 exit(0);
677 }
678
679 void
680 perfused_umount(struct puffs_usermount *pu)
681 {
682 int fd;
683
684 fd = (int)(intptr_t)perfuse_getspecific(pu);
685
686 if (shutdown(fd, SHUT_RDWR) != 0)
687 DWARN("shutdown() failed");
688
689 if (perfuse_diagflags & PDF_MISC)
690 DPRINTF("unmount");
691
692 return;
693 }
694