1 /* $NetBSD: rumpfs.c,v 1.91 2011/02/02 15:58:09 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2009, 2010 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __KERNEL_RCSID(0, "$NetBSD: rumpfs.c,v 1.91 2011/02/02 15:58:09 pooka Exp $"); 30 31 #include <sys/param.h> 32 #include <sys/atomic.h> 33 #include <sys/buf.h> 34 #include <sys/dirent.h> 35 #include <sys/errno.h> 36 #include <sys/filedesc.h> 37 #include <sys/fcntl.h> 38 #include <sys/kauth.h> 39 #include <sys/malloc.h> 40 #include <sys/module.h> 41 #include <sys/mount.h> 42 #include <sys/namei.h> 43 #include <sys/lock.h> 44 #include <sys/lockf.h> 45 #include <sys/queue.h> 46 #include <sys/stat.h> 47 #include <sys/syscallargs.h> 48 #include <sys/vnode.h> 49 #include <sys/unistd.h> 50 51 #include <miscfs/fifofs/fifo.h> 52 #include <miscfs/specfs/specdev.h> 53 #include <miscfs/genfs/genfs.h> 54 #include <miscfs/genfs/genfs_node.h> 55 56 #include <uvm/uvm_extern.h> 57 58 #include <rump/rumpuser.h> 59 60 #include "rump_private.h" 61 #include "rump_vfs_private.h" 62 63 static int rump_vop_lookup(void *); 64 static int rump_vop_getattr(void *); 65 static int rump_vop_setattr(void *); 66 static int rump_vop_mkdir(void *); 67 static int rump_vop_rmdir(void *); 68 static int rump_vop_remove(void *); 69 static int rump_vop_mknod(void *); 70 static int rump_vop_create(void *); 71 static int rump_vop_inactive(void *); 72 static int rump_vop_reclaim(void *); 73 static int rump_vop_success(void *); 74 static int rump_vop_readdir(void *); 75 static int rump_vop_spec(void *); 76 static int rump_vop_read(void *); 77 static int rump_vop_write(void *); 78 static int rump_vop_open(void *); 79 static int rump_vop_symlink(void *); 80 static int rump_vop_readlink(void *); 81 static int rump_vop_whiteout(void *); 82 static int rump_vop_pathconf(void *); 83 static int rump_vop_bmap(void *); 84 static int rump_vop_strategy(void *); 85 static int rump_vop_advlock(void *); 86 static int rump_vop_access(void *); 87 88 int (**fifo_vnodeop_p)(void *); 89 const struct vnodeopv_entry_desc fifo_vnodeop_entries[] = { 90 { &vop_default_desc, vn_default_error }, 91 { NULL, NULL } 92 }; 93 const struct vnodeopv_desc fifo_vnodeop_opv_desc = 94 { &fifo_vnodeop_p, fifo_vnodeop_entries }; 95 96 int (**rump_vnodeop_p)(void *); 97 const struct vnodeopv_entry_desc rump_vnodeop_entries[] = { 98 { &vop_default_desc, vn_default_error }, 99 { &vop_lookup_desc, rump_vop_lookup }, 100 { &vop_getattr_desc, rump_vop_getattr }, 101 { &vop_setattr_desc, rump_vop_setattr }, 102 { &vop_mkdir_desc, rump_vop_mkdir }, 103 { &vop_rmdir_desc, rump_vop_rmdir }, 104 { &vop_remove_desc, rump_vop_remove }, 105 { &vop_mknod_desc, rump_vop_mknod }, 106 { &vop_create_desc, rump_vop_create }, 107 { &vop_symlink_desc, rump_vop_symlink }, 108 { &vop_readlink_desc, rump_vop_readlink }, 109 { &vop_access_desc, rump_vop_access }, 110 { &vop_readdir_desc, rump_vop_readdir }, 111 { &vop_read_desc, rump_vop_read }, 112 { &vop_write_desc, rump_vop_write }, 113 { &vop_open_desc, rump_vop_open }, 114 { &vop_close_desc, genfs_nullop }, 115 { &vop_seek_desc, genfs_seek }, 116 { &vop_getpages_desc, genfs_getpages }, 117 { &vop_putpages_desc, genfs_putpages }, 118 { &vop_whiteout_desc, rump_vop_whiteout }, 119 { &vop_fsync_desc, rump_vop_success }, 120 { &vop_lock_desc, genfs_lock }, 121 { &vop_unlock_desc, genfs_unlock }, 122 { &vop_islocked_desc, genfs_islocked }, 123 { &vop_inactive_desc, rump_vop_inactive }, 124 { &vop_reclaim_desc, rump_vop_reclaim }, 125 { &vop_link_desc, genfs_eopnotsupp }, 126 { &vop_pathconf_desc, rump_vop_pathconf }, 127 { &vop_bmap_desc, rump_vop_bmap }, 128 { &vop_strategy_desc, rump_vop_strategy }, 129 { &vop_advlock_desc, rump_vop_advlock }, 130 { NULL, NULL } 131 }; 132 const struct vnodeopv_desc rump_vnodeop_opv_desc = 133 { &rump_vnodeop_p, rump_vnodeop_entries }; 134 135 int (**rump_specop_p)(void *); 136 const struct vnodeopv_entry_desc rump_specop_entries[] = { 137 { &vop_default_desc, rump_vop_spec }, 138 { NULL, NULL } 139 }; 140 const struct vnodeopv_desc rump_specop_opv_desc = 141 { &rump_specop_p, rump_specop_entries }; 142 143 const struct vnodeopv_desc * const rump_opv_descs[] = { 144 &rump_vnodeop_opv_desc, 145 &rump_specop_opv_desc, 146 NULL 147 }; 148 149 #define RUMPFS_WHITEOUT ((void *)-1) 150 #define RDENT_ISWHITEOUT(rdp) (rdp->rd_node == RUMPFS_WHITEOUT) 151 struct rumpfs_dent { 152 char *rd_name; 153 int rd_namelen; 154 struct rumpfs_node *rd_node; 155 156 LIST_ENTRY(rumpfs_dent) rd_entries; 157 }; 158 159 struct genfs_ops rumpfs_genfsops = { 160 .gop_size = genfs_size, 161 .gop_write = genfs_gop_write, 162 163 /* optional */ 164 .gop_alloc = NULL, 165 .gop_markupdate = NULL, 166 }; 167 168 struct rumpfs_node { 169 struct genfs_node rn_gn; 170 struct vattr rn_va; 171 struct vnode *rn_vp; 172 char *rn_hostpath; 173 int rn_flags; 174 struct lockf *rn_lockf; 175 176 union { 177 struct { /* VREG */ 178 int readfd; 179 int writefd; 180 uint64_t offset; 181 } reg; 182 struct { 183 void *data; 184 size_t dlen; 185 } reg_noet; 186 struct { /* VDIR */ 187 LIST_HEAD(, rumpfs_dent) dents; 188 struct rumpfs_node *parent; 189 int flags; 190 } dir; 191 struct { 192 char *target; 193 size_t len; 194 } link; 195 } rn_u; 196 }; 197 #define rn_readfd rn_u.reg.readfd 198 #define rn_writefd rn_u.reg.writefd 199 #define rn_offset rn_u.reg.offset 200 #define rn_data rn_u.reg_noet.data 201 #define rn_dlen rn_u.reg_noet.dlen 202 #define rn_dir rn_u.dir.dents 203 #define rn_parent rn_u.dir.parent 204 #define rn_linktarg rn_u.link.target 205 #define rn_linklen rn_u.link.len 206 207 #define RUMPNODE_CANRECLAIM 0x01 208 #define RUMPNODE_DIR_ET 0x02 209 #define RUMPNODE_DIR_ETSUBS 0x04 210 #define RUMPNODE_ET_PHONE_HOST 0x10 211 212 struct rumpfs_mount { 213 struct vnode *rfsmp_rvp; 214 }; 215 216 #define INO_WHITEOUT 1 217 static int lastino = 2; 218 static kmutex_t reclock; 219 220 static struct rumpfs_node *makeprivate(enum vtype, dev_t, off_t, bool); 221 222 /* 223 * Extra Terrestrial stuff. We map a given key (pathname) to a file on 224 * the host FS. ET phones home only from the root node of rumpfs. 225 * 226 * When an etfs node is removed, a vnode potentially behind it is not 227 * immediately recycled. 228 */ 229 230 struct etfs { 231 char et_key[MAXPATHLEN]; 232 size_t et_keylen; 233 bool et_prefixkey; 234 bool et_removing; 235 devminor_t et_blkmin; 236 237 LIST_ENTRY(etfs) et_entries; 238 239 struct rumpfs_node *et_rn; 240 }; 241 static kmutex_t etfs_lock; 242 static LIST_HEAD(, etfs) etfs_list = LIST_HEAD_INITIALIZER(etfs_list); 243 244 static enum vtype 245 ettype_to_vtype(enum rump_etfs_type et) 246 { 247 enum vtype vt; 248 249 switch (et) { 250 case RUMP_ETFS_REG: 251 vt = VREG; 252 break; 253 case RUMP_ETFS_BLK: 254 vt = VBLK; 255 break; 256 case RUMP_ETFS_CHR: 257 vt = VCHR; 258 break; 259 case RUMP_ETFS_DIR: 260 vt = VDIR; 261 break; 262 case RUMP_ETFS_DIR_SUBDIRS: 263 vt = VDIR; 264 break; 265 default: 266 panic("invalid et type: %d", et); 267 } 268 269 return vt; 270 } 271 272 static enum vtype 273 hft_to_vtype(int hft) 274 { 275 enum vtype vt; 276 277 switch (hft) { 278 case RUMPUSER_FT_OTHER: 279 vt = VNON; 280 break; 281 case RUMPUSER_FT_DIR: 282 vt = VDIR; 283 break; 284 case RUMPUSER_FT_REG: 285 vt = VREG; 286 break; 287 case RUMPUSER_FT_BLK: 288 vt = VBLK; 289 break; 290 case RUMPUSER_FT_CHR: 291 vt = VCHR; 292 break; 293 default: 294 vt = VNON; 295 break; 296 } 297 298 return vt; 299 } 300 301 static bool 302 etfs_find(const char *key, struct etfs **etp, bool forceprefix) 303 { 304 struct etfs *et; 305 size_t keylen = strlen(key); 306 307 KASSERT(mutex_owned(&etfs_lock)); 308 309 LIST_FOREACH(et, &etfs_list, et_entries) { 310 if ((keylen == et->et_keylen || et->et_prefixkey || forceprefix) 311 && strncmp(key, et->et_key, et->et_keylen) == 0) { 312 if (etp) 313 *etp = et; 314 return true; 315 } 316 } 317 318 return false; 319 } 320 321 #define REGDIR(ftype) \ 322 ((ftype) == RUMP_ETFS_DIR || (ftype) == RUMP_ETFS_DIR_SUBDIRS) 323 static int 324 doregister(const char *key, const char *hostpath, 325 enum rump_etfs_type ftype, uint64_t begin, uint64_t size) 326 { 327 char buf[9]; 328 struct etfs *et; 329 struct rumpfs_node *rn; 330 uint64_t fsize; 331 dev_t rdev = NODEV; 332 devminor_t dmin = -1; 333 int hft, error; 334 335 if (key[0] != '/') { 336 return EINVAL; 337 } 338 while (key[0] == '/') { 339 key++; 340 } 341 342 if (rumpuser_getfileinfo(hostpath, &fsize, &hft, &error)) 343 return error; 344 345 /* etfs directory requires a directory on the host */ 346 if (REGDIR(ftype)) { 347 if (hft != RUMPUSER_FT_DIR) 348 return ENOTDIR; 349 if (begin != 0) 350 return EISDIR; 351 if (size != RUMP_ETFS_SIZE_ENDOFF) 352 return EISDIR; 353 size = fsize; 354 } else { 355 if (begin > fsize) 356 return EINVAL; 357 if (size == RUMP_ETFS_SIZE_ENDOFF) 358 size = fsize - begin; 359 if (begin + size > fsize) 360 return EINVAL; 361 } 362 363 if (ftype == RUMP_ETFS_BLK || ftype == RUMP_ETFS_CHR) { 364 error = rumpblk_register(hostpath, &dmin, begin, size); 365 if (error != 0) { 366 return error; 367 } 368 rdev = makedev(RUMPBLK_DEVMAJOR, dmin); 369 } 370 371 et = kmem_alloc(sizeof(*et), KM_SLEEP); 372 strcpy(et->et_key, key); 373 et->et_keylen = strlen(et->et_key); 374 et->et_rn = rn = makeprivate(ettype_to_vtype(ftype), rdev, size, true); 375 et->et_removing = false; 376 et->et_blkmin = dmin; 377 378 rn->rn_flags |= RUMPNODE_ET_PHONE_HOST; 379 380 if (ftype == RUMP_ETFS_REG || REGDIR(ftype) || et->et_blkmin != -1) { 381 size_t len = strlen(hostpath)+1; 382 383 rn->rn_hostpath = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 384 memcpy(rn->rn_hostpath, hostpath, len); 385 rn->rn_offset = begin; 386 } 387 388 if (REGDIR(ftype)) { 389 rn->rn_flags |= RUMPNODE_DIR_ET; 390 et->et_prefixkey = true; 391 } else { 392 et->et_prefixkey = false; 393 } 394 395 if (ftype == RUMP_ETFS_DIR_SUBDIRS) 396 rn->rn_flags |= RUMPNODE_DIR_ETSUBS; 397 398 mutex_enter(&etfs_lock); 399 if (etfs_find(key, NULL, REGDIR(ftype))) { 400 mutex_exit(&etfs_lock); 401 if (et->et_blkmin != -1) 402 rumpblk_deregister(hostpath); 403 if (et->et_rn->rn_hostpath != NULL) 404 free(et->et_rn->rn_hostpath, M_TEMP); 405 kmem_free(et->et_rn, sizeof(*et->et_rn)); 406 kmem_free(et, sizeof(*et)); 407 return EEXIST; 408 } 409 LIST_INSERT_HEAD(&etfs_list, et, et_entries); 410 mutex_exit(&etfs_lock); 411 412 if (ftype == RUMP_ETFS_BLK) { 413 format_bytes(buf, sizeof(buf), size); 414 aprint_verbose("/%s: hostpath %s (%s)\n", key, hostpath, buf); 415 } 416 417 return 0; 418 } 419 #undef REGDIR 420 421 int 422 rump_etfs_register(const char *key, const char *hostpath, 423 enum rump_etfs_type ftype) 424 { 425 426 return doregister(key, hostpath, ftype, 0, RUMP_ETFS_SIZE_ENDOFF); 427 } 428 429 int 430 rump_etfs_register_withsize(const char *key, const char *hostpath, 431 enum rump_etfs_type ftype, uint64_t begin, uint64_t size) 432 { 433 434 return doregister(key, hostpath, ftype, begin, size); 435 } 436 437 /* remove etfs mapping. caller's responsibility to make sure it's not in use */ 438 int 439 rump_etfs_remove(const char *key) 440 { 441 struct etfs *et; 442 size_t keylen; 443 int rv; 444 445 if (key[0] != '/') { 446 return EINVAL; 447 } 448 while (key[0] == '/') { 449 key++; 450 } 451 452 keylen = strlen(key); 453 454 mutex_enter(&etfs_lock); 455 LIST_FOREACH(et, &etfs_list, et_entries) { 456 if (keylen == et->et_keylen && strcmp(et->et_key, key) == 0) { 457 if (et->et_removing) 458 et = NULL; 459 else 460 et->et_removing = true; 461 break; 462 } 463 } 464 mutex_exit(&etfs_lock); 465 if (!et) 466 return ENOENT; 467 468 /* 469 * ok, we know what we want to remove and have signalled there 470 * actually are men at work. first, unregister from rumpblk 471 */ 472 if (et->et_blkmin != -1) { 473 rv = rumpblk_deregister(et->et_rn->rn_hostpath); 474 } else { 475 rv = 0; 476 } 477 KASSERT(rv == 0); 478 479 /* then do the actual removal */ 480 mutex_enter(&etfs_lock); 481 LIST_REMOVE(et, et_entries); 482 mutex_exit(&etfs_lock); 483 484 /* node is unreachable, safe to nuke all device copies */ 485 if (et->et_blkmin != -1) { 486 vdevgone(RUMPBLK_DEVMAJOR, et->et_blkmin, et->et_blkmin, VBLK); 487 } else { 488 struct vnode *vp; 489 490 mutex_enter(&reclock); 491 if ((vp = et->et_rn->rn_vp) != NULL) 492 mutex_enter(&vp->v_interlock); 493 mutex_exit(&reclock); 494 if (vp && vget(vp, 0) == 0) 495 vgone(vp); 496 } 497 498 if (et->et_rn->rn_hostpath != NULL) 499 free(et->et_rn->rn_hostpath, M_TEMP); 500 kmem_free(et->et_rn, sizeof(*et->et_rn)); 501 kmem_free(et, sizeof(*et)); 502 503 return 0; 504 } 505 506 /* 507 * rumpfs 508 */ 509 510 static struct rumpfs_node * 511 makeprivate(enum vtype vt, dev_t rdev, off_t size, bool et) 512 { 513 struct rumpfs_node *rn; 514 struct vattr *va; 515 struct timespec ts; 516 517 rn = kmem_zalloc(sizeof(*rn), KM_SLEEP); 518 519 switch (vt) { 520 case VDIR: 521 LIST_INIT(&rn->rn_dir); 522 break; 523 case VREG: 524 if (et) { 525 rn->rn_readfd = -1; 526 rn->rn_writefd = -1; 527 } 528 break; 529 default: 530 break; 531 } 532 533 nanotime(&ts); 534 535 va = &rn->rn_va; 536 va->va_type = vt; 537 va->va_mode = 0755; 538 if (vt == VDIR) 539 va->va_nlink = 2; 540 else 541 va->va_nlink = 1; 542 va->va_uid = 0; 543 va->va_gid = 0; 544 va->va_fsid = 545 va->va_fileid = atomic_inc_uint_nv(&lastino); 546 va->va_size = size; 547 va->va_blocksize = 512; 548 va->va_atime = ts; 549 va->va_mtime = ts; 550 va->va_ctime = ts; 551 va->va_birthtime = ts; 552 va->va_gen = 0; 553 va->va_flags = 0; 554 va->va_rdev = rdev; 555 va->va_bytes = 512; 556 va->va_filerev = 0; 557 va->va_vaflags = 0; 558 559 return rn; 560 } 561 562 static int 563 makevnode(struct mount *mp, struct rumpfs_node *rn, struct vnode **vpp) 564 { 565 struct vnode *vp; 566 int (**vpops)(void *); 567 struct vattr *va = &rn->rn_va; 568 int rv; 569 570 KASSERT(!mutex_owned(&reclock)); 571 572 if (va->va_type == VCHR || va->va_type == VBLK) { 573 vpops = rump_specop_p; 574 } else { 575 vpops = rump_vnodeop_p; 576 } 577 578 rv = getnewvnode(VT_RUMP, mp, vpops, &vp); 579 if (rv) 580 return rv; 581 582 vp->v_size = vp->v_writesize = va->va_size; 583 vp->v_type = va->va_type; 584 585 if (vpops == rump_specop_p) { 586 spec_node_init(vp, va->va_rdev); 587 } 588 vp->v_data = rn; 589 590 genfs_node_init(vp, &rumpfs_genfsops); 591 vn_lock(vp, LK_RETRY | LK_EXCLUSIVE); 592 mutex_enter(&reclock); 593 rn->rn_vp = vp; 594 mutex_exit(&reclock); 595 596 *vpp = vp; 597 598 return 0; 599 } 600 601 602 static void 603 makedir(struct rumpfs_node *rnd, 604 struct componentname *cnp, struct rumpfs_node *rn) 605 { 606 struct rumpfs_dent *rdent; 607 608 rdent = kmem_alloc(sizeof(*rdent), KM_SLEEP); 609 rdent->rd_name = kmem_alloc(cnp->cn_namelen+1, KM_SLEEP); 610 rdent->rd_node = rn; 611 strlcpy(rdent->rd_name, cnp->cn_nameptr, cnp->cn_namelen+1); 612 rdent->rd_namelen = strlen(rdent->rd_name); 613 614 LIST_INSERT_HEAD(&rnd->rn_dir, rdent, rd_entries); 615 } 616 617 static void 618 freedir(struct rumpfs_node *rnd, struct componentname *cnp) 619 { 620 struct rumpfs_dent *rd = NULL; 621 622 LIST_FOREACH(rd, &rnd->rn_dir, rd_entries) { 623 if (rd->rd_namelen == cnp->cn_namelen && 624 strncmp(rd->rd_name, cnp->cn_nameptr, 625 cnp->cn_namelen) == 0) 626 break; 627 } 628 if (rd == NULL) 629 panic("could not find directory entry: %s", cnp->cn_nameptr); 630 631 if (cnp->cn_flags & DOWHITEOUT) { 632 rd->rd_node = RUMPFS_WHITEOUT; 633 } else { 634 LIST_REMOVE(rd, rd_entries); 635 kmem_free(rd->rd_name, rd->rd_namelen+1); 636 kmem_free(rd, sizeof(*rd)); 637 } 638 } 639 640 /* 641 * Simple lookup for rump file systems. 642 * 643 * uhm, this is twisted. C F C C, hope of C C F C looming 644 */ 645 static int 646 rump_vop_lookup(void *v) 647 { 648 struct vop_lookup_args /* { 649 struct vnode *a_dvp; 650 struct vnode **a_vpp; 651 struct componentname *a_cnp; 652 }; */ *ap = v; 653 struct componentname *cnp = ap->a_cnp; 654 struct vnode *dvp = ap->a_dvp; 655 struct vnode **vpp = ap->a_vpp; 656 struct vnode *vp; 657 struct rumpfs_node *rnd = dvp->v_data, *rn; 658 struct rumpfs_dent *rd = NULL; 659 struct etfs *et; 660 bool dotdot = (cnp->cn_flags & ISDOTDOT) != 0; 661 int rv = 0; 662 663 *vpp = NULL; 664 665 if ((cnp->cn_flags & ISLASTCN) 666 && (dvp->v_mount->mnt_flag & MNT_RDONLY) 667 && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 668 return EROFS; 669 670 /* check for dot, return directly if the case */ 671 if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 672 vref(dvp); 673 *vpp = dvp; 674 return 0; 675 } 676 677 /* we don't do rename */ 678 if (!(((cnp->cn_flags & ISLASTCN) == 0) || (cnp->cn_nameiop != RENAME))) 679 return EOPNOTSUPP; 680 681 /* check for etfs */ 682 if (dvp == rootvnode && 683 (cnp->cn_nameiop == LOOKUP || cnp->cn_nameiop == CREATE)) { 684 bool found; 685 mutex_enter(&etfs_lock); 686 found = etfs_find(cnp->cn_nameptr, &et, false); 687 mutex_exit(&etfs_lock); 688 689 if (found) { 690 rn = et->et_rn; 691 cnp->cn_consume += et->et_keylen - cnp->cn_namelen; 692 if (rn->rn_va.va_type != VDIR) 693 cnp->cn_flags &= ~REQUIREDIR; 694 goto getvnode; 695 } 696 } 697 698 if (rnd->rn_flags & RUMPNODE_DIR_ET) { 699 uint64_t fsize; 700 char *newpath; 701 size_t newpathlen; 702 int hft, error; 703 704 if (dotdot) 705 return EOPNOTSUPP; 706 707 newpathlen = strlen(rnd->rn_hostpath) + 1 + cnp->cn_namelen + 1; 708 newpath = malloc(newpathlen, M_TEMP, M_WAITOK); 709 710 strlcpy(newpath, rnd->rn_hostpath, newpathlen); 711 strlcat(newpath, "/", newpathlen); 712 strlcat(newpath, cnp->cn_nameptr, newpathlen); 713 714 if (rumpuser_getfileinfo(newpath, &fsize, &hft, &error)) { 715 free(newpath, M_TEMP); 716 return error; 717 } 718 719 /* allow only dirs and regular files */ 720 if (hft != RUMPUSER_FT_REG && hft != RUMPUSER_FT_DIR) { 721 free(newpath, M_TEMP); 722 return ENOENT; 723 } 724 725 rn = makeprivate(hft_to_vtype(hft), NODEV, fsize, true); 726 rn->rn_flags |= RUMPNODE_CANRECLAIM; 727 if (rnd->rn_flags & RUMPNODE_DIR_ETSUBS) { 728 rn->rn_flags |= RUMPNODE_DIR_ET | RUMPNODE_DIR_ETSUBS; 729 rn->rn_flags |= RUMPNODE_ET_PHONE_HOST; 730 } 731 rn->rn_hostpath = newpath; 732 733 goto getvnode; 734 } else { 735 if (dotdot) { 736 rn = rnd->rn_parent; 737 goto getvnode; 738 } else { 739 LIST_FOREACH(rd, &rnd->rn_dir, rd_entries) { 740 if (rd->rd_namelen == cnp->cn_namelen && 741 strncmp(rd->rd_name, cnp->cn_nameptr, 742 cnp->cn_namelen) == 0) 743 break; 744 } 745 } 746 } 747 748 if (!rd && ((cnp->cn_flags & ISLASTCN) == 0||cnp->cn_nameiop != CREATE)) 749 return ENOENT; 750 751 if (!rd && (cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) { 752 if (dvp->v_mount->mnt_flag & MNT_RDONLY) 753 return EROFS; 754 return EJUSTRETURN; 755 } 756 757 if (RDENT_ISWHITEOUT(rd)) { 758 cnp->cn_flags |= ISWHITEOUT; 759 return ENOENT; 760 } 761 762 rn = rd->rd_node; 763 764 getvnode: 765 KASSERT(rn); 766 if (dotdot) 767 VOP_UNLOCK(dvp); 768 mutex_enter(&reclock); 769 if ((vp = rn->rn_vp)) { 770 mutex_enter(&vp->v_interlock); 771 mutex_exit(&reclock); 772 if (vget(vp, LK_EXCLUSIVE)) { 773 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); 774 goto getvnode; 775 } 776 *vpp = vp; 777 } else { 778 mutex_exit(&reclock); 779 rv = makevnode(dvp->v_mount, rn, vpp); 780 } 781 if (dotdot) 782 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); 783 784 return rv; 785 } 786 787 int 788 rump_vop_access(void *v) 789 { 790 struct vop_access_args /* { 791 const struct vnodeop_desc *a_desc; 792 struct vnode *a_vp; 793 int a_mode; 794 kauth_cred_t a_cred; 795 } */ *ap = v; 796 struct vnode *vp = ap->a_vp; 797 int mode = ap->a_mode; 798 799 if (mode & VWRITE) { 800 switch (vp->v_type) { 801 case VDIR: 802 case VLNK: 803 case VREG: 804 if ((vp->v_mount->mnt_flag & MNT_RDONLY)) 805 return EROFS; 806 break; 807 default: 808 break; 809 } 810 } 811 812 return 0; 813 } 814 815 static int 816 rump_vop_getattr(void *v) 817 { 818 struct vop_getattr_args /* { 819 struct vnode *a_vp; 820 struct vattr *a_vap; 821 kauth_cred_t a_cred; 822 } */ *ap = v; 823 struct vnode *vp = ap->a_vp; 824 struct rumpfs_node *rn = vp->v_data; 825 struct vattr *vap = ap->a_vap; 826 827 memcpy(vap, &rn->rn_va, sizeof(struct vattr)); 828 vap->va_size = vp->v_size; 829 return 0; 830 } 831 832 static int 833 rump_vop_setattr(void *v) 834 { 835 struct vop_getattr_args /* { 836 struct vnode *a_vp; 837 struct vattr *a_vap; 838 kauth_cred_t a_cred; 839 } */ *ap = v; 840 struct vnode *vp = ap->a_vp; 841 struct vattr *vap = ap->a_vap; 842 struct rumpfs_node *rn = vp->v_data; 843 844 #define SETIFVAL(a,t) if (vap->a != (t)VNOVAL) rn->rn_va.a = vap->a 845 SETIFVAL(va_mode, mode_t); 846 SETIFVAL(va_uid, uid_t); 847 SETIFVAL(va_gid, gid_t); 848 SETIFVAL(va_atime.tv_sec, time_t); 849 SETIFVAL(va_ctime.tv_sec, time_t); 850 SETIFVAL(va_mtime.tv_sec, time_t); 851 SETIFVAL(va_birthtime.tv_sec, time_t); 852 SETIFVAL(va_atime.tv_nsec, long); 853 SETIFVAL(va_ctime.tv_nsec, long); 854 SETIFVAL(va_mtime.tv_nsec, long); 855 SETIFVAL(va_birthtime.tv_nsec, long); 856 SETIFVAL(va_flags, u_long); 857 #undef SETIFVAL 858 859 if (vp->v_type == VREG && 860 vap->va_size != VSIZENOTSET && 861 vap->va_size != rn->rn_dlen) { 862 void *newdata; 863 size_t copylen, newlen; 864 865 newlen = vap->va_size; 866 newdata = rump_hypermalloc(newlen, 0, true, "rumpfs"); 867 868 copylen = MIN(rn->rn_dlen, newlen); 869 memset(newdata, 0, newlen); 870 memcpy(newdata, rn->rn_data, copylen); 871 rump_hyperfree(rn->rn_data, rn->rn_dlen); 872 873 rn->rn_data = newdata; 874 rn->rn_dlen = newlen; 875 uvm_vnp_setsize(vp, newlen); 876 } 877 return 0; 878 } 879 880 static int 881 rump_vop_mkdir(void *v) 882 { 883 struct vop_mkdir_args /* { 884 struct vnode *a_dvp; 885 struct vnode **a_vpp; 886 struct componentname *a_cnp; 887 struct vattr *a_vap; 888 }; */ *ap = v; 889 struct vnode *dvp = ap->a_dvp; 890 struct vnode **vpp = ap->a_vpp; 891 struct componentname *cnp = ap->a_cnp; 892 struct rumpfs_node *rnd = dvp->v_data, *rn; 893 int rv = 0; 894 895 rn = makeprivate(VDIR, NODEV, DEV_BSIZE, false); 896 rn->rn_parent = rnd; 897 rv = makevnode(dvp->v_mount, rn, vpp); 898 if (rv) 899 goto out; 900 901 makedir(rnd, cnp, rn); 902 903 out: 904 vput(dvp); 905 return rv; 906 } 907 908 static int 909 rump_vop_rmdir(void *v) 910 { 911 struct vop_rmdir_args /* { 912 struct vnode *a_dvp; 913 struct vnode *a_vp; 914 struct componentname *a_cnp; 915 }; */ *ap = v; 916 struct vnode *dvp = ap->a_dvp; 917 struct vnode *vp = ap->a_vp; 918 struct componentname *cnp = ap->a_cnp; 919 struct rumpfs_node *rnd = dvp->v_data; 920 struct rumpfs_node *rn = vp->v_data; 921 int rv = 0; 922 923 if (!LIST_EMPTY(&rn->rn_dir)) { 924 rv = ENOTEMPTY; 925 goto out; 926 } 927 928 freedir(rnd, cnp); 929 rn->rn_flags |= RUMPNODE_CANRECLAIM; 930 931 out: 932 vput(dvp); 933 vput(vp); 934 935 return rv; 936 } 937 938 static int 939 rump_vop_remove(void *v) 940 { 941 struct vop_rmdir_args /* { 942 struct vnode *a_dvp; 943 struct vnode *a_vp; 944 struct componentname *a_cnp; 945 }; */ *ap = v; 946 struct vnode *dvp = ap->a_dvp; 947 struct vnode *vp = ap->a_vp; 948 struct componentname *cnp = ap->a_cnp; 949 struct rumpfs_node *rnd = dvp->v_data; 950 struct rumpfs_node *rn = vp->v_data; 951 int rv = 0; 952 953 if (rn->rn_flags & RUMPNODE_ET_PHONE_HOST) 954 return EOPNOTSUPP; 955 956 if (vp->v_type == VREG) { 957 rump_hyperfree(rn->rn_data, rn->rn_dlen); 958 } 959 960 freedir(rnd, cnp); 961 rn->rn_flags |= RUMPNODE_CANRECLAIM; 962 963 vput(dvp); 964 vput(vp); 965 966 return rv; 967 } 968 969 static int 970 rump_vop_mknod(void *v) 971 { 972 struct vop_mknod_args /* { 973 struct vnode *a_dvp; 974 struct vnode **a_vpp; 975 struct componentname *a_cnp; 976 struct vattr *a_vap; 977 }; */ *ap = v; 978 struct vnode *dvp = ap->a_dvp; 979 struct vnode **vpp = ap->a_vpp; 980 struct componentname *cnp = ap->a_cnp; 981 struct vattr *va = ap->a_vap; 982 struct rumpfs_node *rnd = dvp->v_data, *rn; 983 int rv; 984 985 rn = makeprivate(va->va_type, va->va_rdev, DEV_BSIZE, false); 986 rv = makevnode(dvp->v_mount, rn, vpp); 987 if (rv) 988 goto out; 989 990 makedir(rnd, cnp, rn); 991 992 out: 993 vput(dvp); 994 return rv; 995 } 996 997 static int 998 rump_vop_create(void *v) 999 { 1000 struct vop_create_args /* { 1001 struct vnode *a_dvp; 1002 struct vnode **a_vpp; 1003 struct componentname *a_cnp; 1004 struct vattr *a_vap; 1005 }; */ *ap = v; 1006 struct vnode *dvp = ap->a_dvp; 1007 struct vnode **vpp = ap->a_vpp; 1008 struct componentname *cnp = ap->a_cnp; 1009 struct vattr *va = ap->a_vap; 1010 struct rumpfs_node *rnd = dvp->v_data, *rn; 1011 off_t newsize; 1012 int rv; 1013 1014 newsize = va->va_type == VSOCK ? DEV_BSIZE : 0; 1015 rn = makeprivate(va->va_type, NODEV, newsize, false); 1016 rv = makevnode(dvp->v_mount, rn, vpp); 1017 if (rv) 1018 goto out; 1019 1020 makedir(rnd, cnp, rn); 1021 1022 out: 1023 vput(dvp); 1024 return rv; 1025 } 1026 1027 static int 1028 rump_vop_symlink(void *v) 1029 { 1030 struct vop_symlink_args /* { 1031 struct vnode *a_dvp; 1032 struct vnode **a_vpp; 1033 struct componentname *a_cnp; 1034 struct vattr *a_vap; 1035 char *a_target; 1036 }; */ *ap = v; 1037 struct vnode *dvp = ap->a_dvp; 1038 struct vnode **vpp = ap->a_vpp; 1039 struct componentname *cnp = ap->a_cnp; 1040 struct rumpfs_node *rnd = dvp->v_data, *rn; 1041 const char *target = ap->a_target; 1042 size_t linklen; 1043 int rv; 1044 1045 linklen = strlen(target); 1046 KASSERT(linklen < MAXPATHLEN); 1047 rn = makeprivate(VLNK, NODEV, linklen, false); 1048 rv = makevnode(dvp->v_mount, rn, vpp); 1049 if (rv) 1050 goto out; 1051 1052 makedir(rnd, cnp, rn); 1053 1054 KASSERT(linklen < MAXPATHLEN); 1055 rn->rn_linktarg = PNBUF_GET(); 1056 rn->rn_linklen = linklen; 1057 strcpy(rn->rn_linktarg, target); 1058 1059 out: 1060 vput(dvp); 1061 return rv; 1062 } 1063 1064 static int 1065 rump_vop_readlink(void *v) 1066 { 1067 struct vop_readlink_args /* { 1068 struct vnode *a_vp; 1069 struct uio *a_uio; 1070 kauth_cred_t a_cred; 1071 }; */ *ap = v; 1072 struct vnode *vp = ap->a_vp; 1073 struct rumpfs_node *rn = vp->v_data; 1074 struct uio *uio = ap->a_uio; 1075 1076 return uiomove(rn->rn_linktarg, rn->rn_linklen, uio); 1077 } 1078 1079 static int 1080 rump_vop_whiteout(void *v) 1081 { 1082 struct vop_whiteout_args /* { 1083 struct vnode *a_dvp; 1084 struct componentname *a_cnp; 1085 int a_flags; 1086 } */ *ap = v; 1087 struct vnode *dvp = ap->a_dvp; 1088 struct rumpfs_node *rnd = dvp->v_data; 1089 struct componentname *cnp = ap->a_cnp; 1090 int flags = ap->a_flags; 1091 1092 switch (flags) { 1093 case LOOKUP: 1094 break; 1095 case CREATE: 1096 makedir(rnd, cnp, RUMPFS_WHITEOUT); 1097 break; 1098 case DELETE: 1099 cnp->cn_flags &= ~DOWHITEOUT; /* cargo culting never fails ? */ 1100 freedir(rnd, cnp); 1101 break; 1102 default: 1103 panic("unknown whiteout op %d", flags); 1104 } 1105 1106 return 0; 1107 } 1108 1109 static int 1110 rump_vop_open(void *v) 1111 { 1112 struct vop_open_args /* { 1113 struct vnode *a_vp; 1114 int a_mode; 1115 kauth_cred_t a_cred; 1116 } */ *ap = v; 1117 struct vnode *vp = ap->a_vp; 1118 struct rumpfs_node *rn = vp->v_data; 1119 int mode = ap->a_mode; 1120 int error = EINVAL; 1121 1122 if (vp->v_type != VREG || (rn->rn_flags & RUMPNODE_ET_PHONE_HOST) == 0) 1123 return 0; 1124 1125 if (mode & FREAD) { 1126 if (rn->rn_readfd != -1) 1127 return 0; 1128 rn->rn_readfd = rumpuser_open(rn->rn_hostpath, 1129 O_RDONLY, &error); 1130 } 1131 1132 if (mode & FWRITE) { 1133 if (rn->rn_writefd != -1) 1134 return 0; 1135 rn->rn_writefd = rumpuser_open(rn->rn_hostpath, 1136 O_WRONLY, &error); 1137 } 1138 1139 return error; 1140 } 1141 1142 /* simple readdir. event omits dotstuff and periods */ 1143 static int 1144 rump_vop_readdir(void *v) 1145 { 1146 struct vop_readdir_args /* { 1147 struct vnode *a_vp; 1148 struct uio *a_uio; 1149 kauth_cred_t a_cred; 1150 int *a_eofflag; 1151 off_t **a_cookies; 1152 int *a_ncookies; 1153 } */ *ap = v; 1154 struct vnode *vp = ap->a_vp; 1155 struct uio *uio = ap->a_uio; 1156 struct rumpfs_node *rnd = vp->v_data; 1157 struct rumpfs_dent *rdent; 1158 unsigned i; 1159 int rv = 0; 1160 1161 /* seek to current entry */ 1162 for (i = 0, rdent = LIST_FIRST(&rnd->rn_dir); 1163 (i < uio->uio_offset) && rdent; 1164 i++, rdent = LIST_NEXT(rdent, rd_entries)) 1165 continue; 1166 if (!rdent) 1167 goto out; 1168 1169 /* copy entries */ 1170 for (; rdent && uio->uio_resid > 0; 1171 rdent = LIST_NEXT(rdent, rd_entries), i++) { 1172 struct dirent dent; 1173 1174 strlcpy(dent.d_name, rdent->rd_name, sizeof(dent.d_name)); 1175 dent.d_namlen = strlen(dent.d_name); 1176 dent.d_reclen = _DIRENT_RECLEN(&dent, dent.d_namlen); 1177 1178 if (__predict_false(RDENT_ISWHITEOUT(rdent))) { 1179 dent.d_fileno = INO_WHITEOUT; 1180 dent.d_type = DT_WHT; 1181 } else { 1182 dent.d_fileno = rdent->rd_node->rn_va.va_fileid; 1183 dent.d_type = vtype2dt(rdent->rd_node->rn_va.va_type); 1184 } 1185 1186 if (uio->uio_resid < dent.d_reclen) { 1187 i--; 1188 break; 1189 } 1190 1191 rv = uiomove(&dent, dent.d_reclen, uio); 1192 if (rv) { 1193 i--; 1194 break; 1195 } 1196 } 1197 1198 out: 1199 if (ap->a_cookies) { 1200 *ap->a_ncookies = 0; 1201 *ap->a_cookies = NULL; 1202 } 1203 if (rdent) 1204 *ap->a_eofflag = 0; 1205 else 1206 *ap->a_eofflag = 1; 1207 uio->uio_offset = i; 1208 1209 return rv; 1210 } 1211 1212 static int 1213 etread(struct rumpfs_node *rn, struct uio *uio) 1214 { 1215 uint8_t *buf; 1216 size_t bufsize; 1217 ssize_t n; 1218 int error = 0; 1219 1220 bufsize = uio->uio_resid; 1221 buf = kmem_alloc(bufsize, KM_SLEEP); 1222 if ((n = rumpuser_pread(rn->rn_readfd, buf, bufsize, 1223 uio->uio_offset + rn->rn_offset, &error)) == -1) 1224 goto out; 1225 KASSERT(n <= bufsize); 1226 error = uiomove(buf, n, uio); 1227 1228 out: 1229 kmem_free(buf, bufsize); 1230 return error; 1231 1232 } 1233 1234 static int 1235 rump_vop_read(void *v) 1236 { 1237 struct vop_read_args /* { 1238 struct vnode *a_vp; 1239 struct uio *a_uio; 1240 int ioflags a_ioflag; 1241 kauth_cred_t a_cred; 1242 }; */ *ap = v; 1243 struct vnode *vp = ap->a_vp; 1244 struct rumpfs_node *rn = vp->v_data; 1245 struct uio *uio = ap->a_uio; 1246 const int advice = IO_ADV_DECODE(ap->a_ioflag); 1247 off_t chunk; 1248 int error = 0; 1249 1250 /* et op? */ 1251 if (rn->rn_flags & RUMPNODE_ET_PHONE_HOST) 1252 return etread(rn, uio); 1253 1254 /* otherwise, it's off to ubc with us */ 1255 while (uio->uio_resid > 0) { 1256 chunk = MIN(uio->uio_resid, (off_t)rn->rn_dlen-uio->uio_offset); 1257 if (chunk == 0) 1258 break; 1259 error = ubc_uiomove(&vp->v_uobj, uio, chunk, advice, 1260 UBC_READ | UBC_PARTIALOK | UBC_WANT_UNMAP(vp)?UBC_UNMAP:0); 1261 if (error) 1262 break; 1263 } 1264 1265 return error; 1266 } 1267 1268 static int 1269 etwrite(struct rumpfs_node *rn, struct uio *uio) 1270 { 1271 uint8_t *buf; 1272 size_t bufsize; 1273 ssize_t n; 1274 int error = 0; 1275 1276 bufsize = uio->uio_resid; 1277 buf = kmem_alloc(bufsize, KM_SLEEP); 1278 error = uiomove(buf, bufsize, uio); 1279 if (error) 1280 goto out; 1281 KASSERT(uio->uio_resid == 0); 1282 n = rumpuser_pwrite(rn->rn_writefd, buf, bufsize, 1283 (uio->uio_offset-bufsize) + rn->rn_offset, &error); 1284 if (n >= 0) { 1285 KASSERT(n <= bufsize); 1286 uio->uio_resid = bufsize - n; 1287 } 1288 1289 out: 1290 kmem_free(buf, bufsize); 1291 return error; 1292 } 1293 1294 static int 1295 rump_vop_write(void *v) 1296 { 1297 struct vop_read_args /* { 1298 struct vnode *a_vp; 1299 struct uio *a_uio; 1300 int ioflags a_ioflag; 1301 kauth_cred_t a_cred; 1302 }; */ *ap = v; 1303 struct vnode *vp = ap->a_vp; 1304 struct rumpfs_node *rn = vp->v_data; 1305 struct uio *uio = ap->a_uio; 1306 const int advice = IO_ADV_DECODE(ap->a_ioflag); 1307 void *olddata; 1308 size_t oldlen, newlen; 1309 off_t chunk; 1310 int error = 0; 1311 bool allocd = false; 1312 1313 if (ap->a_ioflag & IO_APPEND) 1314 uio->uio_offset = vp->v_size; 1315 1316 /* consult et? */ 1317 if (rn->rn_flags & RUMPNODE_ET_PHONE_HOST) 1318 return etwrite(rn, uio); 1319 1320 /* 1321 * Otherwise, it's a case of ubcmove. 1322 */ 1323 1324 /* 1325 * First, make sure we have enough storage. 1326 * 1327 * No, you don't need to tell me it's not very efficient. 1328 * No, it doesn't really support sparse files, just fakes it. 1329 */ 1330 newlen = uio->uio_offset + uio->uio_resid; 1331 oldlen = 0; /* XXXgcc */ 1332 olddata = NULL; 1333 if (rn->rn_dlen < newlen) { 1334 oldlen = rn->rn_dlen; 1335 olddata = rn->rn_data; 1336 1337 rn->rn_data = rump_hypermalloc(newlen, 0, true, "rumpfs"); 1338 rn->rn_dlen = newlen; 1339 memset(rn->rn_data, 0, newlen); 1340 memcpy(rn->rn_data, olddata, oldlen); 1341 allocd = true; 1342 uvm_vnp_setsize(vp, newlen); 1343 } 1344 1345 /* ok, we have enough stooorage. write */ 1346 while (uio->uio_resid > 0) { 1347 chunk = MIN(uio->uio_resid, (off_t)rn->rn_dlen-uio->uio_offset); 1348 if (chunk == 0) 1349 break; 1350 error = ubc_uiomove(&vp->v_uobj, uio, chunk, advice, 1351 UBC_WRITE | UBC_PARTIALOK | UBC_WANT_UNMAP(vp)?UBC_UNMAP:0); 1352 if (error) 1353 break; 1354 } 1355 1356 if (allocd) { 1357 if (error) { 1358 rump_hyperfree(rn->rn_data, newlen); 1359 rn->rn_data = olddata; 1360 rn->rn_dlen = oldlen; 1361 uvm_vnp_setsize(vp, oldlen); 1362 } else { 1363 rump_hyperfree(olddata, oldlen); 1364 } 1365 } 1366 1367 return error; 1368 } 1369 1370 static int 1371 rump_vop_bmap(void *v) 1372 { 1373 struct vop_bmap_args /* { 1374 struct vnode *a_vp; 1375 daddr_t a_bn; 1376 struct vnode **a_vpp; 1377 daddr_t *a_bnp; 1378 int *a_runp; 1379 } */ *ap = v; 1380 1381 /* 1:1 mapping */ 1382 if (ap->a_vpp) 1383 *ap->a_vpp = ap->a_vp; 1384 if (ap->a_bnp) 1385 *ap->a_bnp = ap->a_bn; 1386 if (ap->a_runp) 1387 *ap->a_runp = 16; 1388 1389 return 0; 1390 } 1391 1392 static int 1393 rump_vop_strategy(void *v) 1394 { 1395 struct vop_strategy_args /* { 1396 struct vnode *a_vp; 1397 struct buf *a_bp; 1398 } */ *ap = v; 1399 struct vnode *vp = ap->a_vp; 1400 struct rumpfs_node *rn = vp->v_data; 1401 struct buf *bp = ap->a_bp; 1402 off_t copylen, copyoff; 1403 int error; 1404 1405 if (vp->v_type != VREG || rn->rn_flags & RUMPNODE_ET_PHONE_HOST) { 1406 error = EINVAL; 1407 goto out; 1408 } 1409 1410 copyoff = bp->b_blkno << DEV_BSHIFT; 1411 copylen = MIN(rn->rn_dlen - copyoff, bp->b_bcount); 1412 if (BUF_ISWRITE(bp)) { 1413 memcpy((uint8_t *)rn->rn_data + copyoff, bp->b_data, copylen); 1414 } else { 1415 memset((uint8_t*)bp->b_data + copylen, 0, bp->b_bcount-copylen); 1416 memcpy(bp->b_data, (uint8_t *)rn->rn_data + copyoff, copylen); 1417 } 1418 bp->b_resid = 0; 1419 error = 0; 1420 1421 out: 1422 bp->b_error = error; 1423 biodone(bp); 1424 return 0; 1425 } 1426 1427 static int 1428 rump_vop_pathconf(void *v) 1429 { 1430 struct vop_pathconf_args /* { 1431 struct vnode *a_vp; 1432 int a_name; 1433 register_t *a_retval; 1434 }; */ *ap = v; 1435 int name = ap->a_name; 1436 register_t *retval = ap->a_retval; 1437 1438 switch (name) { 1439 case _PC_LINK_MAX: 1440 *retval = LINK_MAX; 1441 return 0; 1442 case _PC_NAME_MAX: 1443 *retval = NAME_MAX; 1444 return 0; 1445 case _PC_PATH_MAX: 1446 *retval = PATH_MAX; 1447 return 0; 1448 case _PC_PIPE_BUF: 1449 *retval = PIPE_BUF; 1450 return 0; 1451 case _PC_CHOWN_RESTRICTED: 1452 *retval = 1; 1453 return 0; 1454 case _PC_NO_TRUNC: 1455 *retval = 1; 1456 return 0; 1457 case _PC_SYNC_IO: 1458 *retval = 1; 1459 return 0; 1460 case _PC_FILESIZEBITS: 1461 *retval = 43; /* this one goes to 11 */ 1462 return 0; 1463 case _PC_SYMLINK_MAX: 1464 *retval = MAXPATHLEN; 1465 return 0; 1466 case _PC_2_SYMLINKS: 1467 *retval = 1; 1468 return 0; 1469 default: 1470 return EINVAL; 1471 } 1472 } 1473 1474 static int 1475 rump_vop_success(void *v) 1476 { 1477 1478 return 0; 1479 } 1480 1481 static int 1482 rump_vop_inactive(void *v) 1483 { 1484 struct vop_inactive_args /* { 1485 struct vnode *a_vp; 1486 bool *a_recycle; 1487 } */ *ap = v; 1488 struct vnode *vp = ap->a_vp; 1489 struct rumpfs_node *rn = vp->v_data; 1490 int error; 1491 1492 if (rn->rn_flags & RUMPNODE_ET_PHONE_HOST && vp->v_type == VREG) { 1493 if (rn->rn_readfd != -1) { 1494 rumpuser_close(rn->rn_readfd, &error); 1495 rn->rn_readfd = -1; 1496 } 1497 if (rn->rn_writefd != -1) { 1498 rumpuser_close(rn->rn_writefd, &error); 1499 rn->rn_writefd = -1; 1500 } 1501 } 1502 *ap->a_recycle = (rn->rn_flags & RUMPNODE_CANRECLAIM) ? true : false; 1503 1504 VOP_UNLOCK(vp); 1505 return 0; 1506 } 1507 1508 static int 1509 rump_vop_reclaim(void *v) 1510 { 1511 struct vop_reclaim_args /* { 1512 struct vnode *a_vp; 1513 } */ *ap = v; 1514 struct vnode *vp = ap->a_vp; 1515 struct rumpfs_node *rn = vp->v_data; 1516 1517 mutex_enter(&reclock); 1518 rn->rn_vp = NULL; 1519 mutex_exit(&reclock); 1520 genfs_node_destroy(vp); 1521 vp->v_data = NULL; 1522 1523 if (rn->rn_flags & RUMPNODE_CANRECLAIM) { 1524 if (vp->v_type == VLNK) 1525 PNBUF_PUT(rn->rn_linktarg); 1526 if (rn->rn_hostpath) 1527 free(rn->rn_hostpath, M_TEMP); 1528 kmem_free(rn, sizeof(*rn)); 1529 } 1530 1531 return 0; 1532 } 1533 1534 static int 1535 rump_vop_spec(void *v) 1536 { 1537 struct vop_generic_args *ap = v; 1538 int (**opvec)(void *); 1539 1540 switch (ap->a_desc->vdesc_offset) { 1541 case VOP_ACCESS_DESCOFFSET: 1542 case VOP_GETATTR_DESCOFFSET: 1543 case VOP_SETATTR_DESCOFFSET: 1544 case VOP_LOCK_DESCOFFSET: 1545 case VOP_UNLOCK_DESCOFFSET: 1546 case VOP_ISLOCKED_DESCOFFSET: 1547 case VOP_RECLAIM_DESCOFFSET: 1548 opvec = rump_vnodeop_p; 1549 break; 1550 default: 1551 opvec = spec_vnodeop_p; 1552 break; 1553 } 1554 1555 return VOCALL(opvec, ap->a_desc->vdesc_offset, v); 1556 } 1557 1558 static int 1559 rump_vop_advlock(void *v) 1560 { 1561 struct vop_advlock_args /* { 1562 const struct vnodeop_desc *a_desc; 1563 struct vnode *a_vp; 1564 void *a_id; 1565 int a_op; 1566 struct flock *a_fl; 1567 int a_flags; 1568 } */ *ap = v; 1569 struct vnode *vp = ap->a_vp; 1570 struct rumpfs_node *rn = vp->v_data; 1571 1572 return lf_advlock(ap, &rn->rn_lockf, vp->v_size); 1573 } 1574 1575 /* 1576 * Begin vfs-level stuff 1577 */ 1578 1579 VFS_PROTOS(rumpfs); 1580 struct vfsops rumpfs_vfsops = { 1581 .vfs_name = MOUNT_RUMPFS, 1582 .vfs_min_mount_data = 0, 1583 .vfs_mount = rumpfs_mount, 1584 .vfs_start = (void *)nullop, 1585 .vfs_unmount = rumpfs_unmount, 1586 .vfs_root = rumpfs_root, 1587 .vfs_quotactl = (void *)eopnotsupp, 1588 .vfs_statvfs = genfs_statvfs, 1589 .vfs_sync = (void *)nullop, 1590 .vfs_vget = rumpfs_vget, 1591 .vfs_fhtovp = (void *)eopnotsupp, 1592 .vfs_vptofh = (void *)eopnotsupp, 1593 .vfs_init = rumpfs_init, 1594 .vfs_reinit = NULL, 1595 .vfs_done = rumpfs_done, 1596 .vfs_mountroot = rumpfs_mountroot, 1597 .vfs_snapshot = (void *)eopnotsupp, 1598 .vfs_extattrctl = (void *)eopnotsupp, 1599 .vfs_suspendctl = (void *)eopnotsupp, 1600 .vfs_renamelock_enter = genfs_renamelock_enter, 1601 .vfs_renamelock_exit = genfs_renamelock_exit, 1602 .vfs_opv_descs = rump_opv_descs, 1603 /* vfs_refcount */ 1604 /* vfs_list */ 1605 }; 1606 1607 static int 1608 rumpfs_mountfs(struct mount *mp) 1609 { 1610 struct rumpfs_mount *rfsmp; 1611 struct rumpfs_node *rn; 1612 int error; 1613 1614 rfsmp = kmem_alloc(sizeof(*rfsmp), KM_SLEEP); 1615 1616 rn = makeprivate(VDIR, NODEV, DEV_BSIZE, false); 1617 rn->rn_parent = rn; 1618 if ((error = makevnode(mp, rn, &rfsmp->rfsmp_rvp)) != 0) 1619 return error; 1620 1621 rfsmp->rfsmp_rvp->v_vflag |= VV_ROOT; 1622 VOP_UNLOCK(rfsmp->rfsmp_rvp); 1623 1624 mp->mnt_data = rfsmp; 1625 mp->mnt_stat.f_namemax = MAXNAMLEN; 1626 mp->mnt_stat.f_iosize = 512; 1627 mp->mnt_flag |= MNT_LOCAL; 1628 mp->mnt_iflag |= IMNT_MPSAFE | IMNT_CAN_RWTORO; 1629 mp->mnt_fs_bshift = DEV_BSHIFT; 1630 vfs_getnewfsid(mp); 1631 1632 return 0; 1633 } 1634 1635 int 1636 rumpfs_mount(struct mount *mp, const char *mntpath, void *arg, size_t *alen) 1637 { 1638 int error; 1639 1640 if (mp->mnt_flag & MNT_UPDATE) { 1641 return 0; 1642 } 1643 1644 error = set_statvfs_info(mntpath, UIO_USERSPACE, "rumpfs", UIO_SYSSPACE, 1645 mp->mnt_op->vfs_name, mp, curlwp); 1646 if (error) 1647 return error; 1648 1649 return rumpfs_mountfs(mp); 1650 } 1651 1652 int 1653 rumpfs_unmount(struct mount *mp, int mntflags) 1654 { 1655 struct rumpfs_mount *rfsmp = mp->mnt_data; 1656 int flags = 0, error; 1657 1658 if (panicstr || mntflags & MNT_FORCE) 1659 flags |= FORCECLOSE; 1660 1661 if ((error = vflush(mp, rfsmp->rfsmp_rvp, flags)) != 0) 1662 return error; 1663 vgone(rfsmp->rfsmp_rvp); /* XXX */ 1664 1665 kmem_free(rfsmp, sizeof(*rfsmp)); 1666 1667 return 0; 1668 } 1669 1670 int 1671 rumpfs_root(struct mount *mp, struct vnode **vpp) 1672 { 1673 struct rumpfs_mount *rfsmp = mp->mnt_data; 1674 1675 vref(rfsmp->rfsmp_rvp); 1676 vn_lock(rfsmp->rfsmp_rvp, LK_EXCLUSIVE | LK_RETRY); 1677 *vpp = rfsmp->rfsmp_rvp; 1678 return 0; 1679 } 1680 1681 int 1682 rumpfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) 1683 { 1684 1685 return EOPNOTSUPP; 1686 } 1687 1688 void 1689 rumpfs_init() 1690 { 1691 1692 CTASSERT(RUMP_ETFS_SIZE_ENDOFF == RUMPBLK_SIZENOTSET); 1693 1694 mutex_init(&reclock, MUTEX_DEFAULT, IPL_NONE); 1695 mutex_init(&etfs_lock, MUTEX_DEFAULT, IPL_NONE); 1696 } 1697 1698 void 1699 rumpfs_done() 1700 { 1701 1702 mutex_destroy(&reclock); 1703 mutex_destroy(&etfs_lock); 1704 } 1705 1706 int 1707 rumpfs_mountroot() 1708 { 1709 struct mount *mp; 1710 int error; 1711 1712 if ((error = vfs_rootmountalloc(MOUNT_RUMPFS, "rootdev", &mp)) != 0) { 1713 vrele(rootvp); 1714 return error; 1715 } 1716 1717 if ((error = rumpfs_mountfs(mp)) != 0) 1718 panic("mounting rootfs failed: %d", error); 1719 1720 mutex_enter(&mountlist_lock); 1721 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); 1722 mutex_exit(&mountlist_lock); 1723 1724 error = set_statvfs_info("/", UIO_SYSSPACE, "rumpfs", UIO_SYSSPACE, 1725 mp->mnt_op->vfs_name, mp, curlwp); 1726 if (error) 1727 panic("set_statvfs_info failed for rootfs: %d", error); 1728 1729 mp->mnt_flag &= ~MNT_RDONLY; 1730 vfs_unbusy(mp, false, NULL); 1731 1732 return 0; 1733 } 1734