xref: /dflybsd-src/sys/vfs/tmpfs/tmpfs_vnops.c (revision 2b3f93ea6d1f70880f3e87f3c2cbe0dc0bfc9332)
17a2de9a4SMatthew Dillon /*-
27a2de9a4SMatthew Dillon  * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
37a2de9a4SMatthew Dillon  * All rights reserved.
47a2de9a4SMatthew Dillon  *
57a2de9a4SMatthew Dillon  * This code is derived from software contributed to The NetBSD Foundation
67a2de9a4SMatthew Dillon  * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
77a2de9a4SMatthew Dillon  * 2005 program.
87a2de9a4SMatthew Dillon  *
97a2de9a4SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
107a2de9a4SMatthew Dillon  * modification, are permitted provided that the following conditions
117a2de9a4SMatthew Dillon  * are met:
127a2de9a4SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
137a2de9a4SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
147a2de9a4SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
157a2de9a4SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in the
167a2de9a4SMatthew Dillon  *    documentation and/or other materials provided with the distribution.
177a2de9a4SMatthew Dillon  *
187a2de9a4SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
197a2de9a4SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
207a2de9a4SMatthew Dillon  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
217a2de9a4SMatthew Dillon  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
227a2de9a4SMatthew Dillon  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
237a2de9a4SMatthew Dillon  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
247a2de9a4SMatthew Dillon  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
257a2de9a4SMatthew Dillon  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
267a2de9a4SMatthew Dillon  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
277a2de9a4SMatthew Dillon  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
287a2de9a4SMatthew Dillon  * POSSIBILITY OF SUCH DAMAGE.
2980ae59d7SMatthew Dillon  *
3080ae59d7SMatthew Dillon  * $NetBSD: tmpfs_vnops.c,v 1.39 2007/07/23 15:41:01 jmmv Exp $
317a2de9a4SMatthew Dillon  */
327a2de9a4SMatthew Dillon 
337a2de9a4SMatthew Dillon /*
347a2de9a4SMatthew Dillon  * tmpfs vnode interface.
357a2de9a4SMatthew Dillon  */
367a2de9a4SMatthew Dillon 
377a2de9a4SMatthew Dillon #include <sys/kernel.h>
387a2de9a4SMatthew Dillon #include <sys/kern_syscall.h>
397a2de9a4SMatthew Dillon #include <sys/param.h>
4013dd34d8Szrj #include <sys/uio.h>
417a2de9a4SMatthew Dillon #include <sys/fcntl.h>
427a2de9a4SMatthew Dillon #include <sys/lockf.h>
43*2b3f93eaSMatthew Dillon #include <sys/caps.h>
447a2de9a4SMatthew Dillon #include <sys/proc.h>
457a2de9a4SMatthew Dillon #include <sys/resourcevar.h>
467a2de9a4SMatthew Dillon #include <sys/sched.h>
477a2de9a4SMatthew Dillon #include <sys/stat.h>
487a2de9a4SMatthew Dillon #include <sys/systm.h>
49a1b829f2SMatthew Dillon #include <sys/sysctl.h>
507a2de9a4SMatthew Dillon #include <sys/unistd.h>
517a2de9a4SMatthew Dillon #include <sys/vfsops.h>
527a2de9a4SMatthew Dillon #include <sys/vnode.h>
5366fa44e7SVenkatesh Srinivas #include <sys/mountctl.h>
547a2de9a4SMatthew Dillon 
557a2de9a4SMatthew Dillon #include <vm/vm.h>
56cad75e56SSascha Wildner #include <vm/vm_extern.h>
577a2de9a4SMatthew Dillon #include <vm/vm_object.h>
587a2de9a4SMatthew Dillon #include <vm/vm_page.h>
5903697313SSascha Wildner #include <vm/vm_pageout.h>
607a2de9a4SMatthew Dillon #include <vm/vm_pager.h>
61b7545cb3SAlex Hornung #include <vm/swap_pager.h>
627a2de9a4SMatthew Dillon 
6354341a3bSMatthew Dillon #include <sys/buf2.h>
643c54bb74SMatthew Dillon #include <vm/vm_page2.h>
6554341a3bSMatthew Dillon 
667a2de9a4SMatthew Dillon #include <vfs/fifofs/fifo.h>
677a2de9a4SMatthew Dillon #include <vfs/tmpfs/tmpfs_vnops.h>
68aa1adbf0SMatthew Dillon #include "tmpfs.h"
697a2de9a4SMatthew Dillon 
708f9ba07bSMatthew Dillon static void tmpfs_strategy_done(struct bio *bio);
71cfd59c5aSMatthew Dillon static void tmpfs_move_pages(vm_object_t src, vm_object_t dst, int movflags);
72a1b829f2SMatthew Dillon 
73cfd59c5aSMatthew Dillon /*
74cfd59c5aSMatthew Dillon  * bufcache_mode:
75307bf766SMatthew Dillon  *	0	Normal page queue operation on flush.  Run through the buffer
76307bf766SMatthew Dillon  *		cache if free memory is under the minimum.
77108fbfcbSMatthew Dillon  *
78307bf766SMatthew Dillon  *	1	Try to keep in memory, but run through the buffer cache if
79307bf766SMatthew Dillon  *		the system is under memory pressure (though this might just
80307bf766SMatthew Dillon  *		require inactive cleaning).
81108fbfcbSMatthew Dillon  *
82307bf766SMatthew Dillon  *	2	Be a bit more aggressive when running writes through the
83307bf766SMatthew Dillon  *		buffer cache when the system is under memory pressure.
84307bf766SMatthew Dillon  *
85307bf766SMatthew Dillon  *	3	Always run tmpfs writes through the buffer cache, thus forcing
86307bf766SMatthew Dillon  *		them out to swap.
87cfd59c5aSMatthew Dillon  */
88cfd59c5aSMatthew Dillon __read_mostly static int tmpfs_cluster_rd_enable = 1;
89cfd59c5aSMatthew Dillon __read_mostly static int tmpfs_cluster_wr_enable = 1;
90307bf766SMatthew Dillon __read_mostly int tmpfs_bufcache_mode = 0;
91a1b829f2SMatthew Dillon SYSCTL_NODE(_vfs, OID_AUTO, tmpfs, CTLFLAG_RW, 0, "TMPFS filesystem");
92cfd59c5aSMatthew Dillon SYSCTL_INT(_vfs_tmpfs, OID_AUTO, cluster_rd_enable, CTLFLAG_RW,
93cfd59c5aSMatthew Dillon 		&tmpfs_cluster_rd_enable, 0, "");
94cfd59c5aSMatthew Dillon SYSCTL_INT(_vfs_tmpfs, OID_AUTO, cluster_wr_enable, CTLFLAG_RW,
95cfd59c5aSMatthew Dillon 		&tmpfs_cluster_wr_enable, 0, "");
96cfd59c5aSMatthew Dillon SYSCTL_INT(_vfs_tmpfs, OID_AUTO, bufcache_mode, CTLFLAG_RW,
97cfd59c5aSMatthew Dillon 		&tmpfs_bufcache_mode, 0, "");
98cfd59c5aSMatthew Dillon 
99cfd59c5aSMatthew Dillon #define TMPFS_MOVF_FROMBACKING	0x0001
100cfd59c5aSMatthew Dillon #define TMPFS_MOVF_DEACTIVATE	0x0002
101cfd59c5aSMatthew Dillon 
1028f9ba07bSMatthew Dillon 
10380ae59d7SMatthew Dillon static __inline
10480ae59d7SMatthew Dillon void
tmpfs_knote(struct vnode * vp,int flags)10580ae59d7SMatthew Dillon tmpfs_knote(struct vnode *vp, int flags)
10680ae59d7SMatthew Dillon {
10780ae59d7SMatthew Dillon 	if (flags)
10880ae59d7SMatthew Dillon 		KNOTE(&vp->v_pollinfo.vpi_kqinfo.ki_note, flags);
10980ae59d7SMatthew Dillon }
11080ae59d7SMatthew Dillon 
11180ae59d7SMatthew Dillon 
1127a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
1137a2de9a4SMatthew Dillon 
1147a2de9a4SMatthew Dillon static int
tmpfs_nresolve(struct vop_nresolve_args * ap)115ba25006aSSascha Wildner tmpfs_nresolve(struct vop_nresolve_args *ap)
1167a2de9a4SMatthew Dillon {
117ba25006aSSascha Wildner 	struct vnode *dvp = ap->a_dvp;
1187a2de9a4SMatthew Dillon 	struct vnode *vp = NULL;
119ba25006aSSascha Wildner 	struct namecache *ncp = ap->a_nch->ncp;
120d89ce96aSMatthew Dillon 	struct tmpfs_node *tnode;
1217a2de9a4SMatthew Dillon 	struct tmpfs_dirent *de;
1227a2de9a4SMatthew Dillon 	struct tmpfs_node *dnode;
123ff837cd5SMatthew Dillon 	int error;
1247a2de9a4SMatthew Dillon 
1257a2de9a4SMatthew Dillon 	dnode = VP_TO_TMPFS_DIR(dvp);
1267a2de9a4SMatthew Dillon 
127ff837cd5SMatthew Dillon 	TMPFS_NODE_LOCK_SH(dnode);
128d89a0e31SMatthew Dillon loop:
1297a2de9a4SMatthew Dillon 	de = tmpfs_dir_lookup(dnode, NULL, ncp);
1307a2de9a4SMatthew Dillon 	if (de == NULL) {
1317a2de9a4SMatthew Dillon 		error = ENOENT;
1327a2de9a4SMatthew Dillon 	} else {
133d89ce96aSMatthew Dillon 		/*
134d89a0e31SMatthew Dillon 		 * Allocate a vnode for the node we found.  Use
135d89a0e31SMatthew Dillon 		 * tmpfs_alloc_vp()'s deadlock handling mode.
136d89ce96aSMatthew Dillon 		 */
1377a2de9a4SMatthew Dillon 		tnode = de->td_node;
138d89a0e31SMatthew Dillon 		error = tmpfs_alloc_vp(dvp->v_mount, dnode, tnode,
1397a2de9a4SMatthew Dillon 				       LK_EXCLUSIVE | LK_RETRY, &vp);
140d89a0e31SMatthew Dillon 		if (error == EAGAIN)
141d89a0e31SMatthew Dillon 			goto loop;
142974226b4STomohiro Kusumi 		if (error)
143974226b4STomohiro Kusumi 			goto out;
1447a2de9a4SMatthew Dillon 		KKASSERT(vp);
145d89ce96aSMatthew Dillon 	}
146974226b4STomohiro Kusumi 
147974226b4STomohiro Kusumi out:
14881aadc82STomohiro Kusumi 	TMPFS_NODE_UNLOCK(dnode);
149ff837cd5SMatthew Dillon 
150bed3b851STomohiro Kusumi 	if ((dnode->tn_status & TMPFS_NODE_ACCESSED) == 0) {
15181aadc82STomohiro Kusumi 		TMPFS_NODE_LOCK(dnode);
152ff837cd5SMatthew Dillon 		dnode->tn_status |= TMPFS_NODE_ACCESSED;
153ff837cd5SMatthew Dillon 		TMPFS_NODE_UNLOCK(dnode);
154bed3b851STomohiro Kusumi 	}
155ff837cd5SMatthew Dillon 
156d89ce96aSMatthew Dillon 	/*
157d89ce96aSMatthew Dillon 	 * Store the result of this lookup in the cache.  Avoid this if the
1587a2de9a4SMatthew Dillon 	 * request was for creation, as it does not improve timings on
159d89ce96aSMatthew Dillon 	 * emprical tests.
160d89ce96aSMatthew Dillon 	 */
1617a2de9a4SMatthew Dillon 	if (vp) {
1627a2de9a4SMatthew Dillon 		vn_unlock(vp);
163ba25006aSSascha Wildner 		cache_setvp(ap->a_nch, vp);
1647a2de9a4SMatthew Dillon 		vrele(vp);
165d89ce96aSMatthew Dillon 	} else if (error == ENOENT) {
166ba25006aSSascha Wildner 		cache_setvp(ap->a_nch, NULL);
1677a2de9a4SMatthew Dillon 	}
168aa1adbf0SMatthew Dillon 	return (error);
1697a2de9a4SMatthew Dillon }
1707a2de9a4SMatthew Dillon 
1717a2de9a4SMatthew Dillon static int
tmpfs_nlookupdotdot(struct vop_nlookupdotdot_args * ap)172ba25006aSSascha Wildner tmpfs_nlookupdotdot(struct vop_nlookupdotdot_args *ap)
1737a2de9a4SMatthew Dillon {
174ba25006aSSascha Wildner 	struct vnode *dvp = ap->a_dvp;
175ba25006aSSascha Wildner 	struct vnode **vpp = ap->a_vpp;
1767a2de9a4SMatthew Dillon 	struct tmpfs_node *dnode = VP_TO_TMPFS_NODE(dvp);
177ba25006aSSascha Wildner 	struct ucred *cred = ap->a_cred;
1787a2de9a4SMatthew Dillon 	int error;
1797a2de9a4SMatthew Dillon 
1807a2de9a4SMatthew Dillon 	*vpp = NULL;
181aa1adbf0SMatthew Dillon 
1827a2de9a4SMatthew Dillon 	/* Check accessibility of requested node as a first step. */
1837a2de9a4SMatthew Dillon 	error = VOP_ACCESS(dvp, VEXEC, cred);
184ff837cd5SMatthew Dillon 	if (error != 0)
1857a2de9a4SMatthew Dillon 		return error;
1867a2de9a4SMatthew Dillon 
1877a2de9a4SMatthew Dillon 	if (dnode->tn_dir.tn_parent != NULL) {
1887a2de9a4SMatthew Dillon 		/* Allocate a new vnode on the matching entry. */
189d89a0e31SMatthew Dillon 		error = tmpfs_alloc_vp(dvp->v_mount,
190d89a0e31SMatthew Dillon 				       NULL, dnode->tn_dir.tn_parent,
1917a2de9a4SMatthew Dillon 				       LK_EXCLUSIVE | LK_RETRY, vpp);
1927a2de9a4SMatthew Dillon 
1937a2de9a4SMatthew Dillon 		if (*vpp)
1947a2de9a4SMatthew Dillon 			vn_unlock(*vpp);
1957a2de9a4SMatthew Dillon 	}
1967a2de9a4SMatthew Dillon 	return (*vpp == NULL) ? ENOENT : 0;
1977a2de9a4SMatthew Dillon }
1987a2de9a4SMatthew Dillon 
1997a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
2007a2de9a4SMatthew Dillon 
2017a2de9a4SMatthew Dillon static int
tmpfs_ncreate(struct vop_ncreate_args * ap)202ba25006aSSascha Wildner tmpfs_ncreate(struct vop_ncreate_args *ap)
2037a2de9a4SMatthew Dillon {
204ba25006aSSascha Wildner 	struct vnode *dvp = ap->a_dvp;
205ba25006aSSascha Wildner 	struct vnode **vpp = ap->a_vpp;
206ba25006aSSascha Wildner 	struct namecache *ncp = ap->a_nch->ncp;
207ba25006aSSascha Wildner 	struct vattr *vap = ap->a_vap;
208ba25006aSSascha Wildner 	struct ucred *cred = ap->a_cred;
2097a2de9a4SMatthew Dillon 	int error;
2107a2de9a4SMatthew Dillon 
2117a2de9a4SMatthew Dillon 	KKASSERT(vap->va_type == VREG || vap->va_type == VSOCK);
2127a2de9a4SMatthew Dillon 
2137a2de9a4SMatthew Dillon 	error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL);
2147a2de9a4SMatthew Dillon 	if (error == 0) {
215ba25006aSSascha Wildner 		cache_setunresolved(ap->a_nch);
216ba25006aSSascha Wildner 		cache_setvp(ap->a_nch, *vpp);
21780ae59d7SMatthew Dillon 		tmpfs_knote(dvp, NOTE_WRITE);
2187a2de9a4SMatthew Dillon 	}
219aa1adbf0SMatthew Dillon 	return (error);
2207a2de9a4SMatthew Dillon }
2217a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
2227a2de9a4SMatthew Dillon 
2237a2de9a4SMatthew Dillon static int
tmpfs_nmknod(struct vop_nmknod_args * ap)224ba25006aSSascha Wildner tmpfs_nmknod(struct vop_nmknod_args *ap)
2257a2de9a4SMatthew Dillon {
226ba25006aSSascha Wildner 	struct vnode *dvp = ap->a_dvp;
227ba25006aSSascha Wildner 	struct vnode **vpp = ap->a_vpp;
228ba25006aSSascha Wildner 	struct namecache *ncp = ap->a_nch->ncp;
229ba25006aSSascha Wildner 	struct vattr *vap = ap->a_vap;
230ba25006aSSascha Wildner 	struct ucred *cred = ap->a_cred;
2317a2de9a4SMatthew Dillon 	int error;
2327a2de9a4SMatthew Dillon 
2337a2de9a4SMatthew Dillon 	if (vap->va_type != VBLK && vap->va_type != VCHR &&
234aa1adbf0SMatthew Dillon 	    vap->va_type != VFIFO) {
235aa1adbf0SMatthew Dillon 		return (EINVAL);
236aa1adbf0SMatthew Dillon 	}
2377a2de9a4SMatthew Dillon 
2387a2de9a4SMatthew Dillon 	error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL);
2397a2de9a4SMatthew Dillon 	if (error == 0) {
240ba25006aSSascha Wildner 		cache_setunresolved(ap->a_nch);
241ba25006aSSascha Wildner 		cache_setvp(ap->a_nch, *vpp);
24280ae59d7SMatthew Dillon 		tmpfs_knote(dvp, NOTE_WRITE);
2437a2de9a4SMatthew Dillon 	}
2447a2de9a4SMatthew Dillon 	return error;
2457a2de9a4SMatthew Dillon }
2467a2de9a4SMatthew Dillon 
2477a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
2487a2de9a4SMatthew Dillon 
2497a2de9a4SMatthew Dillon static int
tmpfs_open(struct vop_open_args * ap)250ba25006aSSascha Wildner tmpfs_open(struct vop_open_args *ap)
2517a2de9a4SMatthew Dillon {
252ba25006aSSascha Wildner 	struct vnode *vp = ap->a_vp;
253ba25006aSSascha Wildner 	int mode = ap->a_mode;
2547a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
255aa1adbf0SMatthew Dillon 	int error;
2567a2de9a4SMatthew Dillon 
2577a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
2587a2de9a4SMatthew Dillon 
2596e0c5aabSMatthew Dillon #if 0
2607a2de9a4SMatthew Dillon 	/* The file is still active but all its names have been removed
2617a2de9a4SMatthew Dillon 	 * (e.g. by a "rmdir $(pwd)").  It cannot be opened any more as
2627a2de9a4SMatthew Dillon 	 * it is about to die. */
2637a2de9a4SMatthew Dillon 	if (node->tn_links < 1)
2647a2de9a4SMatthew Dillon 		return (ENOENT);
2656e0c5aabSMatthew Dillon #endif
2667a2de9a4SMatthew Dillon 
2677a2de9a4SMatthew Dillon 	/* If the file is marked append-only, deny write requests. */
268630e3a33SMatthew Dillon 	if ((node->tn_flags & APPEND) &&
269630e3a33SMatthew Dillon 	    (mode & (FWRITE | O_APPEND)) == FWRITE) {
2707a2de9a4SMatthew Dillon 		error = EPERM;
271630e3a33SMatthew Dillon 	} else {
272a1b829f2SMatthew Dillon 		if (node->tn_reg.tn_pages_in_aobj) {
273a1b829f2SMatthew Dillon 			TMPFS_NODE_LOCK(node);
274a1b829f2SMatthew Dillon 			if (node->tn_reg.tn_pages_in_aobj) {
275a1b829f2SMatthew Dillon 				tmpfs_move_pages(node->tn_reg.tn_aobj,
276cfd59c5aSMatthew Dillon 						 vp->v_object,
277cfd59c5aSMatthew Dillon 						 TMPFS_MOVF_FROMBACKING);
278a1b829f2SMatthew Dillon 				node->tn_reg.tn_pages_in_aobj = 0;
279a1b829f2SMatthew Dillon 			}
280a1b829f2SMatthew Dillon 			TMPFS_NODE_UNLOCK(node);
281a1b829f2SMatthew Dillon 		}
282a1b829f2SMatthew Dillon 		error = vop_stdopen(ap);
2837a2de9a4SMatthew Dillon 	}
284aa1adbf0SMatthew Dillon 
285aa1adbf0SMatthew Dillon 	return (error);
2867a2de9a4SMatthew Dillon }
2877a2de9a4SMatthew Dillon 
2887a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
2897a2de9a4SMatthew Dillon 
2907a2de9a4SMatthew Dillon static int
tmpfs_close(struct vop_close_args * ap)291ba25006aSSascha Wildner tmpfs_close(struct vop_close_args *ap)
2927a2de9a4SMatthew Dillon {
293ba25006aSSascha Wildner 	struct vnode *vp = ap->a_vp;
2947a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
295aa1adbf0SMatthew Dillon 	int error;
2967a2de9a4SMatthew Dillon 
2977a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
2987a2de9a4SMatthew Dillon 
2997a2de9a4SMatthew Dillon 	if (node->tn_links > 0) {
3006e0c5aabSMatthew Dillon 		/*
3016e0c5aabSMatthew Dillon 		 * Update node times.  No need to do it if the node has
3026e0c5aabSMatthew Dillon 		 * been deleted, because it will vanish after we return.
3036e0c5aabSMatthew Dillon 		 */
3047a2de9a4SMatthew Dillon 		tmpfs_update(vp);
3057a2de9a4SMatthew Dillon 	}
3067a2de9a4SMatthew Dillon 
307ba25006aSSascha Wildner 	error = vop_stdclose(ap);
308aa1adbf0SMatthew Dillon 
309aa1adbf0SMatthew Dillon 	return (error);
3107a2de9a4SMatthew Dillon }
3117a2de9a4SMatthew Dillon 
3127a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
3137a2de9a4SMatthew Dillon 
3147a2de9a4SMatthew Dillon int
tmpfs_access(struct vop_access_args * ap)315ba25006aSSascha Wildner tmpfs_access(struct vop_access_args *ap)
3167a2de9a4SMatthew Dillon {
317ba25006aSSascha Wildner 	struct vnode *vp = ap->a_vp;
3187a2de9a4SMatthew Dillon 	int error;
3197a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
3207a2de9a4SMatthew Dillon 
3217a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
3227a2de9a4SMatthew Dillon 
3237a2de9a4SMatthew Dillon 	switch (vp->v_type) {
3247a2de9a4SMatthew Dillon 	case VDIR:
3257a2de9a4SMatthew Dillon 		/* FALLTHROUGH */
3267a2de9a4SMatthew Dillon 	case VLNK:
3277a2de9a4SMatthew Dillon 		/* FALLTHROUGH */
3287a2de9a4SMatthew Dillon 	case VREG:
329ba25006aSSascha Wildner 		if ((ap->a_mode & VWRITE) &&
330aa1adbf0SMatthew Dillon 	            (vp->v_mount->mnt_flag & MNT_RDONLY)) {
3317a2de9a4SMatthew Dillon 			error = EROFS;
3327a2de9a4SMatthew Dillon 			goto out;
3337a2de9a4SMatthew Dillon 		}
3347a2de9a4SMatthew Dillon 		break;
3357a2de9a4SMatthew Dillon 
3367a2de9a4SMatthew Dillon 	case VBLK:
3377a2de9a4SMatthew Dillon 		/* FALLTHROUGH */
3387a2de9a4SMatthew Dillon 	case VCHR:
3397a2de9a4SMatthew Dillon 		/* FALLTHROUGH */
3407a2de9a4SMatthew Dillon 	case VSOCK:
3417a2de9a4SMatthew Dillon 		/* FALLTHROUGH */
3427a2de9a4SMatthew Dillon 	case VFIFO:
3437a2de9a4SMatthew Dillon 		break;
3447a2de9a4SMatthew Dillon 
3457a2de9a4SMatthew Dillon 	default:
3467a2de9a4SMatthew Dillon 		error = EINVAL;
3477a2de9a4SMatthew Dillon 		goto out;
3487a2de9a4SMatthew Dillon 	}
3497a2de9a4SMatthew Dillon 
350ba25006aSSascha Wildner 	if ((ap->a_mode & VWRITE) && (node->tn_flags & IMMUTABLE)) {
3517a2de9a4SMatthew Dillon 		error = EPERM;
3527a2de9a4SMatthew Dillon 		goto out;
3537a2de9a4SMatthew Dillon 	}
3547a2de9a4SMatthew Dillon 
355ba25006aSSascha Wildner 	error = vop_helper_access(ap, node->tn_uid, node->tn_gid,
356aa1adbf0SMatthew Dillon 			          node->tn_mode, 0);
3577a2de9a4SMatthew Dillon out:
3587a2de9a4SMatthew Dillon 	return error;
3597a2de9a4SMatthew Dillon }
3607a2de9a4SMatthew Dillon 
3617a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
3627a2de9a4SMatthew Dillon 
3637a2de9a4SMatthew Dillon int
tmpfs_getattr(struct vop_getattr_args * ap)364ba25006aSSascha Wildner tmpfs_getattr(struct vop_getattr_args *ap)
3657a2de9a4SMatthew Dillon {
366ba25006aSSascha Wildner 	struct vnode *vp = ap->a_vp;
367ba25006aSSascha Wildner 	struct vattr *vap = ap->a_vap;
3687a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
3697a2de9a4SMatthew Dillon 
3707a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
3717a2de9a4SMatthew Dillon 
3727a2de9a4SMatthew Dillon 	tmpfs_update(vp);
3737a2de9a4SMatthew Dillon 
3747a2de9a4SMatthew Dillon 	vap->va_type = vp->v_type;
3757a2de9a4SMatthew Dillon 	vap->va_mode = node->tn_mode;
3767a2de9a4SMatthew Dillon 	vap->va_nlink = node->tn_links;
3777a2de9a4SMatthew Dillon 	vap->va_uid = node->tn_uid;
3787a2de9a4SMatthew Dillon 	vap->va_gid = node->tn_gid;
3797a2de9a4SMatthew Dillon 	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
3807a2de9a4SMatthew Dillon 	vap->va_fileid = node->tn_id;
3817a2de9a4SMatthew Dillon 	vap->va_size = node->tn_size;
3827a2de9a4SMatthew Dillon 	vap->va_blocksize = PAGE_SIZE;
3837a2de9a4SMatthew Dillon 	vap->va_atime.tv_sec = node->tn_atime;
3847a2de9a4SMatthew Dillon 	vap->va_atime.tv_nsec = node->tn_atimensec;
3857a2de9a4SMatthew Dillon 	vap->va_mtime.tv_sec = node->tn_mtime;
3867a2de9a4SMatthew Dillon 	vap->va_mtime.tv_nsec = node->tn_mtimensec;
3877a2de9a4SMatthew Dillon 	vap->va_ctime.tv_sec = node->tn_ctime;
3887a2de9a4SMatthew Dillon 	vap->va_ctime.tv_nsec = node->tn_ctimensec;
3897a2de9a4SMatthew Dillon 	vap->va_gen = node->tn_gen;
3907a2de9a4SMatthew Dillon 	vap->va_flags = node->tn_flags;
391ff837cd5SMatthew Dillon 	if (vp->v_type == VBLK || vp->v_type == VCHR) {
3927a2de9a4SMatthew Dillon 		vap->va_rmajor = umajor(node->tn_rdev);
3937a2de9a4SMatthew Dillon 		vap->va_rminor = uminor(node->tn_rdev);
3947a2de9a4SMatthew Dillon 	}
3957a2de9a4SMatthew Dillon 	vap->va_bytes = round_page(node->tn_size);
3967a2de9a4SMatthew Dillon 	vap->va_filerev = 0;
397e575e508SVenkatesh Srinivas 
3987a2de9a4SMatthew Dillon 	return 0;
3997a2de9a4SMatthew Dillon }
4007a2de9a4SMatthew Dillon 
4017a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
4027a2de9a4SMatthew Dillon 
4037a2de9a4SMatthew Dillon int
tmpfs_getattr_lite(struct vop_getattr_lite_args * ap)404de9bb133SMatthew Dillon tmpfs_getattr_lite(struct vop_getattr_lite_args *ap)
405fc36a10bSMatthew Dillon {
406fc36a10bSMatthew Dillon 	struct vnode *vp = ap->a_vp;
407de9bb133SMatthew Dillon 	struct vattr_lite *lvap = ap->a_lvap;
408fc36a10bSMatthew Dillon 	struct tmpfs_node *node;
409fc36a10bSMatthew Dillon 
410fc36a10bSMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
411fc36a10bSMatthew Dillon 
412fc36a10bSMatthew Dillon 	tmpfs_update(vp);
413fc36a10bSMatthew Dillon 
414de9bb133SMatthew Dillon 	lvap->va_type = vp->v_type;
415de9bb133SMatthew Dillon 	lvap->va_mode = node->tn_mode;
416de9bb133SMatthew Dillon 	lvap->va_nlink = node->tn_links;
417de9bb133SMatthew Dillon 	lvap->va_uid = node->tn_uid;
418de9bb133SMatthew Dillon 	lvap->va_gid = node->tn_gid;
419de9bb133SMatthew Dillon #if 0
420fc36a10bSMatthew Dillon 	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
421fc36a10bSMatthew Dillon 	vap->va_fileid = node->tn_id;
422de9bb133SMatthew Dillon #endif
423de9bb133SMatthew Dillon 	lvap->va_size = node->tn_size;
424de9bb133SMatthew Dillon #if 0
425fc36a10bSMatthew Dillon 	vap->va_blocksize = PAGE_SIZE;
426fc36a10bSMatthew Dillon 	vap->va_gen = node->tn_gen;
427de9bb133SMatthew Dillon #endif
428de9bb133SMatthew Dillon 	lvap->va_flags = node->tn_flags;
429de9bb133SMatthew Dillon #if 0
430fc36a10bSMatthew Dillon 	if (vp->v_type == VBLK || vp->v_type == VCHR) {
431fc36a10bSMatthew Dillon 		vap->va_rmajor = umajor(node->tn_rdev);
432fc36a10bSMatthew Dillon 		vap->va_rminor = uminor(node->tn_rdev);
433fc36a10bSMatthew Dillon 	}
434fc36a10bSMatthew Dillon 	vap->va_bytes = -1;
435fc36a10bSMatthew Dillon 	vap->va_filerev = 0;
436de9bb133SMatthew Dillon #endif
437fc36a10bSMatthew Dillon 
438fc36a10bSMatthew Dillon 	return 0;
439fc36a10bSMatthew Dillon }
440fc36a10bSMatthew Dillon 
441fc36a10bSMatthew Dillon 
442fc36a10bSMatthew Dillon /* --------------------------------------------------------------------- */
443fc36a10bSMatthew Dillon 
444fc36a10bSMatthew Dillon int
tmpfs_setattr(struct vop_setattr_args * ap)445ba25006aSSascha Wildner tmpfs_setattr(struct vop_setattr_args *ap)
4467a2de9a4SMatthew Dillon {
447ba25006aSSascha Wildner 	struct vnode *vp = ap->a_vp;
448ba25006aSSascha Wildner 	struct vattr *vap = ap->a_vap;
449ba25006aSSascha Wildner 	struct ucred *cred = ap->a_cred;
45080ae59d7SMatthew Dillon 	struct tmpfs_node *node = VP_TO_TMPFS_NODE(vp);
4517a2de9a4SMatthew Dillon 	int error = 0;
45280ae59d7SMatthew Dillon 	int kflags = 0;
4537a2de9a4SMatthew Dillon 
454ff837cd5SMatthew Dillon 	TMPFS_NODE_LOCK(node);
45580ae59d7SMatthew Dillon 	if (error == 0 && (vap->va_flags != VNOVAL)) {
4567a2de9a4SMatthew Dillon 		error = tmpfs_chflags(vp, vap->va_flags, cred);
45780ae59d7SMatthew Dillon 		kflags |= NOTE_ATTRIB;
45880ae59d7SMatthew Dillon 	}
4597a2de9a4SMatthew Dillon 
46080ae59d7SMatthew Dillon 	if (error == 0 && (vap->va_size != VNOVAL)) {
461a1b829f2SMatthew Dillon 		/* restore any saved pages before proceeding */
462a1b829f2SMatthew Dillon 		if (node->tn_reg.tn_pages_in_aobj) {
463cfd59c5aSMatthew Dillon 			tmpfs_move_pages(node->tn_reg.tn_aobj, vp->v_object,
464cfd59c5aSMatthew Dillon 					 TMPFS_MOVF_FROMBACKING |
465cfd59c5aSMatthew Dillon 					 TMPFS_MOVF_DEACTIVATE);
466a1b829f2SMatthew Dillon 			node->tn_reg.tn_pages_in_aobj = 0;
467a1b829f2SMatthew Dillon 		}
46880ae59d7SMatthew Dillon 		if (vap->va_size > node->tn_size)
46980ae59d7SMatthew Dillon 			kflags |= NOTE_WRITE | NOTE_EXTEND;
47080ae59d7SMatthew Dillon 		else
47180ae59d7SMatthew Dillon 			kflags |= NOTE_WRITE;
4727a2de9a4SMatthew Dillon 		error = tmpfs_chsize(vp, vap->va_size, cred);
47380ae59d7SMatthew Dillon 	}
4747a2de9a4SMatthew Dillon 
475d89ce96aSMatthew Dillon 	if (error == 0 && (vap->va_uid != (uid_t)VNOVAL ||
476d89ce96aSMatthew Dillon 			   vap->va_gid != (gid_t)VNOVAL)) {
4777a2de9a4SMatthew Dillon 		error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred);
47880ae59d7SMatthew Dillon 		kflags |= NOTE_ATTRIB;
479d89ce96aSMatthew Dillon 	}
4807a2de9a4SMatthew Dillon 
48180ae59d7SMatthew Dillon 	if (error == 0 && (vap->va_mode != (mode_t)VNOVAL)) {
4827a2de9a4SMatthew Dillon 		error = tmpfs_chmod(vp, vap->va_mode, cred);
48380ae59d7SMatthew Dillon 		kflags |= NOTE_ATTRIB;
48480ae59d7SMatthew Dillon 	}
4857a2de9a4SMatthew Dillon 
4867a2de9a4SMatthew Dillon 	if (error == 0 && ((vap->va_atime.tv_sec != VNOVAL &&
4877a2de9a4SMatthew Dillon 	    vap->va_atime.tv_nsec != VNOVAL) ||
4887a2de9a4SMatthew Dillon 	    (vap->va_mtime.tv_sec != VNOVAL &&
489d89ce96aSMatthew Dillon 	    vap->va_mtime.tv_nsec != VNOVAL) )) {
4907a2de9a4SMatthew Dillon 		error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime,
4917a2de9a4SMatthew Dillon 				      vap->va_vaflags, cred);
49280ae59d7SMatthew Dillon 		kflags |= NOTE_ATTRIB;
493d89ce96aSMatthew Dillon 	}
4947a2de9a4SMatthew Dillon 
495ff837cd5SMatthew Dillon 	/*
496ff837cd5SMatthew Dillon 	 * Update the node times.  We give preference to the error codes
4977a2de9a4SMatthew Dillon 	 * generated by this function rather than the ones that may arise
498ff837cd5SMatthew Dillon 	 * from tmpfs_update.
499ff837cd5SMatthew Dillon 	 */
5007a2de9a4SMatthew Dillon 	tmpfs_update(vp);
501ff837cd5SMatthew Dillon 	TMPFS_NODE_UNLOCK(node);
50280ae59d7SMatthew Dillon 	tmpfs_knote(vp, kflags);
5037a2de9a4SMatthew Dillon 
504aa1adbf0SMatthew Dillon 	return (error);
5057a2de9a4SMatthew Dillon }
5067a2de9a4SMatthew Dillon 
5077a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
5087a2de9a4SMatthew Dillon 
5099fc94b5fSMatthew Dillon /*
510630e3a33SMatthew Dillon  * fsync is usually a NOP, but we must take action when unmounting or
511630e3a33SMatthew Dillon  * when recycling.
5129fc94b5fSMatthew Dillon  */
5137a2de9a4SMatthew Dillon static int
tmpfs_fsync(struct vop_fsync_args * ap)514ba25006aSSascha Wildner tmpfs_fsync(struct vop_fsync_args *ap)
5157a2de9a4SMatthew Dillon {
516630e3a33SMatthew Dillon 	struct tmpfs_node *node;
517ba25006aSSascha Wildner 	struct vnode *vp = ap->a_vp;
5187a2de9a4SMatthew Dillon 
519630e3a33SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
520630e3a33SMatthew Dillon 
521d883c473SMatthew Dillon 	/*
522d883c473SMatthew Dillon 	 * tmpfs vnodes typically remain dirty, avoid long syncer scans
523d883c473SMatthew Dillon 	 * by forcing removal from the syncer list.
524d883c473SMatthew Dillon 	 */
525d883c473SMatthew Dillon 	vn_syncer_remove(vp, 1);
526d883c473SMatthew Dillon 
5277a2de9a4SMatthew Dillon 	tmpfs_update(vp);
528630e3a33SMatthew Dillon 	if (vp->v_type == VREG) {
529d4623db3SMatthew Dillon 		if (vp->v_flag & VRECLAIMED) {
530630e3a33SMatthew Dillon 			if (node->tn_links == 0)
531630e3a33SMatthew Dillon 				tmpfs_truncate(vp, 0);
532630e3a33SMatthew Dillon 			else
533ba25006aSSascha Wildner 				vfsync(ap->a_vp, ap->a_waitfor, 1, NULL, NULL);
534630e3a33SMatthew Dillon 		}
5359fc94b5fSMatthew Dillon 	}
536d883c473SMatthew Dillon 
5377a2de9a4SMatthew Dillon 	return 0;
5387a2de9a4SMatthew Dillon }
5397a2de9a4SMatthew Dillon 
5407a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
5417a2de9a4SMatthew Dillon 
5427a2de9a4SMatthew Dillon static int
tmpfs_read(struct vop_read_args * ap)5437a2de9a4SMatthew Dillon tmpfs_read(struct vop_read_args *ap)
5447a2de9a4SMatthew Dillon {
5457a2de9a4SMatthew Dillon 	struct buf *bp;
5467a2de9a4SMatthew Dillon 	struct vnode *vp = ap->a_vp;
5477a2de9a4SMatthew Dillon 	struct uio *uio = ap->a_uio;
5487a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
5497a2de9a4SMatthew Dillon 	off_t base_offset;
5509fc94b5fSMatthew Dillon 	size_t offset;
5517a2de9a4SMatthew Dillon 	size_t len;
5522446b819SMatthew Dillon 	size_t resid;
5539fc94b5fSMatthew Dillon 	int error;
554a1b829f2SMatthew Dillon 	int seqcount;
5557a2de9a4SMatthew Dillon 
5562446b819SMatthew Dillon 	/*
5572446b819SMatthew Dillon 	 * Check the basics
5582446b819SMatthew Dillon 	 */
5597a2de9a4SMatthew Dillon 	if (uio->uio_offset < 0)
5607a2de9a4SMatthew Dillon 		return (EINVAL);
5617a2de9a4SMatthew Dillon 	if (vp->v_type != VREG)
5627a2de9a4SMatthew Dillon 		return (EINVAL);
5637a2de9a4SMatthew Dillon 
5642446b819SMatthew Dillon 	/*
5652446b819SMatthew Dillon 	 * Extract node, try to shortcut the operation through
5662446b819SMatthew Dillon 	 * the VM page cache, allowing us to avoid buffer cache
5672446b819SMatthew Dillon 	 * overheads.
5682446b819SMatthew Dillon 	 */
5692446b819SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
5702446b819SMatthew Dillon         resid = uio->uio_resid;
5710d540a44STomohiro Kusumi 	seqcount = ap->a_ioflag >> IO_SEQSHIFT;
5722446b819SMatthew Dillon         error = vop_helper_read_shortcut(ap);
5732446b819SMatthew Dillon         if (error)
5742446b819SMatthew Dillon                 return error;
5752446b819SMatthew Dillon         if (uio->uio_resid == 0) {
5762446b819SMatthew Dillon 		if (resid)
5772446b819SMatthew Dillon 			goto finished;
5782446b819SMatthew Dillon 		return error;
5792446b819SMatthew Dillon 	}
5802446b819SMatthew Dillon 
5812446b819SMatthew Dillon 	/*
582a1b829f2SMatthew Dillon 	 * restore any saved pages before proceeding
583a1b829f2SMatthew Dillon 	 */
584a1b829f2SMatthew Dillon 	if (node->tn_reg.tn_pages_in_aobj) {
585a1b829f2SMatthew Dillon 		TMPFS_NODE_LOCK(node);
586a1b829f2SMatthew Dillon 		if (node->tn_reg.tn_pages_in_aobj) {
587cfd59c5aSMatthew Dillon 			tmpfs_move_pages(node->tn_reg.tn_aobj, vp->v_object,
588cfd59c5aSMatthew Dillon 					 TMPFS_MOVF_FROMBACKING);
589a1b829f2SMatthew Dillon 			node->tn_reg.tn_pages_in_aobj = 0;
590a1b829f2SMatthew Dillon 		}
591a1b829f2SMatthew Dillon 		TMPFS_NODE_UNLOCK(node);
592a1b829f2SMatthew Dillon 	}
593a1b829f2SMatthew Dillon 
594a1b829f2SMatthew Dillon 	/*
5952446b819SMatthew Dillon 	 * Fall-through to our normal read code.
5962446b819SMatthew Dillon 	 */
5977a2de9a4SMatthew Dillon 	while (uio->uio_resid > 0 && uio->uio_offset < node->tn_size) {
5987a2de9a4SMatthew Dillon 		/*
5997a2de9a4SMatthew Dillon 		 * Use buffer cache I/O (via tmpfs_strategy)
6007a2de9a4SMatthew Dillon 		 */
6013c54bb74SMatthew Dillon 		offset = (size_t)uio->uio_offset & TMPFS_BLKMASK64;
6027a2de9a4SMatthew Dillon 		base_offset = (off_t)uio->uio_offset - offset;
6039cd86db5SMatthew Dillon 		bp = getcacheblk(vp, base_offset,
6049cd86db5SMatthew Dillon 				 node->tn_blksize, GETBLK_KVABIO);
6059de13b88SMatthew Dillon 		if (bp == NULL) {
606cfd59c5aSMatthew Dillon 			if (tmpfs_cluster_rd_enable) {
607a1b829f2SMatthew Dillon 				error = cluster_readx(vp, node->tn_size,
608a1b829f2SMatthew Dillon 						     base_offset,
6099cd86db5SMatthew Dillon 						     node->tn_blksize,
610a1b829f2SMatthew Dillon 						     B_NOTMETA | B_KVABIO,
611a1b829f2SMatthew Dillon 						     uio->uio_resid,
612a1b829f2SMatthew Dillon 						     seqcount * MAXBSIZE,
613a1b829f2SMatthew Dillon 						     &bp);
614a1b829f2SMatthew Dillon 			} else {
61548db4e20SMatthew Dillon 				error = bread_kvabio(vp, base_offset,
6169cd86db5SMatthew Dillon 						     node->tn_blksize, &bp);
617a1b829f2SMatthew Dillon 			}
6187a2de9a4SMatthew Dillon 			if (error) {
6197a2de9a4SMatthew Dillon 				brelse(bp);
6207a2de9a4SMatthew Dillon 				kprintf("tmpfs_read bread error %d\n", error);
6217a2de9a4SMatthew Dillon 				break;
6227a2de9a4SMatthew Dillon 			}
623527cd77dSMatthew Dillon 
624527cd77dSMatthew Dillon 			/*
625527cd77dSMatthew Dillon 			 * tmpfs pretty much fiddles directly with the VM
626527cd77dSMatthew Dillon 			 * system, don't let it exhaust it or we won't play
627527cd77dSMatthew Dillon 			 * nice with other processes.
628527cd77dSMatthew Dillon 			 *
629527cd77dSMatthew Dillon 			 * Only do this if the VOP is coming from a normal
630527cd77dSMatthew Dillon 			 * read/write.  The VM system handles the case for
631527cd77dSMatthew Dillon 			 * UIO_NOCOPY.
632527cd77dSMatthew Dillon 			 */
633527cd77dSMatthew Dillon 			if (uio->uio_segflg != UIO_NOCOPY)
634527cd77dSMatthew Dillon 				vm_wait_nominal();
6357a2de9a4SMatthew Dillon 		}
6363c54bb74SMatthew Dillon 		bp->b_flags |= B_CLUSTEROK;
637b3f55d88SMatthew Dillon 		bkvasync(bp);
6387a2de9a4SMatthew Dillon 
6397a2de9a4SMatthew Dillon 		/*
6407a2de9a4SMatthew Dillon 		 * Figure out how many bytes we can actually copy this loop.
6417a2de9a4SMatthew Dillon 		 */
6429cd86db5SMatthew Dillon 		len = node->tn_blksize - offset;
6437a2de9a4SMatthew Dillon 		if (len > uio->uio_resid)
6447a2de9a4SMatthew Dillon 			len = uio->uio_resid;
6457a2de9a4SMatthew Dillon 		if (len > node->tn_size - uio->uio_offset)
6467a2de9a4SMatthew Dillon 			len = (size_t)(node->tn_size - uio->uio_offset);
6477a2de9a4SMatthew Dillon 
64844480e31SMatthew Dillon 		error = uiomovebp(bp, (char *)bp->b_data + offset, len, uio);
6497a2de9a4SMatthew Dillon 		bqrelse(bp);
6507a2de9a4SMatthew Dillon 		if (error) {
6517a2de9a4SMatthew Dillon 			kprintf("tmpfs_read uiomove error %d\n", error);
6527a2de9a4SMatthew Dillon 			break;
6537a2de9a4SMatthew Dillon 		}
6547a2de9a4SMatthew Dillon 	}
6557a2de9a4SMatthew Dillon 
6562446b819SMatthew Dillon finished:
657bed3b851STomohiro Kusumi 	if ((node->tn_status & TMPFS_NODE_ACCESSED) == 0) {
6587a2de9a4SMatthew Dillon 		TMPFS_NODE_LOCK(node);
6597a2de9a4SMatthew Dillon 		node->tn_status |= TMPFS_NODE_ACCESSED;
6607a2de9a4SMatthew Dillon 		TMPFS_NODE_UNLOCK(node);
661bed3b851STomohiro Kusumi 	}
6627a2de9a4SMatthew Dillon 	return (error);
6637a2de9a4SMatthew Dillon }
6647a2de9a4SMatthew Dillon 
6657a2de9a4SMatthew Dillon static int
tmpfs_write(struct vop_write_args * ap)6667a2de9a4SMatthew Dillon tmpfs_write(struct vop_write_args *ap)
6677a2de9a4SMatthew Dillon {
6687a2de9a4SMatthew Dillon 	struct buf *bp;
6697a2de9a4SMatthew Dillon 	struct vnode *vp = ap->a_vp;
6707a2de9a4SMatthew Dillon 	struct uio *uio = ap->a_uio;
6717a2de9a4SMatthew Dillon 	struct thread *td = uio->uio_td;
6727a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
6737a2de9a4SMatthew Dillon 	boolean_t extended;
6747a2de9a4SMatthew Dillon 	off_t oldsize;
6757a2de9a4SMatthew Dillon 	int error;
6767a2de9a4SMatthew Dillon 	off_t base_offset;
6779fc94b5fSMatthew Dillon 	size_t offset;
6787a2de9a4SMatthew Dillon 	size_t len;
6797a2de9a4SMatthew Dillon 	struct rlimit limit;
6807a2de9a4SMatthew Dillon 	int trivial = 0;
68180ae59d7SMatthew Dillon 	int kflags = 0;
6823c54bb74SMatthew Dillon 	int seqcount;
6837a2de9a4SMatthew Dillon 
6847a2de9a4SMatthew Dillon 	error = 0;
6857a2de9a4SMatthew Dillon 	if (uio->uio_resid == 0) {
6867a2de9a4SMatthew Dillon 		return error;
6877a2de9a4SMatthew Dillon 	}
6887a2de9a4SMatthew Dillon 
6897a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
6907a2de9a4SMatthew Dillon 
6917a2de9a4SMatthew Dillon 	if (vp->v_type != VREG)
6927a2de9a4SMatthew Dillon 		return (EINVAL);
6930d540a44STomohiro Kusumi 	seqcount = ap->a_ioflag >> IO_SEQSHIFT;
6947a2de9a4SMatthew Dillon 
695b37a7c00SMatthew Dillon 	TMPFS_NODE_LOCK(node);
696b37a7c00SMatthew Dillon 
697a1b829f2SMatthew Dillon 	/*
698a1b829f2SMatthew Dillon 	 * restore any saved pages before proceeding
699a1b829f2SMatthew Dillon 	 */
700a1b829f2SMatthew Dillon 	if (node->tn_reg.tn_pages_in_aobj) {
701cfd59c5aSMatthew Dillon 		tmpfs_move_pages(node->tn_reg.tn_aobj, vp->v_object,
702cfd59c5aSMatthew Dillon 				 TMPFS_MOVF_FROMBACKING);
703a1b829f2SMatthew Dillon 		node->tn_reg.tn_pages_in_aobj = 0;
704a1b829f2SMatthew Dillon 	}
705a1b829f2SMatthew Dillon 
7067a2de9a4SMatthew Dillon 	oldsize = node->tn_size;
7077a2de9a4SMatthew Dillon 	if (ap->a_ioflag & IO_APPEND)
7087a2de9a4SMatthew Dillon 		uio->uio_offset = node->tn_size;
7097a2de9a4SMatthew Dillon 
7107a2de9a4SMatthew Dillon 	/*
7117a2de9a4SMatthew Dillon 	 * Check for illegal write offsets.
7127a2de9a4SMatthew Dillon 	 */
7137a2de9a4SMatthew Dillon 	if (uio->uio_offset + uio->uio_resid >
7141be4932cSVenkatesh Srinivas 	  VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize) {
715b37a7c00SMatthew Dillon 		error = EFBIG;
716b37a7c00SMatthew Dillon 		goto done;
7171be4932cSVenkatesh Srinivas 	}
7187a2de9a4SMatthew Dillon 
7194c5cdd6bSMatthew Dillon 	/*
7204c5cdd6bSMatthew Dillon 	 * NOTE: Ignore if UIO does not come from a user thread (e.g. VN).
7214c5cdd6bSMatthew Dillon 	 */
7224c5cdd6bSMatthew Dillon 	if (vp->v_type == VREG && td != NULL && td->td_lwp != NULL) {
7237a2de9a4SMatthew Dillon 		error = kern_getrlimit(RLIMIT_FSIZE, &limit);
724b37a7c00SMatthew Dillon 		if (error)
725b37a7c00SMatthew Dillon 			goto done;
7267a2de9a4SMatthew Dillon 		if (uio->uio_offset + uio->uio_resid > limit.rlim_cur) {
7277a2de9a4SMatthew Dillon 			ksignal(td->td_proc, SIGXFSZ);
728b37a7c00SMatthew Dillon 			error = EFBIG;
729b37a7c00SMatthew Dillon 			goto done;
7307a2de9a4SMatthew Dillon 		}
7317a2de9a4SMatthew Dillon 	}
7327a2de9a4SMatthew Dillon 
7337a2de9a4SMatthew Dillon 	/*
7347a2de9a4SMatthew Dillon 	 * Extend the file's size if necessary
7357a2de9a4SMatthew Dillon 	 */
7369fc94b5fSMatthew Dillon 	extended = ((uio->uio_offset + uio->uio_resid) > node->tn_size);
7377a2de9a4SMatthew Dillon 
7387a2de9a4SMatthew Dillon 	while (uio->uio_resid > 0) {
7397a2de9a4SMatthew Dillon 		/*
7403c54bb74SMatthew Dillon 		 * Don't completely blow out running buffer I/O
7413c54bb74SMatthew Dillon 		 * when being hit from the pageout daemon.
7423c54bb74SMatthew Dillon 		 */
7433c54bb74SMatthew Dillon 		if (uio->uio_segflg == UIO_NOCOPY &&
7443c54bb74SMatthew Dillon 		    (ap->a_ioflag & IO_RECURSE) == 0) {
7459cd86db5SMatthew Dillon 			bwillwrite(node->tn_blksize);
7463c54bb74SMatthew Dillon 		}
7473c54bb74SMatthew Dillon 
7483c54bb74SMatthew Dillon 		/*
7497a2de9a4SMatthew Dillon 		 * Use buffer cache I/O (via tmpfs_strategy)
7509cd86db5SMatthew Dillon 		 *
7519cd86db5SMatthew Dillon 		 * Calculate the maximum bytes we can write to the buffer at
7529cd86db5SMatthew Dillon 		 * this offset (after resizing).
7537a2de9a4SMatthew Dillon 		 */
7543c54bb74SMatthew Dillon 		offset = (size_t)uio->uio_offset & TMPFS_BLKMASK64;
7557a2de9a4SMatthew Dillon 		base_offset = (off_t)uio->uio_offset - offset;
7567a2de9a4SMatthew Dillon 		len = uio->uio_resid;
7579cd86db5SMatthew Dillon 		if (len > TMPFS_BLKSIZE - offset)
7589cd86db5SMatthew Dillon 			len = TMPFS_BLKSIZE - offset;
7597a2de9a4SMatthew Dillon 
7607a2de9a4SMatthew Dillon 		if ((uio->uio_offset + len) > node->tn_size) {
7619fc94b5fSMatthew Dillon 			trivial = (uio->uio_offset <= node->tn_size);
762b37a7c00SMatthew Dillon 			error = tmpfs_reg_resize(vp, uio->uio_offset + len,
763b37a7c00SMatthew Dillon 						 trivial);
7647a2de9a4SMatthew Dillon 			if (error)
7657a2de9a4SMatthew Dillon 				break;
7667a2de9a4SMatthew Dillon 		}
7677a2de9a4SMatthew Dillon 
7689fc94b5fSMatthew Dillon 		/*
7699fc94b5fSMatthew Dillon 		 * Read to fill in any gaps.  Theoretically we could
7709fc94b5fSMatthew Dillon 		 * optimize this if the write covers the entire buffer
7719fc94b5fSMatthew Dillon 		 * and is not a UIO_NOCOPY write, however this can lead
7729fc94b5fSMatthew Dillon 		 * to a security violation exposing random kernel memory
7739fc94b5fSMatthew Dillon 		 * (whatever junk was in the backing VM pages before).
7749fc94b5fSMatthew Dillon 		 *
7759fc94b5fSMatthew Dillon 		 * So just use bread() to do the right thing.
7769fc94b5fSMatthew Dillon 		 */
7779cd86db5SMatthew Dillon 		error = bread_kvabio(vp, base_offset, node->tn_blksize, &bp);
778b3f55d88SMatthew Dillon 		bkvasync(bp);
77944480e31SMatthew Dillon 		error = uiomovebp(bp, (char *)bp->b_data + offset, len, uio);
7807a2de9a4SMatthew Dillon 		if (error) {
7817a2de9a4SMatthew Dillon 			kprintf("tmpfs_write uiomove error %d\n", error);
7827a2de9a4SMatthew Dillon 			brelse(bp);
7837a2de9a4SMatthew Dillon 			break;
7847a2de9a4SMatthew Dillon 		}
7857a2de9a4SMatthew Dillon 
78680ae59d7SMatthew Dillon 		if (uio->uio_offset > node->tn_size) {
7877a2de9a4SMatthew Dillon 			node->tn_size = uio->uio_offset;
78880ae59d7SMatthew Dillon 			kflags |= NOTE_EXTEND;
78980ae59d7SMatthew Dillon 		}
79080ae59d7SMatthew Dillon 		kflags |= NOTE_WRITE;
7917a2de9a4SMatthew Dillon 
7927a2de9a4SMatthew Dillon 		/*
7934eb0bb82SMatthew Dillon 		 * UIO_NOCOPY is a sensitive state due to potentially being
7944eb0bb82SMatthew Dillon 		 * issued from the pageout daemon while in a low-memory
7954eb0bb82SMatthew Dillon 		 * situation.  However, in order to cluster the I/O nicely
7964eb0bb82SMatthew Dillon 		 * (e.g. 64KB+ writes instead of 16KB writes), we still try
7974eb0bb82SMatthew Dillon 		 * to follow the same semantics that any other filesystem
7984eb0bb82SMatthew Dillon 		 * might use.
7997a2de9a4SMatthew Dillon 		 *
800d86d27a8SMatthew Dillon 		 * For the normal case we buwrite(), dirtying the underlying
801d86d27a8SMatthew Dillon 		 * VM pages instead of dirtying the buffer and releasing the
802d86d27a8SMatthew Dillon 		 * buffer as a clean buffer.  This allows tmpfs to use
803d86d27a8SMatthew Dillon 		 * essentially all available memory to cache file data.
804d86d27a8SMatthew Dillon 		 * If we used bdwrite() the buffer cache would wind up
805d86d27a8SMatthew Dillon 		 * flushing the data to swap too quickly.
806527cd77dSMatthew Dillon 		 *
8073c54bb74SMatthew Dillon 		 * But because tmpfs can seriously load the VM system we
8083c54bb74SMatthew Dillon 		 * fall-back to using bdwrite() when free memory starts
8093c54bb74SMatthew Dillon 		 * to get low.  This shifts the load away from the VM system
8103c54bb74SMatthew Dillon 		 * and makes tmpfs act more like a normal filesystem with
8113c54bb74SMatthew Dillon 		 * regards to disk activity.
8123c54bb74SMatthew Dillon 		 *
813527cd77dSMatthew Dillon 		 * tmpfs pretty much fiddles directly with the VM
814527cd77dSMatthew Dillon 		 * system, don't let it exhaust it or we won't play
815527cd77dSMatthew Dillon 		 * nice with other processes.  Only do this if the
816527cd77dSMatthew Dillon 		 * VOP is coming from a normal read/write.  The VM system
817527cd77dSMatthew Dillon 		 * handles the case for UIO_NOCOPY.
8187a2de9a4SMatthew Dillon 		 */
8193c54bb74SMatthew Dillon 		bp->b_flags |= B_CLUSTEROK;
820d86d27a8SMatthew Dillon 		if (uio->uio_segflg == UIO_NOCOPY) {
8216ff1f162SMatthew Dillon 			/*
822108fbfcbSMatthew Dillon 			 * Flush from the pageout daemon, deal with potentially
823108fbfcbSMatthew Dillon 			 * very heavy tmpfs write activity causing long stalls
824108fbfcbSMatthew Dillon 			 * in the pageout daemon before pages get to free/cache.
825108fbfcbSMatthew Dillon 			 *
826108fbfcbSMatthew Dillon 			 * We have to be careful not to bypass the page queues
827108fbfcbSMatthew Dillon 			 * entirely or we can cause write-read thrashing and
828108fbfcbSMatthew Dillon 			 * delay the paging of data that is more pageable then
829108fbfcbSMatthew Dillon 			 * our current data.
8306ff1f162SMatthew Dillon 			 *
8316ff1f162SMatthew Dillon 			 * (a) Under severe pressure setting B_DIRECT will
8326ff1f162SMatthew Dillon 			 *     cause a buffer release to try to free the
8336ff1f162SMatthew Dillon 			 *     underlying pages.
8346ff1f162SMatthew Dillon 			 *
835108fbfcbSMatthew Dillon 			 * (b) Under modest memory pressure the B_AGE flag
836108fbfcbSMatthew Dillon 			 *     we retire the buffer and its underlying pages
837108fbfcbSMatthew Dillon 			 *     more quickly than normal.
838108fbfcbSMatthew Dillon 			 *
839108fbfcbSMatthew Dillon 			 *     We could also force this by setting B_NOTMETA
840108fbfcbSMatthew Dillon 			 *     but that might have other unintended side-
841108fbfcbSMatthew Dillon 			 *     effects (e.g. setting PG_NOTMETA on the VM page).
8426ff1f162SMatthew Dillon 			 *
8434eb0bb82SMatthew Dillon 			 * (c) For the pageout->putpages->generic_putpages->
8444eb0bb82SMatthew Dillon 			 *     UIO_NOCOPY-write (here), issuing an immediate
8454eb0bb82SMatthew Dillon 			 *     write prevents any real clustering from
8464eb0bb82SMatthew Dillon 			 *     happening because the buffers probably aren't
8474eb0bb82SMatthew Dillon 			 *     (yet) marked dirty, or lost due to prior use
8484eb0bb82SMatthew Dillon 			 *     of buwrite().  Try to use the normal
8494eb0bb82SMatthew Dillon 			 *     cluster_write() mechanism for performance.
8504eb0bb82SMatthew Dillon 			 *
8516ff1f162SMatthew Dillon 			 * Hopefully this will unblock the VM system more
8526ff1f162SMatthew Dillon 			 * quickly under extreme tmpfs write load.
8536ff1f162SMatthew Dillon 			 */
854307bf766SMatthew Dillon 			if (tmpfs_bufcache_mode >= 2) {
855e91e64c7SMatthew Dillon 				if (vm_paging_min_dnc(vm_page_free_hysteresis))
856108fbfcbSMatthew Dillon 					bp->b_flags |= B_DIRECT | B_TTC;
857e91e64c7SMatthew Dillon 				if (vm_pages_needed || vm_paging_start(0))
858108fbfcbSMatthew Dillon 					bp->b_flags |= B_AGE;
859108fbfcbSMatthew Dillon 			}
860307bf766SMatthew Dillon 			bp->b_flags |= B_RELBUF;
8613c54bb74SMatthew Dillon 			bp->b_act_count = 0;	/* buffer->deactivate pgs */
8624eb0bb82SMatthew Dillon 			if (tmpfs_cluster_wr_enable &&
8634eb0bb82SMatthew Dillon 			    (ap->a_ioflag & (IO_SYNC | IO_DIRECT)) == 0) {
8644eb0bb82SMatthew Dillon 				cluster_write(bp, node->tn_size,
8659cd86db5SMatthew Dillon 					      node->tn_blksize, seqcount);
8664eb0bb82SMatthew Dillon 			} else {
8673c54bb74SMatthew Dillon 				cluster_awrite(bp);
8684eb0bb82SMatthew Dillon 			}
869e91e64c7SMatthew Dillon 		} else if (vm_paging_min() ||
870e91e64c7SMatthew Dillon 			   ((vm_pages_needed || vm_paging_start(0)) &&
871307bf766SMatthew Dillon 			    tmpfs_bufcache_mode >= 1)) {
8726ff1f162SMatthew Dillon 			/*
873ca1197e2SMatthew Dillon 			 * If the pageout daemon is running we cycle the
874ca1197e2SMatthew Dillon 			 * write through the buffer cache normally to
875ca1197e2SMatthew Dillon 			 * pipeline the flush, thus avoiding adding any
876ca1197e2SMatthew Dillon 			 * more memory pressure to the pageout daemon.
8776ff1f162SMatthew Dillon 			 */
8783c54bb74SMatthew Dillon 			bp->b_act_count = 0;	/* buffer->deactivate pgs */
879cfd59c5aSMatthew Dillon 			if (tmpfs_cluster_wr_enable) {
880cfd59c5aSMatthew Dillon 				cluster_write(bp, node->tn_size,
8819cd86db5SMatthew Dillon 					      node->tn_blksize, seqcount);
882cfd59c5aSMatthew Dillon 			} else {
8833c54bb74SMatthew Dillon 				bdwrite(bp);
884cfd59c5aSMatthew Dillon 			}
885d89ce96aSMatthew Dillon 		} else {
8866ff1f162SMatthew Dillon 			/*
8876ff1f162SMatthew Dillon 			 * Otherwise run the buffer directly through to the
888cfd59c5aSMatthew Dillon 			 * backing VM store, leaving the buffer clean so
889cfd59c5aSMatthew Dillon 			 * buffer limits do not force early flushes to swap.
8906ff1f162SMatthew Dillon 			 */
8919fc94b5fSMatthew Dillon 			buwrite(bp);
8923c54bb74SMatthew Dillon 			/*vm_wait_nominal();*/
893d89ce96aSMatthew Dillon 		}
8949fc94b5fSMatthew Dillon 
8957a2de9a4SMatthew Dillon 		if (bp->b_error) {
8962cd8c774SRumko 			kprintf("tmpfs_write bwrite error %d\n", bp->b_error);
8977a2de9a4SMatthew Dillon 			break;
8987a2de9a4SMatthew Dillon 		}
8997a2de9a4SMatthew Dillon 	}
9007a2de9a4SMatthew Dillon 
9017a2de9a4SMatthew Dillon 	if (error) {
90280ae59d7SMatthew Dillon 		if (extended) {
9037a2de9a4SMatthew Dillon 			(void)tmpfs_reg_resize(vp, oldsize, trivial);
90480ae59d7SMatthew Dillon 			kflags &= ~NOTE_EXTEND;
90580ae59d7SMatthew Dillon 		}
90680ae59d7SMatthew Dillon 		goto done;
9077a2de9a4SMatthew Dillon 	}
9087a2de9a4SMatthew Dillon 
909fc1a9118SMatthew Dillon 	/*
910fc1a9118SMatthew Dillon 	 * Currently we don't set the mtime on files modified via mmap()
911fc1a9118SMatthew Dillon 	 * because we can't tell the difference between those modifications
912fc1a9118SMatthew Dillon 	 * and an attempt by the pageout daemon to flush tmpfs pages to
913fc1a9118SMatthew Dillon 	 * swap.
914fc1a9118SMatthew Dillon 	 *
915fc1a9118SMatthew Dillon 	 * This is because in order to defer flushes as long as possible
916fc1a9118SMatthew Dillon 	 * buwrite() works by marking the underlying VM pages dirty in
917fc1a9118SMatthew Dillon 	 * order to be able to dispose of the buffer cache buffer without
918fc1a9118SMatthew Dillon 	 * flushing it.
919fc1a9118SMatthew Dillon 	 */
920fa4a12c4SMatthew Dillon 	if (uio->uio_segflg == UIO_NOCOPY) {
921fa4a12c4SMatthew Dillon 		if (vp->v_flag & VLASTWRITETS) {
922723be147SMatthew Dillon 			node->tn_mtime = vp->v_lastwrite_ts.tv_sec;
923723be147SMatthew Dillon 			node->tn_mtimensec = vp->v_lastwrite_ts.tv_nsec;
924723be147SMatthew Dillon 		}
925fa4a12c4SMatthew Dillon 	} else {
926fa4a12c4SMatthew Dillon 		node->tn_status |= TMPFS_NODE_MODIFIED;
927fa4a12c4SMatthew Dillon 		vclrflags(vp, VLASTWRITETS);
928fa4a12c4SMatthew Dillon 	}
929723be147SMatthew Dillon 
930fc1a9118SMatthew Dillon 	if (extended)
931fc1a9118SMatthew Dillon 		node->tn_status |= TMPFS_NODE_CHANGED;
9327a2de9a4SMatthew Dillon 
9337a2de9a4SMatthew Dillon 	if (node->tn_mode & (S_ISUID | S_ISGID)) {
934*2b3f93eaSMatthew Dillon 		if (caps_priv_check(ap->a_cred, SYSCAP_NOVFS_RETAINSUGID))
9357a2de9a4SMatthew Dillon 			node->tn_mode &= ~(S_ISUID | S_ISGID);
9367a2de9a4SMatthew Dillon 	}
93780ae59d7SMatthew Dillon done:
938b37a7c00SMatthew Dillon 	TMPFS_NODE_UNLOCK(node);
939b37a7c00SMatthew Dillon 	if (kflags)
94080ae59d7SMatthew Dillon 		tmpfs_knote(vp, kflags);
9411be4932cSVenkatesh Srinivas 
9427a2de9a4SMatthew Dillon 	return(error);
9437a2de9a4SMatthew Dillon }
9447a2de9a4SMatthew Dillon 
9457a2de9a4SMatthew Dillon static int
tmpfs_advlock(struct vop_advlock_args * ap)9467a2de9a4SMatthew Dillon tmpfs_advlock(struct vop_advlock_args *ap)
9477a2de9a4SMatthew Dillon {
9487a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
9497a2de9a4SMatthew Dillon 	struct vnode *vp = ap->a_vp;
950aa1adbf0SMatthew Dillon 	int error;
9517a2de9a4SMatthew Dillon 
9527a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
953aa1adbf0SMatthew Dillon 	error = (lf_advlock(ap, &node->tn_advlock, node->tn_size));
954aa1adbf0SMatthew Dillon 
955aa1adbf0SMatthew Dillon 	return (error);
9567a2de9a4SMatthew Dillon }
9577a2de9a4SMatthew Dillon 
958fc1a9118SMatthew Dillon /*
959fc1a9118SMatthew Dillon  * The strategy function is typically only called when memory pressure
960fc1a9118SMatthew Dillon  * forces the system to attempt to pageout pages.  It can also be called
961fc1a9118SMatthew Dillon  * by [n]vtruncbuf() when a truncation cuts a page in half.  Normal write
962fc1a9118SMatthew Dillon  * operations
96348db4e20SMatthew Dillon  *
96448db4e20SMatthew Dillon  * We set VKVABIO for VREG files so bp->b_data may not be synchronized to
96548db4e20SMatthew Dillon  * our cpu.  swap_pager_strategy() is all we really use, and it directly
96648db4e20SMatthew Dillon  * supports this.
967fc1a9118SMatthew Dillon  */
9687a2de9a4SMatthew Dillon static int
tmpfs_strategy(struct vop_strategy_args * ap)9697a2de9a4SMatthew Dillon tmpfs_strategy(struct vop_strategy_args *ap)
9707a2de9a4SMatthew Dillon {
9717a2de9a4SMatthew Dillon 	struct bio *bio = ap->a_bio;
9728f9ba07bSMatthew Dillon 	struct bio *nbio;
9739fc94b5fSMatthew Dillon 	struct buf *bp = bio->bio_buf;
9747a2de9a4SMatthew Dillon 	struct vnode *vp = ap->a_vp;
9757a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
9767a2de9a4SMatthew Dillon 	vm_object_t uobj;
9778f9ba07bSMatthew Dillon 	vm_page_t m;
9788f9ba07bSMatthew Dillon 	int i;
9797a2de9a4SMatthew Dillon 
9809fc94b5fSMatthew Dillon 	if (vp->v_type != VREG) {
9819fc94b5fSMatthew Dillon 		bp->b_resid = bp->b_bcount;
9829fc94b5fSMatthew Dillon 		bp->b_flags |= B_ERROR | B_INVAL;
9839fc94b5fSMatthew Dillon 		bp->b_error = EINVAL;
9849fc94b5fSMatthew Dillon 		biodone(bio);
9859fc94b5fSMatthew Dillon 		return(0);
9869fc94b5fSMatthew Dillon 	}
9877a2de9a4SMatthew Dillon 
9887a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
9897a2de9a4SMatthew Dillon 
9907a2de9a4SMatthew Dillon 	uobj = node->tn_reg.tn_aobj;
9919fc94b5fSMatthew Dillon 
9927a2de9a4SMatthew Dillon 	/*
993fc1a9118SMatthew Dillon 	 * Don't bother flushing to swap if there is no swap, just
994fc1a9118SMatthew Dillon 	 * ensure that the pages are marked as needing a commit (still).
9957a2de9a4SMatthew Dillon 	 */
9968f9ba07bSMatthew Dillon 	if (bp->b_cmd == BUF_CMD_WRITE && vm_swap_size == 0) {
9978f9ba07bSMatthew Dillon 		for (i = 0; i < bp->b_xio.xio_npages; ++i) {
9988f9ba07bSMatthew Dillon 			m = bp->b_xio.xio_pages[i];
999fc1a9118SMatthew Dillon 			vm_page_need_commit(m);
10008f9ba07bSMatthew Dillon 		}
10018f9ba07bSMatthew Dillon 		bp->b_resid = 0;
10028f9ba07bSMatthew Dillon 		bp->b_error = 0;
10038f9ba07bSMatthew Dillon 		biodone(bio);
10048f9ba07bSMatthew Dillon 	} else {
1005108fbfcbSMatthew Dillon #if 0
1006cfd59c5aSMatthew Dillon 		/*
1007108fbfcbSMatthew Dillon 		 * XXX removed, this does not work well because under heavy
1008108fbfcbSMatthew Dillon 		 * filesystem loads it often
1009108fbfcbSMatthew Dillon 		 * forces the data to be read right back in again after
1010108fbfcbSMatthew Dillon 		 * being written due to bypassing normal LRU operation.
1011108fbfcbSMatthew Dillon 		 *
1012cfd59c5aSMatthew Dillon 		 * Tell the buffer cache to try to recycle the pages
1013cfd59c5aSMatthew Dillon 		 * to PQ_CACHE on release.
1014cfd59c5aSMatthew Dillon 		 */
1015cfd59c5aSMatthew Dillon 		if (tmpfs_bufcache_mode >= 2 ||
1016cfd59c5aSMatthew Dillon 		    (tmpfs_bufcache_mode == 1 && vm_paging_needed(0))) {
1017cfd59c5aSMatthew Dillon 			bp->b_flags |= B_TTC;
1018cfd59c5aSMatthew Dillon 		}
1019108fbfcbSMatthew Dillon #endif
10208f9ba07bSMatthew Dillon 		nbio = push_bio(bio);
10218f9ba07bSMatthew Dillon 		nbio->bio_done = tmpfs_strategy_done;
10228f9ba07bSMatthew Dillon 		nbio->bio_offset = bio->bio_offset;
10238f9ba07bSMatthew Dillon 		swap_pager_strategy(uobj, nbio);
10248f9ba07bSMatthew Dillon 	}
10257a2de9a4SMatthew Dillon 	return 0;
10267a2de9a4SMatthew Dillon }
10277a2de9a4SMatthew Dillon 
10288f9ba07bSMatthew Dillon /*
1029fc1a9118SMatthew Dillon  * If we were unable to commit the pages to swap make sure they are marked
1030fc1a9118SMatthew Dillon  * as needing a commit (again).  If we were, clear the flag to allow the
1031fc1a9118SMatthew Dillon  * pages to be freed.
1032a1b829f2SMatthew Dillon  *
1033a1b829f2SMatthew Dillon  * Do not error-out the buffer.  In particular, vinvalbuf() needs to
1034a1b829f2SMatthew Dillon  * always work.
10358f9ba07bSMatthew Dillon  */
10368f9ba07bSMatthew Dillon static void
tmpfs_strategy_done(struct bio * bio)10378f9ba07bSMatthew Dillon tmpfs_strategy_done(struct bio *bio)
10388f9ba07bSMatthew Dillon {
10398f9ba07bSMatthew Dillon 	struct buf *bp;
10408f9ba07bSMatthew Dillon 	vm_page_t m;
10418f9ba07bSMatthew Dillon 	int i;
10428f9ba07bSMatthew Dillon 
10438f9ba07bSMatthew Dillon 	bp = bio->bio_buf;
10448f9ba07bSMatthew Dillon 
1045fc1a9118SMatthew Dillon 	if (bp->b_flags & B_ERROR) {
10468f9ba07bSMatthew Dillon 		bp->b_flags &= ~B_ERROR;
10478f9ba07bSMatthew Dillon 		bp->b_error = 0;
10488f9ba07bSMatthew Dillon 		bp->b_resid = 0;
10498f9ba07bSMatthew Dillon 		for (i = 0; i < bp->b_xio.xio_npages; ++i) {
10508f9ba07bSMatthew Dillon 			m = bp->b_xio.xio_pages[i];
1051fc1a9118SMatthew Dillon 			vm_page_need_commit(m);
1052fc1a9118SMatthew Dillon 		}
1053fc1a9118SMatthew Dillon 	} else {
1054fc1a9118SMatthew Dillon 		for (i = 0; i < bp->b_xio.xio_npages; ++i) {
1055fc1a9118SMatthew Dillon 			m = bp->b_xio.xio_pages[i];
1056fc1a9118SMatthew Dillon 			vm_page_clear_commit(m);
10578f9ba07bSMatthew Dillon 		}
10588f9ba07bSMatthew Dillon 	}
10598f9ba07bSMatthew Dillon 	bio = pop_bio(bio);
10608f9ba07bSMatthew Dillon 	biodone(bio);
10618f9ba07bSMatthew Dillon }
10628f9ba07bSMatthew Dillon 
10634eb0bb82SMatthew Dillon /*
10644eb0bb82SMatthew Dillon  * To make write clustering work well make the backing store look
10654eb0bb82SMatthew Dillon  * contiguous to the cluster_*() code.  The swap_strategy() function
10664eb0bb82SMatthew Dillon  * will take it from there.
10674eb0bb82SMatthew Dillon  *
10684eb0bb82SMatthew Dillon  * Use MAXBSIZE-sized chunks as a micro-optimization to make random
10694eb0bb82SMatthew Dillon  * flushes leave full-sized gaps.
10704eb0bb82SMatthew Dillon  */
10717a2de9a4SMatthew Dillon static int
tmpfs_bmap(struct vop_bmap_args * ap)10727a2de9a4SMatthew Dillon tmpfs_bmap(struct vop_bmap_args *ap)
10737a2de9a4SMatthew Dillon {
10747a2de9a4SMatthew Dillon 	if (ap->a_doffsetp != NULL)
10757a2de9a4SMatthew Dillon 		*ap->a_doffsetp = ap->a_loffset;
10767a2de9a4SMatthew Dillon 	if (ap->a_runp != NULL)
10774eb0bb82SMatthew Dillon 		*ap->a_runp = MAXBSIZE - (ap->a_loffset & (MAXBSIZE - 1));
10787a2de9a4SMatthew Dillon 	if (ap->a_runb != NULL)
10794eb0bb82SMatthew Dillon 		*ap->a_runb = ap->a_loffset & (MAXBSIZE - 1);
10807a2de9a4SMatthew Dillon 
10817a2de9a4SMatthew Dillon 	return 0;
10827a2de9a4SMatthew Dillon }
10839fc94b5fSMatthew Dillon 
10847a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
10857a2de9a4SMatthew Dillon 
10867a2de9a4SMatthew Dillon static int
tmpfs_nremove(struct vop_nremove_args * ap)1087ba25006aSSascha Wildner tmpfs_nremove(struct vop_nremove_args *ap)
10887a2de9a4SMatthew Dillon {
1089ba25006aSSascha Wildner 	struct vnode *dvp = ap->a_dvp;
1090ba25006aSSascha Wildner 	struct namecache *ncp = ap->a_nch->ncp;
10919fc94b5fSMatthew Dillon 	struct vnode *vp;
10927a2de9a4SMatthew Dillon 	int error;
10937a2de9a4SMatthew Dillon 	struct tmpfs_dirent *de;
10947a2de9a4SMatthew Dillon 	struct tmpfs_mount *tmp;
10957a2de9a4SMatthew Dillon 	struct tmpfs_node *dnode;
10967a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
1097aa1adbf0SMatthew Dillon 
10989fc94b5fSMatthew Dillon 	/*
1099ba25006aSSascha Wildner 	 * We have to acquire the vp from ap->a_nch because we will likely
1100a1fa5d8dSMatthew Dillon 	 * unresolve the namecache entry, and a vrele/vput is needed to
1101a1fa5d8dSMatthew Dillon 	 * trigger the tmpfs_inactive/tmpfs_reclaim sequence.
1102a1fa5d8dSMatthew Dillon 	 *
1103a1fa5d8dSMatthew Dillon 	 * We have to use vget to clear any inactive state on the vnode,
1104a1fa5d8dSMatthew Dillon 	 * otherwise the vnode may remain inactive and thus tmpfs_inactive
1105a1fa5d8dSMatthew Dillon 	 * will not get called when we release it.
11069fc94b5fSMatthew Dillon 	 */
1107ba25006aSSascha Wildner 	error = cache_vget(ap->a_nch, ap->a_cred, LK_SHARED, &vp);
1108aa1adbf0SMatthew Dillon 	KKASSERT(vp->v_mount == dvp->v_mount);
11099fc94b5fSMatthew Dillon 	KKASSERT(error == 0);
1110a1fa5d8dSMatthew Dillon 	vn_unlock(vp);
11117a2de9a4SMatthew Dillon 
11127a2de9a4SMatthew Dillon 	if (vp->v_type == VDIR) {
11137a2de9a4SMatthew Dillon 		error = EISDIR;
1114ff837cd5SMatthew Dillon 		goto out2;
11157a2de9a4SMatthew Dillon 	}
11167a2de9a4SMatthew Dillon 
11177a2de9a4SMatthew Dillon 	dnode = VP_TO_TMPFS_DIR(dvp);
11187a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
11197a2de9a4SMatthew Dillon 	tmp = VFS_TO_TMPFS(vp->v_mount);
1120ff837cd5SMatthew Dillon 
1121ff837cd5SMatthew Dillon 	TMPFS_NODE_LOCK(dnode);
112278ce1036SMatthew Dillon 	TMPFS_NODE_LOCK(node);
11237a2de9a4SMatthew Dillon 	de = tmpfs_dir_lookup(dnode, node, ncp);
11247a2de9a4SMatthew Dillon 	if (de == NULL) {
11257a2de9a4SMatthew Dillon 		error = ENOENT;
112678ce1036SMatthew Dillon 		TMPFS_NODE_UNLOCK(node);
1127837afe1aSMatthew Dillon 		TMPFS_NODE_UNLOCK(dnode);
11287a2de9a4SMatthew Dillon 		goto out;
11297a2de9a4SMatthew Dillon 	}
11307a2de9a4SMatthew Dillon 
11317a2de9a4SMatthew Dillon 	/* Files marked as immutable or append-only cannot be deleted. */
11327a2de9a4SMatthew Dillon 	if ((node->tn_flags & (IMMUTABLE | APPEND | NOUNLINK)) ||
11337a2de9a4SMatthew Dillon 	    (dnode->tn_flags & APPEND)) {
11347a2de9a4SMatthew Dillon 		error = EPERM;
113578ce1036SMatthew Dillon 		TMPFS_NODE_UNLOCK(node);
1136837afe1aSMatthew Dillon 		TMPFS_NODE_UNLOCK(dnode);
11377a2de9a4SMatthew Dillon 		goto out;
11387a2de9a4SMatthew Dillon 	}
11397a2de9a4SMatthew Dillon 
11407a2de9a4SMatthew Dillon 	/* Remove the entry from the directory; as it is a file, we do not
11417a2de9a4SMatthew Dillon 	 * have to change the number of hard links of the directory. */
1142307bf766SMatthew Dillon 	tmpfs_dir_detach_locked(dnode, de);
1143837afe1aSMatthew Dillon 	TMPFS_NODE_UNLOCK(dnode);
11447a2de9a4SMatthew Dillon 
11457a2de9a4SMatthew Dillon 	/* Free the directory entry we just deleted.  Note that the node
11467a2de9a4SMatthew Dillon 	 * referred by it will not be removed until the vnode is really
11477a2de9a4SMatthew Dillon 	 * reclaimed. */
11480786baf1SMatthew Dillon 	tmpfs_free_dirent(tmp, de);
11497a2de9a4SMatthew Dillon 
115078ce1036SMatthew Dillon 	if (node->tn_links > 0)
115191321284SMatthew Dillon 		node->tn_status |= TMPFS_NODE_CHANGED;
11527a2de9a4SMatthew Dillon 	TMPFS_NODE_UNLOCK(node);
11537a2de9a4SMatthew Dillon 
1154ba25006aSSascha Wildner 	cache_unlink(ap->a_nch);
115580ae59d7SMatthew Dillon 	tmpfs_knote(vp, NOTE_DELETE);
11567a2de9a4SMatthew Dillon 	error = 0;
11577a2de9a4SMatthew Dillon 
11587a2de9a4SMatthew Dillon out:
1159ff837cd5SMatthew Dillon 	if (error == 0)
1160ff837cd5SMatthew Dillon 		tmpfs_knote(dvp, NOTE_WRITE);
1161ff837cd5SMatthew Dillon out2:
11629fc94b5fSMatthew Dillon 	vrele(vp);
11637a2de9a4SMatthew Dillon 
11647a2de9a4SMatthew Dillon 	return error;
11657a2de9a4SMatthew Dillon }
11667a2de9a4SMatthew Dillon 
11677a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
11687a2de9a4SMatthew Dillon 
11697a2de9a4SMatthew Dillon static int
tmpfs_nlink(struct vop_nlink_args * ap)1170ba25006aSSascha Wildner tmpfs_nlink(struct vop_nlink_args *ap)
11717a2de9a4SMatthew Dillon {
1172ba25006aSSascha Wildner 	struct vnode *dvp = ap->a_dvp;
1173ba25006aSSascha Wildner 	struct vnode *vp = ap->a_vp;
1174307bf766SMatthew Dillon 	struct tmpfs_mount *tmp = VFS_TO_TMPFS(vp->v_mount);
1175ba25006aSSascha Wildner 	struct namecache *ncp = ap->a_nch->ncp;
11767a2de9a4SMatthew Dillon 	struct tmpfs_dirent *de;
11777a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
117822d3b394SMatthew Dillon 	struct tmpfs_node *dnode;
117922d3b394SMatthew Dillon 	int error;
11807a2de9a4SMatthew Dillon 
11817a2de9a4SMatthew Dillon 	KKASSERT(dvp != vp); /* XXX When can this be false? */
11827a2de9a4SMatthew Dillon 
11837a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
118422d3b394SMatthew Dillon 	dnode = VP_TO_TMPFS_NODE(dvp);
1185ff837cd5SMatthew Dillon 	TMPFS_NODE_LOCK(dnode);
11867a2de9a4SMatthew Dillon 
11877a2de9a4SMatthew Dillon 	/* XXX: Why aren't the following two tests done by the caller? */
11887a2de9a4SMatthew Dillon 
11897a2de9a4SMatthew Dillon 	/* Hard links of directories are forbidden. */
11907a2de9a4SMatthew Dillon 	if (vp->v_type == VDIR) {
11917a2de9a4SMatthew Dillon 		error = EPERM;
11927a2de9a4SMatthew Dillon 		goto out;
11937a2de9a4SMatthew Dillon 	}
11947a2de9a4SMatthew Dillon 
11957a2de9a4SMatthew Dillon 	/* Cannot create cross-device links. */
11967a2de9a4SMatthew Dillon 	if (dvp->v_mount != vp->v_mount) {
11977a2de9a4SMatthew Dillon 		error = EXDEV;
11987a2de9a4SMatthew Dillon 		goto out;
11997a2de9a4SMatthew Dillon 	}
12007a2de9a4SMatthew Dillon 
1201307bf766SMatthew Dillon 	/* Cannot hard-link into a deleted directory */
1202307bf766SMatthew Dillon 	if (dnode != tmp->tm_root && dnode->tn_dir.tn_parent == NULL) {
1203307bf766SMatthew Dillon 		error = ENOENT;
1204307bf766SMatthew Dillon 		goto out;
1205307bf766SMatthew Dillon 	}
1206307bf766SMatthew Dillon 
12077a2de9a4SMatthew Dillon 	/* Ensure that we do not overflow the maximum number of links imposed
12087a2de9a4SMatthew Dillon 	 * by the system. */
12097a2de9a4SMatthew Dillon 	KKASSERT(node->tn_links <= LINK_MAX);
1210ff837cd5SMatthew Dillon 	if (node->tn_links >= LINK_MAX) {
12117a2de9a4SMatthew Dillon 		error = EMLINK;
12127a2de9a4SMatthew Dillon 		goto out;
12137a2de9a4SMatthew Dillon 	}
12147a2de9a4SMatthew Dillon 
12157a2de9a4SMatthew Dillon 	/* We cannot create links of files marked immutable or append-only. */
12167a2de9a4SMatthew Dillon 	if (node->tn_flags & (IMMUTABLE | APPEND)) {
12177a2de9a4SMatthew Dillon 		error = EPERM;
12187a2de9a4SMatthew Dillon 		goto out;
12197a2de9a4SMatthew Dillon 	}
12207a2de9a4SMatthew Dillon 
12217a2de9a4SMatthew Dillon 	/* Allocate a new directory entry to represent the node. */
12227a2de9a4SMatthew Dillon 	error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node,
12237a2de9a4SMatthew Dillon 				   ncp->nc_name, ncp->nc_nlen, &de);
12247a2de9a4SMatthew Dillon 	if (error != 0)
12257a2de9a4SMatthew Dillon 		goto out;
12267a2de9a4SMatthew Dillon 
12277a2de9a4SMatthew Dillon 	/* Insert the new directory entry into the appropriate directory. */
1228307bf766SMatthew Dillon 	tmpfs_dir_attach_locked(dnode, de);
12297a2de9a4SMatthew Dillon 
12307a2de9a4SMatthew Dillon 	/* vp link count has changed, so update node times. */
12317a2de9a4SMatthew Dillon 
12327a2de9a4SMatthew Dillon 	TMPFS_NODE_LOCK(node);
12337a2de9a4SMatthew Dillon 	node->tn_status |= TMPFS_NODE_CHANGED;
12347a2de9a4SMatthew Dillon 	TMPFS_NODE_UNLOCK(node);
12357a2de9a4SMatthew Dillon 	tmpfs_update(vp);
12367a2de9a4SMatthew Dillon 
123780ae59d7SMatthew Dillon 	tmpfs_knote(vp, NOTE_LINK);
1238ba25006aSSascha Wildner 	cache_setunresolved(ap->a_nch);
1239ba25006aSSascha Wildner 	cache_setvp(ap->a_nch, vp);
12407a2de9a4SMatthew Dillon 	error = 0;
12417a2de9a4SMatthew Dillon 
12427a2de9a4SMatthew Dillon out:
1243ff837cd5SMatthew Dillon 	TMPFS_NODE_UNLOCK(dnode);
1244ff837cd5SMatthew Dillon 	if (error == 0)
1245ff837cd5SMatthew Dillon 		tmpfs_knote(dvp, NOTE_WRITE);
12467a2de9a4SMatthew Dillon 	return error;
12477a2de9a4SMatthew Dillon }
12487a2de9a4SMatthew Dillon 
12497a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
12507a2de9a4SMatthew Dillon 
12517a2de9a4SMatthew Dillon static int
tmpfs_nrename(struct vop_nrename_args * ap)1252ba25006aSSascha Wildner tmpfs_nrename(struct vop_nrename_args *ap)
12537a2de9a4SMatthew Dillon {
1254ba25006aSSascha Wildner 	struct vnode *fdvp = ap->a_fdvp;
1255ba25006aSSascha Wildner 	struct namecache *fncp = ap->a_fnch->ncp;
12567a2de9a4SMatthew Dillon 	struct vnode *fvp = fncp->nc_vp;
1257ba25006aSSascha Wildner 	struct vnode *tdvp = ap->a_tdvp;
1258ba25006aSSascha Wildner 	struct namecache *tncp = ap->a_tnch->ncp;
1259a1fa5d8dSMatthew Dillon 	struct vnode *tvp;
1260fd67ed24SMatthew Dillon 	struct tmpfs_dirent *de, *tde, *de2;
12617a2de9a4SMatthew Dillon 	struct tmpfs_mount *tmp;
12627a2de9a4SMatthew Dillon 	struct tmpfs_node *fdnode;
12634c154053SMatthew Dillon 	struct tmpfs_node *tdnode;
12647a2de9a4SMatthew Dillon 	struct tmpfs_node *fnode;
12657a2de9a4SMatthew Dillon 	struct tmpfs_node *tnode;
126622d3b394SMatthew Dillon 	char *newname;
1267dca262fbSMatthew Dillon 	char *oldname;
126822d3b394SMatthew Dillon 	int error;
12697a2de9a4SMatthew Dillon 
1270aa1adbf0SMatthew Dillon 	KKASSERT(fdvp->v_mount == fvp->v_mount);
1271aa1adbf0SMatthew Dillon 
1272a1fa5d8dSMatthew Dillon 	/*
1273a1fa5d8dSMatthew Dillon 	 * Because tvp can get overwritten we have to vget it instead of
1274a1fa5d8dSMatthew Dillon 	 * just vref or use it, otherwise it's VINACTIVE flag may not get
1275a1fa5d8dSMatthew Dillon 	 * cleared and the node won't get destroyed.
1276a1fa5d8dSMatthew Dillon 	 */
1277ba25006aSSascha Wildner 	error = cache_vget(ap->a_tnch, ap->a_cred, LK_SHARED, &tvp);
1278a1fa5d8dSMatthew Dillon 	if (error == 0) {
1279a1fa5d8dSMatthew Dillon 		tnode = VP_TO_TMPFS_NODE(tvp);
1280a1fa5d8dSMatthew Dillon 		vn_unlock(tvp);
1281a1fa5d8dSMatthew Dillon 	} else {
1282a1fa5d8dSMatthew Dillon 		tnode = NULL;
1283a1fa5d8dSMatthew Dillon 	}
12847a2de9a4SMatthew Dillon 
12857a2de9a4SMatthew Dillon 	/* Disallow cross-device renames.
12867a2de9a4SMatthew Dillon 	 * XXX Why isn't this done by the caller? */
12877a2de9a4SMatthew Dillon 	if (fvp->v_mount != tdvp->v_mount ||
12887a2de9a4SMatthew Dillon 	    (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
12897a2de9a4SMatthew Dillon 		error = EXDEV;
12907a2de9a4SMatthew Dillon 		goto out;
12917a2de9a4SMatthew Dillon 	}
12927a2de9a4SMatthew Dillon 
12937a2de9a4SMatthew Dillon 	tmp = VFS_TO_TMPFS(tdvp->v_mount);
12947a2de9a4SMatthew Dillon 	tdnode = VP_TO_TMPFS_DIR(tdvp);
12957a2de9a4SMatthew Dillon 
12967a2de9a4SMatthew Dillon 	/* If source and target are the same file, there is nothing to do. */
12977a2de9a4SMatthew Dillon 	if (fvp == tvp) {
12987a2de9a4SMatthew Dillon 		error = 0;
12997a2de9a4SMatthew Dillon 		goto out;
13007a2de9a4SMatthew Dillon 	}
13017a2de9a4SMatthew Dillon 
13027a2de9a4SMatthew Dillon 	fdnode = VP_TO_TMPFS_DIR(fdvp);
13037a2de9a4SMatthew Dillon 	fnode = VP_TO_TMPFS_NODE(fvp);
13044c154053SMatthew Dillon 
13054c154053SMatthew Dillon 	tmpfs_lock4(fdnode, tdnode, fnode, tnode);
13064c154053SMatthew Dillon 
1307307bf766SMatthew Dillon 	/*
1308307bf766SMatthew Dillon 	 * Cannot rename into a deleted directory
1309307bf766SMatthew Dillon 	 */
1310307bf766SMatthew Dillon 	if (tdnode != tmp->tm_root && tdnode->tn_dir.tn_parent == NULL) {
1311307bf766SMatthew Dillon 		error = ENOENT;
1312307bf766SMatthew Dillon 		goto out_locked;
1313307bf766SMatthew Dillon 	}
13147a2de9a4SMatthew Dillon 
13157a2de9a4SMatthew Dillon 	/* Avoid manipulating '.' and '..' entries. */
1316307bf766SMatthew Dillon 	de = tmpfs_dir_lookup(fdnode, fnode, fncp);
13177a2de9a4SMatthew Dillon 	if (de == NULL) {
13187a2de9a4SMatthew Dillon 		error = ENOENT;
13197a2de9a4SMatthew Dillon 		goto out_locked;
13207a2de9a4SMatthew Dillon 	}
13217a2de9a4SMatthew Dillon 	KKASSERT(de->td_node == fnode);
13227a2de9a4SMatthew Dillon 
1323dca262fbSMatthew Dillon 	/*
1324dca262fbSMatthew Dillon 	 * If replacing an entry in the target directory and that entry
1325dca262fbSMatthew Dillon 	 * is a directory, it must be empty.
1326dca262fbSMatthew Dillon 	 *
13277a2de9a4SMatthew Dillon 	 * Kern_rename gurantees the destination to be a directory
1328dca262fbSMatthew Dillon 	 * if the source is one (it does?).
1329dca262fbSMatthew Dillon 	 */
13307a2de9a4SMatthew Dillon 	if (tvp != NULL) {
13317a2de9a4SMatthew Dillon 		KKASSERT(tnode != NULL);
13327a2de9a4SMatthew Dillon 
13337a2de9a4SMatthew Dillon 		if ((tnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
13347a2de9a4SMatthew Dillon 		    (tdnode->tn_flags & (APPEND | IMMUTABLE))) {
13357a2de9a4SMatthew Dillon 			error = EPERM;
13367a2de9a4SMatthew Dillon 			goto out_locked;
13377a2de9a4SMatthew Dillon 		}
13387a2de9a4SMatthew Dillon 
13397a2de9a4SMatthew Dillon 		if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
13407a2de9a4SMatthew Dillon 			if (tnode->tn_size > 0) {
13417a2de9a4SMatthew Dillon 				error = ENOTEMPTY;
13427a2de9a4SMatthew Dillon 				goto out_locked;
13437a2de9a4SMatthew Dillon 			}
13447a2de9a4SMatthew Dillon 		} else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) {
13457a2de9a4SMatthew Dillon 			error = ENOTDIR;
13467a2de9a4SMatthew Dillon 			goto out_locked;
13477a2de9a4SMatthew Dillon 		} else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) {
13487a2de9a4SMatthew Dillon 			error = EISDIR;
13497a2de9a4SMatthew Dillon 			goto out_locked;
13507a2de9a4SMatthew Dillon 		} else {
13517a2de9a4SMatthew Dillon 			KKASSERT(fnode->tn_type != VDIR &&
13527a2de9a4SMatthew Dillon 				tnode->tn_type != VDIR);
13537a2de9a4SMatthew Dillon 		}
13547a2de9a4SMatthew Dillon 	}
13557a2de9a4SMatthew Dillon 
1356dca262fbSMatthew Dillon 	if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
1357dca262fbSMatthew Dillon 	    (fdnode->tn_flags & (APPEND | IMMUTABLE))) {
13587a2de9a4SMatthew Dillon 		error = EPERM;
13597a2de9a4SMatthew Dillon 		goto out_locked;
13607a2de9a4SMatthew Dillon 	}
13617a2de9a4SMatthew Dillon 
1362dca262fbSMatthew Dillon 	/*
1363dca262fbSMatthew Dillon 	 * Ensure that we have enough memory to hold the new name, if it
1364dca262fbSMatthew Dillon 	 * has to be changed.
1365dca262fbSMatthew Dillon 	 */
13667a2de9a4SMatthew Dillon 	if (fncp->nc_nlen != tncp->nc_nlen ||
13677a2de9a4SMatthew Dillon 	    bcmp(fncp->nc_name, tncp->nc_name, fncp->nc_nlen) != 0) {
1368d00cd01cSVenkatesh Srinivas 		newname = kmalloc(tncp->nc_nlen + 1, tmp->tm_name_zone,
136942f6f6b1SVenkatesh Srinivas 				  M_WAITOK | M_NULLOK);
137042f6f6b1SVenkatesh Srinivas 		if (newname == NULL) {
137142f6f6b1SVenkatesh Srinivas 			error = ENOSPC;
137242f6f6b1SVenkatesh Srinivas 			goto out_locked;
137342f6f6b1SVenkatesh Srinivas 		}
13747a2de9a4SMatthew Dillon 		bcopy(tncp->nc_name, newname, tncp->nc_nlen);
13757a2de9a4SMatthew Dillon 		newname[tncp->nc_nlen] = '\0';
1376dca262fbSMatthew Dillon 	} else {
1377dca262fbSMatthew Dillon 		newname = NULL;
1378dca262fbSMatthew Dillon 	}
1379dca262fbSMatthew Dillon 
1380dca262fbSMatthew Dillon 	/*
1381dca262fbSMatthew Dillon 	 * Unlink entry from source directory.  Note that the kernel has
1382dca262fbSMatthew Dillon 	 * already checked for illegal recursion cases (renaming a directory
1383dca262fbSMatthew Dillon 	 * into a subdirectory of itself).
1384dca262fbSMatthew Dillon 	 */
13856e0c5aabSMatthew Dillon 	if (fdnode != tdnode) {
1386307bf766SMatthew Dillon 		tmpfs_dir_detach_locked(fdnode, de);
13876e0c5aabSMatthew Dillon 	} else {
1388e195a8feSMatthew Dillon 		/* XXX depend on namecache lock */
1389e195a8feSMatthew Dillon 		KKASSERT(de == tmpfs_dir_lookup(fdnode, fnode, fncp));
139029ca4fd6SJohannes Hofmann 		RB_REMOVE(tmpfs_dirtree, &fdnode->tn_dir.tn_dirtree, de);
1391f5f22af6SMatthew Dillon 		RB_REMOVE(tmpfs_dirtree_cookie,
1392f5f22af6SMatthew Dillon 			  &fdnode->tn_dir.tn_cookietree, de);
139329ca4fd6SJohannes Hofmann 	}
1394dca262fbSMatthew Dillon 
1395dca262fbSMatthew Dillon 	/*
1396dca262fbSMatthew Dillon 	 * Handle any name change.  Swap with newname, we will
1397dca262fbSMatthew Dillon 	 * deallocate it at the end.
1398dca262fbSMatthew Dillon 	 */
1399dca262fbSMatthew Dillon 	if (newname != NULL) {
1400dca262fbSMatthew Dillon 		oldname = de->td_name;
14017a2de9a4SMatthew Dillon 		de->td_name = newname;
1402dca262fbSMatthew Dillon 		de->td_namelen = (uint16_t)tncp->nc_nlen;
1403dca262fbSMatthew Dillon 		newname = oldname;
1404dca262fbSMatthew Dillon 	}
1405dca262fbSMatthew Dillon 
1406dca262fbSMatthew Dillon 	/*
140729ca4fd6SJohannes Hofmann 	 * If we are overwriting an entry, we have to remove the old one
140829ca4fd6SJohannes Hofmann 	 * from the target directory.
140929ca4fd6SJohannes Hofmann 	 */
141029ca4fd6SJohannes Hofmann 	if (tvp != NULL) {
141129ca4fd6SJohannes Hofmann 		/* Remove the old entry from the target directory. */
141229ca4fd6SJohannes Hofmann 		tde = tmpfs_dir_lookup(tdnode, tnode, tncp);
1413307bf766SMatthew Dillon 		tmpfs_dir_detach_locked(tdnode, tde);
141429ca4fd6SJohannes Hofmann 		tmpfs_knote(tdnode->tn_vnode, NOTE_DELETE);
141529ca4fd6SJohannes Hofmann 
141629ca4fd6SJohannes Hofmann 		/*
141729ca4fd6SJohannes Hofmann 		 * Free the directory entry we just deleted.  Note that the
141829ca4fd6SJohannes Hofmann 		 * node referred by it will not be removed until the vnode is
141929ca4fd6SJohannes Hofmann 		 * really reclaimed.
142029ca4fd6SJohannes Hofmann 		 */
142129ca4fd6SJohannes Hofmann 		tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), tde);
142229ca4fd6SJohannes Hofmann 		/*cache_inval_vp(tvp, CINV_DESTROY);*/
142329ca4fd6SJohannes Hofmann 	}
142429ca4fd6SJohannes Hofmann 
142529ca4fd6SJohannes Hofmann 	/*
1426dca262fbSMatthew Dillon 	 * Link entry to target directory.  If the entry
1427dca262fbSMatthew Dillon 	 * represents a directory move the parent linkage
1428dca262fbSMatthew Dillon 	 * as well.
1429dca262fbSMatthew Dillon 	 */
1430dca262fbSMatthew Dillon 	if (fdnode != tdnode) {
1431dca262fbSMatthew Dillon 		if (de->td_node->tn_type == VDIR) {
1432dca262fbSMatthew Dillon 			TMPFS_VALIDATE_DIR(fnode);
1433dca262fbSMatthew Dillon 		}
1434307bf766SMatthew Dillon 		tmpfs_dir_attach_locked(tdnode, de);
1435dca262fbSMatthew Dillon 	} else {
1436dca262fbSMatthew Dillon 		tdnode->tn_status |= TMPFS_NODE_MODIFIED;
1437fd67ed24SMatthew Dillon 		de2 = RB_INSERT(tmpfs_dirtree, &tdnode->tn_dir.tn_dirtree, de);
1438fd67ed24SMatthew Dillon 		KASSERT(de2 == NULL,
1439fd67ed24SMatthew Dillon 			("tmpfs_nrenameA: duplicate insertion of %p, has %p\n",
1440fd67ed24SMatthew Dillon 			de, de2));
1441fd67ed24SMatthew Dillon 		de2 = RB_INSERT(tmpfs_dirtree_cookie,
1442f5f22af6SMatthew Dillon 				&tdnode->tn_dir.tn_cookietree, de);
1443fd67ed24SMatthew Dillon 		KASSERT(de2 == NULL,
1444fd67ed24SMatthew Dillon 			("tmpfs_nrenameB: duplicate insertion of %p, has %p\n",
1445fd67ed24SMatthew Dillon 			de, de2));
14467a2de9a4SMatthew Dillon 	}
14474c154053SMatthew Dillon 	tmpfs_unlock4(fdnode, tdnode, fnode, tnode);
14487a2de9a4SMatthew Dillon 
1449dca262fbSMatthew Dillon 	/*
1450dca262fbSMatthew Dillon 	 * Finish up
1451dca262fbSMatthew Dillon 	 */
1452dca262fbSMatthew Dillon 	if (newname) {
1453d00cd01cSVenkatesh Srinivas 		kfree(newname, tmp->tm_name_zone);
1454dca262fbSMatthew Dillon 		newname = NULL;
1455dca262fbSMatthew Dillon 	}
1456ba25006aSSascha Wildner 	cache_rename(ap->a_fnch, ap->a_tnch);
1457ba25006aSSascha Wildner 	tmpfs_knote(ap->a_fdvp, NOTE_WRITE);
1458ba25006aSSascha Wildner 	tmpfs_knote(ap->a_tdvp, NOTE_WRITE);
145980ae59d7SMatthew Dillon 	if (fnode->tn_vnode)
146080ae59d7SMatthew Dillon 		tmpfs_knote(fnode->tn_vnode, NOTE_RENAME);
14614c154053SMatthew Dillon 	if (tvp)
14624c154053SMatthew Dillon 		vrele(tvp);
14634c154053SMatthew Dillon 	return 0;
14647a2de9a4SMatthew Dillon 
14657a2de9a4SMatthew Dillon out_locked:
14664c154053SMatthew Dillon 	tmpfs_unlock4(fdnode, tdnode, fnode, tnode);
14677a2de9a4SMatthew Dillon out:
1468a1fa5d8dSMatthew Dillon 	if (tvp)
1469a1fa5d8dSMatthew Dillon 		vrele(tvp);
14707a2de9a4SMatthew Dillon 	return error;
14717a2de9a4SMatthew Dillon }
14727a2de9a4SMatthew Dillon 
14737a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
14747a2de9a4SMatthew Dillon 
14757a2de9a4SMatthew Dillon static int
tmpfs_nmkdir(struct vop_nmkdir_args * ap)1476ba25006aSSascha Wildner tmpfs_nmkdir(struct vop_nmkdir_args *ap)
14777a2de9a4SMatthew Dillon {
1478ba25006aSSascha Wildner 	struct vnode *dvp = ap->a_dvp;
1479ba25006aSSascha Wildner 	struct vnode **vpp = ap->a_vpp;
1480ba25006aSSascha Wildner 	struct namecache *ncp = ap->a_nch->ncp;
1481ba25006aSSascha Wildner 	struct vattr *vap = ap->a_vap;
1482ba25006aSSascha Wildner 	struct ucred *cred = ap->a_cred;
14837a2de9a4SMatthew Dillon 	int error;
14847a2de9a4SMatthew Dillon 
14857a2de9a4SMatthew Dillon 	KKASSERT(vap->va_type == VDIR);
14867a2de9a4SMatthew Dillon 
14877a2de9a4SMatthew Dillon 	error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL);
14887a2de9a4SMatthew Dillon 	if (error == 0) {
1489ba25006aSSascha Wildner 		cache_setunresolved(ap->a_nch);
1490ba25006aSSascha Wildner 		cache_setvp(ap->a_nch, *vpp);
149180ae59d7SMatthew Dillon 		tmpfs_knote(dvp, NOTE_WRITE | NOTE_LINK);
14927a2de9a4SMatthew Dillon 	}
14937a2de9a4SMatthew Dillon 	return error;
14947a2de9a4SMatthew Dillon }
14957a2de9a4SMatthew Dillon 
14967a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
14977a2de9a4SMatthew Dillon 
14987a2de9a4SMatthew Dillon static int
tmpfs_nrmdir(struct vop_nrmdir_args * ap)1499ba25006aSSascha Wildner tmpfs_nrmdir(struct vop_nrmdir_args *ap)
15007a2de9a4SMatthew Dillon {
1501ba25006aSSascha Wildner 	struct vnode *dvp = ap->a_dvp;
1502ba25006aSSascha Wildner 	struct namecache *ncp = ap->a_nch->ncp;
15039fc94b5fSMatthew Dillon 	struct vnode *vp;
15047a2de9a4SMatthew Dillon 	struct tmpfs_dirent *de;
15057a2de9a4SMatthew Dillon 	struct tmpfs_mount *tmp;
15067a2de9a4SMatthew Dillon 	struct tmpfs_node *dnode;
15077a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
150838e5e604SMatthew Dillon 	int error;
150938e5e604SMatthew Dillon 
151038e5e604SMatthew Dillon 	/*
1511ba25006aSSascha Wildner 	 * We have to acquire the vp from ap->a_nch because we will likely
1512a1fa5d8dSMatthew Dillon 	 * unresolve the namecache entry, and a vrele/vput is needed to
1513a1fa5d8dSMatthew Dillon 	 * trigger the tmpfs_inactive/tmpfs_reclaim sequence.
1514a1fa5d8dSMatthew Dillon 	 *
1515a1fa5d8dSMatthew Dillon 	 * We have to use vget to clear any inactive state on the vnode,
1516a1fa5d8dSMatthew Dillon 	 * otherwise the vnode may remain inactive and thus tmpfs_inactive
1517a1fa5d8dSMatthew Dillon 	 * will not get called when we release it.
15189fc94b5fSMatthew Dillon 	 */
1519ba25006aSSascha Wildner 	error = cache_vget(ap->a_nch, ap->a_cred, LK_SHARED, &vp);
15209fc94b5fSMatthew Dillon 	KKASSERT(error == 0);
1521a1fa5d8dSMatthew Dillon 	vn_unlock(vp);
15227a2de9a4SMatthew Dillon 
1523e527fb6bSMatthew Dillon 	/*
1524e527fb6bSMatthew Dillon 	 * Prevalidate so we don't hit an assertion later
1525e527fb6bSMatthew Dillon 	 */
1526e527fb6bSMatthew Dillon 	if (vp->v_type != VDIR) {
1527e527fb6bSMatthew Dillon 		error = ENOTDIR;
1528e527fb6bSMatthew Dillon 		goto out;
1529e527fb6bSMatthew Dillon 	}
1530e527fb6bSMatthew Dillon 
15317a2de9a4SMatthew Dillon 	tmp = VFS_TO_TMPFS(dvp->v_mount);
15327a2de9a4SMatthew Dillon 	dnode = VP_TO_TMPFS_DIR(dvp);
15337a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_DIR(vp);
15347a2de9a4SMatthew Dillon 
1535ff837cd5SMatthew Dillon 	/*
15364c154053SMatthew Dillon 	 *
15374c154053SMatthew Dillon 	 */
15384c154053SMatthew Dillon 	TMPFS_NODE_LOCK(dnode);
15394c154053SMatthew Dillon 	TMPFS_NODE_LOCK(node);
15404c154053SMatthew Dillon 
15414c154053SMatthew Dillon 	/*
1542a44ecf5cSMatthew Dillon 	 * Only empty directories can be removed.
1543ff837cd5SMatthew Dillon 	 */
15447a2de9a4SMatthew Dillon 	if (node->tn_size > 0) {
15457a2de9a4SMatthew Dillon 		error = ENOTEMPTY;
15464c154053SMatthew Dillon 		goto out_locked;
15477a2de9a4SMatthew Dillon 	}
15487a2de9a4SMatthew Dillon 
15497a2de9a4SMatthew Dillon 	if ((dnode->tn_flags & APPEND)
15507a2de9a4SMatthew Dillon 	    || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
15517a2de9a4SMatthew Dillon 		error = EPERM;
15524c154053SMatthew Dillon 		goto out_locked;
15537a2de9a4SMatthew Dillon 	}
15547a2de9a4SMatthew Dillon 
1555ff837cd5SMatthew Dillon 	/*
1556ff837cd5SMatthew Dillon 	 * This invariant holds only if we are not trying to
1557ff837cd5SMatthew Dillon 	 * remove "..".  We checked for that above so this is safe now.
1558ff837cd5SMatthew Dillon 	 */
15597a2de9a4SMatthew Dillon 	KKASSERT(node->tn_dir.tn_parent == dnode);
15607a2de9a4SMatthew Dillon 
1561ff837cd5SMatthew Dillon 	/*
15624c154053SMatthew Dillon 	 * Get the directory entry associated with node (vp)
1563ff837cd5SMatthew Dillon 	 */
15647a2de9a4SMatthew Dillon 	de = tmpfs_dir_lookup(dnode, node, ncp);
1565ff837cd5SMatthew Dillon 	KKASSERT(TMPFS_DIRENT_MATCHES(de, ncp->nc_name, ncp->nc_nlen));
15667a2de9a4SMatthew Dillon 
15677a2de9a4SMatthew Dillon 	/* Check flags to see if we are allowed to remove the directory. */
1568b7fe63afSMatthew Dillon 	if ((dnode->tn_flags & APPEND) ||
1569b7fe63afSMatthew Dillon 	    node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) {
15707a2de9a4SMatthew Dillon 		error = EPERM;
15714c154053SMatthew Dillon 		goto out_locked;
15727a2de9a4SMatthew Dillon 	}
15737a2de9a4SMatthew Dillon 
15747a2de9a4SMatthew Dillon 	/* Detach the directory entry from the directory (dnode). */
1575307bf766SMatthew Dillon 	tmpfs_dir_detach_locked(dnode, de);
15767a2de9a4SMatthew Dillon 
15776e0c5aabSMatthew Dillon 	/*
15786e0c5aabSMatthew Dillon 	 * Must set parent linkage to NULL (tested by ncreate to disallow
15796e0c5aabSMatthew Dillon 	 * the creation of new files/dirs in a deleted directory)
15806e0c5aabSMatthew Dillon 	 */
158191321284SMatthew Dillon 	node->tn_status |= TMPFS_NODE_CHANGED;
15827a2de9a4SMatthew Dillon 
1583ff837cd5SMatthew Dillon 	dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED |
1584ff837cd5SMatthew Dillon 			    TMPFS_NODE_MODIFIED;
15857a2de9a4SMatthew Dillon 
15867a2de9a4SMatthew Dillon 	/* Free the directory entry we just deleted.  Note that the node
15877a2de9a4SMatthew Dillon 	 * referred by it will not be removed until the vnode is really
15887a2de9a4SMatthew Dillon 	 * reclaimed. */
15890786baf1SMatthew Dillon 	tmpfs_free_dirent(tmp, de);
15907a2de9a4SMatthew Dillon 
15917a2de9a4SMatthew Dillon 	/* Release the deleted vnode (will destroy the node, notify
15927a2de9a4SMatthew Dillon 	 * interested parties and clean it from the cache). */
15937a2de9a4SMatthew Dillon 
15947a2de9a4SMatthew Dillon 	dnode->tn_status |= TMPFS_NODE_CHANGED;
15957a2de9a4SMatthew Dillon 
15964c154053SMatthew Dillon 	TMPFS_NODE_UNLOCK(node);
15974c154053SMatthew Dillon 	TMPFS_NODE_UNLOCK(dnode);
15984c154053SMatthew Dillon 
15994c154053SMatthew Dillon 	tmpfs_update(dvp);
1600ba25006aSSascha Wildner 	cache_unlink(ap->a_nch);
1601b9f715bcSMatthew Dillon 	tmpfs_knote(vp, NOTE_DELETE);
160280ae59d7SMatthew Dillon 	tmpfs_knote(dvp, NOTE_WRITE | NOTE_LINK);
16034c154053SMatthew Dillon 	vrele(vp);
16044c154053SMatthew Dillon 	return 0;
16054c154053SMatthew Dillon 
16064c154053SMatthew Dillon out_locked:
16074c154053SMatthew Dillon 	TMPFS_NODE_UNLOCK(node);
16084c154053SMatthew Dillon 	TMPFS_NODE_UNLOCK(dnode);
16097a2de9a4SMatthew Dillon 
16107a2de9a4SMatthew Dillon out:
16119fc94b5fSMatthew Dillon 	vrele(vp);
16127a2de9a4SMatthew Dillon 
16137a2de9a4SMatthew Dillon 	return error;
16147a2de9a4SMatthew Dillon }
16157a2de9a4SMatthew Dillon 
16167a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
16177a2de9a4SMatthew Dillon 
16187a2de9a4SMatthew Dillon static int
tmpfs_nsymlink(struct vop_nsymlink_args * ap)1619ba25006aSSascha Wildner tmpfs_nsymlink(struct vop_nsymlink_args *ap)
16207a2de9a4SMatthew Dillon {
1621ba25006aSSascha Wildner 	struct vnode *dvp = ap->a_dvp;
1622ba25006aSSascha Wildner 	struct vnode **vpp = ap->a_vpp;
1623ba25006aSSascha Wildner 	struct namecache *ncp = ap->a_nch->ncp;
1624ba25006aSSascha Wildner 	struct vattr *vap = ap->a_vap;
1625ba25006aSSascha Wildner 	struct ucred *cred = ap->a_cred;
1626ba25006aSSascha Wildner 	char *target = ap->a_target;
16277a2de9a4SMatthew Dillon 	int error;
16287a2de9a4SMatthew Dillon 
16297a2de9a4SMatthew Dillon 	vap->va_type = VLNK;
16307a2de9a4SMatthew Dillon 	error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, target);
16317a2de9a4SMatthew Dillon 	if (error == 0) {
163280ae59d7SMatthew Dillon 		tmpfs_knote(*vpp, NOTE_WRITE);
1633ba25006aSSascha Wildner 		cache_setunresolved(ap->a_nch);
1634ba25006aSSascha Wildner 		cache_setvp(ap->a_nch, *vpp);
16357a2de9a4SMatthew Dillon 	}
16367a2de9a4SMatthew Dillon 	return error;
16377a2de9a4SMatthew Dillon }
16387a2de9a4SMatthew Dillon 
16397a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
16407a2de9a4SMatthew Dillon 
16417a2de9a4SMatthew Dillon static int
tmpfs_readdir(struct vop_readdir_args * ap)1642ba25006aSSascha Wildner tmpfs_readdir(struct vop_readdir_args *ap)
16437a2de9a4SMatthew Dillon {
1644ba25006aSSascha Wildner 	struct vnode *vp = ap->a_vp;
1645ba25006aSSascha Wildner 	struct uio *uio = ap->a_uio;
1646ba25006aSSascha Wildner 	int *eofflag = ap->a_eofflag;
1647ba25006aSSascha Wildner 	off_t **cookies = ap->a_cookies;
1648ba25006aSSascha Wildner 	int *ncookies = ap->a_ncookies;
164922d3b394SMatthew Dillon 	struct tmpfs_mount *tmp;
16507a2de9a4SMatthew Dillon 	int error;
16517a2de9a4SMatthew Dillon 	off_t startoff;
16527a2de9a4SMatthew Dillon 	off_t cnt = 0;
16537a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
16547a2de9a4SMatthew Dillon 
16557a2de9a4SMatthew Dillon 	/* This operation only makes sense on directory nodes. */
1656aa1adbf0SMatthew Dillon 	if (vp->v_type != VDIR) {
16577a2de9a4SMatthew Dillon 		return ENOTDIR;
1658aa1adbf0SMatthew Dillon 	}
16597a2de9a4SMatthew Dillon 
166022d3b394SMatthew Dillon 	tmp = VFS_TO_TMPFS(vp->v_mount);
16617a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_DIR(vp);
16627a2de9a4SMatthew Dillon 	startoff = uio->uio_offset;
16637a2de9a4SMatthew Dillon 
16647a2de9a4SMatthew Dillon 	if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) {
16657a2de9a4SMatthew Dillon 		error = tmpfs_dir_getdotdent(node, uio);
1666e91c5b26SMatthew Dillon 		if (error && error != EINVAL) {
1667ff837cd5SMatthew Dillon 			TMPFS_NODE_LOCK_SH(node);
16687a2de9a4SMatthew Dillon 			goto outok;
1669ff837cd5SMatthew Dillon 		}
16707a2de9a4SMatthew Dillon 		cnt++;
16717a2de9a4SMatthew Dillon 	}
16727a2de9a4SMatthew Dillon 
16737a2de9a4SMatthew Dillon 	if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) {
1674ff837cd5SMatthew Dillon 		/* may lock parent, cannot hold node lock */
167522d3b394SMatthew Dillon 		error = tmpfs_dir_getdotdotdent(tmp, node, uio);
1676e91c5b26SMatthew Dillon 		if (error && error != EINVAL) {
1677ff837cd5SMatthew Dillon 			TMPFS_NODE_LOCK_SH(node);
16787a2de9a4SMatthew Dillon 			goto outok;
1679ff837cd5SMatthew Dillon 		}
16807a2de9a4SMatthew Dillon 		cnt++;
16817a2de9a4SMatthew Dillon 	}
16827a2de9a4SMatthew Dillon 
1683ff837cd5SMatthew Dillon 	TMPFS_NODE_LOCK_SH(node);
16847a2de9a4SMatthew Dillon 	error = tmpfs_dir_getdents(node, uio, &cnt);
16857a2de9a4SMatthew Dillon 
16867a2de9a4SMatthew Dillon outok:
16877a2de9a4SMatthew Dillon 	KKASSERT(error >= -1);
16887a2de9a4SMatthew Dillon 
16897a2de9a4SMatthew Dillon 	if (error == -1)
16907a2de9a4SMatthew Dillon 		error = 0;
16917a2de9a4SMatthew Dillon 
16927a2de9a4SMatthew Dillon 	if (eofflag != NULL)
16937a2de9a4SMatthew Dillon 		*eofflag =
16947a2de9a4SMatthew Dillon 		    (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF);
16957a2de9a4SMatthew Dillon 
16967a2de9a4SMatthew Dillon 	/* Update NFS-related variables. */
16977a2de9a4SMatthew Dillon 	if (error == 0 && cookies != NULL && ncookies != NULL) {
16987a2de9a4SMatthew Dillon 		off_t i;
16997a2de9a4SMatthew Dillon 		off_t off = startoff;
17007a2de9a4SMatthew Dillon 		struct tmpfs_dirent *de = NULL;
17017a2de9a4SMatthew Dillon 
17027a2de9a4SMatthew Dillon 		*ncookies = cnt;
17037a2de9a4SMatthew Dillon 		*cookies = kmalloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK);
17047a2de9a4SMatthew Dillon 
17057a2de9a4SMatthew Dillon 		for (i = 0; i < cnt; i++) {
17067a2de9a4SMatthew Dillon 			KKASSERT(off != TMPFS_DIRCOOKIE_EOF);
17077a2de9a4SMatthew Dillon 			if (off == TMPFS_DIRCOOKIE_DOT) {
17087a2de9a4SMatthew Dillon 				off = TMPFS_DIRCOOKIE_DOTDOT;
17097a2de9a4SMatthew Dillon 			} else {
17107a2de9a4SMatthew Dillon 				if (off == TMPFS_DIRCOOKIE_DOTDOT) {
1711f5f22af6SMatthew Dillon 					de = RB_MIN(tmpfs_dirtree_cookie,
1712f5f22af6SMatthew Dillon 						&node->tn_dir.tn_cookietree);
17137a2de9a4SMatthew Dillon 				} else if (de != NULL) {
1714f5f22af6SMatthew Dillon 					de = RB_NEXT(tmpfs_dirtree_cookie,
1715f5f22af6SMatthew Dillon 					       &node->tn_dir.tn_cookietree, de);
17167a2de9a4SMatthew Dillon 				} else {
1717e91c5b26SMatthew Dillon 					de = tmpfs_dir_lookupbycookie(node, off,
1718e91c5b26SMatthew Dillon 								      1);
17197a2de9a4SMatthew Dillon 					KKASSERT(de != NULL);
1720f5f22af6SMatthew Dillon 					de = RB_NEXT(tmpfs_dirtree_cookie,
1721f5f22af6SMatthew Dillon 					       &node->tn_dir.tn_cookietree, de);
17227a2de9a4SMatthew Dillon 				}
17237a2de9a4SMatthew Dillon 				if (de == NULL)
17247a2de9a4SMatthew Dillon 					off = TMPFS_DIRCOOKIE_EOF;
17257a2de9a4SMatthew Dillon 				else
17267a2de9a4SMatthew Dillon 					off = tmpfs_dircookie(de);
17277a2de9a4SMatthew Dillon 			}
17287a2de9a4SMatthew Dillon 			(*cookies)[i] = off;
17297a2de9a4SMatthew Dillon 		}
17307a2de9a4SMatthew Dillon 		KKASSERT(uio->uio_offset == off);
17317a2de9a4SMatthew Dillon 	}
173281aadc82STomohiro Kusumi 	TMPFS_NODE_UNLOCK(node);
17337a2de9a4SMatthew Dillon 
1734bed3b851STomohiro Kusumi 	if ((node->tn_status & TMPFS_NODE_ACCESSED) == 0) {
173581aadc82STomohiro Kusumi 		TMPFS_NODE_LOCK(node);
1736ff837cd5SMatthew Dillon 		node->tn_status |= TMPFS_NODE_ACCESSED;
1737ff837cd5SMatthew Dillon 		TMPFS_NODE_UNLOCK(node);
1738bed3b851STomohiro Kusumi 	}
17397a2de9a4SMatthew Dillon 	return error;
17407a2de9a4SMatthew Dillon }
17417a2de9a4SMatthew Dillon 
17427a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
17437a2de9a4SMatthew Dillon 
17447a2de9a4SMatthew Dillon static int
tmpfs_readlink(struct vop_readlink_args * ap)1745ba25006aSSascha Wildner tmpfs_readlink(struct vop_readlink_args *ap)
17467a2de9a4SMatthew Dillon {
1747ba25006aSSascha Wildner 	struct vnode *vp = ap->a_vp;
1748ba25006aSSascha Wildner 	struct uio *uio = ap->a_uio;
17497a2de9a4SMatthew Dillon 	int error;
17507a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
17517a2de9a4SMatthew Dillon 
17527a2de9a4SMatthew Dillon 	KKASSERT(uio->uio_offset == 0);
17537a2de9a4SMatthew Dillon 	KKASSERT(vp->v_type == VLNK);
17547a2de9a4SMatthew Dillon 
17557a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
1756ff837cd5SMatthew Dillon 	TMPFS_NODE_LOCK_SH(node);
1757ff837cd5SMatthew Dillon 	error = uiomove(node->tn_link,
1758ff837cd5SMatthew Dillon 			MIN(node->tn_size, uio->uio_resid), uio);
175981aadc82STomohiro Kusumi 	TMPFS_NODE_UNLOCK(node);
1760bed3b851STomohiro Kusumi 	if ((node->tn_status & TMPFS_NODE_ACCESSED) == 0) {
176181aadc82STomohiro Kusumi 		TMPFS_NODE_LOCK(node);
17627a2de9a4SMatthew Dillon 		node->tn_status |= TMPFS_NODE_ACCESSED;
17637a2de9a4SMatthew Dillon 		TMPFS_NODE_UNLOCK(node);
1764bed3b851STomohiro Kusumi 	}
17657a2de9a4SMatthew Dillon 	return error;
17667a2de9a4SMatthew Dillon }
17677a2de9a4SMatthew Dillon 
17687a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
17697a2de9a4SMatthew Dillon 
17707a2de9a4SMatthew Dillon static int
tmpfs_inactive(struct vop_inactive_args * ap)1771ba25006aSSascha Wildner tmpfs_inactive(struct vop_inactive_args *ap)
17727a2de9a4SMatthew Dillon {
1773ba25006aSSascha Wildner 	struct vnode *vp = ap->a_vp;
17747a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
1775aa1adbf0SMatthew Dillon 	struct mount *mp;
17767a2de9a4SMatthew Dillon 
1777aa1adbf0SMatthew Dillon 	mp = vp->v_mount;
1778aa1adbf0SMatthew Dillon 	lwkt_gettoken(&mp->mnt_token);
17797a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
17807a2de9a4SMatthew Dillon 
17819fc94b5fSMatthew Dillon 	/*
1782a1fa5d8dSMatthew Dillon 	 * Degenerate case
1783a1fa5d8dSMatthew Dillon 	 */
1784a1fa5d8dSMatthew Dillon 	if (node == NULL) {
1785a1fa5d8dSMatthew Dillon 		vrecycle(vp);
1786aa1adbf0SMatthew Dillon 		lwkt_reltoken(&mp->mnt_token);
1787a1fa5d8dSMatthew Dillon 		return(0);
1788a1fa5d8dSMatthew Dillon 	}
1789a1fa5d8dSMatthew Dillon 
1790a1fa5d8dSMatthew Dillon 	/*
17919fc94b5fSMatthew Dillon 	 * Get rid of unreferenced deleted vnodes sooner rather than
17929fc94b5fSMatthew Dillon 	 * later so the data memory can be recovered immediately.
1793f96f2f39SMatthew Dillon 	 *
1794f96f2f39SMatthew Dillon 	 * We must truncate the vnode to prevent the normal reclamation
1795f96f2f39SMatthew Dillon 	 * path from flushing the data for the removed file to disk.
17969fc94b5fSMatthew Dillon 	 */
17977a2de9a4SMatthew Dillon 	TMPFS_NODE_LOCK(node);
1798a44ecf5cSMatthew Dillon 	if (node->tn_links == 0) {
17999fc94b5fSMatthew Dillon 		node->tn_vpstate = TMPFS_VNODE_DOOMED;
18007a2de9a4SMatthew Dillon 		TMPFS_NODE_UNLOCK(node);
1801f96f2f39SMatthew Dillon 		if (node->tn_type == VREG)
1802f96f2f39SMatthew Dillon 			tmpfs_truncate(vp, 0);
18037a2de9a4SMatthew Dillon 		vrecycle(vp);
18049fc94b5fSMatthew Dillon 	} else {
1805a1b829f2SMatthew Dillon 		/*
1806a1b829f2SMatthew Dillon 		 * We must retain any VM pages belonging to the vnode's
1807a1b829f2SMatthew Dillon 		 * object as the vnode will destroy the object during a
1808a1b829f2SMatthew Dillon 		 * later reclaim.  We call vinvalbuf(V_SAVE) to clean
1809a1b829f2SMatthew Dillon 		 * out the buffer cache.
1810a1b829f2SMatthew Dillon 		 *
1811a1b829f2SMatthew Dillon 		 * On DragonFlyBSD, vnodes are not immediately deactivated
1812a1b829f2SMatthew Dillon 		 * on the 1->0 refs, so this is a relatively optimal
1813a1b829f2SMatthew Dillon 		 * operation.  We have to do this in tmpfs_inactive()
1814a1b829f2SMatthew Dillon 		 * because the pages will have already been thrown away
1815a1b829f2SMatthew Dillon 		 * at the time tmpfs_reclaim() is called.
1816a1b829f2SMatthew Dillon 		 */
1817a1b829f2SMatthew Dillon 		if (node->tn_type == VREG &&
1818a1b829f2SMatthew Dillon 		    node->tn_reg.tn_pages_in_aobj == 0) {
1819a1b829f2SMatthew Dillon 			vinvalbuf(vp, V_SAVE, 0, 0);
1820a1b829f2SMatthew Dillon 			KKASSERT(RB_EMPTY(&vp->v_rbdirty_tree));
1821a1b829f2SMatthew Dillon 			KKASSERT(RB_EMPTY(&vp->v_rbclean_tree));
1822cfd59c5aSMatthew Dillon 			tmpfs_move_pages(vp->v_object, node->tn_reg.tn_aobj,
1823cfd59c5aSMatthew Dillon 					 TMPFS_MOVF_DEACTIVATE);
1824a1b829f2SMatthew Dillon 			node->tn_reg.tn_pages_in_aobj = 1;
1825a1b829f2SMatthew Dillon 		}
1826a1b829f2SMatthew Dillon 
18277a2de9a4SMatthew Dillon 		TMPFS_NODE_UNLOCK(node);
18289fc94b5fSMatthew Dillon 	}
1829aa1adbf0SMatthew Dillon 	lwkt_reltoken(&mp->mnt_token);
18307a2de9a4SMatthew Dillon 
18317a2de9a4SMatthew Dillon 	return 0;
18327a2de9a4SMatthew Dillon }
18337a2de9a4SMatthew Dillon 
18347a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
18357a2de9a4SMatthew Dillon 
18367a2de9a4SMatthew Dillon int
tmpfs_reclaim(struct vop_reclaim_args * ap)1837ba25006aSSascha Wildner tmpfs_reclaim(struct vop_reclaim_args *ap)
18387a2de9a4SMatthew Dillon {
1839ba25006aSSascha Wildner 	struct vnode *vp = ap->a_vp;
18407a2de9a4SMatthew Dillon 	struct tmpfs_mount *tmp;
18417a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
1842aa1adbf0SMatthew Dillon 	struct mount *mp;
1843aa1adbf0SMatthew Dillon 
1844aa1adbf0SMatthew Dillon 	mp = vp->v_mount;
1845aa1adbf0SMatthew Dillon 	lwkt_gettoken(&mp->mnt_token);
18467a2de9a4SMatthew Dillon 
18477a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
18487a2de9a4SMatthew Dillon 	tmp = VFS_TO_TMPFS(vp->v_mount);
1849aa1adbf0SMatthew Dillon 	KKASSERT(mp == tmp->tm_mount);
18507a2de9a4SMatthew Dillon 
185189984f3dSMatthew Dillon         TMPFS_NODE_LOCK(node);
185289984f3dSMatthew Dillon 	KKASSERT(node->tn_vnode == vp);
185389984f3dSMatthew Dillon         node->tn_vnode = NULL;
185489984f3dSMatthew Dillon         vp->v_data = NULL;
18557a2de9a4SMatthew Dillon 
1856b7fe63afSMatthew Dillon 	/*
1857b7fe63afSMatthew Dillon 	 * If the node referenced by this vnode was deleted by the
1858b7fe63afSMatthew Dillon 	 * user, we must free its associated data structures now that
1859b7fe63afSMatthew Dillon 	 * the vnode is being reclaimed.
1860b7fe63afSMatthew Dillon 	 *
1861b7fe63afSMatthew Dillon 	 * Directories have an extra link ref.
1862b7fe63afSMatthew Dillon 	 */
1863a44ecf5cSMatthew Dillon 	if (node->tn_links == 0) {
18647a2de9a4SMatthew Dillon 		node->tn_vpstate = TMPFS_VNODE_DOOMED;
18657a2de9a4SMatthew Dillon 		tmpfs_free_node(tmp, node);
18660786baf1SMatthew Dillon 		/* eats the lock */
18679fc94b5fSMatthew Dillon 	} else {
18687a2de9a4SMatthew Dillon 		TMPFS_NODE_UNLOCK(node);
18699fc94b5fSMatthew Dillon 	}
1870aa1adbf0SMatthew Dillon 	lwkt_reltoken(&mp->mnt_token);
18717a2de9a4SMatthew Dillon 
18727a2de9a4SMatthew Dillon 	KKASSERT(vp->v_data == NULL);
18737a2de9a4SMatthew Dillon 	return 0;
18747a2de9a4SMatthew Dillon }
18757a2de9a4SMatthew Dillon 
18767a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
18777a2de9a4SMatthew Dillon 
18787a2de9a4SMatthew Dillon static int
tmpfs_mountctl(struct vop_mountctl_args * ap)187966fa44e7SVenkatesh Srinivas tmpfs_mountctl(struct vop_mountctl_args *ap)
188066fa44e7SVenkatesh Srinivas {
188166fa44e7SVenkatesh Srinivas 	struct tmpfs_mount *tmp;
188266fa44e7SVenkatesh Srinivas 	struct mount *mp;
188366fa44e7SVenkatesh Srinivas 	int rc;
188466fa44e7SVenkatesh Srinivas 
1885aa1adbf0SMatthew Dillon 	mp = ap->a_head.a_ops->head.vv_mount;
1886aa1adbf0SMatthew Dillon 	lwkt_gettoken(&mp->mnt_token);
1887aa1adbf0SMatthew Dillon 
188866fa44e7SVenkatesh Srinivas 	switch (ap->a_op) {
188966fa44e7SVenkatesh Srinivas 	case (MOUNTCTL_SET_EXPORT):
189066fa44e7SVenkatesh Srinivas 		tmp = (struct tmpfs_mount *) mp->mnt_data;
189166fa44e7SVenkatesh Srinivas 
189266fa44e7SVenkatesh Srinivas 		if (ap->a_ctllen != sizeof(struct export_args))
189366fa44e7SVenkatesh Srinivas 			rc = (EINVAL);
189466fa44e7SVenkatesh Srinivas 		else
189566fa44e7SVenkatesh Srinivas 			rc = vfs_export(mp, &tmp->tm_export,
189666fa44e7SVenkatesh Srinivas 					(const struct export_args *) ap->a_ctl);
189766fa44e7SVenkatesh Srinivas 		break;
189866fa44e7SVenkatesh Srinivas 	default:
189966fa44e7SVenkatesh Srinivas 		rc = vop_stdmountctl(ap);
190066fa44e7SVenkatesh Srinivas 		break;
190166fa44e7SVenkatesh Srinivas 	}
1902aa1adbf0SMatthew Dillon 
1903aa1adbf0SMatthew Dillon 	lwkt_reltoken(&mp->mnt_token);
190466fa44e7SVenkatesh Srinivas 	return (rc);
190566fa44e7SVenkatesh Srinivas }
190666fa44e7SVenkatesh Srinivas 
190766fa44e7SVenkatesh Srinivas /* --------------------------------------------------------------------- */
190866fa44e7SVenkatesh Srinivas 
190966fa44e7SVenkatesh Srinivas static int
tmpfs_print(struct vop_print_args * ap)1910ba25006aSSascha Wildner tmpfs_print(struct vop_print_args *ap)
19117a2de9a4SMatthew Dillon {
1912ba25006aSSascha Wildner 	struct vnode *vp = ap->a_vp;
19137a2de9a4SMatthew Dillon 
19147a2de9a4SMatthew Dillon 	struct tmpfs_node *node;
19157a2de9a4SMatthew Dillon 
19167a2de9a4SMatthew Dillon 	node = VP_TO_TMPFS_NODE(vp);
19177a2de9a4SMatthew Dillon 
19187a2de9a4SMatthew Dillon 	kprintf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n",
19197a2de9a4SMatthew Dillon 	    node, node->tn_flags, node->tn_links);
19207a2de9a4SMatthew Dillon 	kprintf("\tmode 0%o, owner %d, group %d, size %ju, status 0x%x\n",
19217a2de9a4SMatthew Dillon 	    node->tn_mode, node->tn_uid, node->tn_gid,
19227a2de9a4SMatthew Dillon 	    (uintmax_t)node->tn_size, node->tn_status);
19237a2de9a4SMatthew Dillon 
19247a2de9a4SMatthew Dillon 	if (vp->v_type == VFIFO)
19257a2de9a4SMatthew Dillon 		fifo_printinfo(vp);
19267a2de9a4SMatthew Dillon 
19277a2de9a4SMatthew Dillon 	kprintf("\n");
19287a2de9a4SMatthew Dillon 
19297a2de9a4SMatthew Dillon 	return 0;
19307a2de9a4SMatthew Dillon }
19317a2de9a4SMatthew Dillon 
19327a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
19337a2de9a4SMatthew Dillon 
19347a2de9a4SMatthew Dillon static int
tmpfs_pathconf(struct vop_pathconf_args * ap)1935ba25006aSSascha Wildner tmpfs_pathconf(struct vop_pathconf_args *ap)
19367a2de9a4SMatthew Dillon {
1937ba25006aSSascha Wildner 	struct vnode *vp = ap->a_vp;
1938ba25006aSSascha Wildner 	int name = ap->a_name;
1939ba25006aSSascha Wildner 	register_t *retval = ap->a_retval;
194036799b8eSSascha Wildner 	struct tmpfs_mount *tmp;
19417a2de9a4SMatthew Dillon 	int error;
19427a2de9a4SMatthew Dillon 
19437a2de9a4SMatthew Dillon 	error = 0;
19447a2de9a4SMatthew Dillon 
19457a2de9a4SMatthew Dillon 	switch (name) {
1946b3b8fc63SSascha Wildner 	case _PC_CHOWN_RESTRICTED:
1947b3b8fc63SSascha Wildner 		*retval = 1;
1948b3b8fc63SSascha Wildner 		break;
1949b3b8fc63SSascha Wildner 
1950b3b8fc63SSascha Wildner 	case _PC_FILESIZEBITS:
195136799b8eSSascha Wildner 		tmp = VFS_TO_TMPFS(vp->v_mount);
195236799b8eSSascha Wildner 		*retval = max(32, flsll(tmp->tm_pages_max * PAGE_SIZE) + 1);
1953b3b8fc63SSascha Wildner 		break;
1954b3b8fc63SSascha Wildner 
19557a2de9a4SMatthew Dillon 	case _PC_LINK_MAX:
19567a2de9a4SMatthew Dillon 		*retval = LINK_MAX;
19577a2de9a4SMatthew Dillon 		break;
19587a2de9a4SMatthew Dillon 
19597a2de9a4SMatthew Dillon 	case _PC_NAME_MAX:
19607a2de9a4SMatthew Dillon 		*retval = NAME_MAX;
19617a2de9a4SMatthew Dillon 		break;
19627a2de9a4SMatthew Dillon 
1963b3b8fc63SSascha Wildner 	case _PC_NO_TRUNC:
1964b3b8fc63SSascha Wildner 		*retval = 1;
1965b3b8fc63SSascha Wildner 		break;
1966b3b8fc63SSascha Wildner 
19677a2de9a4SMatthew Dillon 	case _PC_PATH_MAX:
19687a2de9a4SMatthew Dillon 		*retval = PATH_MAX;
19697a2de9a4SMatthew Dillon 		break;
19707a2de9a4SMatthew Dillon 
19717a2de9a4SMatthew Dillon 	case _PC_PIPE_BUF:
19727a2de9a4SMatthew Dillon 		*retval = PIPE_BUF;
19737a2de9a4SMatthew Dillon 		break;
19747a2de9a4SMatthew Dillon 
19757a2de9a4SMatthew Dillon 	case _PC_SYNC_IO:
19767a2de9a4SMatthew Dillon 		*retval = 1;
19777a2de9a4SMatthew Dillon 		break;
19787a2de9a4SMatthew Dillon 
197909789cfdSSascha Wildner 	case _PC_2_SYMLINKS:
198009789cfdSSascha Wildner 		*retval = 1;
198109789cfdSSascha Wildner 		break;
198209789cfdSSascha Wildner 
19837a2de9a4SMatthew Dillon 	default:
19847a2de9a4SMatthew Dillon 		error = EINVAL;
19857a2de9a4SMatthew Dillon 	}
19867a2de9a4SMatthew Dillon 
19877a2de9a4SMatthew Dillon 	return error;
19887a2de9a4SMatthew Dillon }
19897a2de9a4SMatthew Dillon 
199080ae59d7SMatthew Dillon /************************************************************************
199180ae59d7SMatthew Dillon  *                          KQFILTER OPS                                *
199280ae59d7SMatthew Dillon  ************************************************************************/
199380ae59d7SMatthew Dillon 
199480ae59d7SMatthew Dillon static void filt_tmpfsdetach(struct knote *kn);
199580ae59d7SMatthew Dillon static int filt_tmpfsread(struct knote *kn, long hint);
199680ae59d7SMatthew Dillon static int filt_tmpfswrite(struct knote *kn, long hint);
199780ae59d7SMatthew Dillon static int filt_tmpfsvnode(struct knote *kn, long hint);
199880ae59d7SMatthew Dillon 
199980ae59d7SMatthew Dillon static struct filterops tmpfsread_filtops =
2000ff837cd5SMatthew Dillon 	{ FILTEROP_ISFD | FILTEROP_MPSAFE,
2001ff837cd5SMatthew Dillon 	  NULL, filt_tmpfsdetach, filt_tmpfsread };
200280ae59d7SMatthew Dillon static struct filterops tmpfswrite_filtops =
2003ff837cd5SMatthew Dillon 	{ FILTEROP_ISFD | FILTEROP_MPSAFE,
2004ff837cd5SMatthew Dillon 	  NULL, filt_tmpfsdetach, filt_tmpfswrite };
200580ae59d7SMatthew Dillon static struct filterops tmpfsvnode_filtops =
2006ff837cd5SMatthew Dillon 	{ FILTEROP_ISFD | FILTEROP_MPSAFE,
2007ff837cd5SMatthew Dillon 	  NULL, filt_tmpfsdetach, filt_tmpfsvnode };
200880ae59d7SMatthew Dillon 
200980ae59d7SMatthew Dillon static int
tmpfs_kqfilter(struct vop_kqfilter_args * ap)201080ae59d7SMatthew Dillon tmpfs_kqfilter (struct vop_kqfilter_args *ap)
201180ae59d7SMatthew Dillon {
201280ae59d7SMatthew Dillon 	struct vnode *vp = ap->a_vp;
201380ae59d7SMatthew Dillon 	struct knote *kn = ap->a_kn;
201480ae59d7SMatthew Dillon 
201580ae59d7SMatthew Dillon 	switch (kn->kn_filter) {
201680ae59d7SMatthew Dillon 	case EVFILT_READ:
201780ae59d7SMatthew Dillon 		kn->kn_fop = &tmpfsread_filtops;
201880ae59d7SMatthew Dillon 		break;
201980ae59d7SMatthew Dillon 	case EVFILT_WRITE:
202080ae59d7SMatthew Dillon 		kn->kn_fop = &tmpfswrite_filtops;
202180ae59d7SMatthew Dillon 		break;
202280ae59d7SMatthew Dillon 	case EVFILT_VNODE:
202380ae59d7SMatthew Dillon 		kn->kn_fop = &tmpfsvnode_filtops;
202480ae59d7SMatthew Dillon 		break;
202580ae59d7SMatthew Dillon 	default:
202680ae59d7SMatthew Dillon 		return (EOPNOTSUPP);
202780ae59d7SMatthew Dillon 	}
202880ae59d7SMatthew Dillon 
202980ae59d7SMatthew Dillon 	kn->kn_hook = (caddr_t)vp;
203080ae59d7SMatthew Dillon 
203180ae59d7SMatthew Dillon 	knote_insert(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn);
203280ae59d7SMatthew Dillon 
203380ae59d7SMatthew Dillon 	return(0);
203480ae59d7SMatthew Dillon }
203580ae59d7SMatthew Dillon 
203680ae59d7SMatthew Dillon static void
filt_tmpfsdetach(struct knote * kn)203780ae59d7SMatthew Dillon filt_tmpfsdetach(struct knote *kn)
203880ae59d7SMatthew Dillon {
203980ae59d7SMatthew Dillon 	struct vnode *vp = (void *)kn->kn_hook;
204080ae59d7SMatthew Dillon 
204180ae59d7SMatthew Dillon 	knote_remove(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn);
204280ae59d7SMatthew Dillon }
204380ae59d7SMatthew Dillon 
204480ae59d7SMatthew Dillon static int
filt_tmpfsread(struct knote * kn,long hint)204580ae59d7SMatthew Dillon filt_tmpfsread(struct knote *kn, long hint)
204680ae59d7SMatthew Dillon {
204780ae59d7SMatthew Dillon 	struct vnode *vp = (void *)kn->kn_hook;
204880ae59d7SMatthew Dillon 	struct tmpfs_node *node = VP_TO_TMPFS_NODE(vp);
204980ae59d7SMatthew Dillon 	off_t off;
205080ae59d7SMatthew Dillon 
205180ae59d7SMatthew Dillon 	if (hint == NOTE_REVOKE) {
20523bcb6e5eSSepherosa Ziehau 		kn->kn_flags |= (EV_EOF | EV_NODATA | EV_ONESHOT);
205380ae59d7SMatthew Dillon 		return(1);
205480ae59d7SMatthew Dillon 	}
205580ae59d7SMatthew Dillon 
205680ae59d7SMatthew Dillon 	/*
2057f79d9cc9SMatthew Dillon 	 * Interlock against MP races when performing this function.
205880ae59d7SMatthew Dillon 	 */
2059ff837cd5SMatthew Dillon 	TMPFS_NODE_LOCK_SH(node);
2060f79d9cc9SMatthew Dillon 	off = node->tn_size - kn->kn_fp->f_offset;
2061f79d9cc9SMatthew Dillon 	kn->kn_data = (off < INTPTR_MAX) ? off : INTPTR_MAX;
2062f79d9cc9SMatthew Dillon 	if (kn->kn_sfflags & NOTE_OLDAPI) {
2063ff837cd5SMatthew Dillon 		TMPFS_NODE_UNLOCK(node);
2064f79d9cc9SMatthew Dillon 		return(1);
2065f79d9cc9SMatthew Dillon 	}
206680ae59d7SMatthew Dillon 	if (kn->kn_data == 0) {
206780ae59d7SMatthew Dillon 		kn->kn_data = (off < INTPTR_MAX) ? off : INTPTR_MAX;
206880ae59d7SMatthew Dillon 	}
2069ff837cd5SMatthew Dillon 	TMPFS_NODE_UNLOCK(node);
207080ae59d7SMatthew Dillon 	return (kn->kn_data != 0);
207180ae59d7SMatthew Dillon }
207280ae59d7SMatthew Dillon 
207380ae59d7SMatthew Dillon static int
filt_tmpfswrite(struct knote * kn,long hint)207480ae59d7SMatthew Dillon filt_tmpfswrite(struct knote *kn, long hint)
207580ae59d7SMatthew Dillon {
207680ae59d7SMatthew Dillon 	if (hint == NOTE_REVOKE)
20773bcb6e5eSSepherosa Ziehau 		kn->kn_flags |= (EV_EOF | EV_NODATA | EV_ONESHOT);
207880ae59d7SMatthew Dillon 	kn->kn_data = 0;
207980ae59d7SMatthew Dillon 	return (1);
208080ae59d7SMatthew Dillon }
208180ae59d7SMatthew Dillon 
208280ae59d7SMatthew Dillon static int
filt_tmpfsvnode(struct knote * kn,long hint)208380ae59d7SMatthew Dillon filt_tmpfsvnode(struct knote *kn, long hint)
208480ae59d7SMatthew Dillon {
208580ae59d7SMatthew Dillon 	if (kn->kn_sfflags & hint)
208680ae59d7SMatthew Dillon 		kn->kn_fflags |= hint;
208780ae59d7SMatthew Dillon 	if (hint == NOTE_REVOKE) {
20883bcb6e5eSSepherosa Ziehau 		kn->kn_flags |= (EV_EOF | EV_NODATA);
208980ae59d7SMatthew Dillon 		return (1);
209080ae59d7SMatthew Dillon 	}
209180ae59d7SMatthew Dillon 	return (kn->kn_fflags != 0);
209280ae59d7SMatthew Dillon }
209380ae59d7SMatthew Dillon 
2094a1b829f2SMatthew Dillon /*
2095a1b829f2SMatthew Dillon  * Helper to move VM pages between objects
2096a1b829f2SMatthew Dillon  *
2097a1b829f2SMatthew Dillon  * NOTE: The vm_page_rename() dirties the page, so we can clear the
2098a1b829f2SMatthew Dillon  *	 PG_NEED_COMMIT flag.  If the pages are being moved into tn_aobj,
2099a1b829f2SMatthew Dillon  *	 the pageout daemon will be able to page them out.
2100a1b829f2SMatthew Dillon  */
2101a1b829f2SMatthew Dillon static int
tmpfs_move_pages_callback(vm_page_t p,void * data)2102a1b829f2SMatthew Dillon tmpfs_move_pages_callback(vm_page_t p, void *data)
2103a1b829f2SMatthew Dillon {
2104a1b829f2SMatthew Dillon 	struct rb_vm_page_scan_info *info = data;
2105a1b829f2SMatthew Dillon 	vm_pindex_t pindex;
2106a1b829f2SMatthew Dillon 
2107965c458cSMatthew Dillon 	/*
2108965c458cSMatthew Dillon 	 * Take control of the page
2109965c458cSMatthew Dillon 	 */
2110a1b829f2SMatthew Dillon 	pindex = p->pindex;
2111a1b829f2SMatthew Dillon 	if (vm_page_busy_try(p, TRUE)) {
2112a1b829f2SMatthew Dillon 		vm_page_sleep_busy(p, TRUE, "tpgmov");
2113a1b829f2SMatthew Dillon 		info->error = -1;
2114a1b829f2SMatthew Dillon 		return -1;
2115a1b829f2SMatthew Dillon 	}
2116a1b829f2SMatthew Dillon 	if (p->object != info->object || p->pindex != pindex) {
2117a1b829f2SMatthew Dillon 		vm_page_wakeup(p);
2118a1b829f2SMatthew Dillon 		info->error = -1;
2119a1b829f2SMatthew Dillon 		return -1;
2120a1b829f2SMatthew Dillon 	}
2121cfd59c5aSMatthew Dillon 
2122965c458cSMatthew Dillon 	/*
2123965c458cSMatthew Dillon 	 * Make sure the page is not mapped.  These flags might also still be
2124965c458cSMatthew Dillon 	 * set heuristically even if we know the page is not mapped and must
2125965c458cSMatthew Dillon 	 * be properly cleaned up.
2126965c458cSMatthew Dillon 	 */
2127965c458cSMatthew Dillon 	if (__predict_false((p->flags & (PG_MAPPED|PG_WRITEABLE)) != 0))
2128965c458cSMatthew Dillon 		vm_page_protect(p, VM_PROT_NONE);
2129965c458cSMatthew Dillon 
2130965c458cSMatthew Dillon 	/*
2131965c458cSMatthew Dillon 	 * Free or rename the page as appropriate
2132965c458cSMatthew Dillon 	 */
2133cfd59c5aSMatthew Dillon 	if ((info->pagerflags & TMPFS_MOVF_FROMBACKING) &&
2134cfd59c5aSMatthew Dillon 	    (p->flags & PG_SWAPPED) &&
2135cfd59c5aSMatthew Dillon 	    (p->flags & PG_NEED_COMMIT) == 0 &&
2136cfd59c5aSMatthew Dillon 	    p->dirty == 0) {
2137cfd59c5aSMatthew Dillon 		/*
2138cfd59c5aSMatthew Dillon 		 * If the page in the backing aobj was paged out to swap
2139cfd59c5aSMatthew Dillon 		 * it will be clean and it is better to free it rather
2140cfd59c5aSMatthew Dillon 		 * than re-dirty it.  We will assume that the page was
2141cfd59c5aSMatthew Dillon 		 * paged out to swap for a reason!
2142cfd59c5aSMatthew Dillon 		 *
2143cfd59c5aSMatthew Dillon 		 * This helps avoid unnecessary swap thrashing on the page.
2144cfd59c5aSMatthew Dillon 		 */
2145cfd59c5aSMatthew Dillon 		vm_page_free(p);
2146cfd59c5aSMatthew Dillon 	} else if ((info->pagerflags & TMPFS_MOVF_FROMBACKING) == 0 &&
2147cfd59c5aSMatthew Dillon 		   (p->flags & PG_NEED_COMMIT) == 0 &&
2148cfd59c5aSMatthew Dillon 		   p->dirty == 0) {
2149cfd59c5aSMatthew Dillon 		/*
2150cfd59c5aSMatthew Dillon 		 * If the page associated with the vnode was cleaned via
2151cfd59c5aSMatthew Dillon 		 * a tmpfs_strategy() call, it exists as a swap block in
2152cfd59c5aSMatthew Dillon 		 * aobj and it is again better to free it rather than
2153cfd59c5aSMatthew Dillon 		 * re-dirty it.  We will assume that the page was
2154cfd59c5aSMatthew Dillon 		 * paged out to swap for a reason!
2155cfd59c5aSMatthew Dillon 		 *
2156cfd59c5aSMatthew Dillon 		 * This helps avoid unnecessary swap thrashing on the page.
2157cfd59c5aSMatthew Dillon 		 */
2158cfd59c5aSMatthew Dillon 		vm_page_free(p);
2159cfd59c5aSMatthew Dillon 	} else {
2160cfd59c5aSMatthew Dillon 		/*
2161cfd59c5aSMatthew Dillon 		 * Rename the page, which will also ensure that it is flagged
2162cfd59c5aSMatthew Dillon 		 * as dirty and check whether a swap block association exists
2163cfd59c5aSMatthew Dillon 		 * in the target object or not, setting appropriate flags if
2164cfd59c5aSMatthew Dillon 		 * it does.
2165cfd59c5aSMatthew Dillon 		 */
2166530e94fcSMatthew Dillon 		vm_page_rename(p, info->dest_object, pindex);
2167a1b829f2SMatthew Dillon 		vm_page_clear_commit(p);
2168cfd59c5aSMatthew Dillon 		if (info->pagerflags & TMPFS_MOVF_DEACTIVATE)
2169cfd59c5aSMatthew Dillon 			vm_page_deactivate(p);
2170a1b829f2SMatthew Dillon 		vm_page_wakeup(p);
2171a1b829f2SMatthew Dillon 		/* page automaticaly made dirty */
2172cfd59c5aSMatthew Dillon 	}
2173a1b829f2SMatthew Dillon 
2174a1b829f2SMatthew Dillon 	return 0;
2175a1b829f2SMatthew Dillon }
2176a1b829f2SMatthew Dillon 
2177a1b829f2SMatthew Dillon static
2178a1b829f2SMatthew Dillon void
tmpfs_move_pages(vm_object_t src,vm_object_t dst,int movflags)2179cfd59c5aSMatthew Dillon tmpfs_move_pages(vm_object_t src, vm_object_t dst, int movflags)
2180a1b829f2SMatthew Dillon {
2181a1b829f2SMatthew Dillon 	struct rb_vm_page_scan_info info;
2182a1b829f2SMatthew Dillon 
2183a1b829f2SMatthew Dillon 	vm_object_hold(src);
2184a1b829f2SMatthew Dillon 	vm_object_hold(dst);
2185a1b829f2SMatthew Dillon 	info.object = src;
2186530e94fcSMatthew Dillon 	info.dest_object = dst;
2187cfd59c5aSMatthew Dillon 	info.pagerflags = movflags;
2188a1b829f2SMatthew Dillon 	do {
218969940345SMatthew Dillon 		if (src->paging_in_progress)
219069940345SMatthew Dillon 			vm_object_pip_wait(src, "objtfs");
2191a1b829f2SMatthew Dillon 		info.error = 1;
2192a1b829f2SMatthew Dillon 		vm_page_rb_tree_RB_SCAN(&src->rb_memq, NULL,
2193a1b829f2SMatthew Dillon 					tmpfs_move_pages_callback, &info);
219469940345SMatthew Dillon 	} while (info.error < 0 || !RB_EMPTY(&src->rb_memq) ||
219569940345SMatthew Dillon 		 src->paging_in_progress);
2196a1b829f2SMatthew Dillon 	vm_object_drop(dst);
2197a1b829f2SMatthew Dillon 	vm_object_drop(src);
2198a1b829f2SMatthew Dillon }
219980ae59d7SMatthew Dillon 
22007a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
22017a2de9a4SMatthew Dillon 
22027a2de9a4SMatthew Dillon /*
22037a2de9a4SMatthew Dillon  * vnode operations vector used for files stored in a tmpfs file system.
22047a2de9a4SMatthew Dillon  */
22057a2de9a4SMatthew Dillon struct vop_ops tmpfs_vnode_vops = {
22067a2de9a4SMatthew Dillon 	.vop_default =			vop_defaultop,
22077a2de9a4SMatthew Dillon 	.vop_getpages = 		vop_stdgetpages,
22087a2de9a4SMatthew Dillon 	.vop_putpages = 		vop_stdputpages,
22097a2de9a4SMatthew Dillon 	.vop_ncreate =			tmpfs_ncreate,
22107a2de9a4SMatthew Dillon 	.vop_nresolve =			tmpfs_nresolve,
22117a2de9a4SMatthew Dillon 	.vop_nlookupdotdot =		tmpfs_nlookupdotdot,
22127a2de9a4SMatthew Dillon 	.vop_nmknod =			tmpfs_nmknod,
22137a2de9a4SMatthew Dillon 	.vop_open =			tmpfs_open,
22147a2de9a4SMatthew Dillon 	.vop_close =			tmpfs_close,
22157a2de9a4SMatthew Dillon 	.vop_access =			tmpfs_access,
22167a2de9a4SMatthew Dillon 	.vop_getattr =			tmpfs_getattr,
2217de9bb133SMatthew Dillon 	.vop_getattr_lite =		tmpfs_getattr_lite,
22187a2de9a4SMatthew Dillon 	.vop_setattr =			tmpfs_setattr,
22197a2de9a4SMatthew Dillon 	.vop_read =			tmpfs_read,
22207a2de9a4SMatthew Dillon 	.vop_write =			tmpfs_write,
22217a2de9a4SMatthew Dillon 	.vop_fsync =			tmpfs_fsync,
222266fa44e7SVenkatesh Srinivas 	.vop_mountctl =			tmpfs_mountctl,
22237a2de9a4SMatthew Dillon 	.vop_nremove =			tmpfs_nremove,
22247a2de9a4SMatthew Dillon 	.vop_nlink =			tmpfs_nlink,
22257a2de9a4SMatthew Dillon 	.vop_nrename =			tmpfs_nrename,
22267a2de9a4SMatthew Dillon 	.vop_nmkdir =			tmpfs_nmkdir,
22277a2de9a4SMatthew Dillon 	.vop_nrmdir =			tmpfs_nrmdir,
22287a2de9a4SMatthew Dillon 	.vop_nsymlink =			tmpfs_nsymlink,
22297a2de9a4SMatthew Dillon 	.vop_readdir =			tmpfs_readdir,
22307a2de9a4SMatthew Dillon 	.vop_readlink =			tmpfs_readlink,
22317a2de9a4SMatthew Dillon 	.vop_inactive =			tmpfs_inactive,
22327a2de9a4SMatthew Dillon 	.vop_reclaim =			tmpfs_reclaim,
22337a2de9a4SMatthew Dillon 	.vop_print =			tmpfs_print,
22347a2de9a4SMatthew Dillon 	.vop_pathconf =			tmpfs_pathconf,
22359fc94b5fSMatthew Dillon 	.vop_bmap =			tmpfs_bmap,
22367a2de9a4SMatthew Dillon 	.vop_strategy =			tmpfs_strategy,
22377a2de9a4SMatthew Dillon 	.vop_advlock =			tmpfs_advlock,
223880ae59d7SMatthew Dillon 	.vop_kqfilter =			tmpfs_kqfilter
22397a2de9a4SMatthew Dillon };
2240