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 46static int xchg_pb_inloop(struct puffs_usermount *a, struct puffs_framebuf *, 47 int, enum perfuse_xchg_pb_reply); 48static int xchg_pb_early(struct puffs_usermount *a, struct puffs_framebuf *, 49 int, enum perfuse_xchg_pb_reply); 50 51int 52perfused_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 110void * 111perfused_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 165perfuse_msg_t * 166perfused_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 */ 231static int 232xchg_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 260static int 261xchg_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 277int 278perfused_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 369struct fuse_in_header * 370perfused_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 389struct fuse_out_header * 390perfused_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 409char * 410perfused_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 436char * 437perfused_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 */ 477int 478perfused_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 */ 563int 564perfused_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 */ 616int 617perfused_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 */ 638void 639perfused_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 656void 657perfused_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 679void 680perfused_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