1 /* $NetBSD: puffs.c,v 1.74 2007/11/06 15:09:08 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2005, 2006, 2007 Antti Kantee. All Rights Reserved. 5 * 6 * Development of this software was supported by the 7 * Google Summer of Code program and the Ulla Tuominen Foundation. 8 * The Google SoC project was mentored by Bill Studenmund. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #if !defined(lint) 34 __RCSID("$NetBSD: puffs.c,v 1.74 2007/11/06 15:09:08 pooka Exp $"); 35 #endif /* !lint */ 36 37 #include <sys/param.h> 38 #include <sys/mount.h> 39 40 #include <assert.h> 41 #include <err.h> 42 #include <errno.h> 43 #include <fcntl.h> 44 #include <mntopts.h> 45 #include <paths.h> 46 #include <puffs.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <syslog.h> 51 #include <unistd.h> 52 53 #include "puffs_priv.h" 54 55 /* Most file systems want this for opts, so just give it to them */ 56 const struct mntopt puffsmopts[] = { 57 MOPT_STDOPTS, 58 PUFFSMOPT_STD, 59 MOPT_NULL, 60 }; 61 62 #ifdef PUFFS_WITH_THREADS 63 #include <pthread.h> 64 pthread_mutex_t pu_lock = PTHREAD_MUTEX_INITIALIZER; 65 #endif 66 67 #define FILLOP(lower, upper) \ 68 do { \ 69 if (pops->puffs_node_##lower) \ 70 opmask[PUFFS_VN_##upper] = 1; \ 71 } while (/*CONSTCOND*/0) 72 static void 73 fillvnopmask(struct puffs_ops *pops, uint8_t *opmask) 74 { 75 76 memset(opmask, 0, PUFFS_VN_MAX); 77 78 FILLOP(create, CREATE); 79 FILLOP(mknod, MKNOD); 80 FILLOP(open, OPEN); 81 FILLOP(close, CLOSE); 82 FILLOP(access, ACCESS); 83 FILLOP(getattr, GETATTR); 84 FILLOP(setattr, SETATTR); 85 FILLOP(poll, POLL); /* XXX: not ready in kernel */ 86 FILLOP(mmap, MMAP); 87 FILLOP(fsync, FSYNC); 88 FILLOP(seek, SEEK); 89 FILLOP(remove, REMOVE); 90 FILLOP(link, LINK); 91 FILLOP(rename, RENAME); 92 FILLOP(mkdir, MKDIR); 93 FILLOP(rmdir, RMDIR); 94 FILLOP(symlink, SYMLINK); 95 FILLOP(readdir, READDIR); 96 FILLOP(readlink, READLINK); 97 FILLOP(reclaim, RECLAIM); 98 FILLOP(inactive, INACTIVE); 99 FILLOP(print, PRINT); 100 FILLOP(read, READ); 101 FILLOP(write, WRITE); 102 } 103 #undef FILLOP 104 105 /*ARGSUSED*/ 106 static void 107 puffs_defaulterror(struct puffs_usermount *pu, uint8_t type, 108 int error, const char *str, void *cookie) 109 { 110 111 fprintf(stderr, "%s\n", str); 112 abort(); 113 } 114 115 int 116 puffs_getselectable(struct puffs_usermount *pu) 117 { 118 119 return pu->pu_fd; 120 } 121 122 int 123 puffs_setblockingmode(struct puffs_usermount *pu, int mode) 124 { 125 int rv, x; 126 127 assert(puffs_getstate(pu) == PUFFS_STATE_RUNNING); 128 129 if (mode != PUFFSDEV_BLOCK && mode != PUFFSDEV_NONBLOCK) { 130 errno = EINVAL; 131 return -1; 132 } 133 134 x = mode; 135 rv = ioctl(pu->pu_fd, FIONBIO, &x); 136 137 if (rv == 0) { 138 if (mode == PUFFSDEV_BLOCK) 139 pu->pu_state &= ~PU_ASYNCFD; 140 else 141 pu->pu_state |= PU_ASYNCFD; 142 } 143 144 return rv; 145 } 146 147 int 148 puffs_getstate(struct puffs_usermount *pu) 149 { 150 151 return pu->pu_state & PU_STATEMASK; 152 } 153 154 void 155 puffs_setstacksize(struct puffs_usermount *pu, size_t ss) 156 { 157 int stackshift; 158 159 assert(puffs_getstate(pu) == PUFFS_STATE_BEFOREMOUNT); 160 stackshift = -1; 161 while (ss) { 162 ss >>= 1; 163 stackshift++; 164 } 165 pu->pu_cc_stackshift = stackshift; 166 assert(1<<stackshift == ss); 167 } 168 169 struct puffs_pathobj * 170 puffs_getrootpathobj(struct puffs_usermount *pu) 171 { 172 struct puffs_node *pnr; 173 174 pnr = pu->pu_pn_root; 175 if (pnr == NULL) { 176 errno = ENOENT; 177 return NULL; 178 } 179 180 return &pnr->pn_po; 181 } 182 183 void 184 puffs_setroot(struct puffs_usermount *pu, struct puffs_node *pn) 185 { 186 187 pu->pu_pn_root = pn; 188 } 189 190 struct puffs_node * 191 puffs_getroot(struct puffs_usermount *pu) 192 { 193 194 return pu->pu_pn_root; 195 } 196 197 void 198 puffs_setrootinfo(struct puffs_usermount *pu, enum vtype vt, 199 vsize_t vsize, dev_t rdev) 200 { 201 struct puffs_kargs *pargs = pu->pu_kargp; 202 203 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) { 204 warnx("puffs_setrootinfo: call has effect only " 205 "before mount\n"); 206 return; 207 } 208 209 pargs->pa_root_vtype = vt; 210 pargs->pa_root_vsize = vsize; 211 pargs->pa_root_rdev = rdev; 212 } 213 214 void * 215 puffs_getspecific(struct puffs_usermount *pu) 216 { 217 218 return pu->pu_privdata; 219 } 220 221 size_t 222 puffs_getmaxreqlen(struct puffs_usermount *pu) 223 { 224 225 return pu->pu_maxreqlen; 226 } 227 228 void 229 puffs_setmaxreqlen(struct puffs_usermount *pu, size_t reqlen) 230 { 231 232 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) 233 warnx("puffs_setmaxreqlen: call has effect only " 234 "before mount\n"); 235 236 pu->pu_kargp->pa_maxmsglen = reqlen; 237 } 238 239 void 240 puffs_setfhsize(struct puffs_usermount *pu, size_t fhsize, int flags) 241 { 242 243 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) 244 warnx("puffs_setfhsize: call has effect only before mount\n"); 245 246 pu->pu_kargp->pa_fhsize = fhsize; 247 pu->pu_kargp->pa_fhflags = flags; 248 } 249 250 void 251 puffs_setncookiehash(struct puffs_usermount *pu, int nhash) 252 { 253 254 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) 255 warnx("puffs_setfhsize: call has effect only before mount\n"); 256 257 pu->pu_kargp->pa_nhashbuckets = nhash; 258 } 259 260 void 261 puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn) 262 { 263 264 pu->pu_pathbuild = fn; 265 } 266 267 void 268 puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn) 269 { 270 271 pu->pu_pathtransform = fn; 272 } 273 274 void 275 puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn) 276 { 277 278 pu->pu_pathcmp = fn; 279 } 280 281 void 282 puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn) 283 { 284 285 pu->pu_pathfree = fn; 286 } 287 288 void 289 puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn) 290 { 291 292 pu->pu_namemod = fn; 293 } 294 295 void 296 puffs_set_errnotify(struct puffs_usermount *pu, pu_errnotify_fn fn) 297 { 298 299 pu->pu_errnotify = fn; 300 } 301 302 void 303 puffs_ml_setloopfn(struct puffs_usermount *pu, puffs_ml_loop_fn lfn) 304 { 305 306 pu->pu_ml_lfn = lfn; 307 } 308 309 void 310 puffs_ml_settimeout(struct puffs_usermount *pu, struct timespec *ts) 311 { 312 313 if (ts == NULL) { 314 pu->pu_ml_timep = NULL; 315 } else { 316 pu->pu_ml_timeout = *ts; 317 pu->pu_ml_timep = &pu->pu_ml_timeout; 318 } 319 } 320 321 void 322 puffs_set_prepost(struct puffs_usermount *pu, 323 pu_prepost_fn pre, pu_prepost_fn pst) 324 { 325 326 pu->pu_oppre = pre; 327 pu->pu_oppost = pst; 328 } 329 330 void 331 puffs_setback(struct puffs_cc *pcc, int whatback) 332 { 333 struct puffs_req *preq = pcc->pcc_preq; 334 335 assert(PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN && ( 336 preq->preq_optype == PUFFS_VN_OPEN || 337 preq->preq_optype == PUFFS_VN_MMAP || 338 preq->preq_optype == PUFFS_VN_REMOVE || 339 preq->preq_optype == PUFFS_VN_RMDIR || 340 preq->preq_optype == PUFFS_VN_INACTIVE)); 341 342 preq->preq_setbacks |= whatback & PUFFS_SETBACK_MASK; 343 } 344 345 int 346 puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags, 347 void *cookie) 348 { 349 char rp[MAXPATHLEN]; 350 int rv, fd; 351 352 #if 1 353 /* XXXkludgehere */ 354 /* kauth doesn't provide this service any longer */ 355 if (geteuid() != 0) 356 mntflags |= MNT_NOSUID | MNT_NODEV; 357 #endif 358 359 if (realpath(dir, rp) == NULL) 360 return -1; 361 362 if (strcmp(dir, rp) != 0) { 363 warnx("puffs_mount: \"%s\" is a relative path.", dir); 364 warnx("puffs_mount: using \"%s\" instead.", rp); 365 } 366 367 fd = open(_PATH_PUFFS, O_RDONLY); 368 if (fd == -1) { 369 warnx("puffs_mount: cannot open %s", _PATH_PUFFS); 370 return -1; 371 } 372 if (fd <= 2) 373 warnx("puffs_init: device fd %d (<= 2), sure this is " 374 "what you want?", fd); 375 376 pu->pu_kargp->pa_fd = pu->pu_fd = fd; 377 pu->pu_kargp->pa_root_cookie = cookie; 378 if ((rv = mount(MOUNT_PUFFS, rp, mntflags, 379 pu->pu_kargp, sizeof(struct puffs_kargs))) == -1) 380 goto out; 381 #if 0 382 if ((rv = ioctl(pu->pu_fd, PUFFSREQSIZEOP, &pu->pu_maxreqlen)) == -1) 383 goto out; 384 #endif 385 PU_SETSTATE(pu, PUFFS_STATE_RUNNING); 386 387 out: 388 free(pu->pu_kargp); 389 pu->pu_kargp = NULL; 390 391 return rv; 392 } 393 394 struct puffs_usermount * 395 _puffs_init(int develv, struct puffs_ops *pops, const char *mntfromname, 396 const char *puffsname, void *priv, uint32_t pflags) 397 { 398 struct puffs_usermount *pu; 399 struct puffs_kargs *pargs; 400 int sverrno; 401 402 if (develv != PUFFS_DEVEL_LIBVERSION) { 403 warnx("puffs_init: mounting with lib version %d, need %d", 404 develv, PUFFS_DEVEL_LIBVERSION); 405 errno = EINVAL; 406 return NULL; 407 } 408 409 pu = malloc(sizeof(struct puffs_usermount)); 410 if (pu == NULL) 411 goto failfree; 412 memset(pu, 0, sizeof(struct puffs_usermount)); 413 414 pargs = pu->pu_kargp = malloc(sizeof(struct puffs_kargs)); 415 if (pargs == NULL) 416 goto failfree; 417 memset(pargs, 0, sizeof(struct puffs_kargs)); 418 419 pargs->pa_vers = PUFFSDEVELVERS | PUFFSVERSION; 420 pargs->pa_flags = PUFFS_FLAG_KERN(pflags); 421 fillvnopmask(pops, pargs->pa_vnopmask); 422 (void)strlcpy(pargs->pa_typename, puffsname, 423 sizeof(pargs->pa_typename)); 424 (void)strlcpy(pargs->pa_mntfromname, mntfromname, 425 sizeof(pargs->pa_mntfromname)); 426 427 puffs_zerostatvfs(&pargs->pa_svfsb); 428 pargs->pa_root_cookie = NULL; 429 pargs->pa_root_vtype = VDIR; 430 pargs->pa_root_vsize = 0; 431 pargs->pa_root_rdev = 0; 432 pargs->pa_maxmsglen = 0; 433 434 pu->pu_flags = pflags; 435 pu->pu_ops = *pops; 436 free(pops); /* XXX */ 437 438 pu->pu_privdata = priv; 439 pu->pu_cc_stackshift = PUFFS_CC_STACKSHIFT_DEFAULT; 440 LIST_INIT(&pu->pu_pnodelst); 441 LIST_INIT(&pu->pu_framectrl.fb_ios); 442 LIST_INIT(&pu->pu_ccnukelst); 443 TAILQ_INIT(&pu->pu_sched); 444 TAILQ_INIT(&pu->pu_exq); 445 446 /* defaults for some user-settable translation functions */ 447 pu->pu_cmap = NULL; /* identity translation */ 448 449 pu->pu_pathbuild = puffs_stdpath_buildpath; 450 pu->pu_pathfree = puffs_stdpath_freepath; 451 pu->pu_pathcmp = puffs_stdpath_cmppath; 452 pu->pu_pathtransform = NULL; 453 pu->pu_namemod = NULL; 454 455 pu->pu_errnotify = puffs_defaulterror; 456 457 PU_SETSTATE(pu, PUFFS_STATE_BEFOREMOUNT); 458 459 return pu; 460 461 failfree: 462 /* can't unmount() from here for obvious reasons */ 463 sverrno = errno; 464 free(pu); 465 errno = sverrno; 466 return NULL; 467 } 468 469 /* 470 * XXX: there's currently no clean way to request unmount from 471 * within the user server, so be very brutal about it. 472 */ 473 /*ARGSUSED1*/ 474 int 475 puffs_exit(struct puffs_usermount *pu, int force) 476 { 477 struct puffs_node *pn; 478 479 force = 1; /* currently */ 480 481 if (pu->pu_fd) 482 close(pu->pu_fd); 483 484 while ((pn = LIST_FIRST(&pu->pu_pnodelst)) != NULL) 485 puffs_pn_put(pn); 486 487 puffs_framev_exit(pu); 488 if (pu->pu_state & PU_HASKQ) 489 close(pu->pu_kq); 490 free(pu); 491 492 return 0; /* always succesful for now, WILL CHANGE */ 493 } 494 495 int 496 puffs_mainloop(struct puffs_usermount *pu) 497 { 498 struct puffs_getreq *pgr = NULL; 499 struct puffs_putreq *ppr = NULL; 500 struct puffs_framectrl *pfctrl = &pu->pu_framectrl; 501 struct puffs_fctrl_io *fio; 502 struct puffs_cc *pcc; 503 struct kevent *curev, *newevs; 504 size_t nchanges; 505 int puffsfd, sverrno; 506 int ndone; 507 508 assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING); 509 510 pgr = puffs_req_makeget(pu, puffs_getmaxreqlen(pu), 0); 511 if (pgr == NULL) 512 goto out; 513 514 ppr = puffs_req_makeput(pu); 515 if (ppr == NULL) 516 goto out; 517 518 newevs = realloc(pfctrl->evs, (2*pfctrl->nfds+1)*sizeof(struct kevent)); 519 if (newevs == NULL) 520 goto out; 521 pfctrl->evs = newevs; 522 523 pu->pu_state |= PU_INLOOP; 524 525 pu->pu_kq = kqueue(); 526 if (pu->pu_kq == -1) 527 goto out; 528 pu->pu_state |= PU_HASKQ; 529 530 curev = pfctrl->evs; 531 LIST_FOREACH(fio, &pfctrl->fb_ios, fio_entries) { 532 EV_SET(curev, fio->io_fd, EVFILT_READ, EV_ADD, 533 0, 0, (uintptr_t)fio); 534 curev++; 535 EV_SET(curev, fio->io_fd, EVFILT_WRITE, EV_ADD | EV_DISABLE, 536 0, 0, (uintptr_t)fio); 537 curev++; 538 } 539 puffsfd = puffs_getselectable(pu); 540 EV_SET(curev, puffsfd, EVFILT_READ, EV_ADD, 0, 0, 0); 541 if (kevent(pu->pu_kq, pfctrl->evs, 2*pfctrl->nfds+1, 542 NULL, 0, NULL) == -1) 543 goto out; 544 545 while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) { 546 if (pu->pu_ml_lfn) 547 pu->pu_ml_lfn(pu); 548 549 /* 550 * Do this here, because: 551 * a) loopfunc might generate some results 552 * b) it's still "after" event handling (except for round 1) 553 */ 554 if (puffs_req_putput(ppr) == -1) 555 goto out; 556 puffs_req_resetput(ppr); 557 558 /* micro optimization: skip kevent syscall if possible */ 559 if (pfctrl->nfds == 0 && pu->pu_ml_timep == NULL 560 && (pu->pu_state & PU_ASYNCFD) == 0) { 561 if (puffs_req_handle(pgr, ppr, 0) == -1) 562 goto out; 563 continue; 564 } 565 566 /* else: do full processing */ 567 568 /* 569 * Build list of which to enable/disable in writecheck. 570 * Don't bother worrying about O(n) for now. 571 */ 572 nchanges = 0; 573 LIST_FOREACH(fio, &pfctrl->fb_ios, fio_entries) { 574 if (fio->stat & FIO_WRGONE) 575 continue; 576 577 /* 578 * Try to write out everything to avoid the 579 * need for enabling EVFILT_WRITE. The likely 580 * case is that we can fit everything into the 581 * socket buffer. 582 */ 583 if (puffs_framev_output(pu, pfctrl, fio, ppr)) { 584 /* need kernel notify? (error condition) */ 585 if (puffs_req_putput(ppr) == -1) 586 goto out; 587 puffs_req_resetput(ppr); 588 } 589 590 /* en/disable write checks for kqueue as needed */ 591 assert((FIO_EN_WRITE(fio) && FIO_RM_WRITE(fio)) == 0); 592 if (FIO_EN_WRITE(fio)) { 593 EV_SET(&pfctrl->evs[nchanges], fio->io_fd, 594 EVFILT_WRITE, EV_ENABLE, 0, 0, 595 (uintptr_t)fio); 596 fio->stat |= FIO_WR; 597 nchanges++; 598 } 599 if (FIO_RM_WRITE(fio)) { 600 EV_SET(&pfctrl->evs[nchanges], fio->io_fd, 601 EVFILT_WRITE, EV_DISABLE, 0, 0, 602 (uintptr_t)fio); 603 fio->stat &= ~FIO_WR; 604 nchanges++; 605 } 606 assert(nchanges <= pfctrl->nfds); 607 } 608 609 ndone = kevent(pu->pu_kq, pfctrl->evs, nchanges, 610 pfctrl->evs, pfctrl->nfds+1, pu->pu_ml_timep); 611 612 if (ndone == -1) { 613 if (errno != EINTR) 614 goto out; 615 else 616 continue; 617 } 618 619 /* uoptimize */ 620 if (ndone == 0) 621 continue; 622 623 /* iterate over the results */ 624 for (curev = pfctrl->evs; ndone--; curev++) { 625 int what; 626 627 /* get & possibly dispatch events from kernel */ 628 if (curev->ident == puffsfd) { 629 if (puffs_req_handle(pgr, ppr, 0) == -1) 630 goto out; 631 continue; 632 } 633 634 fio = (void *)curev->udata; 635 if (curev->flags & EV_ERROR) { 636 assert(curev->filter == EVFILT_WRITE); 637 fio->stat &= ~FIO_WR; 638 639 /* XXX: how to know if it's a transient error */ 640 puffs_framev_writeclose(pu, fio, 641 (int)curev->data); 642 puffs_framev_notify(fio, PUFFS_FBIO_ERROR); 643 continue; 644 } 645 646 what = 0; 647 if (curev->filter == EVFILT_READ) { 648 puffs_framev_input(pu, pfctrl, fio, ppr); 649 what |= PUFFS_FBIO_READ; 650 } 651 652 else if (curev->filter == EVFILT_WRITE) { 653 puffs_framev_output(pu, pfctrl, fio, ppr); 654 what |= PUFFS_FBIO_WRITE; 655 } 656 if (what) 657 puffs_framev_notify(fio, what); 658 } 659 660 /* 661 * Schedule continuations. 662 */ 663 while ((pcc = TAILQ_FIRST(&pu->pu_sched)) != NULL) { 664 TAILQ_REMOVE(&pu->pu_sched, pcc, entries); 665 puffs_goto(pcc); 666 } 667 668 /* 669 * Really free fd's now that we don't have references 670 * to them. 671 */ 672 while ((fio = LIST_FIRST(&pfctrl->fb_ios_rmlist)) != NULL) { 673 LIST_REMOVE(fio, fio_entries); 674 free(fio); 675 } 676 } 677 errno = 0; 678 puffs_req_putput(ppr); 679 680 out: 681 /* store the real error for a while */ 682 sverrno = errno; 683 684 if (ppr) 685 puffs_req_destroyput(ppr); 686 if (pgr) 687 puffs_req_destroyget(pgr); 688 689 errno = sverrno; 690 if (errno) 691 return -1; 692 else 693 return 0; 694 } 695