Home | History | Annotate | Line # | Download | only in puffs
puffs_vnops.c revision 1.3
      1 /*	$NetBSD: puffs_vnops.c,v 1.3 2006/10/25 18:15:39 pooka Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2005, 2006  Antti Kantee.  All Rights Reserved.
      5  *
      6  * Development of this software was supported by the
      7  * Google Summer of Code program and the Ulla Tuominen Foundation.
      8  * The Google SoC project was mentored by Bill Studenmund.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. The name of the company nor the name of the author may be used to
     19  *    endorse or promote products derived from this software without specific
     20  *    prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     23  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     25  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 #include <sys/cdefs.h>
     36 __KERNEL_RCSID(0, "$NetBSD: puffs_vnops.c,v 1.3 2006/10/25 18:15:39 pooka Exp $");
     37 
     38 #include <sys/param.h>
     39 #include <sys/vnode.h>
     40 #include <sys/mount.h>
     41 #include <sys/malloc.h>
     42 #include <sys/namei.h>
     43 
     44 #include <fs/puffs/puffs_msgif.h>
     45 #include <fs/puffs/puffs_sys.h>
     46 
     47 #include <miscfs/genfs/genfs.h>
     48 
     49 int	puffs_lookup(void *);
     50 int	puffs_create(void *);
     51 int	puffs_access(void *);
     52 int	puffs_mknod(void *);
     53 int	puffs_open(void *);
     54 int	puffs_close(void *);
     55 int	puffs_getattr(void *);
     56 int	puffs_setattr(void *);
     57 int	puffs_revoke(void *);
     58 int	puffs_reclaim(void *);
     59 int	puffs_readdir(void *);
     60 int	puffs_poll(void *);
     61 int	puffs_fsync(void *);
     62 int	puffs_seek(void *);
     63 int	puffs_remove(void *);
     64 int	puffs_mkdir(void *);
     65 int	puffs_rmdir(void *);
     66 int	puffs_link(void *);
     67 int	puffs_readlink(void *);
     68 int	puffs_symlink(void *);
     69 int	puffs_rename(void *);
     70 int	puffs_read(void *);
     71 int	puffs_write(void *);
     72 int	puffs_fcntl(void *);
     73 int	puffs_ioctl(void *);
     74 int	puffs_inactive(void *);
     75 int	puffs_print(void *);
     76 int	puffs_pathconf(void *);
     77 int	puffs_advlock(void *);
     78 
     79 
     80 /* Need to support */
     81 #define puffs_putpages	puffs_generic
     82 #define puffs_getpages	puffs_generic
     83 
     84 /* VOP_LEASE() not included */
     85 
     86 int	puffs_generic(void *);
     87 
     88 #if 0
     89 #define puffs_lock genfs_lock
     90 #define puffs_unlock genfs_unlock
     91 #define puffs_islocked genfs_islocked
     92 #else
     93 int puffs_lock(void *);
     94 int puffs_unlock(void *);
     95 int puffs_islocked(void *);
     96 #endif
     97 
     98 int (**puffs_vnodeop_p)(void *);
     99 const struct vnodeopv_entry_desc puffs_vnodeop_entries[] = {
    100 	{ &vop_default_desc, vn_default_error },
    101 	{ &vop_lookup_desc, puffs_lookup },		/* lookup */
    102 	{ &vop_create_desc, puffs_create },		/* create */
    103         { &vop_mknod_desc, puffs_mknod },		/* mknod */
    104         { &vop_open_desc, puffs_open },			/* open */
    105         { &vop_close_desc, puffs_close },		/* close */
    106         { &vop_access_desc, puffs_access },		/* access */
    107         { &vop_getattr_desc, puffs_getattr },		/* getattr */
    108         { &vop_setattr_desc, puffs_setattr },		/* setattr */
    109         { &vop_read_desc, puffs_read },			/* read */
    110         { &vop_write_desc, puffs_write },		/* write */
    111         { &vop_fcntl_desc, puffs_fcntl },		/* fcntl */
    112         { &vop_ioctl_desc, puffs_ioctl },		/* ioctl */
    113         { &vop_revoke_desc, puffs_revoke },		/* revoke */
    114         { &vop_fsync_desc, puffs_fsync },		/* fsync */
    115         { &vop_seek_desc, puffs_seek },			/* seek */
    116         { &vop_remove_desc, puffs_remove },		/* remove */
    117         { &vop_link_desc, puffs_link },			/* link */
    118         { &vop_rename_desc, puffs_rename },		/* rename */
    119         { &vop_mkdir_desc, puffs_mkdir },		/* mkdir */
    120         { &vop_rmdir_desc, puffs_rmdir },		/* rmdir */
    121         { &vop_symlink_desc, puffs_symlink },		/* symlink */
    122         { &vop_readdir_desc, puffs_readdir },		/* readdir */
    123         { &vop_readlink_desc, puffs_readlink },		/* readlink */
    124         { &vop_abortop_desc, genfs_abortop },		/* abortop */
    125         { &vop_inactive_desc, puffs_inactive },		/* inactive */
    126         { &vop_reclaim_desc, puffs_reclaim },		/* reclaim */
    127         { &vop_lock_desc, puffs_lock },			/* lock */
    128         { &vop_unlock_desc, puffs_unlock },		/* unlock */
    129         { &vop_bmap_desc, genfs_eopnotsupp },		/* bmap */
    130         { &vop_strategy_desc, genfs_eopnotsupp },	/* strategy */
    131         { &vop_print_desc, puffs_print },		/* print */
    132         { &vop_islocked_desc, puffs_islocked },		/* islocked */
    133         { &vop_pathconf_desc, puffs_pathconf },		/* pathconf */
    134         { &vop_advlock_desc, puffs_advlock },		/* advlock */
    135         { &vop_bwrite_desc, genfs_nullop },		/* bwrite */
    136 #if 0
    137         { &vop_getpages_desc, puffs_getpages },		/* getpages */
    138 #endif
    139         { &vop_putpages_desc, genfs_null_putpages },	/* putpages */
    140 
    141         { &vop_poll_desc, genfs_eopnotsupp },		/* poll XXX */
    142         { &vop_poll_desc, genfs_eopnotsupp },		/* kqfilter XXX */
    143         { &vop_mmap_desc, genfs_eopnotsupp },		/* mmap XXX */
    144 	{ NULL, NULL }
    145 };
    146 const struct vnodeopv_desc puffs_vnodeop_opv_desc =
    147 	{ &puffs_vnodeop_p, puffs_vnodeop_entries };
    148 
    149 #define LOCKEDVP(a) (VOP_ISLOCKED(a) ? (a) : NULL)
    150 
    151 
    152 int
    153 puffs_lookup(void *v)
    154 {
    155         struct vop_lookup_args /* {
    156                 struct vnode * a_dvp;
    157                 struct vnode ** a_vpp;
    158                 struct componentname * a_cnp;
    159         } */ *ap = v;
    160 	struct puffs_mount *pmp;
    161 	struct componentname *cnp;
    162 	struct vnode *vp, *dvp;
    163 	int wantpunlock, isdot;
    164 	int error;
    165 
    166 	PUFFS_VNREQ(lookup);
    167 
    168 	pmp = MPTOPUFFSMP(ap->a_dvp->v_mount);
    169 	cnp = ap->a_cnp;
    170 	dvp = ap->a_dvp;
    171 	*ap->a_vpp = NULL;
    172 
    173 	/* first things first: check access */
    174 	error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, cnp->cn_lwp);
    175 	if (error)
    176 		return error;
    177 
    178 	wantpunlock = ~cnp->cn_flags & (LOCKPARENT | ISLASTCN);
    179 	isdot = cnp->cn_namelen == 1 && *cnp->cn_nameptr == '.';
    180 
    181 	DPRINTF(("puffs_lookup: \"%s\", parent vnode %p, op: %lx\n",
    182 	    cnp->cn_nameptr, dvp, cnp->cn_nameiop));
    183 
    184 	/*
    185 	 * Do sanity checks we can do without consulting userland.
    186 	 */
    187 
    188 	/*
    189 	 * last component check & ro fs
    190 	 *
    191 	 * hmmm... why doesn't this check for create?
    192 	 */
    193 	if ((cnp->cn_flags & ISLASTCN)
    194 	    && (ap->a_dvp->v_mount->mnt_flag & MNT_RDONLY)
    195 	    && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
    196 		DPRINTF(("puffs_lookup: write lookup for read-only fs!\n"));
    197 		return EROFS;
    198 	}
    199 
    200 	/*
    201 	 * Check if someone fed it into the cache
    202 	 */
    203 	error = cache_lookup(dvp, ap->a_vpp, cnp);
    204 	if (error >= 0)
    205 		return error;
    206 
    207 	if (isdot) {
    208 		vp = ap->a_dvp;
    209 		vref(vp);
    210 		*ap->a_vpp = vp;
    211 		return 0;
    212 	}
    213 
    214 	puffs_makecn(&lookup_arg.pvnr_cn, cnp);
    215 
    216 	if (cnp->cn_flags & ISDOTDOT)
    217 		VOP_UNLOCK(dvp, 0);
    218 
    219 	error = puffs_vntouser(pmp, PUFFS_VN_LOOKUP,
    220 	    &lookup_arg, sizeof(lookup_arg), VPTOPNC(dvp), LOCKEDVP(dvp), NULL);
    221 	DPRINTF(("puffs_lookup: return of the userspace, part %d\n", error));
    222 
    223 	/*
    224 	 * In case of error, leave parent locked.  There is no new
    225 	 * vnode to play with, so be happy with the NULL value given
    226 	 * to vpp in the beginning.
    227 	 */
    228 	if (error) {
    229 		if (error == -1) {
    230 			if ((cnp->cn_flags & ISLASTCN)
    231 			    && (cnp->cn_nameiop == CREATE
    232 			      || cnp->cn_nameiop == RENAME)) {
    233 				cnp->cn_flags |= SAVENAME;
    234 				error = EJUSTRETURN;
    235 			} else
    236 				/* userspace is on crack */
    237 				error = ENOENT;
    238 		}
    239 		*ap->a_vpp = NULL;
    240 		if (cnp->cn_flags & ISDOTDOT)
    241 			if (vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY) != 0)
    242 				cnp->cn_flags |= PDIRUNLOCK;
    243 		return error;
    244 	}
    245 
    246 	vp = puffs_pnode2vnode(pmp, lookup_arg.pvnr_newnode);
    247 	if (!vp) {
    248 		error = puffs_getvnode(dvp->v_mount,
    249 		    lookup_arg.pvnr_newnode, &vp);
    250 		if (error) {
    251 			if (cnp->cn_flags & ISDOTDOT)
    252 				if (vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY) != 0)
    253 					cnp->cn_flags |= PDIRUNLOCK;
    254 			return error;
    255 		}
    256 		vp->v_type = lookup_arg.pvnr_vtype;
    257 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
    258 	}
    259 
    260 	if (cnp->cn_flags & ISDOTDOT) {
    261 		if (cnp->cn_flags & LOCKPARENT &&
    262 		    cnp->cn_flags & ISLASTCN) {
    263 			if (vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY) != 0) {
    264 				cnp->cn_flags |= PDIRUNLOCK;
    265 			}
    266 		}
    267 	} else  {
    268 		if (wantpunlock) {
    269 			VOP_UNLOCK(dvp, 0);
    270 			cnp->cn_flags |= PDIRUNLOCK;
    271 		}
    272 	}
    273 
    274 	if (cnp->cn_flags & MAKEENTRY)
    275 		cache_enter(dvp, vp, cnp);
    276 	*ap->a_vpp = vp;
    277 
    278 	return 0;
    279 }
    280 
    281 int
    282 puffs_create(void *v)
    283 {
    284 	struct vop_create_args /* {
    285 		const struct vnodeop_desc *a_desc;
    286 		struct vnode *a_dvp;
    287 		struct vnode **a_vpp;
    288 		struct componentname *a_cnp;
    289 		struct vattr *a_vap;
    290 	} */ *ap = v;
    291 	int error;
    292 
    293 	PUFFS_VNREQ(create);
    294 
    295 	puffs_makecn(&create_arg.pvnr_cn, ap->a_cnp);
    296 	create_arg.pvnr_va = *ap->a_vap;
    297 
    298 	error = puffs_vntouser(MPTOPUFFSMP(ap->a_dvp->v_mount), PUFFS_VN_CREATE,
    299 	    &create_arg, sizeof(create_arg), VPTOPNC(ap->a_dvp),
    300 	    ap->a_dvp, NULL);
    301 	if (error)
    302 		return error;
    303 
    304 	return puffs_newnode(ap->a_dvp->v_mount, ap->a_dvp, ap->a_vpp,
    305 	    create_arg.pvnr_newnode, ap->a_cnp, ap->a_vap->va_type);
    306 }
    307 
    308 int
    309 puffs_mknod(void *v)
    310 {
    311 	struct vop_mknod_args /* {
    312 		const struct vnodeop_desc *a_desc;
    313 		struct vnode *a_dvp;
    314 		struct vnode **a_vpp;
    315 		struct componentname *a_cnp;
    316 		struct vattr *a_vap;
    317 	} */ *ap = v;
    318 	int error;
    319 
    320 	PUFFS_VNREQ(mknod);
    321 
    322 	puffs_makecn(&mknod_arg.pvnr_cn, ap->a_cnp);
    323 	mknod_arg.pvnr_va = *ap->a_vap;
    324 
    325 	error = puffs_vntouser(MPTOPUFFSMP(ap->a_dvp->v_mount), PUFFS_VN_MKNOD,
    326 	    &mknod_arg, sizeof(mknod_arg), VPTOPNC(ap->a_dvp), ap->a_dvp, NULL);
    327 	if (error)
    328 		return error;
    329 
    330 	return puffs_newnode(ap->a_dvp->v_mount, ap->a_dvp, ap->a_vpp,
    331 	    mknod_arg.pvnr_newnode, ap->a_cnp, ap->a_vap->va_type);
    332 }
    333 
    334 int
    335 puffs_open(void *v)
    336 {
    337 	struct vop_open_args /* {
    338 		const struct vnodeop_desc *a_desc;
    339 		struct vnode *a_vp;
    340 		int a_mode;
    341 		kauth_cred_t a_cred;
    342 		struct lwp *a_l;
    343 	} */ *ap = v;
    344 
    345 	PUFFS_VNREQ(open);
    346 
    347 	open_arg.pvnr_mode = ap->a_mode;
    348 	puffs_credcvt(&open_arg.pvnr_cred, ap->a_cred);
    349 	open_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
    350 
    351 	return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_OPEN,
    352 	    &open_arg, sizeof(open_arg), VPTOPNC(ap->a_vp), ap->a_vp, NULL);
    353 }
    354 
    355 int
    356 puffs_close(void *v)
    357 {
    358 	struct vop_close_args /* {
    359 		const struct vnodeop_desc *a_desc;
    360 		struct vnode *a_vp;
    361 		int a_fflag;
    362 		kauth_cred_t a_cred;
    363 		struct lwp *a_l;
    364 	} */ *ap = v;
    365 
    366 	PUFFS_VNREQ(close);
    367 
    368 	close_arg.pvnr_fflag = ap->a_fflag;
    369 	puffs_credcvt(&close_arg.pvnr_cred, ap->a_cred);
    370 	close_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
    371 
    372 	return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_CLOSE,
    373 	    &close_arg, sizeof(close_arg), VPTOPNC(ap->a_vp), ap->a_vp, NULL);
    374 }
    375 
    376 int
    377 puffs_access(void *v)
    378 {
    379 	struct vop_access_args /* {
    380 		const struct vnodeop_desc *a_desc;
    381 		struct vnode *a_vp;
    382 		int a_mode;
    383 		kauth_cred_t a_cred;
    384 		struct lwp *a_l;
    385 	} */ *ap = v;
    386 	int error;
    387 
    388 	PUFFS_VNREQ(access);
    389 
    390 	access_arg.pvnr_mode = ap->a_mode;
    391 	access_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
    392 	puffs_credcvt(&access_arg.pvnr_cred, ap->a_cred);
    393 
    394 	error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_ACCESS,
    395 	    &access_arg, sizeof(access_arg), VPTOPNC(ap->a_vp), ap->a_vp, NULL);
    396 	if (error)
    397 		return error;
    398 
    399 	/*
    400 	 * XXXtothepeople: no execute permissions yet.  Otherwise
    401 	 * all hell will break loose if we try to execute a file
    402 	 * without VOP_GETPAGES support.  It is forthcoming, just
    403 	 * not there yet ...
    404 	 */
    405 	if (ap->a_mode == VEXEC && ap->a_vp->v_type != VDIR)
    406 		return EACCES;
    407 
    408 	return 0;
    409 }
    410 
    411 int
    412 puffs_getattr(void *v)
    413 {
    414 	struct vop_getattr_args /* {
    415 		const struct vnodeop_desc *a_desc;
    416 		struct vnode *a_vp;
    417 		struct vattr *a_vap;
    418 		kauth_cred_t a_cred;
    419 		struct lwp *a_l;
    420 	} */ *ap = v;
    421 	int error;
    422 
    423 	PUFFS_VNREQ(getattr);
    424 
    425 	vattr_null(&getattr_arg.pvnr_va);
    426 	puffs_credcvt(&getattr_arg.pvnr_cred, ap->a_cred);
    427 	getattr_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
    428 
    429 	/*
    430 	 * XXX + XX (dos equis): this can't go through the unlock/lock
    431 	 * cycle, since it can be called from uvn_attach(), which fiddles
    432 	 * around with VXLOCK and therefore breaks vn_lock().  Proper
    433 	 * fix pending.
    434 	 */
    435 	error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_GETATTR,
    436 	    &getattr_arg, sizeof(getattr_arg), VPTOPNC(ap->a_vp),
    437 	    NULL /* XXXseeabove: should be LOCKEDVP(ap->a_vp) */, NULL);
    438 	if (error)
    439 		return error;
    440 
    441 	(void)memcpy(ap->a_vap, &getattr_arg.pvnr_va, sizeof(struct vattr));
    442 
    443 	/*
    444 	 * XXXtothepeople: adjust the return value so that we don't
    445 	 * advertise execute bits.  Otherwise all hell will break
    446 	 * loose if we try to execute a file without VOP_GETPAGES
    447 	 * support.  It is forthcoming, just not there yet ...
    448 	 */
    449 	if (ap->a_vp->v_type != VDIR)
    450 		ap->a_vap->va_mode &= ~0111;
    451 
    452 	return 0;
    453 }
    454 
    455 int
    456 puffs_setattr(void *v)
    457 {
    458 	struct vop_getattr_args /* {
    459 		const struct vnodeop_desc *a_desc;
    460 		struct vnode *a_vp;
    461 		struct vattr *a_vap;
    462 		kauth_cred_t a_cred;
    463 		struct lwp *a_l;
    464 	} */ *ap = v;
    465 
    466 	PUFFS_VNREQ(setattr);
    467 
    468 	(void)memcpy(&setattr_arg.pvnr_va, ap->a_vap, sizeof(struct vattr));
    469 	puffs_credcvt(&setattr_arg.pvnr_cred, ap->a_cred);
    470 	setattr_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
    471 
    472 	return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_SETATTR,
    473 	    &setattr_arg, sizeof(setattr_arg), VPTOPNC(ap->a_vp),
    474 	    ap->a_vp, NULL);
    475 }
    476 
    477 int
    478 puffs_revoke(void *v)
    479 {
    480 	struct vop_revoke_args /* {
    481 		const struct vnodeop_desc *a_desc;
    482 		struct vnode *a_vp;
    483 		int a_flags;
    484 	} */ *ap = v;
    485 	PUFFS_VNREQ(revoke);
    486 
    487 	revoke_arg.pvnr_flags = ap->a_flags;
    488 
    489 	/* don't really care if userspace doesn't want to play along */
    490 	puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_REVOKE,
    491 	    &revoke_arg, sizeof(revoke_arg), VPTOPNC(ap->a_vp), NULL, NULL);
    492 
    493 	return genfs_revoke(v);
    494 }
    495 
    496 int
    497 puffs_inactive(void *v)
    498 {
    499 	struct vop_inactive_args /* {
    500 		const struct vnodeop_desc *a_desc;
    501 		struct vnode *a_vp;
    502 		struct lwp *a_l;
    503 	} */ *ap = v;
    504 	struct puffs_node *pnode;
    505 	int rv, vnrefs;
    506 
    507 	PUFFS_VNREQ(inactive);
    508 
    509 	/*
    510 	 * XXX: think about this after we really start unlocking
    511 	 * when going to userspace
    512 	 */
    513 	pnode = ap->a_vp->v_data;
    514 	pnode->pn_stat |= PNODE_INACTIVE;
    515 
    516 	inactive_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
    517 
    518 	rv = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_INACTIVE,
    519 	    &inactive_arg, sizeof(inactive_arg), VPTOPNC(ap->a_vp),
    520 	    ap->a_vp, NULL);
    521 
    522 	/* can't trust userspace return value?  simulate safe answer */
    523 	if (rv)
    524 		vnrefs = 1;
    525 	else
    526 		vnrefs = inactive_arg.pvnr_backendrefs;
    527 
    528 	VOP_UNLOCK(ap->a_vp, 0);
    529 
    530 	/*
    531 	 * user server thinks it's gone?  then don't be afraid care,
    532 	 * node's life was already all it would ever be
    533 	 */
    534 	if (vnrefs == 0)
    535 		vrecycle(ap->a_vp, NULL, ap->a_l);
    536 
    537 	return 0;
    538 }
    539 
    540 int
    541 puffs_reclaim(void *v)
    542 {
    543 	struct vop_reclaim_args /* {
    544 		const struct vnodeop_desc *a_desc;
    545 		struct vnode *a_vp;
    546 		struct lwp *a_l;
    547 	} */ *ap = v;
    548 	struct puffs_mount *pmp;
    549 	int error;
    550 
    551 	PUFFS_VNREQ(reclaim);
    552 
    553 	/*
    554 	 * first things first: check if someone is trying to reclaim the
    555 	 * root vnode.  do not allow that to travel to userspace.
    556 	 * Note that we don't need to take the lock similarly to
    557 	 * puffs_root(), since there is only one of us.
    558 	 */
    559 	if (ap->a_vp->v_flag & VROOT) {
    560 		pmp = MPTOPUFFSMP(ap->a_vp->v_mount);
    561 #ifdef DIAGNOSTIC
    562 		simple_lock(&pmp->pmp_lock);
    563 		if (pmp->pmp_root == NULL)
    564 			panic("puffs_reclaim: releasing root vnode (%p) twice",
    565 			    ap->a_vp);
    566 		simple_unlock(&pmp->pmp_lock);
    567 #endif
    568 		pmp->pmp_root = NULL;
    569 		puffs_putvnode(ap->a_vp);
    570 		return 0;
    571 	}
    572 
    573 	reclaim_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
    574 
    575 	error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_RECLAIM,
    576 	    &reclaim_arg, sizeof(reclaim_arg), VPTOPNC(ap->a_vp), NULL, NULL);
    577 #if 0
    578 	/*
    579 	 * XXX: if reclaim fails for any other reason than the userspace
    580 	 * being dead, we should consider unmounting the filesystem, since
    581 	 * we can't trust it to be in a consistent state anymore.  But for
    582 	 * now, just ignore all errors.
    583 	 */
    584 	if (error)
    585 		return error;
    586 #endif
    587 
    588 	puffs_putvnode(ap->a_vp);
    589 
    590 	return 0;
    591 }
    592 
    593 int
    594 puffs_readdir(void *v)
    595 {
    596 	struct vop_readdir_args /* {
    597 		const struct vnodeop_desc *a_desc;
    598 		struct vnode *a_vp;
    599 		struct uio *a_uio;
    600 		kauth_cred_t a_cred;
    601 		int *a_eofflag;
    602 		off_t **a_cookies;
    603 		int *a_ncookies;
    604 	} */ *ap = v;
    605 	struct puffs_vnreq_readdir *readdir_argp;
    606 	size_t argsize;
    607 	struct uio *uio = ap->a_uio;
    608 	size_t howmuch;
    609 	int error;
    610 
    611 	/* worry about these later */
    612 	if (!(ap->a_cookies == NULL && ap->a_ncookies == NULL))
    613 		return EOPNOTSUPP;
    614 
    615 	argsize = sizeof(struct puffs_vnreq_readdir);
    616 	readdir_argp = malloc(argsize, M_PUFFS, M_ZERO | M_WAITOK);
    617 
    618 	puffs_credcvt(&readdir_argp->pvnr_cred, ap->a_cred);
    619 	readdir_argp->pvnr_offset = uio->uio_offset;
    620 	readdir_argp->pvnr_resid = uio->uio_resid;
    621 
    622 	error = puffs_vntouser_adjbuf(MPTOPUFFSMP(ap->a_vp->v_mount),
    623 	    PUFFS_VN_READDIR, (void **)&readdir_argp, &argsize,
    624 	    sizeof(struct puffs_vnreq_readdir),
    625 	    VPTOPNC(ap->a_vp), ap->a_vp, NULL);
    626 	if (error)
    627 		goto out;
    628 
    629 	/* userspace is cheating? */
    630 	if (readdir_argp->pvnr_resid > uio->uio_resid) {
    631 		error = EINVAL;
    632 		goto out;
    633 	}
    634 
    635 	/* bouncy-wouncy with the directory data */
    636 	howmuch = uio->uio_resid - readdir_argp->pvnr_resid;
    637 	error = uiomove(readdir_argp->pvnr_dent, howmuch, uio);
    638 	if (error)
    639 		goto out;
    640 	uio->uio_offset = readdir_argp->pvnr_offset;
    641 
    642  out:
    643 	free(readdir_argp, M_PUFFS);
    644 	return error;
    645 }
    646 
    647 int
    648 puffs_poll(void *v)
    649 {
    650 	struct vop_poll_args /* {
    651 		const struct vnodeop_desc *a_desc;
    652 		struct vnode *a_vp;
    653 		int a_events;
    654 		struct lwp *a_l;
    655 	}*/ *ap = v;
    656 
    657 	PUFFS_VNREQ(poll);
    658 
    659 	poll_arg.pvnr_events = ap->a_events;
    660 	poll_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
    661 
    662 	return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_POLL,
    663 	    &poll_arg, sizeof(poll_arg), VPTOPNC(ap->a_vp), NULL, NULL);
    664 }
    665 
    666 int
    667 puffs_fsync(void *v)
    668 {
    669 	struct vop_fsync_args /* {
    670 		const struct vnodeop_desc *a_desc;
    671 		struct vnode *a_vp;
    672 		kauth_cred_t a_cred;
    673 		int a_flags;
    674 		off_t a_offlo;
    675 		off_t a_offhi;
    676 		struct lwp *a_l;
    677 	} */ *ap = v;
    678 
    679 	PUFFS_VNREQ(fsync);
    680 
    681 	puffs_credcvt(&fsync_arg.pvnr_cred, ap->a_cred);
    682 	fsync_arg.pvnr_flags = ap->a_flags;
    683 	fsync_arg.pvnr_offlo = ap->a_offlo;
    684 	fsync_arg.pvnr_offhi = ap->a_offhi;
    685 	fsync_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
    686 
    687 	/*
    688 	 * XXX: see comment at puffs_getattr about locking
    689 	 */
    690 	return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_FSYNC,
    691 	    &fsync_arg, sizeof(fsync_arg), VPTOPNC(ap->a_vp),
    692 	    NULL /* XXXshouldbe: ap->a_vp */, NULL);
    693 }
    694 
    695 int
    696 puffs_seek(void *v)
    697 {
    698 	struct vop_seek_args /* {
    699 		const struct vnodeop_desc *a_desc;
    700 		struct vnode *a_vp;
    701 		off_t a_oldoff;
    702 		off_t a_newoff;
    703 		kauth_cred_t a_cred;
    704 	} */ *ap = v;
    705 
    706 	PUFFS_VNREQ(seek);
    707 
    708 	seek_arg.pvnr_oldoff = ap->a_oldoff;
    709 	seek_arg.pvnr_newoff = ap->a_newoff;
    710 	puffs_credcvt(&seek_arg.pvnr_cred, ap->a_cred);
    711 
    712 	/*
    713 	 * XXX: seems like seek is called with an unlocked vp, but
    714 	 * it can't hurt to play safe
    715 	 */
    716 	return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_SEEK,
    717 	    &seek_arg, sizeof(seek_arg), VPTOPNC(ap->a_vp),
    718 	    LOCKEDVP(ap->a_vp), NULL);
    719 }
    720 
    721 int
    722 puffs_remove(void *v)
    723 {
    724 	struct vop_remove_args /* {
    725 		const struct vnodeop_desc *a_desc;
    726 		struct vnode *a_dvp;
    727 		struct vnode *a_vp;
    728 		struct componentname *a_cnp;
    729 	} */ *ap = v;
    730 	int error;
    731 
    732 	PUFFS_VNREQ(remove);
    733 
    734 	remove_arg.pvnr_cookie_targ = VPTOPNC(ap->a_vp);
    735 	puffs_makecn(&remove_arg.pvnr_cn, ap->a_cnp);
    736 
    737 	error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_REMOVE,
    738 	    &remove_arg, sizeof(remove_arg), VPTOPNC(ap->a_dvp),
    739 	    ap->a_dvp, ap->a_vp);
    740 
    741 	vput(ap->a_vp);
    742 	vput(ap->a_dvp);
    743 
    744 	return error;
    745 }
    746 
    747 int
    748 puffs_mkdir(void *v)
    749 {
    750 	struct vop_mkdir_args /* {
    751 		const struct vnodeop_desc *a_desc;
    752 		struct vnode *a_dvp;
    753 		struct vnode **a_vpp;
    754 		struct componentname *a_cnp;
    755 		struct vattr *a_vap;
    756 	} */ *ap = v;
    757 	int error;
    758 
    759 	PUFFS_VNREQ(mkdir);
    760 
    761 	puffs_makecn(&mkdir_arg.pvnr_cn, ap->a_cnp);
    762 	mkdir_arg.pvnr_va = *ap->a_vap;
    763 
    764 	/* XXX: wouldn't need to relock dvp, but that's life */
    765 	error = puffs_vntouser(MPTOPUFFSMP(ap->a_dvp->v_mount), PUFFS_VN_MKDIR,
    766 	    &mkdir_arg, sizeof(mkdir_arg), VPTOPNC(ap->a_dvp), ap->a_dvp, NULL);
    767 	if (error)
    768 		return error;
    769 
    770 	return puffs_newnode(ap->a_dvp->v_mount, ap->a_dvp, ap->a_vpp,
    771 	    mkdir_arg.pvnr_newnode, ap->a_cnp, VDIR);
    772 }
    773 
    774 int
    775 puffs_rmdir(void *v)
    776 {
    777 	struct vop_rmdir_args /* {
    778 		const struct vnodeop_desc *a_desc;
    779 		struct vnode *a_dvp;
    780 		struct vnode *a_vp;
    781 		struct componentname *a_cnp;
    782 	} */ *ap = v;
    783 	int error;
    784 
    785 	PUFFS_VNREQ(rmdir);
    786 
    787 	rmdir_arg.pvnr_cookie_targ = VPTOPNC(ap->a_vp);
    788 	puffs_makecn(&rmdir_arg.pvnr_cn, ap->a_cnp);
    789 
    790 	error = puffs_vntouser(MPTOPUFFSMP(ap->a_dvp->v_mount), PUFFS_VN_RMDIR,
    791 	    &rmdir_arg, sizeof(rmdir_arg), VPTOPNC(ap->a_dvp),
    792 	    ap->a_dvp, ap->a_vp);
    793 
    794 	vput(ap->a_dvp);
    795 	vput(ap->a_vp);
    796 
    797 	return error;
    798 }
    799 
    800 int
    801 puffs_link(void *v)
    802 {
    803 	struct vop_link_args /* {
    804 		const struct vnodeop_desc *a_desc;
    805 		struct vnode *a_dvp;
    806 		struct vnode *a_vp;
    807 		struct componentname *a_cnp;
    808 	}*/ *ap = v;
    809 	int error;
    810 
    811 	PUFFS_VNREQ(link);
    812 
    813 	link_arg.pvnr_cookie_targ = VPTOPNC(ap->a_vp);
    814 	puffs_makecn(&link_arg.pvnr_cn, ap->a_cnp);
    815 
    816 	error = puffs_vntouser(MPTOPUFFSMP(ap->a_dvp->v_mount), PUFFS_VN_LINK,
    817 	    &link_arg, sizeof(link_arg), VPTOPNC(ap->a_dvp), ap->a_dvp, NULL);
    818 
    819 	vput(ap->a_dvp);
    820 
    821 	return error;
    822 }
    823 
    824 int
    825 puffs_symlink(void *v)
    826 {
    827 	struct vop_symlink_args /* {
    828 		const struct vnodeop_desc *a_desc;
    829 		struct vnode *a_dvp;
    830 		struct vnode **a_vpp;
    831 		struct componentname *a_cnp;
    832 		struct vattr *a_vap;
    833 		char *a_target;
    834 	}*/ *ap = v;
    835 	int error;
    836 
    837 	PUFFS_VNREQ(symlink); /* XXX: large structure */
    838 
    839 	*ap->a_vpp = NULL;
    840 
    841 	puffs_makecn(&symlink_arg.pvnr_cn, ap->a_cnp);
    842 	symlink_arg.pvnr_va = *ap->a_vap;
    843 	(void)strlcpy(symlink_arg.pvnr_link, ap->a_target,
    844 	    sizeof(symlink_arg.pvnr_link));
    845 
    846 	/* XXX: don't need to relock parent */
    847 	error =  puffs_vntouser(MPTOPUFFSMP(ap->a_dvp->v_mount),
    848 	    PUFFS_VN_SYMLINK, &symlink_arg, sizeof(symlink_arg),
    849 	    VPTOPNC(ap->a_dvp), ap->a_dvp, NULL);
    850 	if (error)
    851 		return error;
    852 
    853 	return puffs_newnode(ap->a_dvp->v_mount, ap->a_dvp, ap->a_vpp,
    854 	    symlink_arg.pvnr_newnode, ap->a_cnp, VLNK);
    855 }
    856 
    857 int
    858 puffs_readlink(void *v)
    859 {
    860 	struct vop_readlink_args /* {
    861 		const struct vnodeop_desc *a_desc;
    862 		struct vnode *a_vp;
    863 		struct uio *a_uio;
    864 		kauth_cred_t a_cred;
    865 	} */ *ap = v;
    866 	int error;
    867 
    868 	PUFFS_VNREQ(readlink);
    869 
    870 	puffs_credcvt(&readlink_arg.pvnr_cred, ap->a_cred);
    871 	readlink_arg.pvnr_linklen = sizeof(readlink_arg.pvnr_link);
    872 
    873 	error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount),
    874 	    PUFFS_VN_READLINK, &readlink_arg, sizeof(readlink_arg),
    875 	    VPTOPNC(ap->a_vp), ap->a_vp, NULL);
    876 	if (error)
    877 		return error;
    878 
    879 	readlink_arg.pvnr_link[readlink_arg.pvnr_linklen] = '\0';
    880 	return uiomove(&readlink_arg.pvnr_link, readlink_arg.pvnr_linklen,
    881 	    ap->a_uio);
    882 }
    883 
    884 /* XXXXXXX: think about locking & userspace op delocking... */
    885 int
    886 puffs_rename(void *v)
    887 {
    888 	struct vop_rename_args /* {
    889 		const struct vnodeop_desc *a_desc;
    890 		struct vnode *a_fdvp;
    891 		struct vnode *a_fvp;
    892 		struct componentname *a_fcnp;
    893 		struct vnode *a_tdvp;
    894 		struct vnode *a_tvp;
    895 		struct componentname *a_tcnp;
    896 	}*/ *ap = v;
    897 	int error;
    898 
    899 	PUFFS_VNREQ(rename);
    900 
    901 	/*
    902 	 * participate in the duck hunt
    903 	 * (I could do with some canard a la presse, so hopefully
    904 	 *  this is succesful)
    905 	 */
    906 	KASSERT(ap->a_tdvp != ap->a_tvp);
    907 
    908 	if (ap->a_fvp->v_mount != ap->a_tdvp->v_mount) {
    909 		error = EXDEV;
    910 		goto out;
    911 	}
    912 
    913 	rename_arg.pvnr_cookie_src = VPTOPNC(ap->a_fvp);
    914 	rename_arg.pvnr_cookie_targdir = VPTOPNC(ap->a_tdvp);
    915 	if (ap->a_tvp)
    916 		rename_arg.pvnr_cookie_targ = VPTOPNC(ap->a_tvp);
    917 	else
    918 		rename_arg.pvnr_cookie_targ = NULL;
    919 	puffs_makecn(&rename_arg.pvnr_cn_src, ap->a_fcnp);
    920 	puffs_makecn(&rename_arg.pvnr_cn_targ, ap->a_tcnp);
    921 
    922 	error = puffs_vntouser(MPTOPUFFSMP(ap->a_fdvp->v_mount),
    923 	    PUFFS_VN_RENAME, &rename_arg, sizeof(rename_arg),
    924 	    VPTOPNC(ap->a_fdvp), NULL, NULL);
    925 
    926  out:
    927 	vput(ap->a_tdvp);
    928 	if (ap->a_tvp != NULL)
    929 		vput(ap->a_tvp);
    930 
    931 	vrele(ap->a_fdvp);
    932 	vrele(ap->a_fvp);
    933 
    934 	return error;
    935 }
    936 
    937 int
    938 puffs_read(void *v)
    939 {
    940 	struct vop_read_args /* {
    941 		const struct vnodeop_desc *a_desc;
    942 		struct vnode *a_vp;
    943 		struct uio *a_uio;
    944 		int a_ioflag;
    945 		kauth_cred_t a_cred;
    946 	} */ *ap = v;
    947 	struct puffs_vnreq_read *read_argp;
    948 	struct puffs_mount *pmp;
    949 	struct uio *uio;
    950 	size_t tomove, argsize;
    951 	int error;
    952 
    953 	uio = ap->a_uio;
    954 
    955 	pmp = MPTOPUFFSMP(ap->a_vp->v_mount);
    956 	tomove = PUFFS_TOMOVE(uio->uio_resid, pmp);
    957 	argsize = sizeof(struct puffs_vnreq_read);
    958 	read_argp = malloc(argsize, M_PUFFS, M_WAITOK | M_ZERO);
    959 
    960 	error = 0;
    961 	while (uio->uio_resid > 0) {
    962 		read_argp->pvnr_ioflag = ap->a_ioflag;
    963 		read_argp->pvnr_resid = tomove;
    964 		read_argp->pvnr_offset = uio->uio_offset;
    965 		puffs_credcvt(&read_argp->pvnr_cred, ap->a_cred);
    966 
    967 		argsize = sizeof(struct puffs_vnreq_read);
    968 		error = puffs_vntouser_adjbuf(pmp, PUFFS_VN_READ,
    969 		    (void **)&read_argp, &argsize,
    970 		    sizeof(struct puffs_vnreq_read), VPTOPNC(ap->a_vp),
    971 		    ap->a_vp, NULL);
    972 		if (error)
    973 			goto out;
    974 
    975 		if (read_argp->pvnr_resid > tomove) {
    976 			error = EINVAL;
    977 			goto out;
    978 		}
    979 
    980 		error = uiomove(read_argp->pvnr_data,
    981 		    tomove - read_argp->pvnr_resid, uio);
    982 
    983 		/*
    984 		 * in case the file is out of juice, resid from userspace
    985 		 * is != 0.  and the error-case is quite obvious
    986 		 */
    987 		if (error || read_argp->pvnr_resid)
    988 			goto out;
    989 
    990 		tomove = PUFFS_TOMOVE(uio->uio_resid, pmp);
    991 	}
    992 
    993  out:
    994 	free(read_argp, M_PUFFS);
    995 	return error;
    996 }
    997 
    998 int
    999 puffs_write(void *v)
   1000 {
   1001 	struct vop_write_args /* {
   1002 		const struct vnodeop_desc *a_desc;
   1003 		struct vnode *a_vp;
   1004 		struct uio *a_uio;
   1005 		int a_ioflag;
   1006 		kauth_cred_t a_cred;
   1007 	}*/ *ap = v;
   1008 	struct puffs_vnreq_write *write_argp;
   1009 	struct puffs_mount *pmp;
   1010 	struct uio *uio;
   1011 	size_t tomove, argsize;
   1012 	int error;
   1013 
   1014 	uio = ap->a_uio;
   1015 
   1016 	pmp = MPTOPUFFSMP(ap->a_vp->v_mount);
   1017 	tomove = PUFFS_TOMOVE(uio->uio_resid, pmp);
   1018 	argsize = sizeof(struct puffs_vnreq_write) + tomove;
   1019 	write_argp = malloc(argsize, M_PUFFS, M_WAITOK | M_ZERO);
   1020 
   1021 	error = 0;
   1022 	while (uio->uio_resid > 0) {
   1023 		write_argp->pvnr_ioflag = ap->a_ioflag;
   1024 		write_argp->pvnr_resid = tomove;
   1025 		write_argp->pvnr_offset = uio->uio_offset;
   1026 		puffs_credcvt(&write_argp->pvnr_cred, ap->a_cred);
   1027 		error = uiomove(write_argp->pvnr_data, tomove, ap->a_uio);
   1028 		if (error)
   1029 			goto out;
   1030 
   1031 		error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount),
   1032 		    PUFFS_VN_WRITE, write_argp, argsize, VPTOPNC(ap->a_vp),
   1033 		    ap->a_vp, NULL);
   1034 		if (error) {
   1035 			/* restore uiomove */
   1036 			uio->uio_resid += tomove;
   1037 			uio->uio_offset -= tomove;
   1038 			goto out;
   1039 		}
   1040 		if (write_argp->pvnr_resid > tomove) {
   1041 			error = EINVAL;
   1042 			goto out;
   1043 		}
   1044 
   1045 		/* didn't move everything?  bad userspace.  bail */
   1046 		if (write_argp->pvnr_resid != 0) {
   1047 			uio->uio_resid += write_argp->pvnr_resid;
   1048 			uio->uio_offset -= write_argp->pvnr_resid;
   1049 			error = EIO;
   1050 			break;
   1051 		}
   1052 
   1053 		tomove = PUFFS_TOMOVE(uio->uio_resid, pmp);
   1054 	}
   1055 
   1056  out:
   1057 	free(write_argp, M_PUFFS);
   1058 	return error;
   1059 }
   1060 
   1061 static int	puffs_fcnioctl(struct vop_ioctl_args * /*XXX*/, int);
   1062 
   1063 #define FCNIOCTL_ARG_MAX 1<<16
   1064 int
   1065 puffs_fcnioctl(struct vop_ioctl_args *ap, int puffsop)
   1066 {
   1067 	/* struct vop_ioctl_args {
   1068 		const struct vnodeop_desc *a_desc;
   1069 		struct vnode *a_vp;
   1070 		u_long a_command;
   1071 		void *a_data;
   1072 		int a_fflag;
   1073 		kauth_cred_t a_cred;
   1074 		struct lwp *a_l;
   1075 	}*ap = v; */
   1076 	struct puffs_mount *pmp;
   1077 	struct puffs_sizepark pspark;
   1078 	void *kernbuf;
   1079 	size_t copylen;
   1080 	int error;
   1081 
   1082 	PUFFS_VNREQ(fcnioctl);
   1083 
   1084 	/*
   1085 	 * Since this op gives the filesystem (almost) complete control on
   1086 	 * how much it is allowed to copy from the calling process
   1087 	 * address space, do not enable it by default, since it would
   1088 	 * be a whopping security hole.
   1089 	 */
   1090 	pmp = MPTOPUFFSMP(ap->a_vp->v_mount);
   1091 	if ((pmp->pmp_args.pa_flags & PUFFS_FLAG_ALLOWCTL) == 0)
   1092 		return EINVAL; /* only shoe that fits */
   1093 
   1094 	/* fill in sizereq and store it */
   1095 	pspark.pkso_reqid = puffs_getreqid(pmp);
   1096 	pspark.pkso_reqtype = PUFFS_SIZEOPREQ_BUF_IN;
   1097 	pspark.pkso_copybuf = ap->a_data;
   1098 	pspark.pkso_bufsize = FCNIOCTL_ARG_MAX;
   1099 	TAILQ_INSERT_TAIL(&pmp->pmp_req_sizepark, &pspark, pkso_entries);
   1100 
   1101 	/* then fill in actual request and shoot it off */
   1102 	fcnioctl_arg.pvnr_command = ap->a_command;
   1103 	fcnioctl_arg.pvnr_fflag = ap->a_fflag;
   1104 	puffs_credcvt(&fcnioctl_arg.pvnr_cred, ap->a_cred);
   1105 	fcnioctl_arg.pvnr_pid = puffs_lwp2pid(ap->a_l);
   1106 
   1107 	error = puffs_vntouser_req(MPTOPUFFSMP(ap->a_vp->v_mount), puffsop,
   1108 	    &fcnioctl_arg, sizeof(fcnioctl_arg), VPTOPNC(ap->a_vp),
   1109 	    pspark.pkso_reqid, NULL, NULL);
   1110 
   1111 	/* if we don't need to copy data, we're done */
   1112 	if (error || !fcnioctl_arg.pvnr_copyback)
   1113 		return error;
   1114 
   1115 	copylen = MIN(FCNIOCTL_ARG_MAX, fcnioctl_arg.pvnr_datalen);
   1116 	kernbuf = malloc(copylen, M_PUFFS, M_WAITOK);
   1117 	error = copyin(fcnioctl_arg.pvnr_data, kernbuf, copylen);
   1118 	if (error)
   1119 		goto out;
   1120 	error = copyout(kernbuf, ap->a_data, copylen);
   1121 
   1122  out:
   1123 	free(kernbuf, M_PUFFS);
   1124 	return error;
   1125 }
   1126 
   1127 int
   1128 puffs_ioctl(void *v)
   1129 {
   1130 
   1131 	return puffs_fcnioctl(v, PUFFS_VN_IOCTL);
   1132 }
   1133 
   1134 int
   1135 puffs_fcntl(void *v)
   1136 {
   1137 
   1138 	return puffs_fcnioctl(v, PUFFS_VN_FCNTL);
   1139 }
   1140 
   1141 int
   1142 puffs_print(void *v)
   1143 {
   1144 	struct vop_print_args /* {
   1145 		struct vnode *a_vp;
   1146 	} */ *ap = v;
   1147 	struct vnode *vp = ap->a_vp;
   1148 	struct puffs_node *pn = vp->v_data;
   1149 
   1150 	PUFFS_VNREQ(print);
   1151 
   1152 	/* kernel portion */
   1153 	printf("tag VT_PUFFS, vnode %p, puffs node: %p,\n"
   1154 	    "    userspace cookie: %p\n", vp, pn, pn->pn_cookie);
   1155 	lockmgr_printinfo(&vp->v_lock);
   1156 
   1157 	/* userspace portion */
   1158 	return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_PRINT,
   1159 	    &print_arg, sizeof(print_arg), VPTOPNC(ap->a_vp),
   1160 	    LOCKEDVP(ap->a_vp), NULL);
   1161 }
   1162 
   1163 int
   1164 puffs_pathconf(void *v)
   1165 {
   1166 	struct vop_pathconf_args /* {
   1167 		const struct vnodeop_desc *a_desc;
   1168 		struct vnode *a_vp;
   1169 		int a_name;
   1170 		register_t *a_retval;
   1171 	} */ *ap = v;
   1172 	int error;
   1173 
   1174 	PUFFS_VNREQ(pathconf);
   1175 
   1176 	pathconf_arg.pvnr_name = ap->a_name;
   1177 
   1178 	error = puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount),
   1179 	    PUFFS_VN_PATHCONF, &pathconf_arg, sizeof(pathconf_arg),
   1180 	    VPTOPNC(ap->a_vp), ap->a_vp, NULL);
   1181 	if (error)
   1182 		return error;
   1183 
   1184 	*ap->a_retval = pathconf_arg.pvnr_retval;
   1185 
   1186 	return 0;
   1187 }
   1188 
   1189 int
   1190 puffs_advlock(void *v)
   1191 {
   1192 	struct vop_advlock_args /* {
   1193 		const struct vnodeop_desc *a_desc;
   1194 		struct vnode *a_vp;
   1195 		void *a_id;
   1196 		int a_op;
   1197 		struct flock *a_fl;
   1198 		int a_flags;
   1199 	} */ *ap = v;
   1200 	int error;
   1201 
   1202 	PUFFS_VNREQ(advlock);
   1203 
   1204 	error = copyin(ap->a_fl, &advlock_arg.pvnr_fl, sizeof(struct flock));
   1205 	if (error)
   1206 		return error;
   1207 	advlock_arg.pvnr_id = ap->a_id;
   1208 	advlock_arg.pvnr_op = ap->a_op;
   1209 	advlock_arg.pvnr_flags = ap->a_flags;
   1210 
   1211 	return puffs_vntouser(MPTOPUFFSMP(ap->a_vp->v_mount), PUFFS_VN_ADVLOCK,
   1212 	    &advlock_arg, sizeof(advlock_arg), VPTOPNC(ap->a_vp), NULL, NULL);
   1213 }
   1214 
   1215 /*
   1216  * The rest don't get a free trip to userspace and back, they
   1217  * have to stay within the kernel.
   1218  */
   1219 
   1220 /*
   1221  * moreXXX: yes, todo
   1222  */
   1223 int
   1224 puffs_lock(void *v)
   1225 {
   1226 	struct vop_lock_args /* {
   1227 		struct vnode *a_vp;
   1228 		int a_flags;
   1229 	}*/ *ap = v;
   1230 	struct vnode *vp = ap->a_vp;
   1231 
   1232 #if 0
   1233 	DPRINTF(("puffs_lock: lock %p, args 0x%x\n", vp, ap->a_flags));
   1234 #endif
   1235 
   1236 	return lockmgr(&vp->v_lock, ap->a_flags, &vp->v_interlock);
   1237 }
   1238 
   1239 int
   1240 puffs_unlock(void *v)
   1241 {
   1242 	struct vop_unlock_args /* {
   1243 		struct vnode *a_vp;
   1244 		int a_flags;
   1245 	} */ *ap = v;
   1246 	struct vnode *vp = ap->a_vp;
   1247 
   1248 #if 0
   1249 	DPRINTF(("puffs_unlock: lock %p, args 0x%x\n", vp, ap->a_flags));
   1250 #endif
   1251 
   1252 	return lockmgr(&vp->v_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock);
   1253 }
   1254 
   1255 int
   1256 puffs_islocked(void *v)
   1257 {
   1258 	struct vop_islocked_args *ap = v;
   1259 	int rv;
   1260 
   1261 	rv = lockstatus(&ap->a_vp->v_lock);
   1262 	return rv;
   1263 }
   1264 
   1265 #if 0
   1266 int
   1267 puffs_getpages(void *v)
   1268 {
   1269 	struct vop_getpages_args /* {
   1270 		const struct vnodeop_desc *a_desc;
   1271 		struct vnode *a_vp;
   1272 		voff_t a_offset;
   1273 		struct vm_page **a_m;
   1274 		int *a_count;
   1275 		int a_centeridx;
   1276 		vm_prot_t a_access_type;
   1277 		int a_advice;
   1278 		int a_flags;
   1279 	} */ *ap = v;
   1280 
   1281 
   1282 }
   1283 #endif
   1284 
   1285 int
   1286 puffs_generic(void *v)
   1287 {
   1288 	struct vop_generic_args *ap = v;
   1289 
   1290 	(void)ap;
   1291 	DPRINTF(("puffs_generic: ap->a_desc = %s\n", ap->a_desc->vdesc_name));
   1292 
   1293 	return EOPNOTSUPP;
   1294 }
   1295