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