1 /* $NetBSD: ukfs.c,v 1.4 2008/08/01 19:52:10 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2007, 2008 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 <pthread.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 #include <stdint.h> 59 60 #include <rump/ukfs.h> 61 62 #include <rump/rump.h> 63 #include <rump/rump_syscalls.h> 64 65 #define UKFS_MODE_DEFAULT 0555 66 67 struct ukfs { 68 struct mount *ukfs_mp; 69 struct vnode *ukfs_rvp; 70 71 pthread_spinlock_t ukfs_spin; 72 pid_t ukfs_nextpid; 73 struct vnode *ukfs_cdir; 74 }; 75 76 struct mount * 77 ukfs_getmp(struct ukfs *ukfs) 78 { 79 80 return ukfs->ukfs_mp; 81 } 82 83 struct vnode * 84 ukfs_getrvp(struct ukfs *ukfs) 85 { 86 struct vnode *rvp; 87 88 rvp = ukfs->ukfs_rvp; 89 rump_vp_incref(rvp); 90 91 return rvp; 92 } 93 94 static pid_t 95 nextpid(struct ukfs *ukfs) 96 { 97 pid_t npid; 98 99 pthread_spin_lock(&ukfs->ukfs_spin); 100 npid = ukfs->ukfs_nextpid++; 101 pthread_spin_unlock(&ukfs->ukfs_spin); 102 103 return npid; 104 } 105 106 static void 107 precall(struct ukfs *ukfs) 108 { 109 struct vnode *rvp, *cvp; 110 111 rump_setup_curlwp(nextpid(ukfs), 1, 1); 112 rvp = ukfs_getrvp(ukfs); 113 pthread_spin_lock(&ukfs->ukfs_spin); 114 cvp = ukfs->ukfs_cdir; 115 pthread_spin_unlock(&ukfs->ukfs_spin); 116 rump_rcvp_set(rvp, cvp); /* takes refs */ 117 rump_vp_rele(rvp); 118 } 119 120 static void 121 postcall(struct ukfs *ukfs) 122 { 123 struct vnode *rvp; 124 125 rvp = ukfs_getrvp(ukfs); 126 rump_rcvp_set(NULL, rvp); 127 rump_vp_rele(rvp); 128 rump_clear_curlwp(); 129 } 130 131 int 132 ukfs_init() 133 { 134 135 rump_init(); 136 137 return 0; 138 } 139 140 struct ukfs * 141 ukfs_mount(const char *vfsname, const char *devpath, const char *mountpath, 142 int mntflags, void *arg, size_t alen) 143 { 144 struct ukfs *fs = NULL; 145 struct vfsops *vfsops; 146 struct mount *mp; 147 int rv = 0; 148 149 vfsops = rump_vfs_getopsbyname(vfsname); 150 if (vfsops == NULL) { 151 rv = ENOENT; 152 goto out; 153 } 154 155 mp = rump_mnt_init(vfsops, mntflags); 156 157 fs = malloc(sizeof(struct ukfs)); 158 if (fs == NULL) { 159 rv = ENOMEM; 160 goto out; 161 } 162 memset(fs, 0, sizeof(struct ukfs)); 163 pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED); 164 165 rump_fakeblk_register(devpath); 166 rv = rump_mnt_mount(mp, mountpath, arg, &alen); 167 rump_fakeblk_deregister(devpath); 168 if (rv) { 169 warnx("VFS_MOUNT %d", rv); 170 goto out; 171 } 172 fs->ukfs_mp = mp; 173 174 rv = rump_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0); 175 fs->ukfs_cdir = ukfs_getrvp(fs); 176 177 out: 178 if (rv) { 179 if (fs && fs->ukfs_mp) 180 rump_mnt_destroy(fs->ukfs_mp); 181 if (fs) 182 free(fs); 183 errno = rv; 184 fs = NULL; 185 } 186 187 return fs; 188 } 189 190 void 191 ukfs_release(struct ukfs *fs, int flags) 192 { 193 int rv; 194 195 if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) { 196 rump_vp_rele(fs->ukfs_cdir); 197 rv = rump_vfs_sync(fs->ukfs_mp, 1, NULL); 198 rump_vp_recycle_nokidding(ukfs_getrvp(fs)); 199 rv |= rump_vfs_unmount(fs->ukfs_mp, 0); 200 assert(rv == 0); 201 } 202 203 rump_vfs_syncwait(fs->ukfs_mp); 204 rump_mnt_destroy(fs->ukfs_mp); 205 206 pthread_spin_destroy(&fs->ukfs_spin); 207 free(fs); 208 } 209 210 /* don't need vn_lock(), since we don't have VXLOCK */ 211 #define VLE(a) rump_vp_lock_exclusive(a) 212 #define VLS(a) rump_vp_lock_shared(a) 213 #define VUL(a) rump_vp_unlock(a) 214 #define AUL(a) assert(rump_vp_islocked(a) == 0) 215 216 #define STDCALL(ukfs, thecall) \ 217 int rv = 0; \ 218 \ 219 precall(ukfs); \ 220 thecall; \ 221 postcall(ukfs); \ 222 if (rv) { \ 223 errno = rv; \ 224 return -1; \ 225 } \ 226 return 0; 227 228 int 229 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off, 230 uint8_t *buf, size_t bufsize) 231 { 232 struct uio *uio; 233 struct vnode *vp; 234 size_t resid; 235 int rv, eofflag; 236 237 precall(ukfs); 238 rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname, 239 NULL, &vp, NULL); 240 postcall(ukfs); 241 if (rv) 242 goto out; 243 244 uio = rump_uio_setup(buf, bufsize, *off, RUMPUIO_READ); 245 rv = RUMP_VOP_READDIR(vp, uio, NULL, &eofflag, NULL, NULL); 246 VUL(vp); 247 *off = rump_uio_getoff(uio); 248 resid = rump_uio_free(uio); 249 rump_vp_rele(vp); 250 251 out: 252 if (rv) { 253 errno = rv; 254 return -1; 255 } 256 257 /* LINTED: not totally correct return type, but follows syscall */ 258 return bufsize - resid; 259 } 260 261 ssize_t 262 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off, 263 uint8_t *buf, size_t bufsize) 264 { 265 int fd, rv = 0, dummy; 266 ssize_t xfer = -1; /* XXXgcc */ 267 268 precall(ukfs); 269 fd = rump_sys_open(filename, RUMP_O_RDONLY, 0, &rv); 270 if (rv) 271 goto out; 272 273 xfer = rump_sys_pread(fd, buf, bufsize, 0, off, &rv); 274 rump_sys_close(fd, &dummy); 275 276 out: 277 postcall(ukfs); 278 if (rv) { 279 errno = rv; 280 return -1; 281 } 282 return xfer; 283 } 284 285 ssize_t 286 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off, 287 uint8_t *buf, size_t bufsize) 288 { 289 int fd, rv = 0, dummy; 290 ssize_t xfer = -1; /* XXXgcc */ 291 292 precall(ukfs); 293 fd = rump_sys_open(filename, RUMP_O_WRONLY, 0, &rv); 294 if (rv) 295 goto out; 296 297 /* write and commit */ 298 xfer = rump_sys_pwrite(fd, buf, bufsize, 0, off, &rv); 299 if (rv == 0) 300 rump_sys_fsync(fd, &dummy); 301 302 rump_sys_close(fd, &dummy); 303 304 out: 305 postcall(ukfs); 306 if (rv) { 307 errno = rv; 308 return -1; 309 } 310 return xfer; 311 } 312 313 int 314 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode) 315 { 316 int rv, fd, dummy; 317 318 precall(ukfs); 319 fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode, &rv); 320 rump_sys_close(fd, &dummy); 321 322 postcall(ukfs); 323 if (rv) { 324 errno = rv; 325 return -1; 326 } 327 return 0; 328 } 329 330 int 331 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev) 332 { 333 334 STDCALL(ukfs, rump_sys_mknod(path, mode, dev, &rv)); 335 } 336 337 int 338 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode) 339 { 340 341 STDCALL(ukfs, rump_sys_mkfifo(path, mode, &rv)); 342 } 343 344 int 345 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode) 346 { 347 348 STDCALL(ukfs, rump_sys_mkdir(filename, mode, &rv)); 349 } 350 351 int 352 ukfs_remove(struct ukfs *ukfs, const char *filename) 353 { 354 355 STDCALL(ukfs, rump_sys_unlink(filename, &rv)); 356 } 357 358 int 359 ukfs_rmdir(struct ukfs *ukfs, const char *filename) 360 { 361 362 STDCALL(ukfs, rump_sys_rmdir(filename, &rv)); 363 } 364 365 int 366 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create) 367 { 368 369 STDCALL(ukfs, rump_sys_link(filename, f_create, &rv)); 370 } 371 372 int 373 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname) 374 { 375 376 STDCALL(ukfs, rump_sys_symlink(filename, linkname, &rv)); 377 } 378 379 ssize_t 380 ukfs_readlink(struct ukfs *ukfs, const char *filename, 381 char *linkbuf, size_t buflen) 382 { 383 ssize_t rv; 384 int myerr = 0; 385 386 precall(ukfs); 387 rv = rump_sys_readlink(filename, linkbuf, buflen, &myerr); 388 postcall(ukfs); 389 if (myerr) { 390 errno = myerr; 391 return -1; 392 } 393 return rv; 394 } 395 396 int 397 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to) 398 { 399 400 STDCALL(ukfs, rump_sys_rename(from, to, &rv)); 401 } 402 403 int 404 ukfs_chdir(struct ukfs *ukfs, const char *path) 405 { 406 struct vnode *newvp, *oldvp; 407 int rv; 408 409 precall(ukfs); 410 rump_sys_chdir(path, &rv); 411 if (rv) 412 goto out; 413 414 newvp = rump_cdir_get(); 415 pthread_spin_lock(&ukfs->ukfs_spin); 416 oldvp = ukfs->ukfs_cdir; 417 ukfs->ukfs_cdir = newvp; 418 pthread_spin_unlock(&ukfs->ukfs_spin); 419 if (oldvp) 420 rump_vp_rele(oldvp); 421 422 out: 423 postcall(ukfs); 424 if (rv) { 425 errno = rv; 426 return -1; 427 } 428 return 0; 429 } 430 431 int 432 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) 433 { 434 435 STDCALL(ukfs, rump_sys___stat30(filename, file_stat, &rv)); 436 } 437 438 int 439 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat) 440 { 441 442 STDCALL(ukfs, rump_sys___lstat30(filename, file_stat, &rv)); 443 } 444 445 int 446 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode) 447 { 448 449 STDCALL(ukfs, rump_sys_chmod(filename, mode, &rv)); 450 } 451 452 int 453 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode) 454 { 455 456 STDCALL(ukfs, rump_sys_lchmod(filename, mode, &rv)); 457 } 458 459 int 460 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) 461 { 462 463 STDCALL(ukfs, rump_sys_chown(filename, uid, gid, &rv)); 464 } 465 466 int 467 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid) 468 { 469 470 STDCALL(ukfs, rump_sys_lchown(filename, uid, gid, &rv)); 471 } 472 473 int 474 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags) 475 { 476 477 STDCALL(ukfs, rump_sys_chflags(filename, flags, &rv)); 478 } 479 480 int 481 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags) 482 { 483 484 STDCALL(ukfs, rump_sys_lchflags(filename, flags, &rv)); 485 } 486 487 int 488 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr) 489 { 490 491 STDCALL(ukfs, rump_sys_utimes(filename, tptr, &rv)); 492 } 493 494 int 495 ukfs_lutimes(struct ukfs *ukfs, const char *filename, 496 const struct timeval *tptr) 497 { 498 499 STDCALL(ukfs, rump_sys_lutimes(filename, tptr, &rv)); 500 } 501 502 /* 503 * Dynamic module support 504 */ 505 506 /* load one library */ 507 508 /* 509 * XXX: the dlerror stuff isn't really threadsafe, but then again I 510 * can't protect against other threads calling dl*() outside of ukfs, 511 * so just live with it being flimsy 512 */ 513 #define UFSLIB "librumpfs_ufs.so" 514 int 515 ukfs_modload(const char *fname) 516 { 517 void *handle, *thesym; 518 struct stat sb; 519 const char *p; 520 int error; 521 522 if (stat(fname, &sb) == -1) 523 return -1; 524 525 handle = dlopen(fname, RTLD_GLOBAL); 526 if (handle == NULL) { 527 if (strstr(dlerror(), "Undefined symbol")) 528 return 0; 529 warnx("dlopen %s failed: %s\n", fname, dlerror()); 530 /* XXXerrno */ 531 return -1; 532 } 533 534 /* 535 * XXX: the ufs module is not loaded in the same fashion as the 536 * others. But we can't do dlclose() for it, since that would 537 * lead to not being able to load ffs/ext2fs/lfs. Hence hardcode 538 * and kludge around the issue for now. But this should really 539 * be fixed by fixing sys/ufs/ufs to be a kernel module. 540 */ 541 if ((p = strrchr(fname, '/')) != NULL) 542 p++; 543 else 544 p = fname; 545 if (strcmp(p, UFSLIB) == 0) 546 return 1; 547 548 thesym = dlsym(handle, "__start_link_set_modules"); 549 if (thesym) { 550 error = rump_vfs_load(thesym); 551 if (error) 552 goto errclose; 553 return 1; 554 } 555 error = EINVAL; 556 557 errclose: 558 dlclose(handle); 559 errno = error; 560 return -1; 561 } 562 563 struct loadfail { 564 char *pname; 565 566 LIST_ENTRY(loadfail) entries; 567 }; 568 569 #define RUMPFSMOD_PREFIX "librumpfs_" 570 #define RUMPFSMOD_SUFFIX ".so" 571 572 int 573 ukfs_modload_dir(const char *dir) 574 { 575 char nbuf[MAXPATHLEN+1], *p; 576 struct dirent entry, *result; 577 DIR *libdir; 578 struct loadfail *lf, *nlf; 579 int error, nloaded = 0, redo; 580 LIST_HEAD(, loadfail) lfs; 581 582 libdir = opendir(dir); 583 if (libdir == NULL) 584 return -1; 585 586 LIST_INIT(&lfs); 587 for (;;) { 588 if ((error = readdir_r(libdir, &entry, &result)) != 0) 589 break; 590 if (!result) 591 break; 592 if (strncmp(result->d_name, RUMPFSMOD_PREFIX, 593 strlen(RUMPFSMOD_PREFIX)) != 0) 594 continue; 595 if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL) 596 || strlen(p) != strlen(RUMPFSMOD_SUFFIX)) 597 continue; 598 strlcpy(nbuf, dir, sizeof(nbuf)); 599 strlcat(nbuf, "/", sizeof(nbuf)); 600 strlcat(nbuf, result->d_name, sizeof(nbuf)); 601 switch (ukfs_modload(nbuf)) { 602 case 0: 603 lf = malloc(sizeof(*lf)); 604 if (lf == NULL) { 605 error = ENOMEM; 606 break; 607 } 608 lf->pname = strdup(nbuf); 609 if (lf->pname == NULL) { 610 free(lf); 611 error = ENOMEM; 612 break; 613 } 614 LIST_INSERT_HEAD(&lfs, lf, entries); 615 break; 616 case 1: 617 nloaded++; 618 break; 619 default: 620 /* ignore errors */ 621 break; 622 } 623 } 624 closedir(libdir); 625 if (error && nloaded != 0) 626 error = 0; 627 628 /* 629 * El-cheapo dependency calculator. Just try to load the 630 * modules n times in a loop 631 */ 632 for (redo = 1; redo;) { 633 redo = 0; 634 nlf = LIST_FIRST(&lfs); 635 while ((lf = nlf) != NULL) { 636 nlf = LIST_NEXT(lf, entries); 637 if (ukfs_modload(lf->pname) == 1) { 638 nloaded++; 639 redo = 1; 640 LIST_REMOVE(lf, entries); 641 free(lf->pname); 642 free(lf); 643 } 644 } 645 } 646 647 while ((lf = LIST_FIRST(&lfs)) != NULL) { 648 LIST_REMOVE(lf, entries); 649 free(lf->pname); 650 free(lf); 651 } 652 653 if (error && nloaded == 0) { 654 errno = error; 655 return -1; 656 } 657 658 return nloaded; 659 } 660 661 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */ 662 ssize_t 663 ukfs_vfstypes(char *buf, size_t buflen) 664 { 665 int mib[3]; 666 struct sysctlnode q, ans[128]; 667 size_t alen; 668 int error, i; 669 670 mib[0] = CTL_VFS; 671 mib[1] = VFS_GENERIC; 672 mib[2] = CTL_QUERY; 673 alen = sizeof(ans); 674 675 memset(&q, 0, sizeof(q)); 676 q.sysctl_flags = SYSCTL_VERSION; 677 678 if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q), &error) == -1){ 679 errno = error; 680 return -1; 681 } 682 683 for (i = 0; i < alen/sizeof(ans[0]); i++) 684 if (strcmp("fstypes", ans[i].sysctl_name) == 0) 685 break; 686 if (i == alen/sizeof(ans[0])) { 687 errno = ENXIO; 688 return -1; 689 } 690 691 mib[0] = CTL_VFS; 692 mib[1] = VFS_GENERIC; 693 mib[2] = ans[i].sysctl_num; 694 695 if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0, &error) == -1) { 696 errno = error; 697 return -1; 698 } 699 700 return buflen; 701 } 702 703 /* 704 * Utilities 705 */ 706 int 707 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode) 708 { 709 char *f1, *f2; 710 int rv; 711 mode_t mask; 712 bool end; 713 714 /*ukfs_umask((mask = ukfs_umask(0)));*/ 715 umask((mask = umask(0))); 716 717 f1 = f2 = strdup(pathname); 718 if (f1 == NULL) { 719 errno = ENOMEM; 720 return -1; 721 } 722 723 end = false; 724 for (;;) { 725 /* find next component */ 726 f2 += strspn(f2, "/"); 727 f2 += strcspn(f2, "/"); 728 if (*f2 == '\0') 729 end = true; 730 else 731 *f2 = '\0'; 732 733 rv = ukfs_mkdir(ukfs, f1, mode & ~mask); 734 if (errno == EEXIST) 735 rv = 0; 736 737 if (rv == -1 || *f2 != '\0' || end) 738 break; 739 740 *f2 = '/'; 741 } 742 743 free(f1); 744 745 return rv; 746 } 747