puffs.c revision 1.59
1/* $NetBSD: puffs.c,v 1.59 2007/07/19 07:54:46 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.59 2007/07/19 07:54:46 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_kargs.pa_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_kargs.pa_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_kargs; 182 183 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) 184 warnx("puffs_setrootinfo: call has effect only " 185 "before mount\n"); 186 187 pargs->pa_root_vtype = vt; 188 pargs->pa_root_vsize = vsize; 189 pargs->pa_root_rdev = rdev; 190} 191 192void * 193puffs_getspecific(struct puffs_usermount *pu) 194{ 195 196 return pu->pu_privdata; 197} 198 199size_t 200puffs_getmaxreqlen(struct puffs_usermount *pu) 201{ 202 203 return pu->pu_kargs.pa_maxreqlen; 204} 205 206void 207puffs_setmaxreqlen(struct puffs_usermount *pu, size_t reqlen) 208{ 209 210 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) 211 warnx("puffs_setmaxreqlen: call has effect only " 212 "before mount\n"); 213 214 pu->pu_kargs.pa_maxreqlen = reqlen; 215} 216 217void 218puffs_setfhsize(struct puffs_usermount *pu, size_t fhsize, int flags) 219{ 220 221 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) 222 warnx("puffs_setfhsize: call has effect only before mount\n"); 223 224 pu->pu_kargs.pa_fhsize = fhsize; 225 pu->pu_kargs.pa_fhflags = flags; 226} 227 228void 229puffs_setncookiehash(struct puffs_usermount *pu, int nhash) 230{ 231 232 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) 233 warnx("puffs_setfhsize: call has effect only before mount\n"); 234 235 pu->pu_kargs.pa_nhashbuckets = nhash; 236} 237 238void 239puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn) 240{ 241 242 pu->pu_pathbuild = fn; 243} 244 245void 246puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn) 247{ 248 249 pu->pu_pathtransform = fn; 250} 251 252void 253puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn) 254{ 255 256 pu->pu_pathcmp = fn; 257} 258 259void 260puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn) 261{ 262 263 pu->pu_pathfree = fn; 264} 265 266void 267puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn) 268{ 269 270 pu->pu_namemod = fn; 271} 272 273void 274puffs_ml_setloopfn(struct puffs_usermount *pu, puffs_ml_loop_fn lfn) 275{ 276 277 pu->pu_ml_lfn = lfn; 278} 279 280void 281puffs_ml_settimeout(struct puffs_usermount *pu, struct timespec *ts) 282{ 283 284 if (ts == NULL) { 285 pu->pu_ml_timep = NULL; 286 } else { 287 pu->pu_ml_timeout = *ts; 288 pu->pu_ml_timep = &pu->pu_ml_timeout; 289 } 290} 291 292void 293puffs_setback(struct puffs_cc *pcc, int whatback) 294{ 295 struct puffs_req *preq = pcc->pcc_preq; 296 297 assert(PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN && ( 298 preq->preq_optype == PUFFS_VN_OPEN || 299 preq->preq_optype == PUFFS_VN_MMAP || 300 preq->preq_optype == PUFFS_VN_REMOVE || 301 preq->preq_optype == PUFFS_VN_RMDIR || 302 preq->preq_optype == PUFFS_VN_INACTIVE)); 303 304 preq->preq_setbacks |= whatback & PUFFS_SETBACK_MASK; 305} 306 307int 308puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags, 309 void *cookie) 310{ 311 312#if 1 313 /* XXXkludgehere */ 314 /* kauth doesn't provide this service any longer */ 315 if (geteuid() != 0) 316 mntflags |= MNT_NOSUID | MNT_NODEV; 317#endif 318 319 pu->pu_kargs.pa_root_cookie = cookie; 320 if (mount(MOUNT_PUFFS, dir, mntflags, 321 &pu->pu_kargs, sizeof(struct puffs_kargs)) == -1) 322 return -1; 323 if (ioctl(pu->pu_kargs.pa_fd, PUFFSREQSIZEOP, 324 &pu->pu_kargs.pa_maxreqlen) == -1) 325 return -1; 326 PU_SETSTATE(pu, PUFFS_STATE_RUNNING); 327 328 return 0; 329} 330 331struct puffs_usermount * 332_puffs_init(int develv, struct puffs_ops *pops, const char *mntfromname, 333 const char *puffsname, void *priv, uint32_t pflags) 334{ 335 struct puffs_usermount *pu; 336 struct puffs_kargs *pargs; 337 int fd; 338 339 if (develv != PUFFS_DEVEL_LIBVERSION) { 340 warnx("puffs_init: mounting with lib version %d, need %d", 341 develv, PUFFS_DEVEL_LIBVERSION); 342 errno = EINVAL; 343 return NULL; 344 } 345 346 fd = open(_PATH_PUFFS, O_RDONLY); 347 if (fd == -1) { 348 warnx("puffs_init: cannot open %s", _PATH_PUFFS); 349 return NULL; 350 } 351 if (fd <= 2) 352 warnx("puffs_init: device fd %d (<= 2), sure this is " 353 "what you want?", fd); 354 355 pu = malloc(sizeof(struct puffs_usermount)); 356 if (pu == NULL) 357 goto failfree; 358 memset(pu, 0, sizeof(struct puffs_usermount)); 359 360 pargs = &pu->pu_kargs; 361 pargs->pa_vers = PUFFSDEVELVERS | PUFFSVERSION; 362 pargs->pa_flags = PUFFS_FLAG_KERN(pflags); 363 pargs->pa_fd = fd; 364 fillvnopmask(pops, pargs->pa_vnopmask); 365 (void)strlcpy(pargs->pa_typename, puffsname, 366 sizeof(pargs->pa_typename)); 367 (void)strlcpy(pargs->pa_mntfromname, mntfromname, 368 sizeof(pargs->pa_mntfromname)); 369 370 puffs_zerostatvfs(&pargs->pa_svfsb); 371 pargs->pa_root_cookie = NULL; 372 pargs->pa_root_vtype = VDIR; 373 pargs->pa_root_vsize = 0; 374 pargs->pa_root_rdev = 0; 375 pargs->pa_maxreqlen = 0; 376 377 pu->pu_flags = pflags; 378 pu->pu_ops = *pops; 379 free(pops); /* XXX */ 380 381 pu->pu_privdata = priv; 382 pu->pu_cc_stacksize = PUFFS_CC_STACKSIZE_DEFAULT; 383 LIST_INIT(&pu->pu_pnodelst); 384 LIST_INIT(&pu->pu_framectrl.fb_ios); 385 LIST_INIT(&pu->pu_ccnukelst); 386 387 /* defaults for some user-settable translation functions */ 388 pu->pu_cmap = NULL; /* identity translation */ 389 390 pu->pu_pathbuild = puffs_stdpath_buildpath; 391 pu->pu_pathfree = puffs_stdpath_freepath; 392 pu->pu_pathcmp = puffs_stdpath_cmppath; 393 pu->pu_pathtransform = NULL; 394 pu->pu_namemod = NULL; 395 396 PU_SETSTATE(pu, PUFFS_STATE_BEFOREMOUNT); 397 398 return pu; 399 400 failfree: 401 /* can't unmount() from here for obvious reasons */ 402 close(fd); 403 free(pu); 404 return NULL; 405} 406 407/* 408 * XXX: there's currently no clean way to request unmount from 409 * within the user server, so be very brutal about it. 410 */ 411/*ARGSUSED1*/ 412int 413puffs_exit(struct puffs_usermount *pu, int force) 414{ 415 struct puffs_node *pn; 416 417 force = 1; /* currently */ 418 419 if (pu->pu_kargs.pa_fd) 420 close(pu->pu_kargs.pa_fd); 421 422 while ((pn = LIST_FIRST(&pu->pu_pnodelst)) != NULL) 423 puffs_pn_put(pn); 424 425 puffs_framev_exit(pu); 426 if (pu->pu_haskq) 427 close(pu->pu_kq); 428 free(pu); 429 430 return 0; /* always succesful for now, WILL CHANGE */ 431} 432 433int 434puffs_mainloop(struct puffs_usermount *pu, int flags) 435{ 436 struct puffs_getreq *pgr = NULL; 437 struct puffs_putreq *ppr = NULL; 438 struct puffs_framectrl *pfctrl = &pu->pu_framectrl; 439 struct puffs_fctrl_io *fio; 440 struct kevent *curev, *newevs; 441 size_t nchanges; 442 int puffsfd, sverrno; 443 int ndone; 444 445 assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING); 446 447 pgr = puffs_req_makeget(pu, puffs_getmaxreqlen(pu), 0); 448 if (pgr == NULL) 449 goto out; 450 451 ppr = puffs_req_makeput(pu); 452 if (ppr == NULL) 453 goto out; 454 455 newevs = realloc(pfctrl->evs, (2*pfctrl->nfds+1)*sizeof(struct kevent)); 456 if (newevs == NULL) 457 goto out; 458 pfctrl->evs = newevs; 459 460 if ((flags & PUFFSLOOP_NODAEMON) == 0) 461 if (daemon(1, 0) == -1) 462 goto out; 463 pu->pu_state |= PU_INLOOP; 464 465 pu->pu_kq = kqueue(); 466 if (pu->pu_kq == -1) 467 goto out; 468 pu->pu_haskq = 1; 469 470 curev = pfctrl->evs; 471 LIST_FOREACH(fio, &pfctrl->fb_ios, fio_entries) { 472 EV_SET(curev, fio->io_fd, EVFILT_READ, EV_ADD, 473 0, 0, (uintptr_t)fio); 474 curev++; 475 EV_SET(curev, fio->io_fd, EVFILT_WRITE, EV_ADD | EV_DISABLE, 476 0, 0, (uintptr_t)fio); 477 curev++; 478 } 479 puffsfd = puffs_getselectable(pu); 480 EV_SET(curev, puffsfd, EVFILT_READ, EV_ADD, 0, 0, 0); 481 if (kevent(pu->pu_kq, pfctrl->evs, 2*pfctrl->nfds+1, 482 NULL, 0, NULL) == -1) 483 goto out; 484 485 while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) { 486 if (pu->pu_ml_lfn) 487 pu->pu_ml_lfn(pu); 488 489 /* 490 * Do this here, because: 491 * a) loopfunc might generate some results 492 * b) it's still "after" event handling (except for round 1) 493 */ 494 if (puffs_req_putput(ppr) == -1) 495 goto out; 496 puffs_req_resetput(ppr); 497 498 /* micro optimization: skip kevent syscall if possible */ 499 if (pfctrl->nfds == 0 && pu->pu_ml_timep == NULL 500 && (pu->pu_state & PU_ASYNCFD) == 0) { 501 if (puffs_req_handle(pgr, ppr, 0) == -1) 502 goto out; 503 continue; 504 } 505 506 /* else: do full processing */ 507 508 /* 509 * Build list of which to enable/disable in writecheck. 510 * Don't bother worrying about O(n) for now. 511 */ 512 nchanges = 0; 513 LIST_FOREACH(fio, &pfctrl->fb_ios, fio_entries) { 514 if (fio->stat & FIO_WRGONE) 515 continue; 516 517 /* 518 * Try to write out everything to avoid the 519 * need for enabling EVFILT_WRITE. The likely 520 * case is that we can fit everything into the 521 * socket buffer. 522 */ 523 if (puffs_framev_output(pu, pfctrl, fio, ppr)) { 524 /* need kernel notify? (error condition) */ 525 if (puffs_req_putput(ppr) == -1) 526 goto out; 527 puffs_req_resetput(ppr); 528 } 529 530 /* en/disable write checks for kqueue as needed */ 531 assert((FIO_EN_WRITE(fio) && FIO_RM_WRITE(fio)) == 0); 532 if (FIO_EN_WRITE(fio)) { 533 EV_SET(&pfctrl->evs[nchanges], fio->io_fd, 534 EVFILT_WRITE, EV_ENABLE, 0, 0, 535 (uintptr_t)fio); 536 fio->stat |= FIO_WR; 537 nchanges++; 538 } 539 if (FIO_RM_WRITE(fio)) { 540 EV_SET(&pfctrl->evs[nchanges], fio->io_fd, 541 EVFILT_WRITE, EV_DISABLE, 0, 0, 542 (uintptr_t)fio); 543 fio->stat &= ~FIO_WR; 544 nchanges++; 545 } 546 assert(nchanges <= pfctrl->nfds); 547 } 548 549 ndone = kevent(pu->pu_kq, pfctrl->evs, nchanges, 550 pfctrl->evs, pfctrl->nfds+1, pu->pu_ml_timep); 551 552 if (ndone == -1) { 553 if (errno != EINTR) 554 goto out; 555 else 556 continue; 557 } 558 559 /* uoptimize */ 560 if (ndone == 0) 561 continue; 562 563 /* iterate over the results */ 564 for (curev = pfctrl->evs; ndone--; curev++) { 565 /* get & possibly dispatch events from kernel */ 566 if (curev->ident == puffsfd) { 567 if (puffs_req_handle(pgr, ppr, 0) == -1) 568 goto out; 569 continue; 570 } 571 572 fio = (void *)curev->udata; 573 if (curev->flags & EV_ERROR) { 574 assert(curev->filter == EVFILT_WRITE); 575 fio->stat &= ~FIO_WR; 576 577 /* XXX: how to know if it's a transient error */ 578 puffs_framev_writeclose(pu, fio, 579 (int)curev->data); 580 continue; 581 } 582 583 if (curev->filter == EVFILT_READ) 584 puffs_framev_input(pu, pfctrl, fio, ppr); 585 586 else if (curev->filter == EVFILT_WRITE) 587 puffs_framev_output(pu, pfctrl, fio, ppr); 588 } 589 590 /* 591 * Really free fd's now that we don't have references 592 * to them. 593 */ 594 while ((fio = LIST_FIRST(&pfctrl->fb_ios_rmlist)) != NULL) { 595 LIST_REMOVE(fio, fio_entries); 596 free(fio); 597 } 598 } 599 errno = 0; 600 puffs_req_putput(ppr); 601 602 out: 603 /* store the real error for a while */ 604 sverrno = errno; 605 606 if (ppr) 607 puffs_req_destroyput(ppr); 608 if (pgr) 609 puffs_req_destroyget(pgr); 610 611 errno = sverrno; 612 if (errno) 613 return -1; 614 else 615 return 0; 616} 617