1 /* $NetBSD: ukfs.c,v 1.52 2010/07/19 15:35:38 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2007, 2008, 2009 Antti Kantee. All Rights Reserved. 5 * 6 * Development of this software was supported by the 7 * Finnish Cultural Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * This library enables access to files systems directly without 33 * involving system calls. 34 */ 35 36 #ifdef __linux__ 37 #define _XOPEN_SOURCE 500 38 #define _BSD_SOURCE 39 #define _FILE_OFFSET_BITS 64 40 #endif 41 42 #include <sys/param.h> 43 #include <sys/queue.h> 44 #include <sys/stat.h> 45 #include <sys/sysctl.h> 46 #include <sys/mount.h> 47 48 #include <assert.h> 49 #include <dirent.h> 50 #include <dlfcn.h> 51 #include <err.h> 52 #include <errno.h> 53 #include <fcntl.h> 54 #include <pthread.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 59 #include <stdint.h> 60 61 #include <rump/ukfs.h> 62 63 #include <rump/rump.h> 64 #include <rump/rump_syscalls.h> 65 #include <rump/rumpuser.h> 66 67 #include "ukfs_int_disklabel.h" 68 69 #define UKFS_MODE_DEFAULT 0555 70 71 struct ukfs { 72 struct mount *ukfs_mp; 73 struct vnode *ukfs_rvp; 74 void *ukfs_specific; 75 76 pthread_spinlock_t ukfs_spin; 77 pid_t ukfs_nextpid; 78 struct vnode *ukfs_cdir; 79 int ukfs_devfd; 80 char *ukfs_devpath; 81 char *ukfs_mountpath; 82 struct ukfs_part *ukfs_part; 83 }; 84 85 static int builddirs(const char *, mode_t, 86 int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *); 87 88 struct mount * 89 ukfs_getmp(struct ukfs *ukfs) 90 { 91 92 return ukfs->ukfs_mp; 93 } 94 95 struct vnode * 96 ukfs_getrvp(struct ukfs *ukfs) 97 { 98 struct vnode *rvp; 99 100 rvp = ukfs->ukfs_rvp; 101 rump_pub_vp_incref(rvp); 102 103 return rvp; 104 } 105 106 void 107 ukfs_setspecific(struct ukfs *ukfs, void *priv) 108 { 109 110 ukfs->ukfs_specific = priv; 111 } 112 113 void * 114 ukfs_getspecific(struct ukfs *ukfs) 115 { 116 117 return ukfs->ukfs_specific; 118 } 119 120 #ifdef DONT_WANT_PTHREAD_LINKAGE 121 #define pthread_spin_lock(a) 122 #define pthread_spin_unlock(a) 123 #define pthread_spin_init(a,b) 124 #define pthread_spin_destroy(a) 125 #endif 126 127 static pid_t 128 nextpid(struct ukfs *ukfs) 129 { 130 pid_t npid; 131 132 pthread_spin_lock(&ukfs->ukfs_spin); 133 if (ukfs->ukfs_nextpid == 0) 134 ukfs->ukfs_nextpid++; 135 npid = ukfs->ukfs_nextpid++; 136 pthread_spin_unlock(&ukfs->ukfs_spin); 137 138 return npid; 139 } 140 141 static void 142 precall(struct ukfs *ukfs) 143 { 144 struct vnode *rvp, *cvp; 145 146 rump_pub_lwp_alloc_and_switch(nextpid(ukfs), 1); 147 rvp = ukfs_getrvp(ukfs); 148 pthread_spin_lock(&ukfs->ukfs_spin); 149 cvp = ukfs->ukfs_cdir; 150 pthread_spin_unlock(&ukfs->ukfs_spin); 151 rump_pub_rcvp_set(rvp, cvp); /* takes refs */ 152 rump_pub_vp_rele(rvp); 153 } 154 155 static void 156 postcall(struct ukfs *ukfs) 157 { 158 struct vnode *rvp; 159 160 rvp = ukfs_getrvp(ukfs); 161 rump_pub_rcvp_set(NULL, rvp); 162 rump_pub_vp_rele(rvp); 163 rump_pub_lwp_release(rump_pub_lwp_curlwp()); 164 } 165 166 struct ukfs_part { 167 pthread_spinlock_t part_lck; 168 int part_refcount; 169 170 int part_type; 171 char part_labelchar; 172 off_t part_devoff; 173 off_t part_devsize; 174 }; 175 176 enum ukfs_parttype { UKFS_PART_NONE, UKFS_PART_DISKLABEL, UKFS_PART_OFFSET }; 177 178 static struct ukfs_part ukfs__part_none = { 179 .part_type = UKFS_PART_NONE, 180 .part_devoff = 0, 181 .part_devsize = RUMP_ETFS_SIZE_ENDOFF, 182 }; 183 static struct ukfs_part ukfs__part_na; 184 struct ukfs_part *ukfs_part_none = &ukfs__part_none; 185 struct ukfs_part *ukfs_part_na = &ukfs__part_na; 186 187 #define PART2LOCKSIZE(len) ((len) == RUMP_ETFS_SIZE_ENDOFF ? 0 : (len)) 188 189 int 190 _ukfs_init(int version) 191 { 192 int rv; 193 194 if (version != UKFS_VERSION) { 195 printf("incompatible ukfs version, %d vs. %d\n", 196 version, UKFS_VERSION); 197 errno = EPROGMISMATCH; 198 return -1; 199 } 200 201 if ((rv = rump_init()) != 0) { 202 errno = rv; 203 return -1; 204 } 205 206 return 0; 207 } 208 209 /*ARGSUSED*/ 210 static int 211 rumpmkdir(struct ukfs *dummy, const char *path, mode_t mode) 212 { 213 214 return rump_sys_mkdir(path, mode); 215 } 216 217 int 218 ukfs_part_probe(char *devpath, struct ukfs_part **partp) 219 { 220 struct ukfs_part *part; 221 char *p; 222 int error = 0; 223 int devfd = -1; 224 225 if ((p = strstr(devpath, UKFS_PARTITION_SCANMAGIC)) != NULL) { 226 fprintf(stderr, "ukfs: %%PART is deprecated. use " 227 "%%DISKLABEL instead\n"); 228 errno = ENODEV; 229 return -1; 230 } 231 232 part = malloc(sizeof(*part)); 233 if (part == NULL) { 234 errno = ENOMEM; 235 return -1; 236 } 237 if (pthread_spin_init(&part->part_lck, PTHREAD_PROCESS_PRIVATE) == -1) { 238 error = errno; 239 free(part); 240 errno = error; 241 return -1; 242 } 243 part->part_type = UKFS_PART_NONE; 244 part->part_refcount = 1; 245 246 /* 247 * Check for magic in pathname: 248 * disklabel: /regularpath%DISKLABEL:labelchar%\0 249 * offsets: /regularpath%OFFSET:start,end%\0 250 */ 251 #define MAGICADJ_DISKLABEL(p, n) (p+sizeof(UKFS_DISKLABEL_SCANMAGIC)-1+n) 252 if ((p = strstr(devpath, UKFS_DISKLABEL_SCANMAGIC)) != NULL 253 && strlen(p) == UKFS_DISKLABEL_MAGICLEN 254 && *(MAGICADJ_DISKLABEL(p,1)) == '%') { 255 if (*(MAGICADJ_DISKLABEL(p,0)) >= 'a' && 256 *(MAGICADJ_DISKLABEL(p,0)) < 'a' + UKFS_MAXPARTITIONS) { 257 struct ukfs__disklabel dl; 258 struct ukfs__partition *pp; 259 char buf[65536]; 260 char labelchar = *(MAGICADJ_DISKLABEL(p,0)); 261 int partition = labelchar - 'a'; 262 263 *p = '\0'; 264 devfd = open(devpath, O_RDONLY); 265 if (devfd == -1) { 266 error = errno; 267 goto out; 268 } 269 270 /* Locate the disklabel and find the partition. */ 271 if (pread(devfd, buf, sizeof(buf), 0) == -1) { 272 error = errno; 273 goto out; 274 } 275 276 if (ukfs__disklabel_scan(&dl, buf, sizeof(buf)) != 0) { 277 error = ENOENT; 278 goto out; 279 } 280 281 if (dl.d_npartitions < partition) { 282 error = ENOENT; 283 goto out; 284 } 285 286 pp = &dl.d_partitions[partition]; 287 part->part_type = UKFS_PART_DISKLABEL; 288 part->part_labelchar = labelchar; 289 part->part_devoff = pp->p_offset << DEV_BSHIFT; 290 part->part_devsize = pp->p_size << 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 int rv = 0, devfd = -1; 534 int mounted = 0; 535 int regged = 0; 536 537 pthread_spin_lock(&part->part_lck); 538 part->part_refcount++; 539 pthread_spin_unlock(&part->part_lck); 540 if (part != ukfs_part_na) { 541 if ((rv = process_diskdevice(devpath, part, 542 mntflags & MNT_RDONLY, &devfd)) != 0) 543 goto out; 544 } 545 546 fs = malloc(sizeof(struct ukfs)); 547 if (fs == NULL) { 548 rv = ENOMEM; 549 goto out; 550 } 551 memset(fs, 0, sizeof(struct ukfs)); 552 553 /* create our mountpoint. this is never removed. */ 554 if (builddirs(mountpath, 0777, rumpmkdir, NULL) == -1) { 555 if (errno != EEXIST) { 556 rv = errno; 557 goto out; 558 } 559 } 560 561 if (part != ukfs_part_na) { 562 /* LINTED */ 563 rv = rump_pub_etfs_register_withsize(devpath, devpath, 564 RUMP_ETFS_BLK, part->part_devoff, part->part_devsize); 565 if (rv) { 566 goto out; 567 } 568 regged = 1; 569 } 570 571 /* 572 * MFS is special since mount(2) doesn't return. Hence, we 573 * create a thread here. Could fix mfs to return, but there's 574 * too much history for me to bother. 575 */ 576 if (strcmp(vfsname, MOUNT_MFS) == 0) { 577 pthread_t pt; 578 struct mountinfo mi; 579 int i; 580 581 mi.mi_vfsname = vfsname; 582 mi.mi_mountpath = mountpath; 583 mi.mi_mntflags = mntflags; 584 mi.mi_arg = arg; 585 mi.mi_alen = alen; 586 587 if (pthread_create(&pt, NULL, mfs_mounter, &mi) == -1) { 588 rv = errno; 589 goto out; 590 } 591 592 for (i = 0;i < 100000; i++) { 593 struct statvfs svfsb; 594 595 rv = rump_sys_statvfs1(mountpath, &svfsb, ST_WAIT); 596 if (rv == -1) { 597 rv = errno; 598 goto out; 599 } 600 601 if (strcmp(svfsb.f_mntonname, mountpath) == 0 && 602 strcmp(svfsb.f_fstypename, MOUNT_MFS) == 0) { 603 break; 604 } 605 usleep(1); 606 } 607 } else { 608 rv = rump_sys_mount(vfsname, mountpath, mntflags, arg, alen); 609 if (rv) { 610 rv = errno; 611 goto out; 612 } 613 } 614 615 mounted = 1; 616 rv = rump_pub_vfs_getmp(mountpath, &fs->ukfs_mp); 617 if (rv) { 618 goto out; 619 } 620 rv = rump_pub_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0); 621 if (rv) { 622 goto out; 623 } 624 625 if (regged) { 626 fs->ukfs_devpath = strdup(devpath); 627 } 628 fs->ukfs_mountpath = strdup(mountpath); 629 fs->ukfs_cdir = ukfs_getrvp(fs); 630 pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED); 631 fs->ukfs_devfd = devfd; 632 fs->ukfs_part = part; 633 assert(rv == 0); 634 635 out: 636 if (rv) { 637 if (fs) { 638 if (fs->ukfs_rvp) 639 rump_pub_vp_rele(fs->ukfs_rvp); 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 681 if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) { 682 int rv, mntflag, error; 683 684 ukfs_chdir(fs, "/"); 685 mntflag = 0; 686 if (flags & UKFS_RELFLAG_FORCE) 687 mntflag = MNT_FORCE; 688 rump_pub_lwp_alloc_and_switch(nextpid(fs), 1); 689 rump_pub_vp_rele(fs->ukfs_rvp); 690 fs->ukfs_rvp = NULL; 691 rv = rump_sys_unmount(fs->ukfs_mountpath, mntflag); 692 if (rv == -1) { 693 error = errno; 694 rump_pub_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0); 695 rump_pub_lwp_release(rump_pub_lwp_curlwp()); 696 ukfs_chdir(fs, fs->ukfs_mountpath); 697 errno = error; 698 return -1; 699 } 700 rump_pub_lwp_release(rump_pub_lwp_curlwp()); 701 } 702 703 if (fs->ukfs_devpath) { 704 rump_pub_etfs_remove(fs->ukfs_devpath); 705 free(fs->ukfs_devpath); 706 } 707 free(fs->ukfs_mountpath); 708 709 pthread_spin_destroy(&fs->ukfs_spin); 710 if (fs->ukfs_devfd != -1) { 711 unlockdev(fs->ukfs_devfd, fs->ukfs_part); 712 close(fs->ukfs_devfd); 713 } 714 ukfs_part_release(fs->ukfs_part); 715 free(fs); 716 717 return 0; 718 } 719 720 void 721 ukfs_part_release(struct ukfs_part *part) 722 { 723 int release; 724 725 if (part != ukfs_part_none && part != ukfs_part_na) { 726 pthread_spin_lock(&part->part_lck); 727 release = --part->part_refcount == 0; 728 pthread_spin_unlock(&part->part_lck); 729 if (release) { 730 pthread_spin_destroy(&part->part_lck); 731 free(part); 732 } 733 } 734 } 735 736 #define STDCALL(ukfs, thecall) \ 737 int rv = 0; \ 738 \ 739 precall(ukfs); \ 740 rv = thecall; \ 741 postcall(ukfs); \ 742 return rv; 743 744 int 745 ukfs_opendir(struct ukfs *ukfs, const char *dirname, struct ukfs_dircookie **c) 746 { 747 struct vnode *vp; 748 int rv; 749 750 precall(ukfs); 751 rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname, 752 NULL, &vp, NULL); 753 postcall(ukfs); 754 755 if (rv == 0) { 756 RUMP_VOP_UNLOCK(vp); 757 } else { 758 errno = rv; 759 rv = -1; 760 } 761 762 /*LINTED*/ 763 *c = (struct ukfs_dircookie *)vp; 764 return rv; 765 } 766 767 static int 768 getmydents(struct vnode *vp, off_t *off, uint8_t *buf, size_t bufsize) 769 { 770 struct uio *uio; 771 size_t resid; 772 int rv, eofflag; 773 struct kauth_cred *cred; 774 775 uio = rump_pub_uio_setup(buf, bufsize, *off, RUMPUIO_READ); 776 cred = rump_pub_cred_suserget(); 777 rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL); 778 rump_pub_cred_put(cred); 779 RUMP_VOP_UNLOCK(vp); 780 *off = rump_pub_uio_getoff(uio); 781 resid = rump_pub_uio_free(uio); 782 783 if (rv) { 784 errno = rv; 785 return -1; 786 } 787 788 /* LINTED: not totally correct return type, but follows syscall */ 789 return bufsize - resid; 790 } 791 792 /*ARGSUSED*/ 793 int 794 ukfs_getdents_cookie(struct ukfs *ukfs, struct ukfs_dircookie *c, off_t *off, 795 uint8_t *buf, size_t bufsize) 796 { 797 /*LINTED*/ 798 struct vnode *vp = (struct vnode *)c; 799 800 RUMP_VOP_LOCK(vp, RUMP_LK_SHARED); 801 return getmydents(vp, off, buf, bufsize); 802 } 803 804 int 805 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off, 806 uint8_t *buf, size_t bufsize) 807 { 808 struct vnode *vp; 809 int rv; 810 811 precall(ukfs); 812 rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname, 813 NULL, &vp, NULL); 814 postcall(ukfs); 815 if (rv) { 816 errno = rv; 817 return -1; 818 } 819 820 rv = getmydents(vp, off, buf, bufsize); 821 rump_pub_vp_rele(vp); 822 return rv; 823 } 824 825 /*ARGSUSED*/ 826 int 827 ukfs_closedir(struct ukfs *ukfs, struct ukfs_dircookie *c) 828 { 829 830 /*LINTED*/ 831 rump_pub_vp_rele((struct vnode *)c); 832 return 0; 833 } 834 835 int 836 ukfs_open(struct ukfs *ukfs, const char *filename, int flags) 837 { 838 int fd; 839 840 precall(ukfs); 841 fd = rump_sys_open(filename, flags, 0); 842 postcall(ukfs); 843 if (fd == -1) 844 return -1; 845 846 return fd; 847 } 848 849 ssize_t 850 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off, 851 uint8_t *buf, size_t bufsize) 852 { 853 int fd; 854 ssize_t xfer = -1; /* XXXgcc */ 855 856 precall(ukfs); 857 fd = rump_sys_open(filename, RUMP_O_RDONLY, 0); 858 if (fd == -1) 859 goto out; 860 861 xfer = rump_sys_pread(fd, buf, bufsize, off); 862 rump_sys_close(fd); 863 864 out: 865 postcall(ukfs); 866 if (fd == -1) { 867 return -1; 868 } 869 return xfer; 870 } 871 872 /*ARGSUSED*/ 873 ssize_t 874 ukfs_read_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen) 875 { 876 877 return rump_sys_pread(fd, buf, buflen, off); 878 } 879 880 ssize_t 881 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off, 882 uint8_t *buf, size_t bufsize) 883 { 884 int fd; 885 ssize_t xfer = -1; /* XXXgcc */ 886 887 precall(ukfs); 888 fd = rump_sys_open(filename, RUMP_O_WRONLY, 0); 889 if (fd == -1) 890 goto out; 891 892 /* write and commit */ 893 xfer = rump_sys_pwrite(fd, buf, bufsize, off); 894 if (xfer > 0) 895 rump_sys_fsync(fd); 896 897 rump_sys_close(fd); 898 899 out: 900 postcall(ukfs); 901 if (fd == -1) { 902 return -1; 903 } 904 return xfer; 905 } 906 907 /*ARGSUSED*/ 908 ssize_t 909 ukfs_write_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen, 910 int dosync) 911 { 912 ssize_t xfer; 913 914 xfer = rump_sys_pwrite(fd, buf, buflen, off); 915 if (xfer > 0 && dosync) 916 rump_sys_fsync(fd); 917 918 return xfer; 919 } 920 921 /*ARGSUSED*/ 922 int 923 ukfs_close(struct ukfs *ukfs, int fd) 924 { 925 926 rump_sys_close(fd); 927 return 0; 928 } 929 930 int 931 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode) 932 { 933 int fd; 934 935 precall(ukfs); 936 fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode); 937 if (fd == -1) 938 return -1; 939 rump_sys_close(fd); 940 941 postcall(ukfs); 942 return 0; 943 } 944 945 int 946 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev) 947 { 948 949 STDCALL(ukfs, rump_sys_mknod(path, mode, dev)); 950 } 951 952 int 953 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode) 954 { 955 956 STDCALL(ukfs, rump_sys_mkfifo(path, mode)); 957 } 958 959 int 960 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode) 961 { 962 963 STDCALL(ukfs, rump_sys_mkdir(filename, mode)); 964 } 965 966 int 967 ukfs_remove(struct ukfs *ukfs, const char *filename) 968 { 969 970 STDCALL(ukfs, rump_sys_unlink(filename)); 971 } 972 973 int 974 ukfs_rmdir(struct ukfs *ukfs, const char *filename) 975 { 976 977 STDCALL(ukfs, rump_sys_rmdir(filename)); 978 } 979 980 int 981 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create) 982 { 983 984 STDCALL(ukfs, rump_sys_link(filename, f_create)); 985 } 986 987 int 988 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname) 989 { 990 991 STDCALL(ukfs, rump_sys_symlink(filename, linkname)); 992 } 993 994 ssize_t 995 ukfs_readlink(struct ukfs *ukfs, const char *filename, 996 char *linkbuf, size_t buflen) 997 { 998 ssize_t rv; 999 1000 precall(ukfs); 1001 rv = rump_sys_readlink(filename, linkbuf, buflen); 1002 postcall(ukfs); 1003 return rv; 1004 } 1005 1006 int 1007 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to) 1008 { 1009 1010 STDCALL(ukfs, rump_sys_rename(from, to)); 1011 } 1012 1013 int 1014 ukfs_chdir(struct ukfs *ukfs, const char *path) 1015 { 1016 struct vnode *newvp, *oldvp; 1017 int rv; 1018 1019 precall(ukfs); 1020 rv = rump_sys_chdir(path); 1021 if (rv == -1) 1022 goto out; 1023 1024 newvp = rump_pub_cdir_get(); 1025 pthread_spin_lock(&ukfs->ukfs_spin); 1026 oldvp = ukfs->ukfs_cdir; 1027 ukfs->ukfs_cdir = newvp; 1028 pthread_spin_unlock(&ukfs->ukfs_spin); 1029 if (oldvp) 1030 rump_pub_vp_rele(oldvp); 1031 1032 out: 1033 postcall(ukfs); 1034 return rv; 1035 } 1036 1037 int 1038 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) 1039 { 1040 int rv; 1041 1042 precall(ukfs); 1043 rv = rump_sys_stat(filename, file_stat); 1044 postcall(ukfs); 1045 1046 return rv; 1047 } 1048 1049 int 1050 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) 1051 { 1052 int rv; 1053 1054 precall(ukfs); 1055 rv = rump_sys_lstat(filename, file_stat); 1056 postcall(ukfs); 1057 1058 return rv; 1059 } 1060 1061 int 1062 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode) 1063 { 1064 1065 STDCALL(ukfs, rump_sys_chmod(filename, mode)); 1066 } 1067 1068 int 1069 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode) 1070 { 1071 1072 STDCALL(ukfs, rump_sys_lchmod(filename, mode)); 1073 } 1074 1075 int 1076 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) 1077 { 1078 1079 STDCALL(ukfs, rump_sys_chown(filename, uid, gid)); 1080 } 1081 1082 int 1083 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) 1084 { 1085 1086 STDCALL(ukfs, rump_sys_lchown(filename, uid, gid)); 1087 } 1088 1089 int 1090 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags) 1091 { 1092 1093 STDCALL(ukfs, rump_sys_chflags(filename, flags)); 1094 } 1095 1096 int 1097 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags) 1098 { 1099 1100 STDCALL(ukfs, rump_sys_lchflags(filename, flags)); 1101 } 1102 1103 int 1104 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr) 1105 { 1106 1107 STDCALL(ukfs, rump_sys_utimes(filename, tptr)); 1108 } 1109 1110 int 1111 ukfs_lutimes(struct ukfs *ukfs, const char *filename, 1112 const struct timeval *tptr) 1113 { 1114 1115 STDCALL(ukfs, rump_sys_lutimes(filename, tptr)); 1116 } 1117 1118 /* 1119 * Dynamic module support 1120 */ 1121 1122 /* load one library */ 1123 1124 /* 1125 * XXX: the dlerror stuff isn't really threadsafe, but then again I 1126 * can't protect against other threads calling dl*() outside of ukfs, 1127 * so just live with it being flimsy 1128 */ 1129 int 1130 ukfs_modload(const char *fname) 1131 { 1132 void *handle; 1133 const struct modinfo *const *mi_start, *const *mi_end; 1134 int error; 1135 1136 handle = dlopen(fname, RTLD_LAZY|RTLD_GLOBAL); 1137 if (handle == NULL) { 1138 const char *dlmsg = dlerror(); 1139 if (strstr(dlmsg, "Undefined symbol")) 1140 return 0; 1141 warnx("dlopen %s failed: %s\n", fname, dlmsg); 1142 /* XXXerrno */ 1143 return -1; 1144 } 1145 1146 mi_start = dlsym(handle, "__start_link_set_modules"); 1147 mi_end = dlsym(handle, "__stop_link_set_modules"); 1148 if (mi_start && mi_end) { 1149 error = rump_pub_module_init(mi_start, 1150 (size_t)(mi_end-mi_start)); 1151 if (error) 1152 goto errclose; 1153 return 1; 1154 } 1155 error = EINVAL; 1156 1157 errclose: 1158 dlclose(handle); 1159 errno = error; 1160 return -1; 1161 } 1162 1163 struct loadfail { 1164 char *pname; 1165 1166 LIST_ENTRY(loadfail) entries; 1167 }; 1168 1169 #define RUMPFSMOD_PREFIX "librumpfs_" 1170 #define RUMPFSMOD_SUFFIX ".so" 1171 1172 int 1173 ukfs_modload_dir(const char *dir) 1174 { 1175 char nbuf[MAXPATHLEN+1], *p; 1176 struct dirent entry, *result; 1177 DIR *libdir; 1178 struct loadfail *lf, *nlf; 1179 int error, nloaded = 0, redo; 1180 LIST_HEAD(, loadfail) lfs; 1181 1182 libdir = opendir(dir); 1183 if (libdir == NULL) 1184 return -1; 1185 1186 LIST_INIT(&lfs); 1187 for (;;) { 1188 if ((error = readdir_r(libdir, &entry, &result)) != 0) 1189 break; 1190 if (!result) 1191 break; 1192 if (strncmp(result->d_name, RUMPFSMOD_PREFIX, 1193 strlen(RUMPFSMOD_PREFIX)) != 0) 1194 continue; 1195 if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL) 1196 || strlen(p) != strlen(RUMPFSMOD_SUFFIX)) 1197 continue; 1198 strlcpy(nbuf, dir, sizeof(nbuf)); 1199 strlcat(nbuf, "/", sizeof(nbuf)); 1200 strlcat(nbuf, result->d_name, sizeof(nbuf)); 1201 switch (ukfs_modload(nbuf)) { 1202 case 0: 1203 lf = malloc(sizeof(*lf)); 1204 if (lf == NULL) { 1205 error = ENOMEM; 1206 break; 1207 } 1208 lf->pname = strdup(nbuf); 1209 if (lf->pname == NULL) { 1210 free(lf); 1211 error = ENOMEM; 1212 break; 1213 } 1214 LIST_INSERT_HEAD(&lfs, lf, entries); 1215 break; 1216 case 1: 1217 nloaded++; 1218 break; 1219 default: 1220 /* ignore errors */ 1221 break; 1222 } 1223 } 1224 closedir(libdir); 1225 if (error && nloaded != 0) 1226 error = 0; 1227 1228 /* 1229 * El-cheapo dependency calculator. Just try to load the 1230 * modules n times in a loop 1231 */ 1232 for (redo = 1; redo;) { 1233 redo = 0; 1234 nlf = LIST_FIRST(&lfs); 1235 while ((lf = nlf) != NULL) { 1236 nlf = LIST_NEXT(lf, entries); 1237 if (ukfs_modload(lf->pname) == 1) { 1238 nloaded++; 1239 redo = 1; 1240 LIST_REMOVE(lf, entries); 1241 free(lf->pname); 1242 free(lf); 1243 } 1244 } 1245 } 1246 1247 while ((lf = LIST_FIRST(&lfs)) != NULL) { 1248 LIST_REMOVE(lf, entries); 1249 free(lf->pname); 1250 free(lf); 1251 } 1252 1253 if (error && nloaded == 0) { 1254 errno = error; 1255 return -1; 1256 } 1257 1258 return nloaded; 1259 } 1260 1261 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */ 1262 ssize_t 1263 ukfs_vfstypes(char *buf, size_t buflen) 1264 { 1265 int mib[3]; 1266 struct sysctlnode q, ans[128]; 1267 size_t alen; 1268 int i; 1269 1270 mib[0] = CTL_VFS; 1271 mib[1] = VFS_GENERIC; 1272 mib[2] = CTL_QUERY; 1273 alen = sizeof(ans); 1274 1275 memset(&q, 0, sizeof(q)); 1276 q.sysctl_flags = SYSCTL_VERSION; 1277 1278 if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) { 1279 return -1; 1280 } 1281 1282 for (i = 0; i < alen/sizeof(ans[0]); i++) 1283 if (strcmp("fstypes", ans[i].sysctl_name) == 0) 1284 break; 1285 if (i == alen/sizeof(ans[0])) { 1286 errno = ENXIO; 1287 return -1; 1288 } 1289 1290 mib[0] = CTL_VFS; 1291 mib[1] = VFS_GENERIC; 1292 mib[2] = ans[i].sysctl_num; 1293 1294 if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) { 1295 return -1; 1296 } 1297 1298 return buflen; 1299 } 1300 1301 /* 1302 * Utilities 1303 */ 1304 static int 1305 builddirs(const char *pathname, mode_t mode, 1306 int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *fs) 1307 { 1308 char *f1, *f2; 1309 int rv; 1310 mode_t mask; 1311 bool end; 1312 1313 /*ukfs_umask((mask = ukfs_umask(0)));*/ 1314 umask((mask = umask(0))); 1315 1316 f1 = f2 = strdup(pathname); 1317 if (f1 == NULL) { 1318 errno = ENOMEM; 1319 return -1; 1320 } 1321 1322 end = false; 1323 for (;;) { 1324 /* find next component */ 1325 f2 += strspn(f2, "/"); 1326 f2 += strcspn(f2, "/"); 1327 if (*f2 == '\0') 1328 end = true; 1329 else 1330 *f2 = '\0'; 1331 1332 rv = mkdirfn(fs, f1, mode & ~mask); 1333 if (errno == EEXIST) 1334 rv = 0; 1335 1336 if (rv == -1 || *f2 != '\0' || end) 1337 break; 1338 1339 *f2 = '/'; 1340 } 1341 1342 free(f1); 1343 1344 return rv; 1345 } 1346 1347 int 1348 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode) 1349 { 1350 1351 return builddirs(pathname, mode, ukfs_mkdir, ukfs); 1352 } 1353