puffs.c revision 1.62
1/* $NetBSD: puffs.c,v 1.62 2007/08/11 18:04:50 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.62 2007/08/11 18:04:50 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 */ 56const struct mntopt puffsmopts[] = { 57 MOPT_STDOPTS, 58 PUFFSMOPT_STD, 59 MOPT_NULL, 60}; 61 62#define FILLOP(lower, upper) \ 63do { \ 64 if (pops->puffs_node_##lower) \ 65 opmask[PUFFS_VN_##upper] = 1; \ 66} while (/*CONSTCOND*/0) 67static void 68fillvnopmask(struct puffs_ops *pops, uint8_t *opmask) 69{ 70 71 memset(opmask, 0, PUFFS_VN_MAX); 72 73 FILLOP(create, CREATE); 74 FILLOP(mknod, MKNOD); 75 FILLOP(open, OPEN); 76 FILLOP(close, CLOSE); 77 FILLOP(access, ACCESS); 78 FILLOP(getattr, GETATTR); 79 FILLOP(setattr, SETATTR); 80 FILLOP(poll, POLL); /* XXX: not ready in kernel */ 81 FILLOP(mmap, MMAP); 82 FILLOP(fsync, FSYNC); 83 FILLOP(seek, SEEK); 84 FILLOP(remove, REMOVE); 85 FILLOP(link, LINK); 86 FILLOP(rename, RENAME); 87 FILLOP(mkdir, MKDIR); 88 FILLOP(rmdir, RMDIR); 89 FILLOP(symlink, SYMLINK); 90 FILLOP(readdir, READDIR); 91 FILLOP(readlink, READLINK); 92 FILLOP(reclaim, RECLAIM); 93 FILLOP(inactive, INACTIVE); 94 FILLOP(print, PRINT); 95 FILLOP(read, READ); 96 FILLOP(write, WRITE); 97 98 /* XXX: not implemented in the kernel */ 99 FILLOP(getextattr, GETEXTATTR); 100 FILLOP(setextattr, SETEXTATTR); 101 FILLOP(listextattr, LISTEXTATTR); 102} 103#undef FILLOP 104 105int 106puffs_getselectable(struct puffs_usermount *pu) 107{ 108 109 return pu->pu_fd; 110} 111 112int 113puffs_setblockingmode(struct puffs_usermount *pu, int mode) 114{ 115 int rv, x; 116 117 if (mode != PUFFSDEV_BLOCK && mode != PUFFSDEV_NONBLOCK) { 118 errno = EINVAL; 119 return -1; 120 } 121 122 x = mode; 123 rv = ioctl(pu->pu_fd, FIONBIO, &x); 124 125 if (rv == 0) { 126 if (mode == PUFFSDEV_BLOCK) 127 pu->pu_state &= ~PU_ASYNCFD; 128 else 129 pu->pu_state |= PU_ASYNCFD; 130 } 131 132 return rv; 133} 134 135int 136puffs_getstate(struct puffs_usermount *pu) 137{ 138 139 return pu->pu_state & PU_STATEMASK; 140} 141 142void 143puffs_setstacksize(struct puffs_usermount *pu, size_t ss) 144{ 145 146 pu->pu_cc_stacksize = ss; 147} 148 149struct puffs_pathobj * 150puffs_getrootpathobj(struct puffs_usermount *pu) 151{ 152 struct puffs_node *pnr; 153 154 pnr = pu->pu_pn_root; 155 if (pnr == NULL) { 156 errno = ENOENT; 157 return NULL; 158 } 159 160 return &pnr->pn_po; 161} 162 163void 164puffs_setroot(struct puffs_usermount *pu, struct puffs_node *pn) 165{ 166 167 pu->pu_pn_root = pn; 168} 169 170struct puffs_node * 171puffs_getroot(struct puffs_usermount *pu) 172{ 173 174 return pu->pu_pn_root; 175} 176 177void 178puffs_setrootinfo(struct puffs_usermount *pu, enum vtype vt, 179 vsize_t vsize, dev_t rdev) 180{ 181 struct puffs_kargs *pargs = pu->pu_kargp; 182 183 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) { 184 warnx("puffs_setrootinfo: call has effect only " 185 "before mount\n"); 186 return; 187 } 188 189 pargs->pa_root_vtype = vt; 190 pargs->pa_root_vsize = vsize; 191 pargs->pa_root_rdev = rdev; 192} 193 194void * 195puffs_getspecific(struct puffs_usermount *pu) 196{ 197 198 return pu->pu_privdata; 199} 200 201size_t 202puffs_getmaxreqlen(struct puffs_usermount *pu) 203{ 204 205 return pu->pu_maxreqlen; 206} 207 208void 209puffs_setmaxreqlen(struct puffs_usermount *pu, size_t reqlen) 210{ 211 212 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) 213 warnx("puffs_setmaxreqlen: call has effect only " 214 "before mount\n"); 215 216 pu->pu_kargp->pa_maxreqlen = reqlen; 217} 218 219void 220puffs_setfhsize(struct puffs_usermount *pu, size_t fhsize, int flags) 221{ 222 223 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) 224 warnx("puffs_setfhsize: call has effect only before mount\n"); 225 226 pu->pu_kargp->pa_fhsize = fhsize; 227 pu->pu_kargp->pa_fhflags = flags; 228} 229 230void 231puffs_setncookiehash(struct puffs_usermount *pu, int nhash) 232{ 233 234 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) 235 warnx("puffs_setfhsize: call has effect only before mount\n"); 236 237 pu->pu_kargp->pa_nhashbuckets = nhash; 238} 239 240void 241puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn) 242{ 243 244 pu->pu_pathbuild = fn; 245} 246 247void 248puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn) 249{ 250 251 pu->pu_pathtransform = fn; 252} 253 254void 255puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn) 256{ 257 258 pu->pu_pathcmp = fn; 259} 260 261void 262puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn) 263{ 264 265 pu->pu_pathfree = fn; 266} 267 268void 269puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn) 270{ 271 272 pu->pu_namemod = fn; 273} 274 275void 276puffs_ml_setloopfn(struct puffs_usermount *pu, puffs_ml_loop_fn lfn) 277{ 278 279 pu->pu_ml_lfn = lfn; 280} 281 282void 283puffs_ml_settimeout(struct puffs_usermount *pu, struct timespec *ts) 284{ 285 286 if (ts == NULL) { 287 pu->pu_ml_timep = NULL; 288 } else { 289 pu->pu_ml_timeout = *ts; 290 pu->pu_ml_timep = &pu->pu_ml_timeout; 291 } 292} 293 294void 295puffs_setback(struct puffs_cc *pcc, int whatback) 296{ 297 struct puffs_req *preq = pcc->pcc_preq; 298 299 assert(PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN && ( 300 preq->preq_optype == PUFFS_VN_OPEN || 301 preq->preq_optype == PUFFS_VN_MMAP || 302 preq->preq_optype == PUFFS_VN_REMOVE || 303 preq->preq_optype == PUFFS_VN_RMDIR || 304 preq->preq_optype == PUFFS_VN_INACTIVE)); 305 306 preq->preq_setbacks |= whatback & PUFFS_SETBACK_MASK; 307} 308 309int 310puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags, 311 void *cookie) 312{ 313 char rp[MAXPATHLEN]; 314 int rv; 315 316#if 1 317 /* XXXkludgehere */ 318 /* kauth doesn't provide this service any longer */ 319 if (geteuid() != 0) 320 mntflags |= MNT_NOSUID | MNT_NODEV; 321#endif 322 323 if (realpath(dir, rp) == NULL) 324 return -1; 325 326 if (strcmp(dir, rp) != 0) { 327 warnx("puffs_mount: \"%s\" is a relative path.", dir); 328 warnx("puffs_mount: using \"%s\" instead.", rp); 329 } 330 331 pu->pu_kargp->pa_root_cookie = cookie; 332 if ((rv = mount(MOUNT_PUFFS, rp, mntflags, 333 pu->pu_kargp, sizeof(struct puffs_kargs))) == -1) 334 goto out; 335 if ((rv = ioctl(pu->pu_fd, PUFFSREQSIZEOP, &pu->pu_maxreqlen)) == -1) 336 goto out; 337 PU_SETSTATE(pu, PUFFS_STATE_RUNNING); 338 339 out: 340 free(pu->pu_kargp); 341 pu->pu_kargp = NULL; 342 343 return rv; 344} 345 346struct puffs_usermount * 347_puffs_init(int develv, struct puffs_ops *pops, const char *mntfromname, 348 const char *puffsname, void *priv, uint32_t pflags) 349{ 350 struct puffs_usermount *pu; 351 struct puffs_kargs *pargs; 352 int sverrno, fd; 353 354 if (develv != PUFFS_DEVEL_LIBVERSION) { 355 warnx("puffs_init: mounting with lib version %d, need %d", 356 develv, PUFFS_DEVEL_LIBVERSION); 357 errno = EINVAL; 358 return NULL; 359 } 360 361 fd = open(_PATH_PUFFS, O_RDONLY); 362 if (fd == -1) { 363 warnx("puffs_init: cannot open %s", _PATH_PUFFS); 364 return NULL; 365 } 366 if (fd <= 2) 367 warnx("puffs_init: device fd %d (<= 2), sure this is " 368 "what you want?", fd); 369 370 pu = malloc(sizeof(struct puffs_usermount)); 371 if (pu == NULL) 372 goto failfree; 373 memset(pu, 0, sizeof(struct puffs_usermount)); 374 375 pargs = pu->pu_kargp = malloc(sizeof(struct puffs_kargs)); 376 if (pargs == NULL) 377 goto failfree; 378 memset(pargs, 0, sizeof(struct puffs_kargs)); 379 380 pargs->pa_vers = PUFFSDEVELVERS | PUFFSVERSION; 381 pargs->pa_flags = PUFFS_FLAG_KERN(pflags); 382 pargs->pa_fd = pu->pu_fd = fd; 383 fillvnopmask(pops, pargs->pa_vnopmask); 384 (void)strlcpy(pargs->pa_typename, puffsname, 385 sizeof(pargs->pa_typename)); 386 (void)strlcpy(pargs->pa_mntfromname, mntfromname, 387 sizeof(pargs->pa_mntfromname)); 388 389 puffs_zerostatvfs(&pargs->pa_svfsb); 390 pargs->pa_root_cookie = NULL; 391 pargs->pa_root_vtype = VDIR; 392 pargs->pa_root_vsize = 0; 393 pargs->pa_root_rdev = 0; 394 pargs->pa_maxreqlen = 0; 395 396 pu->pu_flags = pflags; 397 pu->pu_ops = *pops; 398 free(pops); /* XXX */ 399 400 pu->pu_privdata = priv; 401 pu->pu_cc_stacksize = PUFFS_CC_STACKSIZE_DEFAULT; 402 LIST_INIT(&pu->pu_pnodelst); 403 LIST_INIT(&pu->pu_framectrl.fb_ios); 404 LIST_INIT(&pu->pu_ccnukelst); 405 406 /* defaults for some user-settable translation functions */ 407 pu->pu_cmap = NULL; /* identity translation */ 408 409 pu->pu_pathbuild = puffs_stdpath_buildpath; 410 pu->pu_pathfree = puffs_stdpath_freepath; 411 pu->pu_pathcmp = puffs_stdpath_cmppath; 412 pu->pu_pathtransform = NULL; 413 pu->pu_namemod = NULL; 414 415 PU_SETSTATE(pu, PUFFS_STATE_BEFOREMOUNT); 416 417 return pu; 418 419 failfree: 420 /* can't unmount() from here for obvious reasons */ 421 sverrno = errno; 422 close(fd); 423 free(pu); 424 errno = sverrno; 425 return NULL; 426} 427 428/* 429 * XXX: there's currently no clean way to request unmount from 430 * within the user server, so be very brutal about it. 431 */ 432/*ARGSUSED1*/ 433int 434puffs_exit(struct puffs_usermount *pu, int force) 435{ 436 struct puffs_node *pn; 437 438 force = 1; /* currently */ 439 440 if (pu->pu_fd) 441 close(pu->pu_fd); 442 443 while ((pn = LIST_FIRST(&pu->pu_pnodelst)) != NULL) 444 puffs_pn_put(pn); 445 446 puffs_framev_exit(pu); 447 if (pu->pu_haskq) 448 close(pu->pu_kq); 449 free(pu); 450 451 return 0; /* always succesful for now, WILL CHANGE */ 452} 453 454int 455puffs_mainloop(struct puffs_usermount *pu, int flags) 456{ 457 struct puffs_getreq *pgr = NULL; 458 struct puffs_putreq *ppr = NULL; 459 struct puffs_framectrl *pfctrl = &pu->pu_framectrl; 460 struct puffs_fctrl_io *fio; 461 struct kevent *curev, *newevs; 462 size_t nchanges; 463 int puffsfd, sverrno; 464 int ndone; 465 466 assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING); 467 468 pgr = puffs_req_makeget(pu, puffs_getmaxreqlen(pu), 0); 469 if (pgr == NULL) 470 goto out; 471 472 ppr = puffs_req_makeput(pu); 473 if (ppr == NULL) 474 goto out; 475 476 newevs = realloc(pfctrl->evs, (2*pfctrl->nfds+1)*sizeof(struct kevent)); 477 if (newevs == NULL) 478 goto out; 479 pfctrl->evs = newevs; 480 481 if ((flags & PUFFSLOOP_NODAEMON) == 0) 482 if (daemon(1, 0) == -1) 483 goto out; 484 pu->pu_state |= PU_INLOOP; 485 486 pu->pu_kq = kqueue(); 487 if (pu->pu_kq == -1) 488 goto out; 489 pu->pu_haskq = 1; 490 491 curev = pfctrl->evs; 492 LIST_FOREACH(fio, &pfctrl->fb_ios, fio_entries) { 493 EV_SET(curev, fio->io_fd, EVFILT_READ, EV_ADD, 494 0, 0, (uintptr_t)fio); 495 curev++; 496 EV_SET(curev, fio->io_fd, EVFILT_WRITE, EV_ADD | EV_DISABLE, 497 0, 0, (uintptr_t)fio); 498 curev++; 499 } 500 puffsfd = puffs_getselectable(pu); 501 EV_SET(curev, puffsfd, EVFILT_READ, EV_ADD, 0, 0, 0); 502 if (kevent(pu->pu_kq, pfctrl->evs, 2*pfctrl->nfds+1, 503 NULL, 0, NULL) == -1) 504 goto out; 505 506 while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) { 507 if (pu->pu_ml_lfn) 508 pu->pu_ml_lfn(pu); 509 510 /* 511 * Do this here, because: 512 * a) loopfunc might generate some results 513 * b) it's still "after" event handling (except for round 1) 514 */ 515 if (puffs_req_putput(ppr) == -1) 516 goto out; 517 puffs_req_resetput(ppr); 518 519 /* micro optimization: skip kevent syscall if possible */ 520 if (pfctrl->nfds == 0 && pu->pu_ml_timep == NULL 521 && (pu->pu_state & PU_ASYNCFD) == 0) { 522 if (puffs_req_handle(pgr, ppr, 0) == -1) 523 goto out; 524 continue; 525 } 526 527 /* else: do full processing */ 528 529 /* 530 * Build list of which to enable/disable in writecheck. 531 * Don't bother worrying about O(n) for now. 532 */ 533 nchanges = 0; 534 LIST_FOREACH(fio, &pfctrl->fb_ios, fio_entries) { 535 if (fio->stat & FIO_WRGONE) 536 continue; 537 538 /* 539 * Try to write out everything to avoid the 540 * need for enabling EVFILT_WRITE. The likely 541 * case is that we can fit everything into the 542 * socket buffer. 543 */ 544 if (puffs_framev_output(pu, pfctrl, fio, ppr)) { 545 /* need kernel notify? (error condition) */ 546 if (puffs_req_putput(ppr) == -1) 547 goto out; 548 puffs_req_resetput(ppr); 549 } 550 551 /* en/disable write checks for kqueue as needed */ 552 assert((FIO_EN_WRITE(fio) && FIO_RM_WRITE(fio)) == 0); 553 if (FIO_EN_WRITE(fio)) { 554 EV_SET(&pfctrl->evs[nchanges], fio->io_fd, 555 EVFILT_WRITE, EV_ENABLE, 0, 0, 556 (uintptr_t)fio); 557 fio->stat |= FIO_WR; 558 nchanges++; 559 } 560 if (FIO_RM_WRITE(fio)) { 561 EV_SET(&pfctrl->evs[nchanges], fio->io_fd, 562 EVFILT_WRITE, EV_DISABLE, 0, 0, 563 (uintptr_t)fio); 564 fio->stat &= ~FIO_WR; 565 nchanges++; 566 } 567 assert(nchanges <= pfctrl->nfds); 568 } 569 570 ndone = kevent(pu->pu_kq, pfctrl->evs, nchanges, 571 pfctrl->evs, pfctrl->nfds+1, pu->pu_ml_timep); 572 573 if (ndone == -1) { 574 if (errno != EINTR) 575 goto out; 576 else 577 continue; 578 } 579 580 /* uoptimize */ 581 if (ndone == 0) 582 continue; 583 584 /* iterate over the results */ 585 for (curev = pfctrl->evs; ndone--; curev++) { 586 int what; 587 588 /* get & possibly dispatch events from kernel */ 589 if (curev->ident == puffsfd) { 590 if (puffs_req_handle(pgr, ppr, 0) == -1) 591 goto out; 592 continue; 593 } 594 595 fio = (void *)curev->udata; 596 if (curev->flags & EV_ERROR) { 597 assert(curev->filter == EVFILT_WRITE); 598 fio->stat &= ~FIO_WR; 599 600 /* XXX: how to know if it's a transient error */ 601 puffs_framev_writeclose(pu, fio, 602 (int)curev->data); 603 puffs_framev_notify(fio, PUFFS_FBIO_ERROR); 604 continue; 605 } 606 607 what = 0; 608 if (curev->filter == EVFILT_READ) { 609 puffs_framev_input(pu, pfctrl, fio, ppr); 610 what |= PUFFS_FBIO_READ; 611 } 612 613 else if (curev->filter == EVFILT_WRITE) { 614 puffs_framev_output(pu, pfctrl, fio, ppr); 615 what |= PUFFS_FBIO_WRITE; 616 } 617 if (what) 618 puffs_framev_notify(fio, what); 619 } 620 621 /* 622 * Really free fd's now that we don't have references 623 * to them. 624 */ 625 while ((fio = LIST_FIRST(&pfctrl->fb_ios_rmlist)) != NULL) { 626 LIST_REMOVE(fio, fio_entries); 627 free(fio); 628 } 629 } 630 errno = 0; 631 puffs_req_putput(ppr); 632 633 out: 634 /* store the real error for a while */ 635 sverrno = errno; 636 637 if (ppr) 638 puffs_req_destroyput(ppr); 639 if (pgr) 640 puffs_req_destroyget(pgr); 641 642 errno = sverrno; 643 if (errno) 644 return -1; 645 else 646 return 0; 647} 648