xref: /netbsd-src/sys/fs/tmpfs/tmpfs_vnops.c (revision 06cc9cbbb4f435859c052866e8c5ef3f7f621217)
1*06cc9cbbShannken /*	$NetBSD: tmpfs_vnops.c,v 1.150 2022/06/01 08:42:38 hannken Exp $	*/
2ec933656Sjmmv 
3ec933656Sjmmv /*
45c373ea8Sad  * Copyright (c) 2005, 2006, 2007, 2020 The NetBSD Foundation, Inc.
5ec933656Sjmmv  * All rights reserved.
6ec933656Sjmmv  *
7ec933656Sjmmv  * This code is derived from software contributed to The NetBSD Foundation
8b0085cabSjmmv  * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
9b0085cabSjmmv  * 2005 program.
10ec933656Sjmmv  *
11ec933656Sjmmv  * Redistribution and use in source and binary forms, with or without
12ec933656Sjmmv  * modification, are permitted provided that the following conditions
13ec933656Sjmmv  * are met:
14ec933656Sjmmv  * 1. Redistributions of source code must retain the above copyright
15ec933656Sjmmv  *    notice, this list of conditions and the following disclaimer.
16ec933656Sjmmv  * 2. Redistributions in binary form must reproduce the above copyright
17ec933656Sjmmv  *    notice, this list of conditions and the following disclaimer in the
18ec933656Sjmmv  *    documentation and/or other materials provided with the distribution.
19ec933656Sjmmv  *
20ec933656Sjmmv  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21ec933656Sjmmv  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22ec933656Sjmmv  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23ec933656Sjmmv  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24ec933656Sjmmv  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25ec933656Sjmmv  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26ec933656Sjmmv  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27ec933656Sjmmv  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28ec933656Sjmmv  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29ec933656Sjmmv  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30ec933656Sjmmv  * POSSIBILITY OF SUCH DAMAGE.
31ec933656Sjmmv  */
32ec933656Sjmmv 
33ec933656Sjmmv /*
34ec933656Sjmmv  * tmpfs vnode interface.
35ec933656Sjmmv  */
36ec933656Sjmmv 
37ec933656Sjmmv #include <sys/cdefs.h>
38*06cc9cbbShannken __KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.150 2022/06/01 08:42:38 hannken Exp $");
39ec933656Sjmmv 
40ec933656Sjmmv #include <sys/param.h>
41ec933656Sjmmv #include <sys/dirent.h>
42ec933656Sjmmv #include <sys/fcntl.h>
43ec933656Sjmmv #include <sys/event.h>
44ec933656Sjmmv #include <sys/malloc.h>
45ec933656Sjmmv #include <sys/namei.h>
46ec933656Sjmmv #include <sys/stat.h>
47ec933656Sjmmv #include <sys/uio.h>
48ec933656Sjmmv #include <sys/unistd.h>
49ec933656Sjmmv #include <sys/vnode.h>
50b6d141c7Sjmmv #include <sys/lockf.h>
51fc6d984bSchristos #include <sys/kauth.h>
528ea5485dShannken #include <sys/atomic.h>
53ec933656Sjmmv 
549fc45356Sriastradh #include <uvm/uvm_object.h>
55ec933656Sjmmv 
56ec933656Sjmmv #include <miscfs/fifofs/fifo.h>
5787092026Selad #include <miscfs/genfs/genfs.h>
58ec933656Sjmmv #include <fs/tmpfs/tmpfs_vnops.h>
59ec933656Sjmmv #include <fs/tmpfs/tmpfs.h>
60ec933656Sjmmv 
61ec933656Sjmmv /*
628e0a777aSjmmv  * vnode operations vector used for files stored in a tmpfs file system.
63ec933656Sjmmv  */
64ec933656Sjmmv int (**tmpfs_vnodeop_p)(void *);
65ec933656Sjmmv const struct vnodeopv_entry_desc tmpfs_vnodeop_entries[] = {
66ec933656Sjmmv 	{ &vop_default_desc,		vn_default_error },
67c6c16cd0Sdholland 	{ &vop_parsepath_desc,		genfs_parsepath },
68ec933656Sjmmv 	{ &vop_lookup_desc,		tmpfs_lookup },
69ec933656Sjmmv 	{ &vop_create_desc,		tmpfs_create },
70ec933656Sjmmv 	{ &vop_mknod_desc,		tmpfs_mknod },
71ec933656Sjmmv 	{ &vop_open_desc,		tmpfs_open },
72ec933656Sjmmv 	{ &vop_close_desc,		tmpfs_close },
73ec933656Sjmmv 	{ &vop_access_desc,		tmpfs_access },
749aa2a9c3Schristos 	{ &vop_accessx_desc,		genfs_accessx },
75ec933656Sjmmv 	{ &vop_getattr_desc,		tmpfs_getattr },
76ec933656Sjmmv 	{ &vop_setattr_desc,		tmpfs_setattr },
77ec933656Sjmmv 	{ &vop_read_desc,		tmpfs_read },
78ec933656Sjmmv 	{ &vop_write_desc,		tmpfs_write },
7905d075b3Sdholland 	{ &vop_fallocate_desc,		genfs_eopnotsupp },
8005d075b3Sdholland 	{ &vop_fdiscard_desc,		genfs_eopnotsupp },
8141715070Sdholland 	{ &vop_ioctl_desc,		genfs_enoioctl },
8241715070Sdholland 	{ &vop_fcntl_desc,		genfs_fcntl },
8341715070Sdholland 	{ &vop_poll_desc,		genfs_poll },
8441715070Sdholland 	{ &vop_kqfilter_desc,		genfs_kqfilter },
8541715070Sdholland 	{ &vop_revoke_desc,		genfs_revoke },
8641715070Sdholland 	{ &vop_mmap_desc,		genfs_mmap },
87ec933656Sjmmv 	{ &vop_fsync_desc,		tmpfs_fsync },
8841715070Sdholland 	{ &vop_seek_desc,		genfs_seek },
89ec933656Sjmmv 	{ &vop_remove_desc,		tmpfs_remove },
90ec933656Sjmmv 	{ &vop_link_desc,		tmpfs_link },
91ec933656Sjmmv 	{ &vop_rename_desc,		tmpfs_rename },
92ec933656Sjmmv 	{ &vop_mkdir_desc,		tmpfs_mkdir },
93ec933656Sjmmv 	{ &vop_rmdir_desc,		tmpfs_rmdir },
94ec933656Sjmmv 	{ &vop_symlink_desc,		tmpfs_symlink },
95ec933656Sjmmv 	{ &vop_readdir_desc,		tmpfs_readdir },
96ec933656Sjmmv 	{ &vop_readlink_desc,		tmpfs_readlink },
9741715070Sdholland 	{ &vop_abortop_desc,		genfs_abortop },
98ec933656Sjmmv 	{ &vop_inactive_desc,		tmpfs_inactive },
99ec933656Sjmmv 	{ &vop_reclaim_desc,		tmpfs_reclaim },
10041715070Sdholland 	{ &vop_lock_desc,		genfs_lock },
10141715070Sdholland 	{ &vop_unlock_desc,		genfs_unlock },
10241715070Sdholland 	{ &vop_bmap_desc,		genfs_eopnotsupp },
10341715070Sdholland 	{ &vop_strategy_desc,		genfs_eopnotsupp },
104ec933656Sjmmv 	{ &vop_print_desc,		tmpfs_print },
105ec933656Sjmmv 	{ &vop_pathconf_desc,		tmpfs_pathconf },
10641715070Sdholland 	{ &vop_islocked_desc,		genfs_islocked },
107ec933656Sjmmv 	{ &vop_advlock_desc,		tmpfs_advlock },
10841715070Sdholland 	{ &vop_bwrite_desc,		genfs_nullop },
109ec933656Sjmmv 	{ &vop_getpages_desc,		tmpfs_getpages },
110ec933656Sjmmv 	{ &vop_putpages_desc,		tmpfs_putpages },
11107b10a7bSpooka 	{ &vop_whiteout_desc,		tmpfs_whiteout },
112ec933656Sjmmv 	{ NULL, NULL }
113ec933656Sjmmv };
1149d8a0628Srmind 
1159d8a0628Srmind const struct vnodeopv_desc tmpfs_vnodeop_opv_desc = {
1169d8a0628Srmind 	&tmpfs_vnodeop_p, tmpfs_vnodeop_entries
1179d8a0628Srmind };
118ec933656Sjmmv 
11971cf548aSrmind /*
12060c9a518Srmind  * tmpfs_lookup: path name traversal routine.
12171cf548aSrmind  *
12271cf548aSrmind  * Arguments: dvp (directory being searched), vpp (result),
12371cf548aSrmind  * cnp (component name - path).
12471cf548aSrmind  *
12571cf548aSrmind  * => Caller holds a reference and lock on dvp.
12671cf548aSrmind  * => We return looked-up vnode (vpp) locked, with a reference held.
12771cf548aSrmind  */
128ec933656Sjmmv int
tmpfs_lookup(void * v)129ec933656Sjmmv tmpfs_lookup(void *v)
130ec933656Sjmmv {
13197834f7bShannken 	struct vop_lookup_v2_args /* {
13271cf548aSrmind 		struct vnode *a_dvp;
13371cf548aSrmind 		struct vnode **a_vpp;
13471cf548aSrmind 		struct componentname *a_cnp;
13571cf548aSrmind 	} */ *ap = v;
1369d8a0628Srmind 	vnode_t *dvp = ap->a_dvp, **vpp = ap->a_vpp;
13771cf548aSrmind 	struct componentname *cnp = ap->a_cnp;
138e9d92e9cSrmind 	const bool lastcn = (cnp->cn_flags & ISLASTCN) != 0;
139e9d92e9cSrmind 	tmpfs_node_t *dnode, *tnode;
1409d8a0628Srmind 	tmpfs_dirent_t *de;
1411617a81dSdholland 	int cachefound, iswhiteout;
14271cf548aSrmind 	int error;
143ec933656Sjmmv 
144ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
145ec933656Sjmmv 
146ec933656Sjmmv 	dnode = VP_TO_TMPFS_DIR(dvp);
147ec933656Sjmmv 	*vpp = NULL;
148ec933656Sjmmv 
1494781942cSrmind 	/* Check accessibility of directory. */
15061e8303eSpooka 	error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred);
1519d8a0628Srmind 	if (error) {
152ec933656Sjmmv 		goto out;
1539d8a0628Srmind 	}
1544781942cSrmind 
15571cf548aSrmind 	/*
15671cf548aSrmind 	 * If requesting the last path component on a read-only file system
15771cf548aSrmind 	 * with a write operation, deny it.
15871cf548aSrmind 	 */
159e9d92e9cSrmind 	if (lastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) != 0 &&
160ec933656Sjmmv 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
161ec933656Sjmmv 		error = EROFS;
162ec933656Sjmmv 		goto out;
163ec933656Sjmmv 	}
164ec933656Sjmmv 
16571cf548aSrmind 	/*
16671cf548aSrmind 	 * Avoid doing a linear scan of the directory if the requested
16771cf548aSrmind 	 * directory/name couple is already in the cache.
16871cf548aSrmind 	 */
16935ed6905Sdholland 	cachefound = cache_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen,
17035ed6905Sdholland 				  cnp->cn_nameiop, cnp->cn_flags,
17135ed6905Sdholland 				  &iswhiteout, vpp);
1721617a81dSdholland 	if (iswhiteout) {
1731617a81dSdholland 		cnp->cn_flags |= ISWHITEOUT;
1741617a81dSdholland 	}
1751617a81dSdholland 	if (cachefound && *vpp == NULLVP) {
1761617a81dSdholland 		/* Negative cache hit. */
1771617a81dSdholland 		error = ENOENT;
1788c80da52Shannken 		goto out;
1791617a81dSdholland 	} else if (cachefound) {
1801617a81dSdholland 		error = 0;
1818c80da52Shannken 		goto out;
18260c9a518Srmind 	}
183ec933656Sjmmv 
184905b6b77Shannken 	/*
185905b6b77Shannken 	 * Treat an unlinked directory as empty (no "." or "..")
186905b6b77Shannken 	 */
187905b6b77Shannken 	if (dnode->tn_links == 0) {
188905b6b77Shannken 		KASSERT(dnode->tn_size == 0);
189905b6b77Shannken 		error = ENOENT;
190905b6b77Shannken 		goto out;
191905b6b77Shannken 	}
192905b6b77Shannken 
193ec933656Sjmmv 	if (cnp->cn_flags & ISDOTDOT) {
1949d8a0628Srmind 		tmpfs_node_t *pnode;
1954781942cSrmind 
19660c9a518Srmind 		/*
19760c9a518Srmind 		 * Lookup of ".." case.
19860c9a518Srmind 		 */
1994781942cSrmind 		if (lastcn && cnp->cn_nameiop == RENAME) {
2004781942cSrmind 			error = EINVAL;
2014781942cSrmind 			goto out;
2024781942cSrmind 		}
2034781942cSrmind 		KASSERT(dnode->tn_type == VDIR);
20460c9a518Srmind 		pnode = dnode->tn_spec.tn_dir.tn_parent;
2054781942cSrmind 		if (pnode == NULL) {
2064781942cSrmind 			error = ENOENT;
207ced25823Sad 			goto done;
2084781942cSrmind 		}
2094781942cSrmind 
2108c80da52Shannken 		error = vcache_get(dvp->v_mount, &pnode, sizeof(pnode), vpp);
211ced25823Sad 		goto done;
212ec933656Sjmmv 	} else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
21360c9a518Srmind 		/*
21460c9a518Srmind 		 * Lookup of "." case.
21560c9a518Srmind 		 */
216e9d92e9cSrmind 		if (lastcn && cnp->cn_nameiop == RENAME) {
217083df92bSpooka 			error = EISDIR;
218083df92bSpooka 			goto out;
219083df92bSpooka 		}
220c3183f32Spooka 		vref(dvp);
221ec933656Sjmmv 		*vpp = dvp;
222ec933656Sjmmv 		error = 0;
22371cf548aSrmind 		goto done;
22471cf548aSrmind 	}
22571cf548aSrmind 
22660c9a518Srmind 	/*
22760c9a518Srmind 	 * Other lookup cases: perform directory scan.
22860c9a518Srmind 	 */
229ec933656Sjmmv 	de = tmpfs_dir_lookup(dnode, cnp);
23007b10a7bSpooka 	if (de == NULL || de->td_node == TMPFS_NODE_WHITEOUT) {
23171cf548aSrmind 		/*
23271cf548aSrmind 		 * The entry was not found in the directory.  This is valid
23371cf548aSrmind 		 * if we are creating or renaming an entry and are working
23471cf548aSrmind 		 * on the last component of the path name.
23571cf548aSrmind 		 */
236e9d92e9cSrmind 		if (lastcn && (cnp->cn_nameiop == CREATE ||
237ec933656Sjmmv 		    cnp->cn_nameiop == RENAME)) {
23861e8303eSpooka 			error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred);
23971cf548aSrmind 			if (error) {
240ec933656Sjmmv 				goto out;
24171cf548aSrmind 			}
242ec933656Sjmmv 			error = EJUSTRETURN;
243ec933656Sjmmv 		} else {
24471cf548aSrmind 			error = ENOENT;
24571cf548aSrmind 		}
24607b10a7bSpooka 		if (de) {
24707b10a7bSpooka 			KASSERT(de->td_node == TMPFS_NODE_WHITEOUT);
24807b10a7bSpooka 			cnp->cn_flags |= ISWHITEOUT;
24907b10a7bSpooka 		}
250e9d92e9cSrmind 		goto done;
251e9d92e9cSrmind 	}
252e9d92e9cSrmind 
253e9d92e9cSrmind 	tnode = de->td_node;
254ec933656Sjmmv 
25571cf548aSrmind 	/*
256e9d92e9cSrmind 	 * If it is not the last path component and found a non-directory
257e9d92e9cSrmind 	 * or non-link entry (which may itself be pointing to a directory),
258e9d92e9cSrmind 	 * raise an error.
25971cf548aSrmind 	 */
260e9d92e9cSrmind 	if (!lastcn && tnode->tn_type != VDIR && tnode->tn_type != VLNK) {
261ec933656Sjmmv 		error = ENOTDIR;
262ec933656Sjmmv 		goto out;
263ec933656Sjmmv 	}
264ec933656Sjmmv 
265e9d92e9cSrmind 	/* Check the permissions. */
266e9d92e9cSrmind 	if (lastcn && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
26761e8303eSpooka 		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred);
2680c9d8d15Selad 		if (error)
269ec933656Sjmmv 			goto out;
2700c9d8d15Selad 
2710c9d8d15Selad 		if ((dnode->tn_mode & S_ISTXT) != 0) {
2720c9d8d15Selad 			error = kauth_authorize_vnode(cnp->cn_cred,
2730c9d8d15Selad 			    KAUTH_VNODE_DELETE, tnode->tn_vnode,
2749aa2a9c3Schristos 			    dnode->tn_vnode, genfs_can_sticky(dvp, cnp->cn_cred,
2750c9d8d15Selad 			    dnode->tn_uid, tnode->tn_uid));
2760c9d8d15Selad 			if (error) {
2770c9d8d15Selad 				error = EPERM;
2780c9d8d15Selad 				goto out;
2790c9d8d15Selad 			}
28071cf548aSrmind 		}
28171cf548aSrmind 	}
282e9d92e9cSrmind 
2834781942cSrmind 	/* Get a vnode for the matching entry. */
2848c80da52Shannken 	error = vcache_get(dvp->v_mount, &tnode, sizeof(tnode), vpp);
28571cf548aSrmind done:
28671cf548aSrmind 	/*
28760c9a518Srmind 	 * Cache the result, unless request was for creation (as it does
28860c9a518Srmind 	 * not improve the performance).
28971cf548aSrmind 	 */
290d65753d9Srmind 	if (cnp->cn_nameiop != CREATE) {
29135ed6905Sdholland 		cache_enter(dvp, *vpp, cnp->cn_nameptr, cnp->cn_namelen,
29235ed6905Sdholland 			    cnp->cn_flags);
29360c9a518Srmind 	}
294ec933656Sjmmv out:
295c398ae97Schs 	KASSERT(VOP_ISLOCKED(dvp));
29607b10a7bSpooka 
297ec933656Sjmmv 	return error;
298ec933656Sjmmv }
299ec933656Sjmmv 
300ec933656Sjmmv int
tmpfs_create(void * v)301ec933656Sjmmv tmpfs_create(void *v)
302ec933656Sjmmv {
30304c776e5Shannken 	struct vop_create_v3_args /* {
3049d8a0628Srmind 		struct vnode		*a_dvp;
3059d8a0628Srmind 		struct vnode		**a_vpp;
3069d8a0628Srmind 		struct componentname	*a_cnp;
3079d8a0628Srmind 		struct vattr		*a_vap;
3089d8a0628Srmind 	} */ *ap = v;
3099d8a0628Srmind 	vnode_t *dvp = ap->a_dvp, **vpp = ap->a_vpp;
3109d8a0628Srmind 	struct componentname *cnp = ap->a_cnp;
3119d8a0628Srmind 	struct vattr *vap = ap->a_vap;
312ec933656Sjmmv 
3139d8a0628Srmind 	KASSERT(VOP_ISLOCKED(dvp));
314ec933656Sjmmv 	KASSERT(vap->va_type == VREG || vap->va_type == VSOCK);
315e63cf28eSrmind 	return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL);
316ec933656Sjmmv }
317ec933656Sjmmv 
318ec933656Sjmmv int
tmpfs_mknod(void * v)319ec933656Sjmmv tmpfs_mknod(void *v)
320ec933656Sjmmv {
32104c776e5Shannken 	struct vop_mknod_v3_args /* {
3229d8a0628Srmind 		struct vnode		*a_dvp;
3239d8a0628Srmind 		struct vnode		**a_vpp;
3249d8a0628Srmind 		struct componentname	*a_cnp;
3259d8a0628Srmind 		struct vattr		*a_vap;
3269d8a0628Srmind 	} */ *ap = v;
3279d8a0628Srmind 	vnode_t *dvp = ap->a_dvp, **vpp = ap->a_vpp;
3289d8a0628Srmind 	struct componentname *cnp = ap->a_cnp;
3299d8a0628Srmind 	struct vattr *vap = ap->a_vap;
3309d8a0628Srmind 	enum vtype vt = vap->va_type;
331ec933656Sjmmv 
3329d8a0628Srmind 	if (vt != VBLK && vt != VCHR && vt != VFIFO) {
333ce079fc9Srmind 		*vpp = NULL;
334ec933656Sjmmv 		return EINVAL;
3355ac22576Spooka 	}
336e63cf28eSrmind 	return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL);
337ec933656Sjmmv }
338ec933656Sjmmv 
339ec933656Sjmmv int
tmpfs_open(void * v)340ec933656Sjmmv tmpfs_open(void *v)
341ec933656Sjmmv {
3429d8a0628Srmind 	struct vop_open_args /* {
3439d8a0628Srmind 		struct vnode	*a_vp;
3449d8a0628Srmind 		int		a_mode;
3459d8a0628Srmind 		kauth_cred_t	a_cred;
3469d8a0628Srmind 	} */ *ap = v;
3479d8a0628Srmind 	vnode_t *vp = ap->a_vp;
3489d8a0628Srmind 	mode_t mode = ap->a_mode;
3499d8a0628Srmind 	tmpfs_node_t *node;
350ec933656Sjmmv 
351ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
352ec933656Sjmmv 
353ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
354ccb670c8Sjmmv 
355ec933656Sjmmv 	/* If the file is marked append-only, deny write requests. */
3569d8a0628Srmind 	if ((node->tn_flags & APPEND) != 0 &&
3579d8a0628Srmind 	    (mode & (FWRITE | O_APPEND)) == FWRITE) {
3589d8a0628Srmind 		return EPERM;
359ec933656Sjmmv 	}
3609d8a0628Srmind 	return 0;
3619d8a0628Srmind }
362ec933656Sjmmv 
363ec933656Sjmmv int
tmpfs_close(void * v)364ec933656Sjmmv tmpfs_close(void *v)
365ec933656Sjmmv {
3669d8a0628Srmind 	struct vop_close_args /* {
3679d8a0628Srmind 		struct vnode	*a_vp;
3689d8a0628Srmind 		int		a_fflag;
3699d8a0628Srmind 		kauth_cred_t	a_cred;
3709d8a0628Srmind 	} */ *ap = v;
371b05b0389Srmind 	vnode_t *vp __diagused = ap->a_vp;
372ec933656Sjmmv 
373ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
374a748ea88Syamt 	return 0;
375ec933656Sjmmv }
376ec933656Sjmmv 
3770adcb88bSrmind int
tmpfs_access(void * v)3780adcb88bSrmind tmpfs_access(void *v)
379ec933656Sjmmv {
3800adcb88bSrmind 	struct vop_access_args /* {
3810adcb88bSrmind 		struct vnode	*a_vp;
3829aa2a9c3Schristos 		accmode_t	a_accmode;
3830adcb88bSrmind 		kauth_cred_t	a_cred;
3840adcb88bSrmind 	} */ *ap = v;
3850adcb88bSrmind 	vnode_t *vp = ap->a_vp;
3869aa2a9c3Schristos 	accmode_t accmode = ap->a_accmode;
3870adcb88bSrmind 	kauth_cred_t cred = ap->a_cred;
3880adcb88bSrmind 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
3899aa2a9c3Schristos 	const bool writing = (accmode & VWRITE) != 0;
390ec933656Sjmmv 
3910adcb88bSrmind 	KASSERT(VOP_ISLOCKED(vp));
3920adcb88bSrmind 
3930adcb88bSrmind 	/* Possible? */
394ec933656Sjmmv 	switch (vp->v_type) {
395ec933656Sjmmv 	case VDIR:
396ec933656Sjmmv 	case VLNK:
397ec933656Sjmmv 	case VREG:
3989d8a0628Srmind 		if (writing && (vp->v_mount->mnt_flag & MNT_RDONLY) != 0) {
3999d8a0628Srmind 			return EROFS;
400ec933656Sjmmv 		}
401ec933656Sjmmv 		break;
402ec933656Sjmmv 	case VBLK:
403ec933656Sjmmv 	case VCHR:
404ec933656Sjmmv 	case VSOCK:
405ec933656Sjmmv 	case VFIFO:
406ec933656Sjmmv 		break;
407ec933656Sjmmv 	default:
4089d8a0628Srmind 		return EINVAL;
409ec933656Sjmmv 	}
4100adcb88bSrmind 	if (writing && (node->tn_flags & IMMUTABLE) != 0) {
4110adcb88bSrmind 		return EPERM;
412009f5d2fSelad 	}
413009f5d2fSelad 
4149aa2a9c3Schristos 	return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(accmode,
4159aa2a9c3Schristos 	    vp->v_type, node->tn_mode), vp, NULL, genfs_can_access(vp, cred,
4169aa2a9c3Schristos 	    node->tn_uid, node->tn_gid, node->tn_mode, NULL, accmode));
4179d8a0628Srmind }
418ec933656Sjmmv 
419ec933656Sjmmv int
tmpfs_getattr(void * v)420ec933656Sjmmv tmpfs_getattr(void *v)
421ec933656Sjmmv {
4229d8a0628Srmind 	struct vop_getattr_args /* {
4239d8a0628Srmind 		struct vnode	*a_vp;
4249d8a0628Srmind 		struct vattr	*a_vap;
4259d8a0628Srmind 		kauth_cred_t	a_cred;
4269d8a0628Srmind 	} */ *ap = v;
4279d8a0628Srmind 	vnode_t *vp = ap->a_vp;
4289d8a0628Srmind 	struct vattr *vap = ap->a_vap;
4299d8a0628Srmind 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
430ec933656Sjmmv 
431c3183f32Spooka 	vattr_null(vap);
432ec933656Sjmmv 
433ec933656Sjmmv 	vap->va_type = vp->v_type;
434ec933656Sjmmv 	vap->va_mode = node->tn_mode;
435ec933656Sjmmv 	vap->va_nlink = node->tn_links;
436ec933656Sjmmv 	vap->va_uid = node->tn_uid;
437ec933656Sjmmv 	vap->va_gid = node->tn_gid;
438ec933656Sjmmv 	vap->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
439ec933656Sjmmv 	vap->va_fileid = node->tn_id;
440ec933656Sjmmv 	vap->va_size = node->tn_size;
441ec933656Sjmmv 	vap->va_blocksize = PAGE_SIZE;
4424781942cSrmind 	vap->va_gen = TMPFS_NODE_GEN(node);
443ec933656Sjmmv 	vap->va_flags = node->tn_flags;
444ec933656Sjmmv 	vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ?
445064fbe7eSjmmv 	    node->tn_spec.tn_dev.tn_rdev : VNOVAL;
446ec933656Sjmmv 	vap->va_bytes = round_page(node->tn_size);
447ec933656Sjmmv 	vap->va_filerev = VNOVAL;
448ec933656Sjmmv 	vap->va_vaflags = 0;
449ec933656Sjmmv 	vap->va_spare = VNOVAL; /* XXX */
450ec933656Sjmmv 
4515c373ea8Sad 	mutex_enter(&node->tn_timelock);
4525c373ea8Sad 	tmpfs_update_locked(vp, 0);
4535c373ea8Sad 	vap->va_atime = node->tn_atime;
4545c373ea8Sad 	vap->va_mtime = node->tn_mtime;
4555c373ea8Sad 	vap->va_ctime = node->tn_ctime;
4565c373ea8Sad 	vap->va_birthtime = node->tn_birthtime;
4575c373ea8Sad 	mutex_exit(&node->tn_timelock);
4585c373ea8Sad 
459ec933656Sjmmv 	return 0;
460ec933656Sjmmv }
461ec933656Sjmmv 
462ec933656Sjmmv int
tmpfs_setattr(void * v)463ec933656Sjmmv tmpfs_setattr(void *v)
464ec933656Sjmmv {
4659d8a0628Srmind 	struct vop_setattr_args /* {
4669d8a0628Srmind 		struct vnode	*a_vp;
4679d8a0628Srmind 		struct vattr	*a_vap;
4689d8a0628Srmind 		kauth_cred_t	a_cred;
4699d8a0628Srmind 	} */ *ap = v;
4709d8a0628Srmind 	vnode_t *vp = ap->a_vp;
4719d8a0628Srmind 	struct vattr *vap = ap->a_vap;
4729d8a0628Srmind 	kauth_cred_t cred = ap->a_cred;
4739d8a0628Srmind 	lwp_t *l = curlwp;
4749d8a0628Srmind 	int error = 0;
475ec933656Sjmmv 
476ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
477ec933656Sjmmv 
478ec933656Sjmmv 	/* Abort if any unsettable attribute is given. */
4799d8a0628Srmind 	if (vap->va_type != VNON || vap->va_nlink != VNOVAL ||
4809d8a0628Srmind 	    vap->va_fsid != VNOVAL || vap->va_fileid != VNOVAL ||
481e63cf28eSrmind 	    vap->va_blocksize != VNOVAL || vap->va_ctime.tv_sec != VNOVAL ||
4829d8a0628Srmind 	    vap->va_gen != VNOVAL || vap->va_rdev != VNOVAL ||
4839d8a0628Srmind 	    vap->va_bytes != VNOVAL) {
4849d8a0628Srmind 		return EINVAL;
4859d8a0628Srmind 	}
486e63cf28eSrmind 
487e63cf28eSrmind 	if (error == 0 && vap->va_flags != VNOVAL)
488f474dcebSad 		error = tmpfs_chflags(vp, vap->va_flags, cred, l);
489ec933656Sjmmv 
490e63cf28eSrmind 	if (error == 0 && vap->va_size != VNOVAL)
491f474dcebSad 		error = tmpfs_chsize(vp, vap->va_size, cred, l);
492ec933656Sjmmv 
493ec933656Sjmmv 	if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL))
494f474dcebSad 		error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, l);
495ec933656Sjmmv 
496e63cf28eSrmind 	if (error == 0 && vap->va_mode != VNOVAL)
497f474dcebSad 		error = tmpfs_chmod(vp, vap->va_mode, cred, l);
498ec933656Sjmmv 
499e63cf28eSrmind 	const bool chsometime =
500e63cf28eSrmind 	    vap->va_atime.tv_sec != VNOVAL ||
501e63cf28eSrmind 	    vap->va_mtime.tv_sec != VNOVAL ||
502e63cf28eSrmind 	    vap->va_birthtime.tv_sec != VNOVAL;
503e63cf28eSrmind 	if (error == 0 && chsometime) {
5049d8a0628Srmind 		error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime,
5059d8a0628Srmind 		    &vap->va_birthtime, vap->va_vaflags, cred, l);
5069d8a0628Srmind 	}
507ec933656Sjmmv 	return error;
508ec933656Sjmmv }
509ec933656Sjmmv 
510ec933656Sjmmv int
tmpfs_read(void * v)511ec933656Sjmmv tmpfs_read(void *v)
512ec933656Sjmmv {
5139d8a0628Srmind 	struct vop_read_args /* {
5149d8a0628Srmind 		struct vnode *a_vp;
5159d8a0628Srmind 		struct uio *a_uio;
5169d8a0628Srmind 		int a_ioflag;
5179d8a0628Srmind 		kauth_cred_t a_cred;
5189d8a0628Srmind 	} */ *ap = v;
5199d8a0628Srmind 	vnode_t *vp = ap->a_vp;
5209d8a0628Srmind 	struct uio *uio = ap->a_uio;
5219d8a0628Srmind 	const int ioflag = ap->a_ioflag;
5229d8a0628Srmind 	tmpfs_node_t *node;
523647aa775Syamt 	struct uvm_object *uobj;
5249d8a0628Srmind 	int error;
525ec933656Sjmmv 
526ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
527ec933656Sjmmv 
5281036f1d9Smaxv 	if (vp->v_type == VDIR) {
5291036f1d9Smaxv 		return EISDIR;
5301036f1d9Smaxv 	}
5316b47753fSmaxv 	if (uio->uio_offset < 0 || vp->v_type != VREG) {
5329d8a0628Srmind 		return EINVAL;
533ec933656Sjmmv 	}
534ec933656Sjmmv 
535e63cf28eSrmind 	/* Note: reading zero bytes should not update atime. */
536e63cf28eSrmind 	if (uio->uio_resid == 0) {
537e63cf28eSrmind 		return 0;
538e63cf28eSrmind 	}
539e63cf28eSrmind 
5409d8a0628Srmind 	node = VP_TO_TMPFS_NODE(vp);
541064fbe7eSjmmv 	uobj = node->tn_spec.tn_reg.tn_aobj;
542ec933656Sjmmv 	error = 0;
5439d8a0628Srmind 
5445f4b660eSjmmv 	while (error == 0 && uio->uio_resid > 0) {
545647aa775Syamt 		vsize_t len;
546ec933656Sjmmv 
5479d8a0628Srmind 		if (node->tn_size <= uio->uio_offset) {
5487720dda1Syamt 			break;
5499d8a0628Srmind 		}
550ec933656Sjmmv 		len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid);
5519d8a0628Srmind 		if (len == 0) {
552647aa775Syamt 			break;
5539d8a0628Srmind 		}
554b4099c3eSpooka 		error = ubc_uiomove(uobj, uio, len, IO_ADV_DECODE(ioflag),
555f5ad84fdSad 		    UBC_READ | UBC_PARTIALOK | UBC_VNODE_FLAGS(vp));
556647aa775Syamt 	}
557e63cf28eSrmind 
558*06cc9cbbShannken 	if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
559e63cf28eSrmind 		tmpfs_update(vp, TMPFS_UPDATE_ATIME);
560*06cc9cbbShannken 
561ec933656Sjmmv 	return error;
562ec933656Sjmmv }
563ec933656Sjmmv 
564ec933656Sjmmv int
tmpfs_write(void * v)565ec933656Sjmmv tmpfs_write(void *v)
566ec933656Sjmmv {
5679d8a0628Srmind 	struct vop_write_args /* {
5689d8a0628Srmind 		struct vnode	*a_vp;
5699d8a0628Srmind 		struct uio	*a_uio;
5709d8a0628Srmind 		int		a_ioflag;
5719d8a0628Srmind 		kauth_cred_t	a_cred;
5729d8a0628Srmind 	} */ *ap = v;
5739d8a0628Srmind 	vnode_t *vp = ap->a_vp;
5749d8a0628Srmind 	struct uio *uio = ap->a_uio;
5759d8a0628Srmind 	const int ioflag = ap->a_ioflag;
5769d8a0628Srmind 	tmpfs_node_t *node;
5779d8a0628Srmind 	struct uvm_object *uobj;
5789dbda5eaSmartin 	off_t oldsize;
579913a7799Sad 	int error, ubc_flags;
580ec933656Sjmmv 
581ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
582ec933656Sjmmv 
583641d22c5Skardel 	node = VP_TO_TMPFS_NODE(vp);
584641d22c5Skardel 	oldsize = node->tn_size;
585641d22c5Skardel 
586b2f46950Sjoerg 	if ((vp->v_mount->mnt_flag & MNT_RDONLY) != 0) {
587b2f46950Sjoerg 		error = EROFS;
588b2f46950Sjoerg 		goto out;
589b2f46950Sjoerg 	}
590b2f46950Sjoerg 
591ec933656Sjmmv 	if (uio->uio_offset < 0 || vp->v_type != VREG) {
592ec933656Sjmmv 		error = EINVAL;
593ec933656Sjmmv 		goto out;
594ec933656Sjmmv 	}
595ec933656Sjmmv 	if (uio->uio_resid == 0) {
596ec933656Sjmmv 		error = 0;
597ec933656Sjmmv 		goto out;
598ec933656Sjmmv 	}
5999d8a0628Srmind 	if (ioflag & IO_APPEND) {
600ec933656Sjmmv 		uio->uio_offset = node->tn_size;
6019d8a0628Srmind 	}
602ec933656Sjmmv 
603e63cf28eSrmind 	if (uio->uio_offset + uio->uio_resid > node->tn_size) {
604ec933656Sjmmv 		error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid);
6059d8a0628Srmind 		if (error)
606ec933656Sjmmv 			goto out;
607ec933656Sjmmv 	}
608ec933656Sjmmv 
609913a7799Sad 	/*
610913a7799Sad 	 * If we're extending the file and have data to write that would
611913a7799Sad 	 * not leave an un-zeroed hole, we can avoid fault processing and
612913a7799Sad 	 * zeroing of pages on allocation.
613913a7799Sad 	 *
614913a7799Sad 	 * Don't do this if the file is mapped and we need to touch an
615913a7799Sad 	 * existing page, because writing a mapping of the file into itself
616913a7799Sad 	 * could cause a deadlock on PG_BUSY.
617913a7799Sad 	 *
618913a7799Sad 	 * New pages will not become visible until finished here (because
619913a7799Sad 	 * of PG_BUSY and the vnode lock).
620913a7799Sad 	 */
621913a7799Sad 	ubc_flags = UBC_WRITE | UBC_VNODE_FLAGS(vp);
62281a07a71Schs #if 0
62381a07a71Schs 	/*
62481a07a71Schs 	 * XXX disable use of UBC_FAULTBUSY for now, this check is insufficient
62581a07a71Schs 	 * because it does not zero uninitialized parts of pages in all of
62681a07a71Schs 	 * the cases where zeroing is needed.
62781a07a71Schs 	 */
628913a7799Sad 	if (uio->uio_offset >= oldsize &&
629913a7799Sad 	    ((uio->uio_offset & (PAGE_SIZE - 1)) == 0 ||
630913a7799Sad 	    ((vp->v_vflag & VV_MAPPED) == 0 &&
631913a7799Sad 	    trunc_page(uio->uio_offset) == trunc_page(oldsize)))) {
632913a7799Sad 		ubc_flags |= UBC_FAULTBUSY;
633913a7799Sad 	}
63481a07a71Schs #endif
635913a7799Sad 
636064fbe7eSjmmv 	uobj = node->tn_spec.tn_reg.tn_aobj;
637647aa775Syamt 	error = 0;
6385f4b660eSjmmv 	while (error == 0 && uio->uio_resid > 0) {
639647aa775Syamt 		vsize_t len;
640647aa775Syamt 
641647aa775Syamt 		len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid);
6429d8a0628Srmind 		if (len == 0) {
643647aa775Syamt 			break;
6449d8a0628Srmind 		}
645b4099c3eSpooka 		error = ubc_uiomove(uobj, uio, len, IO_ADV_DECODE(ioflag),
646913a7799Sad 		    ubc_flags);
647647aa775Syamt 	}
6489d8a0628Srmind 	if (error) {
6499d8a0628Srmind 		(void)tmpfs_reg_resize(vp, oldsize);
6509d8a0628Srmind 	}
651647aa775Syamt 
652e63cf28eSrmind 	tmpfs_update(vp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
653ec933656Sjmmv out:
6549d8a0628Srmind 	if (error) {
6559d8a0628Srmind 		KASSERT(oldsize == node->tn_size);
6569d8a0628Srmind 	} else {
6579d8a0628Srmind 		KASSERT(uio->uio_resid == 0);
6589d8a0628Srmind 	}
659ec933656Sjmmv 	return error;
660ec933656Sjmmv }
661ec933656Sjmmv 
662ec933656Sjmmv int
tmpfs_fsync(void * v)663ec933656Sjmmv tmpfs_fsync(void *v)
664ec933656Sjmmv {
6659d8a0628Srmind 	struct vop_fsync_args /* {
6669d8a0628Srmind 		struct vnode *a_vp;
6679d8a0628Srmind 		kauth_cred_t a_cred;
6689d8a0628Srmind 		int a_flags;
6699d8a0628Srmind 		off_t a_offlo;
6709d8a0628Srmind 		off_t a_offhi;
6719d8a0628Srmind 		struct lwp *a_l;
6729d8a0628Srmind 	} */ *ap = v;
673b05b0389Srmind 	vnode_t *vp __diagused = ap->a_vp;
674ec933656Sjmmv 
675e63cf28eSrmind 	/* Nothing to do.  Should be up to date. */
676ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
677a748ea88Syamt 	return 0;
678ec933656Sjmmv }
679ec933656Sjmmv 
6809d8a0628Srmind /*
6819d8a0628Srmind  * tmpfs_remove: unlink a file.
6829d8a0628Srmind  *
6839d8a0628Srmind  * => Both directory (dvp) and file (vp) are locked.
6849d8a0628Srmind  * => We unlock and drop the reference on both.
6859d8a0628Srmind  */
686ec933656Sjmmv int
tmpfs_remove(void * v)687ec933656Sjmmv tmpfs_remove(void *v)
688ec933656Sjmmv {
689982ae832Sthorpej 	struct vop_remove_v3_args /* {
69060c9a518Srmind 		struct vnode *a_dvp;
69160c9a518Srmind 		struct vnode *a_vp;
69260c9a518Srmind 		struct componentname *a_cnp;
693982ae832Sthorpej 		nlink_t ctx_vp_new_nlink;
69460c9a518Srmind 	} */ *ap = v;
6959d8a0628Srmind 	vnode_t *dvp = ap->a_dvp, *vp = ap->a_vp;
69649ce9c94Srmind 	tmpfs_node_t *dnode, *node;
6979d8a0628Srmind 	tmpfs_dirent_t *de;
6985c373ea8Sad 	int error, tflags;
699ec933656Sjmmv 
700ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
701ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
702ec933656Sjmmv 
7034f931d80Spooka 	if (vp->v_type == VDIR) {
7044f931d80Spooka 		error = EPERM;
7054f931d80Spooka 		goto out;
7064f931d80Spooka 	}
70749ce9c94Srmind 	dnode = VP_TO_TMPFS_DIR(dvp);
708ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
709ec933656Sjmmv 
71049ce9c94Srmind 	/*
71149ce9c94Srmind 	 * Files marked as immutable or append-only cannot be deleted.
71249ce9c94Srmind 	 * Likewise, files residing on directories marked as append-only
71349ce9c94Srmind 	 * cannot be deleted.
71449ce9c94Srmind 	 */
715ec933656Sjmmv 	if (node->tn_flags & (IMMUTABLE | APPEND)) {
716ec933656Sjmmv 		error = EPERM;
717ec933656Sjmmv 		goto out;
718ec933656Sjmmv 	}
71949ce9c94Srmind 	if (dnode->tn_flags & APPEND) {
72049ce9c94Srmind 		error = EPERM;
72149ce9c94Srmind 		goto out;
72249ce9c94Srmind 	}
723ec933656Sjmmv 
7244781942cSrmind 	/* Lookup the directory entry (check the cached hint first). */
7254781942cSrmind 	de = tmpfs_dir_cached(node);
7264781942cSrmind 	if (de == NULL) {
7274781942cSrmind 		struct componentname *cnp = ap->a_cnp;
72860c9a518Srmind 		de = tmpfs_dir_lookup(dnode, cnp);
7294781942cSrmind 	}
73060c9a518Srmind 	KASSERT(de && de->td_node == node);
731ec933656Sjmmv 
73260c9a518Srmind 	/*
7334781942cSrmind 	 * Remove the entry from the directory (drops the link count) and
734e63cf28eSrmind 	 * destroy it or replace with a whiteout.
735e63cf28eSrmind 	 *
736e63cf28eSrmind 	 * Note: the inode referred by it will not be destroyed until the
737e63cf28eSrmind 	 * vnode is reclaimed/recycled.
73860c9a518Srmind 	 */
739e63cf28eSrmind 
7401f5dbc94Srmind 	tmpfs_dir_detach(dnode, de);
741e63cf28eSrmind 
742f68873a3Shannken 	if (ap->a_cnp->cn_flags & DOWHITEOUT)
7431f5dbc94Srmind 		tmpfs_dir_attach(dnode, de, TMPFS_NODE_WHITEOUT);
744f68873a3Shannken 	else
7454781942cSrmind 		tmpfs_free_dirent(VFS_TO_TMPFS(vp->v_mount), de);
74649ce9c94Srmind 
7475c373ea8Sad 	tflags = TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME;
748be677426Srmind 	if (node->tn_links > 0) {
749be677426Srmind 		/* We removed a hard link. */
7505c373ea8Sad 		tflags |= TMPFS_UPDATE_CTIME;
751be677426Srmind 	}
752982ae832Sthorpej 	ap->ctx_vp_new_nlink = node->tn_links;
7535c373ea8Sad 	tmpfs_update(dvp, tflags);
754ec933656Sjmmv 	error = 0;
755ec933656Sjmmv out:
7566fa7b158Sriastradh 	/* Drop the reference and unlock the node. */
7579d8a0628Srmind 	if (dvp == vp) {
7586fa7b158Sriastradh 		vrele(vp);
7599d8a0628Srmind 	} else {
7606fa7b158Sriastradh 		vput(vp);
7619d8a0628Srmind 	}
762ec933656Sjmmv 	return error;
763ec933656Sjmmv }
764ec933656Sjmmv 
765800683e3Srmind /*
7669d8a0628Srmind  * tmpfs_link: create a hard link.
767800683e3Srmind  */
768ec933656Sjmmv int
tmpfs_link(void * v)769ec933656Sjmmv tmpfs_link(void *v)
770ec933656Sjmmv {
77146e71c7dSriastradh 	struct vop_link_v2_args /* {
772800683e3Srmind 		struct vnode *a_dvp;
773800683e3Srmind 		struct vnode *a_vp;
774800683e3Srmind 		struct componentname *a_cnp;
775800683e3Srmind 	} */ *ap = v;
7769d8a0628Srmind 	vnode_t *dvp = ap->a_dvp;
7779d8a0628Srmind 	vnode_t *vp = ap->a_vp;
77860c9a518Srmind 	struct componentname *cnp = ap->a_cnp;
7791f5dbc94Srmind 	tmpfs_node_t *dnode, *node;
7809d8a0628Srmind 	tmpfs_dirent_t *de;
781800683e3Srmind 	int error;
782ec933656Sjmmv 
783800683e3Srmind 	KASSERT(dvp != vp);
784ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
785800683e3Srmind 	KASSERT(vp->v_type != VDIR);
786800683e3Srmind 	KASSERT(dvp->v_mount == vp->v_mount);
787ec933656Sjmmv 
7881f5dbc94Srmind 	dnode = VP_TO_TMPFS_DIR(dvp);
789ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
790ec933656Sjmmv 
7919abdb3b7Srmind 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
792ec933656Sjmmv 
793e9d92e9cSrmind 	/* Check for maximum number of links limit. */
794ec933656Sjmmv 	if (node->tn_links == LINK_MAX) {
795ec933656Sjmmv 		error = EMLINK;
796ec933656Sjmmv 		goto out;
797ec933656Sjmmv 	}
7984781942cSrmind 	KASSERT(node->tn_links < LINK_MAX);
799ec933656Sjmmv 
800ec933656Sjmmv 	/* We cannot create links of files marked immutable or append-only. */
801ec933656Sjmmv 	if (node->tn_flags & (IMMUTABLE | APPEND)) {
802ec933656Sjmmv 		error = EPERM;
803ec933656Sjmmv 		goto out;
804ec933656Sjmmv 	}
805ec933656Sjmmv 
8066a3c4a6fSchristos 	error = kauth_authorize_vnode(cnp->cn_cred, KAUTH_VNODE_ADD_LINK, vp,
8076a3c4a6fSchristos 	    dvp, 0);
8086a3c4a6fSchristos 	if (error)
8096a3c4a6fSchristos 		goto out;
8106a3c4a6fSchristos 
8114781942cSrmind 	/* Allocate a new directory entry to represent the inode. */
8124781942cSrmind 	error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount),
813ec933656Sjmmv 	    cnp->cn_nameptr, cnp->cn_namelen, &de);
8149d8a0628Srmind 	if (error) {
815ec933656Sjmmv 		goto out;
8169d8a0628Srmind 	}
817ec933656Sjmmv 
8184781942cSrmind 	/*
8194781942cSrmind 	 * Insert the entry into the directory.
8204781942cSrmind 	 * It will increase the inode link count.
8214781942cSrmind 	 */
8221f5dbc94Srmind 	tmpfs_dir_attach(dnode, de, node);
823e63cf28eSrmind 	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
824ec933656Sjmmv 
825982ae832Sthorpej 	/* Update the timestamps. */
826e63cf28eSrmind 	tmpfs_update(vp, TMPFS_UPDATE_CTIME);
827ec933656Sjmmv 	error = 0;
828ec933656Sjmmv out:
8291423e65bShannken 	VOP_UNLOCK(vp);
830ec933656Sjmmv 	return error;
831ec933656Sjmmv }
832ec933656Sjmmv 
833ec933656Sjmmv int
tmpfs_mkdir(void * v)834ec933656Sjmmv tmpfs_mkdir(void *v)
835ec933656Sjmmv {
83604c776e5Shannken 	struct vop_mkdir_v3_args /* {
8379d8a0628Srmind 		struct vnode		*a_dvp;
8389d8a0628Srmind 		struct vnode		**a_vpp;
8399d8a0628Srmind 		struct componentname	*a_cnp;
8409d8a0628Srmind 		struct vattr		*a_vap;
8419d8a0628Srmind 	} */ *ap = v;
8429d8a0628Srmind 	vnode_t *dvp = ap->a_dvp;
8439d8a0628Srmind 	vnode_t **vpp = ap->a_vpp;
8449d8a0628Srmind 	struct componentname *cnp = ap->a_cnp;
8459d8a0628Srmind 	struct vattr *vap = ap->a_vap;
846ec933656Sjmmv 
847ec933656Sjmmv 	KASSERT(vap->va_type == VDIR);
848e63cf28eSrmind 	return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL);
849ec933656Sjmmv }
850ec933656Sjmmv 
851ec933656Sjmmv int
tmpfs_rmdir(void * v)852ec933656Sjmmv tmpfs_rmdir(void *v)
853ec933656Sjmmv {
8546fa7b158Sriastradh 	struct vop_rmdir_v2_args /* {
8559d8a0628Srmind 		struct vnode		*a_dvp;
8569d8a0628Srmind 		struct vnode		*a_vp;
8579d8a0628Srmind 		struct componentname	*a_cnp;
8589d8a0628Srmind 	} */ *ap = v;
8599d8a0628Srmind 	vnode_t *dvp = ap->a_dvp;
8609d8a0628Srmind 	vnode_t *vp = ap->a_vp;
8619d8a0628Srmind 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount);
8629d8a0628Srmind 	tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp);
8639d8a0628Srmind 	tmpfs_node_t *node = VP_TO_TMPFS_DIR(vp);
8649d8a0628Srmind 	tmpfs_dirent_t *de;
8659d8a0628Srmind 	int error = 0;
866ec933656Sjmmv 
867ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
868ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
869ec933656Sjmmv 
8709d8a0628Srmind 	/*
871e63cf28eSrmind 	 * Directories with more than two entries ('.' and '..') cannot be
872e63cf28eSrmind 	 * removed.  There may be whiteout entries, which we will destroy.
8739d8a0628Srmind 	 */
8744f931d80Spooka 	if (node->tn_size > 0) {
875e63cf28eSrmind 		/*
876e63cf28eSrmind 		 * If never had whiteout entries, the directory is certainly
877e63cf28eSrmind 		 * not empty.  Otherwise, scan for any non-whiteout entry.
878e63cf28eSrmind 		 */
879e63cf28eSrmind 		if ((node->tn_gen & TMPFS_WHITEOUT_BIT) == 0) {
880e63cf28eSrmind 			error = ENOTEMPTY;
881e63cf28eSrmind 			goto out;
882e63cf28eSrmind 		}
883f68873a3Shannken 		TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {
884f68873a3Shannken 			if (de->td_node != TMPFS_NODE_WHITEOUT) {
8854f931d80Spooka 				error = ENOTEMPTY;
8864f931d80Spooka 				goto out;
8874f931d80Spooka 			}
888e63cf28eSrmind 		}
889e63cf28eSrmind 		KASSERT(error == 0);
890e63cf28eSrmind 	}
8914f931d80Spooka 
89265d0f000Spedro 	KASSERT(node->tn_spec.tn_dir.tn_parent == dnode);
89365d0f000Spedro 
8944781942cSrmind 	/* Lookup the directory entry (check the cached hint first). */
8954781942cSrmind 	de = tmpfs_dir_cached(node);
8964781942cSrmind 	if (de == NULL) {
8974781942cSrmind 		struct componentname *cnp = ap->a_cnp;
8984a780c9aSad 		de = tmpfs_dir_lookup(dnode, cnp);
8994781942cSrmind 	}
9009d8a0628Srmind 	KASSERT(de && de->td_node == node);
901ec933656Sjmmv 
902ec933656Sjmmv 	/* Check flags to see if we are allowed to remove the directory. */
903ec933656Sjmmv 	if (dnode->tn_flags & APPEND || node->tn_flags & (IMMUTABLE | APPEND)) {
904ec933656Sjmmv 		error = EPERM;
905ec933656Sjmmv 		goto out;
906ec933656Sjmmv 	}
907ec933656Sjmmv 
9084781942cSrmind 	/* Decrement the link count for the virtual '.' entry. */
909ec933656Sjmmv 	node->tn_links--;
910ec933656Sjmmv 
9114cffafacSrmind 	/* Detach the directory entry from the directory. */
9121f5dbc94Srmind 	tmpfs_dir_detach(dnode, de);
9134cffafacSrmind 
9149d8a0628Srmind 	/* Purge the cache for parent. */
9159d8a0628Srmind 	cache_purge(dvp);
916ec933656Sjmmv 
9179d8a0628Srmind 	/*
918f68873a3Shannken 	 * Destroy the directory entry or replace it with a whiteout.
919e63cf28eSrmind 	 *
920e63cf28eSrmind 	 * Note: the inode referred by it will not be destroyed until the
921e63cf28eSrmind 	 * vnode is reclaimed.
9229d8a0628Srmind 	 */
923f68873a3Shannken 	if (ap->a_cnp->cn_flags & DOWHITEOUT)
9241f5dbc94Srmind 		tmpfs_dir_attach(dnode, de, TMPFS_NODE_WHITEOUT);
925f68873a3Shannken 	else
9264781942cSrmind 		tmpfs_free_dirent(tmp, de);
927f68873a3Shannken 
928f68873a3Shannken 	/* Destroy the whiteout entries from the node. */
929f68873a3Shannken 	while ((de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir)) != NULL) {
930f68873a3Shannken 		KASSERT(de->td_node == TMPFS_NODE_WHITEOUT);
9311f5dbc94Srmind 		tmpfs_dir_detach(node, de);
932f68873a3Shannken 		tmpfs_free_dirent(tmp, de);
933f68873a3Shannken 	}
934e63cf28eSrmind 	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
935f68873a3Shannken 
936e63cf28eSrmind 	KASSERT(node->tn_size == 0);
9374a780c9aSad 	KASSERT(node->tn_links == 0);
938ec933656Sjmmv out:
9396fa7b158Sriastradh 	/* Release the node. */
9406fa7b158Sriastradh 	KASSERT(dvp != vp);
941ec933656Sjmmv 	vput(vp);
942ec933656Sjmmv 	return error;
943ec933656Sjmmv }
944ec933656Sjmmv 
945ec933656Sjmmv int
tmpfs_symlink(void * v)946ec933656Sjmmv tmpfs_symlink(void *v)
947ec933656Sjmmv {
94804c776e5Shannken 	struct vop_symlink_v3_args /* {
9499d8a0628Srmind 		struct vnode		*a_dvp;
9509d8a0628Srmind 		struct vnode		**a_vpp;
9519d8a0628Srmind 		struct componentname	*a_cnp;
9529d8a0628Srmind 		struct vattr		*a_vap;
9539d8a0628Srmind 		char			*a_target;
9549d8a0628Srmind 	} */ *ap = v;
9559d8a0628Srmind 	vnode_t *dvp = ap->a_dvp;
9569d8a0628Srmind 	vnode_t **vpp = ap->a_vpp;
9579d8a0628Srmind 	struct componentname *cnp = ap->a_cnp;
9589d8a0628Srmind 	struct vattr *vap = ap->a_vap;
9599d8a0628Srmind 	char *target = ap->a_target;
960ec933656Sjmmv 
961ec933656Sjmmv 	KASSERT(vap->va_type == VLNK);
962e63cf28eSrmind 	return tmpfs_construct_node(dvp, vpp, vap, cnp, target);
963ec933656Sjmmv }
964ec933656Sjmmv 
965ec933656Sjmmv int
tmpfs_readdir(void * v)966ec933656Sjmmv tmpfs_readdir(void *v)
967ec933656Sjmmv {
9689d8a0628Srmind 	struct vop_readdir_args /* {
9699d8a0628Srmind 		struct vnode	*a_vp;
9709d8a0628Srmind 		struct uio	*a_uio;
9719d8a0628Srmind 		kauth_cred_t	a_cred;
9729d8a0628Srmind 		int		*a_eofflag;
9739d8a0628Srmind 		off_t		**a_cookies;
9749d8a0628Srmind 		int		*ncookies;
9759d8a0628Srmind 	} */ *ap = v;
9769d8a0628Srmind 	vnode_t *vp = ap->a_vp;
9779d8a0628Srmind 	struct uio *uio = ap->a_uio;
9789d8a0628Srmind 	int *eofflag = ap->a_eofflag;
9799d8a0628Srmind 	off_t **cookies = ap->a_cookies;
9809d8a0628Srmind 	int *ncookies = ap->a_ncookies;
9819d8a0628Srmind 	off_t startoff, cnt;
9829d8a0628Srmind 	tmpfs_node_t *node;
983ec933656Sjmmv 	int error;
984ec933656Sjmmv 
985ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
986ec933656Sjmmv 
987ec933656Sjmmv 	/* This operation only makes sense on directory nodes. */
988ec933656Sjmmv 	if (vp->v_type != VDIR) {
9899d8a0628Srmind 		return ENOTDIR;
990ec933656Sjmmv 	}
991ec933656Sjmmv 	node = VP_TO_TMPFS_DIR(vp);
992ec933656Sjmmv 	startoff = uio->uio_offset;
9938e6209cfSyamt 	cnt = 0;
9941f5dbc94Srmind 
9951f5dbc94Srmind 	/*
9961f5dbc94Srmind 	 * Retrieve the directory entries, unless it is being destroyed.
9971f5dbc94Srmind 	 */
9981f5dbc94Srmind 	if (node->tn_links) {
9991f5dbc94Srmind 		error = tmpfs_dir_getdents(node, uio, &cnt);
10001f5dbc94Srmind 	} else {
100145153411Schs 		error = 0;
100245153411Schs 	}
10039d8a0628Srmind 
10049d8a0628Srmind 	if (eofflag != NULL) {
10051f5dbc94Srmind 		*eofflag = !error && uio->uio_offset == TMPFS_DIRSEQ_EOF;
10069d8a0628Srmind 	}
10079d8a0628Srmind 	if (error || cookies == NULL || ncookies == NULL) {
10089d8a0628Srmind 		return error;
10099d8a0628Srmind 	}
1010ec933656Sjmmv 
10119d8a0628Srmind 	/* Update NFS-related variables, if any. */
10129d8a0628Srmind 	tmpfs_dirent_t *de = NULL;
10131f5dbc94Srmind 	off_t i, off = startoff;
1014ec933656Sjmmv 
10158e6209cfSyamt 	*cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK);
10169d8a0628Srmind 	*ncookies = cnt;
1017ec933656Sjmmv 
10188e6209cfSyamt 	for (i = 0; i < cnt; i++) {
10191f5dbc94Srmind 		KASSERT(off != TMPFS_DIRSEQ_EOF);
10201f5dbc94Srmind 		if (off != TMPFS_DIRSEQ_DOT) {
10211f5dbc94Srmind 			if (off == TMPFS_DIRSEQ_DOTDOT) {
10229d8a0628Srmind 				de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir);
10238e6209cfSyamt 			} else if (de != NULL) {
10248e6209cfSyamt 				de = TAILQ_NEXT(de, td_entries);
10258e6209cfSyamt 			} else {
10261f5dbc94Srmind 				de = tmpfs_dir_lookupbyseq(node, off);
10278e6209cfSyamt 				KASSERT(de != NULL);
10288e6209cfSyamt 				de = TAILQ_NEXT(de, td_entries);
10298e6209cfSyamt 			}
10308e6209cfSyamt 			if (de == NULL) {
10311f5dbc94Srmind 				off = TMPFS_DIRSEQ_EOF;
10328e6209cfSyamt 			} else {
10331f5dbc94Srmind 				off = tmpfs_dir_getseq(node, de);
10348e6209cfSyamt 			}
10359d8a0628Srmind 		} else {
10361f5dbc94Srmind 			off = TMPFS_DIRSEQ_DOTDOT;
10378e6209cfSyamt 		}
10388e6209cfSyamt 		(*cookies)[i] = off;
10398e6209cfSyamt 	}
10408e6209cfSyamt 	KASSERT(uio->uio_offset == off);
1041ec933656Sjmmv 	return error;
1042ec933656Sjmmv }
1043ec933656Sjmmv 
1044ec933656Sjmmv int
tmpfs_readlink(void * v)1045ec933656Sjmmv tmpfs_readlink(void *v)
1046ec933656Sjmmv {
10479d8a0628Srmind 	struct vop_readlink_args /* {
10489d8a0628Srmind 		struct vnode	*a_vp;
10499d8a0628Srmind 		struct uio	*a_uio;
10509d8a0628Srmind 		kauth_cred_t	a_cred;
10519d8a0628Srmind 	} */ *ap = v;
10529d8a0628Srmind 	vnode_t *vp = ap->a_vp;
10539d8a0628Srmind 	struct uio *uio = ap->a_uio;
1054f8abe6cbSrmind 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
1055ec933656Sjmmv 	int error;
1056ec933656Sjmmv 
1057ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1058ec933656Sjmmv 	KASSERT(uio->uio_offset == 0);
1059ec933656Sjmmv 	KASSERT(vp->v_type == VLNK);
1060ec933656Sjmmv 
106120a51a97Srmind 	/* Note: readlink(2) returns the path without NUL terminator. */
106220a51a97Srmind 	if (node->tn_size > 0) {
1063064fbe7eSjmmv 		error = uiomove(node->tn_spec.tn_lnk.tn_link,
10641b265e67Sgson 		    MIN(node->tn_size, uio->uio_resid), uio);
106520a51a97Srmind 	} else {
106620a51a97Srmind 		error = 0;
106720a51a97Srmind 	}
1068e63cf28eSrmind 	tmpfs_update(vp, TMPFS_UPDATE_ATIME);
1069ec933656Sjmmv 
1070ec933656Sjmmv 	return error;
1071ec933656Sjmmv }
1072ec933656Sjmmv 
1073ec933656Sjmmv int
tmpfs_inactive(void * v)1074ec933656Sjmmv tmpfs_inactive(void *v)
1075ec933656Sjmmv {
107687fb3229Sriastradh 	struct vop_inactive_v2_args /* {
107760c9a518Srmind 		struct vnode *a_vp;
107860c9a518Srmind 		bool *a_recycle;
107960c9a518Srmind 	} */ *ap = v;
10809d8a0628Srmind 	vnode_t *vp = ap->a_vp;
10819d8a0628Srmind 	tmpfs_node_t *node;
1082d39cb654Sad 	int error = 0;
1083ec933656Sjmmv 
1084ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1085ec933656Sjmmv 
1086ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
10878ea5485dShannken 	if (node->tn_links == 0) {
10888ea5485dShannken 		/*
10898ea5485dShannken 		 * Mark node as dead by setting its generation to zero.
10908ea5485dShannken 		 */
10918ea5485dShannken 		atomic_and_32(&node->tn_gen, ~TMPFS_NODE_GEN_MASK);
1092d39cb654Sad 
1093d39cb654Sad 		/*
1094d39cb654Sad 		 * If the file has been deleted, truncate it, otherwise VFS
1095d39cb654Sad 		 * will quite rightly try to write back dirty data, which in
1096d39cb654Sad 		 * the case of tmpfs/UAO means needless page deactivations.
1097d39cb654Sad 		 */
1098d39cb654Sad 		if (vp->v_type == VREG) {
1099d39cb654Sad 			error = tmpfs_reg_resize(vp, 0);
1100d39cb654Sad 		}
11018ea5485dShannken 		*ap->a_recycle = true;
11028ea5485dShannken 	} else {
11035c373ea8Sad 		tmpfs_update(vp, 0);
11048ea5485dShannken 		*ap->a_recycle = false;
11058ea5485dShannken 	}
1106ec933656Sjmmv 
1107d39cb654Sad 	return error;
1108ec933656Sjmmv }
1109ec933656Sjmmv 
1110ec933656Sjmmv int
tmpfs_reclaim(void * v)1111ec933656Sjmmv tmpfs_reclaim(void *v)
1112ec933656Sjmmv {
11137f7aad09Sriastradh 	struct vop_reclaim_v2_args /* {
111460c9a518Srmind 		struct vnode *a_vp;
111560c9a518Srmind 	} */ *ap = v;
11169d8a0628Srmind 	vnode_t *vp = ap->a_vp;
11179d8a0628Srmind 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(vp->v_mount);
11189d8a0628Srmind 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
1119ec933656Sjmmv 
11207f7aad09Sriastradh 	/* Unlock vnode.  We still have exclusive access to it. */
11217f7aad09Sriastradh 	VOP_UNLOCK(vp);
11227f7aad09Sriastradh 
112360c9a518Srmind 	/* Disassociate inode from vnode. */
11244781942cSrmind 	node->tn_vnode = NULL;
11254781942cSrmind 	vp->v_data = NULL;
1126ccc45228Srmind 
1127ccc45228Srmind 	/* If inode is not referenced, i.e. no links, then destroy it. */
11288c80da52Shannken 	if (node->tn_links == 0)
112960c9a518Srmind 		tmpfs_free_node(tmp, node);
1130ec933656Sjmmv 	return 0;
1131ec933656Sjmmv }
1132ec933656Sjmmv 
1133ec933656Sjmmv int
tmpfs_pathconf(void * v)1134ec933656Sjmmv tmpfs_pathconf(void *v)
1135ec933656Sjmmv {
11369d8a0628Srmind 	struct vop_pathconf_args /* {
11379d8a0628Srmind 		struct vnode	*a_vp;
11389d8a0628Srmind 		int		a_name;
11399d8a0628Srmind 		register_t	*a_retval;
11409d8a0628Srmind 	} */ *ap = v;
11419d8a0628Srmind 	register_t *retval = ap->a_retval;
1142ec933656Sjmmv 
114379e3c74fSchristos 	switch (ap->a_name) {
1144ec933656Sjmmv 	case _PC_LINK_MAX:
1145ec933656Sjmmv 		*retval = LINK_MAX;
114679e3c74fSchristos 		return 0;
1147ec933656Sjmmv 	case _PC_NAME_MAX:
1148367fc932Schristos 		*retval = TMPFS_MAXNAMLEN;
114979e3c74fSchristos 		return 0;
1150ec933656Sjmmv 	case _PC_PATH_MAX:
1151ec933656Sjmmv 		*retval = PATH_MAX;
115279e3c74fSchristos 		return 0;
1153ec933656Sjmmv 	case _PC_PIPE_BUF:
1154ec933656Sjmmv 		*retval = PIPE_BUF;
115579e3c74fSchristos 		return 0;
1156ec933656Sjmmv 	case _PC_CHOWN_RESTRICTED:
1157ec933656Sjmmv 		*retval = 1;
115879e3c74fSchristos 		return 0;
1159ec933656Sjmmv 	case _PC_NO_TRUNC:
1160ec933656Sjmmv 		*retval = 1;
116179e3c74fSchristos 		return 0;
1162ec933656Sjmmv 	case _PC_SYNC_IO:
1163ec933656Sjmmv 		*retval = 1;
116479e3c74fSchristos 		return 0;
1165ec933656Sjmmv 	case _PC_FILESIZEBITS:
11664781942cSrmind 		*retval = sizeof(off_t) * CHAR_BIT;
116779e3c74fSchristos 		return 0;
1168ec933656Sjmmv 	default:
116979e3c74fSchristos 		return genfs_pathconf(ap);
1170ec933656Sjmmv 	}
1171ec933656Sjmmv }
1172ec933656Sjmmv 
1173ec933656Sjmmv int
tmpfs_advlock(void * v)1174b6d141c7Sjmmv tmpfs_advlock(void *v)
1175b6d141c7Sjmmv {
11769d8a0628Srmind 	struct vop_advlock_args /* {
11779d8a0628Srmind 		struct vnode	*a_vp;
11789d8a0628Srmind 		void *		a_id;
11799d8a0628Srmind 		int		a_op;
11809d8a0628Srmind 		struct flock	*a_fl;
11819d8a0628Srmind 		int		a_flags;
11829d8a0628Srmind 	} */ *ap = v;
11839d8a0628Srmind 	vnode_t *vp = ap->a_vp;
11849d8a0628Srmind 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
1185b6d141c7Sjmmv 
1186b6d141c7Sjmmv 	return lf_advlock(v, &node->tn_lockf, node->tn_size);
1187b6d141c7Sjmmv }
1188b6d141c7Sjmmv 
1189b6d141c7Sjmmv int
tmpfs_getpages(void * v)1190ec933656Sjmmv tmpfs_getpages(void *v)
1191ec933656Sjmmv {
11928f2efd02Srmind 	struct vop_getpages_args /* {
11938f2efd02Srmind 		struct vnode *a_vp;
11948f2efd02Srmind 		voff_t a_offset;
11958f2efd02Srmind 		struct vm_page **a_m;
11968f2efd02Srmind 		int *a_count;
11978f2efd02Srmind 		int a_centeridx;
11988f2efd02Srmind 		vm_prot_t a_access_type;
11998f2efd02Srmind 		int a_advice;
12008f2efd02Srmind 		int a_flags;
12018f2efd02Srmind 	} */ * const ap = v;
12029d8a0628Srmind 	vnode_t *vp = ap->a_vp;
12038f2efd02Srmind 	const voff_t offset = ap->a_offset;
12048f2efd02Srmind 	struct vm_page **pgs = ap->a_m;
12058f2efd02Srmind 	const int centeridx = ap->a_centeridx;
12068f2efd02Srmind 	const vm_prot_t access_type = ap->a_access_type;
12078f2efd02Srmind 	const int advice = ap->a_advice;
12088f2efd02Srmind 	const int flags = ap->a_flags;
12095c373ea8Sad 	int error, iflag, npages = *ap->a_count;
12109d8a0628Srmind 	tmpfs_node_t *node;
12115f4b660eSjmmv 	struct uvm_object *uobj;
1212ec933656Sjmmv 
1213647aa775Syamt 	KASSERT(vp->v_type == VREG);
1214d2a0ebb6Sad 	KASSERT(rw_lock_held(vp->v_uobj.vmobjlock));
1215ec933656Sjmmv 
12168f2efd02Srmind 	/*
12178f2efd02Srmind 	 * Currently, PGO_PASTEOF is not supported.
12188f2efd02Srmind 	 */
121920bb9654Syamt 	if (vp->v_size <= offset + (centeridx << PAGE_SHIFT)) {
122020bb9654Syamt 		if ((flags & PGO_LOCKED) == 0)
1221d2a0ebb6Sad 			rw_exit(vp->v_uobj.vmobjlock);
122220bb9654Syamt 		return EINVAL;
122320bb9654Syamt 	}
122420bb9654Syamt 
122520bb9654Syamt 	if (vp->v_size < offset + (npages << PAGE_SHIFT)) {
122620bb9654Syamt 		npages = (round_page(vp->v_size) - offset) >> PAGE_SHIFT;
122720bb9654Syamt 	}
122820bb9654Syamt 
12295c373ea8Sad 	/*
12305c373ea8Sad 	 * Check for reclaimed vnode.  v_interlock is not held here, but
12315c373ea8Sad 	 * VI_DEADCHECK is set with vmobjlock held.
12325c373ea8Sad 	 */
12335c373ea8Sad 	iflag = atomic_load_relaxed(&vp->v_iflag);
1234c3cbbd5eSad 	if (__predict_false((iflag & VI_DEADCHECK) != 0)) {
1235d2a0ebb6Sad 		mutex_enter(vp->v_interlock);
1236d2a0ebb6Sad 		error = vdead_check(vp, VDEAD_NOWAIT);
1237d2a0ebb6Sad 		mutex_exit(vp->v_interlock);
12385c373ea8Sad 		if (error) {
12395c373ea8Sad 			if ((flags & PGO_LOCKED) == 0)
12405c373ea8Sad 				rw_exit(vp->v_uobj.vmobjlock);
12415c373ea8Sad 			return error;
12425c373ea8Sad 		}
12435c373ea8Sad 	}
12443c404d2cShannken 
12453c404d2cShannken 	node = VP_TO_TMPFS_NODE(vp);
12463c404d2cShannken 	uobj = node->tn_spec.tn_reg.tn_aobj;
12473c404d2cShannken 
12485c373ea8Sad 	/*
12495c373ea8Sad 	 * Update timestamp lazily.  The update will be made real when
12505c373ea8Sad 	 * a synchronous update is next made -- or by tmpfs_getattr,
12515c373ea8Sad 	 * tmpfs_putpages, and tmpfs_inactive.
12525c373ea8Sad 	 */
1253647aa775Syamt 	if ((flags & PGO_NOTIMESTAMP) == 0) {
1254e63cf28eSrmind 		u_int tflags = 0;
1255e63cf28eSrmind 
12565f4b660eSjmmv 		if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
1257e63cf28eSrmind 			tflags |= TMPFS_UPDATE_ATIME;
12585f4b660eSjmmv 
1259d11ea3eaSchristos 		if ((access_type & VM_PROT_WRITE) != 0) {
1260e63cf28eSrmind 			tflags |= TMPFS_UPDATE_MTIME;
1261d11ea3eaSchristos 			if (vp->v_mount->mnt_flag & MNT_RELATIME)
1262e63cf28eSrmind 				tflags |= TMPFS_UPDATE_ATIME;
1263d11ea3eaSchristos 		}
12645c373ea8Sad 		tmpfs_update_lazily(vp, tflags);
1265647aa775Syamt 	}
1266ec933656Sjmmv 
1267812b46dfSad 	/* Invoke the pager.  The vnode vmobjlock is shared with the UAO. */
1268d2a0ebb6Sad 	KASSERT(vp->v_uobj.vmobjlock == uobj->vmobjlock);
12698f2efd02Srmind 	error = (*uobj->pgops->pgo_get)(uobj, offset, pgs, &npages, centeridx,
12705c373ea8Sad 	    access_type, advice, flags);
127151634dfdSjmmv #if defined(DEBUG)
12728f2efd02Srmind 	if (!error && pgs) {
12735c373ea8Sad 		KASSERT(pgs[centeridx] != NULL);
127451634dfdSjmmv 	}
127551634dfdSjmmv #endif
1276647aa775Syamt 	return error;
1277647aa775Syamt }
1278647aa775Syamt 
1279647aa775Syamt int
tmpfs_putpages(void * v)1280647aa775Syamt tmpfs_putpages(void *v)
1281647aa775Syamt {
12828f2efd02Srmind 	struct vop_putpages_args /* {
12838f2efd02Srmind 		struct vnode *a_vp;
12848f2efd02Srmind 		voff_t a_offlo;
12858f2efd02Srmind 		voff_t a_offhi;
12868f2efd02Srmind 		int a_flags;
12878f2efd02Srmind 	} */ * const ap = v;
12889d8a0628Srmind 	vnode_t *vp = ap->a_vp;
12898f2efd02Srmind 	const voff_t offlo = ap->a_offlo;
12908f2efd02Srmind 	const voff_t offhi = ap->a_offhi;
12918f2efd02Srmind 	const int flags = ap->a_flags;
12929d8a0628Srmind 	tmpfs_node_t *node;
12935f4b660eSjmmv 	struct uvm_object *uobj;
12948f2efd02Srmind 	int error;
1295647aa775Syamt 
1296d2a0ebb6Sad 	KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
12975f4b660eSjmmv 
1298647aa775Syamt 	if (vp->v_type != VREG) {
1299d2a0ebb6Sad 		rw_exit(vp->v_uobj.vmobjlock);
1300647aa775Syamt 		return 0;
1301647aa775Syamt 	}
1302647aa775Syamt 
1303e225b7bdSrmind 	node = VP_TO_TMPFS_NODE(vp);
1304064fbe7eSjmmv 	uobj = node->tn_spec.tn_reg.tn_aobj;
1305647aa775Syamt 
1306d2a0ebb6Sad 	KASSERT(vp->v_uobj.vmobjlock == uobj->vmobjlock);
13075f4b660eSjmmv 	error = (*uobj->pgops->pgo_put)(uobj, offlo, offhi, flags);
1308647aa775Syamt 
1309647aa775Syamt 	/* XXX mtime */
1310647aa775Syamt 
13115c373ea8Sad 	/* Process deferred updates. */
13125c373ea8Sad 	tmpfs_update(vp, 0);
1313ec933656Sjmmv 	return error;
1314ec933656Sjmmv }
131507b10a7bSpooka 
131607b10a7bSpooka int
tmpfs_whiteout(void * v)131707b10a7bSpooka tmpfs_whiteout(void *v)
131807b10a7bSpooka {
13199d8a0628Srmind 	struct vop_whiteout_args /* {
13209d8a0628Srmind 		struct vnode		*a_dvp;
13219d8a0628Srmind 		struct componentname	*a_cnp;
13229d8a0628Srmind 		int			a_flags;
13239d8a0628Srmind 	} */ *ap = v;
13249d8a0628Srmind 	vnode_t *dvp = ap->a_dvp;
13259d8a0628Srmind 	struct componentname *cnp = ap->a_cnp;
13269d8a0628Srmind 	const int flags = ap->a_flags;
13279d8a0628Srmind 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount);
13281f5dbc94Srmind 	tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp);
13299d8a0628Srmind 	tmpfs_dirent_t *de;
133007b10a7bSpooka 	int error;
133107b10a7bSpooka 
133207b10a7bSpooka 	switch (flags) {
133307b10a7bSpooka 	case LOOKUP:
133407b10a7bSpooka 		break;
133507b10a7bSpooka 	case CREATE:
13364781942cSrmind 		error = tmpfs_alloc_dirent(tmp, cnp->cn_nameptr,
13374781942cSrmind 		    cnp->cn_namelen, &de);
133807b10a7bSpooka 		if (error)
133907b10a7bSpooka 			return error;
13401f5dbc94Srmind 		tmpfs_dir_attach(dnode, de, TMPFS_NODE_WHITEOUT);
134107b10a7bSpooka 		break;
134207b10a7bSpooka 	case DELETE:
134307b10a7bSpooka 		cnp->cn_flags &= ~DOWHITEOUT; /* when in doubt, cargo cult */
13441f5dbc94Srmind 		de = tmpfs_dir_lookup(dnode, cnp);
134507b10a7bSpooka 		if (de == NULL)
134607b10a7bSpooka 			return ENOENT;
13471f5dbc94Srmind 		tmpfs_dir_detach(dnode, de);
13484781942cSrmind 		tmpfs_free_dirent(tmp, de);
134907b10a7bSpooka 		break;
135007b10a7bSpooka 	}
1351e63cf28eSrmind 	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
135207b10a7bSpooka 	return 0;
135307b10a7bSpooka }
13549d8a0628Srmind 
13559d8a0628Srmind int
tmpfs_print(void * v)13569d8a0628Srmind tmpfs_print(void *v)
13579d8a0628Srmind {
13589d8a0628Srmind 	struct vop_print_args /* {
13599d8a0628Srmind 		struct vnode	*a_vp;
13609d8a0628Srmind 	} */ *ap = v;
13619d8a0628Srmind 	vnode_t *vp = ap->a_vp;
13629d8a0628Srmind 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
13639d8a0628Srmind 
13649d8a0628Srmind 	printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n"
1365e63cf28eSrmind 	    "\tmode 0%o, owner %d, group %d, size %" PRIdMAX,
13669d8a0628Srmind 	    node, node->tn_flags, node->tn_links, node->tn_mode, node->tn_uid,
1367e63cf28eSrmind 	    node->tn_gid, (uintmax_t)node->tn_size);
13689d8a0628Srmind 	if (vp->v_type == VFIFO) {
13699d8a0628Srmind 		VOCALL(fifo_vnodeop_p, VOFFSET(vop_print), v);
13709d8a0628Srmind 	}
13719d8a0628Srmind 	printf("\n");
13729d8a0628Srmind 	return 0;
13739d8a0628Srmind }
1374