Home | History | Annotate | Line # | Download | only in rumpvfs
rumpfs.c revision 1.54
      1 /*	$NetBSD: rumpfs.c,v 1.54 2010/06/16 19:03:08 pooka Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2009  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.54 2010/06/16 19:03:08 pooka Exp $");
     30 
     31 #include <sys/param.h>
     32 #include <sys/atomic.h>
     33 #include <sys/dirent.h>
     34 #include <sys/errno.h>
     35 #include <sys/filedesc.h>
     36 #include <sys/fcntl.h>
     37 #include <sys/kauth.h>
     38 #include <sys/malloc.h>
     39 #include <sys/module.h>
     40 #include <sys/mount.h>
     41 #include <sys/namei.h>
     42 #include <sys/lock.h>
     43 #include <sys/lockf.h>
     44 #include <sys/queue.h>
     45 #include <sys/stat.h>
     46 #include <sys/syscallargs.h>
     47 #include <sys/vnode.h>
     48 
     49 #include <miscfs/fifofs/fifo.h>
     50 #include <miscfs/specfs/specdev.h>
     51 #include <miscfs/genfs/genfs.h>
     52 
     53 #include <rump/rumpuser.h>
     54 
     55 #include "rump_private.h"
     56 #include "rump_vfs_private.h"
     57 
     58 static int rump_vop_lookup(void *);
     59 static int rump_vop_getattr(void *);
     60 static int rump_vop_mkdir(void *);
     61 static int rump_vop_rmdir(void *);
     62 static int rump_vop_mknod(void *);
     63 static int rump_vop_create(void *);
     64 static int rump_vop_inactive(void *);
     65 static int rump_vop_reclaim(void *);
     66 static int rump_vop_success(void *);
     67 static int rump_vop_readdir(void *);
     68 static int rump_vop_spec(void *);
     69 static int rump_vop_read(void *);
     70 static int rump_vop_write(void *);
     71 static int rump_vop_open(void *);
     72 
     73 int (**fifo_vnodeop_p)(void *);
     74 const struct vnodeopv_entry_desc fifo_vnodeop_entries[] = {
     75 	{ &vop_default_desc, vn_default_error },
     76 	{ NULL, NULL }
     77 };
     78 const struct vnodeopv_desc fifo_vnodeop_opv_desc =
     79 	{ &fifo_vnodeop_p, fifo_vnodeop_entries };
     80 
     81 int (**rump_vnodeop_p)(void *);
     82 const struct vnodeopv_entry_desc rump_vnodeop_entries[] = {
     83 	{ &vop_default_desc, vn_default_error },
     84 	{ &vop_lookup_desc, rump_vop_lookup },
     85 	{ &vop_getattr_desc, rump_vop_getattr },
     86 	{ &vop_mkdir_desc, rump_vop_mkdir },
     87 	{ &vop_rmdir_desc, rump_vop_rmdir },
     88 	{ &vop_mknod_desc, rump_vop_mknod },
     89 	{ &vop_create_desc, rump_vop_create },
     90 	{ &vop_symlink_desc, genfs_eopnotsupp },
     91 	{ &vop_access_desc, rump_vop_success },
     92 	{ &vop_readdir_desc, rump_vop_readdir },
     93 	{ &vop_read_desc, rump_vop_read },
     94 	{ &vop_write_desc, rump_vop_write },
     95 	{ &vop_open_desc, rump_vop_open },
     96 	{ &vop_putpages_desc, genfs_null_putpages },
     97 	{ &vop_fsync_desc, rump_vop_success },
     98 	{ &vop_lock_desc, genfs_lock },
     99 	{ &vop_unlock_desc, genfs_unlock },
    100 	{ &vop_islocked_desc, genfs_islocked },
    101 	{ &vop_inactive_desc, rump_vop_inactive },
    102 	{ &vop_reclaim_desc, rump_vop_reclaim },
    103 	{ NULL, NULL }
    104 };
    105 const struct vnodeopv_desc rump_vnodeop_opv_desc =
    106 	{ &rump_vnodeop_p, rump_vnodeop_entries };
    107 
    108 int (**rump_specop_p)(void *);
    109 const struct vnodeopv_entry_desc rump_specop_entries[] = {
    110 	{ &vop_default_desc, rump_vop_spec },
    111 	{ NULL, NULL }
    112 };
    113 const struct vnodeopv_desc rump_specop_opv_desc =
    114 	{ &rump_specop_p, rump_specop_entries };
    115 
    116 const struct vnodeopv_desc * const rump_opv_descs[] = {
    117 	&rump_vnodeop_opv_desc,
    118 	&rump_specop_opv_desc,
    119 	NULL
    120 };
    121 
    122 struct rumpfs_dent {
    123 	char *rd_name;
    124 	int rd_namelen;
    125 	struct rumpfs_node *rd_node;
    126 
    127 	LIST_ENTRY(rumpfs_dent) rd_entries;
    128 };
    129 
    130 struct rumpfs_node {
    131 	struct vattr rn_va;
    132 	struct vnode *rn_vp;
    133 	char *rn_hostpath;
    134 	int rn_flags;
    135 
    136 	union {
    137 		struct {		/* VREG */
    138 			int readfd;
    139 			int writefd;
    140 			uint64_t offset;
    141 		} reg;
    142 		struct {		/* VDIR */
    143 			LIST_HEAD(, rumpfs_dent) dents;
    144 			int flags;
    145 		} dir;
    146 	} rn_u;
    147 };
    148 #define rn_readfd	rn_u.reg.readfd
    149 #define rn_writefd	rn_u.reg.writefd
    150 #define rn_offset	rn_u.reg.offset
    151 #define rn_dir		rn_u.dir.dents
    152 
    153 #define RUMPNODE_CANRECLAIM	0x01
    154 #define RUMPNODE_DIR_ET		0x02
    155 #define RUMPNODE_DIR_ETSUBS	0x04
    156 
    157 struct rumpfs_mount {
    158 	struct vnode *rfsmp_rvp;
    159 };
    160 
    161 static struct rumpfs_node *makeprivate(enum vtype, dev_t, off_t);
    162 
    163 /*
    164  * Extra Terrestrial stuff.  We map a given key (pathname) to a file on
    165  * the host FS.  ET phones home only from the root node of rumpfs.
    166  *
    167  * When an etfs node is removed, a vnode potentially behind it is not
    168  * immediately recycled.
    169  */
    170 
    171 struct etfs {
    172 	char et_key[MAXPATHLEN];
    173 	size_t et_keylen;
    174 	bool et_prefixkey;
    175 	bool et_removing;
    176 	devminor_t et_blkmin;
    177 
    178 	LIST_ENTRY(etfs) et_entries;
    179 
    180 	struct rumpfs_node *et_rn;
    181 };
    182 static kmutex_t etfs_lock;
    183 static LIST_HEAD(, etfs) etfs_list = LIST_HEAD_INITIALIZER(etfs_list);
    184 
    185 static enum vtype
    186 ettype_to_vtype(enum rump_etfs_type et)
    187 {
    188 	enum vtype vt;
    189 
    190 	switch (et) {
    191 	case RUMP_ETFS_REG:
    192 		vt = VREG;
    193 		break;
    194 	case RUMP_ETFS_BLK:
    195 		vt = VBLK;
    196 		break;
    197 	case RUMP_ETFS_CHR:
    198 		vt = VCHR;
    199 		break;
    200 	case RUMP_ETFS_DIR:
    201 		vt = VDIR;
    202 		break;
    203 	case RUMP_ETFS_DIR_SUBDIRS:
    204 		vt = VDIR;
    205 		break;
    206 	default:
    207 		panic("invalid et type: %d", et);
    208 	}
    209 
    210 	return vt;
    211 }
    212 
    213 static enum vtype
    214 hft_to_vtype(int hft)
    215 {
    216 	enum vtype vt;
    217 
    218 	switch (hft) {
    219 	case RUMPUSER_FT_OTHER:
    220 		vt = VNON;
    221 		break;
    222 	case RUMPUSER_FT_DIR:
    223 		vt = VDIR;
    224 		break;
    225 	case RUMPUSER_FT_REG:
    226 		vt = VREG;
    227 		break;
    228 	case RUMPUSER_FT_BLK:
    229 		vt = VBLK;
    230 		break;
    231 	case RUMPUSER_FT_CHR:
    232 		vt = VCHR;
    233 		break;
    234 	default:
    235 		vt = VNON;
    236 		break;
    237 	}
    238 
    239 	return vt;
    240 }
    241 
    242 static bool
    243 etfs_find(const char *key, struct etfs **etp, bool forceprefix)
    244 {
    245 	struct etfs *et;
    246 	size_t keylen = strlen(key);
    247 
    248 	KASSERT(mutex_owned(&etfs_lock));
    249 
    250 	LIST_FOREACH(et, &etfs_list, et_entries) {
    251 		if ((keylen == et->et_keylen || et->et_prefixkey || forceprefix)
    252 		    && strncmp(key, et->et_key, et->et_keylen) == 0) {
    253 			if (etp)
    254 				*etp = et;
    255 			return true;
    256 		}
    257 	}
    258 
    259 	return false;
    260 }
    261 
    262 #define REGDIR(ftype) \
    263     ((ftype) == RUMP_ETFS_DIR || (ftype) == RUMP_ETFS_DIR_SUBDIRS)
    264 static int
    265 doregister(const char *key, const char *hostpath,
    266 	enum rump_etfs_type ftype, uint64_t begin, uint64_t size)
    267 {
    268 	struct etfs *et;
    269 	struct rumpfs_node *rn;
    270 	uint64_t fsize;
    271 	dev_t rdev = NODEV;
    272 	devminor_t dmin = -1;
    273 	int hft, error;
    274 
    275 	if (rumpuser_getfileinfo(hostpath, &fsize, &hft, &error))
    276 		return error;
    277 
    278 	/* etfs directory requires a directory on the host */
    279 	if (REGDIR(ftype)) {
    280 		if (hft != RUMPUSER_FT_DIR)
    281 			return ENOTDIR;
    282 		if (begin != 0)
    283 			return EISDIR;
    284 		if (size != RUMP_ETFS_SIZE_ENDOFF)
    285 			return EISDIR;
    286 		size = fsize;
    287 	} else {
    288 		if (begin > fsize)
    289 			return EINVAL;
    290 		if (size == RUMP_ETFS_SIZE_ENDOFF)
    291 			size = fsize - begin;
    292 		if (begin + size > fsize)
    293 			return EINVAL;
    294 	}
    295 
    296 	if (ftype == RUMP_ETFS_BLK || ftype == RUMP_ETFS_CHR) {
    297 		error = rumpblk_register(hostpath, &dmin, begin, size);
    298 		if (error != 0) {
    299 			return error;
    300 		}
    301 		rdev = makedev(RUMPBLK_DEVMAJOR, dmin);
    302 	}
    303 
    304 	et = kmem_alloc(sizeof(*et), KM_SLEEP);
    305 	strcpy(et->et_key, key);
    306 	et->et_keylen = strlen(et->et_key);
    307 	et->et_rn = rn = makeprivate(ettype_to_vtype(ftype), rdev, size);
    308 	et->et_removing = false;
    309 	et->et_blkmin = dmin;
    310 
    311 	if (ftype == RUMP_ETFS_REG || REGDIR(ftype) || et->et_blkmin != -1) {
    312 		size_t len = strlen(hostpath)+1;
    313 
    314 		rn->rn_hostpath = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
    315 		memcpy(rn->rn_hostpath, hostpath, len);
    316 		rn->rn_offset = begin;
    317 	}
    318 
    319 	if (REGDIR(ftype)) {
    320 		rn->rn_flags |= RUMPNODE_DIR_ET;
    321 		et->et_prefixkey = true;
    322 	} else {
    323 		et->et_prefixkey = false;
    324 	}
    325 
    326 	if (ftype == RUMP_ETFS_DIR_SUBDIRS)
    327 		rn->rn_flags |= RUMPNODE_DIR_ETSUBS;
    328 
    329 	mutex_enter(&etfs_lock);
    330 	if (etfs_find(key, NULL, REGDIR(ftype))) {
    331 		mutex_exit(&etfs_lock);
    332 		if (et->et_blkmin != -1)
    333 			rumpblk_deregister(hostpath);
    334 		if (et->et_rn->rn_hostpath != NULL)
    335 			free(et->et_rn->rn_hostpath, M_TEMP);
    336 		kmem_free(et->et_rn, sizeof(*et->et_rn));
    337 		kmem_free(et, sizeof(*et));
    338 		return EEXIST;
    339 	}
    340 	LIST_INSERT_HEAD(&etfs_list, et, et_entries);
    341 	mutex_exit(&etfs_lock);
    342 
    343 	return 0;
    344 }
    345 #undef REGDIR
    346 
    347 int
    348 rump_etfs_register(const char *key, const char *hostpath,
    349 	enum rump_etfs_type ftype)
    350 {
    351 
    352 	return doregister(key, hostpath, ftype, 0, RUMP_ETFS_SIZE_ENDOFF);
    353 }
    354 
    355 int
    356 rump_etfs_register_withsize(const char *key, const char *hostpath,
    357 	enum rump_etfs_type ftype, uint64_t begin, uint64_t size)
    358 {
    359 
    360 	/*
    361 	 * Check that we're mapping at block offsets.  I guess this
    362 	 * is not technically necessary except for BLK/CHR backends
    363 	 * (i.e. what getfileinfo() returns, not ftype) and can be
    364 	 * removed later if there are problems.
    365 	 */
    366 	if ((begin & (DEV_BSIZE-1)) != 0)
    367 		return EINVAL;
    368 	if (size != RUMP_ETFS_SIZE_ENDOFF && (size & (DEV_BSIZE-1)) != 0)
    369 		return EINVAL;
    370 
    371 	return doregister(key, hostpath, ftype, begin, size);
    372 }
    373 
    374 /* remove etfs mapping.  caller's responsibility to make sure it's not in use */
    375 int
    376 rump_etfs_remove(const char *key)
    377 {
    378 	struct etfs *et;
    379 	size_t keylen = strlen(key);
    380 	int rv;
    381 
    382 	mutex_enter(&etfs_lock);
    383 	LIST_FOREACH(et, &etfs_list, et_entries) {
    384 		if (keylen == et->et_keylen && strcmp(et->et_key, key) == 0) {
    385 			if (et->et_removing)
    386 				et = NULL;
    387 			else
    388 				et->et_removing = true;
    389 			break;
    390 		}
    391 	}
    392 	mutex_exit(&etfs_lock);
    393 	if (!et)
    394 		return ENOENT;
    395 
    396 	/*
    397 	 * ok, we know what we want to remove and have signalled there
    398 	 * actually are men at work.  first, unregister from rumpblk
    399 	 */
    400 	if (et->et_blkmin != -1) {
    401 		rv = rumpblk_deregister(et->et_rn->rn_hostpath);
    402 	} else {
    403 		rv = 0;
    404 	}
    405 	KASSERT(rv == 0);
    406 
    407 	/* then do the actual removal */
    408 	mutex_enter(&etfs_lock);
    409 	LIST_REMOVE(et, et_entries);
    410 	mutex_exit(&etfs_lock);
    411 
    412 	/* node is unreachable, safe to nuke all device copies */
    413 	if (et->et_blkmin != -1)
    414 		vdevgone(RUMPBLK_DEVMAJOR, et->et_blkmin, et->et_blkmin, VBLK);
    415 
    416 	if (et->et_rn->rn_hostpath != NULL)
    417 		free(et->et_rn->rn_hostpath, M_TEMP);
    418 	kmem_free(et->et_rn, sizeof(*et->et_rn));
    419 	kmem_free(et, sizeof(*et));
    420 
    421 	return 0;
    422 }
    423 
    424 /*
    425  * rumpfs
    426  */
    427 
    428 static int lastino = 1;
    429 static kmutex_t reclock;
    430 
    431 static struct rumpfs_node *
    432 makeprivate(enum vtype vt, dev_t rdev, off_t size)
    433 {
    434 	struct rumpfs_node *rn;
    435 	struct vattr *va;
    436 	struct timespec ts;
    437 
    438 	rn = kmem_zalloc(sizeof(*rn), KM_SLEEP);
    439 
    440 	switch (vt) {
    441 	case VDIR:
    442 		LIST_INIT(&rn->rn_dir);
    443 		break;
    444 	case VREG:
    445 		rn->rn_readfd = -1;
    446 		rn->rn_writefd = -1;
    447 		break;
    448 	default:
    449 		break;
    450 	}
    451 
    452 	nanotime(&ts);
    453 
    454 	va = &rn->rn_va;
    455 	va->va_type = vt;
    456 	va->va_mode = 0755;
    457 	if (vt == VDIR)
    458 		va->va_nlink = 2;
    459 	else
    460 		va->va_nlink = 1;
    461 	va->va_uid = 0;
    462 	va->va_gid = 0;
    463 	va->va_fsid =
    464 	va->va_fileid = atomic_inc_uint_nv(&lastino);
    465 	va->va_size = size;
    466 	va->va_blocksize = 512;
    467 	va->va_atime = ts;
    468 	va->va_mtime = ts;
    469 	va->va_ctime = ts;
    470 	va->va_birthtime = ts;
    471 	va->va_gen = 0;
    472 	va->va_flags = 0;
    473 	va->va_rdev = rdev;
    474 	va->va_bytes = 512;
    475 	va->va_filerev = 0;
    476 	va->va_vaflags = 0;
    477 
    478 	return rn;
    479 }
    480 
    481 static int
    482 makevnode(struct mount *mp, struct rumpfs_node *rn, struct vnode **vpp)
    483 {
    484 	struct vnode *vp;
    485 	int (**vpops)(void *);
    486 	struct vattr *va = &rn->rn_va;
    487 	int rv;
    488 
    489 	KASSERT(!mutex_owned(&reclock));
    490 
    491 	if (va->va_type == VCHR || va->va_type == VBLK) {
    492 		vpops = rump_specop_p;
    493 	} else {
    494 		vpops = rump_vnodeop_p;
    495 	}
    496 	if (vpops != rump_specop_p && va->va_type != VDIR
    497 	    && !(va->va_type == VREG && rn->rn_hostpath != NULL)
    498 	    && va->va_type != VSOCK)
    499 		return EOPNOTSUPP;
    500 
    501 	rv = getnewvnode(VT_RUMP, mp, vpops, &vp);
    502 	if (rv)
    503 		return rv;
    504 
    505 	vp->v_size = vp->v_writesize = va->va_size;
    506 	vp->v_type = va->va_type;
    507 
    508 	if (vpops == rump_specop_p) {
    509 		spec_node_init(vp, va->va_rdev);
    510 	}
    511 	vp->v_data = rn;
    512 
    513 	vn_lock(vp, LK_RETRY | LK_EXCLUSIVE);
    514 	mutex_enter(&reclock);
    515 	rn->rn_vp = vp;
    516 	mutex_exit(&reclock);
    517 
    518 	*vpp = vp;
    519 
    520 	return 0;
    521 }
    522 
    523 
    524 static void
    525 makedir(struct rumpfs_node *rnd,
    526 	struct componentname *cnp, struct rumpfs_node *rn)
    527 {
    528 	struct rumpfs_dent *rdent;
    529 
    530 	rdent = kmem_alloc(sizeof(*rdent), KM_SLEEP);
    531 	rdent->rd_name = kmem_alloc(cnp->cn_namelen+1, KM_SLEEP);
    532 	rdent->rd_node = rn;
    533 	strlcpy(rdent->rd_name, cnp->cn_nameptr, cnp->cn_namelen+1);
    534 	rdent->rd_namelen = strlen(rdent->rd_name);
    535 
    536 	LIST_INSERT_HEAD(&rnd->rn_dir, rdent, rd_entries);
    537 }
    538 
    539 static void
    540 freedir(struct rumpfs_node *rnd, struct componentname *cnp)
    541 {
    542 	struct rumpfs_dent *rd = NULL;
    543 
    544 	LIST_FOREACH(rd, &rnd->rn_dir, rd_entries) {
    545 		if (rd->rd_namelen == cnp->cn_namelen &&
    546 		    strncmp(rd->rd_name, cnp->cn_nameptr,
    547 		            cnp->cn_namelen) == 0)
    548 			break;
    549 	}
    550 	if (rd == NULL)
    551 		panic("could not find directory entry: %s", cnp->cn_nameptr);
    552 
    553 	LIST_REMOVE(rd, rd_entries);
    554 	kmem_free(rd->rd_name, rd->rd_namelen+1);
    555 	kmem_free(rd, sizeof(*rd));
    556 }
    557 
    558 /*
    559  * Simple lookup for rump file systems.
    560  *
    561  * uhm, this is twisted.  C F C C, hope of C C F C looming
    562  */
    563 static int
    564 rump_vop_lookup(void *v)
    565 {
    566 	struct vop_lookup_args /* {
    567 		struct vnode *a_dvp;
    568 		struct vnode **a_vpp;
    569 		struct componentname *a_cnp;
    570 	}; */ *ap = v;
    571 	struct componentname *cnp = ap->a_cnp;
    572 	struct vnode *dvp = ap->a_dvp;
    573 	struct vnode **vpp = ap->a_vpp;
    574 	struct vnode *vp;
    575 	struct rumpfs_node *rnd = dvp->v_data, *rn;
    576 	struct rumpfs_dent *rd = NULL;
    577 	struct etfs *et;
    578 	int rv;
    579 
    580 	/* we handle only some "non-special" cases */
    581 	if (!(((cnp->cn_flags & ISLASTCN) == 0) || (cnp->cn_nameiop != RENAME)))
    582 		return EOPNOTSUPP;
    583 	if (!((cnp->cn_flags & ISDOTDOT) == 0))
    584 		return EOPNOTSUPP;
    585 
    586 	/* check for dot, return directly if the case */
    587 	if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
    588 		vref(dvp);
    589 		*vpp = dvp;
    590 		return 0;
    591 	}
    592 
    593 	/* check for etfs */
    594 	if (dvp == rootvnode && cnp->cn_nameiop == LOOKUP) {
    595 		bool found;
    596 		mutex_enter(&etfs_lock);
    597 		found = etfs_find(cnp->cn_pnbuf, &et, false);
    598 		mutex_exit(&etfs_lock);
    599 
    600 		if (found) {
    601 			char *offset;
    602 
    603 			offset = strstr(cnp->cn_pnbuf, et->et_key);
    604 			KASSERT(offset);
    605 
    606 			rn = et->et_rn;
    607 			cnp->cn_consume += et->et_keylen
    608 			    - (cnp->cn_nameptr - offset) - cnp->cn_namelen;
    609 			if (rn->rn_va.va_type != VDIR)
    610 				cnp->cn_flags &= ~REQUIREDIR;
    611 			goto getvnode;
    612 		}
    613 	}
    614 
    615 	if (rnd->rn_flags & RUMPNODE_DIR_ET) {
    616 		uint64_t fsize;
    617 		char *newpath;
    618 		size_t newpathlen;
    619 		int hft, error;
    620 
    621 		newpathlen = strlen(rnd->rn_hostpath) + 1 + cnp->cn_namelen + 1;
    622 		newpath = malloc(newpathlen, M_TEMP, M_WAITOK);
    623 
    624 		strlcpy(newpath, rnd->rn_hostpath, newpathlen);
    625 		strlcat(newpath, "/", newpathlen);
    626 		strlcat(newpath, cnp->cn_nameptr, newpathlen);
    627 
    628 		if (rumpuser_getfileinfo(newpath, &fsize, &hft, &error)) {
    629 			free(newpath, M_TEMP);
    630 			return error;
    631 		}
    632 
    633 		/* allow only dirs and regular files */
    634 		if (hft != RUMPUSER_FT_REG && hft != RUMPUSER_FT_DIR) {
    635 			free(newpath, M_TEMP);
    636 			return ENOENT;
    637 		}
    638 
    639 		rn = makeprivate(hft_to_vtype(hft), NODEV, fsize);
    640 		rn->rn_flags |= RUMPNODE_CANRECLAIM;
    641 		if (rnd->rn_flags & RUMPNODE_DIR_ETSUBS) {
    642 			rn->rn_flags |= RUMPNODE_DIR_ET | RUMPNODE_DIR_ETSUBS;
    643 		}
    644 		rn->rn_hostpath = newpath;
    645 
    646 		goto getvnode;
    647 	} else {
    648 		LIST_FOREACH(rd, &rnd->rn_dir, rd_entries) {
    649 			if (rd->rd_namelen == cnp->cn_namelen &&
    650 			    strncmp(rd->rd_name, cnp->cn_nameptr,
    651 			      cnp->cn_namelen) == 0)
    652 				break;
    653 		}
    654 	}
    655 
    656 	if (!rd && ((cnp->cn_flags & ISLASTCN) == 0||cnp->cn_nameiop != CREATE))
    657 		return ENOENT;
    658 
    659 	if (!rd && (cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) {
    660 		cnp->cn_flags |= SAVENAME;
    661 		return EJUSTRETURN;
    662 	}
    663 	if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == DELETE)
    664 		cnp->cn_flags |= SAVENAME;
    665 
    666 	rn = rd->rd_node;
    667 
    668  getvnode:
    669 	KASSERT(rn);
    670 	mutex_enter(&reclock);
    671 	if ((vp = rn->rn_vp)) {
    672 		mutex_enter(&vp->v_interlock);
    673 		mutex_exit(&reclock);
    674 		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK))
    675 			goto getvnode;
    676 		*vpp = vp;
    677 	} else {
    678 		mutex_exit(&reclock);
    679 		rv = makevnode(dvp->v_mount, rn, vpp);
    680 		if (rv)
    681 			return rv;
    682 	}
    683 
    684 	return 0;
    685 }
    686 
    687 static int
    688 rump_vop_getattr(void *v)
    689 {
    690 	struct vop_getattr_args /* {
    691 		struct vnode *a_vp;
    692 		struct vattr *a_vap;
    693 		kauth_cred_t a_cred;
    694 	} */ *ap = v;
    695 	struct rumpfs_node *rn = ap->a_vp->v_data;
    696 
    697 	memcpy(ap->a_vap, &rn->rn_va, sizeof(struct vattr));
    698 	return 0;
    699 }
    700 
    701 static int
    702 rump_vop_mkdir(void *v)
    703 {
    704 	struct vop_mkdir_args /* {
    705 		struct vnode *a_dvp;
    706 		struct vnode **a_vpp;
    707 		struct componentname *a_cnp;
    708 		struct vattr *a_vap;
    709 	}; */ *ap = v;
    710 	struct vnode *dvp = ap->a_dvp;
    711 	struct vnode **vpp = ap->a_vpp;
    712 	struct componentname *cnp = ap->a_cnp;
    713 	struct rumpfs_node *rnd = dvp->v_data, *rn;
    714 	int rv = 0;
    715 
    716 	rn = makeprivate(VDIR, NODEV, DEV_BSIZE);
    717 	rv = makevnode(dvp->v_mount, rn, vpp);
    718 	if (rv)
    719 		goto out;
    720 
    721 	makedir(rnd, cnp, rn);
    722 
    723  out:
    724 	PNBUF_PUT(cnp->cn_pnbuf);
    725 	vput(dvp);
    726 	return rv;
    727 }
    728 
    729 static int
    730 rump_vop_rmdir(void *v)
    731 {
    732         struct vop_rmdir_args /* {
    733                 struct vnode *a_dvp;
    734                 struct vnode *a_vp;
    735                 struct componentname *a_cnp;
    736         }; */ *ap = v;
    737 	struct vnode *dvp = ap->a_dvp;
    738 	struct vnode *vp = ap->a_vp;
    739 	struct componentname *cnp = ap->a_cnp;
    740 	struct rumpfs_node *rnd = dvp->v_data;
    741 	struct rumpfs_node *rn = vp->v_data;
    742 	int rv = 0;
    743 
    744 	if (!LIST_EMPTY(&rn->rn_dir)) {
    745 		rv = ENOTEMPTY;
    746 		goto out;
    747 	}
    748 
    749 	freedir(rnd, cnp);
    750 	rn->rn_flags |= RUMPNODE_CANRECLAIM;
    751 
    752 out:
    753 	PNBUF_PUT(cnp->cn_pnbuf);
    754 	vput(dvp);
    755 	vput(vp);
    756 
    757 	return rv;
    758 }
    759 
    760 static int
    761 rump_vop_mknod(void *v)
    762 {
    763 	struct vop_mknod_args /* {
    764 		struct vnode *a_dvp;
    765 		struct vnode **a_vpp;
    766 		struct componentname *a_cnp;
    767 		struct vattr *a_vap;
    768 	}; */ *ap = v;
    769 	struct vnode *dvp = ap->a_dvp;
    770 	struct vnode **vpp = ap->a_vpp;
    771 	struct componentname *cnp = ap->a_cnp;
    772 	struct vattr *va = ap->a_vap;
    773 	struct rumpfs_node *rnd = dvp->v_data, *rn;
    774 	int rv;
    775 
    776 	rn = makeprivate(va->va_type, va->va_rdev, DEV_BSIZE);
    777 	rv = makevnode(dvp->v_mount, rn, vpp);
    778 	if (rv)
    779 		goto out;
    780 
    781 	makedir(rnd, cnp, rn);
    782 
    783  out:
    784 	vput(dvp);
    785 	return rv;
    786 }
    787 
    788 static int
    789 rump_vop_create(void *v)
    790 {
    791 	struct vop_create_args /* {
    792 		struct vnode *a_dvp;
    793 		struct vnode **a_vpp;
    794 		struct componentname *a_cnp;
    795 		struct vattr *a_vap;
    796 	}; */ *ap = v;
    797 	struct vnode *dvp = ap->a_dvp;
    798 	struct vnode **vpp = ap->a_vpp;
    799 	struct componentname *cnp = ap->a_cnp;
    800 	struct vattr *va = ap->a_vap;
    801 	struct rumpfs_node *rnd = dvp->v_data, *rn;
    802 	int rv;
    803 
    804 	if (va->va_type != VSOCK) {
    805 		rv = EOPNOTSUPP;
    806 		goto out;
    807 	}
    808 	rn = makeprivate(VSOCK, NODEV, DEV_BSIZE);
    809 	rv = makevnode(dvp->v_mount, rn, vpp);
    810 	if (rv)
    811 		goto out;
    812 
    813 	makedir(rnd, cnp, rn);
    814 
    815  out:
    816 	vput(dvp);
    817 	return rv;
    818 }
    819 
    820 static int
    821 rump_vop_open(void *v)
    822 {
    823 	struct vop_open_args /* {
    824 		struct vnode *a_vp;
    825 		int a_mode;
    826 		kauth_cred_t a_cred;
    827 	} */ *ap = v;
    828 	struct vnode *vp = ap->a_vp;
    829 	struct rumpfs_node *rn = vp->v_data;
    830 	int mode = ap->a_mode;
    831 	int error = EINVAL;
    832 
    833 	if (vp->v_type != VREG)
    834 		return 0;
    835 
    836 	if (mode & FREAD) {
    837 		if (rn->rn_readfd != -1)
    838 			return 0;
    839 		rn->rn_readfd = rumpuser_open(rn->rn_hostpath,
    840 		    O_RDONLY, &error);
    841 	} else if (mode & FWRITE) {
    842 		if (rn->rn_writefd != -1)
    843 			return 0;
    844 		rn->rn_writefd = rumpuser_open(rn->rn_hostpath,
    845 		    O_WRONLY, &error);
    846 	}
    847 
    848 	return error;
    849 }
    850 
    851 /* simple readdir.  event omits dotstuff and periods */
    852 static int
    853 rump_vop_readdir(void *v)
    854 {
    855 	struct vop_readdir_args /* {
    856 		struct vnode *a_vp;
    857 		struct uio *a_uio;
    858 		kauth_cred_t a_cred;
    859 		int *a_eofflag;
    860 		off_t **a_cookies;
    861 		int *a_ncookies;
    862 	} */ *ap = v;
    863 	struct vnode *vp = ap->a_vp;
    864 	struct uio *uio = ap->a_uio;
    865 	struct rumpfs_node *rnd = vp->v_data;
    866 	struct rumpfs_dent *rdent;
    867 	unsigned i;
    868 	int rv = 0;
    869 
    870 	/* seek to current entry */
    871 	for (i = 0, rdent = LIST_FIRST(&rnd->rn_dir);
    872 	    (i < uio->uio_offset) && rdent;
    873 	    i++, rdent = LIST_NEXT(rdent, rd_entries))
    874 		continue;
    875 	if (!rdent)
    876 		goto out;
    877 
    878 	/* copy entries */
    879 	for (; rdent && uio->uio_resid > 0;
    880 	    rdent = LIST_NEXT(rdent, rd_entries), i++) {
    881 		struct dirent dent;
    882 
    883 		dent.d_fileno = rdent->rd_node->rn_va.va_fileid;
    884 		strlcpy(dent.d_name, rdent->rd_name, sizeof(dent.d_name));
    885 		dent.d_namlen = strlen(dent.d_name);
    886 		dent.d_type = vtype2dt(rdent->rd_node->rn_va.va_type);
    887 		dent.d_reclen = _DIRENT_RECLEN(&dent, dent.d_namlen);
    888 
    889 		if (uio->uio_resid < dent.d_reclen) {
    890 			i--;
    891 			break;
    892 		}
    893 
    894 		rv = uiomove(&dent, dent.d_reclen, uio);
    895 		if (rv) {
    896 			i--;
    897 			break;
    898 		}
    899 	}
    900 
    901  out:
    902 	if (ap->a_cookies) {
    903 		*ap->a_ncookies = 0;
    904 		*ap->a_cookies = NULL;
    905 	}
    906 	if (rdent)
    907 		*ap->a_eofflag = 0;
    908 	else
    909 		*ap->a_eofflag = 1;
    910 	uio->uio_offset = i;
    911 
    912 	return rv;
    913 }
    914 
    915 static int
    916 rump_vop_read(void *v)
    917 {
    918 	struct vop_read_args /* {
    919 		struct vnode *a_vp;
    920 		struct uio *a_uio;
    921 		int ioflags a_ioflag;
    922 		kauth_cred_t a_cred;
    923 	}; */ *ap = v;
    924 	struct vnode *vp = ap->a_vp;
    925 	struct rumpfs_node *rn = vp->v_data;
    926 	struct uio *uio = ap->a_uio;
    927 	uint8_t *buf;
    928 	size_t bufsize;
    929 	ssize_t n;
    930 	int error = 0;
    931 
    932 	bufsize = uio->uio_resid;
    933 	buf = kmem_alloc(bufsize, KM_SLEEP);
    934 	if ((n = rumpuser_pread(rn->rn_readfd, buf, bufsize,
    935 	    uio->uio_offset + rn->rn_offset, &error)) == -1)
    936 		goto out;
    937 	KASSERT(n <= bufsize);
    938 	error = uiomove(buf, n, uio);
    939 
    940  out:
    941 	kmem_free(buf, bufsize);
    942 	return error;
    943 }
    944 
    945 static int
    946 rump_vop_write(void *v)
    947 {
    948 	struct vop_read_args /* {
    949 		struct vnode *a_vp;
    950 		struct uio *a_uio;
    951 		int ioflags a_ioflag;
    952 		kauth_cred_t a_cred;
    953 	}; */ *ap = v;
    954 	struct vnode *vp = ap->a_vp;
    955 	struct rumpfs_node *rn = vp->v_data;
    956 	struct uio *uio = ap->a_uio;
    957 	uint8_t *buf;
    958 	size_t bufsize;
    959 	ssize_t n;
    960 	int error = 0;
    961 
    962 	bufsize = uio->uio_resid;
    963 	buf = kmem_alloc(bufsize, KM_SLEEP);
    964 	error = uiomove(buf, bufsize, uio);
    965 	if (error)
    966 		goto out;
    967 	KASSERT(uio->uio_resid == 0);
    968 	n = rumpuser_pwrite(rn->rn_writefd, buf, bufsize,
    969 	    uio->uio_offset + rn->rn_offset, &error);
    970 	if (n >= 0) {
    971 		KASSERT(n <= bufsize);
    972 		uio->uio_resid = bufsize - n;
    973 	}
    974 
    975  out:
    976 	kmem_free(buf, bufsize);
    977 	return error;
    978 }
    979 
    980 static int
    981 rump_vop_success(void *v)
    982 {
    983 
    984 	return 0;
    985 }
    986 
    987 static int
    988 rump_vop_inactive(void *v)
    989 {
    990 	struct vop_inactive_args /* {
    991 		struct vnode *a_vp;
    992 		bool *a_recycle;
    993 	} */ *ap = v;
    994 	struct vnode *vp = ap->a_vp;
    995 	struct rumpfs_node *rn = vp->v_data;
    996 	int error;
    997 
    998 	if (vp->v_type == VREG) {
    999 		if (rn->rn_readfd != -1) {
   1000 			rumpuser_close(rn->rn_readfd, &error);
   1001 			rn->rn_readfd = -1;
   1002 		}
   1003 		if (rn->rn_writefd != -1) {
   1004 			rumpuser_close(rn->rn_writefd, &error);
   1005 			rn->rn_writefd = -1;
   1006 		}
   1007 	}
   1008 	*ap->a_recycle = (rn->rn_flags & RUMPNODE_CANRECLAIM) ? true : false;
   1009 
   1010 	VOP_UNLOCK(vp, 0);
   1011 	return 0;
   1012 }
   1013 
   1014 static int
   1015 rump_vop_reclaim(void *v)
   1016 {
   1017 	struct vop_reclaim_args /* {
   1018 		struct vnode *a_vp;
   1019 	} */ *ap = v;
   1020 	struct vnode *vp = ap->a_vp;
   1021 	struct rumpfs_node *rn = vp->v_data;
   1022 
   1023 	mutex_enter(&reclock);
   1024 	rn->rn_vp = NULL;
   1025 	mutex_exit(&reclock);
   1026 	vp->v_data = NULL;
   1027 
   1028 	if (rn->rn_flags & RUMPNODE_CANRECLAIM) {
   1029 		if (rn->rn_hostpath)
   1030 			free(rn->rn_hostpath, M_TEMP);
   1031 		kmem_free(rn, sizeof(*rn));
   1032 	}
   1033 
   1034 	return 0;
   1035 }
   1036 
   1037 static int
   1038 rump_vop_spec(void *v)
   1039 {
   1040 	struct vop_generic_args *ap = v;
   1041 	int (**opvec)(void *);
   1042 
   1043 	switch (ap->a_desc->vdesc_offset) {
   1044 	case VOP_ACCESS_DESCOFFSET:
   1045 	case VOP_GETATTR_DESCOFFSET:
   1046 	case VOP_LOCK_DESCOFFSET:
   1047 	case VOP_UNLOCK_DESCOFFSET:
   1048 	case VOP_RECLAIM_DESCOFFSET:
   1049 		opvec = rump_vnodeop_p;
   1050 		break;
   1051 	default:
   1052 		opvec = spec_vnodeop_p;
   1053 		break;
   1054 	}
   1055 
   1056 	return VOCALL(opvec, ap->a_desc->vdesc_offset, v);
   1057 }
   1058 
   1059 /*
   1060  * Begin vfs-level stuff
   1061  */
   1062 
   1063 VFS_PROTOS(rumpfs);
   1064 struct vfsops rumpfs_vfsops = {
   1065 	.vfs_name =		MOUNT_RUMPFS,
   1066 	.vfs_min_mount_data = 	0,
   1067 	.vfs_mount =		rumpfs_mount,
   1068 	.vfs_start =		(void *)nullop,
   1069 	.vfs_unmount = 		rumpfs_unmount,
   1070 	.vfs_root =		rumpfs_root,
   1071 	.vfs_quotactl =		(void *)eopnotsupp,
   1072 	.vfs_statvfs =		genfs_statvfs,
   1073 	.vfs_sync =		(void *)nullop,
   1074 	.vfs_vget =		rumpfs_vget,
   1075 	.vfs_fhtovp =		(void *)eopnotsupp,
   1076 	.vfs_vptofh =		(void *)eopnotsupp,
   1077 	.vfs_init =		rumpfs_init,
   1078 	.vfs_reinit =		NULL,
   1079 	.vfs_done =		rumpfs_done,
   1080 	.vfs_mountroot =	rumpfs_mountroot,
   1081 	.vfs_snapshot =		(void *)eopnotsupp,
   1082 	.vfs_extattrctl =	(void *)eopnotsupp,
   1083 	.vfs_suspendctl =	(void *)eopnotsupp,
   1084 	.vfs_opv_descs =	rump_opv_descs,
   1085 	/* vfs_refcount */
   1086 	/* vfs_list */
   1087 };
   1088 
   1089 int
   1090 rumpfs_mount(struct mount *mp, const char *mntpath, void *arg, size_t *alen)
   1091 {
   1092 
   1093 	return EOPNOTSUPP;
   1094 }
   1095 
   1096 int
   1097 rumpfs_unmount(struct mount *mp, int flags)
   1098 {
   1099 
   1100 	/* if going for it, just lie about it */
   1101 	if (panicstr)
   1102 		return 0;
   1103 
   1104 	return EOPNOTSUPP; /* ;) */
   1105 }
   1106 
   1107 int
   1108 rumpfs_root(struct mount *mp, struct vnode **vpp)
   1109 {
   1110 	struct rumpfs_mount *rfsmp = mp->mnt_data;
   1111 
   1112 	vget(rfsmp->rfsmp_rvp, LK_EXCLUSIVE | LK_RETRY);
   1113 	*vpp = rfsmp->rfsmp_rvp;
   1114 	return 0;
   1115 }
   1116 
   1117 int
   1118 rumpfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
   1119 {
   1120 
   1121 	return EOPNOTSUPP;
   1122 }
   1123 
   1124 void
   1125 rumpfs_init()
   1126 {
   1127 
   1128 	CTASSERT(RUMP_ETFS_SIZE_ENDOFF == RUMPBLK_SIZENOTSET);
   1129 
   1130 	mutex_init(&reclock, MUTEX_DEFAULT, IPL_NONE);
   1131 	mutex_init(&etfs_lock, MUTEX_DEFAULT, IPL_NONE);
   1132 }
   1133 
   1134 void
   1135 rumpfs_done()
   1136 {
   1137 
   1138 	mutex_destroy(&reclock);
   1139 	mutex_destroy(&etfs_lock);
   1140 }
   1141 
   1142 int
   1143 rumpfs_mountroot()
   1144 {
   1145 	struct mount *mp;
   1146 	struct rumpfs_mount *rfsmp;
   1147 	struct rumpfs_node *rn;
   1148 	int error;
   1149 
   1150 	if ((error = vfs_rootmountalloc(MOUNT_RUMPFS, "rootdev", &mp)) != 0) {
   1151 		vrele(rootvp);
   1152 		return error;
   1153 	}
   1154 
   1155 	rfsmp = kmem_alloc(sizeof(*rfsmp), KM_SLEEP);
   1156 
   1157 	rn = makeprivate(VDIR, NODEV, DEV_BSIZE);
   1158 	error = makevnode(mp, rn, &rfsmp->rfsmp_rvp);
   1159 	if (error)
   1160 		panic("could not create root vnode: %d", error);
   1161 	rfsmp->rfsmp_rvp->v_vflag |= VV_ROOT;
   1162 	VOP_UNLOCK(rfsmp->rfsmp_rvp, 0);
   1163 
   1164 	mutex_enter(&mountlist_lock);
   1165 	CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
   1166 	mutex_exit(&mountlist_lock);
   1167 
   1168 	mp->mnt_data = rfsmp;
   1169 	mp->mnt_stat.f_namemax = MAXNAMLEN;
   1170 	mp->mnt_stat.f_iosize = 512;
   1171 	mp->mnt_flag |= MNT_LOCAL;
   1172 	mp->mnt_iflag |= IMNT_MPSAFE;
   1173 	vfs_getnewfsid(mp);
   1174 
   1175 	error = set_statvfs_info("/", UIO_SYSSPACE, "rumpfs", UIO_SYSSPACE,
   1176 	    mp->mnt_op->vfs_name, mp, curlwp);
   1177 	if (error)
   1178 		panic("set statvfsinfo for rootfs failed");
   1179 
   1180 	vfs_unbusy(mp, false, NULL);
   1181 
   1182 	return 0;
   1183 }
   1184