1 /* $NetBSD: puffs.c,v 1.117 2011/11/14 01:27:42 chs 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.117 2011/11/14 01:27:42 chs Exp $"); 35 #endif /* !lint */ 36 37 #include <sys/param.h> 38 #include <sys/mount.h> 39 40 #if defined(__minix) 41 #include "fs.h" 42 #endif /* defined(__minix) */ 43 44 #include <assert.h> 45 #include <err.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <mntopts.h> 49 #include <paths.h> 50 #include <puffs.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <syslog.h> 55 #include <unistd.h> 56 57 #include "puffs_priv.h" 58 59 /* Most file systems want this for opts, so just give it to them */ 60 const struct mntopt puffsmopts[] = { 61 MOPT_STDOPTS, 62 PUFFSMOPT_STD, 63 MOPT_NULL, 64 }; 65 #ifdef PUFFS_WITH_THREADS 66 #include <pthread.h> 67 pthread_mutex_t pu_lock = PTHREAD_MUTEX_INITIALIZER; 68 #endif 69 70 #if defined(__minix) 71 static message fs_msg; 72 static int fs_ipc_status; 73 #endif 74 75 /* Declare some local functions. */ 76 static int get_work(message *msg, int *ipc_status); 77 78 /* SEF functions and variables. */ 79 static void sef_local_startup(void); 80 static int sef_cb_init_fresh(int type, sef_init_info_t *info); 81 static void sef_cb_signal_handler(int signo); 82 83 EXTERN int env_argc; 84 EXTERN char **env_argv; 85 86 87 #define PUFFS_MAX_ARGS 20 88 89 int __real_main(int argc, char* argv[]); 90 int __wrap_main(int argc, char* argv[]); 91 92 int __wrap_main(int argc, char *argv[]) 93 { 94 int i; 95 int new_argc = 0; 96 static char* new_argv[PUFFS_MAX_ARGS]; 97 char *name; 98 99 /* SEF local startup. */ 100 env_setargs(argc, argv); 101 sef_local_startup(); 102 103 global_kcred.pkcr_type = PUFFCRED_TYPE_INTERNAL; 104 105 if (argc < 3) { 106 panic("Unexpected arguments, use:\ 107 mount -t fs /dev/ /dir [-o option1,option2]\n"); 108 } 109 110 name = argv[0] + strlen(argv[0]); 111 while (*name != '/' && name != argv[0]) 112 name--; 113 if (name != argv[0]) 114 name++; 115 strcpy(fs_name, name); 116 117 new_argv[new_argc] = argv[0]; 118 new_argc++; 119 120 for (i = 1; i < argc; i++) { 121 if (new_argc >= PUFFS_MAX_ARGS) { 122 panic("Too many arguments, change PUFFS_MAX_ARGS"); 123 } 124 new_argv[new_argc] = argv[i]; 125 new_argc++; 126 } 127 128 assert(new_argc > 0); 129 130 /* Get the mount request from VFS, so we can deal with it later. */ 131 (void)get_work(&fs_msg, &fs_ipc_status); 132 133 return __real_main(new_argc, new_argv); 134 } 135 136 137 #define FILLOP(lower, upper) \ 138 do { \ 139 if (pops->puffs_node_##lower) \ 140 opmask[PUFFS_VN_##upper] = 1; \ 141 } while (/*CONSTCOND*/0) 142 static void 143 fillvnopmask(struct puffs_ops *pops, struct puffs_kargs *pa) 144 { 145 uint8_t *opmask = pa->pa_vnopmask; 146 147 memset(opmask, 0, sizeof(pa->pa_vnopmask)); 148 149 FILLOP(create, CREATE); 150 FILLOP(mknod, MKNOD); 151 FILLOP(open, OPEN); 152 FILLOP(close, CLOSE); 153 FILLOP(access, ACCESS); 154 FILLOP(getattr, GETATTR); 155 FILLOP(setattr, SETATTR); 156 FILLOP(poll, POLL); 157 FILLOP(mmap, MMAP); 158 FILLOP(fsync, FSYNC); 159 FILLOP(seek, SEEK); 160 FILLOP(remove, REMOVE); 161 FILLOP(link, LINK); 162 FILLOP(rename, RENAME); 163 FILLOP(mkdir, MKDIR); 164 FILLOP(rmdir, RMDIR); 165 FILLOP(symlink, SYMLINK); 166 FILLOP(readdir, READDIR); 167 FILLOP(readlink, READLINK); 168 FILLOP(reclaim, RECLAIM); 169 FILLOP(inactive, INACTIVE); 170 FILLOP(print, PRINT); 171 FILLOP(read, READ); 172 FILLOP(write, WRITE); 173 FILLOP(abortop, ABORTOP); 174 } 175 #undef FILLOP 176 177 178 /*ARGSUSED*/ 179 __dead static void 180 puffs_defaulterror(struct puffs_usermount *pu, uint8_t type, 181 int error, const char *str, puffs_cookie_t cookie) 182 { 183 184 lpuffs_debug("abort: type %d, error %d, cookie %p (%s)\n", 185 type, error, cookie, str); 186 abort(); 187 } 188 189 190 int 191 puffs_getstate(struct puffs_usermount *pu) 192 { 193 194 return pu->pu_state & PU_STATEMASK; 195 } 196 197 void 198 puffs_setstacksize(struct puffs_usermount *pu, size_t ss) 199 { 200 long psize, minsize; 201 int stackshift; 202 int bonus; 203 204 assert(puffs_getstate(pu) == PUFFS_STATE_BEFOREMOUNT); 205 206 psize = sysconf(_SC_PAGESIZE); 207 minsize = 4*psize; 208 if (ss < (size_t)minsize || ss == PUFFS_STACKSIZE_MIN) { 209 if (ss != PUFFS_STACKSIZE_MIN) 210 lpuffs_debug("puffs_setstacksize: adjusting " 211 "stacksize to minimum %ld\n", minsize); 212 ss = 4*psize; 213 } 214 215 stackshift = -1; 216 bonus = 0; 217 while (ss) { 218 if (ss & 0x1) 219 bonus++; 220 ss >>= 1; 221 stackshift++; 222 } 223 if (bonus > 1) { 224 stackshift++; 225 lpuffs_debug("puffs_setstacksize: using next power of two: " 226 "%d\n", 1<<stackshift); 227 } 228 229 pu->pu_cc_stackshift = stackshift; 230 } 231 232 struct puffs_pathobj * 233 puffs_getrootpathobj(struct puffs_usermount *pu) 234 { 235 struct puffs_node *pnr; 236 237 pnr = pu->pu_pn_root; 238 if (pnr == NULL) { 239 errno = ENOENT; 240 return NULL; 241 } 242 243 return &pnr->pn_po; 244 } 245 246 void 247 puffs_setroot(struct puffs_usermount *pu, struct puffs_node *pn) 248 { 249 250 pu->pu_pn_root = pn; 251 } 252 253 struct puffs_node * 254 puffs_getroot(struct puffs_usermount *pu) 255 { 256 257 return pu->pu_pn_root; 258 } 259 260 void 261 puffs_setrootinfo(struct puffs_usermount *pu, enum vtype vt, 262 vsize_t vsize, dev_t rdev) 263 { 264 struct puffs_kargs *pargs = pu->pu_kargp; 265 266 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) { 267 warnx("puffs_setrootinfo: call has effect only " 268 "before mount\n"); 269 return; 270 } 271 272 pargs->pa_root_vtype = vt; 273 pargs->pa_root_vsize = vsize; 274 pargs->pa_root_rdev = rdev; 275 } 276 277 void * 278 puffs_getspecific(struct puffs_usermount *pu) 279 { 280 281 return pu->pu_privdata; 282 } 283 284 void 285 puffs_setspecific(struct puffs_usermount *pu, void *privdata) 286 { 287 288 pu->pu_privdata = privdata; 289 } 290 291 void 292 puffs_setmntinfo(struct puffs_usermount *pu, 293 const char *mntfromname, const char *puffsname) 294 { 295 struct puffs_kargs *pargs = pu->pu_kargp; 296 297 (void)strlcpy(pargs->pa_mntfromname, mntfromname, 298 sizeof(pargs->pa_mntfromname)); 299 (void)strlcpy(pargs->pa_typename, puffsname, 300 sizeof(pargs->pa_typename)); 301 } 302 303 size_t 304 puffs_getmaxreqlen(struct puffs_usermount *pu) 305 { 306 307 return pu->pu_maxreqlen; 308 } 309 310 void 311 puffs_setmaxreqlen(struct puffs_usermount *pu, size_t reqlen) 312 { 313 314 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) 315 warnx("puffs_setmaxreqlen: call has effect only " 316 "before mount\n"); 317 318 pu->pu_kargp->pa_maxmsglen = reqlen; 319 } 320 321 void 322 puffs_setfhsize(struct puffs_usermount *pu, size_t fhsize, int flags) 323 { 324 325 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) 326 warnx("puffs_setfhsize: call has effect only before mount\n"); 327 328 pu->pu_kargp->pa_fhsize = fhsize; 329 pu->pu_kargp->pa_fhflags = flags; 330 } 331 332 void 333 puffs_setncookiehash(struct puffs_usermount *pu, int nhash) 334 { 335 336 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) 337 warnx("puffs_setfhsize: call has effect only before mount\n"); 338 339 pu->pu_kargp->pa_nhashbuckets = nhash; 340 } 341 342 void 343 puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn) 344 { 345 346 pu->pu_pathbuild = fn; 347 } 348 349 void 350 puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn) 351 { 352 353 pu->pu_pathtransform = fn; 354 } 355 356 void 357 puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn) 358 { 359 360 pu->pu_pathcmp = fn; 361 } 362 363 void 364 puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn) 365 { 366 367 pu->pu_pathfree = fn; 368 } 369 370 void 371 puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn) 372 { 373 374 pu->pu_namemod = fn; 375 } 376 377 void 378 puffs_set_errnotify(struct puffs_usermount *pu, pu_errnotify_fn fn) 379 { 380 381 pu->pu_errnotify = fn; 382 } 383 384 void 385 puffs_set_cmap(struct puffs_usermount *pu, pu_cmap_fn fn) 386 { 387 388 pu->pu_cmap = fn; 389 } 390 391 void 392 puffs_ml_setloopfn(struct puffs_usermount *pu, puffs_ml_loop_fn lfn) 393 { 394 395 pu->pu_ml_lfn = lfn; 396 } 397 398 void 399 puffs_ml_settimeout(struct puffs_usermount *pu, struct timespec *ts) 400 { 401 402 if (ts == NULL) { 403 pu->pu_ml_timep = NULL; 404 } else { 405 pu->pu_ml_timeout = *ts; 406 pu->pu_ml_timep = &pu->pu_ml_timeout; 407 } 408 } 409 410 void 411 puffs_set_prepost(struct puffs_usermount *pu, 412 pu_prepost_fn pre, pu_prepost_fn pst) 413 { 414 415 pu->pu_oppre = pre; 416 pu->pu_oppost = pst; 417 } 418 419 int 420 puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags, 421 puffs_cookie_t cookie) 422 { 423 #if defined(__minix) 424 pu->pu_kargp->pa_root_cookie = cookie; 425 426 /* Process the already-received mount request. */ 427 fsdriver_process(&puffs_table, &fs_msg, fs_ipc_status, FALSE); 428 429 if (!mounted) { 430 /* This should never happen, unless VFS misbehaves.. */ 431 free(pu->pu_kargp); 432 pu->pu_kargp = NULL; 433 errno = -EINVAL; 434 return -1; 435 } 436 437 PU_SETSTATE(pu, PUFFS_STATE_RUNNING); 438 return 0; 439 #endif /* defined(__minix) */ 440 } 441 442 /*ARGSUSED*/ 443 struct puffs_usermount * 444 puffs_init(struct puffs_ops *pops, const char *mntfromname, 445 const char *puffsname, void *priv, uint32_t pflags) 446 { 447 struct puffs_usermount *pu; 448 struct puffs_kargs *pargs; 449 int sverrno; 450 451 if (puffsname == PUFFS_DEFER) 452 puffsname = "n/a"; 453 if (mntfromname == PUFFS_DEFER) 454 mntfromname = "n/a"; 455 if (priv == PUFFS_DEFER) 456 priv = NULL; 457 458 pu = malloc(sizeof(struct puffs_usermount)); 459 if (pu == NULL) 460 goto failfree; 461 memset(pu, 0, sizeof(struct puffs_usermount)); 462 463 pargs = pu->pu_kargp = malloc(sizeof(struct puffs_kargs)); 464 if (pargs == NULL) 465 goto failfree; 466 memset(pargs, 0, sizeof(struct puffs_kargs)); 467 468 pargs->pa_vers = PUFFSVERSION; 469 pargs->pa_flags = PUFFS_FLAG_KERN(pflags); 470 fillvnopmask(pops, pargs); 471 puffs_setmntinfo(pu, mntfromname, puffsname); 472 473 puffs_zerostatvfs(&pargs->pa_svfsb); 474 pargs->pa_root_cookie = NULL; 475 pargs->pa_root_vtype = VDIR; 476 pargs->pa_root_vsize = 0; 477 pargs->pa_root_rdev = 0; 478 pargs->pa_maxmsglen = 0; 479 if (/*CONSTCOND*/ sizeof(time_t) == 4) 480 pargs->pa_time32 = 1; 481 else 482 pargs->pa_time32 = 0; 483 484 pu->pu_flags = pflags; 485 buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH; /* XXX */ 486 pu->pu_ops = *pops; 487 free(pops); /* XXX */ 488 489 pu->pu_privdata = priv; 490 pu->pu_cc_stackshift = PUFFS_CC_STACKSHIFT_DEFAULT; 491 LIST_INIT(&pu->pu_pnodelst); 492 LIST_INIT(&pu->pu_pnode_removed_lst); 493 LIST_INIT(&pu->pu_ios); 494 LIST_INIT(&pu->pu_ios_rmlist); 495 LIST_INIT(&pu->pu_ccmagazin); 496 TAILQ_INIT(&pu->pu_sched); 497 498 /* defaults for some user-settable translation functions */ 499 pu->pu_cmap = NULL; /* identity translation */ 500 501 pu->pu_pathbuild = puffs_stdpath_buildpath; 502 pu->pu_pathfree = puffs_stdpath_freepath; 503 pu->pu_pathcmp = puffs_stdpath_cmppath; 504 pu->pu_pathtransform = NULL; 505 pu->pu_namemod = NULL; 506 507 pu->pu_errnotify = puffs_defaulterror; 508 509 PU_SETSTATE(pu, PUFFS_STATE_BEFOREMOUNT); 510 511 global_pu = pu; 512 513 return pu; 514 515 failfree: 516 /* can't unmount() from here for obvious reasons */ 517 sverrno = errno; 518 free(pu); 519 errno = sverrno; 520 return NULL; 521 } 522 523 void 524 puffs_cancel(struct puffs_usermount *pu, int error) 525 { 526 527 assert(puffs_getstate(pu) < PUFFS_STATE_RUNNING); 528 free(pu); 529 } 530 531 /*ARGSUSED1*/ 532 int 533 puffs_exit(struct puffs_usermount *pu, int force) 534 { 535 struct puffs_node *pn; 536 537 lpuffs_debug("puffs_exit\n"); 538 539 while ((pn = LIST_FIRST(&pu->pu_pnodelst)) != NULL) 540 puffs_pn_put(pn); 541 542 while ((pn = LIST_FIRST(&pu->pu_pnode_removed_lst)) != NULL) 543 puffs_pn_put(pn); 544 545 puffs__cc_exit(pu); 546 if (pu->pu_state & PU_HASKQ) 547 close(pu->pu_kq); 548 free(pu); 549 550 return 0; /* always succesful for now, WILL CHANGE */ 551 } 552 553 /* 554 * Actual mainloop. This is called from a context which can block. 555 * It is called either from puffs_mainloop (indirectly, via 556 * puffs_cc_continue() or from puffs_cc_yield()). 557 */ 558 void 559 puffs__theloop(struct puffs_cc *pcc) 560 { 561 struct puffs_usermount *pu = pcc->pcc_pu; 562 563 while (mounted || !exitsignaled) { 564 /* 565 * Schedule existing requests. 566 */ 567 while ((pcc = TAILQ_FIRST(&pu->pu_sched)) != NULL) { 568 lpuffs_debug("scheduling existing tasks\n"); 569 TAILQ_REMOVE(&pu->pu_sched, pcc, pcc_schedent); 570 puffs__goto(pcc); 571 } 572 573 if (pu->pu_ml_lfn) { 574 lpuffs_debug("Calling user mainloop handler\n"); 575 pu->pu_ml_lfn(pu); 576 } 577 578 /* Wait for request message. */ 579 if (get_work(&fs_msg, &fs_ipc_status) != OK) 580 continue; /* recheck loop conditions */ 581 582 /* Process it, and send a reply. */ 583 fsdriver_process(&puffs_table, &fs_msg, fs_ipc_status, FALSE); 584 } 585 586 if (puffs__cc_restoremain(pu) == -1) 587 warn("cannot restore main context. impending doom"); 588 589 /* May get here, if puffs_fakecc is set to 1. Currently librefuse sets it. 590 * Now we just return to the caller. 591 */ 592 } 593 int 594 puffs_mainloop(struct puffs_usermount *pu) 595 { 596 struct puffs_cc *pcc; 597 int sverrno; 598 599 assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING); 600 601 pu->pu_state |= PU_HASKQ | PU_INLOOP; 602 603 /* 604 * Create alternate execution context and jump to it. Note 605 * that we come "out" of savemain twice. Where we come out 606 * of it depends on the architecture. If the return address is 607 * stored on the stack, we jump out from puffs_cc_continue(), 608 * for a register return address from puffs__cc_savemain(). 609 * PU_MAINRESTORE makes sure we DTRT in both cases. 610 */ 611 if (puffs__cc_create(pu, puffs__theloop, &pcc) == -1) { 612 goto out; 613 } 614 if (puffs__cc_savemain(pu) == -1) { 615 goto out; 616 } 617 if ((pu->pu_state & PU_MAINRESTORE) == 0) 618 puffs_cc_continue(pcc); 619 620 errno = 0; 621 622 out: 623 /* store the real error for a while */ 624 sverrno = errno; 625 626 errno = sverrno; 627 if (errno) 628 return -1; 629 else 630 return 0; 631 } 632 633 #if defined(__minix) 634 /*===========================================================================* 635 * sef_local_startup * 636 *===========================================================================*/ 637 static void sef_local_startup(void) 638 { 639 /* Register init callbacks. */ 640 sef_setcb_init_fresh(sef_cb_init_fresh); 641 642 /* Register signal callbacks. */ 643 sef_setcb_signal_handler(sef_cb_signal_handler); 644 645 /* Let SEF perform startup. */ 646 sef_startup(); 647 } 648 649 /*===========================================================================* 650 * sef_cb_init_fresh * 651 *===========================================================================*/ 652 static int sef_cb_init_fresh(int type, sef_init_info_t *info) 653 { 654 /* Initialize the Minix file server. */ 655 return(OK); 656 } 657 658 /*===========================================================================* 659 * sef_cb_signal_handler * 660 *===========================================================================*/ 661 static void sef_cb_signal_handler(int signo) 662 { 663 /* Only check for termination signal, ignore anything else. */ 664 if (signo != SIGTERM) return; 665 666 exitsignaled = 1; 667 fs_sync(); 668 669 sef_cancel(); 670 } 671 672 /*===========================================================================* 673 * get_work * 674 *===========================================================================*/ 675 static int get_work(message *msg, int *ipc_status) 676 { 677 int r; 678 679 for (;;) { 680 if ((r = sef_receive_status(ANY, msg, ipc_status)) != OK) { 681 if (r == EINTR) /* sef_cancel from signal handler? */ 682 break; /* see if we can exit the main loop */ 683 panic("sef_receive failed: %d", r); 684 } 685 if (msg->m_source == VFS_PROC_NR) 686 break; 687 lpuffs_debug("libpuffs: unexpected source %d\n", msg->m_source); 688 } 689 690 return r; 691 } 692 #endif /* defined(__minix) */ 693