xref: /netbsd-src/sys/fs/tmpfs/tmpfs_vnops.c (revision 913a779900b5d35aac66eb04e3f21e28fb08a58c)
1*913a7799Sad /*	$NetBSD: tmpfs_vnops.c,v 1.142 2020/05/24 20:08:26 ad 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*913a7799Sad __KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.142 2020/05/24 20:08:26 ad 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 
54ec933656Sjmmv #include <uvm/uvm.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 },
67ec933656Sjmmv 	{ &vop_lookup_desc,		tmpfs_lookup },
68ec933656Sjmmv 	{ &vop_create_desc,		tmpfs_create },
69ec933656Sjmmv 	{ &vop_mknod_desc,		tmpfs_mknod },
70ec933656Sjmmv 	{ &vop_open_desc,		tmpfs_open },
71ec933656Sjmmv 	{ &vop_close_desc,		tmpfs_close },
72ec933656Sjmmv 	{ &vop_access_desc,		tmpfs_access },
739aa2a9c3Schristos 	{ &vop_accessx_desc,		genfs_accessx },
74ec933656Sjmmv 	{ &vop_getattr_desc,		tmpfs_getattr },
75ec933656Sjmmv 	{ &vop_setattr_desc,		tmpfs_setattr },
76ec933656Sjmmv 	{ &vop_read_desc,		tmpfs_read },
77ec933656Sjmmv 	{ &vop_write_desc,		tmpfs_write },
7805d075b3Sdholland 	{ &vop_fallocate_desc,		genfs_eopnotsupp },
7905d075b3Sdholland 	{ &vop_fdiscard_desc,		genfs_eopnotsupp },
80ec933656Sjmmv 	{ &vop_ioctl_desc,		tmpfs_ioctl },
81ec933656Sjmmv 	{ &vop_fcntl_desc,		tmpfs_fcntl },
82ec933656Sjmmv 	{ &vop_poll_desc,		tmpfs_poll },
83ec933656Sjmmv 	{ &vop_kqfilter_desc,		tmpfs_kqfilter },
84ec933656Sjmmv 	{ &vop_revoke_desc,		tmpfs_revoke },
85ec933656Sjmmv 	{ &vop_mmap_desc,		tmpfs_mmap },
86ec933656Sjmmv 	{ &vop_fsync_desc,		tmpfs_fsync },
87ec933656Sjmmv 	{ &vop_seek_desc,		tmpfs_seek },
88ec933656Sjmmv 	{ &vop_remove_desc,		tmpfs_remove },
89ec933656Sjmmv 	{ &vop_link_desc,		tmpfs_link },
90ec933656Sjmmv 	{ &vop_rename_desc,		tmpfs_rename },
91ec933656Sjmmv 	{ &vop_mkdir_desc,		tmpfs_mkdir },
92ec933656Sjmmv 	{ &vop_rmdir_desc,		tmpfs_rmdir },
93ec933656Sjmmv 	{ &vop_symlink_desc,		tmpfs_symlink },
94ec933656Sjmmv 	{ &vop_readdir_desc,		tmpfs_readdir },
95ec933656Sjmmv 	{ &vop_readlink_desc,		tmpfs_readlink },
96ec933656Sjmmv 	{ &vop_abortop_desc,		tmpfs_abortop },
97ec933656Sjmmv 	{ &vop_inactive_desc,		tmpfs_inactive },
98ec933656Sjmmv 	{ &vop_reclaim_desc,		tmpfs_reclaim },
99ec933656Sjmmv 	{ &vop_lock_desc,		tmpfs_lock },
100ec933656Sjmmv 	{ &vop_unlock_desc,		tmpfs_unlock },
101ec933656Sjmmv 	{ &vop_bmap_desc,		tmpfs_bmap },
102ec933656Sjmmv 	{ &vop_strategy_desc,		tmpfs_strategy },
103ec933656Sjmmv 	{ &vop_print_desc,		tmpfs_print },
104ec933656Sjmmv 	{ &vop_pathconf_desc,		tmpfs_pathconf },
105ec933656Sjmmv 	{ &vop_islocked_desc,		tmpfs_islocked },
106ec933656Sjmmv 	{ &vop_advlock_desc,		tmpfs_advlock },
107ec933656Sjmmv 	{ &vop_bwrite_desc,		tmpfs_bwrite },
108ec933656Sjmmv 	{ &vop_getpages_desc,		tmpfs_getpages },
109ec933656Sjmmv 	{ &vop_putpages_desc,		tmpfs_putpages },
11007b10a7bSpooka 	{ &vop_whiteout_desc,		tmpfs_whiteout },
111ec933656Sjmmv 	{ NULL, NULL }
112ec933656Sjmmv };
1139d8a0628Srmind 
1149d8a0628Srmind const struct vnodeopv_desc tmpfs_vnodeop_opv_desc = {
1159d8a0628Srmind 	&tmpfs_vnodeop_p, tmpfs_vnodeop_entries
1169d8a0628Srmind };
117ec933656Sjmmv 
11871cf548aSrmind /*
11960c9a518Srmind  * tmpfs_lookup: path name traversal routine.
12071cf548aSrmind  *
12171cf548aSrmind  * Arguments: dvp (directory being searched), vpp (result),
12271cf548aSrmind  * cnp (component name - path).
12371cf548aSrmind  *
12471cf548aSrmind  * => Caller holds a reference and lock on dvp.
12571cf548aSrmind  * => We return looked-up vnode (vpp) locked, with a reference held.
12671cf548aSrmind  */
127ec933656Sjmmv int
128ec933656Sjmmv tmpfs_lookup(void *v)
129ec933656Sjmmv {
13097834f7bShannken 	struct vop_lookup_v2_args /* {
13171cf548aSrmind 		struct vnode *a_dvp;
13271cf548aSrmind 		struct vnode **a_vpp;
13371cf548aSrmind 		struct componentname *a_cnp;
13471cf548aSrmind 	} */ *ap = v;
1359d8a0628Srmind 	vnode_t *dvp = ap->a_dvp, **vpp = ap->a_vpp;
13671cf548aSrmind 	struct componentname *cnp = ap->a_cnp;
137e9d92e9cSrmind 	const bool lastcn = (cnp->cn_flags & ISLASTCN) != 0;
138e9d92e9cSrmind 	tmpfs_node_t *dnode, *tnode;
1399d8a0628Srmind 	tmpfs_dirent_t *de;
1401617a81dSdholland 	int cachefound, iswhiteout;
14171cf548aSrmind 	int error;
142ec933656Sjmmv 
143ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
144ec933656Sjmmv 
145ec933656Sjmmv 	dnode = VP_TO_TMPFS_DIR(dvp);
146ec933656Sjmmv 	*vpp = NULL;
147ec933656Sjmmv 
1484781942cSrmind 	/* Check accessibility of directory. */
14961e8303eSpooka 	error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred);
1509d8a0628Srmind 	if (error) {
151ec933656Sjmmv 		goto out;
1529d8a0628Srmind 	}
1534781942cSrmind 
15471cf548aSrmind 	/*
15571cf548aSrmind 	 * If requesting the last path component on a read-only file system
15671cf548aSrmind 	 * with a write operation, deny it.
15771cf548aSrmind 	 */
158e9d92e9cSrmind 	if (lastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) != 0 &&
159ec933656Sjmmv 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
160ec933656Sjmmv 		error = EROFS;
161ec933656Sjmmv 		goto out;
162ec933656Sjmmv 	}
163ec933656Sjmmv 
16471cf548aSrmind 	/*
16571cf548aSrmind 	 * Avoid doing a linear scan of the directory if the requested
16671cf548aSrmind 	 * directory/name couple is already in the cache.
16771cf548aSrmind 	 */
16835ed6905Sdholland 	cachefound = cache_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen,
16935ed6905Sdholland 				  cnp->cn_nameiop, cnp->cn_flags,
17035ed6905Sdholland 				  &iswhiteout, vpp);
1711617a81dSdholland 	if (iswhiteout) {
1721617a81dSdholland 		cnp->cn_flags |= ISWHITEOUT;
1731617a81dSdholland 	}
1741617a81dSdholland 	if (cachefound && *vpp == NULLVP) {
1751617a81dSdholland 		/* Negative cache hit. */
1761617a81dSdholland 		error = ENOENT;
1778c80da52Shannken 		goto out;
1781617a81dSdholland 	} else if (cachefound) {
1791617a81dSdholland 		error = 0;
1808c80da52Shannken 		goto out;
18160c9a518Srmind 	}
182ec933656Sjmmv 
183905b6b77Shannken 	/*
184905b6b77Shannken 	 * Treat an unlinked directory as empty (no "." or "..")
185905b6b77Shannken 	 */
186905b6b77Shannken 	if (dnode->tn_links == 0) {
187905b6b77Shannken 		KASSERT(dnode->tn_size == 0);
188905b6b77Shannken 		error = ENOENT;
189905b6b77Shannken 		goto out;
190905b6b77Shannken 	}
191905b6b77Shannken 
192ec933656Sjmmv 	if (cnp->cn_flags & ISDOTDOT) {
1939d8a0628Srmind 		tmpfs_node_t *pnode;
1944781942cSrmind 
19560c9a518Srmind 		/*
19660c9a518Srmind 		 * Lookup of ".." case.
19760c9a518Srmind 		 */
1984781942cSrmind 		if (lastcn && cnp->cn_nameiop == RENAME) {
1994781942cSrmind 			error = EINVAL;
2004781942cSrmind 			goto out;
2014781942cSrmind 		}
2024781942cSrmind 		KASSERT(dnode->tn_type == VDIR);
20360c9a518Srmind 		pnode = dnode->tn_spec.tn_dir.tn_parent;
2044781942cSrmind 		if (pnode == NULL) {
2054781942cSrmind 			error = ENOENT;
206ced25823Sad 			goto done;
2074781942cSrmind 		}
2084781942cSrmind 
2098c80da52Shannken 		error = vcache_get(dvp->v_mount, &pnode, sizeof(pnode), vpp);
210ced25823Sad 		goto done;
211ec933656Sjmmv 	} else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
21260c9a518Srmind 		/*
21360c9a518Srmind 		 * Lookup of "." case.
21460c9a518Srmind 		 */
215e9d92e9cSrmind 		if (lastcn && cnp->cn_nameiop == RENAME) {
216083df92bSpooka 			error = EISDIR;
217083df92bSpooka 			goto out;
218083df92bSpooka 		}
219c3183f32Spooka 		vref(dvp);
220ec933656Sjmmv 		*vpp = dvp;
221ec933656Sjmmv 		error = 0;
22271cf548aSrmind 		goto done;
22371cf548aSrmind 	}
22471cf548aSrmind 
22560c9a518Srmind 	/*
22660c9a518Srmind 	 * Other lookup cases: perform directory scan.
22760c9a518Srmind 	 */
228ec933656Sjmmv 	de = tmpfs_dir_lookup(dnode, cnp);
22907b10a7bSpooka 	if (de == NULL || de->td_node == TMPFS_NODE_WHITEOUT) {
23071cf548aSrmind 		/*
23171cf548aSrmind 		 * The entry was not found in the directory.  This is valid
23271cf548aSrmind 		 * if we are creating or renaming an entry and are working
23371cf548aSrmind 		 * on the last component of the path name.
23471cf548aSrmind 		 */
235e9d92e9cSrmind 		if (lastcn && (cnp->cn_nameiop == CREATE ||
236ec933656Sjmmv 		    cnp->cn_nameiop == RENAME)) {
23761e8303eSpooka 			error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred);
23871cf548aSrmind 			if (error) {
239ec933656Sjmmv 				goto out;
24071cf548aSrmind 			}
241ec933656Sjmmv 			error = EJUSTRETURN;
242ec933656Sjmmv 		} else {
24371cf548aSrmind 			error = ENOENT;
24471cf548aSrmind 		}
24507b10a7bSpooka 		if (de) {
24607b10a7bSpooka 			KASSERT(de->td_node == TMPFS_NODE_WHITEOUT);
24707b10a7bSpooka 			cnp->cn_flags |= ISWHITEOUT;
24807b10a7bSpooka 		}
249e9d92e9cSrmind 		goto done;
250e9d92e9cSrmind 	}
251e9d92e9cSrmind 
252e9d92e9cSrmind 	tnode = de->td_node;
253ec933656Sjmmv 
25471cf548aSrmind 	/*
255e9d92e9cSrmind 	 * If it is not the last path component and found a non-directory
256e9d92e9cSrmind 	 * or non-link entry (which may itself be pointing to a directory),
257e9d92e9cSrmind 	 * raise an error.
25871cf548aSrmind 	 */
259e9d92e9cSrmind 	if (!lastcn && tnode->tn_type != VDIR && tnode->tn_type != VLNK) {
260ec933656Sjmmv 		error = ENOTDIR;
261ec933656Sjmmv 		goto out;
262ec933656Sjmmv 	}
263ec933656Sjmmv 
264e9d92e9cSrmind 	/* Check the permissions. */
265e9d92e9cSrmind 	if (lastcn && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
26661e8303eSpooka 		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred);
2670c9d8d15Selad 		if (error)
268ec933656Sjmmv 			goto out;
2690c9d8d15Selad 
2700c9d8d15Selad 		if ((dnode->tn_mode & S_ISTXT) != 0) {
2710c9d8d15Selad 			error = kauth_authorize_vnode(cnp->cn_cred,
2720c9d8d15Selad 			    KAUTH_VNODE_DELETE, tnode->tn_vnode,
2739aa2a9c3Schristos 			    dnode->tn_vnode, genfs_can_sticky(dvp, cnp->cn_cred,
2740c9d8d15Selad 			    dnode->tn_uid, tnode->tn_uid));
2750c9d8d15Selad 			if (error) {
2760c9d8d15Selad 				error = EPERM;
2770c9d8d15Selad 				goto out;
2780c9d8d15Selad 			}
27971cf548aSrmind 		}
28071cf548aSrmind 	}
281e9d92e9cSrmind 
2824781942cSrmind 	/* Get a vnode for the matching entry. */
2838c80da52Shannken 	error = vcache_get(dvp->v_mount, &tnode, sizeof(tnode), vpp);
28471cf548aSrmind done:
28571cf548aSrmind 	/*
28660c9a518Srmind 	 * Cache the result, unless request was for creation (as it does
28760c9a518Srmind 	 * not improve the performance).
28871cf548aSrmind 	 */
289d65753d9Srmind 	if (cnp->cn_nameiop != CREATE) {
29035ed6905Sdholland 		cache_enter(dvp, *vpp, cnp->cn_nameptr, cnp->cn_namelen,
29135ed6905Sdholland 			    cnp->cn_flags);
29260c9a518Srmind 	}
293ec933656Sjmmv out:
294c398ae97Schs 	KASSERT(VOP_ISLOCKED(dvp));
29507b10a7bSpooka 
296ec933656Sjmmv 	return error;
297ec933656Sjmmv }
298ec933656Sjmmv 
299ec933656Sjmmv int
300ec933656Sjmmv tmpfs_create(void *v)
301ec933656Sjmmv {
30204c776e5Shannken 	struct vop_create_v3_args /* {
3039d8a0628Srmind 		struct vnode		*a_dvp;
3049d8a0628Srmind 		struct vnode		**a_vpp;
3059d8a0628Srmind 		struct componentname	*a_cnp;
3069d8a0628Srmind 		struct vattr		*a_vap;
3079d8a0628Srmind 	} */ *ap = v;
3089d8a0628Srmind 	vnode_t *dvp = ap->a_dvp, **vpp = ap->a_vpp;
3099d8a0628Srmind 	struct componentname *cnp = ap->a_cnp;
3109d8a0628Srmind 	struct vattr *vap = ap->a_vap;
311ec933656Sjmmv 
3129d8a0628Srmind 	KASSERT(VOP_ISLOCKED(dvp));
313ec933656Sjmmv 	KASSERT(vap->va_type == VREG || vap->va_type == VSOCK);
314e63cf28eSrmind 	return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL);
315ec933656Sjmmv }
316ec933656Sjmmv 
317ec933656Sjmmv int
318ec933656Sjmmv tmpfs_mknod(void *v)
319ec933656Sjmmv {
32004c776e5Shannken 	struct vop_mknod_v3_args /* {
3219d8a0628Srmind 		struct vnode		*a_dvp;
3229d8a0628Srmind 		struct vnode		**a_vpp;
3239d8a0628Srmind 		struct componentname	*a_cnp;
3249d8a0628Srmind 		struct vattr		*a_vap;
3259d8a0628Srmind 	} */ *ap = v;
3269d8a0628Srmind 	vnode_t *dvp = ap->a_dvp, **vpp = ap->a_vpp;
3279d8a0628Srmind 	struct componentname *cnp = ap->a_cnp;
3289d8a0628Srmind 	struct vattr *vap = ap->a_vap;
3299d8a0628Srmind 	enum vtype vt = vap->va_type;
330ec933656Sjmmv 
3319d8a0628Srmind 	if (vt != VBLK && vt != VCHR && vt != VFIFO) {
332ce079fc9Srmind 		*vpp = NULL;
333ec933656Sjmmv 		return EINVAL;
3345ac22576Spooka 	}
335e63cf28eSrmind 	return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL);
336ec933656Sjmmv }
337ec933656Sjmmv 
338ec933656Sjmmv int
339ec933656Sjmmv tmpfs_open(void *v)
340ec933656Sjmmv {
3419d8a0628Srmind 	struct vop_open_args /* {
3429d8a0628Srmind 		struct vnode	*a_vp;
3439d8a0628Srmind 		int		a_mode;
3449d8a0628Srmind 		kauth_cred_t	a_cred;
3459d8a0628Srmind 	} */ *ap = v;
3469d8a0628Srmind 	vnode_t *vp = ap->a_vp;
3479d8a0628Srmind 	mode_t mode = ap->a_mode;
3489d8a0628Srmind 	tmpfs_node_t *node;
349ec933656Sjmmv 
350ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
351ec933656Sjmmv 
352ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
353ccb670c8Sjmmv 
354ec933656Sjmmv 	/* If the file is marked append-only, deny write requests. */
3559d8a0628Srmind 	if ((node->tn_flags & APPEND) != 0 &&
3569d8a0628Srmind 	    (mode & (FWRITE | O_APPEND)) == FWRITE) {
3579d8a0628Srmind 		return EPERM;
358ec933656Sjmmv 	}
3599d8a0628Srmind 	return 0;
3609d8a0628Srmind }
361ec933656Sjmmv 
362ec933656Sjmmv int
363ec933656Sjmmv tmpfs_close(void *v)
364ec933656Sjmmv {
3659d8a0628Srmind 	struct vop_close_args /* {
3669d8a0628Srmind 		struct vnode	*a_vp;
3679d8a0628Srmind 		int		a_fflag;
3689d8a0628Srmind 		kauth_cred_t	a_cred;
3699d8a0628Srmind 	} */ *ap = v;
370b05b0389Srmind 	vnode_t *vp __diagused = ap->a_vp;
371ec933656Sjmmv 
372ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
373a748ea88Syamt 	return 0;
374ec933656Sjmmv }
375ec933656Sjmmv 
3760adcb88bSrmind int
3770adcb88bSrmind tmpfs_access(void *v)
378ec933656Sjmmv {
3790adcb88bSrmind 	struct vop_access_args /* {
3800adcb88bSrmind 		struct vnode	*a_vp;
3819aa2a9c3Schristos 		accmode_t	a_accmode;
3820adcb88bSrmind 		kauth_cred_t	a_cred;
3830adcb88bSrmind 	} */ *ap = v;
3840adcb88bSrmind 	vnode_t *vp = ap->a_vp;
3859aa2a9c3Schristos 	accmode_t accmode = ap->a_accmode;
3860adcb88bSrmind 	kauth_cred_t cred = ap->a_cred;
3870adcb88bSrmind 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
3889aa2a9c3Schristos 	const bool writing = (accmode & VWRITE) != 0;
389ec933656Sjmmv 
3900adcb88bSrmind 	KASSERT(VOP_ISLOCKED(vp));
3910adcb88bSrmind 
3920adcb88bSrmind 	/* Possible? */
393ec933656Sjmmv 	switch (vp->v_type) {
394ec933656Sjmmv 	case VDIR:
395ec933656Sjmmv 	case VLNK:
396ec933656Sjmmv 	case VREG:
3979d8a0628Srmind 		if (writing && (vp->v_mount->mnt_flag & MNT_RDONLY) != 0) {
3989d8a0628Srmind 			return EROFS;
399ec933656Sjmmv 		}
400ec933656Sjmmv 		break;
401ec933656Sjmmv 	case VBLK:
402ec933656Sjmmv 	case VCHR:
403ec933656Sjmmv 	case VSOCK:
404ec933656Sjmmv 	case VFIFO:
405ec933656Sjmmv 		break;
406ec933656Sjmmv 	default:
4079d8a0628Srmind 		return EINVAL;
408ec933656Sjmmv 	}
4090adcb88bSrmind 	if (writing && (node->tn_flags & IMMUTABLE) != 0) {
4100adcb88bSrmind 		return EPERM;
411009f5d2fSelad 	}
412009f5d2fSelad 
4139aa2a9c3Schristos 	return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(accmode,
4149aa2a9c3Schristos 	    vp->v_type, node->tn_mode), vp, NULL, genfs_can_access(vp, cred,
4159aa2a9c3Schristos 	    node->tn_uid, node->tn_gid, node->tn_mode, NULL, accmode));
4169d8a0628Srmind }
417ec933656Sjmmv 
418ec933656Sjmmv int
419ec933656Sjmmv tmpfs_getattr(void *v)
420ec933656Sjmmv {
4219d8a0628Srmind 	struct vop_getattr_args /* {
4229d8a0628Srmind 		struct vnode	*a_vp;
4239d8a0628Srmind 		struct vattr	*a_vap;
4249d8a0628Srmind 		kauth_cred_t	a_cred;
4259d8a0628Srmind 	} */ *ap = v;
4269d8a0628Srmind 	vnode_t *vp = ap->a_vp;
4279d8a0628Srmind 	struct vattr *vap = ap->a_vap;
4289d8a0628Srmind 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
429ec933656Sjmmv 
430c3183f32Spooka 	vattr_null(vap);
431ec933656Sjmmv 
432ec933656Sjmmv 	vap->va_type = vp->v_type;
433ec933656Sjmmv 	vap->va_mode = node->tn_mode;
434ec933656Sjmmv 	vap->va_nlink = node->tn_links;
435ec933656Sjmmv 	vap->va_uid = node->tn_uid;
436ec933656Sjmmv 	vap->va_gid = node->tn_gid;
437ec933656Sjmmv 	vap->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
438ec933656Sjmmv 	vap->va_fileid = node->tn_id;
439ec933656Sjmmv 	vap->va_size = node->tn_size;
440ec933656Sjmmv 	vap->va_blocksize = PAGE_SIZE;
4414781942cSrmind 	vap->va_gen = TMPFS_NODE_GEN(node);
442ec933656Sjmmv 	vap->va_flags = node->tn_flags;
443ec933656Sjmmv 	vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ?
444064fbe7eSjmmv 	    node->tn_spec.tn_dev.tn_rdev : VNOVAL;
445ec933656Sjmmv 	vap->va_bytes = round_page(node->tn_size);
446ec933656Sjmmv 	vap->va_filerev = VNOVAL;
447ec933656Sjmmv 	vap->va_vaflags = 0;
448ec933656Sjmmv 	vap->va_spare = VNOVAL; /* XXX */
449ec933656Sjmmv 
4505c373ea8Sad 	mutex_enter(&node->tn_timelock);
4515c373ea8Sad 	tmpfs_update_locked(vp, 0);
4525c373ea8Sad 	vap->va_atime = node->tn_atime;
4535c373ea8Sad 	vap->va_mtime = node->tn_mtime;
4545c373ea8Sad 	vap->va_ctime = node->tn_ctime;
4555c373ea8Sad 	vap->va_birthtime = node->tn_birthtime;
4565c373ea8Sad 	mutex_exit(&node->tn_timelock);
4575c373ea8Sad 
458ec933656Sjmmv 	return 0;
459ec933656Sjmmv }
460ec933656Sjmmv 
461ec933656Sjmmv int
462ec933656Sjmmv tmpfs_setattr(void *v)
463ec933656Sjmmv {
4649d8a0628Srmind 	struct vop_setattr_args /* {
4659d8a0628Srmind 		struct vnode	*a_vp;
4669d8a0628Srmind 		struct vattr	*a_vap;
4679d8a0628Srmind 		kauth_cred_t	a_cred;
4689d8a0628Srmind 	} */ *ap = v;
4699d8a0628Srmind 	vnode_t *vp = ap->a_vp;
4709d8a0628Srmind 	struct vattr *vap = ap->a_vap;
4719d8a0628Srmind 	kauth_cred_t cred = ap->a_cred;
4729d8a0628Srmind 	lwp_t *l = curlwp;
4739d8a0628Srmind 	int error = 0;
474ec933656Sjmmv 
475ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
476ec933656Sjmmv 
477ec933656Sjmmv 	/* Abort if any unsettable attribute is given. */
4789d8a0628Srmind 	if (vap->va_type != VNON || vap->va_nlink != VNOVAL ||
4799d8a0628Srmind 	    vap->va_fsid != VNOVAL || vap->va_fileid != VNOVAL ||
480e63cf28eSrmind 	    vap->va_blocksize != VNOVAL || vap->va_ctime.tv_sec != VNOVAL ||
4819d8a0628Srmind 	    vap->va_gen != VNOVAL || vap->va_rdev != VNOVAL ||
4829d8a0628Srmind 	    vap->va_bytes != VNOVAL) {
4839d8a0628Srmind 		return EINVAL;
4849d8a0628Srmind 	}
485e63cf28eSrmind 
486e63cf28eSrmind 	if (error == 0 && vap->va_flags != VNOVAL)
487f474dcebSad 		error = tmpfs_chflags(vp, vap->va_flags, cred, l);
488ec933656Sjmmv 
489e63cf28eSrmind 	if (error == 0 && vap->va_size != VNOVAL)
490f474dcebSad 		error = tmpfs_chsize(vp, vap->va_size, cred, l);
491ec933656Sjmmv 
492ec933656Sjmmv 	if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL))
493f474dcebSad 		error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, l);
494ec933656Sjmmv 
495e63cf28eSrmind 	if (error == 0 && vap->va_mode != VNOVAL)
496f474dcebSad 		error = tmpfs_chmod(vp, vap->va_mode, cred, l);
497ec933656Sjmmv 
498e63cf28eSrmind 	const bool chsometime =
499e63cf28eSrmind 	    vap->va_atime.tv_sec != VNOVAL ||
500e63cf28eSrmind 	    vap->va_mtime.tv_sec != VNOVAL ||
501e63cf28eSrmind 	    vap->va_birthtime.tv_sec != VNOVAL;
502e63cf28eSrmind 	if (error == 0 && chsometime) {
5039d8a0628Srmind 		error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime,
5049d8a0628Srmind 		    &vap->va_birthtime, vap->va_vaflags, cred, l);
5059d8a0628Srmind 	}
506ec933656Sjmmv 	return error;
507ec933656Sjmmv }
508ec933656Sjmmv 
509ec933656Sjmmv int
510ec933656Sjmmv tmpfs_read(void *v)
511ec933656Sjmmv {
5129d8a0628Srmind 	struct vop_read_args /* {
5139d8a0628Srmind 		struct vnode *a_vp;
5149d8a0628Srmind 		struct uio *a_uio;
5159d8a0628Srmind 		int a_ioflag;
5169d8a0628Srmind 		kauth_cred_t a_cred;
5179d8a0628Srmind 	} */ *ap = v;
5189d8a0628Srmind 	vnode_t *vp = ap->a_vp;
5199d8a0628Srmind 	struct uio *uio = ap->a_uio;
5209d8a0628Srmind 	const int ioflag = ap->a_ioflag;
5219d8a0628Srmind 	tmpfs_node_t *node;
522647aa775Syamt 	struct uvm_object *uobj;
5239d8a0628Srmind 	int error;
524ec933656Sjmmv 
525ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
526ec933656Sjmmv 
5271036f1d9Smaxv 	if (vp->v_type == VDIR) {
5281036f1d9Smaxv 		return EISDIR;
5291036f1d9Smaxv 	}
5306b47753fSmaxv 	if (uio->uio_offset < 0 || vp->v_type != VREG) {
5319d8a0628Srmind 		return EINVAL;
532ec933656Sjmmv 	}
533ec933656Sjmmv 
534e63cf28eSrmind 	/* Note: reading zero bytes should not update atime. */
535e63cf28eSrmind 	if (uio->uio_resid == 0) {
536e63cf28eSrmind 		return 0;
537e63cf28eSrmind 	}
538e63cf28eSrmind 
5399d8a0628Srmind 	node = VP_TO_TMPFS_NODE(vp);
540064fbe7eSjmmv 	uobj = node->tn_spec.tn_reg.tn_aobj;
541ec933656Sjmmv 	error = 0;
5429d8a0628Srmind 
5435f4b660eSjmmv 	while (error == 0 && uio->uio_resid > 0) {
544647aa775Syamt 		vsize_t len;
545ec933656Sjmmv 
5469d8a0628Srmind 		if (node->tn_size <= uio->uio_offset) {
5477720dda1Syamt 			break;
5489d8a0628Srmind 		}
549ec933656Sjmmv 		len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid);
5509d8a0628Srmind 		if (len == 0) {
551647aa775Syamt 			break;
5529d8a0628Srmind 		}
553b4099c3eSpooka 		error = ubc_uiomove(uobj, uio, len, IO_ADV_DECODE(ioflag),
554f5ad84fdSad 		    UBC_READ | UBC_PARTIALOK | UBC_VNODE_FLAGS(vp));
555647aa775Syamt 	}
556e63cf28eSrmind 
557e63cf28eSrmind 	tmpfs_update(vp, TMPFS_UPDATE_ATIME);
558ec933656Sjmmv 	return error;
559ec933656Sjmmv }
560ec933656Sjmmv 
561ec933656Sjmmv int
562ec933656Sjmmv tmpfs_write(void *v)
563ec933656Sjmmv {
5649d8a0628Srmind 	struct vop_write_args /* {
5659d8a0628Srmind 		struct vnode	*a_vp;
5669d8a0628Srmind 		struct uio	*a_uio;
5679d8a0628Srmind 		int		a_ioflag;
5689d8a0628Srmind 		kauth_cred_t	a_cred;
5699d8a0628Srmind 	} */ *ap = v;
5709d8a0628Srmind 	vnode_t *vp = ap->a_vp;
5719d8a0628Srmind 	struct uio *uio = ap->a_uio;
5729d8a0628Srmind 	const int ioflag = ap->a_ioflag;
5739d8a0628Srmind 	tmpfs_node_t *node;
5749d8a0628Srmind 	struct uvm_object *uobj;
5759dbda5eaSmartin 	off_t oldsize;
576*913a7799Sad 	int error, ubc_flags;
577ec933656Sjmmv 
578ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
579ec933656Sjmmv 
580641d22c5Skardel 	node = VP_TO_TMPFS_NODE(vp);
581641d22c5Skardel 	oldsize = node->tn_size;
582641d22c5Skardel 
583b2f46950Sjoerg 	if ((vp->v_mount->mnt_flag & MNT_RDONLY) != 0) {
584b2f46950Sjoerg 		error = EROFS;
585b2f46950Sjoerg 		goto out;
586b2f46950Sjoerg 	}
587b2f46950Sjoerg 
588ec933656Sjmmv 	if (uio->uio_offset < 0 || vp->v_type != VREG) {
589ec933656Sjmmv 		error = EINVAL;
590ec933656Sjmmv 		goto out;
591ec933656Sjmmv 	}
592ec933656Sjmmv 	if (uio->uio_resid == 0) {
593ec933656Sjmmv 		error = 0;
594ec933656Sjmmv 		goto out;
595ec933656Sjmmv 	}
5969d8a0628Srmind 	if (ioflag & IO_APPEND) {
597ec933656Sjmmv 		uio->uio_offset = node->tn_size;
5989d8a0628Srmind 	}
599ec933656Sjmmv 
600e63cf28eSrmind 	if (uio->uio_offset + uio->uio_resid > node->tn_size) {
601ec933656Sjmmv 		error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid);
6029d8a0628Srmind 		if (error)
603ec933656Sjmmv 			goto out;
604ec933656Sjmmv 	}
605ec933656Sjmmv 
606*913a7799Sad 	/*
607*913a7799Sad 	 * If we're extending the file and have data to write that would
608*913a7799Sad 	 * not leave an un-zeroed hole, we can avoid fault processing and
609*913a7799Sad 	 * zeroing of pages on allocation.
610*913a7799Sad 	 *
611*913a7799Sad 	 * Don't do this if the file is mapped and we need to touch an
612*913a7799Sad 	 * existing page, because writing a mapping of the file into itself
613*913a7799Sad 	 * could cause a deadlock on PG_BUSY.
614*913a7799Sad 	 *
615*913a7799Sad 	 * New pages will not become visible until finished here (because
616*913a7799Sad 	 * of PG_BUSY and the vnode lock).
617*913a7799Sad 	 */
618*913a7799Sad 	ubc_flags = UBC_WRITE | UBC_VNODE_FLAGS(vp);
619*913a7799Sad 	if (uio->uio_offset >= oldsize &&
620*913a7799Sad 	    ((uio->uio_offset & (PAGE_SIZE - 1)) == 0 ||
621*913a7799Sad 	    ((vp->v_vflag & VV_MAPPED) == 0 &&
622*913a7799Sad 	    trunc_page(uio->uio_offset) == trunc_page(oldsize)))) {
623*913a7799Sad 		ubc_flags |= UBC_FAULTBUSY;
624*913a7799Sad 	}
625*913a7799Sad 
626064fbe7eSjmmv 	uobj = node->tn_spec.tn_reg.tn_aobj;
627647aa775Syamt 	error = 0;
6285f4b660eSjmmv 	while (error == 0 && uio->uio_resid > 0) {
629647aa775Syamt 		vsize_t len;
630647aa775Syamt 
631647aa775Syamt 		len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid);
6329d8a0628Srmind 		if (len == 0) {
633647aa775Syamt 			break;
6349d8a0628Srmind 		}
635b4099c3eSpooka 		error = ubc_uiomove(uobj, uio, len, IO_ADV_DECODE(ioflag),
636*913a7799Sad 		    ubc_flags);
637647aa775Syamt 	}
6389d8a0628Srmind 	if (error) {
6399d8a0628Srmind 		(void)tmpfs_reg_resize(vp, oldsize);
6409d8a0628Srmind 	}
641647aa775Syamt 
642e63cf28eSrmind 	tmpfs_update(vp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
643117b5f51Sjmmv 	VN_KNOTE(vp, NOTE_WRITE);
644ec933656Sjmmv out:
6459d8a0628Srmind 	if (error) {
6469d8a0628Srmind 		KASSERT(oldsize == node->tn_size);
6479d8a0628Srmind 	} else {
6489d8a0628Srmind 		KASSERT(uio->uio_resid == 0);
6499d8a0628Srmind 	}
650ec933656Sjmmv 	return error;
651ec933656Sjmmv }
652ec933656Sjmmv 
653ec933656Sjmmv int
654ec933656Sjmmv tmpfs_fsync(void *v)
655ec933656Sjmmv {
6569d8a0628Srmind 	struct vop_fsync_args /* {
6579d8a0628Srmind 		struct vnode *a_vp;
6589d8a0628Srmind 		kauth_cred_t a_cred;
6599d8a0628Srmind 		int a_flags;
6609d8a0628Srmind 		off_t a_offlo;
6619d8a0628Srmind 		off_t a_offhi;
6629d8a0628Srmind 		struct lwp *a_l;
6639d8a0628Srmind 	} */ *ap = v;
664b05b0389Srmind 	vnode_t *vp __diagused = ap->a_vp;
665ec933656Sjmmv 
666e63cf28eSrmind 	/* Nothing to do.  Should be up to date. */
667ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
668a748ea88Syamt 	return 0;
669ec933656Sjmmv }
670ec933656Sjmmv 
6719d8a0628Srmind /*
6729d8a0628Srmind  * tmpfs_remove: unlink a file.
6739d8a0628Srmind  *
6749d8a0628Srmind  * => Both directory (dvp) and file (vp) are locked.
6759d8a0628Srmind  * => We unlock and drop the reference on both.
6769d8a0628Srmind  */
677ec933656Sjmmv int
678ec933656Sjmmv tmpfs_remove(void *v)
679ec933656Sjmmv {
6806fa7b158Sriastradh 	struct vop_remove_v2_args /* {
68160c9a518Srmind 		struct vnode *a_dvp;
68260c9a518Srmind 		struct vnode *a_vp;
68360c9a518Srmind 		struct componentname *a_cnp;
68460c9a518Srmind 	} */ *ap = v;
6859d8a0628Srmind 	vnode_t *dvp = ap->a_dvp, *vp = ap->a_vp;
68649ce9c94Srmind 	tmpfs_node_t *dnode, *node;
6879d8a0628Srmind 	tmpfs_dirent_t *de;
6885c373ea8Sad 	int error, tflags;
689ec933656Sjmmv 
690ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
691ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
692ec933656Sjmmv 
6934f931d80Spooka 	if (vp->v_type == VDIR) {
6944f931d80Spooka 		error = EPERM;
6954f931d80Spooka 		goto out;
6964f931d80Spooka 	}
69749ce9c94Srmind 	dnode = VP_TO_TMPFS_DIR(dvp);
698ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
699ec933656Sjmmv 
70049ce9c94Srmind 	/*
70149ce9c94Srmind 	 * Files marked as immutable or append-only cannot be deleted.
70249ce9c94Srmind 	 * Likewise, files residing on directories marked as append-only
70349ce9c94Srmind 	 * cannot be deleted.
70449ce9c94Srmind 	 */
705ec933656Sjmmv 	if (node->tn_flags & (IMMUTABLE | APPEND)) {
706ec933656Sjmmv 		error = EPERM;
707ec933656Sjmmv 		goto out;
708ec933656Sjmmv 	}
70949ce9c94Srmind 	if (dnode->tn_flags & APPEND) {
71049ce9c94Srmind 		error = EPERM;
71149ce9c94Srmind 		goto out;
71249ce9c94Srmind 	}
713ec933656Sjmmv 
7144781942cSrmind 	/* Lookup the directory entry (check the cached hint first). */
7154781942cSrmind 	de = tmpfs_dir_cached(node);
7164781942cSrmind 	if (de == NULL) {
7174781942cSrmind 		struct componentname *cnp = ap->a_cnp;
71860c9a518Srmind 		de = tmpfs_dir_lookup(dnode, cnp);
7194781942cSrmind 	}
72060c9a518Srmind 	KASSERT(de && de->td_node == node);
721ec933656Sjmmv 
72260c9a518Srmind 	/*
7234781942cSrmind 	 * Remove the entry from the directory (drops the link count) and
724e63cf28eSrmind 	 * destroy it or replace with a whiteout.
725e63cf28eSrmind 	 *
726e63cf28eSrmind 	 * Note: the inode referred by it will not be destroyed until the
727e63cf28eSrmind 	 * vnode is reclaimed/recycled.
72860c9a518Srmind 	 */
729e63cf28eSrmind 
7301f5dbc94Srmind 	tmpfs_dir_detach(dnode, de);
731e63cf28eSrmind 
732f68873a3Shannken 	if (ap->a_cnp->cn_flags & DOWHITEOUT)
7331f5dbc94Srmind 		tmpfs_dir_attach(dnode, de, TMPFS_NODE_WHITEOUT);
734f68873a3Shannken 	else
7354781942cSrmind 		tmpfs_free_dirent(VFS_TO_TMPFS(vp->v_mount), de);
73649ce9c94Srmind 
7375c373ea8Sad 	tflags = TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME;
738be677426Srmind 	if (node->tn_links > 0) {
739be677426Srmind 		/* We removed a hard link. */
7405c373ea8Sad 		tflags |= TMPFS_UPDATE_CTIME;
741be677426Srmind 	}
7425c373ea8Sad 	tmpfs_update(dvp, tflags);
743ec933656Sjmmv 	error = 0;
744ec933656Sjmmv out:
7456fa7b158Sriastradh 	/* Drop the reference and unlock the node. */
7469d8a0628Srmind 	if (dvp == vp) {
7476fa7b158Sriastradh 		vrele(vp);
7489d8a0628Srmind 	} else {
7496fa7b158Sriastradh 		vput(vp);
7509d8a0628Srmind 	}
751ec933656Sjmmv 	return error;
752ec933656Sjmmv }
753ec933656Sjmmv 
754800683e3Srmind /*
7559d8a0628Srmind  * tmpfs_link: create a hard link.
756800683e3Srmind  */
757ec933656Sjmmv int
758ec933656Sjmmv tmpfs_link(void *v)
759ec933656Sjmmv {
76046e71c7dSriastradh 	struct vop_link_v2_args /* {
761800683e3Srmind 		struct vnode *a_dvp;
762800683e3Srmind 		struct vnode *a_vp;
763800683e3Srmind 		struct componentname *a_cnp;
764800683e3Srmind 	} */ *ap = v;
7659d8a0628Srmind 	vnode_t *dvp = ap->a_dvp;
7669d8a0628Srmind 	vnode_t *vp = ap->a_vp;
76760c9a518Srmind 	struct componentname *cnp = ap->a_cnp;
7681f5dbc94Srmind 	tmpfs_node_t *dnode, *node;
7699d8a0628Srmind 	tmpfs_dirent_t *de;
770800683e3Srmind 	int error;
771ec933656Sjmmv 
772800683e3Srmind 	KASSERT(dvp != vp);
773ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
774800683e3Srmind 	KASSERT(vp->v_type != VDIR);
775800683e3Srmind 	KASSERT(dvp->v_mount == vp->v_mount);
776ec933656Sjmmv 
7771f5dbc94Srmind 	dnode = VP_TO_TMPFS_DIR(dvp);
778ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
779ec933656Sjmmv 
7809abdb3b7Srmind 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
781ec933656Sjmmv 
782e9d92e9cSrmind 	/* Check for maximum number of links limit. */
783ec933656Sjmmv 	if (node->tn_links == LINK_MAX) {
784ec933656Sjmmv 		error = EMLINK;
785ec933656Sjmmv 		goto out;
786ec933656Sjmmv 	}
7874781942cSrmind 	KASSERT(node->tn_links < LINK_MAX);
788ec933656Sjmmv 
789ec933656Sjmmv 	/* We cannot create links of files marked immutable or append-only. */
790ec933656Sjmmv 	if (node->tn_flags & (IMMUTABLE | APPEND)) {
791ec933656Sjmmv 		error = EPERM;
792ec933656Sjmmv 		goto out;
793ec933656Sjmmv 	}
794ec933656Sjmmv 
7954781942cSrmind 	/* Allocate a new directory entry to represent the inode. */
7964781942cSrmind 	error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount),
797ec933656Sjmmv 	    cnp->cn_nameptr, cnp->cn_namelen, &de);
7989d8a0628Srmind 	if (error) {
799ec933656Sjmmv 		goto out;
8009d8a0628Srmind 	}
801ec933656Sjmmv 
8024781942cSrmind 	/*
8034781942cSrmind 	 * Insert the entry into the directory.
8044781942cSrmind 	 * It will increase the inode link count.
8054781942cSrmind 	 */
8061f5dbc94Srmind 	tmpfs_dir_attach(dnode, de, node);
807e63cf28eSrmind 	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
808ec933656Sjmmv 
8094781942cSrmind 	/* Update the timestamps and trigger the event. */
8104781942cSrmind 	if (node->tn_vnode) {
8114781942cSrmind 		VN_KNOTE(node->tn_vnode, NOTE_LINK);
8124781942cSrmind 	}
813e63cf28eSrmind 	tmpfs_update(vp, TMPFS_UPDATE_CTIME);
814ec933656Sjmmv 	error = 0;
815ec933656Sjmmv out:
8161423e65bShannken 	VOP_UNLOCK(vp);
817ec933656Sjmmv 	return error;
818ec933656Sjmmv }
819ec933656Sjmmv 
820ec933656Sjmmv int
821ec933656Sjmmv tmpfs_mkdir(void *v)
822ec933656Sjmmv {
82304c776e5Shannken 	struct vop_mkdir_v3_args /* {
8249d8a0628Srmind 		struct vnode		*a_dvp;
8259d8a0628Srmind 		struct vnode		**a_vpp;
8269d8a0628Srmind 		struct componentname	*a_cnp;
8279d8a0628Srmind 		struct vattr		*a_vap;
8289d8a0628Srmind 	} */ *ap = v;
8299d8a0628Srmind 	vnode_t *dvp = ap->a_dvp;
8309d8a0628Srmind 	vnode_t **vpp = ap->a_vpp;
8319d8a0628Srmind 	struct componentname *cnp = ap->a_cnp;
8329d8a0628Srmind 	struct vattr *vap = ap->a_vap;
833ec933656Sjmmv 
834ec933656Sjmmv 	KASSERT(vap->va_type == VDIR);
835e63cf28eSrmind 	return tmpfs_construct_node(dvp, vpp, vap, cnp, NULL);
836ec933656Sjmmv }
837ec933656Sjmmv 
838ec933656Sjmmv int
839ec933656Sjmmv tmpfs_rmdir(void *v)
840ec933656Sjmmv {
8416fa7b158Sriastradh 	struct vop_rmdir_v2_args /* {
8429d8a0628Srmind 		struct vnode		*a_dvp;
8439d8a0628Srmind 		struct vnode		*a_vp;
8449d8a0628Srmind 		struct componentname	*a_cnp;
8459d8a0628Srmind 	} */ *ap = v;
8469d8a0628Srmind 	vnode_t *dvp = ap->a_dvp;
8479d8a0628Srmind 	vnode_t *vp = ap->a_vp;
8489d8a0628Srmind 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount);
8499d8a0628Srmind 	tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp);
8509d8a0628Srmind 	tmpfs_node_t *node = VP_TO_TMPFS_DIR(vp);
8519d8a0628Srmind 	tmpfs_dirent_t *de;
8529d8a0628Srmind 	int error = 0;
853ec933656Sjmmv 
854ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(dvp));
855ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
856ec933656Sjmmv 
8579d8a0628Srmind 	/*
858e63cf28eSrmind 	 * Directories with more than two entries ('.' and '..') cannot be
859e63cf28eSrmind 	 * removed.  There may be whiteout entries, which we will destroy.
8609d8a0628Srmind 	 */
8614f931d80Spooka 	if (node->tn_size > 0) {
862e63cf28eSrmind 		/*
863e63cf28eSrmind 		 * If never had whiteout entries, the directory is certainly
864e63cf28eSrmind 		 * not empty.  Otherwise, scan for any non-whiteout entry.
865e63cf28eSrmind 		 */
866e63cf28eSrmind 		if ((node->tn_gen & TMPFS_WHITEOUT_BIT) == 0) {
867e63cf28eSrmind 			error = ENOTEMPTY;
868e63cf28eSrmind 			goto out;
869e63cf28eSrmind 		}
870f68873a3Shannken 		TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) {
871f68873a3Shannken 			if (de->td_node != TMPFS_NODE_WHITEOUT) {
8724f931d80Spooka 				error = ENOTEMPTY;
8734f931d80Spooka 				goto out;
8744f931d80Spooka 			}
875e63cf28eSrmind 		}
876e63cf28eSrmind 		KASSERT(error == 0);
877e63cf28eSrmind 	}
8784f931d80Spooka 
87965d0f000Spedro 	KASSERT(node->tn_spec.tn_dir.tn_parent == dnode);
88065d0f000Spedro 
8814781942cSrmind 	/* Lookup the directory entry (check the cached hint first). */
8824781942cSrmind 	de = tmpfs_dir_cached(node);
8834781942cSrmind 	if (de == NULL) {
8844781942cSrmind 		struct componentname *cnp = ap->a_cnp;
8854a780c9aSad 		de = tmpfs_dir_lookup(dnode, cnp);
8864781942cSrmind 	}
8879d8a0628Srmind 	KASSERT(de && de->td_node == node);
888ec933656Sjmmv 
889ec933656Sjmmv 	/* Check flags to see if we are allowed to remove the directory. */
890ec933656Sjmmv 	if (dnode->tn_flags & APPEND || node->tn_flags & (IMMUTABLE | APPEND)) {
891ec933656Sjmmv 		error = EPERM;
892ec933656Sjmmv 		goto out;
893ec933656Sjmmv 	}
894ec933656Sjmmv 
8954781942cSrmind 	/* Decrement the link count for the virtual '.' entry. */
896ec933656Sjmmv 	node->tn_links--;
897ec933656Sjmmv 
8984cffafacSrmind 	/* Detach the directory entry from the directory. */
8991f5dbc94Srmind 	tmpfs_dir_detach(dnode, de);
9004cffafacSrmind 
9019d8a0628Srmind 	/* Purge the cache for parent. */
9029d8a0628Srmind 	cache_purge(dvp);
903ec933656Sjmmv 
9049d8a0628Srmind 	/*
905f68873a3Shannken 	 * Destroy the directory entry or replace it with a whiteout.
906e63cf28eSrmind 	 *
907e63cf28eSrmind 	 * Note: the inode referred by it will not be destroyed until the
908e63cf28eSrmind 	 * vnode is reclaimed.
9099d8a0628Srmind 	 */
910f68873a3Shannken 	if (ap->a_cnp->cn_flags & DOWHITEOUT)
9111f5dbc94Srmind 		tmpfs_dir_attach(dnode, de, TMPFS_NODE_WHITEOUT);
912f68873a3Shannken 	else
9134781942cSrmind 		tmpfs_free_dirent(tmp, de);
914f68873a3Shannken 
915f68873a3Shannken 	/* Destroy the whiteout entries from the node. */
916f68873a3Shannken 	while ((de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir)) != NULL) {
917f68873a3Shannken 		KASSERT(de->td_node == TMPFS_NODE_WHITEOUT);
9181f5dbc94Srmind 		tmpfs_dir_detach(node, de);
919f68873a3Shannken 		tmpfs_free_dirent(tmp, de);
920f68873a3Shannken 	}
921e63cf28eSrmind 	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
922f68873a3Shannken 
923e63cf28eSrmind 	KASSERT(node->tn_size == 0);
9244a780c9aSad 	KASSERT(node->tn_links == 0);
925ec933656Sjmmv out:
9266fa7b158Sriastradh 	/* Release the node. */
9276fa7b158Sriastradh 	KASSERT(dvp != vp);
928ec933656Sjmmv 	vput(vp);
929ec933656Sjmmv 	return error;
930ec933656Sjmmv }
931ec933656Sjmmv 
932ec933656Sjmmv int
933ec933656Sjmmv tmpfs_symlink(void *v)
934ec933656Sjmmv {
93504c776e5Shannken 	struct vop_symlink_v3_args /* {
9369d8a0628Srmind 		struct vnode		*a_dvp;
9379d8a0628Srmind 		struct vnode		**a_vpp;
9389d8a0628Srmind 		struct componentname	*a_cnp;
9399d8a0628Srmind 		struct vattr		*a_vap;
9409d8a0628Srmind 		char			*a_target;
9419d8a0628Srmind 	} */ *ap = v;
9429d8a0628Srmind 	vnode_t *dvp = ap->a_dvp;
9439d8a0628Srmind 	vnode_t **vpp = ap->a_vpp;
9449d8a0628Srmind 	struct componentname *cnp = ap->a_cnp;
9459d8a0628Srmind 	struct vattr *vap = ap->a_vap;
9469d8a0628Srmind 	char *target = ap->a_target;
947ec933656Sjmmv 
948ec933656Sjmmv 	KASSERT(vap->va_type == VLNK);
949e63cf28eSrmind 	return tmpfs_construct_node(dvp, vpp, vap, cnp, target);
950ec933656Sjmmv }
951ec933656Sjmmv 
952ec933656Sjmmv int
953ec933656Sjmmv tmpfs_readdir(void *v)
954ec933656Sjmmv {
9559d8a0628Srmind 	struct vop_readdir_args /* {
9569d8a0628Srmind 		struct vnode	*a_vp;
9579d8a0628Srmind 		struct uio	*a_uio;
9589d8a0628Srmind 		kauth_cred_t	a_cred;
9599d8a0628Srmind 		int		*a_eofflag;
9609d8a0628Srmind 		off_t		**a_cookies;
9619d8a0628Srmind 		int		*ncookies;
9629d8a0628Srmind 	} */ *ap = v;
9639d8a0628Srmind 	vnode_t *vp = ap->a_vp;
9649d8a0628Srmind 	struct uio *uio = ap->a_uio;
9659d8a0628Srmind 	int *eofflag = ap->a_eofflag;
9669d8a0628Srmind 	off_t **cookies = ap->a_cookies;
9679d8a0628Srmind 	int *ncookies = ap->a_ncookies;
9689d8a0628Srmind 	off_t startoff, cnt;
9699d8a0628Srmind 	tmpfs_node_t *node;
970ec933656Sjmmv 	int error;
971ec933656Sjmmv 
972ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
973ec933656Sjmmv 
974ec933656Sjmmv 	/* This operation only makes sense on directory nodes. */
975ec933656Sjmmv 	if (vp->v_type != VDIR) {
9769d8a0628Srmind 		return ENOTDIR;
977ec933656Sjmmv 	}
978ec933656Sjmmv 	node = VP_TO_TMPFS_DIR(vp);
979ec933656Sjmmv 	startoff = uio->uio_offset;
9808e6209cfSyamt 	cnt = 0;
9811f5dbc94Srmind 
9821f5dbc94Srmind 	/*
9831f5dbc94Srmind 	 * Retrieve the directory entries, unless it is being destroyed.
9841f5dbc94Srmind 	 */
9851f5dbc94Srmind 	if (node->tn_links) {
9861f5dbc94Srmind 		error = tmpfs_dir_getdents(node, uio, &cnt);
9871f5dbc94Srmind 	} else {
98845153411Schs 		error = 0;
98945153411Schs 	}
9909d8a0628Srmind 
9919d8a0628Srmind 	if (eofflag != NULL) {
9921f5dbc94Srmind 		*eofflag = !error && uio->uio_offset == TMPFS_DIRSEQ_EOF;
9939d8a0628Srmind 	}
9949d8a0628Srmind 	if (error || cookies == NULL || ncookies == NULL) {
9959d8a0628Srmind 		return error;
9969d8a0628Srmind 	}
997ec933656Sjmmv 
9989d8a0628Srmind 	/* Update NFS-related variables, if any. */
9999d8a0628Srmind 	tmpfs_dirent_t *de = NULL;
10001f5dbc94Srmind 	off_t i, off = startoff;
1001ec933656Sjmmv 
10028e6209cfSyamt 	*cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK);
10039d8a0628Srmind 	*ncookies = cnt;
1004ec933656Sjmmv 
10058e6209cfSyamt 	for (i = 0; i < cnt; i++) {
10061f5dbc94Srmind 		KASSERT(off != TMPFS_DIRSEQ_EOF);
10071f5dbc94Srmind 		if (off != TMPFS_DIRSEQ_DOT) {
10081f5dbc94Srmind 			if (off == TMPFS_DIRSEQ_DOTDOT) {
10099d8a0628Srmind 				de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir);
10108e6209cfSyamt 			} else if (de != NULL) {
10118e6209cfSyamt 				de = TAILQ_NEXT(de, td_entries);
10128e6209cfSyamt 			} else {
10131f5dbc94Srmind 				de = tmpfs_dir_lookupbyseq(node, off);
10148e6209cfSyamt 				KASSERT(de != NULL);
10158e6209cfSyamt 				de = TAILQ_NEXT(de, td_entries);
10168e6209cfSyamt 			}
10178e6209cfSyamt 			if (de == NULL) {
10181f5dbc94Srmind 				off = TMPFS_DIRSEQ_EOF;
10198e6209cfSyamt 			} else {
10201f5dbc94Srmind 				off = tmpfs_dir_getseq(node, de);
10218e6209cfSyamt 			}
10229d8a0628Srmind 		} else {
10231f5dbc94Srmind 			off = TMPFS_DIRSEQ_DOTDOT;
10248e6209cfSyamt 		}
10258e6209cfSyamt 		(*cookies)[i] = off;
10268e6209cfSyamt 	}
10278e6209cfSyamt 	KASSERT(uio->uio_offset == off);
1028ec933656Sjmmv 	return error;
1029ec933656Sjmmv }
1030ec933656Sjmmv 
1031ec933656Sjmmv int
1032ec933656Sjmmv tmpfs_readlink(void *v)
1033ec933656Sjmmv {
10349d8a0628Srmind 	struct vop_readlink_args /* {
10359d8a0628Srmind 		struct vnode	*a_vp;
10369d8a0628Srmind 		struct uio	*a_uio;
10379d8a0628Srmind 		kauth_cred_t	a_cred;
10389d8a0628Srmind 	} */ *ap = v;
10399d8a0628Srmind 	vnode_t *vp = ap->a_vp;
10409d8a0628Srmind 	struct uio *uio = ap->a_uio;
1041f8abe6cbSrmind 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
1042ec933656Sjmmv 	int error;
1043ec933656Sjmmv 
1044ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1045ec933656Sjmmv 	KASSERT(uio->uio_offset == 0);
1046ec933656Sjmmv 	KASSERT(vp->v_type == VLNK);
1047ec933656Sjmmv 
104820a51a97Srmind 	/* Note: readlink(2) returns the path without NUL terminator. */
104920a51a97Srmind 	if (node->tn_size > 0) {
1050064fbe7eSjmmv 		error = uiomove(node->tn_spec.tn_lnk.tn_link,
10511b265e67Sgson 		    MIN(node->tn_size, uio->uio_resid), uio);
105220a51a97Srmind 	} else {
105320a51a97Srmind 		error = 0;
105420a51a97Srmind 	}
1055e63cf28eSrmind 	tmpfs_update(vp, TMPFS_UPDATE_ATIME);
1056ec933656Sjmmv 
1057ec933656Sjmmv 	return error;
1058ec933656Sjmmv }
1059ec933656Sjmmv 
1060ec933656Sjmmv int
1061ec933656Sjmmv tmpfs_inactive(void *v)
1062ec933656Sjmmv {
106387fb3229Sriastradh 	struct vop_inactive_v2_args /* {
106460c9a518Srmind 		struct vnode *a_vp;
106560c9a518Srmind 		bool *a_recycle;
106660c9a518Srmind 	} */ *ap = v;
10679d8a0628Srmind 	vnode_t *vp = ap->a_vp;
10689d8a0628Srmind 	tmpfs_node_t *node;
1069d39cb654Sad 	int error = 0;
1070ec933656Sjmmv 
1071ec933656Sjmmv 	KASSERT(VOP_ISLOCKED(vp));
1072ec933656Sjmmv 
1073ec933656Sjmmv 	node = VP_TO_TMPFS_NODE(vp);
10748ea5485dShannken 	if (node->tn_links == 0) {
10758ea5485dShannken 		/*
10768ea5485dShannken 		 * Mark node as dead by setting its generation to zero.
10778ea5485dShannken 		 */
10788ea5485dShannken 		atomic_and_32(&node->tn_gen, ~TMPFS_NODE_GEN_MASK);
1079d39cb654Sad 
1080d39cb654Sad 		/*
1081d39cb654Sad 		 * If the file has been deleted, truncate it, otherwise VFS
1082d39cb654Sad 		 * will quite rightly try to write back dirty data, which in
1083d39cb654Sad 		 * the case of tmpfs/UAO means needless page deactivations.
1084d39cb654Sad 		 */
1085d39cb654Sad 		if (vp->v_type == VREG) {
1086d39cb654Sad 			error = tmpfs_reg_resize(vp, 0);
1087d39cb654Sad 		}
10888ea5485dShannken 		*ap->a_recycle = true;
10898ea5485dShannken 	} else {
10905c373ea8Sad 		tmpfs_update(vp, 0);
10918ea5485dShannken 		*ap->a_recycle = false;
10928ea5485dShannken 	}
1093ec933656Sjmmv 
1094d39cb654Sad 	return error;
1095ec933656Sjmmv }
1096ec933656Sjmmv 
1097ec933656Sjmmv int
1098ec933656Sjmmv tmpfs_reclaim(void *v)
1099ec933656Sjmmv {
11007f7aad09Sriastradh 	struct vop_reclaim_v2_args /* {
110160c9a518Srmind 		struct vnode *a_vp;
110260c9a518Srmind 	} */ *ap = v;
11039d8a0628Srmind 	vnode_t *vp = ap->a_vp;
11049d8a0628Srmind 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(vp->v_mount);
11059d8a0628Srmind 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
1106ec933656Sjmmv 
11077f7aad09Sriastradh 	/* Unlock vnode.  We still have exclusive access to it. */
11087f7aad09Sriastradh 	VOP_UNLOCK(vp);
11097f7aad09Sriastradh 
111060c9a518Srmind 	/* Disassociate inode from vnode. */
11114781942cSrmind 	node->tn_vnode = NULL;
11124781942cSrmind 	vp->v_data = NULL;
1113ccc45228Srmind 
1114ccc45228Srmind 	/* If inode is not referenced, i.e. no links, then destroy it. */
11158c80da52Shannken 	if (node->tn_links == 0)
111660c9a518Srmind 		tmpfs_free_node(tmp, node);
1117ec933656Sjmmv 	return 0;
1118ec933656Sjmmv }
1119ec933656Sjmmv 
1120ec933656Sjmmv int
1121ec933656Sjmmv tmpfs_pathconf(void *v)
1122ec933656Sjmmv {
11239d8a0628Srmind 	struct vop_pathconf_args /* {
11249d8a0628Srmind 		struct vnode	*a_vp;
11259d8a0628Srmind 		int		a_name;
11269d8a0628Srmind 		register_t	*a_retval;
11279d8a0628Srmind 	} */ *ap = v;
11289d8a0628Srmind 	const int name = ap->a_name;
11299d8a0628Srmind 	register_t *retval = ap->a_retval;
11309d8a0628Srmind 	int error = 0;
1131ec933656Sjmmv 
1132ec933656Sjmmv 	switch (name) {
1133ec933656Sjmmv 	case _PC_LINK_MAX:
1134ec933656Sjmmv 		*retval = LINK_MAX;
1135ec933656Sjmmv 		break;
1136ec933656Sjmmv 	case _PC_NAME_MAX:
1137367fc932Schristos 		*retval = TMPFS_MAXNAMLEN;
1138ec933656Sjmmv 		break;
1139ec933656Sjmmv 	case _PC_PATH_MAX:
1140ec933656Sjmmv 		*retval = PATH_MAX;
1141ec933656Sjmmv 		break;
1142ec933656Sjmmv 	case _PC_PIPE_BUF:
1143ec933656Sjmmv 		*retval = PIPE_BUF;
1144ec933656Sjmmv 		break;
1145ec933656Sjmmv 	case _PC_CHOWN_RESTRICTED:
1146ec933656Sjmmv 		*retval = 1;
1147ec933656Sjmmv 		break;
1148ec933656Sjmmv 	case _PC_NO_TRUNC:
1149ec933656Sjmmv 		*retval = 1;
1150ec933656Sjmmv 		break;
1151ec933656Sjmmv 	case _PC_SYNC_IO:
1152ec933656Sjmmv 		*retval = 1;
1153ec933656Sjmmv 		break;
1154ec933656Sjmmv 	case _PC_FILESIZEBITS:
11554781942cSrmind 		*retval = sizeof(off_t) * CHAR_BIT;
1156ec933656Sjmmv 		break;
1157ec933656Sjmmv 	default:
1158ec933656Sjmmv 		error = EINVAL;
1159ec933656Sjmmv 	}
1160ec933656Sjmmv 	return error;
1161ec933656Sjmmv }
1162ec933656Sjmmv 
1163ec933656Sjmmv int
1164b6d141c7Sjmmv tmpfs_advlock(void *v)
1165b6d141c7Sjmmv {
11669d8a0628Srmind 	struct vop_advlock_args /* {
11679d8a0628Srmind 		struct vnode	*a_vp;
11689d8a0628Srmind 		void *		a_id;
11699d8a0628Srmind 		int		a_op;
11709d8a0628Srmind 		struct flock	*a_fl;
11719d8a0628Srmind 		int		a_flags;
11729d8a0628Srmind 	} */ *ap = v;
11739d8a0628Srmind 	vnode_t *vp = ap->a_vp;
11749d8a0628Srmind 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
1175b6d141c7Sjmmv 
1176b6d141c7Sjmmv 	return lf_advlock(v, &node->tn_lockf, node->tn_size);
1177b6d141c7Sjmmv }
1178b6d141c7Sjmmv 
1179b6d141c7Sjmmv int
1180ec933656Sjmmv tmpfs_getpages(void *v)
1181ec933656Sjmmv {
11828f2efd02Srmind 	struct vop_getpages_args /* {
11838f2efd02Srmind 		struct vnode *a_vp;
11848f2efd02Srmind 		voff_t a_offset;
11858f2efd02Srmind 		struct vm_page **a_m;
11868f2efd02Srmind 		int *a_count;
11878f2efd02Srmind 		int a_centeridx;
11888f2efd02Srmind 		vm_prot_t a_access_type;
11898f2efd02Srmind 		int a_advice;
11908f2efd02Srmind 		int a_flags;
11918f2efd02Srmind 	} */ * const ap = v;
11929d8a0628Srmind 	vnode_t *vp = ap->a_vp;
11938f2efd02Srmind 	const voff_t offset = ap->a_offset;
11948f2efd02Srmind 	struct vm_page **pgs = ap->a_m;
11958f2efd02Srmind 	const int centeridx = ap->a_centeridx;
11968f2efd02Srmind 	const vm_prot_t access_type = ap->a_access_type;
11978f2efd02Srmind 	const int advice = ap->a_advice;
11988f2efd02Srmind 	const int flags = ap->a_flags;
11995c373ea8Sad 	int error, iflag, npages = *ap->a_count;
12009d8a0628Srmind 	tmpfs_node_t *node;
12015f4b660eSjmmv 	struct uvm_object *uobj;
1202ec933656Sjmmv 
1203647aa775Syamt 	KASSERT(vp->v_type == VREG);
1204d2a0ebb6Sad 	KASSERT(rw_lock_held(vp->v_uobj.vmobjlock));
1205ec933656Sjmmv 
12068f2efd02Srmind 	/*
12078f2efd02Srmind 	 * Currently, PGO_PASTEOF is not supported.
12088f2efd02Srmind 	 */
120920bb9654Syamt 	if (vp->v_size <= offset + (centeridx << PAGE_SHIFT)) {
121020bb9654Syamt 		if ((flags & PGO_LOCKED) == 0)
1211d2a0ebb6Sad 			rw_exit(vp->v_uobj.vmobjlock);
121220bb9654Syamt 		return EINVAL;
121320bb9654Syamt 	}
121420bb9654Syamt 
121520bb9654Syamt 	if (vp->v_size < offset + (npages << PAGE_SHIFT)) {
121620bb9654Syamt 		npages = (round_page(vp->v_size) - offset) >> PAGE_SHIFT;
121720bb9654Syamt 	}
121820bb9654Syamt 
12195c373ea8Sad 	/*
12205c373ea8Sad 	 * Check for reclaimed vnode.  v_interlock is not held here, but
12215c373ea8Sad 	 * VI_DEADCHECK is set with vmobjlock held.
12225c373ea8Sad 	 */
12235c373ea8Sad 	iflag = atomic_load_relaxed(&vp->v_iflag);
1224c3cbbd5eSad 	if (__predict_false((iflag & VI_DEADCHECK) != 0)) {
1225d2a0ebb6Sad 		mutex_enter(vp->v_interlock);
1226d2a0ebb6Sad 		error = vdead_check(vp, VDEAD_NOWAIT);
1227d2a0ebb6Sad 		mutex_exit(vp->v_interlock);
12285c373ea8Sad 		if (error) {
12295c373ea8Sad 			if ((flags & PGO_LOCKED) == 0)
12305c373ea8Sad 				rw_exit(vp->v_uobj.vmobjlock);
12315c373ea8Sad 			return error;
12325c373ea8Sad 		}
12335c373ea8Sad 	}
12343c404d2cShannken 
12353c404d2cShannken 	node = VP_TO_TMPFS_NODE(vp);
12363c404d2cShannken 	uobj = node->tn_spec.tn_reg.tn_aobj;
12373c404d2cShannken 
12385c373ea8Sad 	/*
12395c373ea8Sad 	 * Update timestamp lazily.  The update will be made real when
12405c373ea8Sad 	 * a synchronous update is next made -- or by tmpfs_getattr,
12415c373ea8Sad 	 * tmpfs_putpages, and tmpfs_inactive.
12425c373ea8Sad 	 */
1243647aa775Syamt 	if ((flags & PGO_NOTIMESTAMP) == 0) {
1244e63cf28eSrmind 		u_int tflags = 0;
1245e63cf28eSrmind 
12465f4b660eSjmmv 		if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0)
1247e63cf28eSrmind 			tflags |= TMPFS_UPDATE_ATIME;
12485f4b660eSjmmv 
1249d11ea3eaSchristos 		if ((access_type & VM_PROT_WRITE) != 0) {
1250e63cf28eSrmind 			tflags |= TMPFS_UPDATE_MTIME;
1251d11ea3eaSchristos 			if (vp->v_mount->mnt_flag & MNT_RELATIME)
1252e63cf28eSrmind 				tflags |= TMPFS_UPDATE_ATIME;
1253d11ea3eaSchristos 		}
12545c373ea8Sad 		tmpfs_update_lazily(vp, tflags);
1255647aa775Syamt 	}
1256ec933656Sjmmv 
1257812b46dfSad 	/* Invoke the pager.  The vnode vmobjlock is shared with the UAO. */
1258d2a0ebb6Sad 	KASSERT(vp->v_uobj.vmobjlock == uobj->vmobjlock);
12598f2efd02Srmind 	error = (*uobj->pgops->pgo_get)(uobj, offset, pgs, &npages, centeridx,
12605c373ea8Sad 	    access_type, advice, flags);
126151634dfdSjmmv #if defined(DEBUG)
12628f2efd02Srmind 	if (!error && pgs) {
12635c373ea8Sad 		KASSERT(pgs[centeridx] != NULL);
126451634dfdSjmmv 	}
126551634dfdSjmmv #endif
1266647aa775Syamt 	return error;
1267647aa775Syamt }
1268647aa775Syamt 
1269647aa775Syamt int
1270647aa775Syamt tmpfs_putpages(void *v)
1271647aa775Syamt {
12728f2efd02Srmind 	struct vop_putpages_args /* {
12738f2efd02Srmind 		struct vnode *a_vp;
12748f2efd02Srmind 		voff_t a_offlo;
12758f2efd02Srmind 		voff_t a_offhi;
12768f2efd02Srmind 		int a_flags;
12778f2efd02Srmind 	} */ * const ap = v;
12789d8a0628Srmind 	vnode_t *vp = ap->a_vp;
12798f2efd02Srmind 	const voff_t offlo = ap->a_offlo;
12808f2efd02Srmind 	const voff_t offhi = ap->a_offhi;
12818f2efd02Srmind 	const int flags = ap->a_flags;
12829d8a0628Srmind 	tmpfs_node_t *node;
12835f4b660eSjmmv 	struct uvm_object *uobj;
12848f2efd02Srmind 	int error;
1285647aa775Syamt 
1286d2a0ebb6Sad 	KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
12875f4b660eSjmmv 
1288647aa775Syamt 	if (vp->v_type != VREG) {
1289d2a0ebb6Sad 		rw_exit(vp->v_uobj.vmobjlock);
1290647aa775Syamt 		return 0;
1291647aa775Syamt 	}
1292647aa775Syamt 
1293e225b7bdSrmind 	node = VP_TO_TMPFS_NODE(vp);
1294064fbe7eSjmmv 	uobj = node->tn_spec.tn_reg.tn_aobj;
1295647aa775Syamt 
1296d2a0ebb6Sad 	KASSERT(vp->v_uobj.vmobjlock == uobj->vmobjlock);
12975f4b660eSjmmv 	error = (*uobj->pgops->pgo_put)(uobj, offlo, offhi, flags);
1298647aa775Syamt 
1299647aa775Syamt 	/* XXX mtime */
1300647aa775Syamt 
13015c373ea8Sad 	/* Process deferred updates. */
13025c373ea8Sad 	tmpfs_update(vp, 0);
1303ec933656Sjmmv 	return error;
1304ec933656Sjmmv }
130507b10a7bSpooka 
130607b10a7bSpooka int
130707b10a7bSpooka tmpfs_whiteout(void *v)
130807b10a7bSpooka {
13099d8a0628Srmind 	struct vop_whiteout_args /* {
13109d8a0628Srmind 		struct vnode		*a_dvp;
13119d8a0628Srmind 		struct componentname	*a_cnp;
13129d8a0628Srmind 		int			a_flags;
13139d8a0628Srmind 	} */ *ap = v;
13149d8a0628Srmind 	vnode_t *dvp = ap->a_dvp;
13159d8a0628Srmind 	struct componentname *cnp = ap->a_cnp;
13169d8a0628Srmind 	const int flags = ap->a_flags;
13179d8a0628Srmind 	tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount);
13181f5dbc94Srmind 	tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp);
13199d8a0628Srmind 	tmpfs_dirent_t *de;
132007b10a7bSpooka 	int error;
132107b10a7bSpooka 
132207b10a7bSpooka 	switch (flags) {
132307b10a7bSpooka 	case LOOKUP:
132407b10a7bSpooka 		break;
132507b10a7bSpooka 	case CREATE:
13264781942cSrmind 		error = tmpfs_alloc_dirent(tmp, cnp->cn_nameptr,
13274781942cSrmind 		    cnp->cn_namelen, &de);
132807b10a7bSpooka 		if (error)
132907b10a7bSpooka 			return error;
13301f5dbc94Srmind 		tmpfs_dir_attach(dnode, de, TMPFS_NODE_WHITEOUT);
133107b10a7bSpooka 		break;
133207b10a7bSpooka 	case DELETE:
133307b10a7bSpooka 		cnp->cn_flags &= ~DOWHITEOUT; /* when in doubt, cargo cult */
13341f5dbc94Srmind 		de = tmpfs_dir_lookup(dnode, cnp);
133507b10a7bSpooka 		if (de == NULL)
133607b10a7bSpooka 			return ENOENT;
13371f5dbc94Srmind 		tmpfs_dir_detach(dnode, de);
13384781942cSrmind 		tmpfs_free_dirent(tmp, de);
133907b10a7bSpooka 		break;
134007b10a7bSpooka 	}
1341e63cf28eSrmind 	tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME);
134207b10a7bSpooka 	return 0;
134307b10a7bSpooka }
13449d8a0628Srmind 
13459d8a0628Srmind int
13469d8a0628Srmind tmpfs_print(void *v)
13479d8a0628Srmind {
13489d8a0628Srmind 	struct vop_print_args /* {
13499d8a0628Srmind 		struct vnode	*a_vp;
13509d8a0628Srmind 	} */ *ap = v;
13519d8a0628Srmind 	vnode_t *vp = ap->a_vp;
13529d8a0628Srmind 	tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp);
13539d8a0628Srmind 
13549d8a0628Srmind 	printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n"
1355e63cf28eSrmind 	    "\tmode 0%o, owner %d, group %d, size %" PRIdMAX,
13569d8a0628Srmind 	    node, node->tn_flags, node->tn_links, node->tn_mode, node->tn_uid,
1357e63cf28eSrmind 	    node->tn_gid, (uintmax_t)node->tn_size);
13589d8a0628Srmind 	if (vp->v_type == VFIFO) {
13599d8a0628Srmind 		VOCALL(fifo_vnodeop_p, VOFFSET(vop_print), v);
13609d8a0628Srmind 	}
13619d8a0628Srmind 	printf("\n");
13629d8a0628Srmind 	return 0;
13639d8a0628Srmind }
1364