Home | History | Annotate | Line # | Download | only in puffs
puffs_subr.c revision 1.9.2.2
      1 /*	$NetBSD: puffs_subr.c,v 1.9.2.2 2006/11/18 21:39:20 ad 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_subr.c,v 1.9.2.2 2006/11/18 21:39:20 ad Exp $");
     37 
     38 #include <sys/param.h>
     39 #include <sys/conf.h>
     40 #include <sys/malloc.h>
     41 #include <sys/mount.h>
     42 #include <sys/socketvar.h>
     43 #include <sys/vnode.h>
     44 #include <sys/kauth.h>
     45 #include <sys/namei.h>
     46 
     47 #include <fs/puffs/puffs_msgif.h>
     48 #include <fs/puffs/puffs_sys.h>
     49 
     50 #include <miscfs/genfs/genfs_node.h>
     51 #include <miscfs/specfs/specdev.h>
     52 
     53 POOL_INIT(puffs_pnpool, sizeof(struct puffs_node), 0, 0, 0, "puffspnpl",
     54     &pool_allocator_nointr);
     55 
     56 
     57 static void puffs_gop_size(struct vnode *, off_t, off_t *, int);
     58 static void puffs_gop_markupdate(struct vnode *, int);
     59 
     60 static const struct genfs_ops puffs_genfsops = {
     61 	.gop_size = puffs_gop_size,
     62 	.gop_write = genfs_gop_write,
     63 	.gop_markupdate = puffs_gop_markupdate,
     64 #if 0
     65 	.gop_alloc, should ask userspace
     66 #endif
     67 };
     68 
     69 /*
     70  * Grab a vnode, intialize all the puffs-dependant stuff.
     71  */
     72 int
     73 puffs_getvnode(struct mount *mp, void *cookie, enum vtype type,
     74 	voff_t vsize, dev_t rdev, struct vnode **vpp)
     75 {
     76 	struct puffs_mount *pmp;
     77 	struct vnode *vp, *nvp;
     78 	struct puffs_node *pnode;
     79 	int error;
     80 
     81 	pmp = MPTOPUFFSMP(mp);
     82 
     83 	/*
     84 	 * XXX: there is a deadlock condition between vfs_busy() and
     85 	 * vnode locks.  For an unmounting file system the mountpoint
     86 	 * is frozen, but in unmount(FORCE) vflush() wants to access all
     87 	 * of the vnodes.  If we are here waiting for the mountpoint
     88 	 * lock while holding on to a vnode lock, well, we ain't
     89 	 * just pining for the fjords anymore.  If we release the
     90 	 * vnode lock, we will be in the situation "mount point
     91 	 * is dying" and panic() will ensue in insmntque.  So as a
     92 	 * temporary workaround, get a vnode without putting it on
     93 	 * the mount point list, check if mount point is still alive
     94 	 * and kicking and only then add the vnode to the list.
     95 	 */
     96 	error = getnewvnode(VT_PUFFS, NULL, puffs_vnodeop_p, &vp);
     97 	if (error)
     98 		return error;
     99 	vp->v_vnlock = NULL;
    100 	vp->v_type = type;
    101 
    102 	/*
    103 	 * Check what mount point isn't going away.  This will work
    104 	 * until we decide to remove biglock or make the kernel
    105 	 * preemptive.  But hopefully the real problem will be fixed
    106 	 * by then.
    107 	 *
    108 	 * XXX: yes, should call vfs_busy(), but thar be rabbits with
    109 	 * vicious streaks a mile wide ...
    110 	 */
    111 	if (mp->mnt_iflag & IMNT_UNMOUNT) {
    112 		DPRINTF(("puffs_getvnode: mp %p unmount, unable to create "
    113 		    "vnode for cookie %p\n", mp, cookie));
    114 		ungetnewvnode(vp);
    115 		return ENXIO;
    116 	}
    117 
    118 	/* So it's not dead yet.. good.. inform new vnode of its master */
    119 	simple_lock(&mntvnode_slock);
    120 	if (TAILQ_EMPTY(&mp->mnt_vnodelist))
    121 		TAILQ_INSERT_HEAD(&mp->mnt_vnodelist, vp, v_mntvnodes);
    122 	else
    123 		TAILQ_INSERT_TAIL(&mp->mnt_vnodelist, vp, v_mntvnodes);
    124 	simple_unlock(&mntvnode_slock);
    125 	vp->v_mount = mp;
    126 
    127 	/*
    128 	 * clerical tasks & footwork
    129 	 */
    130 
    131 	/* dances based on vnode type. almost ufs_vinit(), but not quite */
    132 	switch (type) {
    133 	case VCHR:
    134 	case VBLK:
    135 		/*
    136 		 * replace vnode operation vector with the specops vector.
    137 		 * our user server has very little control over the node
    138 		 * if it decides its a character or block special file
    139 		 */
    140 		vp->v_op = puffs_specop_p;
    141 
    142 		/* do the standard checkalias-dance */
    143 		if ((nvp = checkalias(vp, rdev, mp)) != NULL) {
    144 			/*
    145 			 * found: release & unallocate aliased
    146 			 * old (well, actually, new) node
    147 			 */
    148 			vp->v_op = spec_vnodeop_p;
    149 			vp->v_flag &= ~VLOCKSWORK;
    150 			vrele(vp);
    151 			vgone(vp); /* cya */
    152 
    153 			/* init "new" vnode */
    154 			vp = nvp;
    155 			vp->v_vnlock = NULL;
    156 			vp->v_mount = mp;
    157 		}
    158 		break;
    159 
    160 	case VFIFO:
    161 		vp->v_op = puffs_fifoop_p;
    162 		break;
    163 
    164 	case VREG:
    165 		uvm_vnp_setsize(vp, vsize);
    166 		break;
    167 
    168 	case VDIR:
    169 	case VLNK:
    170 	case VSOCK:
    171 		break;
    172 	default:
    173 #ifdef DIAGNOSTIC
    174 		panic("puffs_getvnode: invalid vtype %d", type);
    175 #endif
    176 		break;
    177 	}
    178 
    179 	pnode = pool_get(&puffs_pnpool, PR_WAITOK);
    180 	pnode->pn_cookie = cookie;
    181 	pnode->pn_stat = 0;
    182 	LIST_INSERT_HEAD(&pmp->pmp_pnodelist, pnode, pn_entries);
    183 	vp->v_data = pnode;
    184 	vp->v_type = type;
    185 	pnode->pn_vp = vp;
    186 
    187 	genfs_node_init(vp, &puffs_genfsops);
    188 	*vpp = vp;
    189 
    190 	DPRINTF(("new vnode at %p, pnode %p, cookie %p\n", vp,
    191 	    pnode, pnode->pn_cookie));
    192 
    193 	return 0;
    194 }
    195 
    196 /* new node creating for creative vop ops (create, symlink, mkdir, mknod) */
    197 int
    198 puffs_newnode(struct mount *mp, struct vnode *dvp, struct vnode **vpp,
    199 	void *cookie, struct componentname *cnp, enum vtype type, dev_t rdev)
    200 {
    201 	struct vnode *vp;
    202 	int error;
    203 
    204 	/* userspace probably has this as a NULL op */
    205 	if (cookie == NULL) {
    206 		error = EOPNOTSUPP;
    207 		return error;
    208 	}
    209 
    210 	error = puffs_getvnode(dvp->v_mount, cookie, type, 0, rdev, &vp);
    211 	if (error)
    212 		return error;
    213 
    214 	vp->v_type = type;
    215 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
    216 	*vpp = vp;
    217 
    218 	return 0;
    219 }
    220 
    221 void
    222 puffs_putvnode(struct vnode *vp)
    223 {
    224 	struct puffs_mount *pmp;
    225 	struct puffs_node *pnode;
    226 
    227 	pmp = VPTOPUFFSMP(vp);
    228 	pnode = VPTOPP(vp);
    229 
    230 #ifdef DIAGNOSTIC
    231 	if (vp->v_tag != VT_PUFFS)
    232 		panic("puffs_putvnode: %p not a puffs vnode", vp);
    233 #endif
    234 
    235 	LIST_REMOVE(pnode, pn_entries);
    236 	pool_put(&puffs_pnpool, vp->v_data);
    237 	vp->v_data = NULL;
    238 
    239 	return;
    240 }
    241 
    242 /*
    243  * Locate the in-kernel vnode based on the cookie received given
    244  * from userspace.  Returns a locked & referenced vnode, if found,
    245  * NULL otherwise.
    246  *
    247  * XXX: lists, although lookup cache mostly shields us from this
    248  */
    249 struct vnode *
    250 puffs_pnode2vnode(struct puffs_mount *pmp, void *cookie)
    251 {
    252 	struct puffs_node *pnode;
    253 	struct vnode *vp;
    254 
    255 	simple_lock(&pmp->pmp_lock);
    256 	LIST_FOREACH(pnode, &pmp->pmp_pnodelist, pn_entries) {
    257 		if (pnode->pn_cookie == cookie)
    258 			break;
    259 	}
    260 	simple_unlock(&pmp->pmp_lock);
    261 	if (!pnode)
    262 		return NULL;
    263 	vp = pnode->pn_vp;
    264 
    265 	if (pnode->pn_stat & PNODE_INACTIVE) {
    266 		if (vget(vp, LK_EXCLUSIVE | LK_RETRY))
    267 			return NULL;
    268 	} else {
    269 		vref(vp);
    270 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
    271 	}
    272 	return vp;
    273 }
    274 
    275 void
    276 puffs_makecn(struct puffs_cn *pcn, const struct componentname *cn)
    277 {
    278 
    279 	pcn->pcn_nameiop = cn->cn_nameiop;
    280 	pcn->pcn_flags = cn->cn_flags;
    281 	pcn->pcn_pid = cn->cn_lwp->l_proc->p_pid;
    282 	puffs_credcvt(&pcn->pcn_cred, cn->cn_cred);
    283 
    284 	(void)memcpy(&pcn->pcn_name, cn->cn_nameptr, cn->cn_namelen);
    285 	pcn->pcn_name[cn->cn_namelen] = '\0';
    286 	pcn->pcn_namelen = cn->cn_namelen;
    287 }
    288 
    289 /*
    290  * Convert given credentials to struct puffs_cred for userspace.
    291  */
    292 void
    293 puffs_credcvt(struct puffs_cred *pcr, const kauth_cred_t cred)
    294 {
    295 
    296 	memset(pcr, 0, sizeof(struct puffs_cred));
    297 
    298 	if (cred == NOCRED || cred == FSCRED) {
    299 		pcr->pcr_type = PUFFCRED_TYPE_INTERNAL;
    300 		if (cred == NOCRED)
    301 			pcr->pcr_internal = PUFFCRED_CRED_NOCRED;
    302 		if (cred == FSCRED)
    303 			pcr->pcr_internal = PUFFCRED_CRED_FSCRED;
    304  	} else {
    305 		pcr->pcr_type = PUFFCRED_TYPE_UUC;
    306 		kauth_cred_to_uucred(&pcr->pcr_uuc, cred);
    307 	}
    308 }
    309 
    310 /*
    311  * Return pid.  In case the operation is coming from within the
    312  * kernel without any process context, borrow the swapper's pid.
    313  */
    314 pid_t
    315 puffs_lwp2pid(struct lwp *l)
    316 {
    317 
    318 	return l ? l->l_proc->p_pid : 0;
    319 }
    320 
    321 
    322 static void
    323 puffs_gop_size(struct vnode *vp, off_t size, off_t *eobp,
    324 	int flags)
    325 {
    326 
    327 	*eobp = size;
    328 }
    329 
    330 static void
    331 puffs_gop_markupdate(struct vnode *vp, int flags)
    332 {
    333 	int uflags = 0;
    334 
    335 	if (flags & GOP_UPDATE_ACCESSED)
    336 		uflags |= PUFFS_UPDATEATIME;
    337 	if (flags & GOP_UPDATE_MODIFIED)
    338 		uflags |= PUFFS_UPDATEMTIME;
    339 
    340 	puffs_updatenode(vp, uflags);
    341 }
    342 
    343 void
    344 puffs_updatenode(struct vnode *vp, int flags)
    345 {
    346 	struct timespec ts;
    347 	struct puffs_vnreq_setattr *setattr_arg;
    348 
    349 	if (flags == 0)
    350 		return;
    351 
    352 	setattr_arg = malloc(sizeof(struct puffs_vnreq_setattr), M_PUFFS,
    353 	    M_NOWAIT | M_ZERO);
    354 	if (setattr_arg == NULL)
    355 		return; /* 2bad */
    356 
    357 	nanotime(&ts);
    358 
    359 	VATTR_NULL(&setattr_arg->pvnr_va);
    360 	if (flags & PUFFS_UPDATEATIME)
    361 		setattr_arg->pvnr_va.va_atime = ts;
    362 	if (flags & PUFFS_UPDATECTIME)
    363 		setattr_arg->pvnr_va.va_ctime = ts;
    364 	if (flags & PUFFS_UPDATEMTIME)
    365 		setattr_arg->pvnr_va.va_mtime = ts;
    366 	if (flags & PUFFS_UPDATESIZE)
    367 		setattr_arg->pvnr_va.va_size = vp->v_size;
    368 
    369 	setattr_arg->pvnr_pid = 0;
    370 	puffs_credcvt(&setattr_arg->pvnr_cred, NOCRED);
    371 
    372 	/* setattr_arg ownership shifted to callee */
    373 	puffs_vntouser_faf(MPTOPUFFSMP(vp->v_mount), PUFFS_VN_SETATTR,
    374 	    setattr_arg, sizeof(struct puffs_vnreq_setattr), VPTOPNC(vp));
    375 }
    376