1 /* $NetBSD: ukfs.c,v 1.61 2025/12/18 23:15:54 nia 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/endian.h> 44 #include <sys/queue.h> 45 #include <sys/stat.h> 46 #include <sys/sysctl.h> 47 #include <sys/mount.h> 48 49 #include <assert.h> 50 #include <dirent.h> 51 #include <dlfcn.h> 52 #include <err.h> 53 #include <errno.h> 54 #include <fcntl.h> 55 #include <pthread.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 #include <stdint.h> 61 62 #include <rump/ukfs.h> 63 64 #include <rump/rump.h> 65 #include <rump/rumpvnode_if.h> 66 #include <rump/rump_syscalls.h> 67 68 #include "ukfs_int_disklabel.h" 69 70 #define UKFS_MODE_DEFAULT 0555 71 72 struct ukfs { 73 pthread_spinlock_t ukfs_spin; 74 75 struct mount *ukfs_mp; 76 struct lwp *ukfs_lwp; 77 void *ukfs_specific; 78 79 int ukfs_devfd; 80 81 char *ukfs_devpath; 82 char *ukfs_mountpath; 83 char *ukfs_cwd; 84 85 struct ukfs_part *ukfs_part; 86 }; 87 88 static int builddirs(const char *, mode_t, 89 int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *); 90 91 struct mount * 92 ukfs_getmp(struct ukfs *ukfs) 93 { 94 95 return ukfs->ukfs_mp; 96 } 97 98 void 99 ukfs_setspecific(struct ukfs *ukfs, void *priv) 100 { 101 102 ukfs->ukfs_specific = priv; 103 } 104 105 void * 106 ukfs_getspecific(struct ukfs *ukfs) 107 { 108 109 return ukfs->ukfs_specific; 110 } 111 112 #ifdef DONT_WANT_PTHREAD_LINKAGE 113 #define pthread_spin_lock(a) 114 #define pthread_spin_unlock(a) 115 #define pthread_spin_init(a,b) 116 #define pthread_spin_destroy(a) 117 #endif 118 119 static int 120 precall(struct ukfs *ukfs, struct lwp **curlwp) 121 { 122 123 /* save previous. ensure start from pristine context */ 124 *curlwp = rump_pub_lwproc_curlwp(); 125 if (*curlwp) 126 rump_pub_lwproc_switch(ukfs->ukfs_lwp); 127 rump_pub_lwproc_rfork(RUMP_RFCFDG); 128 129 if (rump_sys_chroot(ukfs->ukfs_mountpath) == -1) 130 return errno; 131 if (rump_sys_chdir(ukfs->ukfs_cwd) == -1) 132 return errno; 133 134 return 0; 135 } 136 137 static void 138 postcall(struct lwp *curlwp) 139 { 140 141 rump_pub_lwproc_releaselwp(); 142 if (curlwp) 143 rump_pub_lwproc_switch(curlwp); 144 } 145 146 #define PRECALL() \ 147 struct lwp *ukfs_curlwp; \ 148 do { \ 149 int ukfs_rv; \ 150 if ((ukfs_rv = precall(ukfs, &ukfs_curlwp)) != 0) { \ 151 errno = ukfs_rv; \ 152 return -1; \ 153 } \ 154 } while (0) 155 156 #define POSTCALL() postcall(ukfs_curlwp); 157 158 struct ukfs_part { 159 pthread_spinlock_t part_lck; 160 int part_refcount; 161 162 int part_type; 163 char part_labelchar; 164 off_t part_devoff; 165 off_t part_devsize; 166 }; 167 168 enum ukfs_parttype { UKFS_PART_NONE, UKFS_PART_DISKLABEL, UKFS_PART_OFFSET }; 169 170 static struct ukfs_part ukfs__part_none = { 171 .part_type = UKFS_PART_NONE, 172 .part_devoff = 0, 173 .part_devsize = RUMP_ETFS_SIZE_ENDOFF, 174 }; 175 static struct ukfs_part ukfs__part_na; 176 struct ukfs_part *ukfs_part_none = &ukfs__part_none; 177 struct ukfs_part *ukfs_part_na = &ukfs__part_na; 178 179 #define PART2LOCKSIZE(len) ((len) == RUMP_ETFS_SIZE_ENDOFF ? 0 : (len)) 180 181 int 182 _ukfs_init(int version) 183 { 184 int rv; 185 186 if (version != UKFS_VERSION) { 187 errno = EPROGMISMATCH; 188 warn("incompatible ukfs version, %d vs. %d", 189 version, UKFS_VERSION); 190 return -1; 191 } 192 193 if ((rv = rump_init()) != 0) { 194 errno = rv; 195 return -1; 196 } 197 198 return 0; 199 } 200 201 /*ARGSUSED*/ 202 static int 203 rumpmkdir(struct ukfs *dummy, const char *path, mode_t mode) 204 { 205 206 return rump_sys_mkdir(path, mode); 207 } 208 209 int 210 ukfs_part_probe(char *devpath, struct ukfs_part **partp) 211 { 212 struct ukfs_part *part; 213 char *p; 214 int error = 0; 215 int devfd = -1; 216 217 if ((p = strstr(devpath, UKFS_PARTITION_SCANMAGIC)) != NULL) { 218 warnx("ukfs: %%PART is deprecated. use " 219 "%%DISKLABEL instead"); 220 errno = ENODEV; 221 return -1; 222 } 223 224 part = malloc(sizeof(*part)); 225 if (part == NULL) { 226 errno = ENOMEM; 227 return -1; 228 } 229 if (pthread_spin_init(&part->part_lck, PTHREAD_PROCESS_PRIVATE) == -1) { 230 error = errno; 231 free(part); 232 errno = error; 233 return -1; 234 } 235 part->part_type = UKFS_PART_NONE; 236 part->part_refcount = 1; 237 238 /* 239 * Check for magic in pathname: 240 * disklabel: /regularpath%DISKLABEL:labelchar%\0 241 * offsets: /regularpath%OFFSET:start,end%\0 242 */ 243 #define MAGICADJ_DISKLABEL(p, n) (p+sizeof(UKFS_DISKLABEL_SCANMAGIC)-1+n) 244 if ((p = strstr(devpath, UKFS_DISKLABEL_SCANMAGIC)) != NULL 245 && strlen(p) == UKFS_DISKLABEL_MAGICLEN 246 && *(MAGICADJ_DISKLABEL(p,1)) == '%') { 247 if (*(MAGICADJ_DISKLABEL(p,0)) >= 'a' && 248 *(MAGICADJ_DISKLABEL(p,0)) < 'a' + UKFS_MAXPARTITIONS) { 249 struct ukfs__disklabel dl; 250 struct ukfs__partition *pp; 251 int imswapped; 252 char buf[65536]; 253 char labelchar = *(MAGICADJ_DISKLABEL(p,0)); 254 int partition = labelchar - 'a'; 255 uint32_t poffset, psize; 256 257 *p = '\0'; 258 devfd = open(devpath, O_RDONLY); 259 if (devfd == -1) { 260 error = errno; 261 goto out; 262 } 263 264 /* Locate the disklabel and find the partition. */ 265 if (pread(devfd, buf, sizeof(buf), 0) == -1) { 266 error = errno; 267 goto out; 268 } 269 270 if (ukfs__disklabel_scan(&dl, &imswapped, 271 buf, sizeof(buf)) != 0) { 272 error = ENOENT; 273 goto out; 274 } 275 276 if (dl.d_npartitions < partition) { 277 error = ENOENT; 278 goto out; 279 } 280 281 pp = &dl.d_partitions[partition]; 282 part->part_type = UKFS_PART_DISKLABEL; 283 part->part_labelchar = labelchar; 284 if (imswapped) { 285 poffset = bswap32(pp->p_offset); 286 psize = bswap32(pp->p_size); 287 } else { 288 poffset = pp->p_offset; 289 psize = pp->p_size; 290 } 291 part->part_devoff = poffset << DEV_BSHIFT; 292 part->part_devsize = psize << DEV_BSHIFT; 293 } else { 294 error = EINVAL; 295 } 296 #define MAGICADJ_OFFSET(p, n) (p+sizeof(UKFS_OFFSET_SCANMAGIC)-1+n) 297 } else if (((p = strstr(devpath, UKFS_OFFSET_SCANMAGIC)) != NULL) 298 && (strlen(p) >= UKFS_OFFSET_MINLEN)) { 299 char *comma, *pers, *ep, *nptr; 300 u_quad_t val; 301 302 comma = strchr(p, ','); 303 if (comma == NULL) { 304 error = EINVAL; 305 goto out; 306 } 307 pers = strchr(comma, '%'); 308 if (pers == NULL) { 309 error = EINVAL; 310 goto out; 311 } 312 *comma = '\0'; 313 *pers = '\0'; 314 *p = '\0'; 315 316 nptr = MAGICADJ_OFFSET(p,0); 317 /* check if string is negative */ 318 if (*nptr == '-') { 319 error = ERANGE; 320 goto out; 321 } 322 val = strtouq(nptr, &ep, 10); 323 if (val == UQUAD_MAX) { 324 error = ERANGE; 325 goto out; 326 } 327 if (*ep != '\0') { 328 error = EADDRNOTAVAIL; /* creative ;) */ 329 goto out; 330 } 331 part->part_devoff = val; 332 333 /* omstart */ 334 335 nptr = comma+1; 336 /* check if string is negative */ 337 if (*nptr == '-') { 338 error = ERANGE; 339 goto out; 340 } 341 val = strtouq(nptr, &ep, 10); 342 if (val == UQUAD_MAX) { 343 error = ERANGE; 344 goto out; 345 } 346 if (*ep != '\0') { 347 error = EADDRNOTAVAIL; /* creative ;) */ 348 goto out; 349 } 350 part->part_devsize = val; 351 part->part_type = UKFS_PART_OFFSET; 352 } else { 353 ukfs_part_release(part); 354 part = ukfs_part_none; 355 } 356 357 out: 358 if (devfd != -1) 359 close(devfd); 360 if (error) { 361 free(part); 362 errno = error; 363 } else { 364 *partp = part; 365 } 366 367 return error ? -1 : 0; 368 } 369 370 int 371 ukfs_part_tostring(struct ukfs_part *part, char *str, size_t strsize) 372 { 373 int rv; 374 375 *str = '\0'; 376 /* "pseudo" values */ 377 if (part == ukfs_part_na) { 378 errno = EINVAL; 379 return -1; 380 } 381 if (part == ukfs_part_none) 382 return 0; 383 384 rv = 0; 385 switch (part->part_type) { 386 case UKFS_PART_NONE: 387 break; 388 389 case UKFS_PART_DISKLABEL: 390 snprintf(str, strsize, "%%DISKLABEL:%c%%",part->part_labelchar); 391 rv = 1; 392 break; 393 394 case UKFS_PART_OFFSET: 395 snprintf(str, strsize, "[%llu,%llu]", 396 (unsigned long long)part->part_devoff, 397 (unsigned long long)(part->part_devoff+part->part_devsize)); 398 rv = 1; 399 break; 400 } 401 402 return rv; 403 } 404 405 static void 406 unlockdev(int fd, struct ukfs_part *part) 407 { 408 struct flock flarg; 409 410 if (part == ukfs_part_na) 411 return; 412 413 memset(&flarg, 0, sizeof(flarg)); 414 flarg.l_type = F_UNLCK; 415 flarg.l_whence = SEEK_SET; 416 flarg.l_start = part->part_devoff; 417 flarg.l_len = PART2LOCKSIZE(part->part_devsize); 418 if (fcntl(fd, F_SETLK, &flarg) == -1) 419 warn("ukfs: cannot unlock device file"); 420 } 421 422 /* 423 * Open the disk file and flock it. Also, if we are operation on 424 * an embedded partition, find the partition offset and size from 425 * the disklabel. 426 * 427 * We hard-fail only in two cases: 428 * 1) we failed to get the partition info out (don't know what offset 429 * to mount from) 430 * 2) we failed to flock the source device (i.e. fcntl() fails, 431 * not e.g. open() before it) 432 * 433 * Otherwise we let the code proceed to mount and let the file system 434 * throw the proper error. The only questionable bit is that if we 435 * soft-fail before flock and mount does succeed... 436 * 437 * Returns: -1 error (errno reports error code) 438 * 0 success 439 * 440 * dfdp: -1 device is not open 441 * n device is open 442 */ 443 static int 444 process_diskdevice(const char *devpath, struct ukfs_part *part, int rdonly, 445 int *dfdp) 446 { 447 struct stat sb; 448 int rv = 0, devfd; 449 450 /* defaults */ 451 *dfdp = -1; 452 453 devfd = open(devpath, rdonly ? O_RDONLY : O_RDWR); 454 if (devfd == -1) { 455 rv = errno; 456 goto out; 457 } 458 459 if (fstat(devfd, &sb) == -1) { 460 rv = errno; 461 goto out; 462 } 463 464 /* 465 * We do this only for non-block device since the 466 * (NetBSD) kernel allows block device open only once. 467 * We also need to close the device for fairly obvious reasons. 468 */ 469 if (!S_ISBLK(sb.st_mode)) { 470 struct flock flarg; 471 472 memset(&flarg, 0, sizeof(flarg)); 473 flarg.l_type = rdonly ? F_RDLCK : F_WRLCK; 474 flarg.l_whence = SEEK_SET; 475 flarg.l_start = part->part_devoff; 476 flarg.l_len = PART2LOCKSIZE(part->part_devsize); 477 if (fcntl(devfd, F_SETLK, &flarg) == -1) { 478 pid_t holder; 479 int sverrno; 480 481 sverrno = errno; 482 if (fcntl(devfd, F_GETLK, &flarg) != 1) 483 holder = flarg.l_pid; 484 else 485 holder = -1; 486 warnx("ukfs_mount: cannot lock device. held by pid %d", 487 holder); 488 rv = sverrno; 489 goto out; 490 } 491 } else { 492 close(devfd); 493 devfd = -1; 494 } 495 *dfdp = devfd; 496 497 out: 498 if (rv) { 499 if (devfd != -1) 500 close(devfd); 501 } 502 503 return rv; 504 } 505 506 struct mountinfo { 507 const char *mi_vfsname; 508 const char *mi_mountpath; 509 int mi_mntflags; 510 void *mi_arg; 511 size_t mi_alen; 512 int *mi_error; 513 }; 514 static void * 515 mfs_mounter(void *arg) 516 { 517 struct mountinfo *mi = arg; 518 int rv; 519 520 rv = rump_sys_mount(mi->mi_vfsname, mi->mi_mountpath, mi->mi_mntflags, 521 mi->mi_arg, mi->mi_alen); 522 if (rv) { 523 warn("mfs mount failed. fix me."); 524 abort(); /* XXX */ 525 } 526 527 return NULL; 528 } 529 530 static struct ukfs * 531 doukfsmount(const char *vfsname, const char *devpath, struct ukfs_part *part, 532 const char *mountpath, int mntflags, void *arg, size_t alen) 533 { 534 struct ukfs *fs = NULL; 535 struct lwp *curlwp; 536 int rv = 0, devfd = -1; 537 int mounted = 0; 538 int regged = 0; 539 540 pthread_spin_lock(&part->part_lck); 541 part->part_refcount++; 542 pthread_spin_unlock(&part->part_lck); 543 if (part != ukfs_part_na) { 544 if ((rv = process_diskdevice(devpath, part, 545 mntflags & MNT_RDONLY, &devfd)) != 0) 546 goto out; 547 } 548 549 fs = malloc(sizeof(struct ukfs)); 550 if (fs == NULL) { 551 rv = ENOMEM; 552 goto out; 553 } 554 memset(fs, 0, sizeof(struct ukfs)); 555 556 /* create our mountpoint. this is never removed. */ 557 if (builddirs(mountpath, 0777, rumpmkdir, NULL) == -1) { 558 if (errno != EEXIST) { 559 rv = errno; 560 goto out; 561 } 562 } 563 564 if (part != ukfs_part_na) { 565 /* LINTED */ 566 rv = rump_pub_etfs_register_withsize(devpath, devpath, 567 RUMP_ETFS_BLK, part->part_devoff, part->part_devsize); 568 if (rv) { 569 goto out; 570 } 571 regged = 1; 572 } 573 574 /* 575 * MFS is special since mount(2) doesn't return. Hence, we 576 * create a thread here. Could fix mfs to return, but there's 577 * too much history for me to bother. 578 */ 579 if (strcmp(vfsname, MOUNT_MFS) == 0) { 580 pthread_t pt; 581 struct mountinfo mi; 582 int i; 583 584 mi.mi_vfsname = vfsname; 585 mi.mi_mountpath = mountpath; 586 mi.mi_mntflags = mntflags; 587 mi.mi_arg = arg; 588 mi.mi_alen = alen; 589 590 if (pthread_create(&pt, NULL, mfs_mounter, &mi) == -1) { 591 rv = errno; 592 goto out; 593 } 594 595 for (i = 0;i < 100000; i++) { 596 struct statvfs svfsb; 597 598 rv = rump_sys_statvfs1(mountpath, &svfsb, ST_WAIT); 599 if (rv == -1) { 600 rv = errno; 601 goto out; 602 } 603 604 if (strcmp(svfsb.f_mntonname, mountpath) == 0 && 605 strcmp(svfsb.f_fstypename, MOUNT_MFS) == 0) { 606 break; 607 } 608 usleep(1); 609 } 610 } else { 611 rv = rump_sys_mount(vfsname, mountpath, mntflags, arg, alen); 612 if (rv) { 613 rv = errno; 614 goto out; 615 } 616 } 617 618 mounted = 1; 619 rv = rump_pub_vfs_getmp(mountpath, &fs->ukfs_mp); 620 if (rv) { 621 goto out; 622 } 623 624 if (regged) { 625 fs->ukfs_devpath = strdup(devpath); 626 } 627 fs->ukfs_mountpath = strdup(mountpath); 628 pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED); 629 fs->ukfs_devfd = devfd; 630 fs->ukfs_part = part; 631 assert(rv == 0); 632 633 curlwp = rump_pub_lwproc_curlwp(); 634 rump_pub_lwproc_newlwp(0); 635 fs->ukfs_lwp = rump_pub_lwproc_curlwp(); 636 fs->ukfs_cwd = strdup("/"); 637 rump_pub_lwproc_switch(curlwp); 638 639 out: 640 if (rv) { 641 if (fs) { 642 free(fs); 643 fs = NULL; 644 } 645 if (mounted) 646 rump_sys_unmount(mountpath, MNT_FORCE); 647 if (regged) 648 rump_pub_etfs_remove(devpath); 649 if (devfd != -1) { 650 unlockdev(devfd, part); 651 close(devfd); 652 } 653 ukfs_part_release(part); 654 errno = rv; 655 } 656 657 return fs; 658 } 659 660 struct ukfs * 661 ukfs_mount(const char *vfsname, const char *devpath, 662 const char *mountpath, int mntflags, void *arg, size_t alen) 663 { 664 665 return doukfsmount(vfsname, devpath, ukfs_part_na, 666 mountpath, mntflags, arg, alen); 667 } 668 669 struct ukfs * 670 ukfs_mount_disk(const char *vfsname, const char *devpath, 671 struct ukfs_part *part, const char *mountpath, int mntflags, 672 void *arg, size_t alen) 673 { 674 675 return doukfsmount(vfsname, devpath, part, 676 mountpath, mntflags, arg, alen); 677 } 678 679 int 680 ukfs_release(struct ukfs *fs, int flags) 681 { 682 struct lwp *curlwp = rump_pub_lwproc_curlwp(); 683 684 /* get root lwp */ 685 rump_pub_lwproc_switch(fs->ukfs_lwp); 686 rump_pub_lwproc_rfork(RUMP_RFCFDG); 687 688 if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) { 689 int rv, mntflag, error; 690 691 mntflag = 0; 692 if (flags & UKFS_RELFLAG_FORCE) 693 mntflag = MNT_FORCE; 694 695 rv = rump_sys_unmount(fs->ukfs_mountpath, mntflag); 696 if (rv == -1) { 697 error = errno; 698 rump_pub_lwproc_releaselwp(); 699 if (curlwp) 700 rump_pub_lwproc_switch(curlwp); 701 errno = error; 702 return -1; 703 } 704 } 705 706 if (fs->ukfs_devpath) { 707 rump_pub_etfs_remove(fs->ukfs_devpath); 708 free(fs->ukfs_devpath); 709 } 710 free(fs->ukfs_mountpath); 711 free(fs->ukfs_cwd); 712 713 /* release this routine's lwp and ukfs base lwp */ 714 rump_pub_lwproc_releaselwp(); 715 rump_pub_lwproc_switch(fs->ukfs_lwp); 716 rump_pub_lwproc_releaselwp(); 717 718 pthread_spin_destroy(&fs->ukfs_spin); 719 if (fs->ukfs_devfd != -1) { 720 unlockdev(fs->ukfs_devfd, fs->ukfs_part); 721 close(fs->ukfs_devfd); 722 } 723 ukfs_part_release(fs->ukfs_part); 724 free(fs); 725 726 if (curlwp) 727 rump_pub_lwproc_switch(curlwp); 728 729 return 0; 730 } 731 732 void 733 ukfs_part_release(struct ukfs_part *part) 734 { 735 int release; 736 737 if (part != ukfs_part_none && part != ukfs_part_na) { 738 pthread_spin_lock(&part->part_lck); 739 release = --part->part_refcount == 0; 740 pthread_spin_unlock(&part->part_lck); 741 if (release) { 742 pthread_spin_destroy(&part->part_lck); 743 free(part); 744 } 745 } 746 } 747 748 #define STDCALL(ukfs, thecall) \ 749 int rv = 0; \ 750 \ 751 PRECALL(); \ 752 rv = thecall; \ 753 POSTCALL(); \ 754 return rv; 755 756 int 757 ukfs_opendir(struct ukfs *ukfs, const char *dirname, struct ukfs_dircookie **c) 758 { 759 struct vnode *vp; 760 int rv; 761 762 PRECALL(); 763 rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname, 764 NULL, &vp, NULL); 765 POSTCALL(); 766 767 if (rv == 0) { 768 RUMP_VOP_UNLOCK(vp); 769 } else { 770 errno = rv; 771 rv = -1; 772 } 773 774 /*LINTED*/ 775 *c = (struct ukfs_dircookie *)vp; 776 return rv; 777 } 778 779 static int 780 getmydents(struct vnode *vp, off_t *off, uint8_t *buf, size_t bufsize) 781 { 782 struct uio *uio; 783 size_t resid; 784 int rv, eofflag; 785 struct kauth_cred *cred; 786 787 uio = rump_pub_uio_setup(buf, bufsize, *off, RUMPUIO_READ); 788 cred = rump_pub_cred_create(0, 0, 0, NULL); 789 rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL); 790 rump_pub_cred_put(cred); 791 RUMP_VOP_UNLOCK(vp); 792 *off = rump_pub_uio_getoff(uio); 793 resid = rump_pub_uio_free(uio); 794 795 if (rv) { 796 errno = rv; 797 return -1; 798 } 799 800 /* LINTED: not totally correct return type, but follows syscall */ 801 return bufsize - resid; 802 } 803 804 /*ARGSUSED*/ 805 int 806 ukfs_getdents_cookie(struct ukfs *ukfs, struct ukfs_dircookie *c, off_t *off, 807 uint8_t *buf, size_t bufsize) 808 { 809 /*LINTED*/ 810 struct vnode *vp = (struct vnode *)c; 811 812 RUMP_VOP_LOCK(vp, RUMP_LK_SHARED); 813 return getmydents(vp, off, buf, bufsize); 814 } 815 816 int 817 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off, 818 uint8_t *buf, size_t bufsize) 819 { 820 struct vnode *vp; 821 int rv; 822 823 PRECALL(); 824 rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname, 825 NULL, &vp, NULL); 826 if (rv) { 827 POSTCALL(); 828 errno = rv; 829 return -1; 830 } 831 832 rv = getmydents(vp, off, buf, bufsize); 833 rump_pub_vp_rele(vp); 834 POSTCALL(); 835 return rv; 836 } 837 838 /*ARGSUSED*/ 839 int 840 ukfs_closedir(struct ukfs *ukfs, struct ukfs_dircookie *c) 841 { 842 843 /*LINTED*/ 844 rump_pub_vp_rele((struct vnode *)c); 845 return 0; 846 } 847 848 int 849 ukfs_open(struct ukfs *ukfs, const char *filename, int flags) 850 { 851 int fd; 852 853 PRECALL(); 854 fd = rump_sys_open(filename, flags, 0); 855 POSTCALL(); 856 if (fd == -1) 857 return -1; 858 859 return fd; 860 } 861 862 ssize_t 863 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off, 864 uint8_t *buf, size_t bufsize) 865 { 866 int fd; 867 ssize_t xfer = -1; /* XXXgcc */ 868 869 PRECALL(); 870 fd = rump_sys_open(filename, RUMP_O_RDONLY, 0); 871 if (fd == -1) 872 goto out; 873 874 xfer = rump_sys_pread(fd, buf, bufsize, off); 875 rump_sys_close(fd); 876 877 out: 878 POSTCALL(); 879 if (fd == -1) { 880 return -1; 881 } 882 return xfer; 883 } 884 885 /*ARGSUSED*/ 886 ssize_t 887 ukfs_read_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen) 888 { 889 890 return rump_sys_pread(fd, buf, buflen, off); 891 } 892 893 ssize_t 894 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off, 895 uint8_t *buf, size_t bufsize) 896 { 897 int fd; 898 ssize_t xfer = -1; /* XXXgcc */ 899 900 PRECALL(); 901 fd = rump_sys_open(filename, RUMP_O_WRONLY, 0); 902 if (fd == -1) 903 goto out; 904 905 /* write and commit */ 906 xfer = rump_sys_pwrite(fd, buf, bufsize, off); 907 if (xfer > 0) 908 rump_sys_fsync(fd); 909 910 rump_sys_close(fd); 911 912 out: 913 POSTCALL(); 914 if (fd == -1) { 915 return -1; 916 } 917 return xfer; 918 } 919 920 /*ARGSUSED*/ 921 ssize_t 922 ukfs_write_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen, 923 int dosync) 924 { 925 ssize_t xfer; 926 927 xfer = rump_sys_pwrite(fd, buf, buflen, off); 928 if (xfer > 0 && dosync) 929 rump_sys_fsync(fd); 930 931 return xfer; 932 } 933 934 /*ARGSUSED*/ 935 int 936 ukfs_close(struct ukfs *ukfs, int fd) 937 { 938 939 rump_sys_close(fd); 940 return 0; 941 } 942 943 int 944 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode) 945 { 946 int fd; 947 948 PRECALL(); 949 fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode); 950 if (fd == -1) 951 return -1; 952 rump_sys_close(fd); 953 954 POSTCALL(); 955 return 0; 956 } 957 958 int 959 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev) 960 { 961 962 STDCALL(ukfs, rump_sys_mknod(path, mode, dev)); 963 } 964 965 int 966 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode) 967 { 968 969 STDCALL(ukfs, rump_sys_mkfifo(path, mode)); 970 } 971 972 int 973 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode) 974 { 975 976 STDCALL(ukfs, rump_sys_mkdir(filename, mode)); 977 } 978 979 int 980 ukfs_remove(struct ukfs *ukfs, const char *filename) 981 { 982 983 STDCALL(ukfs, rump_sys_unlink(filename)); 984 } 985 986 int 987 ukfs_rmdir(struct ukfs *ukfs, const char *filename) 988 { 989 990 STDCALL(ukfs, rump_sys_rmdir(filename)); 991 } 992 993 int 994 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create) 995 { 996 997 STDCALL(ukfs, rump_sys_link(filename, f_create)); 998 } 999 1000 int 1001 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname) 1002 { 1003 1004 STDCALL(ukfs, rump_sys_symlink(filename, linkname)); 1005 } 1006 1007 ssize_t 1008 ukfs_readlink(struct ukfs *ukfs, const char *filename, 1009 char *linkbuf, size_t buflen) 1010 { 1011 ssize_t rv; 1012 1013 PRECALL(); 1014 rv = rump_sys_readlink(filename, linkbuf, buflen); 1015 POSTCALL(); 1016 return rv; 1017 } 1018 1019 int 1020 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to) 1021 { 1022 1023 STDCALL(ukfs, rump_sys_rename(from, to)); 1024 } 1025 1026 int 1027 ukfs_chdir(struct ukfs *ukfs, const char *path) 1028 { 1029 char *newpath, *oldpath; 1030 int rv; 1031 1032 PRECALL(); 1033 rv = rump_sys_chdir(path); 1034 if (rv == -1) 1035 goto out; 1036 1037 newpath = malloc(MAXPATHLEN); 1038 if (rump_sys___getcwd(newpath, MAXPATHLEN) == -1) { 1039 goto out; 1040 } 1041 1042 pthread_spin_lock(&ukfs->ukfs_spin); 1043 oldpath = ukfs->ukfs_cwd; 1044 ukfs->ukfs_cwd = newpath; 1045 pthread_spin_unlock(&ukfs->ukfs_spin); 1046 free(oldpath); 1047 1048 out: 1049 POSTCALL(); 1050 return rv; 1051 } 1052 1053 int 1054 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) 1055 { 1056 int rv; 1057 1058 PRECALL(); 1059 rv = rump_sys_stat(filename, file_stat); 1060 POSTCALL(); 1061 1062 return rv; 1063 } 1064 1065 int 1066 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) 1067 { 1068 int rv; 1069 1070 PRECALL(); 1071 rv = rump_sys_lstat(filename, file_stat); 1072 POSTCALL(); 1073 1074 return rv; 1075 } 1076 1077 int 1078 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode) 1079 { 1080 1081 STDCALL(ukfs, rump_sys_chmod(filename, mode)); 1082 } 1083 1084 int 1085 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode) 1086 { 1087 1088 STDCALL(ukfs, rump_sys_lchmod(filename, mode)); 1089 } 1090 1091 int 1092 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) 1093 { 1094 1095 STDCALL(ukfs, rump_sys_chown(filename, uid, gid)); 1096 } 1097 1098 int 1099 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) 1100 { 1101 1102 STDCALL(ukfs, rump_sys_lchown(filename, uid, gid)); 1103 } 1104 1105 int 1106 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags) 1107 { 1108 1109 STDCALL(ukfs, rump_sys_chflags(filename, flags)); 1110 } 1111 1112 int 1113 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags) 1114 { 1115 1116 STDCALL(ukfs, rump_sys_lchflags(filename, flags)); 1117 } 1118 1119 int 1120 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr) 1121 { 1122 1123 STDCALL(ukfs, rump_sys_utimes(filename, tptr)); 1124 } 1125 1126 int 1127 ukfs_lutimes(struct ukfs *ukfs, const char *filename, 1128 const struct timeval *tptr) 1129 { 1130 1131 STDCALL(ukfs, rump_sys_lutimes(filename, tptr)); 1132 } 1133 1134 /* 1135 * Dynamic module support 1136 */ 1137 1138 /* load one library */ 1139 1140 /* 1141 * XXX: the dlerror stuff isn't really threadsafe, but then again I 1142 * can't protect against other threads calling dl*() outside of ukfs, 1143 * so just live with it being flimsy 1144 */ 1145 int 1146 ukfs_modload(const char *fname) 1147 { 1148 void *handle; 1149 const struct modinfo *const *mi_start, *const *mi_end; 1150 int error; 1151 1152 handle = dlopen(fname, RTLD_LAZY|RTLD_GLOBAL); 1153 if (handle == NULL) { 1154 const char *dlmsg = dlerror(); 1155 if (strstr(dlmsg, "Undefined symbol")) 1156 return 0; 1157 warnx("dlopen %s failed: %s", fname, dlmsg); 1158 /* XXXerrno */ 1159 return -1; 1160 } 1161 1162 mi_start = dlsym(handle, "__start_link_set_modules"); 1163 mi_end = dlsym(handle, "__stop_link_set_modules"); 1164 if (mi_start && mi_end) { 1165 error = rump_pub_module_init(mi_start, 1166 (size_t)(mi_end-mi_start)); 1167 if (error) 1168 goto errclose; 1169 return 1; 1170 } 1171 error = EINVAL; 1172 1173 errclose: 1174 dlclose(handle); 1175 errno = error; 1176 return -1; 1177 } 1178 1179 struct loadfail { 1180 char *pname; 1181 1182 LIST_ENTRY(loadfail) entries; 1183 }; 1184 1185 #define RUMPFSMOD_PREFIX "librumpfs_" 1186 #define RUMPFSMOD_SUFFIX ".so" 1187 1188 int 1189 ukfs_modload_dir(const char *dir) 1190 { 1191 char nbuf[MAXPATHLEN+1], *p; 1192 struct dirent entry, *result; 1193 DIR *libdir; 1194 struct loadfail *lf, *nlf; 1195 int error, nloaded = 0, redo; 1196 LIST_HEAD(, loadfail) lfs; 1197 1198 libdir = opendir(dir); 1199 if (libdir == NULL) 1200 return -1; 1201 1202 LIST_INIT(&lfs); 1203 for (;;) { 1204 if ((error = readdir_r(libdir, &entry, &result)) != 0) 1205 break; 1206 if (!result) 1207 break; 1208 if (strncmp(result->d_name, RUMPFSMOD_PREFIX, 1209 strlen(RUMPFSMOD_PREFIX)) != 0) 1210 continue; 1211 if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL) 1212 || strlen(p) != strlen(RUMPFSMOD_SUFFIX)) 1213 continue; 1214 strlcpy(nbuf, dir, sizeof(nbuf)); 1215 strlcat(nbuf, "/", sizeof(nbuf)); 1216 strlcat(nbuf, result->d_name, sizeof(nbuf)); 1217 switch (ukfs_modload(nbuf)) { 1218 case 0: 1219 lf = malloc(sizeof(*lf)); 1220 if (lf == NULL) { 1221 error = ENOMEM; 1222 break; 1223 } 1224 lf->pname = strdup(nbuf); 1225 if (lf->pname == NULL) { 1226 free(lf); 1227 error = ENOMEM; 1228 break; 1229 } 1230 LIST_INSERT_HEAD(&lfs, lf, entries); 1231 break; 1232 case 1: 1233 nloaded++; 1234 break; 1235 default: 1236 /* ignore errors */ 1237 break; 1238 } 1239 } 1240 closedir(libdir); 1241 if (error && nloaded != 0) 1242 error = 0; 1243 1244 /* 1245 * El-cheapo dependency calculator. Just try to load the 1246 * modules n times in a loop 1247 */ 1248 for (redo = 1; redo;) { 1249 redo = 0; 1250 nlf = LIST_FIRST(&lfs); 1251 while ((lf = nlf) != NULL) { 1252 nlf = LIST_NEXT(lf, entries); 1253 if (ukfs_modload(lf->pname) == 1) { 1254 nloaded++; 1255 redo = 1; 1256 LIST_REMOVE(lf, entries); 1257 free(lf->pname); 1258 free(lf); 1259 } 1260 } 1261 } 1262 1263 while ((lf = LIST_FIRST(&lfs)) != NULL) { 1264 LIST_REMOVE(lf, entries); 1265 free(lf->pname); 1266 free(lf); 1267 } 1268 1269 if (error && nloaded == 0) { 1270 errno = error; 1271 return -1; 1272 } 1273 1274 return nloaded; 1275 } 1276 1277 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */ 1278 ssize_t 1279 ukfs_vfstypes(char *buf, size_t buflen) 1280 { 1281 int mib[3]; 1282 struct sysctlnode q, ans[128]; 1283 size_t alen; 1284 int i; 1285 1286 mib[0] = CTL_VFS; 1287 mib[1] = VFS_GENERIC; 1288 mib[2] = CTL_QUERY; 1289 alen = sizeof(ans); 1290 1291 memset(&q, 0, sizeof(q)); 1292 q.sysctl_flags = SYSCTL_VERSION; 1293 1294 if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) { 1295 return -1; 1296 } 1297 1298 for (i = 0; i < alen/sizeof(ans[0]); i++) 1299 if (strcmp("fstypes", ans[i].sysctl_name) == 0) 1300 break; 1301 if (i == alen/sizeof(ans[0])) { 1302 errno = ENXIO; 1303 return -1; 1304 } 1305 1306 mib[0] = CTL_VFS; 1307 mib[1] = VFS_GENERIC; 1308 mib[2] = ans[i].sysctl_num; 1309 1310 if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) { 1311 return -1; 1312 } 1313 1314 return buflen; 1315 } 1316 1317 /* 1318 * Utilities 1319 */ 1320 static int 1321 builddirs(const char *pathname, mode_t mode, 1322 int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *fs) 1323 { 1324 char *f1, *f2; 1325 int rv; 1326 mode_t mask; 1327 bool end; 1328 1329 /*ukfs_umask((mask = ukfs_umask(0)));*/ 1330 umask((mask = umask(0))); 1331 1332 f1 = f2 = strdup(pathname); 1333 if (f1 == NULL) { 1334 errno = ENOMEM; 1335 return -1; 1336 } 1337 1338 end = false; 1339 for (;;) { 1340 /* find next component */ 1341 f2 += strspn(f2, "/"); 1342 f2 += strcspn(f2, "/"); 1343 if (*f2 == '\0') 1344 end = true; 1345 else 1346 *f2 = '\0'; 1347 1348 rv = mkdirfn(fs, f1, mode & ~mask); 1349 if (errno == EEXIST) 1350 rv = 0; 1351 1352 if (rv == -1 || *f2 != '\0' || end) 1353 break; 1354 1355 *f2 = '/'; 1356 } 1357 1358 free(f1); 1359 1360 return rv; 1361 } 1362 1363 int 1364 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode) 1365 { 1366 1367 return builddirs(pathname, mode, ukfs_mkdir, ukfs); 1368 } 1369