1 /* $NetBSD: msg.c,v 1.9 2010/10/11 05:37:58 manu 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 "../../lib/libperfuse/perfuse_if.h" 45 #include "perfused.h" 46 47 static int xchg_pb_inloop(struct puffs_usermount *a, struct puffs_framebuf *, 48 int, enum perfuse_xchg_pb_reply); 49 static int xchg_pb_early(struct puffs_usermount *a, struct puffs_framebuf *, 50 int, enum perfuse_xchg_pb_reply); 51 52 int 53 perfuse_open_sock(void) 54 { 55 int s; 56 struct sockaddr_un sun; 57 const struct sockaddr *sa; 58 uint32_t opt; 59 60 (void)unlink(_PATH_FUSE); 61 62 if ((s = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1) 63 err(EX_OSERR, "socket failed"); 64 65 sa = (const struct sockaddr *)(void *)&sun; 66 sun.sun_len = sizeof(sun); 67 sun.sun_family = AF_LOCAL; 68 (void)strcpy(sun.sun_path, _PATH_FUSE); 69 70 /* 71 * Set a buffer lentgh large enough so that a few FUSE packets 72 * will fit. 73 * XXX We will have to find how many packets we need 74 */ 75 opt = 4 * FUSE_BUFSIZE; 76 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) != 0) 77 DWARN("%s: setsockopt SO_SNDBUF to %d failed", __func__, opt); 78 79 opt = 4 * FUSE_BUFSIZE; 80 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) != 0) 81 DWARN("%s: setsockopt SO_RCVBUF to %d failed", __func__, opt); 82 83 /* 84 * Request peer credentials 85 */ 86 opt = 1; 87 if (setsockopt(s, 0, LOCAL_CREDS, &opt, sizeof(opt)) != 0) 88 DWARN("%s: setsockopt LOCAL_CREDS failed", __func__); 89 90 if (bind(s, sa, (socklen_t )sun.sun_len) == -1) 91 err(EX_OSERR, "cannot open \"%s\" socket", _PATH_FUSE); 92 93 if (connect(s, sa, (socklen_t )sun.sun_len) == -1) 94 err(EX_OSERR, "cannot open \"%s\" socket", _PATH_FUSE); 95 96 return s; 97 } 98 99 100 void * 101 perfuse_recv_early(fd, sockcred, sockcred_len) 102 int fd; 103 struct sockcred *sockcred; 104 size_t sockcred_len; 105 { 106 struct fuse_out_header foh; 107 size_t len; 108 char *buf; 109 struct msghdr msg; 110 char cmsg_buf[sizeof(struct cmsghdr) + SOCKCREDSIZE(NGROUPS_MAX)]; 111 struct cmsghdr *cmsg = (struct cmsghdr *)(void *)&cmsg_buf; 112 struct sockcred *sc = (struct sockcred *)(void *)(cmsg + 1); 113 struct iovec iov; 114 115 len = sizeof(foh); 116 117 /* 118 * We use the complicated recvmsg because we want peer creds. 119 */ 120 iov.iov_base = &foh; 121 iov.iov_len = len; 122 msg.msg_name = NULL; 123 msg.msg_namelen = 0; 124 msg.msg_iov = &iov; 125 msg.msg_iovlen = 1; 126 msg.msg_control = cmsg; 127 msg.msg_controllen = sizeof(cmsg_buf); 128 msg.msg_flags = 0; 129 130 if (recvmsg(fd, &msg, MSG_NOSIGNAL|MSG_PEEK) != (ssize_t)len) { 131 DWARN("short recv (header)"); 132 return NULL; 133 } 134 135 if (cmsg->cmsg_type != SCM_CREDS) { 136 DWARNX("No SCM_CREDS"); 137 return NULL; 138 } 139 140 if (sockcred != NULL) 141 (void)memcpy(sockcred, sc, 142 MIN(cmsg->cmsg_len - sizeof(*cmsg), sockcred_len)); 143 144 145 len = foh.len; 146 if ((buf = malloc(len)) == NULL) 147 err(EX_OSERR, "malloc(%zd) failed", len); 148 149 if (recv(fd, buf, len, MSG_NOSIGNAL) != (ssize_t)len) { 150 DWARN("short recv (frame)"); 151 return NULL; 152 } 153 154 return buf; 155 } 156 157 158 perfuse_msg_t * 159 perfuse_new_pb (pu, opc, opcode, payload_len, cred) 160 struct puffs_usermount *pu; 161 puffs_cookie_t opc; 162 int opcode; 163 size_t payload_len; 164 const struct puffs_cred *cred; 165 { 166 struct puffs_framebuf *pb; 167 struct fuse_in_header *fih; 168 struct puffs_cc *pcc; 169 uint64_t nodeid; 170 void *data; 171 size_t len; 172 173 if ((pb = puffs_framebuf_make()) == NULL) 174 DERR(EX_OSERR, "puffs_framebuf_make failed"); 175 176 len = payload_len + sizeof(*fih); 177 nodeid = (opc != 0) ? perfuse_get_ino(pu, opc) : PERFUSE_UNKNOWN_INO; 178 179 if (puffs_framebuf_reserve_space(pb, len) != 0) 180 DERR(EX_OSERR, "puffs_framebuf_reserve_space failed"); 181 182 if (puffs_framebuf_getwindow(pb, 0, &data, &len) != 0) 183 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); 184 if (len != payload_len + sizeof(*fih)) 185 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short len"); 186 187 (void)memset(data, 0, len); 188 fih = (struct fuse_in_header *)data; 189 fih->len = (uint32_t)len; 190 fih->opcode = opcode; 191 fih->unique = perfuse_next_unique(pu); 192 fih->nodeid = nodeid; 193 fih->uid = (uid_t)-1; 194 fih->gid = (gid_t)-1; 195 fih->pid = 0; 196 if (cred != NULL) { 197 (void)puffs_cred_getuid(cred, &fih->uid); 198 (void)puffs_cred_getgid(cred, &fih->gid); 199 } 200 if ((pcc = puffs_cc_getcc(pu)) != NULL) 201 (void)puffs_cc_getcaller(pcc, (pid_t *)&fih->pid, NULL); 202 203 return (perfuse_msg_t *)(void *)pb; 204 } 205 206 /* 207 * framebuf send/receive primitives based on pcc are 208 * not available until puffs mainloop is entered. 209 * This xchg_pb_inloop() variant allow early communication. 210 */ 211 static int 212 xchg_pb_early(pu, pb, fd, reply) 213 struct puffs_usermount *pu; 214 struct puffs_framebuf *pb; 215 int fd; 216 enum perfuse_xchg_pb_reply reply; 217 { 218 int done; 219 int error; 220 221 done = 0; 222 while (done == 0) { 223 if ((error = perfuse_writeframe(pu, pb, fd, &done)) != 0) 224 return error; 225 } 226 227 if (reply == no_reply) { 228 puffs_framebuf_destroy(pb); 229 return 0; 230 } else { 231 puffs_framebuf_recycle(pb); 232 } 233 234 done = 0; 235 while (done == 0) { 236 if ((error = perfuse_readframe(pu, pb, fd, &done)) != 0) 237 return error; 238 } 239 240 return 0; 241 } 242 243 static int 244 xchg_pb_inloop(pu, pb, fd, reply) 245 struct puffs_usermount *pu; 246 struct puffs_framebuf *pb; 247 int fd; 248 enum perfuse_xchg_pb_reply reply; 249 { 250 struct puffs_cc *pcc; 251 int error; 252 253 if (reply == no_reply) { 254 error = puffs_framev_enqueue_justsend(pu, fd, pb, 0, 0); 255 } else { 256 pcc = puffs_cc_getcc(pu); 257 error = puffs_framev_enqueue_cc(pcc, fd, pb, 0); 258 } 259 260 return error; 261 } 262 263 int 264 perfuse_xchg_pb(pu, pm, expected_len, reply) 265 struct puffs_usermount *pu; 266 perfuse_msg_t *pm; 267 size_t expected_len; 268 enum perfuse_xchg_pb_reply reply; 269 { 270 struct puffs_framebuf *pb = (struct puffs_framebuf *)(void *)pm; 271 int fd; 272 int error; 273 struct fuse_out_header *foh; 274 #ifdef PERFUSE_DEBUG 275 struct fuse_in_header *fih; 276 uint64_t nodeid; 277 int opcode; 278 uint64_t unique_in; 279 uint64_t unique_out; 280 281 fih = perfuse_get_inhdr(pm); 282 unique_in = fih->unique; 283 nodeid = fih->nodeid; 284 opcode = fih->opcode; 285 286 if (perfuse_diagflags & PDF_FUSE) 287 DPRINTF("> unique = %"PRId64", nodeid = %"PRId64", " 288 "opcode = %s (%d)\n", 289 unique_in, nodeid, perfuse_opname(opcode), opcode); 290 291 if (perfuse_diagflags & PDF_DUMP) 292 perfuse_hexdump((char *)fih, fih->len); 293 294 #endif /* PERFUSE_DEBUG */ 295 296 fd = (int)(long)perfuse_getspecific(pu); 297 298 if (perfuse_inloop(pu)) 299 error = xchg_pb_inloop(pu, pb, fd, reply); 300 else 301 error = xchg_pb_early(pu, pb, fd, reply); 302 303 if (error) 304 DERR(EX_SOFTWARE, "xchg_pb failed"); 305 306 if (reply == no_reply) 307 return 0; 308 309 foh = perfuse_get_outhdr((perfuse_msg_t *)(void *)pb); 310 #ifdef PERFUSE_DEBUG 311 unique_out = foh->unique; 312 313 if (perfuse_diagflags & PDF_FUSE) 314 DPRINTF("< unique = %"PRId64", nodeid = %"PRId64", " 315 "opcode = %s (%d), " 316 "error = %d\n", unique_out, nodeid, 317 perfuse_opname(opcode), opcode, error); 318 319 if (perfuse_diagflags & PDF_DUMP) 320 perfuse_hexdump((char *)foh, foh->len); 321 322 if (unique_in != unique_out) { 323 printf("%s: packet mismatch unique %"PRId64" vs %"PRId64"\n", 324 __func__, unique_in, unique_out); 325 abort(); 326 errx(EX_SOFTWARE, "%s: packet mismatch unique " 327 "%"PRId64" vs %"PRId64"\n", 328 __func__, unique_in, unique_out); 329 } 330 #endif /* PERFUSE_DEBUG */ 331 332 if ((expected_len != PERFUSE_UNSPEC_REPLY_LEN) && 333 (foh->len - sizeof(*foh) < expected_len) && 334 (foh->error == 0)) { 335 DERRX(EX_PROTOCOL, 336 "Unexpected short reply: received %zd bytes, expected %zd", 337 foh->len - sizeof(*foh), expected_len); 338 } 339 340 if ((expected_len != 0) && 341 (foh->len - sizeof(*foh) > expected_len)) 342 DWARNX("Unexpected long reply"); 343 344 /* 345 * Negative Linux errno... 346 */ 347 foh->error = -foh->error; 348 349 return foh->error; 350 } 351 352 353 struct fuse_in_header * 354 perfuse_get_inhdr(pm) 355 perfuse_msg_t *pm; 356 { 357 struct puffs_framebuf *pb; 358 struct fuse_in_header *fih; 359 void *hdr; 360 size_t len; 361 362 pb = (struct puffs_framebuf *)(void *)pm; 363 len = sizeof(*fih); 364 if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0) 365 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); 366 if (len != sizeof(*fih)) 367 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header"); 368 369 fih = (struct fuse_in_header *)hdr; 370 371 return fih; 372 } 373 374 struct fuse_out_header * 375 perfuse_get_outhdr(pm) 376 perfuse_msg_t *pm; 377 { 378 struct puffs_framebuf *pb; 379 struct fuse_out_header *foh; 380 void *hdr; 381 size_t len; 382 383 pb = (struct puffs_framebuf *)(void *)pm; 384 len = sizeof(*foh); 385 if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0) 386 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); 387 if (len != sizeof(*foh)) 388 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header"); 389 390 foh = (struct fuse_out_header *)hdr; 391 392 return foh; 393 } 394 395 char * 396 perfuse_get_inpayload(pm) 397 perfuse_msg_t *pm; 398 { 399 struct puffs_framebuf *pb; 400 struct fuse_in_header *fih; 401 void *hdr; 402 void *payload; 403 size_t len; 404 405 pb = (struct puffs_framebuf *)(void *)pm; 406 len = sizeof(*fih); 407 if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0) 408 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); 409 if (len != sizeof(*fih)) 410 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header"); 411 412 fih = (struct fuse_in_header *)hdr; 413 414 len = fih->len - sizeof(*fih); 415 if (puffs_framebuf_getwindow(pb, sizeof(*fih), &payload, &len) != 0) 416 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); 417 if (len != fih->len - sizeof(*fih)) 418 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header"); 419 420 return (char *)payload; 421 } 422 423 char * 424 perfuse_get_outpayload(pm) 425 perfuse_msg_t *pm; 426 { 427 struct puffs_framebuf *pb; 428 struct fuse_out_header *foh; 429 void *hdr; 430 void *payload; 431 size_t len; 432 433 pb = (struct puffs_framebuf *)(void *)pm; 434 len = sizeof(*foh); 435 if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0) 436 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); 437 if (len != sizeof(*foh)) 438 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header"); 439 440 foh = (struct fuse_out_header *)hdr; 441 442 len = foh->len - sizeof(*foh); 443 if (puffs_framebuf_getwindow(pb, sizeof(*foh), &payload, &len) != 0) 444 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed"); 445 if (len != foh->len - sizeof(*foh)) 446 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header"); 447 448 return (char *)payload; 449 } 450 451 #define PUFFS_FRAMEBUF_GETWINDOW(pb, offset, data, len) \ 452 do { \ 453 int pfg_error; \ 454 size_t pfg_len = *(len); \ 455 \ 456 pfg_error = puffs_framebuf_getwindow(pb, offset, data, len); \ 457 if (pfg_error != 0) \ 458 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");\ 459 \ 460 if (*(len) != pfg_len) \ 461 DERRX(EX_SOFTWARE, "puffs_framebuf_getwindow size"); \ 462 } while (0 /* CONSTCOND */); 463 464 /* ARGSUSED0 */ 465 int 466 perfuse_readframe(pu, pufbuf, fd, done) 467 struct puffs_usermount *pu; 468 struct puffs_framebuf *pufbuf; 469 int fd; 470 int *done; 471 { 472 struct fuse_out_header foh; 473 size_t len; 474 ssize_t readen; 475 void *data; 476 477 /* 478 * Read the header 479 */ 480 len = sizeof(foh); 481 PUFFS_FRAMEBUF_GETWINDOW(pufbuf, 0, &data, &len); 482 483 switch (readen = recv(fd, data, len, MSG_NOSIGNAL|MSG_PEEK)) { 484 case 0: 485 DWARNX("%s: recv retunred 0", __func__); 486 return ECONNRESET; 487 /* NOTREACHED */ 488 break; 489 case -1: 490 if (errno == EAGAIN) 491 return 0; 492 DWARN("%s: recv retunred -1", __func__); 493 return errno; 494 /* NOTREACHED */ 495 break; 496 default: 497 break; 498 } 499 500 #ifdef PERFUSE_DEBUG 501 if (readen != len) 502 DERRX(EX_SOFTWARE, "%s: short recv %zd/%zd", 503 __func__, readen, len); 504 #endif 505 506 /* 507 * We have a header, get remaing length to read 508 */ 509 if (puffs_framebuf_getdata_atoff(pufbuf, 0, &foh, sizeof(foh)) != 0) 510 DERR(EX_SOFTWARE, "puffs_framebuf_getdata_atoff failed"); 511 512 len = foh.len; 513 514 #ifdef PERFUSE_DEBUG 515 if (len > FUSE_BUFSIZE) 516 DERRX(EX_SOFTWARE, "%s: foh.len = %d", __func__, len); 517 #endif 518 519 /* 520 * This is time to reserve space. 521 */ 522 if (puffs_framebuf_reserve_space(pufbuf, len) == -1) 523 DERR(EX_OSERR, "puffs_framebuf_reserve_space failed"); 524 525 /* 526 * And read the remaining data 527 */ 528 PUFFS_FRAMEBUF_GETWINDOW(pufbuf, 0, &data, &len); 529 530 switch (readen = recv(fd, data, len, MSG_NOSIGNAL)) { 531 case 0: 532 DWARNX("%s: recv retunred 0", __func__); 533 return ECONNRESET; 534 /* NOTREACHED */ 535 break; 536 case -1: 537 if (errno == EAGAIN) 538 return 0; 539 DWARN("%s: recv retunred -1", __func__); 540 return errno; 541 /* NOTREACHED */ 542 break; 543 default: 544 break; 545 } 546 547 #ifdef PERFUSE_DEBUG 548 if (readen != len) 549 DERRX(EX_SOFTWARE, "%s: short recv %zd/%zd", 550 __func__, readen, len); 551 #endif 552 553 *done = 1; 554 return 0; 555 } 556 557 /* ARGSUSED0 */ 558 int 559 perfuse_writeframe(pu, pufbuf, fd, done) 560 struct puffs_usermount *pu; 561 struct puffs_framebuf *pufbuf; 562 int fd; 563 int *done; 564 { 565 size_t len; 566 ssize_t written; 567 void *data; 568 569 len = puffs_framebuf_tellsize(pufbuf); 570 PUFFS_FRAMEBUF_GETWINDOW(pufbuf, 0, &data, &len); 571 572 switch (written = send(fd, data, len, MSG_NOSIGNAL)) { 573 case 0: 574 DWARNX("%s: send retunred 0", __func__); 575 return ECONNRESET; 576 /* NOTREACHED */ 577 break; 578 case -1: 579 if (errno == EAGAIN) 580 return 0; 581 DWARN("%s: send retunred -1", __func__); 582 return errno; 583 /* NOTREACHED */ 584 break; 585 default: 586 break; 587 } 588 589 #ifdef PERFUSE_DEBUG 590 if (written != len) 591 DERRX(EX_SOFTWARE, "%s: short send %zd/%zd", 592 __func__, written, len); 593 #endif 594 595 *done = 1; 596 return 0; 597 } 598 599 /* ARGSUSED0 */ 600 int 601 perfuse_cmpframe(pu, pb1, pb2, match) 602 struct puffs_usermount *pu; 603 struct puffs_framebuf *pb1; 604 struct puffs_framebuf *pb2; 605 int *match; 606 { 607 struct fuse_in_header *fih; 608 struct fuse_out_header *foh; 609 uint64_t unique_in; 610 uint64_t unique_out; 611 size_t len; 612 613 len = sizeof(*fih); 614 PUFFS_FRAMEBUF_GETWINDOW(pb1, 0, (void **)&fih, &len); 615 unique_in = fih->unique; 616 617 len = sizeof(*foh); 618 PUFFS_FRAMEBUF_GETWINDOW(pb2, 0, (void **)&foh, &len); 619 unique_out = foh->unique; 620 621 return unique_in != unique_out; 622 } 623 624 /* ARGSUSED0 */ 625 void 626 perfuse_gotframe(pu, pb) 627 struct puffs_usermount *pu; 628 struct puffs_framebuf *pb; 629 { 630 struct fuse_out_header *foh; 631 size_t len; 632 633 len = sizeof(*foh); 634 PUFFS_FRAMEBUF_GETWINDOW(pb, 0, (void **)&foh, &len); 635 636 DWARNX("Unexpected frame: unique = %"PRId64", error = %d", 637 foh->unique, foh->error); 638 #ifdef PERFUSE_DEBUG 639 perfuse_hexdump((char *)(void *)foh, foh->len); 640 #endif 641 642 return; 643 } 644 645 void 646 perfuse_fdnotify(pu, fd, what) 647 struct puffs_usermount *pu; 648 int fd; 649 int what; 650 { 651 if (fd != (int)(long)perfuse_getspecific(pu)) 652 DERRX(EX_SOFTWARE, "%s: unexpected notification for fd = %d", 653 __func__, fd); 654 655 if ((what != PUFFS_FBIO_READ) && (what != PUFFS_FBIO_WRITE)) 656 DERRX(EX_SOFTWARE, "%s: unexpected notification what = 0x%x", 657 __func__, what); 658 659 if (perfuse_unmount(pu) != 0) 660 DWARN("unmount() failed"); 661 662 if (shutdown(fd, SHUT_RDWR) != 0) 663 DWARN("shutdown() failed"); 664 665 if (perfuse_diagflags & PDF_MISC) 666 DPRINTF("Exit"); 667 668 exit(0); 669 670 /* NOTREACHED */ 671 return; 672 } 673