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