1 /* $NetBSD: ukfs.c,v 1.32 2009/07/23 01:01:31 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2007, 2008 Antti Kantee. All Rights Reserved. 5 * 6 * Development of this software was supported by the 7 * Finnish Cultural Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * This library enables access to files systems directly without 33 * involving system calls. 34 */ 35 36 #ifdef __linux__ 37 #define _XOPEN_SOURCE 500 38 #define _BSD_SOURCE 39 #define _FILE_OFFSET_BITS 64 40 #endif 41 42 #include <sys/param.h> 43 #include <sys/queue.h> 44 #include <sys/stat.h> 45 #include <sys/sysctl.h> 46 #include <sys/mount.h> 47 48 #include <assert.h> 49 #include <dirent.h> 50 #include <dlfcn.h> 51 #include <err.h> 52 #include <errno.h> 53 #include <fcntl.h> 54 #include <pthread.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 59 #include <stdint.h> 60 61 #include <rump/ukfs.h> 62 63 #include <rump/rump.h> 64 #include <rump/rump_syscalls.h> 65 66 #define UKFS_MODE_DEFAULT 0555 67 68 struct ukfs { 69 struct mount *ukfs_mp; 70 struct vnode *ukfs_rvp; 71 72 pthread_spinlock_t ukfs_spin; 73 pid_t ukfs_nextpid; 74 struct vnode *ukfs_cdir; 75 int ukfs_devfd; 76 char *ukfs_devpath; 77 char *ukfs_mountpath; 78 }; 79 80 static int builddirs(const char *, mode_t, 81 int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *); 82 83 struct mount * 84 ukfs_getmp(struct ukfs *ukfs) 85 { 86 87 return ukfs->ukfs_mp; 88 } 89 90 struct vnode * 91 ukfs_getrvp(struct ukfs *ukfs) 92 { 93 struct vnode *rvp; 94 95 rvp = ukfs->ukfs_rvp; 96 rump_vp_incref(rvp); 97 98 return rvp; 99 } 100 101 #ifdef DONT_WANT_PTHREAD_LINKAGE 102 #define pthread_spin_lock(a) 103 #define pthread_spin_unlock(a) 104 #define pthread_spin_init(a,b) 105 #define pthread_spin_destroy(a) 106 #endif 107 108 static pid_t 109 nextpid(struct ukfs *ukfs) 110 { 111 pid_t npid; 112 113 pthread_spin_lock(&ukfs->ukfs_spin); 114 if (ukfs->ukfs_nextpid == 0) 115 ukfs->ukfs_nextpid++; 116 npid = ukfs->ukfs_nextpid++; 117 pthread_spin_unlock(&ukfs->ukfs_spin); 118 119 return npid; 120 } 121 122 static void 123 precall(struct ukfs *ukfs) 124 { 125 struct vnode *rvp, *cvp; 126 127 rump_setup_curlwp(nextpid(ukfs), 1, 1); 128 rvp = ukfs_getrvp(ukfs); 129 pthread_spin_lock(&ukfs->ukfs_spin); 130 cvp = ukfs->ukfs_cdir; 131 pthread_spin_unlock(&ukfs->ukfs_spin); 132 rump_rcvp_set(rvp, cvp); /* takes refs */ 133 rump_vp_rele(rvp); 134 } 135 136 static void 137 postcall(struct ukfs *ukfs) 138 { 139 struct vnode *rvp; 140 141 rvp = ukfs_getrvp(ukfs); 142 rump_rcvp_set(NULL, rvp); 143 rump_vp_rele(rvp); 144 rump_clear_curlwp(); 145 } 146 147 int 148 _ukfs_init(int version) 149 { 150 int rv; 151 152 if (version != UKFS_VERSION) { 153 printf("incompatible ukfs version, %d vs. %d\n", 154 version, UKFS_VERSION); 155 errno = EPROGMISMATCH; 156 return -1; 157 } 158 159 if ((rv = rump_init()) != 0) { 160 errno = rv; 161 return -1; 162 } 163 164 return 0; 165 } 166 167 /*ARGSUSED*/ 168 static int 169 rumpmkdir(struct ukfs *dummy, const char *path, mode_t mode) 170 { 171 172 return rump_sys_mkdir(path, mode); 173 } 174 175 struct ukfs * 176 ukfs_mount(const char *vfsname, const char *devpath, const char *mountpath, 177 int mntflags, void *arg, size_t alen) 178 { 179 struct stat sb; 180 struct ukfs *fs = NULL; 181 int rv = 0, devfd = -1, rdonly; 182 int mounted = 0; 183 184 /* 185 * Try open and lock the device. if we can't open it, assume 186 * it's a file system which doesn't use a real device and let 187 * it slide. The mount will fail anyway if the fs requires a 188 * device. 189 * 190 * XXX: strictly speaking this is not 100% correct, as virtual 191 * file systems can use a device path which does exist and can 192 * be opened. E.g. tmpfs should be mountable multiple times 193 * with "device" path "/swap", but now isn't. But I think the 194 * chances are so low that it's currently acceptable to let 195 * this one slip. 196 */ 197 rdonly = mntflags & MNT_RDONLY; 198 devfd = open(devpath, rdonly ? O_RDONLY : O_RDWR); 199 if (devfd != -1) { 200 if (fstat(devfd, &sb) == -1) { 201 close(devfd); 202 devfd = -1; 203 rv = errno; 204 goto out; 205 } 206 207 /* 208 * We do this only for non-block device since the 209 * (NetBSD) kernel allows block device open only once. 210 */ 211 if (!S_ISBLK(sb.st_mode)) { 212 if (flock(devfd, LOCK_NB | (rdonly ? LOCK_SH:LOCK_EX)) 213 == -1) { 214 warnx("ukfs_mount: cannot get %s lock on " 215 "device", rdonly ? "shared" : "exclusive"); 216 close(devfd); 217 devfd = -1; 218 rv = errno; 219 goto out; 220 } 221 } else { 222 close(devfd); 223 devfd = -1; 224 } 225 } 226 227 fs = malloc(sizeof(struct ukfs)); 228 if (fs == NULL) { 229 rv = ENOMEM; 230 goto out; 231 } 232 memset(fs, 0, sizeof(struct ukfs)); 233 234 /* create our mountpoint. this is never removed. */ 235 if (builddirs(mountpath, 0777, rumpmkdir, NULL) == -1) { 236 if (errno != EEXIST) { 237 rv = errno; 238 goto out; 239 } 240 } 241 242 rump_fakeblk_register(devpath); 243 rv = rump_sys_mount(vfsname, mountpath, mntflags, arg, alen); 244 if (rv) { 245 goto out; 246 } 247 mounted = 1; 248 rv = rump_vfs_getmp(mountpath, &fs->ukfs_mp); 249 if (rv) { 250 goto out; 251 } 252 rv = rump_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0); 253 if (rv) { 254 goto out; 255 } 256 257 fs->ukfs_devpath = strdup(devpath); 258 fs->ukfs_mountpath = strdup(mountpath); 259 fs->ukfs_cdir = ukfs_getrvp(fs); 260 pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED); 261 fs->ukfs_devfd = devfd; 262 assert(rv == 0); 263 264 out: 265 if (rv) { 266 int sverrno = errno; 267 if (fs) { 268 if (fs->ukfs_rvp) 269 rump_vp_rele(fs->ukfs_rvp); 270 free(fs); 271 fs = NULL; 272 } 273 if (mounted) 274 rump_sys_unmount(mountpath, MNT_FORCE); 275 if (devfd != -1) { 276 flock(devfd, LOCK_UN); 277 close(devfd); 278 } 279 errno = sverrno; 280 } 281 282 return fs; 283 } 284 285 int 286 ukfs_release(struct ukfs *fs, int flags) 287 { 288 289 if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) { 290 int rv, mntflag; 291 292 ukfs_chdir(fs, "/"); 293 mntflag = 0; 294 if (flags & UKFS_RELFLAG_FORCE) 295 mntflag = MNT_FORCE; 296 rv = rump_sys_unmount(fs->ukfs_mountpath, mntflag); 297 if (rv) { 298 ukfs_chdir(fs, fs->ukfs_mountpath); 299 errno = rv; 300 return -1; 301 } 302 } 303 304 rump_fakeblk_deregister(fs->ukfs_devpath); 305 free(fs->ukfs_devpath); 306 free(fs->ukfs_mountpath); 307 308 pthread_spin_destroy(&fs->ukfs_spin); 309 if (fs->ukfs_devfd != -1) { 310 flock(fs->ukfs_devfd, LOCK_UN); 311 close(fs->ukfs_devfd); 312 } 313 free(fs); 314 315 return 0; 316 } 317 318 #define STDCALL(ukfs, thecall) \ 319 int rv = 0; \ 320 \ 321 precall(ukfs); \ 322 rv = thecall; \ 323 postcall(ukfs); \ 324 return rv; 325 326 int 327 ukfs_opendir(struct ukfs *ukfs, const char *dirname, struct ukfs_dircookie **c) 328 { 329 struct vnode *vp; 330 int rv; 331 332 precall(ukfs); 333 rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname, 334 NULL, &vp, NULL); 335 postcall(ukfs); 336 337 if (rv == 0) { 338 RUMP_VOP_UNLOCK(vp, 0); 339 } else { 340 errno = rv; 341 rv = -1; 342 } 343 344 /*LINTED*/ 345 *c = (struct ukfs_dircookie *)vp; 346 return rv; 347 } 348 349 static int 350 getmydents(struct vnode *vp, off_t *off, uint8_t *buf, size_t bufsize) 351 { 352 struct uio *uio; 353 size_t resid; 354 int rv, eofflag; 355 kauth_cred_t cred; 356 357 uio = rump_uio_setup(buf, bufsize, *off, RUMPUIO_READ); 358 cred = rump_cred_suserget(); 359 rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL); 360 rump_cred_suserput(cred); 361 RUMP_VOP_UNLOCK(vp, 0); 362 *off = rump_uio_getoff(uio); 363 resid = rump_uio_free(uio); 364 365 if (rv) { 366 errno = rv; 367 return -1; 368 } 369 370 /* LINTED: not totally correct return type, but follows syscall */ 371 return bufsize - resid; 372 } 373 374 /*ARGSUSED*/ 375 int 376 ukfs_getdents_cookie(struct ukfs *ukfs, struct ukfs_dircookie *c, off_t *off, 377 uint8_t *buf, size_t bufsize) 378 { 379 /*LINTED*/ 380 struct vnode *vp = (struct vnode *)c; 381 382 RUMP_VOP_LOCK(vp, RUMP_LK_SHARED); 383 return getmydents(vp, off, buf, bufsize); 384 } 385 386 int 387 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off, 388 uint8_t *buf, size_t bufsize) 389 { 390 struct vnode *vp; 391 int rv; 392 393 precall(ukfs); 394 rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname, 395 NULL, &vp, NULL); 396 postcall(ukfs); 397 if (rv) { 398 errno = rv; 399 return -1; 400 } 401 402 rv = getmydents(vp, off, buf, bufsize); 403 rump_vp_rele(vp); 404 return rv; 405 } 406 407 /*ARGSUSED*/ 408 int 409 ukfs_closedir(struct ukfs *ukfs, struct ukfs_dircookie *c) 410 { 411 412 /*LINTED*/ 413 rump_vp_rele((struct vnode *)c); 414 return 0; 415 } 416 417 int 418 ukfs_open(struct ukfs *ukfs, const char *filename, int flags) 419 { 420 int fd; 421 422 precall(ukfs); 423 fd = rump_sys_open(filename, flags, 0); 424 postcall(ukfs); 425 if (fd == -1) 426 return -1; 427 428 return fd; 429 } 430 431 ssize_t 432 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off, 433 uint8_t *buf, size_t bufsize) 434 { 435 int fd; 436 ssize_t xfer = -1; /* XXXgcc */ 437 438 precall(ukfs); 439 fd = rump_sys_open(filename, RUMP_O_RDONLY, 0); 440 if (fd == -1) 441 goto out; 442 443 xfer = rump_sys_pread(fd, buf, bufsize, off); 444 rump_sys_close(fd); 445 446 out: 447 postcall(ukfs); 448 if (fd == -1) { 449 return -1; 450 } 451 return xfer; 452 } 453 454 /*ARGSUSED*/ 455 ssize_t 456 ukfs_read_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen) 457 { 458 459 return rump_sys_pread(fd, buf, buflen, off); 460 } 461 462 ssize_t 463 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off, 464 uint8_t *buf, size_t bufsize) 465 { 466 int fd; 467 ssize_t xfer = -1; /* XXXgcc */ 468 469 precall(ukfs); 470 fd = rump_sys_open(filename, RUMP_O_WRONLY, 0); 471 if (fd == -1) 472 goto out; 473 474 /* write and commit */ 475 xfer = rump_sys_pwrite(fd, buf, bufsize, off); 476 if (xfer > 0) 477 rump_sys_fsync(fd); 478 479 rump_sys_close(fd); 480 481 out: 482 postcall(ukfs); 483 if (fd == -1) { 484 return -1; 485 } 486 return xfer; 487 } 488 489 /*ARGSUSED*/ 490 ssize_t 491 ukfs_write_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen, 492 int dosync) 493 { 494 ssize_t xfer; 495 496 xfer = rump_sys_pwrite(fd, buf, buflen, off); 497 if (xfer > 0 && dosync) 498 rump_sys_fsync(fd); 499 500 return xfer; 501 } 502 503 /*ARGSUSED*/ 504 int 505 ukfs_close(struct ukfs *ukfs, int fd) 506 { 507 508 rump_sys_close(fd); 509 return 0; 510 } 511 512 int 513 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode) 514 { 515 int fd; 516 517 precall(ukfs); 518 fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode); 519 if (fd == -1) 520 return -1; 521 rump_sys_close(fd); 522 523 postcall(ukfs); 524 return 0; 525 } 526 527 int 528 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev) 529 { 530 531 STDCALL(ukfs, rump_sys_mknod(path, mode, dev)); 532 } 533 534 int 535 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode) 536 { 537 538 STDCALL(ukfs, rump_sys_mkfifo(path, mode)); 539 } 540 541 int 542 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode) 543 { 544 545 STDCALL(ukfs, rump_sys_mkdir(filename, mode)); 546 } 547 548 int 549 ukfs_remove(struct ukfs *ukfs, const char *filename) 550 { 551 552 STDCALL(ukfs, rump_sys_unlink(filename)); 553 } 554 555 int 556 ukfs_rmdir(struct ukfs *ukfs, const char *filename) 557 { 558 559 STDCALL(ukfs, rump_sys_rmdir(filename)); 560 } 561 562 int 563 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create) 564 { 565 566 STDCALL(ukfs, rump_sys_link(filename, f_create)); 567 } 568 569 int 570 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname) 571 { 572 573 STDCALL(ukfs, rump_sys_symlink(filename, linkname)); 574 } 575 576 ssize_t 577 ukfs_readlink(struct ukfs *ukfs, const char *filename, 578 char *linkbuf, size_t buflen) 579 { 580 ssize_t rv; 581 582 precall(ukfs); 583 rv = rump_sys_readlink(filename, linkbuf, buflen); 584 postcall(ukfs); 585 return rv; 586 } 587 588 int 589 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to) 590 { 591 592 STDCALL(ukfs, rump_sys_rename(from, to)); 593 } 594 595 int 596 ukfs_chdir(struct ukfs *ukfs, const char *path) 597 { 598 struct vnode *newvp, *oldvp; 599 int rv; 600 601 precall(ukfs); 602 rv = rump_sys_chdir(path); 603 if (rv == -1) 604 goto out; 605 606 newvp = rump_cdir_get(); 607 pthread_spin_lock(&ukfs->ukfs_spin); 608 oldvp = ukfs->ukfs_cdir; 609 ukfs->ukfs_cdir = newvp; 610 pthread_spin_unlock(&ukfs->ukfs_spin); 611 if (oldvp) 612 rump_vp_rele(oldvp); 613 614 out: 615 postcall(ukfs); 616 return rv; 617 } 618 619 /* 620 * If we want to use post-time_t file systems on pre-time_t hosts, 621 * we must translate the stat structure. Since we don't currently 622 * have a general method for making compat calls in rump, special-case 623 * this one. 624 * 625 * Note that this does not allow making system calls to older rump 626 * kernels from newer hosts. 627 */ 628 #define VERS_TIMECHANGE 599000700 629 630 static int 631 needcompat(void) 632 { 633 634 #ifdef __NetBSD__ 635 /*LINTED*/ 636 return __NetBSD_Version__ < VERS_TIMECHANGE 637 && rump_getversion() >= VERS_TIMECHANGE; 638 #else 639 return 0; 640 #endif 641 } 642 643 int 644 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) 645 { 646 int rv; 647 648 precall(ukfs); 649 if (needcompat()) 650 rv = rump_sys___stat30(filename, file_stat); 651 else 652 rv = rump_sys_stat(filename, file_stat); 653 postcall(ukfs); 654 655 return rv; 656 } 657 658 int 659 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) 660 { 661 int rv; 662 663 precall(ukfs); 664 if (needcompat()) 665 rv = rump_sys___lstat30(filename, file_stat); 666 else 667 rv = rump_sys_lstat(filename, file_stat); 668 postcall(ukfs); 669 670 return rv; 671 } 672 673 int 674 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode) 675 { 676 677 STDCALL(ukfs, rump_sys_chmod(filename, mode)); 678 } 679 680 int 681 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode) 682 { 683 684 STDCALL(ukfs, rump_sys_lchmod(filename, mode)); 685 } 686 687 int 688 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) 689 { 690 691 STDCALL(ukfs, rump_sys_chown(filename, uid, gid)); 692 } 693 694 int 695 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) 696 { 697 698 STDCALL(ukfs, rump_sys_lchown(filename, uid, gid)); 699 } 700 701 int 702 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags) 703 { 704 705 STDCALL(ukfs, rump_sys_chflags(filename, flags)); 706 } 707 708 int 709 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags) 710 { 711 712 STDCALL(ukfs, rump_sys_lchflags(filename, flags)); 713 } 714 715 int 716 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr) 717 { 718 719 STDCALL(ukfs, rump_sys_utimes(filename, tptr)); 720 } 721 722 int 723 ukfs_lutimes(struct ukfs *ukfs, const char *filename, 724 const struct timeval *tptr) 725 { 726 727 STDCALL(ukfs, rump_sys_lutimes(filename, tptr)); 728 } 729 730 /* 731 * Dynamic module support 732 */ 733 734 /* load one library */ 735 736 /* 737 * XXX: the dlerror stuff isn't really threadsafe, but then again I 738 * can't protect against other threads calling dl*() outside of ukfs, 739 * so just live with it being flimsy 740 */ 741 int 742 ukfs_modload(const char *fname) 743 { 744 void *handle; 745 struct modinfo **mi; 746 int error; 747 748 handle = dlopen(fname, RTLD_GLOBAL); 749 if (handle == NULL) { 750 const char *dlmsg = dlerror(); 751 if (strstr(dlmsg, "Undefined symbol")) 752 return 0; 753 warnx("dlopen %s failed: %s\n", fname, dlmsg); 754 /* XXXerrno */ 755 return -1; 756 } 757 758 mi = dlsym(handle, "__start_link_set_modules"); 759 if (mi) { 760 error = rump_module_init(*mi, NULL); 761 if (error) 762 goto errclose; 763 return 1; 764 } 765 error = EINVAL; 766 767 errclose: 768 dlclose(handle); 769 errno = error; 770 return -1; 771 } 772 773 struct loadfail { 774 char *pname; 775 776 LIST_ENTRY(loadfail) entries; 777 }; 778 779 #define RUMPFSMOD_PREFIX "librumpfs_" 780 #define RUMPFSMOD_SUFFIX ".so" 781 782 int 783 ukfs_modload_dir(const char *dir) 784 { 785 char nbuf[MAXPATHLEN+1], *p; 786 struct dirent entry, *result; 787 DIR *libdir; 788 struct loadfail *lf, *nlf; 789 int error, nloaded = 0, redo; 790 LIST_HEAD(, loadfail) lfs; 791 792 libdir = opendir(dir); 793 if (libdir == NULL) 794 return -1; 795 796 LIST_INIT(&lfs); 797 for (;;) { 798 if ((error = readdir_r(libdir, &entry, &result)) != 0) 799 break; 800 if (!result) 801 break; 802 if (strncmp(result->d_name, RUMPFSMOD_PREFIX, 803 strlen(RUMPFSMOD_PREFIX)) != 0) 804 continue; 805 if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL) 806 || strlen(p) != strlen(RUMPFSMOD_SUFFIX)) 807 continue; 808 strlcpy(nbuf, dir, sizeof(nbuf)); 809 strlcat(nbuf, "/", sizeof(nbuf)); 810 strlcat(nbuf, result->d_name, sizeof(nbuf)); 811 switch (ukfs_modload(nbuf)) { 812 case 0: 813 lf = malloc(sizeof(*lf)); 814 if (lf == NULL) { 815 error = ENOMEM; 816 break; 817 } 818 lf->pname = strdup(nbuf); 819 if (lf->pname == NULL) { 820 free(lf); 821 error = ENOMEM; 822 break; 823 } 824 LIST_INSERT_HEAD(&lfs, lf, entries); 825 break; 826 case 1: 827 nloaded++; 828 break; 829 default: 830 /* ignore errors */ 831 break; 832 } 833 } 834 closedir(libdir); 835 if (error && nloaded != 0) 836 error = 0; 837 838 /* 839 * El-cheapo dependency calculator. Just try to load the 840 * modules n times in a loop 841 */ 842 for (redo = 1; redo;) { 843 redo = 0; 844 nlf = LIST_FIRST(&lfs); 845 while ((lf = nlf) != NULL) { 846 nlf = LIST_NEXT(lf, entries); 847 if (ukfs_modload(lf->pname) == 1) { 848 nloaded++; 849 redo = 1; 850 LIST_REMOVE(lf, entries); 851 free(lf->pname); 852 free(lf); 853 } 854 } 855 } 856 857 while ((lf = LIST_FIRST(&lfs)) != NULL) { 858 LIST_REMOVE(lf, entries); 859 free(lf->pname); 860 free(lf); 861 } 862 863 if (error && nloaded == 0) { 864 errno = error; 865 return -1; 866 } 867 868 return nloaded; 869 } 870 871 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */ 872 ssize_t 873 ukfs_vfstypes(char *buf, size_t buflen) 874 { 875 int mib[3]; 876 struct sysctlnode q, ans[128]; 877 size_t alen; 878 int i; 879 880 mib[0] = CTL_VFS; 881 mib[1] = VFS_GENERIC; 882 mib[2] = CTL_QUERY; 883 alen = sizeof(ans); 884 885 memset(&q, 0, sizeof(q)); 886 q.sysctl_flags = SYSCTL_VERSION; 887 888 if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) { 889 return -1; 890 } 891 892 for (i = 0; i < alen/sizeof(ans[0]); i++) 893 if (strcmp("fstypes", ans[i].sysctl_name) == 0) 894 break; 895 if (i == alen/sizeof(ans[0])) { 896 errno = ENXIO; 897 return -1; 898 } 899 900 mib[0] = CTL_VFS; 901 mib[1] = VFS_GENERIC; 902 mib[2] = ans[i].sysctl_num; 903 904 if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) { 905 return -1; 906 } 907 908 return buflen; 909 } 910 911 /* 912 * Utilities 913 */ 914 static int 915 builddirs(const char *pathname, mode_t mode, 916 int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *fs) 917 { 918 char *f1, *f2; 919 int rv; 920 mode_t mask; 921 bool end; 922 923 /*ukfs_umask((mask = ukfs_umask(0)));*/ 924 umask((mask = umask(0))); 925 926 f1 = f2 = strdup(pathname); 927 if (f1 == NULL) { 928 errno = ENOMEM; 929 return -1; 930 } 931 932 end = false; 933 for (;;) { 934 /* find next component */ 935 f2 += strspn(f2, "/"); 936 f2 += strcspn(f2, "/"); 937 if (*f2 == '\0') 938 end = true; 939 else 940 *f2 = '\0'; 941 942 rv = mkdirfn(fs, f1, mode & ~mask); 943 if (errno == EEXIST) 944 rv = 0; 945 946 if (rv == -1 || *f2 != '\0' || end) 947 break; 948 949 *f2 = '/'; 950 } 951 952 free(f1); 953 954 return rv; 955 } 956 957 int 958 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode) 959 { 960 961 return builddirs(pathname, mode, ukfs_mkdir, ukfs); 962 } 963