1 /* $NetBSD: ukfs.c,v 1.47 2009/12/13 20:52:36 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 static struct ukfs * 505 doukfsmount(const char *vfsname, const char *devpath, struct ukfs_part *part, 506 const char *mountpath, int mntflags, void *arg, size_t alen) 507 { 508 struct ukfs *fs = NULL; 509 int rv = 0, devfd = -1; 510 int mounted = 0; 511 int regged = 0; 512 513 pthread_spin_lock(&part->part_lck); 514 part->part_refcount++; 515 pthread_spin_unlock(&part->part_lck); 516 if (part != ukfs_part_na) { 517 if ((rv = process_diskdevice(devpath, part, 518 mntflags & MNT_RDONLY, &devfd)) != 0) 519 goto out; 520 } 521 522 fs = malloc(sizeof(struct ukfs)); 523 if (fs == NULL) { 524 rv = ENOMEM; 525 goto out; 526 } 527 memset(fs, 0, sizeof(struct ukfs)); 528 529 /* create our mountpoint. this is never removed. */ 530 if (builddirs(mountpath, 0777, rumpmkdir, NULL) == -1) { 531 if (errno != EEXIST) { 532 rv = errno; 533 goto out; 534 } 535 } 536 537 if (part != ukfs_part_na) { 538 /* LINTED */ 539 rv = rump_pub_etfs_register_withsize(devpath, devpath, 540 RUMP_ETFS_BLK, part->part_devoff, part->part_devsize); 541 if (rv) { 542 goto out; 543 } 544 regged = 1; 545 } 546 547 rv = rump_sys_mount(vfsname, mountpath, mntflags, arg, alen); 548 if (rv) { 549 rv = errno; 550 goto out; 551 } 552 mounted = 1; 553 rv = rump_pub_vfs_getmp(mountpath, &fs->ukfs_mp); 554 if (rv) { 555 goto out; 556 } 557 rv = rump_pub_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0); 558 if (rv) { 559 goto out; 560 } 561 562 if (regged) { 563 fs->ukfs_devpath = strdup(devpath); 564 } 565 fs->ukfs_mountpath = strdup(mountpath); 566 fs->ukfs_cdir = ukfs_getrvp(fs); 567 pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED); 568 fs->ukfs_devfd = devfd; 569 fs->ukfs_part = part; 570 assert(rv == 0); 571 572 out: 573 if (rv) { 574 if (fs) { 575 if (fs->ukfs_rvp) 576 rump_pub_vp_rele(fs->ukfs_rvp); 577 free(fs); 578 fs = NULL; 579 } 580 if (mounted) 581 rump_sys_unmount(mountpath, MNT_FORCE); 582 if (regged) 583 rump_pub_etfs_remove(devpath); 584 if (devfd != -1) { 585 unlockdev(devfd, part); 586 close(devfd); 587 } 588 ukfs_part_release(part); 589 errno = rv; 590 } 591 592 return fs; 593 } 594 595 struct ukfs * 596 ukfs_mount(const char *vfsname, const char *devpath, 597 const char *mountpath, int mntflags, void *arg, size_t alen) 598 { 599 600 return doukfsmount(vfsname, devpath, ukfs_part_na, 601 mountpath, mntflags, arg, alen); 602 } 603 604 struct ukfs * 605 ukfs_mount_disk(const char *vfsname, const char *devpath, 606 struct ukfs_part *part, const char *mountpath, int mntflags, 607 void *arg, size_t alen) 608 { 609 610 return doukfsmount(vfsname, devpath, part, 611 mountpath, mntflags, arg, alen); 612 } 613 614 int 615 ukfs_release(struct ukfs *fs, int flags) 616 { 617 618 if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) { 619 int rv, mntflag, error; 620 621 ukfs_chdir(fs, "/"); 622 mntflag = 0; 623 if (flags & UKFS_RELFLAG_FORCE) 624 mntflag = MNT_FORCE; 625 rump_pub_lwp_alloc_and_switch(nextpid(fs), 1); 626 rump_pub_vp_rele(fs->ukfs_rvp); 627 fs->ukfs_rvp = NULL; 628 rv = rump_sys_unmount(fs->ukfs_mountpath, mntflag); 629 if (rv == -1) { 630 error = errno; 631 rump_pub_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0); 632 rump_pub_lwp_release(rump_pub_lwp_curlwp()); 633 ukfs_chdir(fs, fs->ukfs_mountpath); 634 errno = error; 635 return -1; 636 } 637 rump_pub_lwp_release(rump_pub_lwp_curlwp()); 638 } 639 640 if (fs->ukfs_devpath) { 641 rump_pub_etfs_remove(fs->ukfs_devpath); 642 free(fs->ukfs_devpath); 643 } 644 free(fs->ukfs_mountpath); 645 646 pthread_spin_destroy(&fs->ukfs_spin); 647 if (fs->ukfs_devfd != -1) { 648 unlockdev(fs->ukfs_devfd, fs->ukfs_part); 649 close(fs->ukfs_devfd); 650 } 651 ukfs_part_release(fs->ukfs_part); 652 free(fs); 653 654 return 0; 655 } 656 657 void 658 ukfs_part_release(struct ukfs_part *part) 659 { 660 int release; 661 662 if (part != ukfs_part_none && part != ukfs_part_na) { 663 pthread_spin_lock(&part->part_lck); 664 release = --part->part_refcount == 0; 665 pthread_spin_unlock(&part->part_lck); 666 if (release) { 667 pthread_spin_destroy(&part->part_lck); 668 free(part); 669 } 670 } 671 } 672 673 #define STDCALL(ukfs, thecall) \ 674 int rv = 0; \ 675 \ 676 precall(ukfs); \ 677 rv = thecall; \ 678 postcall(ukfs); \ 679 return rv; 680 681 int 682 ukfs_opendir(struct ukfs *ukfs, const char *dirname, struct ukfs_dircookie **c) 683 { 684 struct vnode *vp; 685 int rv; 686 687 precall(ukfs); 688 rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname, 689 NULL, &vp, NULL); 690 postcall(ukfs); 691 692 if (rv == 0) { 693 RUMP_VOP_UNLOCK(vp, 0); 694 } else { 695 errno = rv; 696 rv = -1; 697 } 698 699 /*LINTED*/ 700 *c = (struct ukfs_dircookie *)vp; 701 return rv; 702 } 703 704 static int 705 getmydents(struct vnode *vp, off_t *off, uint8_t *buf, size_t bufsize) 706 { 707 struct uio *uio; 708 size_t resid; 709 int rv, eofflag; 710 kauth_cred_t cred; 711 712 uio = rump_pub_uio_setup(buf, bufsize, *off, RUMPUIO_READ); 713 cred = rump_pub_cred_suserget(); 714 rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL); 715 rump_pub_cred_put(cred); 716 RUMP_VOP_UNLOCK(vp, 0); 717 *off = rump_pub_uio_getoff(uio); 718 resid = rump_pub_uio_free(uio); 719 720 if (rv) { 721 errno = rv; 722 return -1; 723 } 724 725 /* LINTED: not totally correct return type, but follows syscall */ 726 return bufsize - resid; 727 } 728 729 /*ARGSUSED*/ 730 int 731 ukfs_getdents_cookie(struct ukfs *ukfs, struct ukfs_dircookie *c, off_t *off, 732 uint8_t *buf, size_t bufsize) 733 { 734 /*LINTED*/ 735 struct vnode *vp = (struct vnode *)c; 736 737 RUMP_VOP_LOCK(vp, RUMP_LK_SHARED); 738 return getmydents(vp, off, buf, bufsize); 739 } 740 741 int 742 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off, 743 uint8_t *buf, size_t bufsize) 744 { 745 struct vnode *vp; 746 int rv; 747 748 precall(ukfs); 749 rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname, 750 NULL, &vp, NULL); 751 postcall(ukfs); 752 if (rv) { 753 errno = rv; 754 return -1; 755 } 756 757 rv = getmydents(vp, off, buf, bufsize); 758 rump_pub_vp_rele(vp); 759 return rv; 760 } 761 762 /*ARGSUSED*/ 763 int 764 ukfs_closedir(struct ukfs *ukfs, struct ukfs_dircookie *c) 765 { 766 767 /*LINTED*/ 768 rump_pub_vp_rele((struct vnode *)c); 769 return 0; 770 } 771 772 int 773 ukfs_open(struct ukfs *ukfs, const char *filename, int flags) 774 { 775 int fd; 776 777 precall(ukfs); 778 fd = rump_sys_open(filename, flags, 0); 779 postcall(ukfs); 780 if (fd == -1) 781 return -1; 782 783 return fd; 784 } 785 786 ssize_t 787 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off, 788 uint8_t *buf, size_t bufsize) 789 { 790 int fd; 791 ssize_t xfer = -1; /* XXXgcc */ 792 793 precall(ukfs); 794 fd = rump_sys_open(filename, RUMP_O_RDONLY, 0); 795 if (fd == -1) 796 goto out; 797 798 xfer = rump_sys_pread(fd, buf, bufsize, off); 799 rump_sys_close(fd); 800 801 out: 802 postcall(ukfs); 803 if (fd == -1) { 804 return -1; 805 } 806 return xfer; 807 } 808 809 /*ARGSUSED*/ 810 ssize_t 811 ukfs_read_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen) 812 { 813 814 return rump_sys_pread(fd, buf, buflen, off); 815 } 816 817 ssize_t 818 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off, 819 uint8_t *buf, size_t bufsize) 820 { 821 int fd; 822 ssize_t xfer = -1; /* XXXgcc */ 823 824 precall(ukfs); 825 fd = rump_sys_open(filename, RUMP_O_WRONLY, 0); 826 if (fd == -1) 827 goto out; 828 829 /* write and commit */ 830 xfer = rump_sys_pwrite(fd, buf, bufsize, off); 831 if (xfer > 0) 832 rump_sys_fsync(fd); 833 834 rump_sys_close(fd); 835 836 out: 837 postcall(ukfs); 838 if (fd == -1) { 839 return -1; 840 } 841 return xfer; 842 } 843 844 /*ARGSUSED*/ 845 ssize_t 846 ukfs_write_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen, 847 int dosync) 848 { 849 ssize_t xfer; 850 851 xfer = rump_sys_pwrite(fd, buf, buflen, off); 852 if (xfer > 0 && dosync) 853 rump_sys_fsync(fd); 854 855 return xfer; 856 } 857 858 /*ARGSUSED*/ 859 int 860 ukfs_close(struct ukfs *ukfs, int fd) 861 { 862 863 rump_sys_close(fd); 864 return 0; 865 } 866 867 int 868 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode) 869 { 870 int fd; 871 872 precall(ukfs); 873 fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode); 874 if (fd == -1) 875 return -1; 876 rump_sys_close(fd); 877 878 postcall(ukfs); 879 return 0; 880 } 881 882 int 883 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev) 884 { 885 886 STDCALL(ukfs, rump_sys_mknod(path, mode, dev)); 887 } 888 889 int 890 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode) 891 { 892 893 STDCALL(ukfs, rump_sys_mkfifo(path, mode)); 894 } 895 896 int 897 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode) 898 { 899 900 STDCALL(ukfs, rump_sys_mkdir(filename, mode)); 901 } 902 903 int 904 ukfs_remove(struct ukfs *ukfs, const char *filename) 905 { 906 907 STDCALL(ukfs, rump_sys_unlink(filename)); 908 } 909 910 int 911 ukfs_rmdir(struct ukfs *ukfs, const char *filename) 912 { 913 914 STDCALL(ukfs, rump_sys_rmdir(filename)); 915 } 916 917 int 918 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create) 919 { 920 921 STDCALL(ukfs, rump_sys_link(filename, f_create)); 922 } 923 924 int 925 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname) 926 { 927 928 STDCALL(ukfs, rump_sys_symlink(filename, linkname)); 929 } 930 931 ssize_t 932 ukfs_readlink(struct ukfs *ukfs, const char *filename, 933 char *linkbuf, size_t buflen) 934 { 935 ssize_t rv; 936 937 precall(ukfs); 938 rv = rump_sys_readlink(filename, linkbuf, buflen); 939 postcall(ukfs); 940 return rv; 941 } 942 943 int 944 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to) 945 { 946 947 STDCALL(ukfs, rump_sys_rename(from, to)); 948 } 949 950 int 951 ukfs_chdir(struct ukfs *ukfs, const char *path) 952 { 953 struct vnode *newvp, *oldvp; 954 int rv; 955 956 precall(ukfs); 957 rv = rump_sys_chdir(path); 958 if (rv == -1) 959 goto out; 960 961 newvp = rump_pub_cdir_get(); 962 pthread_spin_lock(&ukfs->ukfs_spin); 963 oldvp = ukfs->ukfs_cdir; 964 ukfs->ukfs_cdir = newvp; 965 pthread_spin_unlock(&ukfs->ukfs_spin); 966 if (oldvp) 967 rump_pub_vp_rele(oldvp); 968 969 out: 970 postcall(ukfs); 971 return rv; 972 } 973 974 /* 975 * If we want to use post-time_t file systems on pre-time_t hosts, 976 * we must translate the stat structure. Since we don't currently 977 * have a general method for making compat calls in rump, special-case 978 * this one. 979 * 980 * Note that this does not allow making system calls to older rump 981 * kernels from newer hosts. 982 */ 983 #define VERS_TIMECHANGE 599000700 984 985 static int 986 needcompat(void) 987 { 988 989 #ifdef __NetBSD__ 990 /*LINTED*/ 991 return __NetBSD_Version__ < VERS_TIMECHANGE 992 && rump_pub_getversion() >= VERS_TIMECHANGE; 993 #else 994 return 0; 995 #endif 996 } 997 998 int 999 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) 1000 { 1001 int rv; 1002 1003 precall(ukfs); 1004 if (needcompat()) 1005 rv = rump_pub_sys___stat30(filename, file_stat); 1006 else 1007 rv = rump_sys_stat(filename, file_stat); 1008 postcall(ukfs); 1009 1010 return rv; 1011 } 1012 1013 int 1014 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) 1015 { 1016 int rv; 1017 1018 precall(ukfs); 1019 if (needcompat()) 1020 rv = rump_pub_sys___lstat30(filename, file_stat); 1021 else 1022 rv = rump_sys_lstat(filename, file_stat); 1023 postcall(ukfs); 1024 1025 return rv; 1026 } 1027 1028 int 1029 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode) 1030 { 1031 1032 STDCALL(ukfs, rump_sys_chmod(filename, mode)); 1033 } 1034 1035 int 1036 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode) 1037 { 1038 1039 STDCALL(ukfs, rump_sys_lchmod(filename, mode)); 1040 } 1041 1042 int 1043 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) 1044 { 1045 1046 STDCALL(ukfs, rump_sys_chown(filename, uid, gid)); 1047 } 1048 1049 int 1050 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) 1051 { 1052 1053 STDCALL(ukfs, rump_sys_lchown(filename, uid, gid)); 1054 } 1055 1056 int 1057 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags) 1058 { 1059 1060 STDCALL(ukfs, rump_sys_chflags(filename, flags)); 1061 } 1062 1063 int 1064 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags) 1065 { 1066 1067 STDCALL(ukfs, rump_sys_lchflags(filename, flags)); 1068 } 1069 1070 int 1071 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr) 1072 { 1073 1074 STDCALL(ukfs, rump_sys_utimes(filename, tptr)); 1075 } 1076 1077 int 1078 ukfs_lutimes(struct ukfs *ukfs, const char *filename, 1079 const struct timeval *tptr) 1080 { 1081 1082 STDCALL(ukfs, rump_sys_lutimes(filename, tptr)); 1083 } 1084 1085 /* 1086 * Dynamic module support 1087 */ 1088 1089 /* load one library */ 1090 1091 /* 1092 * XXX: the dlerror stuff isn't really threadsafe, but then again I 1093 * can't protect against other threads calling dl*() outside of ukfs, 1094 * so just live with it being flimsy 1095 */ 1096 int 1097 ukfs_modload(const char *fname) 1098 { 1099 void *handle; 1100 struct modinfo **mi; 1101 int error; 1102 1103 handle = dlopen(fname, RTLD_LAZY|RTLD_GLOBAL); 1104 if (handle == NULL) { 1105 const char *dlmsg = dlerror(); 1106 if (strstr(dlmsg, "Undefined symbol")) 1107 return 0; 1108 warnx("dlopen %s failed: %s\n", fname, dlmsg); 1109 /* XXXerrno */ 1110 return -1; 1111 } 1112 1113 mi = dlsym(handle, "__start_link_set_modules"); 1114 if (mi) { 1115 error = rump_pub_module_init(*mi, NULL); 1116 if (error) 1117 goto errclose; 1118 return 1; 1119 } 1120 error = EINVAL; 1121 1122 errclose: 1123 dlclose(handle); 1124 errno = error; 1125 return -1; 1126 } 1127 1128 struct loadfail { 1129 char *pname; 1130 1131 LIST_ENTRY(loadfail) entries; 1132 }; 1133 1134 #define RUMPFSMOD_PREFIX "librumpfs_" 1135 #define RUMPFSMOD_SUFFIX ".so" 1136 1137 int 1138 ukfs_modload_dir(const char *dir) 1139 { 1140 char nbuf[MAXPATHLEN+1], *p; 1141 struct dirent entry, *result; 1142 DIR *libdir; 1143 struct loadfail *lf, *nlf; 1144 int error, nloaded = 0, redo; 1145 LIST_HEAD(, loadfail) lfs; 1146 1147 libdir = opendir(dir); 1148 if (libdir == NULL) 1149 return -1; 1150 1151 LIST_INIT(&lfs); 1152 for (;;) { 1153 if ((error = readdir_r(libdir, &entry, &result)) != 0) 1154 break; 1155 if (!result) 1156 break; 1157 if (strncmp(result->d_name, RUMPFSMOD_PREFIX, 1158 strlen(RUMPFSMOD_PREFIX)) != 0) 1159 continue; 1160 if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL) 1161 || strlen(p) != strlen(RUMPFSMOD_SUFFIX)) 1162 continue; 1163 strlcpy(nbuf, dir, sizeof(nbuf)); 1164 strlcat(nbuf, "/", sizeof(nbuf)); 1165 strlcat(nbuf, result->d_name, sizeof(nbuf)); 1166 switch (ukfs_modload(nbuf)) { 1167 case 0: 1168 lf = malloc(sizeof(*lf)); 1169 if (lf == NULL) { 1170 error = ENOMEM; 1171 break; 1172 } 1173 lf->pname = strdup(nbuf); 1174 if (lf->pname == NULL) { 1175 free(lf); 1176 error = ENOMEM; 1177 break; 1178 } 1179 LIST_INSERT_HEAD(&lfs, lf, entries); 1180 break; 1181 case 1: 1182 nloaded++; 1183 break; 1184 default: 1185 /* ignore errors */ 1186 break; 1187 } 1188 } 1189 closedir(libdir); 1190 if (error && nloaded != 0) 1191 error = 0; 1192 1193 /* 1194 * El-cheapo dependency calculator. Just try to load the 1195 * modules n times in a loop 1196 */ 1197 for (redo = 1; redo;) { 1198 redo = 0; 1199 nlf = LIST_FIRST(&lfs); 1200 while ((lf = nlf) != NULL) { 1201 nlf = LIST_NEXT(lf, entries); 1202 if (ukfs_modload(lf->pname) == 1) { 1203 nloaded++; 1204 redo = 1; 1205 LIST_REMOVE(lf, entries); 1206 free(lf->pname); 1207 free(lf); 1208 } 1209 } 1210 } 1211 1212 while ((lf = LIST_FIRST(&lfs)) != NULL) { 1213 LIST_REMOVE(lf, entries); 1214 free(lf->pname); 1215 free(lf); 1216 } 1217 1218 if (error && nloaded == 0) { 1219 errno = error; 1220 return -1; 1221 } 1222 1223 return nloaded; 1224 } 1225 1226 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */ 1227 ssize_t 1228 ukfs_vfstypes(char *buf, size_t buflen) 1229 { 1230 int mib[3]; 1231 struct sysctlnode q, ans[128]; 1232 size_t alen; 1233 int i; 1234 1235 mib[0] = CTL_VFS; 1236 mib[1] = VFS_GENERIC; 1237 mib[2] = CTL_QUERY; 1238 alen = sizeof(ans); 1239 1240 memset(&q, 0, sizeof(q)); 1241 q.sysctl_flags = SYSCTL_VERSION; 1242 1243 if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) { 1244 return -1; 1245 } 1246 1247 for (i = 0; i < alen/sizeof(ans[0]); i++) 1248 if (strcmp("fstypes", ans[i].sysctl_name) == 0) 1249 break; 1250 if (i == alen/sizeof(ans[0])) { 1251 errno = ENXIO; 1252 return -1; 1253 } 1254 1255 mib[0] = CTL_VFS; 1256 mib[1] = VFS_GENERIC; 1257 mib[2] = ans[i].sysctl_num; 1258 1259 if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) { 1260 return -1; 1261 } 1262 1263 return buflen; 1264 } 1265 1266 /* 1267 * Utilities 1268 */ 1269 static int 1270 builddirs(const char *pathname, mode_t mode, 1271 int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *fs) 1272 { 1273 char *f1, *f2; 1274 int rv; 1275 mode_t mask; 1276 bool end; 1277 1278 /*ukfs_umask((mask = ukfs_umask(0)));*/ 1279 umask((mask = umask(0))); 1280 1281 f1 = f2 = strdup(pathname); 1282 if (f1 == NULL) { 1283 errno = ENOMEM; 1284 return -1; 1285 } 1286 1287 end = false; 1288 for (;;) { 1289 /* find next component */ 1290 f2 += strspn(f2, "/"); 1291 f2 += strcspn(f2, "/"); 1292 if (*f2 == '\0') 1293 end = true; 1294 else 1295 *f2 = '\0'; 1296 1297 rv = mkdirfn(fs, f1, mode & ~mask); 1298 if (errno == EEXIST) 1299 rv = 0; 1300 1301 if (rv == -1 || *f2 != '\0' || end) 1302 break; 1303 1304 *f2 = '/'; 1305 } 1306 1307 free(f1); 1308 1309 return rv; 1310 } 1311 1312 int 1313 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode) 1314 { 1315 1316 return builddirs(pathname, mode, ukfs_mkdir, ukfs); 1317 } 1318