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