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