1 /* $NetBSD: ukfs.c,v 1.39 2009/10/07 20:53:38 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2007, 2008, 2009 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 #include "ukfs_int_disklabel.h" 67 68 #define UKFS_MODE_DEFAULT 0555 69 70 struct ukfs { 71 struct mount *ukfs_mp; 72 struct vnode *ukfs_rvp; 73 void *ukfs_specific; 74 75 pthread_spinlock_t ukfs_spin; 76 pid_t ukfs_nextpid; 77 struct vnode *ukfs_cdir; 78 int ukfs_devfd; 79 char *ukfs_devpath; 80 char *ukfs_mountpath; 81 }; 82 83 static int builddirs(const char *, mode_t, 84 int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *); 85 86 struct mount * 87 ukfs_getmp(struct ukfs *ukfs) 88 { 89 90 return ukfs->ukfs_mp; 91 } 92 93 struct vnode * 94 ukfs_getrvp(struct ukfs *ukfs) 95 { 96 struct vnode *rvp; 97 98 rvp = ukfs->ukfs_rvp; 99 rump_vp_incref(rvp); 100 101 return rvp; 102 } 103 104 void 105 ukfs_setspecific(struct ukfs *ukfs, void *priv) 106 { 107 108 ukfs->ukfs_specific = priv; 109 } 110 111 void * 112 ukfs_getspecific(struct ukfs *ukfs) 113 { 114 115 return ukfs->ukfs_specific; 116 } 117 118 #ifdef DONT_WANT_PTHREAD_LINKAGE 119 #define pthread_spin_lock(a) 120 #define pthread_spin_unlock(a) 121 #define pthread_spin_init(a,b) 122 #define pthread_spin_destroy(a) 123 #endif 124 125 static pid_t 126 nextpid(struct ukfs *ukfs) 127 { 128 pid_t npid; 129 130 pthread_spin_lock(&ukfs->ukfs_spin); 131 if (ukfs->ukfs_nextpid == 0) 132 ukfs->ukfs_nextpid++; 133 npid = ukfs->ukfs_nextpid++; 134 pthread_spin_unlock(&ukfs->ukfs_spin); 135 136 return npid; 137 } 138 139 static void 140 precall(struct ukfs *ukfs) 141 { 142 struct vnode *rvp, *cvp; 143 144 rump_setup_curlwp(nextpid(ukfs), 1, 1); 145 rvp = ukfs_getrvp(ukfs); 146 pthread_spin_lock(&ukfs->ukfs_spin); 147 cvp = ukfs->ukfs_cdir; 148 pthread_spin_unlock(&ukfs->ukfs_spin); 149 rump_rcvp_set(rvp, cvp); /* takes refs */ 150 rump_vp_rele(rvp); 151 } 152 153 static void 154 postcall(struct ukfs *ukfs) 155 { 156 struct vnode *rvp; 157 158 rvp = ukfs_getrvp(ukfs); 159 rump_rcvp_set(NULL, rvp); 160 rump_vp_rele(rvp); 161 rump_clear_curlwp(); 162 } 163 164 int 165 _ukfs_init(int version) 166 { 167 int rv; 168 169 if (version != UKFS_VERSION) { 170 printf("incompatible ukfs version, %d vs. %d\n", 171 version, UKFS_VERSION); 172 errno = EPROGMISMATCH; 173 return -1; 174 } 175 176 if ((rv = rump_init()) != 0) { 177 errno = rv; 178 return -1; 179 } 180 181 return 0; 182 } 183 184 /*ARGSUSED*/ 185 static int 186 rumpmkdir(struct ukfs *dummy, const char *path, mode_t mode) 187 { 188 189 return rump_sys_mkdir(path, mode); 190 } 191 192 int 193 ukfs_partition_probe(char *devpath, int *partition) 194 { 195 char *p; 196 int rv = 0; 197 198 /* 199 * Check for disklabel magic in pathname: 200 * /regularpath%PART:<char>%\0 201 */ 202 #define MAGICADJ(p, n) (p+sizeof(UKFS_PARTITION_SCANMAGIC)-1+n) 203 if ((p = strstr(devpath, UKFS_PARTITION_SCANMAGIC)) != NULL 204 && strlen(p) == UKFS_PARTITION_MAGICLEN 205 && *(MAGICADJ(p,1)) == '%') { 206 if (*(MAGICADJ(p,0)) >= 'a' && 207 *(MAGICADJ(p,0)) < 'a' + UKFS_MAXPARTITIONS) { 208 *partition = *(MAGICADJ(p,0)) - 'a'; 209 *p = '\0'; 210 } else { 211 rv = EINVAL; 212 } 213 } else { 214 *partition = UKFS_PARTITION_NONE; 215 } 216 217 return rv; 218 } 219 220 /* 221 * Open the disk file and flock it. Also, if we are operation on 222 * an embedded partition, find the partition offset and size from 223 * the disklabel. 224 * 225 * We hard-fail only in two cases: 226 * 1) we failed to get the partition info out (don't know what offset 227 * to mount from) 228 * 2) we failed to flock the source device (i.e. flock() fails, 229 * not e.g. open() before it) 230 * 231 * Otherwise we let the code proceed to mount and let the file system 232 * throw the proper error. The only questionable bit is that if we 233 * soft-fail before flock() and mount does succeed... 234 * 235 * Returns: -1 error (errno reports error code) 236 * 0 success 237 * 238 * dfdp: -1 device is not open 239 * n device is open 240 */ 241 static int 242 process_diskdevice(const char *devpath, int partition, int rdonly, 243 int *dfdp, uint64_t *devoff, uint64_t *devsize) 244 { 245 char buf[65536]; 246 struct stat sb; 247 struct ukfs_disklabel dl; 248 struct ukfs_partition *pp; 249 int rv = 0, devfd; 250 251 /* defaults */ 252 *devoff = 0; 253 *devsize = RUMP_ETFS_SIZE_ENDOFF; 254 *dfdp = -1; 255 256 devfd = open(devpath, rdonly ? O_RDONLY : O_RDWR); 257 if (devfd == -1) { 258 if (UKFS_USEPARTITION(partition)) 259 rv = errno; 260 goto out; 261 } 262 263 /* 264 * Locate the disklabel and find the partition in question. 265 */ 266 if (UKFS_USEPARTITION(partition)) { 267 if (pread(devfd, buf, sizeof(buf), 0) == -1) { 268 rv = errno; 269 goto out; 270 } 271 272 if (ukfs_disklabel_scan(&dl, buf, sizeof(buf)) != 0) { 273 rv = ENOENT; 274 goto out; 275 } 276 277 if (dl.d_npartitions < partition) { 278 rv = ENOENT; 279 goto out; 280 } 281 282 pp = &dl.d_partitions[partition]; 283 *devoff = pp->p_offset << DEV_BSHIFT; 284 *devsize = pp->p_size << DEV_BSHIFT; 285 } 286 287 if (fstat(devfd, &sb) == -1) { 288 rv = errno; 289 goto out; 290 } 291 292 /* 293 * We do this only for non-block device since the 294 * (NetBSD) kernel allows block device open only once. 295 * We also need to close the device for fairly obvious reasons. 296 */ 297 if (!S_ISBLK(sb.st_mode)) { 298 if (flock(devfd, LOCK_NB | (rdonly ? LOCK_SH:LOCK_EX)) == -1) { 299 warnx("ukfs_mount: cannot get %s lock on " 300 "device", rdonly ? "shared" : "exclusive"); 301 rv = errno; 302 goto out; 303 } 304 } else { 305 close(devfd); 306 devfd = -1; 307 } 308 *dfdp = devfd; 309 310 out: 311 if (rv) { 312 if (devfd != -1) 313 close(devfd); 314 errno = rv; 315 rv = -1; 316 } 317 318 return rv; 319 } 320 321 static struct ukfs * 322 doukfsmount(const char *vfsname, const char *devpath, int partition, 323 const char *mountpath, int mntflags, void *arg, size_t alen) 324 { 325 struct ukfs *fs = NULL; 326 int rv = 0, devfd = -1; 327 uint64_t devoff, devsize; 328 int mounted = 0; 329 int regged = 0; 330 331 /* XXX: gcc whine */ 332 devoff = 0; 333 devsize = 0; 334 335 if (partition != UKFS_PARTITION_NA) 336 process_diskdevice(devpath, partition, mntflags & MNT_RDONLY, 337 &devfd, &devoff, &devsize); 338 339 fs = malloc(sizeof(struct ukfs)); 340 if (fs == NULL) { 341 rv = ENOMEM; 342 goto out; 343 } 344 memset(fs, 0, sizeof(struct ukfs)); 345 346 /* create our mountpoint. this is never removed. */ 347 if (builddirs(mountpath, 0777, rumpmkdir, NULL) == -1) { 348 if (errno != EEXIST) { 349 rv = errno; 350 goto out; 351 } 352 } 353 354 if (partition != UKFS_PARTITION_NA) { 355 rv = rump_etfs_register_withsize(devpath, devpath, 356 RUMP_ETFS_BLK, devoff, devsize); 357 if (rv) { 358 goto out; 359 } 360 regged = 1; 361 } 362 363 rv = rump_sys_mount(vfsname, mountpath, mntflags, arg, alen); 364 if (rv) { 365 rv = errno; 366 goto out; 367 } 368 mounted = 1; 369 rv = rump_vfs_getmp(mountpath, &fs->ukfs_mp); 370 if (rv) { 371 goto out; 372 } 373 rv = rump_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0); 374 if (rv) { 375 goto out; 376 } 377 378 if (regged) { 379 fs->ukfs_devpath = strdup(devpath); 380 } 381 fs->ukfs_mountpath = strdup(mountpath); 382 fs->ukfs_cdir = ukfs_getrvp(fs); 383 pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED); 384 fs->ukfs_devfd = devfd; 385 assert(rv == 0); 386 387 out: 388 if (rv) { 389 if (fs) { 390 if (fs->ukfs_rvp) 391 rump_vp_rele(fs->ukfs_rvp); 392 free(fs); 393 fs = NULL; 394 } 395 if (mounted) 396 rump_sys_unmount(mountpath, MNT_FORCE); 397 if (regged) 398 rump_etfs_remove(devpath); 399 if (devfd != -1) { 400 flock(devfd, LOCK_UN); 401 close(devfd); 402 } 403 errno = rv; 404 } 405 406 return fs; 407 } 408 409 struct ukfs * 410 ukfs_mount(const char *vfsname, const char *devpath, 411 const char *mountpath, int mntflags, void *arg, size_t alen) 412 { 413 414 return doukfsmount(vfsname, devpath, UKFS_PARTITION_NA, 415 mountpath, mntflags, arg, alen); 416 } 417 418 struct ukfs * 419 ukfs_mount_disk(const char *vfsname, const char *devpath, int partition, 420 const char *mountpath, int mntflags, void *arg, size_t alen) 421 { 422 423 return doukfsmount(vfsname, devpath, partition, 424 mountpath, mntflags, arg, alen); 425 } 426 427 int 428 ukfs_release(struct ukfs *fs, int flags) 429 { 430 431 if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) { 432 int rv, mntflag, error; 433 434 ukfs_chdir(fs, "/"); 435 mntflag = 0; 436 if (flags & UKFS_RELFLAG_FORCE) 437 mntflag = MNT_FORCE; 438 rump_setup_curlwp(nextpid(fs), 1, 1); 439 rump_vp_rele(fs->ukfs_rvp); 440 fs->ukfs_rvp = NULL; 441 rv = rump_sys_unmount(fs->ukfs_mountpath, mntflag); 442 if (rv == -1) { 443 error = errno; 444 rump_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0); 445 rump_clear_curlwp(); 446 ukfs_chdir(fs, fs->ukfs_mountpath); 447 errno = error; 448 return -1; 449 } 450 rump_clear_curlwp(); 451 } 452 453 if (fs->ukfs_devpath) { 454 rump_etfs_remove(fs->ukfs_devpath); 455 free(fs->ukfs_devpath); 456 } 457 free(fs->ukfs_mountpath); 458 459 pthread_spin_destroy(&fs->ukfs_spin); 460 if (fs->ukfs_devfd != -1) { 461 flock(fs->ukfs_devfd, LOCK_UN); 462 close(fs->ukfs_devfd); 463 } 464 free(fs); 465 466 return 0; 467 } 468 469 #define STDCALL(ukfs, thecall) \ 470 int rv = 0; \ 471 \ 472 precall(ukfs); \ 473 rv = thecall; \ 474 postcall(ukfs); \ 475 return rv; 476 477 int 478 ukfs_opendir(struct ukfs *ukfs, const char *dirname, struct ukfs_dircookie **c) 479 { 480 struct vnode *vp; 481 int rv; 482 483 precall(ukfs); 484 rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname, 485 NULL, &vp, NULL); 486 postcall(ukfs); 487 488 if (rv == 0) { 489 RUMP_VOP_UNLOCK(vp, 0); 490 } else { 491 errno = rv; 492 rv = -1; 493 } 494 495 /*LINTED*/ 496 *c = (struct ukfs_dircookie *)vp; 497 return rv; 498 } 499 500 static int 501 getmydents(struct vnode *vp, off_t *off, uint8_t *buf, size_t bufsize) 502 { 503 struct uio *uio; 504 size_t resid; 505 int rv, eofflag; 506 kauth_cred_t cred; 507 508 uio = rump_uio_setup(buf, bufsize, *off, RUMPUIO_READ); 509 cred = rump_cred_suserget(); 510 rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL); 511 rump_cred_suserput(cred); 512 RUMP_VOP_UNLOCK(vp, 0); 513 *off = rump_uio_getoff(uio); 514 resid = rump_uio_free(uio); 515 516 if (rv) { 517 errno = rv; 518 return -1; 519 } 520 521 /* LINTED: not totally correct return type, but follows syscall */ 522 return bufsize - resid; 523 } 524 525 /*ARGSUSED*/ 526 int 527 ukfs_getdents_cookie(struct ukfs *ukfs, struct ukfs_dircookie *c, off_t *off, 528 uint8_t *buf, size_t bufsize) 529 { 530 /*LINTED*/ 531 struct vnode *vp = (struct vnode *)c; 532 533 RUMP_VOP_LOCK(vp, RUMP_LK_SHARED); 534 return getmydents(vp, off, buf, bufsize); 535 } 536 537 int 538 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off, 539 uint8_t *buf, size_t bufsize) 540 { 541 struct vnode *vp; 542 int rv; 543 544 precall(ukfs); 545 rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname, 546 NULL, &vp, NULL); 547 postcall(ukfs); 548 if (rv) { 549 errno = rv; 550 return -1; 551 } 552 553 rv = getmydents(vp, off, buf, bufsize); 554 rump_vp_rele(vp); 555 return rv; 556 } 557 558 /*ARGSUSED*/ 559 int 560 ukfs_closedir(struct ukfs *ukfs, struct ukfs_dircookie *c) 561 { 562 563 /*LINTED*/ 564 rump_vp_rele((struct vnode *)c); 565 return 0; 566 } 567 568 int 569 ukfs_open(struct ukfs *ukfs, const char *filename, int flags) 570 { 571 int fd; 572 573 precall(ukfs); 574 fd = rump_sys_open(filename, flags, 0); 575 postcall(ukfs); 576 if (fd == -1) 577 return -1; 578 579 return fd; 580 } 581 582 ssize_t 583 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off, 584 uint8_t *buf, size_t bufsize) 585 { 586 int fd; 587 ssize_t xfer = -1; /* XXXgcc */ 588 589 precall(ukfs); 590 fd = rump_sys_open(filename, RUMP_O_RDONLY, 0); 591 if (fd == -1) 592 goto out; 593 594 xfer = rump_sys_pread(fd, buf, bufsize, off); 595 rump_sys_close(fd); 596 597 out: 598 postcall(ukfs); 599 if (fd == -1) { 600 return -1; 601 } 602 return xfer; 603 } 604 605 /*ARGSUSED*/ 606 ssize_t 607 ukfs_read_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen) 608 { 609 610 return rump_sys_pread(fd, buf, buflen, off); 611 } 612 613 ssize_t 614 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off, 615 uint8_t *buf, size_t bufsize) 616 { 617 int fd; 618 ssize_t xfer = -1; /* XXXgcc */ 619 620 precall(ukfs); 621 fd = rump_sys_open(filename, RUMP_O_WRONLY, 0); 622 if (fd == -1) 623 goto out; 624 625 /* write and commit */ 626 xfer = rump_sys_pwrite(fd, buf, bufsize, off); 627 if (xfer > 0) 628 rump_sys_fsync(fd); 629 630 rump_sys_close(fd); 631 632 out: 633 postcall(ukfs); 634 if (fd == -1) { 635 return -1; 636 } 637 return xfer; 638 } 639 640 /*ARGSUSED*/ 641 ssize_t 642 ukfs_write_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen, 643 int dosync) 644 { 645 ssize_t xfer; 646 647 xfer = rump_sys_pwrite(fd, buf, buflen, off); 648 if (xfer > 0 && dosync) 649 rump_sys_fsync(fd); 650 651 return xfer; 652 } 653 654 /*ARGSUSED*/ 655 int 656 ukfs_close(struct ukfs *ukfs, int fd) 657 { 658 659 rump_sys_close(fd); 660 return 0; 661 } 662 663 int 664 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode) 665 { 666 int fd; 667 668 precall(ukfs); 669 fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode); 670 if (fd == -1) 671 return -1; 672 rump_sys_close(fd); 673 674 postcall(ukfs); 675 return 0; 676 } 677 678 int 679 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev) 680 { 681 682 STDCALL(ukfs, rump_sys_mknod(path, mode, dev)); 683 } 684 685 int 686 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode) 687 { 688 689 STDCALL(ukfs, rump_sys_mkfifo(path, mode)); 690 } 691 692 int 693 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode) 694 { 695 696 STDCALL(ukfs, rump_sys_mkdir(filename, mode)); 697 } 698 699 int 700 ukfs_remove(struct ukfs *ukfs, const char *filename) 701 { 702 703 STDCALL(ukfs, rump_sys_unlink(filename)); 704 } 705 706 int 707 ukfs_rmdir(struct ukfs *ukfs, const char *filename) 708 { 709 710 STDCALL(ukfs, rump_sys_rmdir(filename)); 711 } 712 713 int 714 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create) 715 { 716 717 STDCALL(ukfs, rump_sys_link(filename, f_create)); 718 } 719 720 int 721 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname) 722 { 723 724 STDCALL(ukfs, rump_sys_symlink(filename, linkname)); 725 } 726 727 ssize_t 728 ukfs_readlink(struct ukfs *ukfs, const char *filename, 729 char *linkbuf, size_t buflen) 730 { 731 ssize_t rv; 732 733 precall(ukfs); 734 rv = rump_sys_readlink(filename, linkbuf, buflen); 735 postcall(ukfs); 736 return rv; 737 } 738 739 int 740 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to) 741 { 742 743 STDCALL(ukfs, rump_sys_rename(from, to)); 744 } 745 746 int 747 ukfs_chdir(struct ukfs *ukfs, const char *path) 748 { 749 struct vnode *newvp, *oldvp; 750 int rv; 751 752 precall(ukfs); 753 rv = rump_sys_chdir(path); 754 if (rv == -1) 755 goto out; 756 757 newvp = rump_cdir_get(); 758 pthread_spin_lock(&ukfs->ukfs_spin); 759 oldvp = ukfs->ukfs_cdir; 760 ukfs->ukfs_cdir = newvp; 761 pthread_spin_unlock(&ukfs->ukfs_spin); 762 if (oldvp) 763 rump_vp_rele(oldvp); 764 765 out: 766 postcall(ukfs); 767 return rv; 768 } 769 770 /* 771 * If we want to use post-time_t file systems on pre-time_t hosts, 772 * we must translate the stat structure. Since we don't currently 773 * have a general method for making compat calls in rump, special-case 774 * this one. 775 * 776 * Note that this does not allow making system calls to older rump 777 * kernels from newer hosts. 778 */ 779 #define VERS_TIMECHANGE 599000700 780 781 static int 782 needcompat(void) 783 { 784 785 #ifdef __NetBSD__ 786 /*LINTED*/ 787 return __NetBSD_Version__ < VERS_TIMECHANGE 788 && rump_getversion() >= VERS_TIMECHANGE; 789 #else 790 return 0; 791 #endif 792 } 793 794 int 795 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) 796 { 797 int rv; 798 799 precall(ukfs); 800 if (needcompat()) 801 rv = rump_sys___stat30(filename, file_stat); 802 else 803 rv = rump_sys_stat(filename, file_stat); 804 postcall(ukfs); 805 806 return rv; 807 } 808 809 int 810 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) 811 { 812 int rv; 813 814 precall(ukfs); 815 if (needcompat()) 816 rv = rump_sys___lstat30(filename, file_stat); 817 else 818 rv = rump_sys_lstat(filename, file_stat); 819 postcall(ukfs); 820 821 return rv; 822 } 823 824 int 825 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode) 826 { 827 828 STDCALL(ukfs, rump_sys_chmod(filename, mode)); 829 } 830 831 int 832 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode) 833 { 834 835 STDCALL(ukfs, rump_sys_lchmod(filename, mode)); 836 } 837 838 int 839 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) 840 { 841 842 STDCALL(ukfs, rump_sys_chown(filename, uid, gid)); 843 } 844 845 int 846 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) 847 { 848 849 STDCALL(ukfs, rump_sys_lchown(filename, uid, gid)); 850 } 851 852 int 853 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags) 854 { 855 856 STDCALL(ukfs, rump_sys_chflags(filename, flags)); 857 } 858 859 int 860 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags) 861 { 862 863 STDCALL(ukfs, rump_sys_lchflags(filename, flags)); 864 } 865 866 int 867 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr) 868 { 869 870 STDCALL(ukfs, rump_sys_utimes(filename, tptr)); 871 } 872 873 int 874 ukfs_lutimes(struct ukfs *ukfs, const char *filename, 875 const struct timeval *tptr) 876 { 877 878 STDCALL(ukfs, rump_sys_lutimes(filename, tptr)); 879 } 880 881 /* 882 * Dynamic module support 883 */ 884 885 /* load one library */ 886 887 /* 888 * XXX: the dlerror stuff isn't really threadsafe, but then again I 889 * can't protect against other threads calling dl*() outside of ukfs, 890 * so just live with it being flimsy 891 */ 892 int 893 ukfs_modload(const char *fname) 894 { 895 void *handle; 896 struct modinfo **mi; 897 int error; 898 899 handle = dlopen(fname, RTLD_GLOBAL); 900 if (handle == NULL) { 901 const char *dlmsg = dlerror(); 902 if (strstr(dlmsg, "Undefined symbol")) 903 return 0; 904 warnx("dlopen %s failed: %s\n", fname, dlmsg); 905 /* XXXerrno */ 906 return -1; 907 } 908 909 mi = dlsym(handle, "__start_link_set_modules"); 910 if (mi) { 911 error = rump_module_init(*mi, NULL); 912 if (error) 913 goto errclose; 914 return 1; 915 } 916 error = EINVAL; 917 918 errclose: 919 dlclose(handle); 920 errno = error; 921 return -1; 922 } 923 924 struct loadfail { 925 char *pname; 926 927 LIST_ENTRY(loadfail) entries; 928 }; 929 930 #define RUMPFSMOD_PREFIX "librumpfs_" 931 #define RUMPFSMOD_SUFFIX ".so" 932 933 int 934 ukfs_modload_dir(const char *dir) 935 { 936 char nbuf[MAXPATHLEN+1], *p; 937 struct dirent entry, *result; 938 DIR *libdir; 939 struct loadfail *lf, *nlf; 940 int error, nloaded = 0, redo; 941 LIST_HEAD(, loadfail) lfs; 942 943 libdir = opendir(dir); 944 if (libdir == NULL) 945 return -1; 946 947 LIST_INIT(&lfs); 948 for (;;) { 949 if ((error = readdir_r(libdir, &entry, &result)) != 0) 950 break; 951 if (!result) 952 break; 953 if (strncmp(result->d_name, RUMPFSMOD_PREFIX, 954 strlen(RUMPFSMOD_PREFIX)) != 0) 955 continue; 956 if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL) 957 || strlen(p) != strlen(RUMPFSMOD_SUFFIX)) 958 continue; 959 strlcpy(nbuf, dir, sizeof(nbuf)); 960 strlcat(nbuf, "/", sizeof(nbuf)); 961 strlcat(nbuf, result->d_name, sizeof(nbuf)); 962 switch (ukfs_modload(nbuf)) { 963 case 0: 964 lf = malloc(sizeof(*lf)); 965 if (lf == NULL) { 966 error = ENOMEM; 967 break; 968 } 969 lf->pname = strdup(nbuf); 970 if (lf->pname == NULL) { 971 free(lf); 972 error = ENOMEM; 973 break; 974 } 975 LIST_INSERT_HEAD(&lfs, lf, entries); 976 break; 977 case 1: 978 nloaded++; 979 break; 980 default: 981 /* ignore errors */ 982 break; 983 } 984 } 985 closedir(libdir); 986 if (error && nloaded != 0) 987 error = 0; 988 989 /* 990 * El-cheapo dependency calculator. Just try to load the 991 * modules n times in a loop 992 */ 993 for (redo = 1; redo;) { 994 redo = 0; 995 nlf = LIST_FIRST(&lfs); 996 while ((lf = nlf) != NULL) { 997 nlf = LIST_NEXT(lf, entries); 998 if (ukfs_modload(lf->pname) == 1) { 999 nloaded++; 1000 redo = 1; 1001 LIST_REMOVE(lf, entries); 1002 free(lf->pname); 1003 free(lf); 1004 } 1005 } 1006 } 1007 1008 while ((lf = LIST_FIRST(&lfs)) != NULL) { 1009 LIST_REMOVE(lf, entries); 1010 free(lf->pname); 1011 free(lf); 1012 } 1013 1014 if (error && nloaded == 0) { 1015 errno = error; 1016 return -1; 1017 } 1018 1019 return nloaded; 1020 } 1021 1022 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */ 1023 ssize_t 1024 ukfs_vfstypes(char *buf, size_t buflen) 1025 { 1026 int mib[3]; 1027 struct sysctlnode q, ans[128]; 1028 size_t alen; 1029 int i; 1030 1031 mib[0] = CTL_VFS; 1032 mib[1] = VFS_GENERIC; 1033 mib[2] = CTL_QUERY; 1034 alen = sizeof(ans); 1035 1036 memset(&q, 0, sizeof(q)); 1037 q.sysctl_flags = SYSCTL_VERSION; 1038 1039 if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) { 1040 return -1; 1041 } 1042 1043 for (i = 0; i < alen/sizeof(ans[0]); i++) 1044 if (strcmp("fstypes", ans[i].sysctl_name) == 0) 1045 break; 1046 if (i == alen/sizeof(ans[0])) { 1047 errno = ENXIO; 1048 return -1; 1049 } 1050 1051 mib[0] = CTL_VFS; 1052 mib[1] = VFS_GENERIC; 1053 mib[2] = ans[i].sysctl_num; 1054 1055 if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) { 1056 return -1; 1057 } 1058 1059 return buflen; 1060 } 1061 1062 /* 1063 * Utilities 1064 */ 1065 static int 1066 builddirs(const char *pathname, mode_t mode, 1067 int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *fs) 1068 { 1069 char *f1, *f2; 1070 int rv; 1071 mode_t mask; 1072 bool end; 1073 1074 /*ukfs_umask((mask = ukfs_umask(0)));*/ 1075 umask((mask = umask(0))); 1076 1077 f1 = f2 = strdup(pathname); 1078 if (f1 == NULL) { 1079 errno = ENOMEM; 1080 return -1; 1081 } 1082 1083 end = false; 1084 for (;;) { 1085 /* find next component */ 1086 f2 += strspn(f2, "/"); 1087 f2 += strcspn(f2, "/"); 1088 if (*f2 == '\0') 1089 end = true; 1090 else 1091 *f2 = '\0'; 1092 1093 rv = mkdirfn(fs, f1, mode & ~mask); 1094 if (errno == EEXIST) 1095 rv = 0; 1096 1097 if (rv == -1 || *f2 != '\0' || end) 1098 break; 1099 1100 *f2 = '/'; 1101 } 1102 1103 free(f1); 1104 1105 return rv; 1106 } 1107 1108 int 1109 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode) 1110 { 1111 1112 return builddirs(pathname, mode, ukfs_mkdir, ukfs); 1113 } 1114