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