xref: /openbsd-src/sys/tmpfs/tmpfs_vnops.c (revision 5a0ec8146b3a8f74af8f596985d293fb896d1dcb)
1*5a0ec814Smiod /*	$OpenBSD: tmpfs_vnops.c,v 1.56 2024/10/18 05:52:32 miod Exp $	*/
27013b092Sespie /*	$NetBSD: tmpfs_vnops.c,v 1.100 2012/11/05 17:27:39 dholland Exp $	*/
37013b092Sespie 
47013b092Sespie /*
57013b092Sespie  * Copyright (c) 2005, 2006, 2007, 2012 The NetBSD Foundation, Inc.
67013b092Sespie  * Copyright (c) 2013 Pedro Martelletto
77013b092Sespie  * All rights reserved.
87013b092Sespie  *
97013b092Sespie  * This code is derived from software contributed to The NetBSD Foundation
107013b092Sespie  * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
117013b092Sespie  * 2005 program, and by Taylor R Campbell.
127013b092Sespie  *
137013b092Sespie  * Redistribution and use in source and binary forms, with or without
147013b092Sespie  * modification, are permitted provided that the following conditions
157013b092Sespie  * are met:
167013b092Sespie  * 1. Redistributions of source code must retain the above copyright
177013b092Sespie  *    notice, this list of conditions and the following disclaimer.
187013b092Sespie  * 2. Redistributions in binary form must reproduce the above copyright
197013b092Sespie  *    notice, this list of conditions and the following disclaimer in the
207013b092Sespie  *    documentation and/or other materials provided with the distribution.
217013b092Sespie  *
227013b092Sespie  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
237013b092Sespie  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
247013b092Sespie  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
257013b092Sespie  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
267013b092Sespie  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
277013b092Sespie  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
287013b092Sespie  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
297013b092Sespie  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
307013b092Sespie  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
317013b092Sespie  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
327013b092Sespie  * POSSIBILITY OF SUCH DAMAGE.
337013b092Sespie  */
347013b092Sespie 
357013b092Sespie /*
367013b092Sespie  * tmpfs vnode interface.
377013b092Sespie  */
387013b092Sespie 
397013b092Sespie #include <sys/param.h>
4099ef5f49Svisa #include <sys/systm.h>
417013b092Sespie #include <sys/fcntl.h>
427013b092Sespie #include <sys/event.h>
437013b092Sespie #include <sys/namei.h>
447013b092Sespie #include <sys/stat.h>
457013b092Sespie #include <sys/uio.h>
467013b092Sespie #include <sys/unistd.h>
477013b092Sespie #include <sys/vnode.h>
487013b092Sespie #include <sys/lockf.h>
49f9ac7274Stedu #include <sys/file.h>
507013b092Sespie 
517013b092Sespie #include <miscfs/fifofs/fifo.h>
527013b092Sespie #include <tmpfs/tmpfs_vnops.h>
537013b092Sespie #include <tmpfs/tmpfs.h>
547013b092Sespie 
55f9ac7274Stedu int tmpfs_kqfilter(void *v);
56f9ac7274Stedu 
577013b092Sespie /*
587013b092Sespie  * vnode operations vector used for files stored in a tmpfs file system.
597013b092Sespie  */
602d6b9e38Sclaudio const struct vops tmpfs_vops = {
617013b092Sespie 	.vop_lookup	= tmpfs_lookup,
627013b092Sespie 	.vop_create	= tmpfs_create,
637013b092Sespie 	.vop_mknod	= tmpfs_mknod,
647013b092Sespie 	.vop_open	= tmpfs_open,
657013b092Sespie 	.vop_close	= tmpfs_close,
667013b092Sespie 	.vop_access	= tmpfs_access,
677013b092Sespie 	.vop_getattr	= tmpfs_getattr,
687013b092Sespie 	.vop_setattr	= tmpfs_setattr,
697013b092Sespie 	.vop_read	= tmpfs_read,
707013b092Sespie 	.vop_write	= tmpfs_write,
717013b092Sespie 	.vop_ioctl	= tmpfs_ioctl,
72f9ac7274Stedu 	.vop_kqfilter	= tmpfs_kqfilter,
737013b092Sespie 	.vop_revoke	= vop_generic_revoke,
747013b092Sespie 	.vop_fsync	= tmpfs_fsync,
757013b092Sespie 	.vop_remove	= tmpfs_remove,
767013b092Sespie 	.vop_link	= tmpfs_link,
777013b092Sespie 	.vop_rename	= tmpfs_rename,
787013b092Sespie 	.vop_mkdir	= tmpfs_mkdir,
797013b092Sespie 	.vop_rmdir	= tmpfs_rmdir,
807013b092Sespie 	.vop_symlink	= tmpfs_symlink,
817013b092Sespie 	.vop_readdir	= tmpfs_readdir,
827013b092Sespie 	.vop_readlink	= tmpfs_readlink,
837013b092Sespie 	.vop_abortop	= vop_generic_abortop,
847013b092Sespie 	.vop_inactive	= tmpfs_inactive,
857013b092Sespie 	.vop_reclaim	= tmpfs_reclaim,
867013b092Sespie 	.vop_lock	= tmpfs_lock,
877013b092Sespie 	.vop_unlock	= tmpfs_unlock,
887013b092Sespie 	.vop_bmap	= vop_generic_bmap,
897013b092Sespie 	.vop_strategy	= tmpfs_strategy,
907013b092Sespie 	.vop_print	= tmpfs_print,
917013b092Sespie 	.vop_islocked	= tmpfs_islocked,
927013b092Sespie 	.vop_pathconf	= tmpfs_pathconf,
937013b092Sespie 	.vop_advlock	= tmpfs_advlock,
947013b092Sespie 	.vop_bwrite	= tmpfs_bwrite,
957013b092Sespie };
967013b092Sespie 
977013b092Sespie /*
987013b092Sespie  * tmpfs_lookup: path name traversal routine.
997013b092Sespie  *
1007013b092Sespie  * Arguments: dvp (directory being searched), vpp (result),
1017013b092Sespie  * cnp (component name - path).
1027013b092Sespie  *
1037013b092Sespie  * => Caller holds a reference and lock on dvp.
1047013b092Sespie  * => We return looked-up vnode (vpp) locked, with a reference held.
1057013b092Sespie  */
1067013b092Sespie int
1077013b092Sespie tmpfs_lookup(void *v)
1087013b092Sespie {
1097013b092Sespie 	struct vop_lookup_args /* {
1107013b092Sespie 		struct vnode *a_dvp;
1117013b092Sespie 		struct vnode **a_vpp;
1127013b092Sespie 		struct componentname *a_cnp;
1137013b092Sespie 	} */ *ap = v;
1147013b092Sespie 	struct vnode *dvp = ap->a_dvp, **vpp = ap->a_vpp;
1157013b092Sespie 	struct componentname *cnp = ap->a_cnp;
1167013b092Sespie 	struct ucred *cred = cnp->cn_cred;
1177013b092Sespie 	const int lastcn = (cnp->cn_flags & ISLASTCN) != 0;
1187013b092Sespie 	const int lockparent = (cnp->cn_flags & LOCKPARENT) != 0;
1197013b092Sespie 	tmpfs_node_t *dnode, *tnode;
1207013b092Sespie 	tmpfs_dirent_t *de;
1217013b092Sespie 	int cachefound;
1227013b092Sespie 	int error;
1237013b092Sespie 
1247013b092Sespie 	KASSERT(VOP_ISLOCKED(dvp));
1257013b092Sespie 
1267013b092Sespie 	dnode = VP_TO_TMPFS_DIR(dvp);
1277013b092Sespie 	cnp->cn_flags &= ~PDIRUNLOCK;
1287013b092Sespie 	*vpp = NULL;
1297013b092Sespie 
1307013b092Sespie 	/* Check accessibility of directory. */
1317013b092Sespie 	error = VOP_ACCESS(dvp, VEXEC, cred, curproc);
1327013b092Sespie 	if (error) {
1337013b092Sespie 		goto out;
1347013b092Sespie 	}
1357013b092Sespie 
1367013b092Sespie 	/*
1377013b092Sespie 	 * If requesting the last path component on a read-only file system
1387013b092Sespie 	 * with a write operation, deny it.
1397013b092Sespie 	 */
1407013b092Sespie 	if (lastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) != 0 &&
1417013b092Sespie 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
1427013b092Sespie 		error = EROFS;
1437013b092Sespie 		goto out;
1447013b092Sespie 	}
1457013b092Sespie 
1467013b092Sespie 	/*
1477013b092Sespie 	 * Avoid doing a linear scan of the directory if the requested
1487013b092Sespie 	 * directory/name couple is already in the cache.
1497013b092Sespie 	 */
1507013b092Sespie 	cachefound = cache_lookup(dvp, vpp, cnp);
1517013b092Sespie 	if (cachefound == ENOENT /* && *vpp == NULLVP */)
1527013b092Sespie 		return ENOENT; /* Negative cache hit. */
1537013b092Sespie 	else if (cachefound != -1)
1547013b092Sespie 		return 0; /* Found in cache. */
1557013b092Sespie 
1567013b092Sespie 	if (cnp->cn_flags & ISDOTDOT) {
1577013b092Sespie 		tmpfs_node_t *pnode;
1587013b092Sespie 
1597013b092Sespie 		/*
1607013b092Sespie 		 * Lookup of ".." case.
1617013b092Sespie 		 */
16246905cb9Sespie 		if (lastcn) {
16346905cb9Sespie 			if (cnp->cn_nameiop == RENAME) {
1647013b092Sespie 				error = EINVAL;
1657013b092Sespie 				goto out;
1667013b092Sespie 			}
16746905cb9Sespie 			if (cnp->cn_nameiop == DELETE) {
16846905cb9Sespie 				/* Keep the name for tmpfs_rmdir(). */
16946905cb9Sespie 				cnp->cn_flags |= SAVENAME;
17046905cb9Sespie 			}
17146905cb9Sespie 		}
1727013b092Sespie 		KASSERT(dnode->tn_type == VDIR);
1737013b092Sespie 		pnode = dnode->tn_spec.tn_dir.tn_parent;
1747013b092Sespie 		if (pnode == NULL) {
1757013b092Sespie 			error = ENOENT;
1767013b092Sespie 			goto out;
1777013b092Sespie 		}
1787013b092Sespie 
1797013b092Sespie 		/*
1807013b092Sespie 		 * Lock the parent tn_nlock before releasing the vnode lock,
1817013b092Sespie 		 * and thus prevents parent from disappearing.
1827013b092Sespie 		 */
1837013b092Sespie 		rw_enter_write(&pnode->tn_nlock);
18436bb23f1Svisa 		VOP_UNLOCK(dvp);
1857013b092Sespie 
1867013b092Sespie 		/*
1877013b092Sespie 		 * Get a vnode of the '..' entry and re-acquire the lock.
1887013b092Sespie 		 * Release the tn_nlock.
1897013b092Sespie 		 */
1907013b092Sespie 		error = tmpfs_vnode_get(dvp->v_mount, pnode, vpp);
1916e880534Svisa 		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
1927013b092Sespie 		goto out;
1937013b092Sespie 
1947013b092Sespie 	} else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
1957013b092Sespie 		/*
1967013b092Sespie 		 * Lookup of "." case.
1977013b092Sespie 		 */
1987013b092Sespie 		if (lastcn && cnp->cn_nameiop == RENAME) {
1997013b092Sespie 			error = EISDIR;
2007013b092Sespie 			goto out;
2017013b092Sespie 		}
2027013b092Sespie 		vref(dvp);
2037013b092Sespie 		*vpp = dvp;
2047013b092Sespie 		error = 0;
2057013b092Sespie 		goto done;
2067013b092Sespie 	}
2077013b092Sespie 
2087013b092Sespie 	/*
2097013b092Sespie 	 * Other lookup cases: perform directory scan.
2107013b092Sespie 	 */
2117013b092Sespie 	de = tmpfs_dir_lookup(dnode, cnp);
21257331246Sespie 	if (de == NULL) {
2137013b092Sespie 		/*
2147013b092Sespie 		 * The entry was not found in the directory.  This is valid
2157013b092Sespie 		 * if we are creating or renaming an entry and are working
2167013b092Sespie 		 * on the last component of the path name.
2177013b092Sespie 		 */
2187013b092Sespie 		if (lastcn && (cnp->cn_nameiop == CREATE ||
2197013b092Sespie 		    cnp->cn_nameiop == RENAME)) {
2207013b092Sespie 			error = VOP_ACCESS(dvp, VWRITE, cred, curproc);
2217013b092Sespie 			if (error) {
2227013b092Sespie 				goto out;
2237013b092Sespie 			}
2247013b092Sespie 			/*
2257013b092Sespie 			 * We are creating an entry in the file system, so
2267013b092Sespie 			 * save its name for further use by tmpfs_create().
2277013b092Sespie 			 */
2287013b092Sespie 			cnp->cn_flags |= SAVENAME;
2297013b092Sespie 			error = EJUSTRETURN;
2307013b092Sespie 		} else {
2317013b092Sespie 			error = ENOENT;
2327013b092Sespie 		}
2337013b092Sespie 		goto done;
2347013b092Sespie 	}
2357013b092Sespie 
2367013b092Sespie 	tnode = de->td_node;
2377013b092Sespie 
2387013b092Sespie 	/*
2397013b092Sespie 	 * If it is not the last path component and found a non-directory
2407013b092Sespie 	 * or non-link entry (which may itself be pointing to a directory),
2417013b092Sespie 	 * raise an error.
2427013b092Sespie 	 */
2437013b092Sespie 	if (!lastcn && tnode->tn_type != VDIR && tnode->tn_type != VLNK) {
2447013b092Sespie 		error = ENOTDIR;
2457013b092Sespie 		goto out;
2467013b092Sespie 	}
2477013b092Sespie 
2487013b092Sespie 	/* Check the permissions. */
2497013b092Sespie 	if (lastcn && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
2507013b092Sespie 		error = VOP_ACCESS(dvp, VWRITE, cred, curproc);
2517013b092Sespie 		if (error)
2527013b092Sespie 			goto out;
2537013b092Sespie 
2547013b092Sespie 		/*
2557013b092Sespie 		 * If not root and directory is sticky, check for permission
2567013b092Sespie 		 * on directory or on file. This implements append-only
2577013b092Sespie 		 * directories.
2587013b092Sespie 		 */
2597013b092Sespie 		if ((dnode->tn_mode & S_ISTXT) != 0) {
2607013b092Sespie 			if (cred->cr_uid != 0 &&
2617013b092Sespie 			    cred->cr_uid != dnode->tn_uid &&
2627013b092Sespie 			    cred->cr_uid != tnode->tn_uid) {
2637013b092Sespie 				error = EPERM;
2647013b092Sespie 				goto out;
2657013b092Sespie 			}
2667013b092Sespie 		}
2677013b092Sespie 
2687013b092Sespie 		/*
2697013b092Sespie 		 * XXX pedro: We might need cn_nameptr later in tmpfs_remove()
2707013b092Sespie 		 * or tmpfs_rmdir() for a tmpfs_dir_lookup(). We should really
2717013b092Sespie 		 * get rid of SAVENAME at some point.
2727013b092Sespie 		 */
2737013b092Sespie 		if (cnp->cn_nameiop == DELETE)
2747013b092Sespie 			cnp->cn_flags |= SAVENAME;
2757013b092Sespie 	}
2767013b092Sespie 
2777013b092Sespie 	/* Get a vnode for the matching entry. */
2787013b092Sespie 	rw_enter_write(&tnode->tn_nlock);
2797013b092Sespie 	error = tmpfs_vnode_get(dvp->v_mount, tnode, vpp);
2807013b092Sespie done:
2817013b092Sespie 	/*
2827013b092Sespie 	 * Cache the result, unless request was for creation (as it does
2837013b092Sespie 	 * not improve the performance).
2847013b092Sespie 	 */
2857013b092Sespie 	if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE) {
2867013b092Sespie 		cache_enter(dvp, *vpp, cnp);
2877013b092Sespie 	}
2887013b092Sespie out:
2897013b092Sespie 	/*
290ba6398e8Spatrick 	 * If (1) we succeeded, (2) found a distinct vnode != .. to return and (3)
291b66b9ef8Sjsg 	 * were either explicitly told to keep the parent locked or are in the
2927013b092Sespie 	 * middle of a lookup, unlock the parent vnode.
2937013b092Sespie 	 */
2947013b092Sespie 	if ((error == 0 || error == EJUSTRETURN) && /* (1) */
295ba6398e8Spatrick 	    (*vpp != dvp || (cnp->cn_flags & ISDOTDOT))  && /* (2) */
2967013b092Sespie 	    (!lockparent || !lastcn)) {		    /* (3) */
29736bb23f1Svisa 		VOP_UNLOCK(dvp);
2987013b092Sespie 		cnp->cn_flags |= PDIRUNLOCK;
2997013b092Sespie 	} else
3007013b092Sespie 		KASSERT(VOP_ISLOCKED(dvp));
3017013b092Sespie 
3027013b092Sespie 	KASSERT((*vpp && VOP_ISLOCKED(*vpp)) || error);
3037013b092Sespie 
3047013b092Sespie 	return error;
3057013b092Sespie }
3067013b092Sespie 
3077013b092Sespie int
3087013b092Sespie tmpfs_create(void *v)
3097013b092Sespie {
3107013b092Sespie 	struct vop_create_args /* {
3117013b092Sespie 		struct vnode		*a_dvp;
3127013b092Sespie 		struct vnode		**a_vpp;
3137013b092Sespie 		struct componentname	*a_cnp;
3147013b092Sespie 		struct vattr		*a_vap;
3157013b092Sespie 	} */ *ap = v;
3167013b092Sespie 	struct vnode *dvp = ap->a_dvp, **vpp = ap->a_vpp;
3177013b092Sespie 	struct componentname *cnp = ap->a_cnp;
3187013b092Sespie 	struct vattr *vap = ap->a_vap;
3197013b092Sespie 
3207013b092Sespie 	KASSERT(VOP_ISLOCKED(dvp));
3217013b092Sespie 	KASSERT(cnp->cn_flags & HASBUF);
3227013b092Sespie 	KASSERT(vap->va_type == VREG || vap->va_type == VSOCK);
3234dd4d774Svisa 	return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
3247013b092Sespie }
3257013b092Sespie 
3267013b092Sespie int
3277013b092Sespie tmpfs_mknod(void *v)
3287013b092Sespie {
3297013b092Sespie 	struct vop_mknod_args /* {
3307013b092Sespie 		struct vnode		*a_dvp;
3317013b092Sespie 		struct vnode		**a_vpp;
3327013b092Sespie 		struct componentname	*a_cnp;
3337013b092Sespie 		struct vattr		*a_vap;
3347013b092Sespie 	} */ *ap = v;
3357013b092Sespie 	struct vnode *dvp = ap->a_dvp, **vpp = ap->a_vpp;
3367013b092Sespie 	struct componentname *cnp = ap->a_cnp;
3377013b092Sespie 	struct vattr *vap = ap->a_vap;
3387013b092Sespie 	enum vtype vt = vap->va_type;
3397013b092Sespie 	int error;
3407013b092Sespie 
3414dd4d774Svisa 	if (vt != VBLK && vt != VCHR && vt != VFIFO)
3427013b092Sespie 		return EINVAL;
3437013b092Sespie 
3447013b092Sespie 	error = tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
3457013b092Sespie 
3464dd4d774Svisa 	if (error == 0)
3477013b092Sespie 		vput(*vpp);
3487013b092Sespie 
3494dd4d774Svisa 	return error;
3507013b092Sespie }
3517013b092Sespie 
3527013b092Sespie int
3537013b092Sespie tmpfs_open(void *v)
3547013b092Sespie {
3557013b092Sespie 	struct vop_open_args /* {
3567013b092Sespie 		struct vnode	*a_vp;
3577013b092Sespie 		int		a_mode;
3587013b092Sespie 		kauth_cred_t	a_cred;
3597013b092Sespie 	} */ *ap = v;
3607013b092Sespie 	struct vnode *vp = ap->a_vp;
3617013b092Sespie 	mode_t mode = ap->a_mode;
3627013b092Sespie 	tmpfs_node_t *node;
3637013b092Sespie 
3647013b092Sespie 	KASSERT(VOP_ISLOCKED(vp));
3657013b092Sespie 
3667013b092Sespie 	node = VP_TO_TMPFS_NODE(vp);
3677013b092Sespie 	if (node->tn_links < 1) {
3687013b092Sespie 		/*
3697013b092Sespie 		 * The file is still active, but all its names have been
3707013b092Sespie 		 * removed (e.g. by a "rmdir $(pwd)").  It cannot be opened
3717013b092Sespie 		 * any more, as it is about to be destroyed.
3727013b092Sespie 		 */
3737013b092Sespie 		return ENOENT;
3747013b092Sespie 	}
3757013b092Sespie 
3767013b092Sespie 	/* If the file is marked append-only, deny write requests. */
3777013b092Sespie 	if ((node->tn_flags & APPEND) != 0 &&
3787013b092Sespie 	    (mode & (FWRITE | O_APPEND)) == FWRITE) {
3797013b092Sespie 		return EPERM;
3807013b092Sespie 	}
3817013b092Sespie 	return 0;
3827013b092Sespie }
3837013b092Sespie 
3847013b092Sespie int
3857013b092Sespie tmpfs_close(void *v)
3867013b092Sespie {
387a046f53bSreyk #ifdef DIAGNOSTIC
3887013b092Sespie 	struct vop_close_args /* {
3897013b092Sespie 		struct vnode	*a_vp;
3907013b092Sespie 		int		a_fflag;
3917013b092Sespie 		kauth_cred_t	a_cred;
3927013b092Sespie 	} */ *ap = v;
3937013b092Sespie 	struct vnode *vp = ap->a_vp;
3947013b092Sespie 
3957013b092Sespie 	KASSERT(VOP_ISLOCKED(vp));
396a046f53bSreyk #endif
3977013b092Sespie 	return 0;
3987013b092Sespie }
3997013b092Sespie 
4007013b092Sespie int
4017013b092Sespie tmpfs_access(void *v)
4027013b092Sespie {
4037013b092Sespie 	struct vop_access_args /* {
4047013b092Sespie 		struct vnode	*a_vp;
4057013b092Sespie 		int		a_mode;
4067013b092Sespie 		kauth_cred_t	a_cred;
4077013b092Sespie 	} */ *ap = v;
4087013b092Sespie 	struct vnode *vp = ap->a_vp;
4097013b092Sespie 	mode_t mode = ap->a_mode;
4107013b092Sespie 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
4117013b092Sespie 	const int writing = (mode & VWRITE) != 0;
4127013b092Sespie 
4137013b092Sespie 	KASSERT(VOP_ISLOCKED(vp));
4147013b092Sespie 
4157013b092Sespie 	/* Possible? */
4167013b092Sespie 	switch (vp->v_type) {
4177013b092Sespie 	case VDIR:
4187013b092Sespie 	case VLNK:
4197013b092Sespie 	case VREG:
4207013b092Sespie 		if (writing && (vp->v_mount->mnt_flag & MNT_RDONLY) != 0) {
4217013b092Sespie 			return EROFS;
4227013b092Sespie 		}
4237013b092Sespie 		break;
4247013b092Sespie 	case VBLK:
4257013b092Sespie 	case VCHR:
4267013b092Sespie 	case VSOCK:
4277013b092Sespie 	case VFIFO:
4287013b092Sespie 		break;
4297013b092Sespie 	default:
4307013b092Sespie 		return EINVAL;
4317013b092Sespie 	}
4327013b092Sespie 	if (writing && (node->tn_flags & IMMUTABLE) != 0) {
4337013b092Sespie 		return EPERM;
4347013b092Sespie 	}
4357013b092Sespie 
4367013b092Sespie 	return (vaccess(vp->v_type, node->tn_mode, node->tn_uid, node->tn_gid,
4377013b092Sespie 	    mode, ap->a_cred));
4387013b092Sespie }
4397013b092Sespie 
4407013b092Sespie int
4417013b092Sespie tmpfs_getattr(void *v)
4427013b092Sespie {
4437013b092Sespie 	struct vop_getattr_args /* {
4447013b092Sespie 		struct vnode	*a_vp;
4457013b092Sespie 		struct vattr	*a_vap;
4467013b092Sespie 		kauth_cred_t	a_cred;
4477013b092Sespie 	} */ *ap = v;
4487013b092Sespie 	struct vnode *vp = ap->a_vp;
4497013b092Sespie 	struct vattr *vap = ap->a_vap;
4507013b092Sespie 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
4517013b092Sespie 
4527013b092Sespie 	vattr_null(vap);
4537013b092Sespie 
4547013b092Sespie 	vap->va_type = vp->v_type;
4557013b092Sespie 	vap->va_mode = node->tn_mode;
4567013b092Sespie 	vap->va_nlink = node->tn_links;
4577013b092Sespie 	vap->va_uid = node->tn_uid;
4587013b092Sespie 	vap->va_gid = node->tn_gid;
459ccc5513aSespie 	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
4607013b092Sespie 	vap->va_fileid = node->tn_id;
4617013b092Sespie 	vap->va_size = node->tn_size;
4627013b092Sespie 	vap->va_blocksize = PAGE_SIZE;
4637013b092Sespie 	vap->va_atime = node->tn_atime;
4647013b092Sespie 	vap->va_mtime = node->tn_mtime;
4657013b092Sespie 	vap->va_ctime = node->tn_ctime;
4667013b092Sespie 	/* vap->va_birthtime = node->tn_birthtime; */
4677013b092Sespie 	vap->va_gen = TMPFS_NODE_GEN(node);
4687013b092Sespie 	vap->va_flags = node->tn_flags;
4697013b092Sespie 	vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ?
4707013b092Sespie 	    node->tn_spec.tn_dev.tn_rdev : VNOVAL;
4717013b092Sespie 	vap->va_bytes = round_page(node->tn_size);
4727013b092Sespie 	vap->va_filerev = VNOVAL;
4737013b092Sespie 	vap->va_vaflags = 0;
4747013b092Sespie 	vap->va_spare = VNOVAL; /* XXX */
4757013b092Sespie 
4767013b092Sespie 	return 0;
4777013b092Sespie }
4787013b092Sespie 
4794707cbe3Sguenther #define GOODTIME(tv)	((tv)->tv_nsec != VNOVAL)
4807013b092Sespie /* XXX Should this operation be atomic?  I think it should, but code in
4817013b092Sespie  * XXX other places (e.g., ufs) doesn't seem to be... */
4827013b092Sespie int
4837013b092Sespie tmpfs_setattr(void *v)
4847013b092Sespie {
4857013b092Sespie 	struct vop_setattr_args /* {
4867013b092Sespie 		struct vnode	*a_vp;
4877013b092Sespie 		struct vattr	*a_vap;
4887013b092Sespie 		kauth_cred_t	a_cred;
4897013b092Sespie 	} */ *ap = v;
4907013b092Sespie 	struct vnode *vp = ap->a_vp;
4917013b092Sespie 	struct vattr *vap = ap->a_vap;
4927013b092Sespie 	struct ucred *cred = ap->a_cred;
4937013b092Sespie 	struct proc *p = curproc;
4947013b092Sespie 	int error = 0;
4957013b092Sespie 
4967013b092Sespie 	KASSERT(VOP_ISLOCKED(vp));
4977013b092Sespie 
4987013b092Sespie 	/* Abort if any unsettable attribute is given. */
4997013b092Sespie 	if (vap->va_type != VNON || vap->va_nlink != VNOVAL ||
5007013b092Sespie 	    vap->va_fsid != VNOVAL || vap->va_fileid != VNOVAL ||
5017013b092Sespie 	    vap->va_blocksize != VNOVAL || GOODTIME(&vap->va_ctime) ||
5027013b092Sespie 	    vap->va_gen != VNOVAL || vap->va_rdev != VNOVAL ||
5037013b092Sespie 	    vap->va_bytes != VNOVAL) {
5047013b092Sespie 		return EINVAL;
5057013b092Sespie 	}
5067013b092Sespie 	if (error == 0 && (vap->va_flags != VNOVAL))
5077013b092Sespie 		error = tmpfs_chflags(vp, vap->va_flags, cred, p);
5087013b092Sespie 
5097013b092Sespie 	if (error == 0 && (vap->va_size != VNOVAL))
5107013b092Sespie 		error = tmpfs_chsize(vp, vap->va_size, cred, p);
5117013b092Sespie 
5127013b092Sespie 	if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL))
5137013b092Sespie 		error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, p);
5147013b092Sespie 
5157013b092Sespie 	if (error == 0 && (vap->va_mode != VNOVAL))
5167013b092Sespie 		error = tmpfs_chmod(vp, vap->va_mode, cred, p);
5177013b092Sespie 
5184707cbe3Sguenther 	if (error == 0 && ((vap->va_vaflags & VA_UTIMES_CHANGE)
5194707cbe3Sguenther 	    || GOODTIME(&vap->va_atime)
5204707cbe3Sguenther 	    || GOODTIME(&vap->va_mtime)))
5217013b092Sespie 		error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime,
5227013b092Sespie 		    vap->va_vaflags, cred, p);
5234707cbe3Sguenther 
5247013b092Sespie 	return error;
5257013b092Sespie }
5267013b092Sespie 
5277013b092Sespie int
5287013b092Sespie tmpfs_read(void *v)
5297013b092Sespie {
5307013b092Sespie 	struct vop_read_args /* {
5317013b092Sespie 		struct vnode *a_vp;
5327013b092Sespie 		struct uio *a_uio;
5337013b092Sespie 		int a_ioflag;
5347013b092Sespie 		struct ucred *a_cred;
5357013b092Sespie 	} */ *ap = v;
5367013b092Sespie 	struct vnode *vp = ap->a_vp;
5377013b092Sespie 	struct uio *uio = ap->a_uio;
5387013b092Sespie 	/* const int ioflag = ap->a_ioflag; */
5397013b092Sespie 	tmpfs_node_t *node;
5407013b092Sespie 	int error;
5417013b092Sespie 
5427013b092Sespie 	KASSERT(VOP_ISLOCKED(vp));
5437013b092Sespie 
5447013b092Sespie 	if (vp->v_type != VREG) {
5457013b092Sespie 		return EISDIR;
5467013b092Sespie 	}
5477013b092Sespie 	if (uio->uio_offset < 0) {
5487013b092Sespie 		return EINVAL;
5497013b092Sespie 	}
5506a693b5fSnatano 	if (uio->uio_resid == 0)
5516a693b5fSnatano 		return 0;
5527013b092Sespie 
5537013b092Sespie 	node = VP_TO_TMPFS_NODE(vp);
5547013b092Sespie 	error = 0;
5557013b092Sespie 
5567013b092Sespie 	while (error == 0 && uio->uio_resid > 0) {
5577013b092Sespie 		vsize_t len;
5587013b092Sespie 
5597013b092Sespie 		if (node->tn_size <= uio->uio_offset) {
5607013b092Sespie 			break;
5617013b092Sespie 		}
5627013b092Sespie 		len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid);
5637013b092Sespie 		if (len == 0) {
5647013b092Sespie 			break;
5657013b092Sespie 		}
5667013b092Sespie 		error = tmpfs_uiomove(node, uio, len);
5677013b092Sespie 	}
5687013b092Sespie 
5696a693b5fSnatano 	if (!(vp->v_mount->mnt_flag & MNT_NOATIME))
5706a693b5fSnatano 		tmpfs_update(node, TMPFS_NODE_ACCESSED);
5716a693b5fSnatano 
5727013b092Sespie 	return error;
5737013b092Sespie }
5747013b092Sespie 
5757013b092Sespie int
5767013b092Sespie tmpfs_write(void *v)
5777013b092Sespie {
5787013b092Sespie 	struct vop_write_args /* {
5797013b092Sespie 		struct vnode	*a_vp;
5807013b092Sespie 		struct uio	*a_uio;
5817013b092Sespie 		int		a_ioflag;
5827013b092Sespie 		kauth_cred_t	a_cred;
5837013b092Sespie 	} */ *ap = v;
5847013b092Sespie 	struct vnode *vp = ap->a_vp;
5857013b092Sespie 	struct uio *uio = ap->a_uio;
5867013b092Sespie 	const int ioflag = ap->a_ioflag;
5877013b092Sespie 	tmpfs_node_t *node;
5887013b092Sespie 	off_t oldsize;
589081a143fSguenther 	ssize_t overrun;
5907013b092Sespie 	int extended;
5917013b092Sespie 	int error;
5927013b092Sespie 
5937013b092Sespie 	KASSERT(VOP_ISLOCKED(vp));
5947013b092Sespie 
5957013b092Sespie 	node = VP_TO_TMPFS_NODE(vp);
5967013b092Sespie 	oldsize = node->tn_size;
5977013b092Sespie 
598674fe79dSespie 	if (vp->v_type != VREG)
599674fe79dSespie 		return (EINVAL);
600674fe79dSespie 
601674fe79dSespie 	if (uio->uio_resid == 0)
602674fe79dSespie 		return (0);
603674fe79dSespie 
6047013b092Sespie 	if (ioflag & IO_APPEND) {
6057013b092Sespie 		uio->uio_offset = node->tn_size;
6067013b092Sespie 	}
6077013b092Sespie 
608081a143fSguenther 	if (uio->uio_offset < 0 ||
609081a143fSguenther 	    (u_int64_t)uio->uio_offset + uio->uio_resid > LLONG_MAX)
610081a143fSguenther 		return (EFBIG);
611081a143fSguenther 
612081a143fSguenther 	/* do the filesize rlimit check */
613081a143fSguenther 	if ((error = vn_fsizechk(vp, uio, ioflag, &overrun)))
614081a143fSguenther 		return (error);
615081a143fSguenther 
6167013b092Sespie 	extended = uio->uio_offset + uio->uio_resid > node->tn_size;
6177013b092Sespie 	if (extended) {
6187013b092Sespie 		error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid);
6197013b092Sespie 		if (error)
6207013b092Sespie 			goto out;
6217013b092Sespie 	}
6227013b092Sespie 
6237013b092Sespie 	error = 0;
6247013b092Sespie 	while (error == 0 && uio->uio_resid > 0) {
6257013b092Sespie 		vsize_t len;
6269720dc33Spatrick 		uvm_vnp_uncache(vp);
6277013b092Sespie 		len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid);
6287013b092Sespie 		if (len == 0) {
6297013b092Sespie 			break;
6307013b092Sespie 		}
6317013b092Sespie 		error = tmpfs_uiomove(node, uio, len);
6327013b092Sespie 	}
6337013b092Sespie 	if (error) {
6347013b092Sespie 		(void)tmpfs_reg_resize(vp, oldsize);
6357013b092Sespie 	}
6367013b092Sespie 
6376a693b5fSnatano 	tmpfs_update(node, TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED);
6387013b092Sespie 	if (extended)
6397013b092Sespie 		VN_KNOTE(vp, NOTE_WRITE | NOTE_EXTEND);
6407013b092Sespie 	else
6417013b092Sespie 		VN_KNOTE(vp, NOTE_WRITE);
6427013b092Sespie out:
6437013b092Sespie 	if (error) {
6447013b092Sespie 		KASSERT(oldsize == node->tn_size);
6457013b092Sespie 	} else {
6467013b092Sespie 		KASSERT(uio->uio_resid == 0);
647081a143fSguenther 
648081a143fSguenther 		/* correct the result for writes clamped by vn_fsizechk() */
649081a143fSguenther 		uio->uio_resid += overrun;
650081a143fSguenther 
6517013b092Sespie 	}
6527013b092Sespie 	return error;
6537013b092Sespie }
6547013b092Sespie 
6557013b092Sespie int
6567013b092Sespie tmpfs_fsync(void *v)
6577013b092Sespie {
658a046f53bSreyk #ifdef DIAGNOSTIC
6597013b092Sespie 	struct vop_fsync_args /* {
6607013b092Sespie 		struct vnode *a_vp;
6617013b092Sespie 		struct ucred *a_cred;
6627013b092Sespie 		int a_flags;
6637013b092Sespie 		off_t a_offlo;
6647013b092Sespie 		off_t a_offhi;
6657013b092Sespie 		struct lwp *a_l;
6667013b092Sespie 	} */ *ap = v;
6677013b092Sespie 	struct vnode *vp = ap->a_vp;
6687013b092Sespie 
6697013b092Sespie 	/* Nothing to do.  Just update. */
6707013b092Sespie 	KASSERT(VOP_ISLOCKED(vp));
671a046f53bSreyk #endif
6727013b092Sespie 	return 0;
6737013b092Sespie }
6747013b092Sespie 
6757013b092Sespie /*
6767013b092Sespie  * tmpfs_remove: unlink a file.
6777013b092Sespie  *
6787013b092Sespie  * => Both directory (dvp) and file (vp) are locked.
6797013b092Sespie  * => We unlock and drop the reference on both.
6807013b092Sespie  */
6817013b092Sespie int
6827013b092Sespie tmpfs_remove(void *v)
6837013b092Sespie {
6847013b092Sespie 	struct vop_remove_args /* {
6857013b092Sespie 		struct vnode *a_dvp;
6867013b092Sespie 		struct vnode *a_vp;
6877013b092Sespie 		struct componentname *a_cnp;
6887013b092Sespie 	} */ *ap = v;
6897013b092Sespie 	struct vnode *dvp = ap->a_dvp, *vp = ap->a_vp;
6907013b092Sespie 	struct componentname *cnp = ap->a_cnp;
69146905cb9Sespie 	tmpfs_node_t *dnode, *node;
6927013b092Sespie 	tmpfs_dirent_t *de;
6937013b092Sespie 	int error;
6947013b092Sespie 
6957013b092Sespie 	KASSERT(cnp->cn_flags & HASBUF);
6967013b092Sespie 
6977013b092Sespie 	if (vp->v_type == VDIR) {
6987013b092Sespie 		error = EPERM;
6997013b092Sespie 		goto out;
7007013b092Sespie 	}
70146905cb9Sespie 
70246905cb9Sespie 	dnode = VP_TO_TMPFS_NODE(dvp);
7037013b092Sespie 	node = VP_TO_TMPFS_NODE(vp);
7047013b092Sespie 
7057013b092Sespie 	/* Files marked as immutable or append-only cannot be deleted. */
7067013b092Sespie 	if (node->tn_flags & (IMMUTABLE | APPEND)) {
7077013b092Sespie 		error = EPERM;
7087013b092Sespie 		goto out;
7097013b092Sespie 	}
7107013b092Sespie 
71146905cb9Sespie 	/*
71246905cb9Sespie 	 * Likewise, files residing on directories marked as append-only cannot
71346905cb9Sespie 	 * be deleted.
71446905cb9Sespie 	 */
71546905cb9Sespie 	if (dnode->tn_flags & APPEND) {
71646905cb9Sespie 		error = EPERM;
71746905cb9Sespie 		goto out;
71846905cb9Sespie 	}
71946905cb9Sespie 
7207013b092Sespie 	/* Lookup the directory entry (check the cached hint first). */
7217013b092Sespie 	de = tmpfs_dir_cached(node);
7227013b092Sespie 	if (de == NULL) {
7237013b092Sespie 		de = tmpfs_dir_lookup(dnode, cnp);
7247013b092Sespie 	}
7257013b092Sespie 
7267013b092Sespie 	KASSERT(de && de->td_node == node);
7277013b092Sespie 
7287013b092Sespie 	/*
7297013b092Sespie 	 * Remove the entry from the directory (drops the link count) and
73057331246Sespie 	 * destroy it.
7317013b092Sespie 	 * Note: the inode referred by it will not be destroyed
7327013b092Sespie 	 * until the vnode is reclaimed/recycled.
7337013b092Sespie 	 */
73457331246Sespie 	tmpfs_dir_detach(dnode, de);
7357013b092Sespie 	tmpfs_free_dirent(VFS_TO_TMPFS(vp->v_mount), de);
73646905cb9Sespie 	if (node->tn_links > 0)  {
73746905cb9Sespie 		/* We removed a hard link. */
73846905cb9Sespie 		tmpfs_update(node, TMPFS_NODE_CHANGED);
73946905cb9Sespie 	}
7407013b092Sespie 	error = 0;
7417013b092Sespie out:
7427013b092Sespie 	pool_put(&namei_pool, cnp->cn_pnbuf);
7437013b092Sespie 	return error;
7447013b092Sespie }
7457013b092Sespie 
7467013b092Sespie /*
7477013b092Sespie  * tmpfs_link: create a hard link.
7487013b092Sespie  */
7497013b092Sespie int
7507013b092Sespie tmpfs_link(void *v)
7517013b092Sespie {
7527013b092Sespie 	struct vop_link_args /* {
7537013b092Sespie 		struct vnode *a_dvp;
7547013b092Sespie 		struct vnode *a_vp;
7557013b092Sespie 		struct componentname *a_cnp;
7567013b092Sespie 	} */ *ap = v;
7577013b092Sespie 	struct vnode *dvp = ap->a_dvp;
7587013b092Sespie 	struct vnode *vp = ap->a_vp;
7597013b092Sespie 	struct componentname *cnp = ap->a_cnp;
7607013b092Sespie 	tmpfs_node_t *dnode, *node;
7617013b092Sespie 	tmpfs_dirent_t *de;
7627013b092Sespie 	int error;
7637013b092Sespie 
7647013b092Sespie 	KASSERT(VOP_ISLOCKED(dvp));
7654662f565Skettenis 	KASSERT(dvp != vp);
7664662f565Skettenis 
7677013b092Sespie 	dnode = VP_TO_TMPFS_DIR(dvp);
7687013b092Sespie 	node = VP_TO_TMPFS_NODE(vp);
7697013b092Sespie 
7706e880534Svisa 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
7717013b092Sespie 
7727013b092Sespie 	/* Check for maximum number of links limit. */
7737013b092Sespie 	if (node->tn_links == LINK_MAX) {
7747013b092Sespie 		error = EMLINK;
7757013b092Sespie 		goto out;
7767013b092Sespie 	}
7777013b092Sespie 	KASSERT(node->tn_links < LINK_MAX);
7787013b092Sespie 
7797013b092Sespie 	/* We cannot create links of files marked immutable or append-only. */
7807013b092Sespie 	if (node->tn_flags & (IMMUTABLE | APPEND)) {
7817013b092Sespie 		error = EPERM;
7827013b092Sespie 		goto out;
7837013b092Sespie 	}
7847013b092Sespie 
78557331246Sespie 	if (TMPFS_DIRSEQ_FULL(dnode)) {
78657331246Sespie 		error = ENOSPC;
78757331246Sespie 		goto out;
78857331246Sespie 	}
78957331246Sespie 
7907013b092Sespie 	/* Allocate a new directory entry to represent the inode. */
7917013b092Sespie 	error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount),
7927013b092Sespie 	    cnp->cn_nameptr, cnp->cn_namelen, &de);
7937013b092Sespie 	if (error) {
7947013b092Sespie 		goto out;
7957013b092Sespie 	}
7967013b092Sespie 
7977013b092Sespie 	/*
7987013b092Sespie 	 * Insert the entry into the directory.
7997013b092Sespie 	 * It will increase the inode link count.
8007013b092Sespie 	 */
80157331246Sespie 	tmpfs_dir_attach(dnode, de, node);
8027013b092Sespie 
8037013b092Sespie 	/* Update the timestamps and trigger the event. */
8047013b092Sespie 	if (node->tn_vnode) {
8057013b092Sespie 		VN_KNOTE(node->tn_vnode, NOTE_LINK);
8067013b092Sespie 	}
8077013b092Sespie 	tmpfs_update(node, TMPFS_NODE_CHANGED);
8087013b092Sespie 	error = 0;
8097013b092Sespie out:
8109651a1b7Sespie 	pool_put(&namei_pool, cnp->cn_pnbuf);
81136bb23f1Svisa 	VOP_UNLOCK(vp);
8127013b092Sespie 	vput(dvp);
8137013b092Sespie 	return error;
8147013b092Sespie }
8157013b092Sespie 
8167013b092Sespie int
8177013b092Sespie tmpfs_mkdir(void *v)
8187013b092Sespie {
8197013b092Sespie 	struct vop_mkdir_args /* {
8207013b092Sespie 		struct vnode		*a_dvp;
8217013b092Sespie 		struct vnode		**a_vpp;
8227013b092Sespie 		struct componentname	*a_cnp;
8237013b092Sespie 		struct vattr		*a_vap;
8247013b092Sespie 	} */ *ap = v;
8257013b092Sespie 	struct vnode *dvp = ap->a_dvp;
8267013b092Sespie 	struct vnode **vpp = ap->a_vpp;
8277013b092Sespie 	struct componentname *cnp = ap->a_cnp;
8287013b092Sespie 	struct vattr *vap = ap->a_vap;
8299d1c6d70Svisa 	int error;
8307013b092Sespie 
8317013b092Sespie 	KASSERT(vap->va_type == VDIR);
8329d1c6d70Svisa 	error = tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
8339d1c6d70Svisa 	vput(dvp);
8349d1c6d70Svisa 	return error;
8357013b092Sespie }
8367013b092Sespie 
8377013b092Sespie int
8387013b092Sespie tmpfs_rmdir(void *v)
8397013b092Sespie {
8407013b092Sespie 	struct vop_rmdir_args /* {
8417013b092Sespie 		struct vnode		*a_dvp;
8427013b092Sespie 		struct vnode		*a_vp;
8437013b092Sespie 		struct componentname	*a_cnp;
8447013b092Sespie 	} */ *ap = v;
8457013b092Sespie 	struct vnode *dvp = ap->a_dvp;
8467013b092Sespie 	struct vnode *vp = ap->a_vp;
8477013b092Sespie 	struct componentname *cnp = ap->a_cnp;
8487013b092Sespie 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount);
8497013b092Sespie 	tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp);
8507013b092Sespie 	tmpfs_node_t *node = VP_TO_TMPFS_DIR(vp);
8517013b092Sespie 	tmpfs_dirent_t *de;
8527013b092Sespie 	int error = 0;
8537013b092Sespie 
8547013b092Sespie 	KASSERT(VOP_ISLOCKED(dvp));
8557013b092Sespie 	KASSERT(VOP_ISLOCKED(vp));
8567013b092Sespie 	KASSERT(cnp->cn_flags & HASBUF);
8577013b092Sespie 
85846905cb9Sespie 	if (cnp->cn_namelen == 2 && cnp->cn_nameptr[0] == '.' &&
85946905cb9Sespie 	    cnp->cn_nameptr[1] == '.') {
86046905cb9Sespie 		error = ENOTEMPTY;
86146905cb9Sespie 		goto out;
86246905cb9Sespie 	}
86346905cb9Sespie 
86446905cb9Sespie 	KASSERT(node->tn_spec.tn_dir.tn_parent == dnode);
86546905cb9Sespie 
8667013b092Sespie 	/*
86757331246Sespie 	 * Directories with more than two entries ('.' and '..') cannot be
86857331246Sespie 	 * removed.
8697013b092Sespie 	 */
8707013b092Sespie 	if (node->tn_size > 0) {
8717013b092Sespie 		KASSERT(error == 0);
8727013b092Sespie 		TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {
8737013b092Sespie 			error = ENOTEMPTY;
8747013b092Sespie 			break;
8757013b092Sespie 		}
8767013b092Sespie 		if (error)
8777013b092Sespie 			goto out;
8787013b092Sespie 	}
8797013b092Sespie 
8807013b092Sespie 	/* Lookup the directory entry (check the cached hint first). */
8817013b092Sespie 	de = tmpfs_dir_cached(node);
8827013b092Sespie 	if (de == NULL)
8837013b092Sespie 		de = tmpfs_dir_lookup(dnode, cnp);
8847013b092Sespie 
8857013b092Sespie 	KASSERT(de && de->td_node == node);
8867013b092Sespie 
8877013b092Sespie 	/* Check flags to see if we are allowed to remove the directory. */
8887013b092Sespie 	if (dnode->tn_flags & APPEND || node->tn_flags & (IMMUTABLE | APPEND)) {
8897013b092Sespie 		error = EPERM;
8907013b092Sespie 		goto out;
8917013b092Sespie 	}
8927013b092Sespie 
8937013b092Sespie 	/* Decrement the link count for the virtual '.' entry. */
8947013b092Sespie 	node->tn_links--;
8957013b092Sespie 	tmpfs_update(node, TMPFS_NODE_STATUSALL);
8967013b092Sespie 
8977013b092Sespie 	/* Detach the directory entry from the directory. */
89857331246Sespie 	tmpfs_dir_detach(dnode, de);
8997013b092Sespie 
9007013b092Sespie 	/* Purge the cache for parent. */
9017013b092Sespie 	cache_purge(dvp);
9027013b092Sespie 
9037013b092Sespie 	/*
90457331246Sespie 	 * Destroy the directory entry.
9057013b092Sespie 	 * Note: the inode referred by it will not be destroyed
9067013b092Sespie 	 * until the vnode is reclaimed.
9077013b092Sespie 	 */
9087013b092Sespie 	tmpfs_free_dirent(tmp, de);
90957331246Sespie 	KASSERT(TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir) == NULL);
9107013b092Sespie 
9117013b092Sespie 	KASSERT(node->tn_links == 0);
9127013b092Sespie out:
9137013b092Sespie 	pool_put(&namei_pool, cnp->cn_pnbuf);
9147013b092Sespie 	/* Release the nodes. */
9157013b092Sespie 	vput(dvp);
9167013b092Sespie 	vput(vp);
9177013b092Sespie 	return error;
9187013b092Sespie }
9197013b092Sespie 
9207013b092Sespie int
9217013b092Sespie tmpfs_symlink(void *v)
9227013b092Sespie {
9237013b092Sespie 	struct vop_symlink_args /* {
9247013b092Sespie 		struct vnode		*a_dvp;
9257013b092Sespie 		struct vnode		**a_vpp;
9267013b092Sespie 		struct componentname	*a_cnp;
9277013b092Sespie 		struct vattr		*a_vap;
9287013b092Sespie 		char			*a_target;
9297013b092Sespie 	} */ *ap = v;
9307013b092Sespie 	struct vnode *dvp = ap->a_dvp;
9317013b092Sespie 	struct vnode **vpp = ap->a_vpp;
9327013b092Sespie 	struct componentname *cnp = ap->a_cnp;
9337013b092Sespie 	struct vattr *vap = ap->a_vap;
9347013b092Sespie 	char *target = ap->a_target;
9357013b092Sespie 	int error;
9367013b092Sespie 
9377013b092Sespie 	KASSERT(vap->va_type == 0);
9387013b092Sespie 	vap->va_type = VLNK;
9397013b092Sespie 
9407013b092Sespie 	error = tmpfs_alloc_file(dvp, vpp, vap, cnp, target);
9419d1c6d70Svisa 	vput(dvp);
9427013b092Sespie 	if (error == 0)
9437013b092Sespie 		vput(*vpp);
9447013b092Sespie 
94546905cb9Sespie 	return error;
9467013b092Sespie }
9477013b092Sespie 
9487013b092Sespie int
9497013b092Sespie tmpfs_readdir(void *v)
9507013b092Sespie {
9517013b092Sespie 	struct vop_readdir_args /* {
9527013b092Sespie 		struct vnode	*a_vp;
9537013b092Sespie 		struct uio	*a_uio;
9547013b092Sespie 		kauth_cred_t	a_cred;
9557013b092Sespie 		int		*a_eofflag;
9567013b092Sespie 	} */ *ap = v;
9577013b092Sespie 	struct vnode *vp = ap->a_vp;
9587013b092Sespie 	struct uio *uio = ap->a_uio;
9597013b092Sespie 	int *eofflag = ap->a_eofflag;
9607013b092Sespie 	tmpfs_node_t *node;
9617013b092Sespie 	int error;
9627013b092Sespie 
9637013b092Sespie 	KASSERT(VOP_ISLOCKED(vp));
9647013b092Sespie 
9657013b092Sespie 	/* This operation only makes sense on directory nodes. */
9667013b092Sespie 	if (vp->v_type != VDIR) {
9677013b092Sespie 		return ENOTDIR;
9687013b092Sespie 	}
9697013b092Sespie 	node = VP_TO_TMPFS_DIR(vp);
97057331246Sespie 	/*
97157331246Sespie 	 * Retrieve the directory entries, unless it is being destroyed.
97257331246Sespie 	 */
97357331246Sespie 	if (node->tn_links) {
97457331246Sespie 		error = tmpfs_dir_getdents(node, uio);
97557331246Sespie 	} else {
9767013b092Sespie 		error = 0;
9777013b092Sespie 	}
9787013b092Sespie 
9797013b092Sespie 	if (eofflag != NULL) {
98057331246Sespie 		*eofflag = !error && uio->uio_offset == TMPFS_DIRSEQ_EOF;
9817013b092Sespie 	}
9827013b092Sespie 	return error;
9837013b092Sespie }
9847013b092Sespie 
9857013b092Sespie int
9867013b092Sespie tmpfs_readlink(void *v)
9877013b092Sespie {
9887013b092Sespie 	struct vop_readlink_args /* {
9897013b092Sespie 		struct vnode	*a_vp;
9907013b092Sespie 		struct uio	*a_uio;
9917013b092Sespie 		kauth_cred_t	a_cred;
9927013b092Sespie 	} */ *ap = v;
9937013b092Sespie 	struct vnode *vp = ap->a_vp;
9947013b092Sespie 	struct uio *uio = ap->a_uio;
9957013b092Sespie 	tmpfs_node_t *node;
9967013b092Sespie 	int error;
9977013b092Sespie 
9987013b092Sespie 	KASSERT(VOP_ISLOCKED(vp));
9997013b092Sespie 	KASSERT(uio->uio_offset == 0);
10007013b092Sespie 	KASSERT(vp->v_type == VLNK);
10017013b092Sespie 
10027013b092Sespie 	node = VP_TO_TMPFS_NODE(vp);
1003653068caSstefan 	error = uiomove(node->tn_spec.tn_lnk.tn_link,
1004653068caSstefan 	    MIN((size_t)node->tn_size, uio->uio_resid), uio);
10056a693b5fSnatano 
10066a693b5fSnatano 	if (!(vp->v_mount->mnt_flag & MNT_NOATIME))
10077013b092Sespie 		tmpfs_update(node, TMPFS_NODE_ACCESSED);
10087013b092Sespie 
10097013b092Sespie 	return error;
10107013b092Sespie }
10117013b092Sespie 
10127013b092Sespie int
10137013b092Sespie tmpfs_inactive(void *v)
10147013b092Sespie {
10157013b092Sespie 	struct vop_inactive_args /* {
10167013b092Sespie 		struct vnode *a_vp;
10177013b092Sespie 		int *a_recycle;
10187013b092Sespie 	} */ *ap = v;
10197013b092Sespie 	struct vnode *vp = ap->a_vp;
10207013b092Sespie 	tmpfs_node_t *node;
10217013b092Sespie 
10227013b092Sespie 	KASSERT(VOP_ISLOCKED(vp));
10237013b092Sespie 
10247013b092Sespie 	node = VP_TO_TMPFS_NODE(vp);
10257013b092Sespie 
10267013b092Sespie 	if (vp->v_type == VREG && tmpfs_uio_cached(node))
10277013b092Sespie 		tmpfs_uio_uncache(node);
10287013b092Sespie 
102936bb23f1Svisa 	VOP_UNLOCK(vp);
10307013b092Sespie 
10317013b092Sespie 	/*
10327013b092Sespie 	 * If we are done with the node, reclaim it so that it can be reused
10337013b092Sespie 	 * immediately.
10347013b092Sespie 	 */
10357013b092Sespie 	if (node->tn_links == 0)
10367013b092Sespie 		vrecycle(vp, curproc);
10377013b092Sespie 
10387013b092Sespie 	return 0;
10397013b092Sespie }
10407013b092Sespie 
10417013b092Sespie int
10427013b092Sespie tmpfs_reclaim(void *v)
10437013b092Sespie {
10447013b092Sespie 	struct vop_reclaim_args /* {
10457013b092Sespie 		struct vnode *a_vp;
10467013b092Sespie 	} */ *ap = v;
10477013b092Sespie 	struct vnode *vp = ap->a_vp;
10487013b092Sespie 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(vp->v_mount);
10497013b092Sespie 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
10507013b092Sespie 	int racing;
10517013b092Sespie 
10527013b092Sespie 	/* Disassociate inode from vnode. */
10537013b092Sespie 	rw_enter_write(&node->tn_nlock);
10547013b092Sespie 	node->tn_vnode = NULL;
10557013b092Sespie 	vp->v_data = NULL;
10567013b092Sespie 	/* Check if tmpfs_vnode_get() is racing with us. */
10577013b092Sespie 	racing = TMPFS_NODE_RECLAIMING(node);
10587013b092Sespie 	rw_exit_write(&node->tn_nlock);
10597013b092Sespie 
106078b494b9Sgerhard 	cache_purge(vp);
106178b494b9Sgerhard 
10627013b092Sespie 	/*
10637013b092Sespie 	 * If inode is not referenced, i.e. no links, then destroy it.
10647013b092Sespie 	 * Note: if racing - inode is about to get a new vnode, leave it.
10657013b092Sespie 	 */
10667013b092Sespie 	if (node->tn_links == 0 && !racing) {
10677013b092Sespie 		tmpfs_free_node(tmp, node);
10687013b092Sespie 	}
10697013b092Sespie 	return 0;
10707013b092Sespie }
10717013b092Sespie 
10727013b092Sespie int
10737013b092Sespie tmpfs_pathconf(void *v)
10747013b092Sespie {
10757013b092Sespie 	struct vop_pathconf_args /* {
10767013b092Sespie 		struct vnode	*a_vp;
10777013b092Sespie 		int		a_name;
10787013b092Sespie 		register_t	*a_retval;
10797013b092Sespie 	} */ *ap = v;
10807013b092Sespie 	const int name = ap->a_name;
10817013b092Sespie 	register_t *retval = ap->a_retval;
10827013b092Sespie 	int error = 0;
10837013b092Sespie 
10847013b092Sespie 	switch (name) {
10857013b092Sespie 	case _PC_LINK_MAX:
10867013b092Sespie 		*retval = LINK_MAX;
10877013b092Sespie 		break;
10887013b092Sespie 	case _PC_NAME_MAX:
10897013b092Sespie 		*retval = TMPFS_MAXNAMLEN;
10907013b092Sespie 		break;
10917013b092Sespie 	case _PC_CHOWN_RESTRICTED:
10927013b092Sespie 		*retval = 1;
10937013b092Sespie 		break;
10947013b092Sespie 	case _PC_NO_TRUNC:
10957013b092Sespie 		*retval = 1;
10967013b092Sespie 		break;
10977013b092Sespie 	case _PC_FILESIZEBITS:
1098bb41a875Sbrad 		*retval = 64;
1099bb41a875Sbrad 		break;
1100bb41a875Sbrad 	case _PC_TIMESTAMP_RESOLUTION:
1101bb41a875Sbrad 		*retval = 1;
11027013b092Sespie 		break;
11037013b092Sespie 	default:
11047013b092Sespie 		error = EINVAL;
11057013b092Sespie 	}
11067013b092Sespie 	return error;
11077013b092Sespie }
11087013b092Sespie 
11097013b092Sespie int
11107013b092Sespie tmpfs_advlock(void *v)
11117013b092Sespie {
11127013b092Sespie 	struct vop_advlock_args /* {
11137013b092Sespie 		struct vnode	*a_vp;
11147013b092Sespie 		void *		a_id;
11157013b092Sespie 		int		a_op;
11167013b092Sespie 		struct flock	*a_fl;
11177013b092Sespie 		int		a_flags;
11187013b092Sespie 	} */ *ap = v;
11197013b092Sespie 	struct vnode *vp = ap->a_vp;
11207013b092Sespie 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
11217013b092Sespie 
11227013b092Sespie 	return lf_advlock(&node->tn_lockf, node->tn_size, ap->a_id, ap->a_op,
11237013b092Sespie 	    ap->a_fl, ap->a_flags);
11247013b092Sespie }
11257013b092Sespie 
11267013b092Sespie int
11277013b092Sespie tmpfs_print(void *v)
11287013b092Sespie {
1129*5a0ec814Smiod #if defined(DEBUG) || defined(DIAGNOSTIC) || defined(VFSLCKDEBUG)
11307013b092Sespie 	struct vop_print_args /* {
11317013b092Sespie 		struct vnode	*a_vp;
11327013b092Sespie 	} */ *ap = v;
11337013b092Sespie 	struct vnode *vp = ap->a_vp;
11347013b092Sespie 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
11357013b092Sespie 
11367013b092Sespie 	printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n"
11377013b092Sespie 	    "\tmode 0%o, owner %d, group %d, size %lld",
11387013b092Sespie 	    node, node->tn_flags, node->tn_links, node->tn_mode, node->tn_uid,
11397013b092Sespie 	    node->tn_gid, node->tn_size);
11407013b092Sespie #ifdef FIFO
11417013b092Sespie 	if (vp->v_type == VFIFO)
11427013b092Sespie 		fifo_printinfo(vp);
11437013b092Sespie #endif
11447013b092Sespie 	printf("\n");
1145*5a0ec814Smiod #endif
11467013b092Sespie 	return 0;
11477013b092Sespie }
11487013b092Sespie 
11497013b092Sespie /* a null op */
11507013b092Sespie int
11517013b092Sespie tmpfs_bwrite(void *v)
11527013b092Sespie {
11537013b092Sespie 	return 0;
11547013b092Sespie }
11557013b092Sespie 
11567013b092Sespie int
11577013b092Sespie tmpfs_strategy(void *v)
11587013b092Sespie {
11597013b092Sespie 	return EOPNOTSUPP;
11607013b092Sespie }
11617013b092Sespie 
11627013b092Sespie int
11637013b092Sespie tmpfs_ioctl(void *v)
11647013b092Sespie {
11657013b092Sespie 	return ENOTTY;
11667013b092Sespie }
11677013b092Sespie 
11687013b092Sespie int
11697013b092Sespie tmpfs_lock(void *v)
11707013b092Sespie {
11717013b092Sespie 	struct vop_lock_args *ap = v;
11727013b092Sespie 	tmpfs_node_t *tnp = VP_TO_TMPFS_NODE(ap->a_vp);
11737013b092Sespie 
117426b8ec94Snatano 	return rrw_enter(&tnp->tn_vlock, ap->a_flags & LK_RWFLAGS);
11757013b092Sespie }
11767013b092Sespie 
11777013b092Sespie int
11787013b092Sespie tmpfs_unlock(void *v)
11797013b092Sespie {
11807013b092Sespie 	struct vop_unlock_args *ap = v;
11817013b092Sespie 	tmpfs_node_t *tnp = VP_TO_TMPFS_NODE(ap->a_vp);
11827013b092Sespie 
118326b8ec94Snatano 	rrw_exit(&tnp->tn_vlock);
118426b8ec94Snatano 	return 0;
11857013b092Sespie }
11867013b092Sespie 
11877013b092Sespie int
11887013b092Sespie tmpfs_islocked(void *v)
11897013b092Sespie {
11907013b092Sespie 	struct vop_islocked_args *ap = v;
11917013b092Sespie 	tmpfs_node_t *tnp = VP_TO_TMPFS_NODE(ap->a_vp);
11927013b092Sespie 
119326b8ec94Snatano 	return rrw_status(&tnp->tn_vlock);
11947013b092Sespie }
11957013b092Sespie 
11967013b092Sespie /*
11977013b092Sespie  * tmpfs_rename: rename routine, the hairiest system call, with the
11987013b092Sespie  * insane API.
11997013b092Sespie  *
12007013b092Sespie  * Arguments: fdvp (from-parent vnode), fvp (from-leaf), tdvp (to-parent)
12017013b092Sespie  * and tvp (to-leaf), if exists (NULL if not).
12027013b092Sespie  *
12037013b092Sespie  * => Caller holds a reference on fdvp and fvp, they are unlocked.
12047013b092Sespie  *    Note: fdvp and fvp can refer to the same object (i.e. when it is root).
12057013b092Sespie  *
12067013b092Sespie  * => Both tdvp and tvp are referenced and locked.  It is our responsibility
12077013b092Sespie  *    to release the references and unlock them (or destroy).
12087013b092Sespie  */
12097013b092Sespie 
12107013b092Sespie /*
12117013b092Sespie  * First, some forward declarations of subroutines.
12127013b092Sespie  */
12137013b092Sespie 
12147013b092Sespie int tmpfs_sane_rename(struct vnode *, struct componentname *,
12157013b092Sespie     struct vnode *, struct componentname *, struct ucred *, int);
12167013b092Sespie int tmpfs_rename_enter(struct mount *, struct tmpfs_mount *,
12177013b092Sespie     struct ucred *,
12187013b092Sespie     struct vnode *, struct tmpfs_node *, struct componentname *,
12197013b092Sespie     struct tmpfs_dirent **, struct vnode **,
12207013b092Sespie     struct vnode *, struct tmpfs_node *, struct componentname *,
12217013b092Sespie     struct tmpfs_dirent **, struct vnode **);
12227013b092Sespie int tmpfs_rename_enter_common(struct mount *, struct tmpfs_mount *,
12237013b092Sespie     struct ucred *,
12247013b092Sespie     struct vnode *, struct tmpfs_node *,
12257013b092Sespie     struct componentname *, struct tmpfs_dirent **, struct vnode **,
12267013b092Sespie     struct componentname *, struct tmpfs_dirent **, struct vnode **);
12277013b092Sespie int tmpfs_rename_enter_separate(struct mount *, struct tmpfs_mount *,
12287013b092Sespie     struct ucred *,
12297013b092Sespie     struct vnode *, struct tmpfs_node *, struct componentname *,
12307013b092Sespie     struct tmpfs_dirent **, struct vnode **,
12317013b092Sespie     struct vnode *, struct tmpfs_node *, struct componentname *,
12327013b092Sespie     struct tmpfs_dirent **, struct vnode **);
12337013b092Sespie void tmpfs_rename_exit(struct tmpfs_mount *,
12347013b092Sespie     struct vnode *, struct vnode *, struct vnode *, struct vnode *);
12357013b092Sespie int tmpfs_rename_lock_directory(struct vnode *, struct tmpfs_node *);
12367013b092Sespie int tmpfs_rename_genealogy(struct tmpfs_node *, struct tmpfs_node *,
12377013b092Sespie     struct tmpfs_node **);
12387013b092Sespie int tmpfs_rename_lock(struct mount *, struct ucred *, int,
12397013b092Sespie     struct vnode *, struct tmpfs_node *, struct componentname *, int,
12407013b092Sespie     struct tmpfs_dirent **, struct vnode **,
12417013b092Sespie     struct vnode *, struct tmpfs_node *, struct componentname *, int,
12427013b092Sespie     struct tmpfs_dirent **, struct vnode **);
12437013b092Sespie void tmpfs_rename_attachdetach(struct tmpfs_mount *,
12447013b092Sespie     struct vnode *, struct tmpfs_dirent *, struct vnode *,
12457013b092Sespie     struct vnode *, struct tmpfs_dirent *, struct vnode *);
12467013b092Sespie int tmpfs_do_remove(struct tmpfs_mount *, struct vnode *,
12477013b092Sespie     struct tmpfs_node *, struct tmpfs_dirent *, struct vnode *, struct ucred *);
12487013b092Sespie int tmpfs_rename_check_possible(struct tmpfs_node *,
12497013b092Sespie     struct tmpfs_node *, struct tmpfs_node *, struct tmpfs_node *);
12507013b092Sespie int tmpfs_rename_check_permitted(struct ucred *,
12517013b092Sespie     struct tmpfs_node *, struct tmpfs_node *,
12527013b092Sespie     struct tmpfs_node *, struct tmpfs_node *);
12537013b092Sespie int tmpfs_remove_check_possible(struct tmpfs_node *,
12547013b092Sespie     struct tmpfs_node *);
12557013b092Sespie int tmpfs_remove_check_permitted(struct ucred *,
12567013b092Sespie     struct tmpfs_node *, struct tmpfs_node *);
12577013b092Sespie int tmpfs_check_sticky(struct ucred *,
12587013b092Sespie     struct tmpfs_node *, struct tmpfs_node *);
12597013b092Sespie void tmpfs_rename_cache_purge(struct vnode *, struct vnode *, struct vnode *,
12607013b092Sespie     struct vnode *);
126146905cb9Sespie void tmpfs_rename_abort(void *);
12627013b092Sespie 
12637013b092Sespie int
12647013b092Sespie tmpfs_rename(void *v)
12657013b092Sespie {
12667013b092Sespie 	struct vop_rename_args  /* {
12677013b092Sespie 		struct vnode		*a_fdvp;
12687013b092Sespie 		struct vnode		*a_fvp;
12697013b092Sespie 		struct componentname	*a_fcnp;
12707013b092Sespie 		struct vnode		*a_tdvp;
12717013b092Sespie 		struct vnode		*a_tvp;
12727013b092Sespie 		struct componentname	*a_tcnp;
12737013b092Sespie 	} */ *ap = v;
12747013b092Sespie 	struct vnode *fdvp = ap->a_fdvp;
12757013b092Sespie 	struct vnode *fvp = ap->a_fvp;
12767013b092Sespie 	struct componentname *fcnp = ap->a_fcnp;
12777013b092Sespie 	struct vnode *tdvp = ap->a_tdvp;
12787013b092Sespie 	struct vnode *tvp = ap->a_tvp;
12797013b092Sespie 	struct componentname *tcnp = ap->a_tcnp;
12807013b092Sespie 	struct ucred *cred;
12817013b092Sespie 	int error;
12827013b092Sespie 
12837013b092Sespie 	KASSERT(fdvp != NULL);
12847013b092Sespie 	KASSERT(fvp != NULL);
12857013b092Sespie 	KASSERT(fcnp != NULL);
12867013b092Sespie 	KASSERT(fcnp->cn_nameptr != NULL);
12877013b092Sespie 	KASSERT(tdvp != NULL);
12887013b092Sespie 	KASSERT(tcnp != NULL);
12897013b092Sespie 	KASSERT(fcnp->cn_nameptr != NULL);
12907013b092Sespie 	/* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */
12917013b092Sespie 	/* KASSERT(VOP_ISLOCKED(fvp) != LK_EXCLUSIVE); */
12927013b092Sespie 	KASSERT(fdvp->v_type == VDIR);
12937013b092Sespie 	KASSERT(tdvp->v_type == VDIR);
12947013b092Sespie 	KASSERT(fcnp->cn_flags & HASBUF);
12957013b092Sespie 	KASSERT(tcnp->cn_flags & HASBUF);
12967013b092Sespie 
12977013b092Sespie 	cred = fcnp->cn_cred;
12987013b092Sespie 	KASSERT(tcnp->cn_cred == cred);
12997013b092Sespie 
13007013b092Sespie 	/*
13017013b092Sespie 	 * Check for cross-device rename.
13027cab0185Spatrick 	 * Also don't allow renames of mount points.
13037013b092Sespie 	 */
13047013b092Sespie 	if (fvp->v_mount != tdvp->v_mount ||
13057cab0185Spatrick 	    fdvp->v_mount != fvp->v_mount ||
13067013b092Sespie 	    (tvp != NULL && (fvp->v_mount != tvp->v_mount))) {
130746905cb9Sespie 	    	tmpfs_rename_abort(v);
13087013b092Sespie 		return EXDEV;
13097013b092Sespie 	}
13107013b092Sespie 
13117013b092Sespie 	/*
1312d86fc745Sguenther 	 * Can't check the locks on these until we know they're on
1313d86fc745Sguenther 	 * the same FS, as not all FS do locking the same way.
1314d86fc745Sguenther 	 */
1315d86fc745Sguenther 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
1316d86fc745Sguenther 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
1317d86fc745Sguenther 
1318d86fc745Sguenther 	/*
131946905cb9Sespie 	 * Reject renaming '.' and '..'.
132046905cb9Sespie 	 */
132146905cb9Sespie 	if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
132246905cb9Sespie 	    (fcnp->cn_namelen == 2 && fcnp->cn_nameptr[0] == '.' &&
132346905cb9Sespie 	    fcnp->cn_nameptr[1] == '.')) {
132446905cb9Sespie 		tmpfs_rename_abort(v);
132546905cb9Sespie 		return EINVAL;
132646905cb9Sespie 	}
132746905cb9Sespie 
132846905cb9Sespie 	/*
13297013b092Sespie 	 * Sanitize our world from the VFS insanity.  Unlock the target
13307013b092Sespie 	 * directory and node, which are locked.  Release the children,
13317013b092Sespie 	 * which are referenced.  Check for rename("x", "y/."), which
13327013b092Sespie 	 * it is our responsibility to reject, not the caller's.  (But
13337013b092Sespie 	 * the caller does reject rename("x/.", "y").  Go figure.)
13347013b092Sespie 	 */
13357013b092Sespie 
133636bb23f1Svisa 	VOP_UNLOCK(tdvp);
13377013b092Sespie 	if ((tvp != NULL) && (tvp != tdvp))
133836bb23f1Svisa 		VOP_UNLOCK(tvp);
13397013b092Sespie 
13407013b092Sespie 	vrele(fvp);
13417013b092Sespie 	if (tvp != NULL)
13427013b092Sespie 		vrele(tvp);
13437013b092Sespie 
13447013b092Sespie 	if (tvp == tdvp) {
13457013b092Sespie 		error = EINVAL;
13467013b092Sespie 		goto out;
13477013b092Sespie 	}
13487013b092Sespie 
13497013b092Sespie 	error = tmpfs_sane_rename(fdvp, fcnp, tdvp, tcnp, cred, 0);
13507013b092Sespie 
13517013b092Sespie out:	/*
13527013b092Sespie 	 * All done, whether with success or failure.  Release the
13537013b092Sespie 	 * directory nodes now, as the caller expects from the VFS
13547013b092Sespie 	 * protocol.
13557013b092Sespie 	 */
13567013b092Sespie 	vrele(fdvp);
13577013b092Sespie 	vrele(tdvp);
13587013b092Sespie 
13597013b092Sespie 	return error;
13607013b092Sespie }
13617013b092Sespie 
13627013b092Sespie /*
13637013b092Sespie  * tmpfs_sane_rename: rename routine, the hairiest system call, with
13647013b092Sespie  * the sane API.
13657013b092Sespie  *
13667013b092Sespie  * Arguments:
13677013b092Sespie  *
13687013b092Sespie  * . fdvp (from directory vnode),
13697013b092Sespie  * . fcnp (from component name),
13707013b092Sespie  * . tdvp (to directory vnode), and
13717013b092Sespie  * . tcnp (to component name).
13727013b092Sespie  *
13737013b092Sespie  * fdvp and tdvp must be referenced and unlocked.
13747013b092Sespie  */
13757013b092Sespie int
13767013b092Sespie tmpfs_sane_rename(struct vnode *fdvp, struct componentname *fcnp,
13777013b092Sespie     struct vnode *tdvp, struct componentname *tcnp, struct ucred *cred,
13787013b092Sespie     int posixly_correct)
13797013b092Sespie {
13807013b092Sespie 	struct mount *mount;
13817013b092Sespie 	struct tmpfs_mount *tmpfs;
13827013b092Sespie 	struct tmpfs_node *fdnode, *tdnode;
13837013b092Sespie 	struct tmpfs_dirent *fde, *tde;
13847013b092Sespie 	struct vnode *fvp, *tvp;
13857013b092Sespie 	char *newname;
13867013b092Sespie 	int error;
13877013b092Sespie 
13887013b092Sespie 	KASSERT(fdvp != NULL);
13897013b092Sespie 	KASSERT(fcnp != NULL);
13907013b092Sespie 	KASSERT(tdvp != NULL);
13917013b092Sespie 	KASSERT(tcnp != NULL);
13927013b092Sespie 	/* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */
13937013b092Sespie 	/* KASSERT(VOP_ISLOCKED(tdvp) != LK_EXCLUSIVE); */
13947013b092Sespie 	KASSERT(fdvp->v_type == VDIR);
13957013b092Sespie 	KASSERT(tdvp->v_type == VDIR);
13967013b092Sespie 	KASSERT(fdvp->v_mount == tdvp->v_mount);
13977013b092Sespie 	KASSERT((fcnp->cn_flags & ISDOTDOT) == 0);
13987013b092Sespie 	KASSERT((tcnp->cn_flags & ISDOTDOT) == 0);
13997013b092Sespie 	KASSERT((fcnp->cn_namelen != 1) || (fcnp->cn_nameptr[0] != '.'));
14007013b092Sespie 	KASSERT((tcnp->cn_namelen != 1) || (tcnp->cn_nameptr[0] != '.'));
14017013b092Sespie 	KASSERT((fcnp->cn_namelen != 2) || (fcnp->cn_nameptr[0] != '.') ||
14027013b092Sespie 	    (fcnp->cn_nameptr[1] != '.'));
14037013b092Sespie 	KASSERT((tcnp->cn_namelen != 2) || (tcnp->cn_nameptr[0] != '.') ||
14047013b092Sespie 	    (tcnp->cn_nameptr[1] != '.'));
14057013b092Sespie 
14067013b092Sespie 	/*
14077013b092Sespie 	 * Pull out the tmpfs data structures.
14087013b092Sespie 	 */
14097013b092Sespie 	fdnode = VP_TO_TMPFS_NODE(fdvp);
14107013b092Sespie 	tdnode = VP_TO_TMPFS_NODE(tdvp);
14117013b092Sespie 	KASSERT(fdnode != NULL);
14127013b092Sespie 	KASSERT(tdnode != NULL);
14137013b092Sespie 	KASSERT(fdnode->tn_vnode == fdvp);
14147013b092Sespie 	KASSERT(tdnode->tn_vnode == tdvp);
14157013b092Sespie 	KASSERT(fdnode->tn_type == VDIR);
14167013b092Sespie 	KASSERT(tdnode->tn_type == VDIR);
14177013b092Sespie 
14187013b092Sespie 	mount = fdvp->v_mount;
14197013b092Sespie 	KASSERT(mount != NULL);
14207013b092Sespie 	KASSERT(mount == tdvp->v_mount);
14217013b092Sespie 	/* XXX How can we be sure this stays true?  (Not that you're
14227013b092Sespie 	 * likely to mount a tmpfs read-only...)  */
14237013b092Sespie 	KASSERT((mount->mnt_flag & MNT_RDONLY) == 0);
14247013b092Sespie 	tmpfs = VFS_TO_TMPFS(mount);
14257013b092Sespie 	KASSERT(tmpfs != NULL);
14267013b092Sespie 
14277013b092Sespie 	/*
14287013b092Sespie 	 * Decide whether we need a new name, and allocate memory for
14297013b092Sespie 	 * it if so.  Do this before locking anything or taking
14307013b092Sespie 	 * destructive actions so that we can back out safely and sleep
14317013b092Sespie 	 * safely.  XXX Is sleeping an issue here?  Can this just be
14327013b092Sespie 	 * moved into tmpfs_rename_attachdetach?
14337013b092Sespie 	 */
14347013b092Sespie 	if (tmpfs_strname_neqlen(fcnp, tcnp)) {
14357013b092Sespie 		newname = tmpfs_strname_alloc(tmpfs, tcnp->cn_namelen);
14367013b092Sespie 		if (newname == NULL) {
14377013b092Sespie 			error = ENOSPC;
14387013b092Sespie 			goto out_unlocked;
14397013b092Sespie 		}
14407013b092Sespie 	} else {
14417013b092Sespie 		newname = NULL;
14427013b092Sespie 	}
14437013b092Sespie 
14447013b092Sespie 	/*
14457013b092Sespie 	 * Lock and look up everything.  GCC is not very clever.
14467013b092Sespie 	 */
14477013b092Sespie 	fde = tde = NULL;
14487013b092Sespie 	fvp = tvp = NULL;
14497013b092Sespie 	error = tmpfs_rename_enter(mount, tmpfs, cred,
14507013b092Sespie 	    fdvp, fdnode, fcnp, &fde, &fvp,
14517013b092Sespie 	    tdvp, tdnode, tcnp, &tde, &tvp);
14527013b092Sespie 	if (error)
14537013b092Sespie 		goto out_unlocked;
14547013b092Sespie 
14557013b092Sespie 	/*
14567013b092Sespie 	 * Check that everything is locked and looks right.
14577013b092Sespie 	 */
14587013b092Sespie 	KASSERT(fde != NULL);
14597013b092Sespie 	KASSERT(fvp != NULL);
14607013b092Sespie 	KASSERT(fde->td_node != NULL);
14617013b092Sespie 	KASSERT(fde->td_node->tn_vnode == fvp);
14627013b092Sespie 	KASSERT(fde->td_node->tn_type == fvp->v_type);
14637013b092Sespie 	KASSERT((tde == NULL) == (tvp == NULL));
14647013b092Sespie 	KASSERT((tde == NULL) || (tde->td_node != NULL));
14657013b092Sespie 	KASSERT((tde == NULL) || (tde->td_node->tn_vnode == tvp));
14667013b092Sespie 	KASSERT((tde == NULL) || (tde->td_node->tn_type == tvp->v_type));
14677013b092Sespie 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
14687013b092Sespie 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
14697013b092Sespie 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
14707013b092Sespie 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
14717013b092Sespie 
14727013b092Sespie 	/*
14737013b092Sespie 	 * If the source and destination are the same object, we need
14747013b092Sespie 	 * only at most delete the source entry.
14757013b092Sespie 	 */
14767013b092Sespie 	if (fvp == tvp) {
14777013b092Sespie 		KASSERT(tvp != NULL);
14787013b092Sespie 		if (fde->td_node->tn_type == VDIR) {
14797013b092Sespie 			/* XXX How can this possibly happen?  */
14807013b092Sespie 			error = EINVAL;
14817013b092Sespie 			goto out_locked;
14827013b092Sespie 		}
14837013b092Sespie 		if (!posixly_correct && (fde != tde)) {
14847013b092Sespie 			/* XXX Doesn't work because of locking.
14857013b092Sespie 			 * error = VOP_REMOVE(fdvp, fvp);
14867013b092Sespie 			 */
14877013b092Sespie 			error = tmpfs_do_remove(tmpfs, fdvp, fdnode, fde, fvp,
14887013b092Sespie 			    cred);
14897013b092Sespie 			if (error)
14907013b092Sespie 				goto out_locked;
14917013b092Sespie 		}
14927013b092Sespie 		goto success;
14937013b092Sespie 	}
14947013b092Sespie 	KASSERT(fde != tde);
14957013b092Sespie 	KASSERT(fvp != tvp);
14967013b092Sespie 
14977013b092Sespie 	/*
14987013b092Sespie 	 * If the target exists, refuse to rename a directory over a
14997013b092Sespie 	 * non-directory or vice versa, or to clobber a non-empty
15007013b092Sespie 	 * directory.
15017013b092Sespie 	 */
15027013b092Sespie 	if (tvp != NULL) {
15037013b092Sespie 		KASSERT(tde != NULL);
15047013b092Sespie 		KASSERT(tde->td_node != NULL);
15057013b092Sespie 		if (fvp->v_type == VDIR && tvp->v_type == VDIR)
15067013b092Sespie 			error = ((tde->td_node->tn_size > 0)? ENOTEMPTY : 0);
15077013b092Sespie 		else if (fvp->v_type == VDIR && tvp->v_type != VDIR)
15087013b092Sespie 			error = ENOTDIR;
15097013b092Sespie 		else if (fvp->v_type != VDIR && tvp->v_type == VDIR)
15107013b092Sespie 			error = EISDIR;
15117013b092Sespie 		else
15127013b092Sespie 			error = 0;
15137013b092Sespie 		if (error)
15147013b092Sespie 			goto out_locked;
15157013b092Sespie 		KASSERT((fvp->v_type == VDIR) == (tvp->v_type == VDIR));
15167013b092Sespie 	}
15177013b092Sespie 
15187013b092Sespie 	/*
15197013b092Sespie 	 * Authorize the rename.
15207013b092Sespie 	 */
15217013b092Sespie 	error = tmpfs_rename_check_possible(fdnode, fde->td_node,
15227013b092Sespie 	    tdnode, (tde? tde->td_node : NULL));
15237013b092Sespie 	if (error)
15247013b092Sespie 		goto out_locked;
15257013b092Sespie 	error = tmpfs_rename_check_permitted(cred, fdnode, fde->td_node,
15267013b092Sespie 	    tdnode, (tde? tde->td_node : NULL));
15277013b092Sespie 	if (error)
15287013b092Sespie 		goto out_locked;
15297013b092Sespie 
15307013b092Sespie 	/*
15317013b092Sespie 	 * Everything is hunky-dory.  Shuffle the directory entries.
15327013b092Sespie 	 */
15337013b092Sespie 	tmpfs_rename_attachdetach(tmpfs, fdvp, fde, fvp, tdvp, tde, tvp);
15347013b092Sespie 
15357013b092Sespie 	/*
15367013b092Sespie 	 * Update the directory entry's name necessary, and flag
15377013b092Sespie 	 * metadata updates.  A memory allocation failure here is not
15387013b092Sespie 	 * OK because we've already committed some changes that we
15397013b092Sespie 	 * can't back out at this point, and we have things locked so
15407013b092Sespie 	 * we can't sleep, hence the early allocation above.
15417013b092Sespie 	 */
15427013b092Sespie 	if (newname != NULL) {
15437013b092Sespie 		KASSERT(tcnp->cn_namelen <= TMPFS_MAXNAMLEN);
15447013b092Sespie 
15457013b092Sespie 		tmpfs_strname_free(tmpfs, fde->td_name, fde->td_namelen);
15467013b092Sespie 		fde->td_namelen = (uint16_t)tcnp->cn_namelen;
15477013b092Sespie 		(void)memcpy(newname, tcnp->cn_nameptr, tcnp->cn_namelen);
15487013b092Sespie 		/* Commit newname and don't free it on the way out.  */
15497013b092Sespie 		fde->td_name = newname;
15507013b092Sespie 		newname = NULL;
15517013b092Sespie 
15527013b092Sespie 		tmpfs_update(fde->td_node, TMPFS_NODE_CHANGED);
15537013b092Sespie 		tmpfs_update(tdnode, TMPFS_NODE_MODIFIED);
15547013b092Sespie 	}
15557013b092Sespie 
15567013b092Sespie success:
15577013b092Sespie 	VN_KNOTE(fvp, NOTE_RENAME);
15587013b092Sespie 	tmpfs_rename_cache_purge(fdvp, fvp, tdvp, tvp);
15597013b092Sespie 	error = 0;
15607013b092Sespie 
15617013b092Sespie out_locked:
15627013b092Sespie 	tmpfs_rename_exit(tmpfs, fdvp, fvp, tdvp, tvp);
15637013b092Sespie 
15647013b092Sespie out_unlocked:
15657013b092Sespie 	/* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */
15667013b092Sespie 	/* KASSERT(VOP_ISLOCKED(tdvp) != LK_EXCLUSIVE); */
15677013b092Sespie 	/* KASSERT((fvp == NULL) || (VOP_ISLOCKED(fvp) != LK_EXCLUSIVE)); */
15687013b092Sespie 	/* KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) != LK_EXCLUSIVE)); */
15697013b092Sespie 
15707013b092Sespie 	if (newname != NULL)
15717013b092Sespie 		tmpfs_strname_free(tmpfs, newname, tcnp->cn_namelen);
15727013b092Sespie 
15737013b092Sespie 	return error;
15747013b092Sespie }
15757013b092Sespie 
15767013b092Sespie /*
15777013b092Sespie  * Look up fcnp in fdnode/fdvp and store its directory entry in fde_ret
15787013b092Sespie  * and the associated vnode in fvp_ret; fail if not found.  Look up
15797013b092Sespie  * tcnp in tdnode/tdvp and store its directory entry in tde_ret and the
15807013b092Sespie  * associated vnode in tvp_ret; store null instead if not found.  Fail
15817013b092Sespie  * if anything has been mounted on any of the nodes involved.
15827013b092Sespie  *
15837013b092Sespie  * fdvp and tdvp must be referenced.
15847013b092Sespie  *
15857013b092Sespie  * On entry, nothing is locked.
15867013b092Sespie  *
15877013b092Sespie  * On success, everything is locked, and *fvp_ret, and *tvp_ret if
15887013b092Sespie  * nonnull, are referenced.  The only pairs of vnodes that may be
15897013b092Sespie  * identical are {fdvp, tdvp} and {fvp, tvp}.
15907013b092Sespie  *
15917013b092Sespie  * On failure, everything remains as was.
15927013b092Sespie  *
15937013b092Sespie  * Locking everything including the source and target nodes is
15947013b092Sespie  * necessary to make sure that, e.g., link count updates are OK.  The
15957013b092Sespie  * locking order is, in general, ancestor-first, matching the order you
15967013b092Sespie  * need to use to look up a descendant anyway.
15977013b092Sespie  */
15987013b092Sespie int
15997013b092Sespie tmpfs_rename_enter(struct mount *mount, struct tmpfs_mount *tmpfs,
16007013b092Sespie     struct ucred *cred,
16017013b092Sespie     struct vnode *fdvp, struct tmpfs_node *fdnode, struct componentname *fcnp,
16027013b092Sespie     struct tmpfs_dirent **fde_ret, struct vnode **fvp_ret,
16037013b092Sespie     struct vnode *tdvp, struct tmpfs_node *tdnode, struct componentname *tcnp,
16047013b092Sespie     struct tmpfs_dirent **tde_ret, struct vnode **tvp_ret)
16057013b092Sespie {
16067013b092Sespie 	int error;
16077013b092Sespie 
16087013b092Sespie 	KASSERT(mount != NULL);
16097013b092Sespie 	KASSERT(tmpfs != NULL);
16107013b092Sespie 	KASSERT(fdvp != NULL);
16117013b092Sespie 	KASSERT(fdnode != NULL);
16127013b092Sespie 	KASSERT(fcnp != NULL);
16137013b092Sespie 	KASSERT(fde_ret != NULL);
16147013b092Sespie 	KASSERT(fvp_ret != NULL);
16157013b092Sespie 	KASSERT(tdvp != NULL);
16167013b092Sespie 	KASSERT(tdnode != NULL);
16177013b092Sespie 	KASSERT(tcnp != NULL);
16187013b092Sespie 	KASSERT(tde_ret != NULL);
16197013b092Sespie 	KASSERT(tvp_ret != NULL);
16207013b092Sespie 	KASSERT(fdnode->tn_vnode == fdvp);
16217013b092Sespie 	KASSERT(tdnode->tn_vnode == tdvp);
16227013b092Sespie 	KASSERT(fdnode->tn_type == VDIR);
16237013b092Sespie 	KASSERT(tdnode->tn_type == VDIR);
16247013b092Sespie 
16257013b092Sespie 	if (fdvp == tdvp) {
16267013b092Sespie 		KASSERT(fdnode == tdnode);
16277013b092Sespie 		error = tmpfs_rename_enter_common(mount, tmpfs, cred, fdvp,
16287013b092Sespie 		    fdnode, fcnp, fde_ret, fvp_ret, tcnp, tde_ret, tvp_ret);
16297013b092Sespie 	} else {
16307013b092Sespie 		KASSERT(fdnode != tdnode);
16317013b092Sespie 		error = tmpfs_rename_enter_separate(mount, tmpfs, cred,
16327013b092Sespie 		    fdvp, fdnode, fcnp, fde_ret, fvp_ret,
16337013b092Sespie 		    tdvp, tdnode, tcnp, tde_ret, tvp_ret);
16347013b092Sespie 	}
16357013b092Sespie 
16367013b092Sespie 	if (error)
16377013b092Sespie 		return error;
16387013b092Sespie 
16397013b092Sespie 	KASSERT(*fde_ret != NULL);
16407013b092Sespie 	KASSERT(*fvp_ret != NULL);
16417013b092Sespie 	KASSERT((*tde_ret == NULL) == (*tvp_ret == NULL));
16427013b092Sespie 	KASSERT((*tde_ret == NULL) || ((*tde_ret)->td_node != NULL));
16437013b092Sespie 	KASSERT((*tde_ret == NULL) ||
16447013b092Sespie 	    ((*tde_ret)->td_node->tn_vnode == *tvp_ret));
16457013b092Sespie 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
16467013b092Sespie 	KASSERT(VOP_ISLOCKED(*fvp_ret) == LK_EXCLUSIVE);
16477013b092Sespie 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
16487013b092Sespie 	KASSERT((*tvp_ret == NULL) ||
16497013b092Sespie 	    (VOP_ISLOCKED(*tvp_ret) == LK_EXCLUSIVE));
16507013b092Sespie 	KASSERT(*fvp_ret != fdvp);
16517013b092Sespie 	KASSERT(*fvp_ret != tdvp);
16527013b092Sespie 	KASSERT(*tvp_ret != fdvp);
16537013b092Sespie 	KASSERT(*tvp_ret != tdvp);
16547013b092Sespie 	return 0;
16557013b092Sespie }
16567013b092Sespie 
16577013b092Sespie /*
16587013b092Sespie  * Lock and look up with a common source/target directory.
16597013b092Sespie  */
16607013b092Sespie int
16617013b092Sespie tmpfs_rename_enter_common(struct mount *mount, struct tmpfs_mount *tmpfs,
16627013b092Sespie     struct ucred *cred,
16637013b092Sespie     struct vnode *dvp, struct tmpfs_node *dnode,
16647013b092Sespie     struct componentname *fcnp,
16657013b092Sespie     struct tmpfs_dirent **fde_ret, struct vnode **fvp_ret,
16667013b092Sespie     struct componentname *tcnp,
16677013b092Sespie     struct tmpfs_dirent **tde_ret, struct vnode **tvp_ret)
16687013b092Sespie {
16697013b092Sespie 	struct tmpfs_dirent *fde, *tde;
16707013b092Sespie 	struct vnode *fvp, *tvp;
16717013b092Sespie 	int error;
16727013b092Sespie 
16737013b092Sespie 	error = tmpfs_rename_lock_directory(dvp, dnode);
16747013b092Sespie 	if (error)
16757013b092Sespie 		goto fail0;
16767013b092Sespie 
16777013b092Sespie 	/* Did we lose a race with mount?  */
16787013b092Sespie 	if (dvp->v_mountedhere != NULL) {
16797013b092Sespie 		error = EBUSY;
16807013b092Sespie 		goto fail1;
16817013b092Sespie 	}
16827013b092Sespie 
16837013b092Sespie 	/* Make sure the caller may read the directory.  */
16847013b092Sespie 	error = VOP_ACCESS(dvp, VEXEC, cred, curproc);
16857013b092Sespie 	if (error)
16867013b092Sespie 		goto fail1;
16877013b092Sespie 
16887013b092Sespie 	/*
16897013b092Sespie 	 * The order in which we lock the source and target nodes is
16907013b092Sespie 	 * irrelevant because there can only be one rename on this
16917013b092Sespie 	 * directory in flight at a time, and we have it locked.
16927013b092Sespie 	 */
16937013b092Sespie 
16947013b092Sespie 	fde = tmpfs_dir_lookup(dnode, fcnp);
16957013b092Sespie 	if (fde == NULL) {
16967013b092Sespie 		error = ENOENT;
16977013b092Sespie 		goto fail1;
16987013b092Sespie 	}
16997013b092Sespie 
17007013b092Sespie 	KASSERT(fde->td_node != NULL);
17017013b092Sespie 	/* We ruled out `.' earlier.  */
17027013b092Sespie 	KASSERT(fde->td_node != dnode);
17037013b092Sespie 	/* We ruled out `..' earlier.  */
17047013b092Sespie 	KASSERT(fde->td_node != dnode->tn_spec.tn_dir.tn_parent);
17057013b092Sespie 	rw_enter_write(&fde->td_node->tn_nlock);
17067013b092Sespie 	error = tmpfs_vnode_get(mount, fde->td_node, &fvp);
17077013b092Sespie 	if (error)
17087013b092Sespie 		goto fail1;
17097013b092Sespie 	KASSERT(fvp != NULL);
17107013b092Sespie 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
17117013b092Sespie 	KASSERT(fvp != dvp);
17127013b092Sespie 	KASSERT(fvp->v_mount == mount);
17137013b092Sespie 
17147013b092Sespie 	/* Refuse to rename a mount point.  */
17157013b092Sespie 	if ((fvp->v_type == VDIR) && (fvp->v_mountedhere != NULL)) {
17167013b092Sespie 		error = EBUSY;
17177013b092Sespie 		goto fail2;
17187013b092Sespie 	}
17197013b092Sespie 
17207013b092Sespie 	tde = tmpfs_dir_lookup(dnode, tcnp);
17217013b092Sespie 	if (tde == NULL) {
17227013b092Sespie 		tvp = NULL;
17237013b092Sespie 	} else {
17247013b092Sespie 		KASSERT(tde->td_node != NULL);
17257013b092Sespie 		/* We ruled out `.' earlier.  */
17267013b092Sespie 		KASSERT(tde->td_node != dnode);
17277013b092Sespie 		/* We ruled out `..' earlier.  */
17287013b092Sespie 		KASSERT(tde->td_node != dnode->tn_spec.tn_dir.tn_parent);
17297013b092Sespie 		if (tde->td_node != fde->td_node) {
17307013b092Sespie 			rw_enter_write(&tde->td_node->tn_nlock);
17317013b092Sespie 			error = tmpfs_vnode_get(mount, tde->td_node, &tvp);
17327013b092Sespie 			if (error)
17337013b092Sespie 				goto fail2;
17347013b092Sespie 			KASSERT(tvp->v_mount == mount);
17357013b092Sespie 			/* Refuse to rename over a mount point.  */
17367013b092Sespie 			if ((tvp->v_type == VDIR) &&
17377013b092Sespie 			    (tvp->v_mountedhere != NULL)) {
17387013b092Sespie 				error = EBUSY;
17397013b092Sespie 				goto fail3;
17407013b092Sespie 			}
17417013b092Sespie 		} else {
17427013b092Sespie 			tvp = fvp;
17437013b092Sespie 			vref(tvp);
17447013b092Sespie 		}
17457013b092Sespie 		KASSERT(tvp != NULL);
17467013b092Sespie 		KASSERT(VOP_ISLOCKED(tvp) == LK_EXCLUSIVE);
17477013b092Sespie 	}
17487013b092Sespie 	KASSERT(tvp != dvp);
17497013b092Sespie 
17507013b092Sespie 	*fde_ret = fde;
17517013b092Sespie 	*fvp_ret = fvp;
17527013b092Sespie 	*tde_ret = tde;
17537013b092Sespie 	*tvp_ret = tvp;
17547013b092Sespie 	return 0;
17557013b092Sespie 
17567013b092Sespie fail3:	if (tvp != NULL) {
17577013b092Sespie 		if (tvp != fvp)
17587013b092Sespie 			vput(tvp);
17597013b092Sespie 		else
17607013b092Sespie 			vrele(tvp);
17617013b092Sespie 	}
17627013b092Sespie 
17637013b092Sespie fail2:	vput(fvp);
176436bb23f1Svisa fail1:	VOP_UNLOCK(dvp);
17657013b092Sespie fail0:	return error;
17667013b092Sespie }
17677013b092Sespie 
17687013b092Sespie /*
17697013b092Sespie  * Lock and look up with separate source and target directories.
17707013b092Sespie  */
17717013b092Sespie int
17727013b092Sespie tmpfs_rename_enter_separate(struct mount *mount, struct tmpfs_mount *tmpfs,
17737013b092Sespie     struct ucred *cred,
17747013b092Sespie     struct vnode *fdvp, struct tmpfs_node *fdnode, struct componentname *fcnp,
17757013b092Sespie     struct tmpfs_dirent **fde_ret, struct vnode **fvp_ret,
17767013b092Sespie     struct vnode *tdvp, struct tmpfs_node *tdnode, struct componentname *tcnp,
17777013b092Sespie     struct tmpfs_dirent **tde_ret, struct vnode **tvp_ret)
17787013b092Sespie {
17797013b092Sespie 	struct tmpfs_node *intermediate_node;
17807013b092Sespie 	struct tmpfs_dirent *fde, *tde;
17817013b092Sespie 	struct vnode *fvp, *tvp;
17827013b092Sespie 	int error;
17837013b092Sespie 
17847013b092Sespie 	KASSERT(fdvp != tdvp);
17857013b092Sespie 	KASSERT(fdnode != tdnode);
17867013b092Sespie 
17877013b092Sespie #if 0				/* XXX */
17887013b092Sespie 	mutex_enter(&tmpfs->tm_rename_lock);
17897013b092Sespie #endif
17907013b092Sespie 
17917013b092Sespie 	error = tmpfs_rename_genealogy(fdnode, tdnode, &intermediate_node);
17927013b092Sespie 	if (error)
17937013b092Sespie 		goto fail;
17947013b092Sespie 
17957013b092Sespie 	/*
17967013b092Sespie 	 * intermediate_node == NULL means fdnode is not an ancestor of
17977013b092Sespie 	 * tdnode.
17987013b092Sespie 	 */
17997013b092Sespie 	if (intermediate_node == NULL)
18007013b092Sespie 		error = tmpfs_rename_lock(mount, cred, ENOTEMPTY,
18017013b092Sespie 		    tdvp, tdnode, tcnp, 1, &tde, &tvp,
18027013b092Sespie 		    fdvp, fdnode, fcnp, 0, &fde, &fvp);
18037013b092Sespie 	else
18047013b092Sespie 		error = tmpfs_rename_lock(mount, cred, EINVAL,
18057013b092Sespie 		    fdvp, fdnode, fcnp, 0, &fde, &fvp,
18067013b092Sespie 		    tdvp, tdnode, tcnp, 1, &tde, &tvp);
18077013b092Sespie 	if (error)
18087013b092Sespie 		goto fail;
18097013b092Sespie 
18107013b092Sespie 	KASSERT(fde != NULL);
18117013b092Sespie 	KASSERT(fde->td_node != NULL);
18127013b092Sespie 
18137013b092Sespie 	/*
18147013b092Sespie 	 * Reject rename("foo/bar", "foo/bar/baz/quux/zot").
18157013b092Sespie 	 */
18167013b092Sespie 	if (fde->td_node == intermediate_node) {
18177013b092Sespie 		tmpfs_rename_exit(tmpfs, fdvp, fvp, tdvp, tvp);
18187013b092Sespie 		return EINVAL;
18197013b092Sespie 	}
18207013b092Sespie 
18217013b092Sespie 	*fde_ret = fde;
18227013b092Sespie 	*fvp_ret = fvp;
18237013b092Sespie 	*tde_ret = tde;
18247013b092Sespie 	*tvp_ret = tvp;
18257013b092Sespie 	return 0;
18267013b092Sespie 
18277013b092Sespie fail:
18287013b092Sespie #if 0				/* XXX */
18297013b092Sespie 	mutex_exit(&tmpfs->tm_rename_lock);
18307013b092Sespie #endif
18317013b092Sespie 	return error;
18327013b092Sespie }
18337013b092Sespie 
18347013b092Sespie /*
18357013b092Sespie  * Unlock everything we locked for rename.
18367013b092Sespie  *
18377013b092Sespie  * fdvp and tdvp must be referenced.
18387013b092Sespie  *
18397013b092Sespie  * On entry, everything is locked, and fvp and tvp referenced.
18407013b092Sespie  *
18417013b092Sespie  * On exit, everything is unlocked, and fvp and tvp are released.
18427013b092Sespie  */
18437013b092Sespie void
18447013b092Sespie tmpfs_rename_exit(struct tmpfs_mount *tmpfs,
18457013b092Sespie     struct vnode *fdvp, struct vnode *fvp,
18467013b092Sespie     struct vnode *tdvp, struct vnode *tvp)
18477013b092Sespie {
18487013b092Sespie 
18497013b092Sespie 	KASSERT(tmpfs != NULL);
18507013b092Sespie 	KASSERT(fdvp != NULL);
18517013b092Sespie 	KASSERT(fvp != NULL);
18527013b092Sespie 	KASSERT(fdvp != fvp);
18537013b092Sespie 	KASSERT(fdvp != tvp);
18547013b092Sespie 	KASSERT(tdvp != tvp);
18557013b092Sespie 	KASSERT(tdvp != fvp);
18567013b092Sespie 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
18577013b092Sespie 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
18587013b092Sespie 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
18597013b092Sespie 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
18607013b092Sespie 
18617013b092Sespie 	if (tvp != NULL) {
18627013b092Sespie 		if (tvp != fvp)
18637013b092Sespie 			vput(tvp);
18647013b092Sespie 		else
18657013b092Sespie 			vrele(tvp);
18667013b092Sespie 	}
186736bb23f1Svisa 	VOP_UNLOCK(tdvp);
18687013b092Sespie 	vput(fvp);
18697013b092Sespie 	if (fdvp != tdvp)
187036bb23f1Svisa 		VOP_UNLOCK(fdvp);
18717013b092Sespie 
18727013b092Sespie #if 0				/* XXX */
18737013b092Sespie 	if (fdvp != tdvp)
18747013b092Sespie 		mutex_exit(&tmpfs->tm_rename_lock);
18757013b092Sespie #endif
18767013b092Sespie }
18777013b092Sespie 
18787013b092Sespie /*
18797013b092Sespie  * Lock a directory, but fail if it has been rmdir'd.
18807013b092Sespie  *
18817013b092Sespie  * vp must be referenced.
18827013b092Sespie  */
18837013b092Sespie int
18847013b092Sespie tmpfs_rename_lock_directory(struct vnode *vp, struct tmpfs_node *node)
18857013b092Sespie {
18867013b092Sespie 
18877013b092Sespie 	KASSERT(vp != NULL);
18887013b092Sespie 	KASSERT(node != NULL);
18897013b092Sespie 	KASSERT(node->tn_vnode == vp);
18907013b092Sespie 	KASSERT(node->tn_type == VDIR);
18917013b092Sespie 
18926e880534Svisa 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
18937013b092Sespie 	if (node->tn_spec.tn_dir.tn_parent == NULL) {
189436bb23f1Svisa 		VOP_UNLOCK(vp);
18957013b092Sespie 		return ENOENT;
18967013b092Sespie 	}
18977013b092Sespie 
18987013b092Sespie 	return 0;
18997013b092Sespie }
19007013b092Sespie 
19017013b092Sespie /*
19027013b092Sespie  * Analyze the genealogy of the source and target nodes.
19037013b092Sespie  *
19047013b092Sespie  * On success, stores in *intermediate_node_ret either the child of
19057013b092Sespie  * fdnode of which tdnode is a descendant, or null if tdnode is not a
19067013b092Sespie  * descendant of fdnode at all.
19077013b092Sespie  *
19087013b092Sespie  * fdnode and tdnode must be unlocked and referenced.  The file
19097013b092Sespie  * system's rename lock must also be held, to exclude concurrent
19107013b092Sespie  * changes to the file system's genealogy other than rmdir.
19117013b092Sespie  *
19127013b092Sespie  * XXX This causes an extra lock/unlock of tdnode in the case when
19137013b092Sespie  * we're just about to lock it again before locking anything else.
19147013b092Sespie  * However, changing that requires reorganizing the code to make it
19157013b092Sespie  * even more horrifically obscure.
19167013b092Sespie  */
19177013b092Sespie int
19187013b092Sespie tmpfs_rename_genealogy(struct tmpfs_node *fdnode, struct tmpfs_node *tdnode,
19197013b092Sespie     struct tmpfs_node **intermediate_node_ret)
19207013b092Sespie {
19217013b092Sespie 	struct tmpfs_node *node = tdnode, *parent;
19227013b092Sespie 	int error;
19237013b092Sespie 
19247013b092Sespie 	KASSERT(fdnode != NULL);
19257013b092Sespie 	KASSERT(tdnode != NULL);
19267013b092Sespie 	KASSERT(fdnode != tdnode);
19277013b092Sespie 	KASSERT(intermediate_node_ret != NULL);
19287013b092Sespie 
19297013b092Sespie 	KASSERT(fdnode->tn_vnode != NULL);
19307013b092Sespie 	KASSERT(tdnode->tn_vnode != NULL);
19317013b092Sespie 	KASSERT(fdnode->tn_type == VDIR);
19327013b092Sespie 	KASSERT(tdnode->tn_type == VDIR);
19337013b092Sespie 
19347013b092Sespie 	/*
19357013b092Sespie 	 * We need to provisionally lock tdnode->tn_vnode to keep rmdir
19367013b092Sespie 	 * from deleting it -- or any ancestor -- at an inopportune
19377013b092Sespie 	 * moment.
19387013b092Sespie 	 */
19397013b092Sespie 	error = tmpfs_rename_lock_directory(tdnode->tn_vnode, tdnode);
19407013b092Sespie 	if (error)
19417013b092Sespie 		return error;
19427013b092Sespie 
19437013b092Sespie 	for (;;) {
19447013b092Sespie 		parent = node->tn_spec.tn_dir.tn_parent;
19457013b092Sespie 		KASSERT(parent != NULL);
19467013b092Sespie 		KASSERT(parent->tn_type == VDIR);
19477013b092Sespie 
19487013b092Sespie 		/* Did we hit the root without finding fdnode?  */
19497013b092Sespie 		if (parent == node) {
19507013b092Sespie 			*intermediate_node_ret = NULL;
19517013b092Sespie 			break;
19527013b092Sespie 		}
19537013b092Sespie 
19547013b092Sespie 		/* Did we find that fdnode is an ancestor?  */
19557013b092Sespie 		if (parent == fdnode) {
19567013b092Sespie 			*intermediate_node_ret = node;
19577013b092Sespie 			break;
19587013b092Sespie 		}
19597013b092Sespie 
19607013b092Sespie 		/* Neither -- keep ascending the family tree.  */
19617013b092Sespie 		node = parent;
19627013b092Sespie 	}
19637013b092Sespie 
196436bb23f1Svisa 	VOP_UNLOCK(tdnode->tn_vnode);
19657013b092Sespie 	return 0;
19667013b092Sespie }
19677013b092Sespie 
19687013b092Sespie /*
19697013b092Sespie  * Lock directories a and b, which must be distinct, and look up and
19707013b092Sespie  * lock nodes a and b.  Do a first and then b.  Directory b may not be
19717013b092Sespie  * an ancestor of directory a, although directory a may be an ancestor
19727013b092Sespie  * of directory b.  Fail with overlap_error if node a is directory b.
19737013b092Sespie  * Neither componentname may be `.' or `..'.
19747013b092Sespie  *
19757013b092Sespie  * a_dvp and b_dvp must be referenced.
19767013b092Sespie  *
19777013b092Sespie  * On entry, a_dvp and b_dvp are unlocked.
19787013b092Sespie  *
19797013b092Sespie  * On success,
19807013b092Sespie  * . a_dvp and b_dvp are locked,
19817013b092Sespie  * . *a_dirent_ret is filled with a directory entry whose node is
19827013b092Sespie  *     locked and referenced,
19837013b092Sespie  * . *b_vp_ret is filled with the corresponding vnode,
19847013b092Sespie  * . *b_dirent_ret is filled either with null or with a directory entry
19857013b092Sespie  *     whose node is locked and referenced,
19867013b092Sespie  * . *b_vp is filled either with null or with the corresponding vnode,
19877013b092Sespie  *     and
19887013b092Sespie  * . the only pair of vnodes that may be identical is a_vp and b_vp.
19897013b092Sespie  *
19907013b092Sespie  * On failure, a_dvp and b_dvp are left unlocked, and *a_dirent_ret,
19917013b092Sespie  * *a_vp, *b_dirent_ret, and *b_vp are left alone.
19927013b092Sespie  */
19937013b092Sespie int
19947013b092Sespie tmpfs_rename_lock(struct mount *mount, struct ucred *cred, int overlap_error,
19957013b092Sespie     struct vnode *a_dvp, struct tmpfs_node *a_dnode,
19967013b092Sespie     struct componentname *a_cnp, int a_missing_ok,
19977013b092Sespie     struct tmpfs_dirent **a_dirent_ret, struct vnode **a_vp_ret,
19987013b092Sespie     struct vnode *b_dvp, struct tmpfs_node *b_dnode,
19997013b092Sespie     struct componentname *b_cnp, int b_missing_ok,
20007013b092Sespie     struct tmpfs_dirent **b_dirent_ret, struct vnode **b_vp_ret)
20017013b092Sespie {
20027013b092Sespie 	struct tmpfs_dirent *a_dirent, *b_dirent;
20037013b092Sespie 	struct vnode *a_vp, *b_vp;
20047013b092Sespie 	int error;
20057013b092Sespie 
20067013b092Sespie 	KASSERT(a_dvp != NULL);
20077013b092Sespie 	KASSERT(a_dnode != NULL);
20087013b092Sespie 	KASSERT(a_cnp != NULL);
20097013b092Sespie 	KASSERT(a_dirent_ret != NULL);
20107013b092Sespie 	KASSERT(a_vp_ret != NULL);
20117013b092Sespie 	KASSERT(b_dvp != NULL);
20127013b092Sespie 	KASSERT(b_dnode != NULL);
20137013b092Sespie 	KASSERT(b_cnp != NULL);
20147013b092Sespie 	KASSERT(b_dirent_ret != NULL);
20157013b092Sespie 	KASSERT(b_vp_ret != NULL);
20167013b092Sespie 	KASSERT(a_dvp != b_dvp);
20177013b092Sespie 	KASSERT(a_dnode != b_dnode);
20187013b092Sespie 	KASSERT(a_dnode->tn_vnode == a_dvp);
20197013b092Sespie 	KASSERT(b_dnode->tn_vnode == b_dvp);
20207013b092Sespie 	KASSERT(a_dnode->tn_type == VDIR);
20217013b092Sespie 	KASSERT(b_dnode->tn_type == VDIR);
20227013b092Sespie 	KASSERT(a_missing_ok != b_missing_ok);
20237013b092Sespie 
20247013b092Sespie 	error = tmpfs_rename_lock_directory(a_dvp, a_dnode);
20257013b092Sespie 	if (error)
20267013b092Sespie 		goto fail0;
20277013b092Sespie 
20287013b092Sespie 	/* Did we lose a race with mount?  */
20297013b092Sespie 	if (a_dvp->v_mountedhere != NULL) {
20307013b092Sespie 		error = EBUSY;
20317013b092Sespie 		goto fail1;
20327013b092Sespie 	}
20337013b092Sespie 
20347013b092Sespie 	/* Make sure the caller may read the directory.  */
20357013b092Sespie 	error = VOP_ACCESS(a_dvp, VEXEC, cred, curproc);
20367013b092Sespie 	if (error)
20377013b092Sespie 		goto fail1;
20387013b092Sespie 
20397013b092Sespie 	a_dirent = tmpfs_dir_lookup(a_dnode, a_cnp);
20407013b092Sespie 	if (a_dirent != NULL) {
20417013b092Sespie 		KASSERT(a_dirent->td_node != NULL);
20427013b092Sespie 		/* We ruled out `.' earlier.  */
20437013b092Sespie 		KASSERT(a_dirent->td_node != a_dnode);
20447013b092Sespie 		/* We ruled out `..' earlier.  */
20457013b092Sespie 		KASSERT(a_dirent->td_node !=
20467013b092Sespie 		    a_dnode->tn_spec.tn_dir.tn_parent);
20477013b092Sespie 		if (a_dirent->td_node == b_dnode) {
20487013b092Sespie 			error = overlap_error;
20497013b092Sespie 			goto fail1;
20507013b092Sespie 		}
20517013b092Sespie 		rw_enter_write(&a_dirent->td_node->tn_nlock);
20527013b092Sespie 		error = tmpfs_vnode_get(mount, a_dirent->td_node, &a_vp);
20537013b092Sespie 		if (error)
20547013b092Sespie 			goto fail1;
20557013b092Sespie 		KASSERT(a_vp->v_mount == mount);
20567013b092Sespie 		/* Refuse to rename (over) a mount point.  */
20577013b092Sespie 		if ((a_vp->v_type == VDIR) && (a_vp->v_mountedhere != NULL)) {
20587013b092Sespie 			error = EBUSY;
20597013b092Sespie 			goto fail2;
20607013b092Sespie 		}
20617013b092Sespie 	} else if (!a_missing_ok) {
20627013b092Sespie 		error = ENOENT;
20637013b092Sespie 		goto fail1;
20647013b092Sespie 	} else {
20657013b092Sespie 		a_vp = NULL;
20667013b092Sespie 	}
20677013b092Sespie 	KASSERT(a_vp != a_dvp);
20687013b092Sespie 	KASSERT(a_vp != b_dvp);
20697013b092Sespie 
20707013b092Sespie 	error = tmpfs_rename_lock_directory(b_dvp, b_dnode);
20717013b092Sespie 	if (error)
20727013b092Sespie 		goto fail2;
20737013b092Sespie 
20747013b092Sespie 	/* Did we lose a race with mount?  */
20757013b092Sespie 	if (b_dvp->v_mountedhere != NULL) {
20767013b092Sespie 		error = EBUSY;
20777013b092Sespie 		goto fail3;
20787013b092Sespie 	}
20797013b092Sespie 
20807013b092Sespie 	/* Make sure the caller may read the directory.  */
20817013b092Sespie 	error = VOP_ACCESS(b_dvp, VEXEC, cred, curproc);
20827013b092Sespie 	if (error)
20837013b092Sespie 		goto fail3;
20847013b092Sespie 
20857013b092Sespie 	b_dirent = tmpfs_dir_lookup(b_dnode, b_cnp);
20867013b092Sespie 	if (b_dirent != NULL) {
20877013b092Sespie 		KASSERT(b_dirent->td_node != NULL);
20887013b092Sespie 		/* We ruled out `.' earlier.  */
20897013b092Sespie 		KASSERT(b_dirent->td_node != b_dnode);
20907013b092Sespie 		/* We ruled out `..' earlier.  */
20917013b092Sespie 		KASSERT(b_dirent->td_node !=
20927013b092Sespie 		    b_dnode->tn_spec.tn_dir.tn_parent);
20937013b092Sespie 		/* b is not an ancestor of a.  */
20947013b092Sespie 		KASSERT(b_dirent->td_node != a_dnode);
20957013b092Sespie 		/* But the source and target nodes might be the same.  */
20967013b092Sespie 		if ((a_dirent == NULL) ||
20977013b092Sespie 		    (a_dirent->td_node != b_dirent->td_node)) {
20987013b092Sespie 			rw_enter_write(&b_dirent->td_node->tn_nlock);
20997013b092Sespie 			error = tmpfs_vnode_get(mount, b_dirent->td_node,
21007013b092Sespie 			    &b_vp);
21017013b092Sespie 			if (error)
21027013b092Sespie 				goto fail3;
21037013b092Sespie 			KASSERT(b_vp->v_mount == mount);
21047013b092Sespie 			KASSERT(a_vp != b_vp);
21057013b092Sespie 			/* Refuse to rename (over) a mount point.  */
21067013b092Sespie 			if ((b_vp->v_type == VDIR) &&
21077013b092Sespie 			    (b_vp->v_mountedhere != NULL)) {
21087013b092Sespie 				error = EBUSY;
21097013b092Sespie 				goto fail4;
21107013b092Sespie 			}
21117013b092Sespie 		} else {
21127013b092Sespie 			b_vp = a_vp;
21137013b092Sespie 			vref(b_vp);
21147013b092Sespie 		}
21157013b092Sespie 	} else if (!b_missing_ok) {
21167013b092Sespie 		error = ENOENT;
21177013b092Sespie 		goto fail3;
21187013b092Sespie 	} else {
21197013b092Sespie 		b_vp = NULL;
21207013b092Sespie 	}
21217013b092Sespie 	KASSERT(b_vp != a_dvp);
21227013b092Sespie 	KASSERT(b_vp != b_dvp);
21237013b092Sespie 
21247013b092Sespie 	KASSERT(VOP_ISLOCKED(a_dvp) == LK_EXCLUSIVE);
21257013b092Sespie 	KASSERT(VOP_ISLOCKED(b_dvp) == LK_EXCLUSIVE);
21267013b092Sespie 	KASSERT(a_missing_ok || (a_dirent != NULL));
21277013b092Sespie 	KASSERT(a_missing_ok || (a_dirent->td_node != NULL));
21287013b092Sespie 	KASSERT(b_missing_ok || (b_dirent != NULL));
21297013b092Sespie 	KASSERT(b_missing_ok || (b_dirent->td_node != NULL));
21307013b092Sespie 	KASSERT((a_dirent == NULL) || (a_dirent->td_node != NULL));
21317013b092Sespie 	KASSERT((a_dirent == NULL) || (a_dirent->td_node->tn_vnode == a_vp));
21327013b092Sespie 	KASSERT((b_dirent == NULL) || (b_dirent->td_node != NULL));
21337013b092Sespie 	KASSERT((b_dirent == NULL) || (b_dirent->td_node->tn_vnode == b_vp));
21347013b092Sespie 	KASSERT((a_vp == NULL) || (VOP_ISLOCKED(a_vp) == LK_EXCLUSIVE));
21357013b092Sespie 	KASSERT((b_vp == NULL) || (VOP_ISLOCKED(b_vp) == LK_EXCLUSIVE));
21367013b092Sespie 
21377013b092Sespie 	*a_dirent_ret = a_dirent;
21387013b092Sespie 	*b_dirent_ret = b_dirent;
21397013b092Sespie 	*a_vp_ret = a_vp;
21407013b092Sespie 	*b_vp_ret = b_vp;
21417013b092Sespie 	return 0;
21427013b092Sespie 
21437013b092Sespie fail4:	if (b_vp != NULL) {
21447013b092Sespie 		KASSERT(VOP_ISLOCKED(b_vp) == LK_EXCLUSIVE);
21457013b092Sespie 		if (b_vp != a_vp)
21467013b092Sespie 			vput(b_vp);
21477013b092Sespie 		else
21487013b092Sespie 			vrele(a_vp);
21497013b092Sespie 	}
21507013b092Sespie 
21517013b092Sespie fail3:	KASSERT(VOP_ISLOCKED(b_dvp) == LK_EXCLUSIVE);
215236bb23f1Svisa 	VOP_UNLOCK(b_dvp);
21537013b092Sespie 
21547013b092Sespie fail2:	if (a_vp != NULL) {
21557013b092Sespie 		KASSERT(VOP_ISLOCKED(a_vp) == LK_EXCLUSIVE);
21567013b092Sespie 		vput(a_vp);
21577013b092Sespie 	}
21587013b092Sespie 
21597013b092Sespie fail1:	KASSERT(VOP_ISLOCKED(a_dvp) == LK_EXCLUSIVE);
216036bb23f1Svisa 	VOP_UNLOCK(a_dvp);
21617013b092Sespie 
21627013b092Sespie fail0:	/* KASSERT(VOP_ISLOCKED(a_dvp) != LK_EXCLUSIVE); */
21637013b092Sespie 	/* KASSERT(VOP_ISLOCKED(b_dvp) != LK_EXCLUSIVE); */
21647013b092Sespie 	/* KASSERT((a_vp == NULL) || (VOP_ISLOCKED(a_vp) != LK_EXCLUSIVE)); */
21657013b092Sespie 	/* KASSERT((b_vp == NULL) || (VOP_ISLOCKED(b_vp) != LK_EXCLUSIVE)); */
21667013b092Sespie 	return error;
21677013b092Sespie }
21687013b092Sespie 
21697013b092Sespie /*
21707013b092Sespie  * Shuffle the directory entries to move fvp from the directory fdvp
21717013b092Sespie  * into the directory tdvp.  fde is fvp's directory entry in fdvp.  If
21727013b092Sespie  * we are overwriting a target node, it is tvp, and tde is its
21737013b092Sespie  * directory entry in tdvp.
21747013b092Sespie  *
21757013b092Sespie  * fdvp, fvp, tdvp, and tvp must all be locked and referenced.
21767013b092Sespie  */
21777013b092Sespie void
21787013b092Sespie tmpfs_rename_attachdetach(struct tmpfs_mount *tmpfs,
21797013b092Sespie     struct vnode *fdvp, struct tmpfs_dirent *fde, struct vnode *fvp,
21807013b092Sespie     struct vnode *tdvp, struct tmpfs_dirent *tde, struct vnode *tvp)
21817013b092Sespie {
21827013b092Sespie 
21837013b092Sespie 	KASSERT(tmpfs != NULL);
21847013b092Sespie 	KASSERT(fdvp != NULL);
21857013b092Sespie 	KASSERT(fde != NULL);
21867013b092Sespie 	KASSERT(fvp != NULL);
21877013b092Sespie 	KASSERT(tdvp != NULL);
21887013b092Sespie 	KASSERT(fde->td_node != NULL);
21897013b092Sespie 	KASSERT(fde->td_node->tn_vnode == fvp);
21907013b092Sespie 	KASSERT((tde == NULL) == (tvp == NULL));
21917013b092Sespie 	KASSERT((tde == NULL) || (tde->td_node != NULL));
21927013b092Sespie 	KASSERT((tde == NULL) || (tde->td_node->tn_vnode == tvp));
21937013b092Sespie 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
21947013b092Sespie 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
21957013b092Sespie 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
21967013b092Sespie 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
21977013b092Sespie 
21987013b092Sespie 	/*
21997013b092Sespie 	 * If we are moving from one directory to another, detach the
22007013b092Sespie 	 * source entry and reattach it to the target directory.
22017013b092Sespie 	 */
22027013b092Sespie 	if (fdvp != tdvp) {
22037013b092Sespie 		/* tmpfs_dir_detach clobbers fde->td_node, so save it.  */
22047013b092Sespie 		struct tmpfs_node *fnode = fde->td_node;
220557331246Sespie 		tmpfs_node_t *fdnode = VP_TO_TMPFS_DIR(fdvp);
220657331246Sespie 		tmpfs_node_t *tdnode = VP_TO_TMPFS_DIR(tdvp);
220757331246Sespie 		tmpfs_dir_detach(fdnode, fde);
220857331246Sespie 		tmpfs_dir_attach(tdnode, fde, fnode);
22097013b092Sespie 	} else if (tvp == NULL) {
22107013b092Sespie 		/*
22117013b092Sespie 		 * We are changing the directory.  tmpfs_dir_attach and
22127013b092Sespie 		 * tmpfs_dir_detach note the events for us, but for
22137013b092Sespie 		 * this case we don't call them, so we must note the
22147013b092Sespie 		 * event explicitly.
22157013b092Sespie 		 */
22167013b092Sespie 		VN_KNOTE(fdvp, NOTE_WRITE);
22177013b092Sespie 	}
22187013b092Sespie 
22197013b092Sespie 	/*
22207013b092Sespie 	 * If we are replacing an existing target entry, delete it.
22217013b092Sespie 	 */
22227013b092Sespie 	if (tde != NULL) {
222357331246Sespie 		tmpfs_node_t *tdnode = VP_TO_TMPFS_DIR(tdvp);
22247013b092Sespie 		KASSERT(tvp != NULL);
22257013b092Sespie 		KASSERT(tde->td_node != NULL);
22267013b092Sespie 		KASSERT((fvp->v_type == VDIR) == (tvp->v_type == VDIR));
22277013b092Sespie 		if (tde->td_node->tn_type == VDIR) {
22287013b092Sespie 			KASSERT(tde->td_node->tn_size == 0);
22297013b092Sespie 			KASSERT(tde->td_node->tn_links == 2);
22307013b092Sespie 			/* Decrement the extra link count for `.' so
22317013b092Sespie 			 * the vnode will be recycled when released.  */
22327013b092Sespie 			tde->td_node->tn_links--;
22337013b092Sespie 		}
223457331246Sespie 		tmpfs_dir_detach(tdnode, tde);
22357013b092Sespie 		tmpfs_free_dirent(tmpfs, tde);
22367013b092Sespie 	}
22377013b092Sespie }
22387013b092Sespie 
22397013b092Sespie /*
22407013b092Sespie  * Remove the entry de for the non-directory vp from the directory dvp.
22417013b092Sespie  *
22427013b092Sespie  * Everything must be locked and referenced.
22437013b092Sespie  */
22447013b092Sespie int
22457013b092Sespie tmpfs_do_remove(struct tmpfs_mount *tmpfs, struct vnode *dvp,
22467013b092Sespie     struct tmpfs_node *dnode, struct tmpfs_dirent *de, struct vnode *vp,
22477013b092Sespie     struct ucred *cred)
22487013b092Sespie {
22497013b092Sespie 	int error;
22507013b092Sespie 
22517013b092Sespie 	KASSERT(tmpfs != NULL);
22527013b092Sespie 	KASSERT(dvp != NULL);
22537013b092Sespie 	KASSERT(dnode != NULL);
22547013b092Sespie 	KASSERT(de != NULL);
22557013b092Sespie 	KASSERT(vp != NULL);
22567013b092Sespie 	KASSERT(dnode->tn_vnode == dvp);
22577013b092Sespie 	KASSERT(de->td_node != NULL);
22587013b092Sespie 	KASSERT(de->td_node->tn_vnode == vp);
22597013b092Sespie 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
22607013b092Sespie 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
22617013b092Sespie 
22627013b092Sespie 	error = tmpfs_remove_check_possible(dnode, de->td_node);
22637013b092Sespie 	if (error)
22647013b092Sespie 		return error;
22657013b092Sespie 
22667013b092Sespie 	error = tmpfs_remove_check_permitted(cred, dnode, de->td_node);
22677013b092Sespie 	if (error)
22687013b092Sespie 		return error;
22697013b092Sespie 
22707013b092Sespie 	/*
22717013b092Sespie 	 * If not root and directory is sticky, check for permission on
22727013b092Sespie 	 * directory or on file. This implements append-only directories.
22737013b092Sespie 	 */
22747013b092Sespie 	if ((dnode->tn_mode & S_ISTXT) != 0)
22757013b092Sespie 		if (cred->cr_uid != 0 && cred->cr_uid != dnode->tn_uid &&
22767013b092Sespie 		    cred->cr_uid != de->td_node->tn_uid)
22777013b092Sespie 			return EPERM;
22787013b092Sespie 
227957331246Sespie 	tmpfs_dir_detach(dnode, de);
22807013b092Sespie 	tmpfs_free_dirent(tmpfs, de);
22817013b092Sespie 
22827013b092Sespie 	return 0;
22837013b092Sespie }
22847013b092Sespie 
22857013b092Sespie /*
22867013b092Sespie  * Check whether a rename is possible independent of credentials.
22877013b092Sespie  *
22887013b092Sespie  * Everything must be locked and referenced.
22897013b092Sespie  */
22907013b092Sespie int
22917013b092Sespie tmpfs_rename_check_possible(
22927013b092Sespie     struct tmpfs_node *fdnode, struct tmpfs_node *fnode,
22937013b092Sespie     struct tmpfs_node *tdnode, struct tmpfs_node *tnode)
22947013b092Sespie {
22957013b092Sespie 
22967013b092Sespie 	KASSERT(fdnode != NULL);
22977013b092Sespie 	KASSERT(fnode != NULL);
22987013b092Sespie 	KASSERT(tdnode != NULL);
22997013b092Sespie 	KASSERT(fdnode != fnode);
23007013b092Sespie 	KASSERT(tdnode != tnode);
23017013b092Sespie 	KASSERT(fnode != tnode);
23027013b092Sespie 	KASSERT(fdnode->tn_vnode != NULL);
23037013b092Sespie 	KASSERT(fnode->tn_vnode != NULL);
23047013b092Sespie 	KASSERT(tdnode->tn_vnode != NULL);
23057013b092Sespie 	KASSERT((tnode == NULL) || (tnode->tn_vnode != NULL));
23067013b092Sespie 	KASSERT(VOP_ISLOCKED(fdnode->tn_vnode) == LK_EXCLUSIVE);
23077013b092Sespie 	KASSERT(VOP_ISLOCKED(fnode->tn_vnode) == LK_EXCLUSIVE);
23087013b092Sespie 	KASSERT(VOP_ISLOCKED(tdnode->tn_vnode) == LK_EXCLUSIVE);
23097013b092Sespie 	KASSERT((tnode == NULL) ||
23107013b092Sespie 	    (VOP_ISLOCKED(tnode->tn_vnode) == LK_EXCLUSIVE));
23117013b092Sespie 
23127013b092Sespie 	/*
23137013b092Sespie 	 * If fdnode is immutable, we can't write to it.  If fdnode is
23147013b092Sespie 	 * append-only, the only change we can make is to add entries
23157013b092Sespie 	 * to it.  If fnode is immutable, we can't change the links to
23167013b092Sespie 	 * it.  If fnode is append-only...well, this is what UFS does.
23177013b092Sespie 	 */
23187013b092Sespie 	if ((fdnode->tn_flags | fnode->tn_flags) & (IMMUTABLE | APPEND))
23197013b092Sespie 		return EPERM;
23207013b092Sespie 
23217013b092Sespie 	/*
23227013b092Sespie 	 * If tdnode is immutable, we can't write to it.  If tdnode is
23237013b092Sespie 	 * append-only, we can add entries, but we can't change
23247013b092Sespie 	 * existing entries.
23257013b092Sespie 	 */
23267013b092Sespie 	if (tdnode->tn_flags & (IMMUTABLE | (tnode? APPEND : 0)))
23277013b092Sespie 		return EPERM;
23287013b092Sespie 
23297013b092Sespie 	/*
23307013b092Sespie 	 * If tnode is immutable, we can't replace links to it.  If
23317013b092Sespie 	 * tnode is append-only...well, this is what UFS does.
23327013b092Sespie 	 */
23337013b092Sespie 	if (tnode != NULL) {
23347013b092Sespie 		KASSERT(tnode != NULL);
23357013b092Sespie 		if ((tnode->tn_flags & (IMMUTABLE | APPEND)) != 0)
23367013b092Sespie 			return EPERM;
23377013b092Sespie 	}
23387013b092Sespie 
23397013b092Sespie 	return 0;
23407013b092Sespie }
23417013b092Sespie 
23427013b092Sespie /*
23437013b092Sespie  * Check whether a rename is permitted given our credentials.
23447013b092Sespie  *
23457013b092Sespie  * Everything must be locked and referenced.
23467013b092Sespie  */
23477013b092Sespie int
23487013b092Sespie tmpfs_rename_check_permitted(struct ucred *cred,
23497013b092Sespie     struct tmpfs_node *fdnode, struct tmpfs_node *fnode,
23507013b092Sespie     struct tmpfs_node *tdnode, struct tmpfs_node *tnode)
23517013b092Sespie {
23527013b092Sespie 	int error;
23537013b092Sespie 
23547013b092Sespie 	KASSERT(fdnode != NULL);
23557013b092Sespie 	KASSERT(fnode != NULL);
23567013b092Sespie 	KASSERT(tdnode != NULL);
23577013b092Sespie 	KASSERT(fdnode != fnode);
23587013b092Sespie 	KASSERT(tdnode != tnode);
23597013b092Sespie 	KASSERT(fnode != tnode);
23607013b092Sespie 	KASSERT(fdnode->tn_vnode != NULL);
23617013b092Sespie 	KASSERT(fnode->tn_vnode != NULL);
23627013b092Sespie 	KASSERT(tdnode->tn_vnode != NULL);
23637013b092Sespie 	KASSERT((tnode == NULL) || (tnode->tn_vnode != NULL));
23647013b092Sespie 	KASSERT(VOP_ISLOCKED(fdnode->tn_vnode) == LK_EXCLUSIVE);
23657013b092Sespie 	KASSERT(VOP_ISLOCKED(fnode->tn_vnode) == LK_EXCLUSIVE);
23667013b092Sespie 	KASSERT(VOP_ISLOCKED(tdnode->tn_vnode) == LK_EXCLUSIVE);
23677013b092Sespie 	KASSERT((tnode == NULL) ||
23687013b092Sespie 	    (VOP_ISLOCKED(tnode->tn_vnode) == LK_EXCLUSIVE));
23697013b092Sespie 
23707013b092Sespie 	/*
23717013b092Sespie 	 * We need to remove or change an entry in the source directory.
23727013b092Sespie 	 */
23737013b092Sespie 	error = VOP_ACCESS(fdnode->tn_vnode, VWRITE, cred, curproc);
23747013b092Sespie 	if (error)
23757013b092Sespie 		return error;
23767013b092Sespie 
23777013b092Sespie 	/*
23787013b092Sespie 	 * If we are changing directories, then we need to write to the
23797013b092Sespie 	 * target directory to add or change an entry.  Also, if fnode
23807013b092Sespie 	 * is a directory, we need to write to it to change its `..'
23817013b092Sespie 	 * entry.
23827013b092Sespie 	 */
23837013b092Sespie 	if (fdnode != tdnode) {
23847013b092Sespie 		error = VOP_ACCESS(tdnode->tn_vnode, VWRITE, cred, curproc);
23857013b092Sespie 		if (error)
23867013b092Sespie 			return error;
23877013b092Sespie 		if (fnode->tn_type == VDIR) {
23887013b092Sespie 			error = VOP_ACCESS(fnode->tn_vnode, VWRITE, cred,
23897013b092Sespie 			    curproc);
23907013b092Sespie 			if (error)
23917013b092Sespie 				return error;
23927013b092Sespie 		}
23937013b092Sespie 	}
23947013b092Sespie 
23957013b092Sespie 	error = tmpfs_check_sticky(cred, fdnode, fnode);
23967013b092Sespie 	if (error)
23977013b092Sespie 		return error;
23987013b092Sespie 
239957331246Sespie 	if (TMPFS_DIRSEQ_FULL(tdnode))
240057331246Sespie 		return (ENOSPC);
240157331246Sespie 
24027013b092Sespie 	error = tmpfs_check_sticky(cred, tdnode, tnode);
24037013b092Sespie 	if (error)
24047013b092Sespie 		return error;
24057013b092Sespie 
24067013b092Sespie 	return 0;
24077013b092Sespie }
24087013b092Sespie 
24097013b092Sespie /*
24107013b092Sespie  * Check whether removing node's entry in dnode is possible independent
24117013b092Sespie  * of credentials.
24127013b092Sespie  *
24137013b092Sespie  * Everything must be locked and referenced.
24147013b092Sespie  */
24157013b092Sespie int
24167013b092Sespie tmpfs_remove_check_possible(struct tmpfs_node *dnode, struct tmpfs_node *node)
24177013b092Sespie {
24187013b092Sespie 
24197013b092Sespie 	KASSERT(dnode != NULL);
24207013b092Sespie 	KASSERT(dnode->tn_vnode != NULL);
24217013b092Sespie 	KASSERT(node != NULL);
24227013b092Sespie 	KASSERT(dnode != node);
24237013b092Sespie 	KASSERT(VOP_ISLOCKED(dnode->tn_vnode) == LK_EXCLUSIVE);
24247013b092Sespie 	KASSERT(VOP_ISLOCKED(node->tn_vnode) == LK_EXCLUSIVE);
24257013b092Sespie 
24267013b092Sespie 	/*
24277013b092Sespie 	 * We want to delete the entry.  If dnode is immutable, we
24287013b092Sespie 	 * can't write to it to delete the entry.  If dnode is
24297013b092Sespie 	 * append-only, the only change we can make is to add entries,
24307013b092Sespie 	 * so we can't delete entries.  If node is immutable, we can't
24317013b092Sespie 	 * change the links to it, so we can't delete the entry.  If
24327013b092Sespie 	 * node is append-only...well, this is what UFS does.
24337013b092Sespie 	 */
24347013b092Sespie 	if ((dnode->tn_flags | node->tn_flags) & (IMMUTABLE | APPEND))
24357013b092Sespie 		return EPERM;
24367013b092Sespie 
24377013b092Sespie 	return 0;
24387013b092Sespie }
24397013b092Sespie 
24407013b092Sespie /*
24417013b092Sespie  * Check whether removing node's entry in dnode is permitted given our
24427013b092Sespie  * credentials.
24437013b092Sespie  *
24447013b092Sespie  * Everything must be locked and referenced.
24457013b092Sespie  */
24467013b092Sespie int
24477013b092Sespie tmpfs_remove_check_permitted(struct ucred *cred,
24487013b092Sespie     struct tmpfs_node *dnode, struct tmpfs_node *node)
24497013b092Sespie {
24507013b092Sespie 	int error;
24517013b092Sespie 
24527013b092Sespie 	KASSERT(dnode != NULL);
24537013b092Sespie 	KASSERT(dnode->tn_vnode != NULL);
24547013b092Sespie 	KASSERT(node != NULL);
24557013b092Sespie 	KASSERT(dnode != node);
24567013b092Sespie 	KASSERT(VOP_ISLOCKED(dnode->tn_vnode) == LK_EXCLUSIVE);
24577013b092Sespie 	KASSERT(VOP_ISLOCKED(node->tn_vnode) == LK_EXCLUSIVE);
24587013b092Sespie 
24597013b092Sespie 	/*
24607013b092Sespie 	 * Check whether we are permitted to write to the source
24617013b092Sespie 	 * directory in order to delete an entry from it.
24627013b092Sespie 	 */
24637013b092Sespie 	error = VOP_ACCESS(dnode->tn_vnode, VWRITE, cred, curproc);
24647013b092Sespie 	if (error)
24657013b092Sespie 		return error;
24667013b092Sespie 
24677013b092Sespie 	error = tmpfs_check_sticky(cred, dnode, node);
24687013b092Sespie 	if (error)
24697013b092Sespie 		return error;
24707013b092Sespie 
24717013b092Sespie 	return 0;
24727013b092Sespie }
24737013b092Sespie 
24747013b092Sespie /*
24757013b092Sespie  * Check whether we may change an entry in a sticky directory.  If the
24767013b092Sespie  * directory is sticky, the user must own either the directory or, if
24777013b092Sespie  * it exists, the node, in order to change the entry.
24787013b092Sespie  *
24797013b092Sespie  * Everything must be locked and referenced.
24807013b092Sespie  */
24817013b092Sespie int
24827013b092Sespie tmpfs_check_sticky(struct ucred *cred,
24837013b092Sespie     struct tmpfs_node *dnode, struct tmpfs_node *node)
24847013b092Sespie {
24857013b092Sespie 
24867013b092Sespie 	KASSERT(dnode != NULL);
24877013b092Sespie 	KASSERT(dnode->tn_vnode != NULL);
24887013b092Sespie 	KASSERT(VOP_ISLOCKED(dnode->tn_vnode) == LK_EXCLUSIVE);
24897013b092Sespie 	KASSERT((node == NULL) || (node->tn_vnode != NULL));
24907013b092Sespie 	KASSERT((node == NULL) ||
24917013b092Sespie 	    (VOP_ISLOCKED(dnode->tn_vnode) == LK_EXCLUSIVE));
24927013b092Sespie 
24937013b092Sespie 	if (node == NULL)
24947013b092Sespie 		return 0;
24957013b092Sespie 
24967013b092Sespie 	if (dnode->tn_mode & S_ISTXT) {
24977013b092Sespie 		if (cred->cr_uid != 0 &&
24987013b092Sespie 		    cred->cr_uid != dnode->tn_uid &&
24997013b092Sespie 		    cred->cr_uid != node->tn_uid)
25007013b092Sespie 			return EPERM;
25017013b092Sespie 	}
25027013b092Sespie 
25037013b092Sespie 	return 0;
25047013b092Sespie }
25057013b092Sespie 
25067013b092Sespie void
25077013b092Sespie tmpfs_rename_cache_purge(struct vnode *fdvp, struct vnode *fvp,
25087013b092Sespie     struct vnode *tdvp, struct vnode *tvp)
25097013b092Sespie {
25107013b092Sespie 
25117013b092Sespie 	KASSERT(fdvp != NULL);
25127013b092Sespie 	KASSERT(fvp != NULL);
25137013b092Sespie 	KASSERT(tdvp != NULL);
25147013b092Sespie 	KASSERT(fdvp != fvp);
25157013b092Sespie 	KASSERT(fdvp != tvp);
25167013b092Sespie 	KASSERT(tdvp != fvp);
25177013b092Sespie 	KASSERT(tdvp != tvp);
25187013b092Sespie 	KASSERT(fvp != tvp);
25197013b092Sespie 	KASSERT(fdvp->v_type == VDIR);
25207013b092Sespie 	KASSERT(tdvp->v_type == VDIR);
25217013b092Sespie 
25227013b092Sespie 	/*
25237013b092Sespie 	 * XXX What actually needs to be purged?
25247013b092Sespie 	 */
25257013b092Sespie 
25267013b092Sespie 	cache_purge(fdvp);
25277013b092Sespie 
25287013b092Sespie 	if (fvp->v_type == VDIR)
25297013b092Sespie 		cache_purge(fvp);
25307013b092Sespie 
25317013b092Sespie 	if (tdvp != fdvp)
25327013b092Sespie 		cache_purge(tdvp);
25337013b092Sespie 
25347013b092Sespie 	if ((tvp != NULL) && (tvp->v_type == VDIR))
25357013b092Sespie 		cache_purge(tvp);
25367013b092Sespie }
253746905cb9Sespie 
253846905cb9Sespie void
253946905cb9Sespie tmpfs_rename_abort(void *v)
254046905cb9Sespie {
254146905cb9Sespie 	struct vop_rename_args *ap = v;
254246905cb9Sespie 	struct vnode *fdvp = ap->a_fdvp;
254346905cb9Sespie 	struct vnode *fvp = ap->a_fvp;
254446905cb9Sespie 	struct componentname *fcnp = ap->a_fcnp;
254546905cb9Sespie 	struct vnode *tdvp = ap->a_tdvp;
254646905cb9Sespie 	struct vnode *tvp = ap->a_tvp;
254746905cb9Sespie 	struct componentname *tcnp = ap->a_tcnp;
254846905cb9Sespie 
254946905cb9Sespie 	VOP_ABORTOP(tdvp, tcnp);
255046905cb9Sespie 	if (tdvp == tvp)
255146905cb9Sespie 		vrele(tdvp);
255246905cb9Sespie 	else
255346905cb9Sespie 		vput(tdvp);
255446905cb9Sespie 	if (tvp != NULL)
255546905cb9Sespie 		vput(tvp);
255646905cb9Sespie 	VOP_ABORTOP(fdvp, fcnp);
255746905cb9Sespie 	vrele(fdvp);
255846905cb9Sespie 	vrele(fvp);
255946905cb9Sespie }
2560f9ac7274Stedu 
2561f9ac7274Stedu void filt_tmpfsdetach(struct knote *kn);
2562f9ac7274Stedu int filt_tmpfsread(struct knote *kn, long hint);
2563f9ac7274Stedu int filt_tmpfswrite(struct knote *kn, long hint);
2564f9ac7274Stedu int filt_tmpfsvnode(struct knote *kn, long hint);
2565f9ac7274Stedu 
256694321eb4Svisa const struct filterops tmpfsread_filtops = {
2567b8213689Svisa 	.f_flags	= FILTEROP_ISFD,
256894321eb4Svisa 	.f_attach	= NULL,
256994321eb4Svisa 	.f_detach	= filt_tmpfsdetach,
257094321eb4Svisa 	.f_event	= filt_tmpfsread,
257194321eb4Svisa };
257294321eb4Svisa 
257394321eb4Svisa const struct filterops tmpfswrite_filtops = {
2574b8213689Svisa 	.f_flags	= FILTEROP_ISFD,
257594321eb4Svisa 	.f_attach	= NULL,
257694321eb4Svisa 	.f_detach	= filt_tmpfsdetach,
257794321eb4Svisa 	.f_event	= filt_tmpfswrite,
257894321eb4Svisa };
257994321eb4Svisa 
258094321eb4Svisa const struct filterops tmpfsvnode_filtops = {
2581b8213689Svisa 	.f_flags	= FILTEROP_ISFD,
258294321eb4Svisa 	.f_attach	= NULL,
258394321eb4Svisa 	.f_detach	= filt_tmpfsdetach,
258494321eb4Svisa 	.f_event	= filt_tmpfsvnode,
258594321eb4Svisa };
2586f9ac7274Stedu 
2587f9ac7274Stedu int
2588f9ac7274Stedu tmpfs_kqfilter(void *v)
2589f9ac7274Stedu {
2590f9ac7274Stedu 	struct vop_kqfilter_args *ap = v;
2591f9ac7274Stedu 	struct vnode *vp = ap->a_vp;
2592f9ac7274Stedu 	struct knote *kn = ap->a_kn;
2593f9ac7274Stedu 
2594f9ac7274Stedu 	switch (kn->kn_filter) {
2595f9ac7274Stedu 	case EVFILT_READ:
2596f9ac7274Stedu 		kn->kn_fop = &tmpfsread_filtops;
2597f9ac7274Stedu 		break;
2598f9ac7274Stedu 	case EVFILT_WRITE:
2599f9ac7274Stedu 		kn->kn_fop = &tmpfswrite_filtops;
2600f9ac7274Stedu 		break;
2601f9ac7274Stedu 	case EVFILT_VNODE:
2602f9ac7274Stedu 		kn->kn_fop = &tmpfsvnode_filtops;
2603f9ac7274Stedu 		break;
2604f9ac7274Stedu 	default:
2605f9ac7274Stedu 		return (EINVAL);
2606f9ac7274Stedu 	}
2607f9ac7274Stedu 
2608f9ac7274Stedu 	kn->kn_hook = (caddr_t)vp;
2609f9ac7274Stedu 
2610cc53a24cSmvs 	klist_insert_locked(&vp->v_klist, kn);
2611f9ac7274Stedu 
2612f9ac7274Stedu 	return (0);
2613f9ac7274Stedu }
2614f9ac7274Stedu 
2615f9ac7274Stedu void
2616f9ac7274Stedu filt_tmpfsdetach(struct knote *kn)
2617f9ac7274Stedu {
2618f9ac7274Stedu 	struct vnode *vp = (struct vnode *)kn->kn_hook;
2619f9ac7274Stedu 
2620cc53a24cSmvs 	klist_remove_locked(&vp->v_klist, kn);
2621f9ac7274Stedu }
2622f9ac7274Stedu 
2623f9ac7274Stedu int
2624f9ac7274Stedu filt_tmpfsread(struct knote *kn, long hint)
2625f9ac7274Stedu {
2626f9ac7274Stedu 	struct vnode *vp = (struct vnode *)kn->kn_hook;
2627f9ac7274Stedu 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
2628f9ac7274Stedu 
2629f9ac7274Stedu 	/*
2630f9ac7274Stedu 	 * filesystem is gone, so set the EOF flag and schedule
2631f9ac7274Stedu 	 * the knote for deletion.
2632f9ac7274Stedu 	 */
2633f9ac7274Stedu 	if (hint == NOTE_REVOKE) {
2634f9ac7274Stedu 		kn->kn_flags |= (EV_EOF | EV_ONESHOT);
2635f9ac7274Stedu 		return (1);
2636f9ac7274Stedu 	}
2637f9ac7274Stedu 
2638836f297bSanton 	kn->kn_data = node->tn_size - foffset(kn->kn_fp);
2639f9ac7274Stedu 	if (kn->kn_data == 0 && kn->kn_sfflags & NOTE_EOF) {
2640f9ac7274Stedu 		kn->kn_fflags |= NOTE_EOF;
2641f9ac7274Stedu 		return (1);
2642f9ac7274Stedu 	}
2643f9ac7274Stedu 
26446ecc0d7fSvisa 	if (kn->kn_flags & (__EV_POLL | __EV_SELECT))
26456e29a944Smpi 		return (1);
26466e29a944Smpi 
2647f9ac7274Stedu 	return (kn->kn_data != 0);
2648f9ac7274Stedu }
2649f9ac7274Stedu 
2650f9ac7274Stedu int
2651f9ac7274Stedu filt_tmpfswrite(struct knote *kn, long hint)
2652f9ac7274Stedu {
2653f9ac7274Stedu 	/*
2654f9ac7274Stedu 	 * filesystem is gone, so set the EOF flag and schedule
2655f9ac7274Stedu 	 * the knote for deletion.
2656f9ac7274Stedu 	 */
2657f9ac7274Stedu 	if (hint == NOTE_REVOKE) {
2658f9ac7274Stedu 		kn->kn_flags |= (EV_EOF | EV_ONESHOT);
2659f9ac7274Stedu 		return (1);
2660f9ac7274Stedu 	}
2661f9ac7274Stedu 
2662f9ac7274Stedu 	kn->kn_data = 0;
2663f9ac7274Stedu 	return (1);
2664f9ac7274Stedu }
2665f9ac7274Stedu 
2666f9ac7274Stedu int
2667f9ac7274Stedu filt_tmpfsvnode(struct knote *kn, long hint)
2668f9ac7274Stedu {
2669f9ac7274Stedu 	if (kn->kn_sfflags & hint)
2670f9ac7274Stedu 		kn->kn_fflags |= hint;
2671f9ac7274Stedu 	if (hint == NOTE_REVOKE) {
2672f9ac7274Stedu 		kn->kn_flags |= EV_EOF;
2673f9ac7274Stedu 		return (1);
2674f9ac7274Stedu 	}
2675f9ac7274Stedu 	return (kn->kn_fflags != 0);
2676f9ac7274Stedu }
2677