1*5a0ec814Smiod /* $OpenBSD: tmpfs_vnops.c,v 1.56 2024/10/18 05:52:32 miod Exp $ */ 27013b092Sespie /* $NetBSD: tmpfs_vnops.c,v 1.100 2012/11/05 17:27:39 dholland Exp $ */ 37013b092Sespie 47013b092Sespie /* 57013b092Sespie * Copyright (c) 2005, 2006, 2007, 2012 The NetBSD Foundation, Inc. 67013b092Sespie * Copyright (c) 2013 Pedro Martelletto 77013b092Sespie * All rights reserved. 87013b092Sespie * 97013b092Sespie * This code is derived from software contributed to The NetBSD Foundation 107013b092Sespie * by Julio M. Merino Vidal, developed as part of Google's Summer of Code 117013b092Sespie * 2005 program, and by Taylor R Campbell. 127013b092Sespie * 137013b092Sespie * Redistribution and use in source and binary forms, with or without 147013b092Sespie * modification, are permitted provided that the following conditions 157013b092Sespie * are met: 167013b092Sespie * 1. Redistributions of source code must retain the above copyright 177013b092Sespie * notice, this list of conditions and the following disclaimer. 187013b092Sespie * 2. Redistributions in binary form must reproduce the above copyright 197013b092Sespie * notice, this list of conditions and the following disclaimer in the 207013b092Sespie * documentation and/or other materials provided with the distribution. 217013b092Sespie * 227013b092Sespie * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 237013b092Sespie * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 247013b092Sespie * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 257013b092Sespie * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 267013b092Sespie * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 277013b092Sespie * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 287013b092Sespie * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 297013b092Sespie * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 307013b092Sespie * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 317013b092Sespie * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 327013b092Sespie * POSSIBILITY OF SUCH DAMAGE. 337013b092Sespie */ 347013b092Sespie 357013b092Sespie /* 367013b092Sespie * tmpfs vnode interface. 377013b092Sespie */ 387013b092Sespie 397013b092Sespie #include <sys/param.h> 4099ef5f49Svisa #include <sys/systm.h> 417013b092Sespie #include <sys/fcntl.h> 427013b092Sespie #include <sys/event.h> 437013b092Sespie #include <sys/namei.h> 447013b092Sespie #include <sys/stat.h> 457013b092Sespie #include <sys/uio.h> 467013b092Sespie #include <sys/unistd.h> 477013b092Sespie #include <sys/vnode.h> 487013b092Sespie #include <sys/lockf.h> 49f9ac7274Stedu #include <sys/file.h> 507013b092Sespie 517013b092Sespie #include <miscfs/fifofs/fifo.h> 527013b092Sespie #include <tmpfs/tmpfs_vnops.h> 537013b092Sespie #include <tmpfs/tmpfs.h> 547013b092Sespie 55f9ac7274Stedu int tmpfs_kqfilter(void *v); 56f9ac7274Stedu 577013b092Sespie /* 587013b092Sespie * vnode operations vector used for files stored in a tmpfs file system. 597013b092Sespie */ 602d6b9e38Sclaudio const struct vops tmpfs_vops = { 617013b092Sespie .vop_lookup = tmpfs_lookup, 627013b092Sespie .vop_create = tmpfs_create, 637013b092Sespie .vop_mknod = tmpfs_mknod, 647013b092Sespie .vop_open = tmpfs_open, 657013b092Sespie .vop_close = tmpfs_close, 667013b092Sespie .vop_access = tmpfs_access, 677013b092Sespie .vop_getattr = tmpfs_getattr, 687013b092Sespie .vop_setattr = tmpfs_setattr, 697013b092Sespie .vop_read = tmpfs_read, 707013b092Sespie .vop_write = tmpfs_write, 717013b092Sespie .vop_ioctl = tmpfs_ioctl, 72f9ac7274Stedu .vop_kqfilter = tmpfs_kqfilter, 737013b092Sespie .vop_revoke = vop_generic_revoke, 747013b092Sespie .vop_fsync = tmpfs_fsync, 757013b092Sespie .vop_remove = tmpfs_remove, 767013b092Sespie .vop_link = tmpfs_link, 777013b092Sespie .vop_rename = tmpfs_rename, 787013b092Sespie .vop_mkdir = tmpfs_mkdir, 797013b092Sespie .vop_rmdir = tmpfs_rmdir, 807013b092Sespie .vop_symlink = tmpfs_symlink, 817013b092Sespie .vop_readdir = tmpfs_readdir, 827013b092Sespie .vop_readlink = tmpfs_readlink, 837013b092Sespie .vop_abortop = vop_generic_abortop, 847013b092Sespie .vop_inactive = tmpfs_inactive, 857013b092Sespie .vop_reclaim = tmpfs_reclaim, 867013b092Sespie .vop_lock = tmpfs_lock, 877013b092Sespie .vop_unlock = tmpfs_unlock, 887013b092Sespie .vop_bmap = vop_generic_bmap, 897013b092Sespie .vop_strategy = tmpfs_strategy, 907013b092Sespie .vop_print = tmpfs_print, 917013b092Sespie .vop_islocked = tmpfs_islocked, 927013b092Sespie .vop_pathconf = tmpfs_pathconf, 937013b092Sespie .vop_advlock = tmpfs_advlock, 947013b092Sespie .vop_bwrite = tmpfs_bwrite, 957013b092Sespie }; 967013b092Sespie 977013b092Sespie /* 987013b092Sespie * tmpfs_lookup: path name traversal routine. 997013b092Sespie * 1007013b092Sespie * Arguments: dvp (directory being searched), vpp (result), 1017013b092Sespie * cnp (component name - path). 1027013b092Sespie * 1037013b092Sespie * => Caller holds a reference and lock on dvp. 1047013b092Sespie * => We return looked-up vnode (vpp) locked, with a reference held. 1057013b092Sespie */ 1067013b092Sespie int 1077013b092Sespie tmpfs_lookup(void *v) 1087013b092Sespie { 1097013b092Sespie struct vop_lookup_args /* { 1107013b092Sespie struct vnode *a_dvp; 1117013b092Sespie struct vnode **a_vpp; 1127013b092Sespie struct componentname *a_cnp; 1137013b092Sespie } */ *ap = v; 1147013b092Sespie struct vnode *dvp = ap->a_dvp, **vpp = ap->a_vpp; 1157013b092Sespie struct componentname *cnp = ap->a_cnp; 1167013b092Sespie struct ucred *cred = cnp->cn_cred; 1177013b092Sespie const int lastcn = (cnp->cn_flags & ISLASTCN) != 0; 1187013b092Sespie const int lockparent = (cnp->cn_flags & LOCKPARENT) != 0; 1197013b092Sespie tmpfs_node_t *dnode, *tnode; 1207013b092Sespie tmpfs_dirent_t *de; 1217013b092Sespie int cachefound; 1227013b092Sespie int error; 1237013b092Sespie 1247013b092Sespie KASSERT(VOP_ISLOCKED(dvp)); 1257013b092Sespie 1267013b092Sespie dnode = VP_TO_TMPFS_DIR(dvp); 1277013b092Sespie cnp->cn_flags &= ~PDIRUNLOCK; 1287013b092Sespie *vpp = NULL; 1297013b092Sespie 1307013b092Sespie /* Check accessibility of directory. */ 1317013b092Sespie error = VOP_ACCESS(dvp, VEXEC, cred, curproc); 1327013b092Sespie if (error) { 1337013b092Sespie goto out; 1347013b092Sespie } 1357013b092Sespie 1367013b092Sespie /* 1377013b092Sespie * If requesting the last path component on a read-only file system 1387013b092Sespie * with a write operation, deny it. 1397013b092Sespie */ 1407013b092Sespie if (lastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) != 0 && 1417013b092Sespie (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { 1427013b092Sespie error = EROFS; 1437013b092Sespie goto out; 1447013b092Sespie } 1457013b092Sespie 1467013b092Sespie /* 1477013b092Sespie * Avoid doing a linear scan of the directory if the requested 1487013b092Sespie * directory/name couple is already in the cache. 1497013b092Sespie */ 1507013b092Sespie cachefound = cache_lookup(dvp, vpp, cnp); 1517013b092Sespie if (cachefound == ENOENT /* && *vpp == NULLVP */) 1527013b092Sespie return ENOENT; /* Negative cache hit. */ 1537013b092Sespie else if (cachefound != -1) 1547013b092Sespie return 0; /* Found in cache. */ 1557013b092Sespie 1567013b092Sespie if (cnp->cn_flags & ISDOTDOT) { 1577013b092Sespie tmpfs_node_t *pnode; 1587013b092Sespie 1597013b092Sespie /* 1607013b092Sespie * Lookup of ".." case. 1617013b092Sespie */ 16246905cb9Sespie if (lastcn) { 16346905cb9Sespie if (cnp->cn_nameiop == RENAME) { 1647013b092Sespie error = EINVAL; 1657013b092Sespie goto out; 1667013b092Sespie } 16746905cb9Sespie if (cnp->cn_nameiop == DELETE) { 16846905cb9Sespie /* Keep the name for tmpfs_rmdir(). */ 16946905cb9Sespie cnp->cn_flags |= SAVENAME; 17046905cb9Sespie } 17146905cb9Sespie } 1727013b092Sespie KASSERT(dnode->tn_type == VDIR); 1737013b092Sespie pnode = dnode->tn_spec.tn_dir.tn_parent; 1747013b092Sespie if (pnode == NULL) { 1757013b092Sespie error = ENOENT; 1767013b092Sespie goto out; 1777013b092Sespie } 1787013b092Sespie 1797013b092Sespie /* 1807013b092Sespie * Lock the parent tn_nlock before releasing the vnode lock, 1817013b092Sespie * and thus prevents parent from disappearing. 1827013b092Sespie */ 1837013b092Sespie rw_enter_write(&pnode->tn_nlock); 18436bb23f1Svisa VOP_UNLOCK(dvp); 1857013b092Sespie 1867013b092Sespie /* 1877013b092Sespie * Get a vnode of the '..' entry and re-acquire the lock. 1887013b092Sespie * Release the tn_nlock. 1897013b092Sespie */ 1907013b092Sespie error = tmpfs_vnode_get(dvp->v_mount, pnode, vpp); 1916e880534Svisa vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); 1927013b092Sespie goto out; 1937013b092Sespie 1947013b092Sespie } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 1957013b092Sespie /* 1967013b092Sespie * Lookup of "." case. 1977013b092Sespie */ 1987013b092Sespie if (lastcn && cnp->cn_nameiop == RENAME) { 1997013b092Sespie error = EISDIR; 2007013b092Sespie goto out; 2017013b092Sespie } 2027013b092Sespie vref(dvp); 2037013b092Sespie *vpp = dvp; 2047013b092Sespie error = 0; 2057013b092Sespie goto done; 2067013b092Sespie } 2077013b092Sespie 2087013b092Sespie /* 2097013b092Sespie * Other lookup cases: perform directory scan. 2107013b092Sespie */ 2117013b092Sespie de = tmpfs_dir_lookup(dnode, cnp); 21257331246Sespie if (de == NULL) { 2137013b092Sespie /* 2147013b092Sespie * The entry was not found in the directory. This is valid 2157013b092Sespie * if we are creating or renaming an entry and are working 2167013b092Sespie * on the last component of the path name. 2177013b092Sespie */ 2187013b092Sespie if (lastcn && (cnp->cn_nameiop == CREATE || 2197013b092Sespie cnp->cn_nameiop == RENAME)) { 2207013b092Sespie error = VOP_ACCESS(dvp, VWRITE, cred, curproc); 2217013b092Sespie if (error) { 2227013b092Sespie goto out; 2237013b092Sespie } 2247013b092Sespie /* 2257013b092Sespie * We are creating an entry in the file system, so 2267013b092Sespie * save its name for further use by tmpfs_create(). 2277013b092Sespie */ 2287013b092Sespie cnp->cn_flags |= SAVENAME; 2297013b092Sespie error = EJUSTRETURN; 2307013b092Sespie } else { 2317013b092Sespie error = ENOENT; 2327013b092Sespie } 2337013b092Sespie goto done; 2347013b092Sespie } 2357013b092Sespie 2367013b092Sespie tnode = de->td_node; 2377013b092Sespie 2387013b092Sespie /* 2397013b092Sespie * If it is not the last path component and found a non-directory 2407013b092Sespie * or non-link entry (which may itself be pointing to a directory), 2417013b092Sespie * raise an error. 2427013b092Sespie */ 2437013b092Sespie if (!lastcn && tnode->tn_type != VDIR && tnode->tn_type != VLNK) { 2447013b092Sespie error = ENOTDIR; 2457013b092Sespie goto out; 2467013b092Sespie } 2477013b092Sespie 2487013b092Sespie /* Check the permissions. */ 2497013b092Sespie if (lastcn && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { 2507013b092Sespie error = VOP_ACCESS(dvp, VWRITE, cred, curproc); 2517013b092Sespie if (error) 2527013b092Sespie goto out; 2537013b092Sespie 2547013b092Sespie /* 2557013b092Sespie * If not root and directory is sticky, check for permission 2567013b092Sespie * on directory or on file. This implements append-only 2577013b092Sespie * directories. 2587013b092Sespie */ 2597013b092Sespie if ((dnode->tn_mode & S_ISTXT) != 0) { 2607013b092Sespie if (cred->cr_uid != 0 && 2617013b092Sespie cred->cr_uid != dnode->tn_uid && 2627013b092Sespie cred->cr_uid != tnode->tn_uid) { 2637013b092Sespie error = EPERM; 2647013b092Sespie goto out; 2657013b092Sespie } 2667013b092Sespie } 2677013b092Sespie 2687013b092Sespie /* 2697013b092Sespie * XXX pedro: We might need cn_nameptr later in tmpfs_remove() 2707013b092Sespie * or tmpfs_rmdir() for a tmpfs_dir_lookup(). We should really 2717013b092Sespie * get rid of SAVENAME at some point. 2727013b092Sespie */ 2737013b092Sespie if (cnp->cn_nameiop == DELETE) 2747013b092Sespie cnp->cn_flags |= SAVENAME; 2757013b092Sespie } 2767013b092Sespie 2777013b092Sespie /* Get a vnode for the matching entry. */ 2787013b092Sespie rw_enter_write(&tnode->tn_nlock); 2797013b092Sespie error = tmpfs_vnode_get(dvp->v_mount, tnode, vpp); 2807013b092Sespie done: 2817013b092Sespie /* 2827013b092Sespie * Cache the result, unless request was for creation (as it does 2837013b092Sespie * not improve the performance). 2847013b092Sespie */ 2857013b092Sespie if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE) { 2867013b092Sespie cache_enter(dvp, *vpp, cnp); 2877013b092Sespie } 2887013b092Sespie out: 2897013b092Sespie /* 290ba6398e8Spatrick * If (1) we succeeded, (2) found a distinct vnode != .. to return and (3) 291b66b9ef8Sjsg * were either explicitly told to keep the parent locked or are in the 2927013b092Sespie * middle of a lookup, unlock the parent vnode. 2937013b092Sespie */ 2947013b092Sespie if ((error == 0 || error == EJUSTRETURN) && /* (1) */ 295ba6398e8Spatrick (*vpp != dvp || (cnp->cn_flags & ISDOTDOT)) && /* (2) */ 2967013b092Sespie (!lockparent || !lastcn)) { /* (3) */ 29736bb23f1Svisa VOP_UNLOCK(dvp); 2987013b092Sespie cnp->cn_flags |= PDIRUNLOCK; 2997013b092Sespie } else 3007013b092Sespie KASSERT(VOP_ISLOCKED(dvp)); 3017013b092Sespie 3027013b092Sespie KASSERT((*vpp && VOP_ISLOCKED(*vpp)) || error); 3037013b092Sespie 3047013b092Sespie return error; 3057013b092Sespie } 3067013b092Sespie 3077013b092Sespie int 3087013b092Sespie tmpfs_create(void *v) 3097013b092Sespie { 3107013b092Sespie struct vop_create_args /* { 3117013b092Sespie struct vnode *a_dvp; 3127013b092Sespie struct vnode **a_vpp; 3137013b092Sespie struct componentname *a_cnp; 3147013b092Sespie struct vattr *a_vap; 3157013b092Sespie } */ *ap = v; 3167013b092Sespie struct vnode *dvp = ap->a_dvp, **vpp = ap->a_vpp; 3177013b092Sespie struct componentname *cnp = ap->a_cnp; 3187013b092Sespie struct vattr *vap = ap->a_vap; 3197013b092Sespie 3207013b092Sespie KASSERT(VOP_ISLOCKED(dvp)); 3217013b092Sespie KASSERT(cnp->cn_flags & HASBUF); 3227013b092Sespie KASSERT(vap->va_type == VREG || vap->va_type == VSOCK); 3234dd4d774Svisa return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 3247013b092Sespie } 3257013b092Sespie 3267013b092Sespie int 3277013b092Sespie tmpfs_mknod(void *v) 3287013b092Sespie { 3297013b092Sespie struct vop_mknod_args /* { 3307013b092Sespie struct vnode *a_dvp; 3317013b092Sespie struct vnode **a_vpp; 3327013b092Sespie struct componentname *a_cnp; 3337013b092Sespie struct vattr *a_vap; 3347013b092Sespie } */ *ap = v; 3357013b092Sespie struct vnode *dvp = ap->a_dvp, **vpp = ap->a_vpp; 3367013b092Sespie struct componentname *cnp = ap->a_cnp; 3377013b092Sespie struct vattr *vap = ap->a_vap; 3387013b092Sespie enum vtype vt = vap->va_type; 3397013b092Sespie int error; 3407013b092Sespie 3414dd4d774Svisa if (vt != VBLK && vt != VCHR && vt != VFIFO) 3427013b092Sespie return EINVAL; 3437013b092Sespie 3447013b092Sespie error = tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 3457013b092Sespie 3464dd4d774Svisa if (error == 0) 3477013b092Sespie vput(*vpp); 3487013b092Sespie 3494dd4d774Svisa return error; 3507013b092Sespie } 3517013b092Sespie 3527013b092Sespie int 3537013b092Sespie tmpfs_open(void *v) 3547013b092Sespie { 3557013b092Sespie struct vop_open_args /* { 3567013b092Sespie struct vnode *a_vp; 3577013b092Sespie int a_mode; 3587013b092Sespie kauth_cred_t a_cred; 3597013b092Sespie } */ *ap = v; 3607013b092Sespie struct vnode *vp = ap->a_vp; 3617013b092Sespie mode_t mode = ap->a_mode; 3627013b092Sespie tmpfs_node_t *node; 3637013b092Sespie 3647013b092Sespie KASSERT(VOP_ISLOCKED(vp)); 3657013b092Sespie 3667013b092Sespie node = VP_TO_TMPFS_NODE(vp); 3677013b092Sespie if (node->tn_links < 1) { 3687013b092Sespie /* 3697013b092Sespie * The file is still active, but all its names have been 3707013b092Sespie * removed (e.g. by a "rmdir $(pwd)"). It cannot be opened 3717013b092Sespie * any more, as it is about to be destroyed. 3727013b092Sespie */ 3737013b092Sespie return ENOENT; 3747013b092Sespie } 3757013b092Sespie 3767013b092Sespie /* If the file is marked append-only, deny write requests. */ 3777013b092Sespie if ((node->tn_flags & APPEND) != 0 && 3787013b092Sespie (mode & (FWRITE | O_APPEND)) == FWRITE) { 3797013b092Sespie return EPERM; 3807013b092Sespie } 3817013b092Sespie return 0; 3827013b092Sespie } 3837013b092Sespie 3847013b092Sespie int 3857013b092Sespie tmpfs_close(void *v) 3867013b092Sespie { 387a046f53bSreyk #ifdef DIAGNOSTIC 3887013b092Sespie struct vop_close_args /* { 3897013b092Sespie struct vnode *a_vp; 3907013b092Sespie int a_fflag; 3917013b092Sespie kauth_cred_t a_cred; 3927013b092Sespie } */ *ap = v; 3937013b092Sespie struct vnode *vp = ap->a_vp; 3947013b092Sespie 3957013b092Sespie KASSERT(VOP_ISLOCKED(vp)); 396a046f53bSreyk #endif 3977013b092Sespie return 0; 3987013b092Sespie } 3997013b092Sespie 4007013b092Sespie int 4017013b092Sespie tmpfs_access(void *v) 4027013b092Sespie { 4037013b092Sespie struct vop_access_args /* { 4047013b092Sespie struct vnode *a_vp; 4057013b092Sespie int a_mode; 4067013b092Sespie kauth_cred_t a_cred; 4077013b092Sespie } */ *ap = v; 4087013b092Sespie struct vnode *vp = ap->a_vp; 4097013b092Sespie mode_t mode = ap->a_mode; 4107013b092Sespie tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); 4117013b092Sespie const int writing = (mode & VWRITE) != 0; 4127013b092Sespie 4137013b092Sespie KASSERT(VOP_ISLOCKED(vp)); 4147013b092Sespie 4157013b092Sespie /* Possible? */ 4167013b092Sespie switch (vp->v_type) { 4177013b092Sespie case VDIR: 4187013b092Sespie case VLNK: 4197013b092Sespie case VREG: 4207013b092Sespie if (writing && (vp->v_mount->mnt_flag & MNT_RDONLY) != 0) { 4217013b092Sespie return EROFS; 4227013b092Sespie } 4237013b092Sespie break; 4247013b092Sespie case VBLK: 4257013b092Sespie case VCHR: 4267013b092Sespie case VSOCK: 4277013b092Sespie case VFIFO: 4287013b092Sespie break; 4297013b092Sespie default: 4307013b092Sespie return EINVAL; 4317013b092Sespie } 4327013b092Sespie if (writing && (node->tn_flags & IMMUTABLE) != 0) { 4337013b092Sespie return EPERM; 4347013b092Sespie } 4357013b092Sespie 4367013b092Sespie return (vaccess(vp->v_type, node->tn_mode, node->tn_uid, node->tn_gid, 4377013b092Sespie mode, ap->a_cred)); 4387013b092Sespie } 4397013b092Sespie 4407013b092Sespie int 4417013b092Sespie tmpfs_getattr(void *v) 4427013b092Sespie { 4437013b092Sespie struct vop_getattr_args /* { 4447013b092Sespie struct vnode *a_vp; 4457013b092Sespie struct vattr *a_vap; 4467013b092Sespie kauth_cred_t a_cred; 4477013b092Sespie } */ *ap = v; 4487013b092Sespie struct vnode *vp = ap->a_vp; 4497013b092Sespie struct vattr *vap = ap->a_vap; 4507013b092Sespie tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); 4517013b092Sespie 4527013b092Sespie vattr_null(vap); 4537013b092Sespie 4547013b092Sespie vap->va_type = vp->v_type; 4557013b092Sespie vap->va_mode = node->tn_mode; 4567013b092Sespie vap->va_nlink = node->tn_links; 4577013b092Sespie vap->va_uid = node->tn_uid; 4587013b092Sespie vap->va_gid = node->tn_gid; 459ccc5513aSespie vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; 4607013b092Sespie vap->va_fileid = node->tn_id; 4617013b092Sespie vap->va_size = node->tn_size; 4627013b092Sespie vap->va_blocksize = PAGE_SIZE; 4637013b092Sespie vap->va_atime = node->tn_atime; 4647013b092Sespie vap->va_mtime = node->tn_mtime; 4657013b092Sespie vap->va_ctime = node->tn_ctime; 4667013b092Sespie /* vap->va_birthtime = node->tn_birthtime; */ 4677013b092Sespie vap->va_gen = TMPFS_NODE_GEN(node); 4687013b092Sespie vap->va_flags = node->tn_flags; 4697013b092Sespie vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ? 4707013b092Sespie node->tn_spec.tn_dev.tn_rdev : VNOVAL; 4717013b092Sespie vap->va_bytes = round_page(node->tn_size); 4727013b092Sespie vap->va_filerev = VNOVAL; 4737013b092Sespie vap->va_vaflags = 0; 4747013b092Sespie vap->va_spare = VNOVAL; /* XXX */ 4757013b092Sespie 4767013b092Sespie return 0; 4777013b092Sespie } 4787013b092Sespie 4794707cbe3Sguenther #define GOODTIME(tv) ((tv)->tv_nsec != VNOVAL) 4807013b092Sespie /* XXX Should this operation be atomic? I think it should, but code in 4817013b092Sespie * XXX other places (e.g., ufs) doesn't seem to be... */ 4827013b092Sespie int 4837013b092Sespie tmpfs_setattr(void *v) 4847013b092Sespie { 4857013b092Sespie struct vop_setattr_args /* { 4867013b092Sespie struct vnode *a_vp; 4877013b092Sespie struct vattr *a_vap; 4887013b092Sespie kauth_cred_t a_cred; 4897013b092Sespie } */ *ap = v; 4907013b092Sespie struct vnode *vp = ap->a_vp; 4917013b092Sespie struct vattr *vap = ap->a_vap; 4927013b092Sespie struct ucred *cred = ap->a_cred; 4937013b092Sespie struct proc *p = curproc; 4947013b092Sespie int error = 0; 4957013b092Sespie 4967013b092Sespie KASSERT(VOP_ISLOCKED(vp)); 4977013b092Sespie 4987013b092Sespie /* Abort if any unsettable attribute is given. */ 4997013b092Sespie if (vap->va_type != VNON || vap->va_nlink != VNOVAL || 5007013b092Sespie vap->va_fsid != VNOVAL || vap->va_fileid != VNOVAL || 5017013b092Sespie vap->va_blocksize != VNOVAL || GOODTIME(&vap->va_ctime) || 5027013b092Sespie vap->va_gen != VNOVAL || vap->va_rdev != VNOVAL || 5037013b092Sespie vap->va_bytes != VNOVAL) { 5047013b092Sespie return EINVAL; 5057013b092Sespie } 5067013b092Sespie if (error == 0 && (vap->va_flags != VNOVAL)) 5077013b092Sespie error = tmpfs_chflags(vp, vap->va_flags, cred, p); 5087013b092Sespie 5097013b092Sespie if (error == 0 && (vap->va_size != VNOVAL)) 5107013b092Sespie error = tmpfs_chsize(vp, vap->va_size, cred, p); 5117013b092Sespie 5127013b092Sespie if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL)) 5137013b092Sespie error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, p); 5147013b092Sespie 5157013b092Sespie if (error == 0 && (vap->va_mode != VNOVAL)) 5167013b092Sespie error = tmpfs_chmod(vp, vap->va_mode, cred, p); 5177013b092Sespie 5184707cbe3Sguenther if (error == 0 && ((vap->va_vaflags & VA_UTIMES_CHANGE) 5194707cbe3Sguenther || GOODTIME(&vap->va_atime) 5204707cbe3Sguenther || GOODTIME(&vap->va_mtime))) 5217013b092Sespie error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime, 5227013b092Sespie vap->va_vaflags, cred, p); 5234707cbe3Sguenther 5247013b092Sespie return error; 5257013b092Sespie } 5267013b092Sespie 5277013b092Sespie int 5287013b092Sespie tmpfs_read(void *v) 5297013b092Sespie { 5307013b092Sespie struct vop_read_args /* { 5317013b092Sespie struct vnode *a_vp; 5327013b092Sespie struct uio *a_uio; 5337013b092Sespie int a_ioflag; 5347013b092Sespie struct ucred *a_cred; 5357013b092Sespie } */ *ap = v; 5367013b092Sespie struct vnode *vp = ap->a_vp; 5377013b092Sespie struct uio *uio = ap->a_uio; 5387013b092Sespie /* const int ioflag = ap->a_ioflag; */ 5397013b092Sespie tmpfs_node_t *node; 5407013b092Sespie int error; 5417013b092Sespie 5427013b092Sespie KASSERT(VOP_ISLOCKED(vp)); 5437013b092Sespie 5447013b092Sespie if (vp->v_type != VREG) { 5457013b092Sespie return EISDIR; 5467013b092Sespie } 5477013b092Sespie if (uio->uio_offset < 0) { 5487013b092Sespie return EINVAL; 5497013b092Sespie } 5506a693b5fSnatano if (uio->uio_resid == 0) 5516a693b5fSnatano return 0; 5527013b092Sespie 5537013b092Sespie node = VP_TO_TMPFS_NODE(vp); 5547013b092Sespie error = 0; 5557013b092Sespie 5567013b092Sespie while (error == 0 && uio->uio_resid > 0) { 5577013b092Sespie vsize_t len; 5587013b092Sespie 5597013b092Sespie if (node->tn_size <= uio->uio_offset) { 5607013b092Sespie break; 5617013b092Sespie } 5627013b092Sespie len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid); 5637013b092Sespie if (len == 0) { 5647013b092Sespie break; 5657013b092Sespie } 5667013b092Sespie error = tmpfs_uiomove(node, uio, len); 5677013b092Sespie } 5687013b092Sespie 5696a693b5fSnatano if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) 5706a693b5fSnatano tmpfs_update(node, TMPFS_NODE_ACCESSED); 5716a693b5fSnatano 5727013b092Sespie return error; 5737013b092Sespie } 5747013b092Sespie 5757013b092Sespie int 5767013b092Sespie tmpfs_write(void *v) 5777013b092Sespie { 5787013b092Sespie struct vop_write_args /* { 5797013b092Sespie struct vnode *a_vp; 5807013b092Sespie struct uio *a_uio; 5817013b092Sespie int a_ioflag; 5827013b092Sespie kauth_cred_t a_cred; 5837013b092Sespie } */ *ap = v; 5847013b092Sespie struct vnode *vp = ap->a_vp; 5857013b092Sespie struct uio *uio = ap->a_uio; 5867013b092Sespie const int ioflag = ap->a_ioflag; 5877013b092Sespie tmpfs_node_t *node; 5887013b092Sespie off_t oldsize; 589081a143fSguenther ssize_t overrun; 5907013b092Sespie int extended; 5917013b092Sespie int error; 5927013b092Sespie 5937013b092Sespie KASSERT(VOP_ISLOCKED(vp)); 5947013b092Sespie 5957013b092Sespie node = VP_TO_TMPFS_NODE(vp); 5967013b092Sespie oldsize = node->tn_size; 5977013b092Sespie 598674fe79dSespie if (vp->v_type != VREG) 599674fe79dSespie return (EINVAL); 600674fe79dSespie 601674fe79dSespie if (uio->uio_resid == 0) 602674fe79dSespie return (0); 603674fe79dSespie 6047013b092Sespie if (ioflag & IO_APPEND) { 6057013b092Sespie uio->uio_offset = node->tn_size; 6067013b092Sespie } 6077013b092Sespie 608081a143fSguenther if (uio->uio_offset < 0 || 609081a143fSguenther (u_int64_t)uio->uio_offset + uio->uio_resid > LLONG_MAX) 610081a143fSguenther return (EFBIG); 611081a143fSguenther 612081a143fSguenther /* do the filesize rlimit check */ 613081a143fSguenther if ((error = vn_fsizechk(vp, uio, ioflag, &overrun))) 614081a143fSguenther return (error); 615081a143fSguenther 6167013b092Sespie extended = uio->uio_offset + uio->uio_resid > node->tn_size; 6177013b092Sespie if (extended) { 6187013b092Sespie error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid); 6197013b092Sespie if (error) 6207013b092Sespie goto out; 6217013b092Sespie } 6227013b092Sespie 6237013b092Sespie error = 0; 6247013b092Sespie while (error == 0 && uio->uio_resid > 0) { 6257013b092Sespie vsize_t len; 6269720dc33Spatrick uvm_vnp_uncache(vp); 6277013b092Sespie len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid); 6287013b092Sespie if (len == 0) { 6297013b092Sespie break; 6307013b092Sespie } 6317013b092Sespie error = tmpfs_uiomove(node, uio, len); 6327013b092Sespie } 6337013b092Sespie if (error) { 6347013b092Sespie (void)tmpfs_reg_resize(vp, oldsize); 6357013b092Sespie } 6367013b092Sespie 6376a693b5fSnatano tmpfs_update(node, TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED); 6387013b092Sespie if (extended) 6397013b092Sespie VN_KNOTE(vp, NOTE_WRITE | NOTE_EXTEND); 6407013b092Sespie else 6417013b092Sespie VN_KNOTE(vp, NOTE_WRITE); 6427013b092Sespie out: 6437013b092Sespie if (error) { 6447013b092Sespie KASSERT(oldsize == node->tn_size); 6457013b092Sespie } else { 6467013b092Sespie KASSERT(uio->uio_resid == 0); 647081a143fSguenther 648081a143fSguenther /* correct the result for writes clamped by vn_fsizechk() */ 649081a143fSguenther uio->uio_resid += overrun; 650081a143fSguenther 6517013b092Sespie } 6527013b092Sespie return error; 6537013b092Sespie } 6547013b092Sespie 6557013b092Sespie int 6567013b092Sespie tmpfs_fsync(void *v) 6577013b092Sespie { 658a046f53bSreyk #ifdef DIAGNOSTIC 6597013b092Sespie struct vop_fsync_args /* { 6607013b092Sespie struct vnode *a_vp; 6617013b092Sespie struct ucred *a_cred; 6627013b092Sespie int a_flags; 6637013b092Sespie off_t a_offlo; 6647013b092Sespie off_t a_offhi; 6657013b092Sespie struct lwp *a_l; 6667013b092Sespie } */ *ap = v; 6677013b092Sespie struct vnode *vp = ap->a_vp; 6687013b092Sespie 6697013b092Sespie /* Nothing to do. Just update. */ 6707013b092Sespie KASSERT(VOP_ISLOCKED(vp)); 671a046f53bSreyk #endif 6727013b092Sespie return 0; 6737013b092Sespie } 6747013b092Sespie 6757013b092Sespie /* 6767013b092Sespie * tmpfs_remove: unlink a file. 6777013b092Sespie * 6787013b092Sespie * => Both directory (dvp) and file (vp) are locked. 6797013b092Sespie * => We unlock and drop the reference on both. 6807013b092Sespie */ 6817013b092Sespie int 6827013b092Sespie tmpfs_remove(void *v) 6837013b092Sespie { 6847013b092Sespie struct vop_remove_args /* { 6857013b092Sespie struct vnode *a_dvp; 6867013b092Sespie struct vnode *a_vp; 6877013b092Sespie struct componentname *a_cnp; 6887013b092Sespie } */ *ap = v; 6897013b092Sespie struct vnode *dvp = ap->a_dvp, *vp = ap->a_vp; 6907013b092Sespie struct componentname *cnp = ap->a_cnp; 69146905cb9Sespie tmpfs_node_t *dnode, *node; 6927013b092Sespie tmpfs_dirent_t *de; 6937013b092Sespie int error; 6947013b092Sespie 6957013b092Sespie KASSERT(cnp->cn_flags & HASBUF); 6967013b092Sespie 6977013b092Sespie if (vp->v_type == VDIR) { 6987013b092Sespie error = EPERM; 6997013b092Sespie goto out; 7007013b092Sespie } 70146905cb9Sespie 70246905cb9Sespie dnode = VP_TO_TMPFS_NODE(dvp); 7037013b092Sespie node = VP_TO_TMPFS_NODE(vp); 7047013b092Sespie 7057013b092Sespie /* Files marked as immutable or append-only cannot be deleted. */ 7067013b092Sespie if (node->tn_flags & (IMMUTABLE | APPEND)) { 7077013b092Sespie error = EPERM; 7087013b092Sespie goto out; 7097013b092Sespie } 7107013b092Sespie 71146905cb9Sespie /* 71246905cb9Sespie * Likewise, files residing on directories marked as append-only cannot 71346905cb9Sespie * be deleted. 71446905cb9Sespie */ 71546905cb9Sespie if (dnode->tn_flags & APPEND) { 71646905cb9Sespie error = EPERM; 71746905cb9Sespie goto out; 71846905cb9Sespie } 71946905cb9Sespie 7207013b092Sespie /* Lookup the directory entry (check the cached hint first). */ 7217013b092Sespie de = tmpfs_dir_cached(node); 7227013b092Sespie if (de == NULL) { 7237013b092Sespie de = tmpfs_dir_lookup(dnode, cnp); 7247013b092Sespie } 7257013b092Sespie 7267013b092Sespie KASSERT(de && de->td_node == node); 7277013b092Sespie 7287013b092Sespie /* 7297013b092Sespie * Remove the entry from the directory (drops the link count) and 73057331246Sespie * destroy it. 7317013b092Sespie * Note: the inode referred by it will not be destroyed 7327013b092Sespie * until the vnode is reclaimed/recycled. 7337013b092Sespie */ 73457331246Sespie tmpfs_dir_detach(dnode, de); 7357013b092Sespie tmpfs_free_dirent(VFS_TO_TMPFS(vp->v_mount), de); 73646905cb9Sespie if (node->tn_links > 0) { 73746905cb9Sespie /* We removed a hard link. */ 73846905cb9Sespie tmpfs_update(node, TMPFS_NODE_CHANGED); 73946905cb9Sespie } 7407013b092Sespie error = 0; 7417013b092Sespie out: 7427013b092Sespie pool_put(&namei_pool, cnp->cn_pnbuf); 7437013b092Sespie return error; 7447013b092Sespie } 7457013b092Sespie 7467013b092Sespie /* 7477013b092Sespie * tmpfs_link: create a hard link. 7487013b092Sespie */ 7497013b092Sespie int 7507013b092Sespie tmpfs_link(void *v) 7517013b092Sespie { 7527013b092Sespie struct vop_link_args /* { 7537013b092Sespie struct vnode *a_dvp; 7547013b092Sespie struct vnode *a_vp; 7557013b092Sespie struct componentname *a_cnp; 7567013b092Sespie } */ *ap = v; 7577013b092Sespie struct vnode *dvp = ap->a_dvp; 7587013b092Sespie struct vnode *vp = ap->a_vp; 7597013b092Sespie struct componentname *cnp = ap->a_cnp; 7607013b092Sespie tmpfs_node_t *dnode, *node; 7617013b092Sespie tmpfs_dirent_t *de; 7627013b092Sespie int error; 7637013b092Sespie 7647013b092Sespie KASSERT(VOP_ISLOCKED(dvp)); 7654662f565Skettenis KASSERT(dvp != vp); 7664662f565Skettenis 7677013b092Sespie dnode = VP_TO_TMPFS_DIR(dvp); 7687013b092Sespie node = VP_TO_TMPFS_NODE(vp); 7697013b092Sespie 7706e880534Svisa vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 7717013b092Sespie 7727013b092Sespie /* Check for maximum number of links limit. */ 7737013b092Sespie if (node->tn_links == LINK_MAX) { 7747013b092Sespie error = EMLINK; 7757013b092Sespie goto out; 7767013b092Sespie } 7777013b092Sespie KASSERT(node->tn_links < LINK_MAX); 7787013b092Sespie 7797013b092Sespie /* We cannot create links of files marked immutable or append-only. */ 7807013b092Sespie if (node->tn_flags & (IMMUTABLE | APPEND)) { 7817013b092Sespie error = EPERM; 7827013b092Sespie goto out; 7837013b092Sespie } 7847013b092Sespie 78557331246Sespie if (TMPFS_DIRSEQ_FULL(dnode)) { 78657331246Sespie error = ENOSPC; 78757331246Sespie goto out; 78857331246Sespie } 78957331246Sespie 7907013b092Sespie /* Allocate a new directory entry to represent the inode. */ 7917013b092Sespie error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), 7927013b092Sespie cnp->cn_nameptr, cnp->cn_namelen, &de); 7937013b092Sespie if (error) { 7947013b092Sespie goto out; 7957013b092Sespie } 7967013b092Sespie 7977013b092Sespie /* 7987013b092Sespie * Insert the entry into the directory. 7997013b092Sespie * It will increase the inode link count. 8007013b092Sespie */ 80157331246Sespie tmpfs_dir_attach(dnode, de, node); 8027013b092Sespie 8037013b092Sespie /* Update the timestamps and trigger the event. */ 8047013b092Sespie if (node->tn_vnode) { 8057013b092Sespie VN_KNOTE(node->tn_vnode, NOTE_LINK); 8067013b092Sespie } 8077013b092Sespie tmpfs_update(node, TMPFS_NODE_CHANGED); 8087013b092Sespie error = 0; 8097013b092Sespie out: 8109651a1b7Sespie pool_put(&namei_pool, cnp->cn_pnbuf); 81136bb23f1Svisa VOP_UNLOCK(vp); 8127013b092Sespie vput(dvp); 8137013b092Sespie return error; 8147013b092Sespie } 8157013b092Sespie 8167013b092Sespie int 8177013b092Sespie tmpfs_mkdir(void *v) 8187013b092Sespie { 8197013b092Sespie struct vop_mkdir_args /* { 8207013b092Sespie struct vnode *a_dvp; 8217013b092Sespie struct vnode **a_vpp; 8227013b092Sespie struct componentname *a_cnp; 8237013b092Sespie struct vattr *a_vap; 8247013b092Sespie } */ *ap = v; 8257013b092Sespie struct vnode *dvp = ap->a_dvp; 8267013b092Sespie struct vnode **vpp = ap->a_vpp; 8277013b092Sespie struct componentname *cnp = ap->a_cnp; 8287013b092Sespie struct vattr *vap = ap->a_vap; 8299d1c6d70Svisa int error; 8307013b092Sespie 8317013b092Sespie KASSERT(vap->va_type == VDIR); 8329d1c6d70Svisa error = tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 8339d1c6d70Svisa vput(dvp); 8349d1c6d70Svisa return error; 8357013b092Sespie } 8367013b092Sespie 8377013b092Sespie int 8387013b092Sespie tmpfs_rmdir(void *v) 8397013b092Sespie { 8407013b092Sespie struct vop_rmdir_args /* { 8417013b092Sespie struct vnode *a_dvp; 8427013b092Sespie struct vnode *a_vp; 8437013b092Sespie struct componentname *a_cnp; 8447013b092Sespie } */ *ap = v; 8457013b092Sespie struct vnode *dvp = ap->a_dvp; 8467013b092Sespie struct vnode *vp = ap->a_vp; 8477013b092Sespie struct componentname *cnp = ap->a_cnp; 8487013b092Sespie tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount); 8497013b092Sespie tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp); 8507013b092Sespie tmpfs_node_t *node = VP_TO_TMPFS_DIR(vp); 8517013b092Sespie tmpfs_dirent_t *de; 8527013b092Sespie int error = 0; 8537013b092Sespie 8547013b092Sespie KASSERT(VOP_ISLOCKED(dvp)); 8557013b092Sespie KASSERT(VOP_ISLOCKED(vp)); 8567013b092Sespie KASSERT(cnp->cn_flags & HASBUF); 8577013b092Sespie 85846905cb9Sespie if (cnp->cn_namelen == 2 && cnp->cn_nameptr[0] == '.' && 85946905cb9Sespie cnp->cn_nameptr[1] == '.') { 86046905cb9Sespie error = ENOTEMPTY; 86146905cb9Sespie goto out; 86246905cb9Sespie } 86346905cb9Sespie 86446905cb9Sespie KASSERT(node->tn_spec.tn_dir.tn_parent == dnode); 86546905cb9Sespie 8667013b092Sespie /* 86757331246Sespie * Directories with more than two entries ('.' and '..') cannot be 86857331246Sespie * removed. 8697013b092Sespie */ 8707013b092Sespie if (node->tn_size > 0) { 8717013b092Sespie KASSERT(error == 0); 8727013b092Sespie TAILQ_FOREACH(de, &node->tn_spec.tn_dir.tn_dir, td_entries) { 8737013b092Sespie error = ENOTEMPTY; 8747013b092Sespie break; 8757013b092Sespie } 8767013b092Sespie if (error) 8777013b092Sespie goto out; 8787013b092Sespie } 8797013b092Sespie 8807013b092Sespie /* Lookup the directory entry (check the cached hint first). */ 8817013b092Sespie de = tmpfs_dir_cached(node); 8827013b092Sespie if (de == NULL) 8837013b092Sespie de = tmpfs_dir_lookup(dnode, cnp); 8847013b092Sespie 8857013b092Sespie KASSERT(de && de->td_node == node); 8867013b092Sespie 8877013b092Sespie /* Check flags to see if we are allowed to remove the directory. */ 8887013b092Sespie if (dnode->tn_flags & APPEND || node->tn_flags & (IMMUTABLE | APPEND)) { 8897013b092Sespie error = EPERM; 8907013b092Sespie goto out; 8917013b092Sespie } 8927013b092Sespie 8937013b092Sespie /* Decrement the link count for the virtual '.' entry. */ 8947013b092Sespie node->tn_links--; 8957013b092Sespie tmpfs_update(node, TMPFS_NODE_STATUSALL); 8967013b092Sespie 8977013b092Sespie /* Detach the directory entry from the directory. */ 89857331246Sespie tmpfs_dir_detach(dnode, de); 8997013b092Sespie 9007013b092Sespie /* Purge the cache for parent. */ 9017013b092Sespie cache_purge(dvp); 9027013b092Sespie 9037013b092Sespie /* 90457331246Sespie * Destroy the directory entry. 9057013b092Sespie * Note: the inode referred by it will not be destroyed 9067013b092Sespie * until the vnode is reclaimed. 9077013b092Sespie */ 9087013b092Sespie tmpfs_free_dirent(tmp, de); 90957331246Sespie KASSERT(TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir) == NULL); 9107013b092Sespie 9117013b092Sespie KASSERT(node->tn_links == 0); 9127013b092Sespie out: 9137013b092Sespie pool_put(&namei_pool, cnp->cn_pnbuf); 9147013b092Sespie /* Release the nodes. */ 9157013b092Sespie vput(dvp); 9167013b092Sespie vput(vp); 9177013b092Sespie return error; 9187013b092Sespie } 9197013b092Sespie 9207013b092Sespie int 9217013b092Sespie tmpfs_symlink(void *v) 9227013b092Sespie { 9237013b092Sespie struct vop_symlink_args /* { 9247013b092Sespie struct vnode *a_dvp; 9257013b092Sespie struct vnode **a_vpp; 9267013b092Sespie struct componentname *a_cnp; 9277013b092Sespie struct vattr *a_vap; 9287013b092Sespie char *a_target; 9297013b092Sespie } */ *ap = v; 9307013b092Sespie struct vnode *dvp = ap->a_dvp; 9317013b092Sespie struct vnode **vpp = ap->a_vpp; 9327013b092Sespie struct componentname *cnp = ap->a_cnp; 9337013b092Sespie struct vattr *vap = ap->a_vap; 9347013b092Sespie char *target = ap->a_target; 9357013b092Sespie int error; 9367013b092Sespie 9377013b092Sespie KASSERT(vap->va_type == 0); 9387013b092Sespie vap->va_type = VLNK; 9397013b092Sespie 9407013b092Sespie error = tmpfs_alloc_file(dvp, vpp, vap, cnp, target); 9419d1c6d70Svisa vput(dvp); 9427013b092Sespie if (error == 0) 9437013b092Sespie vput(*vpp); 9447013b092Sespie 94546905cb9Sespie return error; 9467013b092Sespie } 9477013b092Sespie 9487013b092Sespie int 9497013b092Sespie tmpfs_readdir(void *v) 9507013b092Sespie { 9517013b092Sespie struct vop_readdir_args /* { 9527013b092Sespie struct vnode *a_vp; 9537013b092Sespie struct uio *a_uio; 9547013b092Sespie kauth_cred_t a_cred; 9557013b092Sespie int *a_eofflag; 9567013b092Sespie } */ *ap = v; 9577013b092Sespie struct vnode *vp = ap->a_vp; 9587013b092Sespie struct uio *uio = ap->a_uio; 9597013b092Sespie int *eofflag = ap->a_eofflag; 9607013b092Sespie tmpfs_node_t *node; 9617013b092Sespie int error; 9627013b092Sespie 9637013b092Sespie KASSERT(VOP_ISLOCKED(vp)); 9647013b092Sespie 9657013b092Sespie /* This operation only makes sense on directory nodes. */ 9667013b092Sespie if (vp->v_type != VDIR) { 9677013b092Sespie return ENOTDIR; 9687013b092Sespie } 9697013b092Sespie node = VP_TO_TMPFS_DIR(vp); 97057331246Sespie /* 97157331246Sespie * Retrieve the directory entries, unless it is being destroyed. 97257331246Sespie */ 97357331246Sespie if (node->tn_links) { 97457331246Sespie error = tmpfs_dir_getdents(node, uio); 97557331246Sespie } else { 9767013b092Sespie error = 0; 9777013b092Sespie } 9787013b092Sespie 9797013b092Sespie if (eofflag != NULL) { 98057331246Sespie *eofflag = !error && uio->uio_offset == TMPFS_DIRSEQ_EOF; 9817013b092Sespie } 9827013b092Sespie return error; 9837013b092Sespie } 9847013b092Sespie 9857013b092Sespie int 9867013b092Sespie tmpfs_readlink(void *v) 9877013b092Sespie { 9887013b092Sespie struct vop_readlink_args /* { 9897013b092Sespie struct vnode *a_vp; 9907013b092Sespie struct uio *a_uio; 9917013b092Sespie kauth_cred_t a_cred; 9927013b092Sespie } */ *ap = v; 9937013b092Sespie struct vnode *vp = ap->a_vp; 9947013b092Sespie struct uio *uio = ap->a_uio; 9957013b092Sespie tmpfs_node_t *node; 9967013b092Sespie int error; 9977013b092Sespie 9987013b092Sespie KASSERT(VOP_ISLOCKED(vp)); 9997013b092Sespie KASSERT(uio->uio_offset == 0); 10007013b092Sespie KASSERT(vp->v_type == VLNK); 10017013b092Sespie 10027013b092Sespie node = VP_TO_TMPFS_NODE(vp); 1003653068caSstefan error = uiomove(node->tn_spec.tn_lnk.tn_link, 1004653068caSstefan MIN((size_t)node->tn_size, uio->uio_resid), uio); 10056a693b5fSnatano 10066a693b5fSnatano if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) 10077013b092Sespie tmpfs_update(node, TMPFS_NODE_ACCESSED); 10087013b092Sespie 10097013b092Sespie return error; 10107013b092Sespie } 10117013b092Sespie 10127013b092Sespie int 10137013b092Sespie tmpfs_inactive(void *v) 10147013b092Sespie { 10157013b092Sespie struct vop_inactive_args /* { 10167013b092Sespie struct vnode *a_vp; 10177013b092Sespie int *a_recycle; 10187013b092Sespie } */ *ap = v; 10197013b092Sespie struct vnode *vp = ap->a_vp; 10207013b092Sespie tmpfs_node_t *node; 10217013b092Sespie 10227013b092Sespie KASSERT(VOP_ISLOCKED(vp)); 10237013b092Sespie 10247013b092Sespie node = VP_TO_TMPFS_NODE(vp); 10257013b092Sespie 10267013b092Sespie if (vp->v_type == VREG && tmpfs_uio_cached(node)) 10277013b092Sespie tmpfs_uio_uncache(node); 10287013b092Sespie 102936bb23f1Svisa VOP_UNLOCK(vp); 10307013b092Sespie 10317013b092Sespie /* 10327013b092Sespie * If we are done with the node, reclaim it so that it can be reused 10337013b092Sespie * immediately. 10347013b092Sespie */ 10357013b092Sespie if (node->tn_links == 0) 10367013b092Sespie vrecycle(vp, curproc); 10377013b092Sespie 10387013b092Sespie return 0; 10397013b092Sespie } 10407013b092Sespie 10417013b092Sespie int 10427013b092Sespie tmpfs_reclaim(void *v) 10437013b092Sespie { 10447013b092Sespie struct vop_reclaim_args /* { 10457013b092Sespie struct vnode *a_vp; 10467013b092Sespie } */ *ap = v; 10477013b092Sespie struct vnode *vp = ap->a_vp; 10487013b092Sespie tmpfs_mount_t *tmp = VFS_TO_TMPFS(vp->v_mount); 10497013b092Sespie tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); 10507013b092Sespie int racing; 10517013b092Sespie 10527013b092Sespie /* Disassociate inode from vnode. */ 10537013b092Sespie rw_enter_write(&node->tn_nlock); 10547013b092Sespie node->tn_vnode = NULL; 10557013b092Sespie vp->v_data = NULL; 10567013b092Sespie /* Check if tmpfs_vnode_get() is racing with us. */ 10577013b092Sespie racing = TMPFS_NODE_RECLAIMING(node); 10587013b092Sespie rw_exit_write(&node->tn_nlock); 10597013b092Sespie 106078b494b9Sgerhard cache_purge(vp); 106178b494b9Sgerhard 10627013b092Sespie /* 10637013b092Sespie * If inode is not referenced, i.e. no links, then destroy it. 10647013b092Sespie * Note: if racing - inode is about to get a new vnode, leave it. 10657013b092Sespie */ 10667013b092Sespie if (node->tn_links == 0 && !racing) { 10677013b092Sespie tmpfs_free_node(tmp, node); 10687013b092Sespie } 10697013b092Sespie return 0; 10707013b092Sespie } 10717013b092Sespie 10727013b092Sespie int 10737013b092Sespie tmpfs_pathconf(void *v) 10747013b092Sespie { 10757013b092Sespie struct vop_pathconf_args /* { 10767013b092Sespie struct vnode *a_vp; 10777013b092Sespie int a_name; 10787013b092Sespie register_t *a_retval; 10797013b092Sespie } */ *ap = v; 10807013b092Sespie const int name = ap->a_name; 10817013b092Sespie register_t *retval = ap->a_retval; 10827013b092Sespie int error = 0; 10837013b092Sespie 10847013b092Sespie switch (name) { 10857013b092Sespie case _PC_LINK_MAX: 10867013b092Sespie *retval = LINK_MAX; 10877013b092Sespie break; 10887013b092Sespie case _PC_NAME_MAX: 10897013b092Sespie *retval = TMPFS_MAXNAMLEN; 10907013b092Sespie break; 10917013b092Sespie case _PC_CHOWN_RESTRICTED: 10927013b092Sespie *retval = 1; 10937013b092Sespie break; 10947013b092Sespie case _PC_NO_TRUNC: 10957013b092Sespie *retval = 1; 10967013b092Sespie break; 10977013b092Sespie case _PC_FILESIZEBITS: 1098bb41a875Sbrad *retval = 64; 1099bb41a875Sbrad break; 1100bb41a875Sbrad case _PC_TIMESTAMP_RESOLUTION: 1101bb41a875Sbrad *retval = 1; 11027013b092Sespie break; 11037013b092Sespie default: 11047013b092Sespie error = EINVAL; 11057013b092Sespie } 11067013b092Sespie return error; 11077013b092Sespie } 11087013b092Sespie 11097013b092Sespie int 11107013b092Sespie tmpfs_advlock(void *v) 11117013b092Sespie { 11127013b092Sespie struct vop_advlock_args /* { 11137013b092Sespie struct vnode *a_vp; 11147013b092Sespie void * a_id; 11157013b092Sespie int a_op; 11167013b092Sespie struct flock *a_fl; 11177013b092Sespie int a_flags; 11187013b092Sespie } */ *ap = v; 11197013b092Sespie struct vnode *vp = ap->a_vp; 11207013b092Sespie tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); 11217013b092Sespie 11227013b092Sespie return lf_advlock(&node->tn_lockf, node->tn_size, ap->a_id, ap->a_op, 11237013b092Sespie ap->a_fl, ap->a_flags); 11247013b092Sespie } 11257013b092Sespie 11267013b092Sespie int 11277013b092Sespie tmpfs_print(void *v) 11287013b092Sespie { 1129*5a0ec814Smiod #if defined(DEBUG) || defined(DIAGNOSTIC) || defined(VFSLCKDEBUG) 11307013b092Sespie struct vop_print_args /* { 11317013b092Sespie struct vnode *a_vp; 11327013b092Sespie } */ *ap = v; 11337013b092Sespie struct vnode *vp = ap->a_vp; 11347013b092Sespie tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); 11357013b092Sespie 11367013b092Sespie printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n" 11377013b092Sespie "\tmode 0%o, owner %d, group %d, size %lld", 11387013b092Sespie node, node->tn_flags, node->tn_links, node->tn_mode, node->tn_uid, 11397013b092Sespie node->tn_gid, node->tn_size); 11407013b092Sespie #ifdef FIFO 11417013b092Sespie if (vp->v_type == VFIFO) 11427013b092Sespie fifo_printinfo(vp); 11437013b092Sespie #endif 11447013b092Sespie printf("\n"); 1145*5a0ec814Smiod #endif 11467013b092Sespie return 0; 11477013b092Sespie } 11487013b092Sespie 11497013b092Sespie /* a null op */ 11507013b092Sespie int 11517013b092Sespie tmpfs_bwrite(void *v) 11527013b092Sespie { 11537013b092Sespie return 0; 11547013b092Sespie } 11557013b092Sespie 11567013b092Sespie int 11577013b092Sespie tmpfs_strategy(void *v) 11587013b092Sespie { 11597013b092Sespie return EOPNOTSUPP; 11607013b092Sespie } 11617013b092Sespie 11627013b092Sespie int 11637013b092Sespie tmpfs_ioctl(void *v) 11647013b092Sespie { 11657013b092Sespie return ENOTTY; 11667013b092Sespie } 11677013b092Sespie 11687013b092Sespie int 11697013b092Sespie tmpfs_lock(void *v) 11707013b092Sespie { 11717013b092Sespie struct vop_lock_args *ap = v; 11727013b092Sespie tmpfs_node_t *tnp = VP_TO_TMPFS_NODE(ap->a_vp); 11737013b092Sespie 117426b8ec94Snatano return rrw_enter(&tnp->tn_vlock, ap->a_flags & LK_RWFLAGS); 11757013b092Sespie } 11767013b092Sespie 11777013b092Sespie int 11787013b092Sespie tmpfs_unlock(void *v) 11797013b092Sespie { 11807013b092Sespie struct vop_unlock_args *ap = v; 11817013b092Sespie tmpfs_node_t *tnp = VP_TO_TMPFS_NODE(ap->a_vp); 11827013b092Sespie 118326b8ec94Snatano rrw_exit(&tnp->tn_vlock); 118426b8ec94Snatano return 0; 11857013b092Sespie } 11867013b092Sespie 11877013b092Sespie int 11887013b092Sespie tmpfs_islocked(void *v) 11897013b092Sespie { 11907013b092Sespie struct vop_islocked_args *ap = v; 11917013b092Sespie tmpfs_node_t *tnp = VP_TO_TMPFS_NODE(ap->a_vp); 11927013b092Sespie 119326b8ec94Snatano return rrw_status(&tnp->tn_vlock); 11947013b092Sespie } 11957013b092Sespie 11967013b092Sespie /* 11977013b092Sespie * tmpfs_rename: rename routine, the hairiest system call, with the 11987013b092Sespie * insane API. 11997013b092Sespie * 12007013b092Sespie * Arguments: fdvp (from-parent vnode), fvp (from-leaf), tdvp (to-parent) 12017013b092Sespie * and tvp (to-leaf), if exists (NULL if not). 12027013b092Sespie * 12037013b092Sespie * => Caller holds a reference on fdvp and fvp, they are unlocked. 12047013b092Sespie * Note: fdvp and fvp can refer to the same object (i.e. when it is root). 12057013b092Sespie * 12067013b092Sespie * => Both tdvp and tvp are referenced and locked. It is our responsibility 12077013b092Sespie * to release the references and unlock them (or destroy). 12087013b092Sespie */ 12097013b092Sespie 12107013b092Sespie /* 12117013b092Sespie * First, some forward declarations of subroutines. 12127013b092Sespie */ 12137013b092Sespie 12147013b092Sespie int tmpfs_sane_rename(struct vnode *, struct componentname *, 12157013b092Sespie struct vnode *, struct componentname *, struct ucred *, int); 12167013b092Sespie int tmpfs_rename_enter(struct mount *, struct tmpfs_mount *, 12177013b092Sespie struct ucred *, 12187013b092Sespie struct vnode *, struct tmpfs_node *, struct componentname *, 12197013b092Sespie struct tmpfs_dirent **, struct vnode **, 12207013b092Sespie struct vnode *, struct tmpfs_node *, struct componentname *, 12217013b092Sespie struct tmpfs_dirent **, struct vnode **); 12227013b092Sespie int tmpfs_rename_enter_common(struct mount *, struct tmpfs_mount *, 12237013b092Sespie struct ucred *, 12247013b092Sespie struct vnode *, struct tmpfs_node *, 12257013b092Sespie struct componentname *, struct tmpfs_dirent **, struct vnode **, 12267013b092Sespie struct componentname *, struct tmpfs_dirent **, struct vnode **); 12277013b092Sespie int tmpfs_rename_enter_separate(struct mount *, struct tmpfs_mount *, 12287013b092Sespie struct ucred *, 12297013b092Sespie struct vnode *, struct tmpfs_node *, struct componentname *, 12307013b092Sespie struct tmpfs_dirent **, struct vnode **, 12317013b092Sespie struct vnode *, struct tmpfs_node *, struct componentname *, 12327013b092Sespie struct tmpfs_dirent **, struct vnode **); 12337013b092Sespie void tmpfs_rename_exit(struct tmpfs_mount *, 12347013b092Sespie struct vnode *, struct vnode *, struct vnode *, struct vnode *); 12357013b092Sespie int tmpfs_rename_lock_directory(struct vnode *, struct tmpfs_node *); 12367013b092Sespie int tmpfs_rename_genealogy(struct tmpfs_node *, struct tmpfs_node *, 12377013b092Sespie struct tmpfs_node **); 12387013b092Sespie int tmpfs_rename_lock(struct mount *, struct ucred *, int, 12397013b092Sespie struct vnode *, struct tmpfs_node *, struct componentname *, int, 12407013b092Sespie struct tmpfs_dirent **, struct vnode **, 12417013b092Sespie struct vnode *, struct tmpfs_node *, struct componentname *, int, 12427013b092Sespie struct tmpfs_dirent **, struct vnode **); 12437013b092Sespie void tmpfs_rename_attachdetach(struct tmpfs_mount *, 12447013b092Sespie struct vnode *, struct tmpfs_dirent *, struct vnode *, 12457013b092Sespie struct vnode *, struct tmpfs_dirent *, struct vnode *); 12467013b092Sespie int tmpfs_do_remove(struct tmpfs_mount *, struct vnode *, 12477013b092Sespie struct tmpfs_node *, struct tmpfs_dirent *, struct vnode *, struct ucred *); 12487013b092Sespie int tmpfs_rename_check_possible(struct tmpfs_node *, 12497013b092Sespie struct tmpfs_node *, struct tmpfs_node *, struct tmpfs_node *); 12507013b092Sespie int tmpfs_rename_check_permitted(struct ucred *, 12517013b092Sespie struct tmpfs_node *, struct tmpfs_node *, 12527013b092Sespie struct tmpfs_node *, struct tmpfs_node *); 12537013b092Sespie int tmpfs_remove_check_possible(struct tmpfs_node *, 12547013b092Sespie struct tmpfs_node *); 12557013b092Sespie int tmpfs_remove_check_permitted(struct ucred *, 12567013b092Sespie struct tmpfs_node *, struct tmpfs_node *); 12577013b092Sespie int tmpfs_check_sticky(struct ucred *, 12587013b092Sespie struct tmpfs_node *, struct tmpfs_node *); 12597013b092Sespie void tmpfs_rename_cache_purge(struct vnode *, struct vnode *, struct vnode *, 12607013b092Sespie struct vnode *); 126146905cb9Sespie void tmpfs_rename_abort(void *); 12627013b092Sespie 12637013b092Sespie int 12647013b092Sespie tmpfs_rename(void *v) 12657013b092Sespie { 12667013b092Sespie struct vop_rename_args /* { 12677013b092Sespie struct vnode *a_fdvp; 12687013b092Sespie struct vnode *a_fvp; 12697013b092Sespie struct componentname *a_fcnp; 12707013b092Sespie struct vnode *a_tdvp; 12717013b092Sespie struct vnode *a_tvp; 12727013b092Sespie struct componentname *a_tcnp; 12737013b092Sespie } */ *ap = v; 12747013b092Sespie struct vnode *fdvp = ap->a_fdvp; 12757013b092Sespie struct vnode *fvp = ap->a_fvp; 12767013b092Sespie struct componentname *fcnp = ap->a_fcnp; 12777013b092Sespie struct vnode *tdvp = ap->a_tdvp; 12787013b092Sespie struct vnode *tvp = ap->a_tvp; 12797013b092Sespie struct componentname *tcnp = ap->a_tcnp; 12807013b092Sespie struct ucred *cred; 12817013b092Sespie int error; 12827013b092Sespie 12837013b092Sespie KASSERT(fdvp != NULL); 12847013b092Sespie KASSERT(fvp != NULL); 12857013b092Sespie KASSERT(fcnp != NULL); 12867013b092Sespie KASSERT(fcnp->cn_nameptr != NULL); 12877013b092Sespie KASSERT(tdvp != NULL); 12887013b092Sespie KASSERT(tcnp != NULL); 12897013b092Sespie KASSERT(fcnp->cn_nameptr != NULL); 12907013b092Sespie /* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */ 12917013b092Sespie /* KASSERT(VOP_ISLOCKED(fvp) != LK_EXCLUSIVE); */ 12927013b092Sespie KASSERT(fdvp->v_type == VDIR); 12937013b092Sespie KASSERT(tdvp->v_type == VDIR); 12947013b092Sespie KASSERT(fcnp->cn_flags & HASBUF); 12957013b092Sespie KASSERT(tcnp->cn_flags & HASBUF); 12967013b092Sespie 12977013b092Sespie cred = fcnp->cn_cred; 12987013b092Sespie KASSERT(tcnp->cn_cred == cred); 12997013b092Sespie 13007013b092Sespie /* 13017013b092Sespie * Check for cross-device rename. 13027cab0185Spatrick * Also don't allow renames of mount points. 13037013b092Sespie */ 13047013b092Sespie if (fvp->v_mount != tdvp->v_mount || 13057cab0185Spatrick fdvp->v_mount != fvp->v_mount || 13067013b092Sespie (tvp != NULL && (fvp->v_mount != tvp->v_mount))) { 130746905cb9Sespie tmpfs_rename_abort(v); 13087013b092Sespie return EXDEV; 13097013b092Sespie } 13107013b092Sespie 13117013b092Sespie /* 1312d86fc745Sguenther * Can't check the locks on these until we know they're on 1313d86fc745Sguenther * the same FS, as not all FS do locking the same way. 1314d86fc745Sguenther */ 1315d86fc745Sguenther KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 1316d86fc745Sguenther KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 1317d86fc745Sguenther 1318d86fc745Sguenther /* 131946905cb9Sespie * Reject renaming '.' and '..'. 132046905cb9Sespie */ 132146905cb9Sespie if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || 132246905cb9Sespie (fcnp->cn_namelen == 2 && fcnp->cn_nameptr[0] == '.' && 132346905cb9Sespie fcnp->cn_nameptr[1] == '.')) { 132446905cb9Sespie tmpfs_rename_abort(v); 132546905cb9Sespie return EINVAL; 132646905cb9Sespie } 132746905cb9Sespie 132846905cb9Sespie /* 13297013b092Sespie * Sanitize our world from the VFS insanity. Unlock the target 13307013b092Sespie * directory and node, which are locked. Release the children, 13317013b092Sespie * which are referenced. Check for rename("x", "y/."), which 13327013b092Sespie * it is our responsibility to reject, not the caller's. (But 13337013b092Sespie * the caller does reject rename("x/.", "y"). Go figure.) 13347013b092Sespie */ 13357013b092Sespie 133636bb23f1Svisa VOP_UNLOCK(tdvp); 13377013b092Sespie if ((tvp != NULL) && (tvp != tdvp)) 133836bb23f1Svisa VOP_UNLOCK(tvp); 13397013b092Sespie 13407013b092Sespie vrele(fvp); 13417013b092Sespie if (tvp != NULL) 13427013b092Sespie vrele(tvp); 13437013b092Sespie 13447013b092Sespie if (tvp == tdvp) { 13457013b092Sespie error = EINVAL; 13467013b092Sespie goto out; 13477013b092Sespie } 13487013b092Sespie 13497013b092Sespie error = tmpfs_sane_rename(fdvp, fcnp, tdvp, tcnp, cred, 0); 13507013b092Sespie 13517013b092Sespie out: /* 13527013b092Sespie * All done, whether with success or failure. Release the 13537013b092Sespie * directory nodes now, as the caller expects from the VFS 13547013b092Sespie * protocol. 13557013b092Sespie */ 13567013b092Sespie vrele(fdvp); 13577013b092Sespie vrele(tdvp); 13587013b092Sespie 13597013b092Sespie return error; 13607013b092Sespie } 13617013b092Sespie 13627013b092Sespie /* 13637013b092Sespie * tmpfs_sane_rename: rename routine, the hairiest system call, with 13647013b092Sespie * the sane API. 13657013b092Sespie * 13667013b092Sespie * Arguments: 13677013b092Sespie * 13687013b092Sespie * . fdvp (from directory vnode), 13697013b092Sespie * . fcnp (from component name), 13707013b092Sespie * . tdvp (to directory vnode), and 13717013b092Sespie * . tcnp (to component name). 13727013b092Sespie * 13737013b092Sespie * fdvp and tdvp must be referenced and unlocked. 13747013b092Sespie */ 13757013b092Sespie int 13767013b092Sespie tmpfs_sane_rename(struct vnode *fdvp, struct componentname *fcnp, 13777013b092Sespie struct vnode *tdvp, struct componentname *tcnp, struct ucred *cred, 13787013b092Sespie int posixly_correct) 13797013b092Sespie { 13807013b092Sespie struct mount *mount; 13817013b092Sespie struct tmpfs_mount *tmpfs; 13827013b092Sespie struct tmpfs_node *fdnode, *tdnode; 13837013b092Sespie struct tmpfs_dirent *fde, *tde; 13847013b092Sespie struct vnode *fvp, *tvp; 13857013b092Sespie char *newname; 13867013b092Sespie int error; 13877013b092Sespie 13887013b092Sespie KASSERT(fdvp != NULL); 13897013b092Sespie KASSERT(fcnp != NULL); 13907013b092Sespie KASSERT(tdvp != NULL); 13917013b092Sespie KASSERT(tcnp != NULL); 13927013b092Sespie /* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */ 13937013b092Sespie /* KASSERT(VOP_ISLOCKED(tdvp) != LK_EXCLUSIVE); */ 13947013b092Sespie KASSERT(fdvp->v_type == VDIR); 13957013b092Sespie KASSERT(tdvp->v_type == VDIR); 13967013b092Sespie KASSERT(fdvp->v_mount == tdvp->v_mount); 13977013b092Sespie KASSERT((fcnp->cn_flags & ISDOTDOT) == 0); 13987013b092Sespie KASSERT((tcnp->cn_flags & ISDOTDOT) == 0); 13997013b092Sespie KASSERT((fcnp->cn_namelen != 1) || (fcnp->cn_nameptr[0] != '.')); 14007013b092Sespie KASSERT((tcnp->cn_namelen != 1) || (tcnp->cn_nameptr[0] != '.')); 14017013b092Sespie KASSERT((fcnp->cn_namelen != 2) || (fcnp->cn_nameptr[0] != '.') || 14027013b092Sespie (fcnp->cn_nameptr[1] != '.')); 14037013b092Sespie KASSERT((tcnp->cn_namelen != 2) || (tcnp->cn_nameptr[0] != '.') || 14047013b092Sespie (tcnp->cn_nameptr[1] != '.')); 14057013b092Sespie 14067013b092Sespie /* 14077013b092Sespie * Pull out the tmpfs data structures. 14087013b092Sespie */ 14097013b092Sespie fdnode = VP_TO_TMPFS_NODE(fdvp); 14107013b092Sespie tdnode = VP_TO_TMPFS_NODE(tdvp); 14117013b092Sespie KASSERT(fdnode != NULL); 14127013b092Sespie KASSERT(tdnode != NULL); 14137013b092Sespie KASSERT(fdnode->tn_vnode == fdvp); 14147013b092Sespie KASSERT(tdnode->tn_vnode == tdvp); 14157013b092Sespie KASSERT(fdnode->tn_type == VDIR); 14167013b092Sespie KASSERT(tdnode->tn_type == VDIR); 14177013b092Sespie 14187013b092Sespie mount = fdvp->v_mount; 14197013b092Sespie KASSERT(mount != NULL); 14207013b092Sespie KASSERT(mount == tdvp->v_mount); 14217013b092Sespie /* XXX How can we be sure this stays true? (Not that you're 14227013b092Sespie * likely to mount a tmpfs read-only...) */ 14237013b092Sespie KASSERT((mount->mnt_flag & MNT_RDONLY) == 0); 14247013b092Sespie tmpfs = VFS_TO_TMPFS(mount); 14257013b092Sespie KASSERT(tmpfs != NULL); 14267013b092Sespie 14277013b092Sespie /* 14287013b092Sespie * Decide whether we need a new name, and allocate memory for 14297013b092Sespie * it if so. Do this before locking anything or taking 14307013b092Sespie * destructive actions so that we can back out safely and sleep 14317013b092Sespie * safely. XXX Is sleeping an issue here? Can this just be 14327013b092Sespie * moved into tmpfs_rename_attachdetach? 14337013b092Sespie */ 14347013b092Sespie if (tmpfs_strname_neqlen(fcnp, tcnp)) { 14357013b092Sespie newname = tmpfs_strname_alloc(tmpfs, tcnp->cn_namelen); 14367013b092Sespie if (newname == NULL) { 14377013b092Sespie error = ENOSPC; 14387013b092Sespie goto out_unlocked; 14397013b092Sespie } 14407013b092Sespie } else { 14417013b092Sespie newname = NULL; 14427013b092Sespie } 14437013b092Sespie 14447013b092Sespie /* 14457013b092Sespie * Lock and look up everything. GCC is not very clever. 14467013b092Sespie */ 14477013b092Sespie fde = tde = NULL; 14487013b092Sespie fvp = tvp = NULL; 14497013b092Sespie error = tmpfs_rename_enter(mount, tmpfs, cred, 14507013b092Sespie fdvp, fdnode, fcnp, &fde, &fvp, 14517013b092Sespie tdvp, tdnode, tcnp, &tde, &tvp); 14527013b092Sespie if (error) 14537013b092Sespie goto out_unlocked; 14547013b092Sespie 14557013b092Sespie /* 14567013b092Sespie * Check that everything is locked and looks right. 14577013b092Sespie */ 14587013b092Sespie KASSERT(fde != NULL); 14597013b092Sespie KASSERT(fvp != NULL); 14607013b092Sespie KASSERT(fde->td_node != NULL); 14617013b092Sespie KASSERT(fde->td_node->tn_vnode == fvp); 14627013b092Sespie KASSERT(fde->td_node->tn_type == fvp->v_type); 14637013b092Sespie KASSERT((tde == NULL) == (tvp == NULL)); 14647013b092Sespie KASSERT((tde == NULL) || (tde->td_node != NULL)); 14657013b092Sespie KASSERT((tde == NULL) || (tde->td_node->tn_vnode == tvp)); 14667013b092Sespie KASSERT((tde == NULL) || (tde->td_node->tn_type == tvp->v_type)); 14677013b092Sespie KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); 14687013b092Sespie KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 14697013b092Sespie KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); 14707013b092Sespie KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 14717013b092Sespie 14727013b092Sespie /* 14737013b092Sespie * If the source and destination are the same object, we need 14747013b092Sespie * only at most delete the source entry. 14757013b092Sespie */ 14767013b092Sespie if (fvp == tvp) { 14777013b092Sespie KASSERT(tvp != NULL); 14787013b092Sespie if (fde->td_node->tn_type == VDIR) { 14797013b092Sespie /* XXX How can this possibly happen? */ 14807013b092Sespie error = EINVAL; 14817013b092Sespie goto out_locked; 14827013b092Sespie } 14837013b092Sespie if (!posixly_correct && (fde != tde)) { 14847013b092Sespie /* XXX Doesn't work because of locking. 14857013b092Sespie * error = VOP_REMOVE(fdvp, fvp); 14867013b092Sespie */ 14877013b092Sespie error = tmpfs_do_remove(tmpfs, fdvp, fdnode, fde, fvp, 14887013b092Sespie cred); 14897013b092Sespie if (error) 14907013b092Sespie goto out_locked; 14917013b092Sespie } 14927013b092Sespie goto success; 14937013b092Sespie } 14947013b092Sespie KASSERT(fde != tde); 14957013b092Sespie KASSERT(fvp != tvp); 14967013b092Sespie 14977013b092Sespie /* 14987013b092Sespie * If the target exists, refuse to rename a directory over a 14997013b092Sespie * non-directory or vice versa, or to clobber a non-empty 15007013b092Sespie * directory. 15017013b092Sespie */ 15027013b092Sespie if (tvp != NULL) { 15037013b092Sespie KASSERT(tde != NULL); 15047013b092Sespie KASSERT(tde->td_node != NULL); 15057013b092Sespie if (fvp->v_type == VDIR && tvp->v_type == VDIR) 15067013b092Sespie error = ((tde->td_node->tn_size > 0)? ENOTEMPTY : 0); 15077013b092Sespie else if (fvp->v_type == VDIR && tvp->v_type != VDIR) 15087013b092Sespie error = ENOTDIR; 15097013b092Sespie else if (fvp->v_type != VDIR && tvp->v_type == VDIR) 15107013b092Sespie error = EISDIR; 15117013b092Sespie else 15127013b092Sespie error = 0; 15137013b092Sespie if (error) 15147013b092Sespie goto out_locked; 15157013b092Sespie KASSERT((fvp->v_type == VDIR) == (tvp->v_type == VDIR)); 15167013b092Sespie } 15177013b092Sespie 15187013b092Sespie /* 15197013b092Sespie * Authorize the rename. 15207013b092Sespie */ 15217013b092Sespie error = tmpfs_rename_check_possible(fdnode, fde->td_node, 15227013b092Sespie tdnode, (tde? tde->td_node : NULL)); 15237013b092Sespie if (error) 15247013b092Sespie goto out_locked; 15257013b092Sespie error = tmpfs_rename_check_permitted(cred, fdnode, fde->td_node, 15267013b092Sespie tdnode, (tde? tde->td_node : NULL)); 15277013b092Sespie if (error) 15287013b092Sespie goto out_locked; 15297013b092Sespie 15307013b092Sespie /* 15317013b092Sespie * Everything is hunky-dory. Shuffle the directory entries. 15327013b092Sespie */ 15337013b092Sespie tmpfs_rename_attachdetach(tmpfs, fdvp, fde, fvp, tdvp, tde, tvp); 15347013b092Sespie 15357013b092Sespie /* 15367013b092Sespie * Update the directory entry's name necessary, and flag 15377013b092Sespie * metadata updates. A memory allocation failure here is not 15387013b092Sespie * OK because we've already committed some changes that we 15397013b092Sespie * can't back out at this point, and we have things locked so 15407013b092Sespie * we can't sleep, hence the early allocation above. 15417013b092Sespie */ 15427013b092Sespie if (newname != NULL) { 15437013b092Sespie KASSERT(tcnp->cn_namelen <= TMPFS_MAXNAMLEN); 15447013b092Sespie 15457013b092Sespie tmpfs_strname_free(tmpfs, fde->td_name, fde->td_namelen); 15467013b092Sespie fde->td_namelen = (uint16_t)tcnp->cn_namelen; 15477013b092Sespie (void)memcpy(newname, tcnp->cn_nameptr, tcnp->cn_namelen); 15487013b092Sespie /* Commit newname and don't free it on the way out. */ 15497013b092Sespie fde->td_name = newname; 15507013b092Sespie newname = NULL; 15517013b092Sespie 15527013b092Sespie tmpfs_update(fde->td_node, TMPFS_NODE_CHANGED); 15537013b092Sespie tmpfs_update(tdnode, TMPFS_NODE_MODIFIED); 15547013b092Sespie } 15557013b092Sespie 15567013b092Sespie success: 15577013b092Sespie VN_KNOTE(fvp, NOTE_RENAME); 15587013b092Sespie tmpfs_rename_cache_purge(fdvp, fvp, tdvp, tvp); 15597013b092Sespie error = 0; 15607013b092Sespie 15617013b092Sespie out_locked: 15627013b092Sespie tmpfs_rename_exit(tmpfs, fdvp, fvp, tdvp, tvp); 15637013b092Sespie 15647013b092Sespie out_unlocked: 15657013b092Sespie /* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */ 15667013b092Sespie /* KASSERT(VOP_ISLOCKED(tdvp) != LK_EXCLUSIVE); */ 15677013b092Sespie /* KASSERT((fvp == NULL) || (VOP_ISLOCKED(fvp) != LK_EXCLUSIVE)); */ 15687013b092Sespie /* KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) != LK_EXCLUSIVE)); */ 15697013b092Sespie 15707013b092Sespie if (newname != NULL) 15717013b092Sespie tmpfs_strname_free(tmpfs, newname, tcnp->cn_namelen); 15727013b092Sespie 15737013b092Sespie return error; 15747013b092Sespie } 15757013b092Sespie 15767013b092Sespie /* 15777013b092Sespie * Look up fcnp in fdnode/fdvp and store its directory entry in fde_ret 15787013b092Sespie * and the associated vnode in fvp_ret; fail if not found. Look up 15797013b092Sespie * tcnp in tdnode/tdvp and store its directory entry in tde_ret and the 15807013b092Sespie * associated vnode in tvp_ret; store null instead if not found. Fail 15817013b092Sespie * if anything has been mounted on any of the nodes involved. 15827013b092Sespie * 15837013b092Sespie * fdvp and tdvp must be referenced. 15847013b092Sespie * 15857013b092Sespie * On entry, nothing is locked. 15867013b092Sespie * 15877013b092Sespie * On success, everything is locked, and *fvp_ret, and *tvp_ret if 15887013b092Sespie * nonnull, are referenced. The only pairs of vnodes that may be 15897013b092Sespie * identical are {fdvp, tdvp} and {fvp, tvp}. 15907013b092Sespie * 15917013b092Sespie * On failure, everything remains as was. 15927013b092Sespie * 15937013b092Sespie * Locking everything including the source and target nodes is 15947013b092Sespie * necessary to make sure that, e.g., link count updates are OK. The 15957013b092Sespie * locking order is, in general, ancestor-first, matching the order you 15967013b092Sespie * need to use to look up a descendant anyway. 15977013b092Sespie */ 15987013b092Sespie int 15997013b092Sespie tmpfs_rename_enter(struct mount *mount, struct tmpfs_mount *tmpfs, 16007013b092Sespie struct ucred *cred, 16017013b092Sespie struct vnode *fdvp, struct tmpfs_node *fdnode, struct componentname *fcnp, 16027013b092Sespie struct tmpfs_dirent **fde_ret, struct vnode **fvp_ret, 16037013b092Sespie struct vnode *tdvp, struct tmpfs_node *tdnode, struct componentname *tcnp, 16047013b092Sespie struct tmpfs_dirent **tde_ret, struct vnode **tvp_ret) 16057013b092Sespie { 16067013b092Sespie int error; 16077013b092Sespie 16087013b092Sespie KASSERT(mount != NULL); 16097013b092Sespie KASSERT(tmpfs != NULL); 16107013b092Sespie KASSERT(fdvp != NULL); 16117013b092Sespie KASSERT(fdnode != NULL); 16127013b092Sespie KASSERT(fcnp != NULL); 16137013b092Sespie KASSERT(fde_ret != NULL); 16147013b092Sespie KASSERT(fvp_ret != NULL); 16157013b092Sespie KASSERT(tdvp != NULL); 16167013b092Sespie KASSERT(tdnode != NULL); 16177013b092Sespie KASSERT(tcnp != NULL); 16187013b092Sespie KASSERT(tde_ret != NULL); 16197013b092Sespie KASSERT(tvp_ret != NULL); 16207013b092Sespie KASSERT(fdnode->tn_vnode == fdvp); 16217013b092Sespie KASSERT(tdnode->tn_vnode == tdvp); 16227013b092Sespie KASSERT(fdnode->tn_type == VDIR); 16237013b092Sespie KASSERT(tdnode->tn_type == VDIR); 16247013b092Sespie 16257013b092Sespie if (fdvp == tdvp) { 16267013b092Sespie KASSERT(fdnode == tdnode); 16277013b092Sespie error = tmpfs_rename_enter_common(mount, tmpfs, cred, fdvp, 16287013b092Sespie fdnode, fcnp, fde_ret, fvp_ret, tcnp, tde_ret, tvp_ret); 16297013b092Sespie } else { 16307013b092Sespie KASSERT(fdnode != tdnode); 16317013b092Sespie error = tmpfs_rename_enter_separate(mount, tmpfs, cred, 16327013b092Sespie fdvp, fdnode, fcnp, fde_ret, fvp_ret, 16337013b092Sespie tdvp, tdnode, tcnp, tde_ret, tvp_ret); 16347013b092Sespie } 16357013b092Sespie 16367013b092Sespie if (error) 16377013b092Sespie return error; 16387013b092Sespie 16397013b092Sespie KASSERT(*fde_ret != NULL); 16407013b092Sespie KASSERT(*fvp_ret != NULL); 16417013b092Sespie KASSERT((*tde_ret == NULL) == (*tvp_ret == NULL)); 16427013b092Sespie KASSERT((*tde_ret == NULL) || ((*tde_ret)->td_node != NULL)); 16437013b092Sespie KASSERT((*tde_ret == NULL) || 16447013b092Sespie ((*tde_ret)->td_node->tn_vnode == *tvp_ret)); 16457013b092Sespie KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); 16467013b092Sespie KASSERT(VOP_ISLOCKED(*fvp_ret) == LK_EXCLUSIVE); 16477013b092Sespie KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 16487013b092Sespie KASSERT((*tvp_ret == NULL) || 16497013b092Sespie (VOP_ISLOCKED(*tvp_ret) == LK_EXCLUSIVE)); 16507013b092Sespie KASSERT(*fvp_ret != fdvp); 16517013b092Sespie KASSERT(*fvp_ret != tdvp); 16527013b092Sespie KASSERT(*tvp_ret != fdvp); 16537013b092Sespie KASSERT(*tvp_ret != tdvp); 16547013b092Sespie return 0; 16557013b092Sespie } 16567013b092Sespie 16577013b092Sespie /* 16587013b092Sespie * Lock and look up with a common source/target directory. 16597013b092Sespie */ 16607013b092Sespie int 16617013b092Sespie tmpfs_rename_enter_common(struct mount *mount, struct tmpfs_mount *tmpfs, 16627013b092Sespie struct ucred *cred, 16637013b092Sespie struct vnode *dvp, struct tmpfs_node *dnode, 16647013b092Sespie struct componentname *fcnp, 16657013b092Sespie struct tmpfs_dirent **fde_ret, struct vnode **fvp_ret, 16667013b092Sespie struct componentname *tcnp, 16677013b092Sespie struct tmpfs_dirent **tde_ret, struct vnode **tvp_ret) 16687013b092Sespie { 16697013b092Sespie struct tmpfs_dirent *fde, *tde; 16707013b092Sespie struct vnode *fvp, *tvp; 16717013b092Sespie int error; 16727013b092Sespie 16737013b092Sespie error = tmpfs_rename_lock_directory(dvp, dnode); 16747013b092Sespie if (error) 16757013b092Sespie goto fail0; 16767013b092Sespie 16777013b092Sespie /* Did we lose a race with mount? */ 16787013b092Sespie if (dvp->v_mountedhere != NULL) { 16797013b092Sespie error = EBUSY; 16807013b092Sespie goto fail1; 16817013b092Sespie } 16827013b092Sespie 16837013b092Sespie /* Make sure the caller may read the directory. */ 16847013b092Sespie error = VOP_ACCESS(dvp, VEXEC, cred, curproc); 16857013b092Sespie if (error) 16867013b092Sespie goto fail1; 16877013b092Sespie 16887013b092Sespie /* 16897013b092Sespie * The order in which we lock the source and target nodes is 16907013b092Sespie * irrelevant because there can only be one rename on this 16917013b092Sespie * directory in flight at a time, and we have it locked. 16927013b092Sespie */ 16937013b092Sespie 16947013b092Sespie fde = tmpfs_dir_lookup(dnode, fcnp); 16957013b092Sespie if (fde == NULL) { 16967013b092Sespie error = ENOENT; 16977013b092Sespie goto fail1; 16987013b092Sespie } 16997013b092Sespie 17007013b092Sespie KASSERT(fde->td_node != NULL); 17017013b092Sespie /* We ruled out `.' earlier. */ 17027013b092Sespie KASSERT(fde->td_node != dnode); 17037013b092Sespie /* We ruled out `..' earlier. */ 17047013b092Sespie KASSERT(fde->td_node != dnode->tn_spec.tn_dir.tn_parent); 17057013b092Sespie rw_enter_write(&fde->td_node->tn_nlock); 17067013b092Sespie error = tmpfs_vnode_get(mount, fde->td_node, &fvp); 17077013b092Sespie if (error) 17087013b092Sespie goto fail1; 17097013b092Sespie KASSERT(fvp != NULL); 17107013b092Sespie KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); 17117013b092Sespie KASSERT(fvp != dvp); 17127013b092Sespie KASSERT(fvp->v_mount == mount); 17137013b092Sespie 17147013b092Sespie /* Refuse to rename a mount point. */ 17157013b092Sespie if ((fvp->v_type == VDIR) && (fvp->v_mountedhere != NULL)) { 17167013b092Sespie error = EBUSY; 17177013b092Sespie goto fail2; 17187013b092Sespie } 17197013b092Sespie 17207013b092Sespie tde = tmpfs_dir_lookup(dnode, tcnp); 17217013b092Sespie if (tde == NULL) { 17227013b092Sespie tvp = NULL; 17237013b092Sespie } else { 17247013b092Sespie KASSERT(tde->td_node != NULL); 17257013b092Sespie /* We ruled out `.' earlier. */ 17267013b092Sespie KASSERT(tde->td_node != dnode); 17277013b092Sespie /* We ruled out `..' earlier. */ 17287013b092Sespie KASSERT(tde->td_node != dnode->tn_spec.tn_dir.tn_parent); 17297013b092Sespie if (tde->td_node != fde->td_node) { 17307013b092Sespie rw_enter_write(&tde->td_node->tn_nlock); 17317013b092Sespie error = tmpfs_vnode_get(mount, tde->td_node, &tvp); 17327013b092Sespie if (error) 17337013b092Sespie goto fail2; 17347013b092Sespie KASSERT(tvp->v_mount == mount); 17357013b092Sespie /* Refuse to rename over a mount point. */ 17367013b092Sespie if ((tvp->v_type == VDIR) && 17377013b092Sespie (tvp->v_mountedhere != NULL)) { 17387013b092Sespie error = EBUSY; 17397013b092Sespie goto fail3; 17407013b092Sespie } 17417013b092Sespie } else { 17427013b092Sespie tvp = fvp; 17437013b092Sespie vref(tvp); 17447013b092Sespie } 17457013b092Sespie KASSERT(tvp != NULL); 17467013b092Sespie KASSERT(VOP_ISLOCKED(tvp) == LK_EXCLUSIVE); 17477013b092Sespie } 17487013b092Sespie KASSERT(tvp != dvp); 17497013b092Sespie 17507013b092Sespie *fde_ret = fde; 17517013b092Sespie *fvp_ret = fvp; 17527013b092Sespie *tde_ret = tde; 17537013b092Sespie *tvp_ret = tvp; 17547013b092Sespie return 0; 17557013b092Sespie 17567013b092Sespie fail3: if (tvp != NULL) { 17577013b092Sespie if (tvp != fvp) 17587013b092Sespie vput(tvp); 17597013b092Sespie else 17607013b092Sespie vrele(tvp); 17617013b092Sespie } 17627013b092Sespie 17637013b092Sespie fail2: vput(fvp); 176436bb23f1Svisa fail1: VOP_UNLOCK(dvp); 17657013b092Sespie fail0: return error; 17667013b092Sespie } 17677013b092Sespie 17687013b092Sespie /* 17697013b092Sespie * Lock and look up with separate source and target directories. 17707013b092Sespie */ 17717013b092Sespie int 17727013b092Sespie tmpfs_rename_enter_separate(struct mount *mount, struct tmpfs_mount *tmpfs, 17737013b092Sespie struct ucred *cred, 17747013b092Sespie struct vnode *fdvp, struct tmpfs_node *fdnode, struct componentname *fcnp, 17757013b092Sespie struct tmpfs_dirent **fde_ret, struct vnode **fvp_ret, 17767013b092Sespie struct vnode *tdvp, struct tmpfs_node *tdnode, struct componentname *tcnp, 17777013b092Sespie struct tmpfs_dirent **tde_ret, struct vnode **tvp_ret) 17787013b092Sespie { 17797013b092Sespie struct tmpfs_node *intermediate_node; 17807013b092Sespie struct tmpfs_dirent *fde, *tde; 17817013b092Sespie struct vnode *fvp, *tvp; 17827013b092Sespie int error; 17837013b092Sespie 17847013b092Sespie KASSERT(fdvp != tdvp); 17857013b092Sespie KASSERT(fdnode != tdnode); 17867013b092Sespie 17877013b092Sespie #if 0 /* XXX */ 17887013b092Sespie mutex_enter(&tmpfs->tm_rename_lock); 17897013b092Sespie #endif 17907013b092Sespie 17917013b092Sespie error = tmpfs_rename_genealogy(fdnode, tdnode, &intermediate_node); 17927013b092Sespie if (error) 17937013b092Sespie goto fail; 17947013b092Sespie 17957013b092Sespie /* 17967013b092Sespie * intermediate_node == NULL means fdnode is not an ancestor of 17977013b092Sespie * tdnode. 17987013b092Sespie */ 17997013b092Sespie if (intermediate_node == NULL) 18007013b092Sespie error = tmpfs_rename_lock(mount, cred, ENOTEMPTY, 18017013b092Sespie tdvp, tdnode, tcnp, 1, &tde, &tvp, 18027013b092Sespie fdvp, fdnode, fcnp, 0, &fde, &fvp); 18037013b092Sespie else 18047013b092Sespie error = tmpfs_rename_lock(mount, cred, EINVAL, 18057013b092Sespie fdvp, fdnode, fcnp, 0, &fde, &fvp, 18067013b092Sespie tdvp, tdnode, tcnp, 1, &tde, &tvp); 18077013b092Sespie if (error) 18087013b092Sespie goto fail; 18097013b092Sespie 18107013b092Sespie KASSERT(fde != NULL); 18117013b092Sespie KASSERT(fde->td_node != NULL); 18127013b092Sespie 18137013b092Sespie /* 18147013b092Sespie * Reject rename("foo/bar", "foo/bar/baz/quux/zot"). 18157013b092Sespie */ 18167013b092Sespie if (fde->td_node == intermediate_node) { 18177013b092Sespie tmpfs_rename_exit(tmpfs, fdvp, fvp, tdvp, tvp); 18187013b092Sespie return EINVAL; 18197013b092Sespie } 18207013b092Sespie 18217013b092Sespie *fde_ret = fde; 18227013b092Sespie *fvp_ret = fvp; 18237013b092Sespie *tde_ret = tde; 18247013b092Sespie *tvp_ret = tvp; 18257013b092Sespie return 0; 18267013b092Sespie 18277013b092Sespie fail: 18287013b092Sespie #if 0 /* XXX */ 18297013b092Sespie mutex_exit(&tmpfs->tm_rename_lock); 18307013b092Sespie #endif 18317013b092Sespie return error; 18327013b092Sespie } 18337013b092Sespie 18347013b092Sespie /* 18357013b092Sespie * Unlock everything we locked for rename. 18367013b092Sespie * 18377013b092Sespie * fdvp and tdvp must be referenced. 18387013b092Sespie * 18397013b092Sespie * On entry, everything is locked, and fvp and tvp referenced. 18407013b092Sespie * 18417013b092Sespie * On exit, everything is unlocked, and fvp and tvp are released. 18427013b092Sespie */ 18437013b092Sespie void 18447013b092Sespie tmpfs_rename_exit(struct tmpfs_mount *tmpfs, 18457013b092Sespie struct vnode *fdvp, struct vnode *fvp, 18467013b092Sespie struct vnode *tdvp, struct vnode *tvp) 18477013b092Sespie { 18487013b092Sespie 18497013b092Sespie KASSERT(tmpfs != NULL); 18507013b092Sespie KASSERT(fdvp != NULL); 18517013b092Sespie KASSERT(fvp != NULL); 18527013b092Sespie KASSERT(fdvp != fvp); 18537013b092Sespie KASSERT(fdvp != tvp); 18547013b092Sespie KASSERT(tdvp != tvp); 18557013b092Sespie KASSERT(tdvp != fvp); 18567013b092Sespie KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); 18577013b092Sespie KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 18587013b092Sespie KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); 18597013b092Sespie KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 18607013b092Sespie 18617013b092Sespie if (tvp != NULL) { 18627013b092Sespie if (tvp != fvp) 18637013b092Sespie vput(tvp); 18647013b092Sespie else 18657013b092Sespie vrele(tvp); 18667013b092Sespie } 186736bb23f1Svisa VOP_UNLOCK(tdvp); 18687013b092Sespie vput(fvp); 18697013b092Sespie if (fdvp != tdvp) 187036bb23f1Svisa VOP_UNLOCK(fdvp); 18717013b092Sespie 18727013b092Sespie #if 0 /* XXX */ 18737013b092Sespie if (fdvp != tdvp) 18747013b092Sespie mutex_exit(&tmpfs->tm_rename_lock); 18757013b092Sespie #endif 18767013b092Sespie } 18777013b092Sespie 18787013b092Sespie /* 18797013b092Sespie * Lock a directory, but fail if it has been rmdir'd. 18807013b092Sespie * 18817013b092Sespie * vp must be referenced. 18827013b092Sespie */ 18837013b092Sespie int 18847013b092Sespie tmpfs_rename_lock_directory(struct vnode *vp, struct tmpfs_node *node) 18857013b092Sespie { 18867013b092Sespie 18877013b092Sespie KASSERT(vp != NULL); 18887013b092Sespie KASSERT(node != NULL); 18897013b092Sespie KASSERT(node->tn_vnode == vp); 18907013b092Sespie KASSERT(node->tn_type == VDIR); 18917013b092Sespie 18926e880534Svisa vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 18937013b092Sespie if (node->tn_spec.tn_dir.tn_parent == NULL) { 189436bb23f1Svisa VOP_UNLOCK(vp); 18957013b092Sespie return ENOENT; 18967013b092Sespie } 18977013b092Sespie 18987013b092Sespie return 0; 18997013b092Sespie } 19007013b092Sespie 19017013b092Sespie /* 19027013b092Sespie * Analyze the genealogy of the source and target nodes. 19037013b092Sespie * 19047013b092Sespie * On success, stores in *intermediate_node_ret either the child of 19057013b092Sespie * fdnode of which tdnode is a descendant, or null if tdnode is not a 19067013b092Sespie * descendant of fdnode at all. 19077013b092Sespie * 19087013b092Sespie * fdnode and tdnode must be unlocked and referenced. The file 19097013b092Sespie * system's rename lock must also be held, to exclude concurrent 19107013b092Sespie * changes to the file system's genealogy other than rmdir. 19117013b092Sespie * 19127013b092Sespie * XXX This causes an extra lock/unlock of tdnode in the case when 19137013b092Sespie * we're just about to lock it again before locking anything else. 19147013b092Sespie * However, changing that requires reorganizing the code to make it 19157013b092Sespie * even more horrifically obscure. 19167013b092Sespie */ 19177013b092Sespie int 19187013b092Sespie tmpfs_rename_genealogy(struct tmpfs_node *fdnode, struct tmpfs_node *tdnode, 19197013b092Sespie struct tmpfs_node **intermediate_node_ret) 19207013b092Sespie { 19217013b092Sespie struct tmpfs_node *node = tdnode, *parent; 19227013b092Sespie int error; 19237013b092Sespie 19247013b092Sespie KASSERT(fdnode != NULL); 19257013b092Sespie KASSERT(tdnode != NULL); 19267013b092Sespie KASSERT(fdnode != tdnode); 19277013b092Sespie KASSERT(intermediate_node_ret != NULL); 19287013b092Sespie 19297013b092Sespie KASSERT(fdnode->tn_vnode != NULL); 19307013b092Sespie KASSERT(tdnode->tn_vnode != NULL); 19317013b092Sespie KASSERT(fdnode->tn_type == VDIR); 19327013b092Sespie KASSERT(tdnode->tn_type == VDIR); 19337013b092Sespie 19347013b092Sespie /* 19357013b092Sespie * We need to provisionally lock tdnode->tn_vnode to keep rmdir 19367013b092Sespie * from deleting it -- or any ancestor -- at an inopportune 19377013b092Sespie * moment. 19387013b092Sespie */ 19397013b092Sespie error = tmpfs_rename_lock_directory(tdnode->tn_vnode, tdnode); 19407013b092Sespie if (error) 19417013b092Sespie return error; 19427013b092Sespie 19437013b092Sespie for (;;) { 19447013b092Sespie parent = node->tn_spec.tn_dir.tn_parent; 19457013b092Sespie KASSERT(parent != NULL); 19467013b092Sespie KASSERT(parent->tn_type == VDIR); 19477013b092Sespie 19487013b092Sespie /* Did we hit the root without finding fdnode? */ 19497013b092Sespie if (parent == node) { 19507013b092Sespie *intermediate_node_ret = NULL; 19517013b092Sespie break; 19527013b092Sespie } 19537013b092Sespie 19547013b092Sespie /* Did we find that fdnode is an ancestor? */ 19557013b092Sespie if (parent == fdnode) { 19567013b092Sespie *intermediate_node_ret = node; 19577013b092Sespie break; 19587013b092Sespie } 19597013b092Sespie 19607013b092Sespie /* Neither -- keep ascending the family tree. */ 19617013b092Sespie node = parent; 19627013b092Sespie } 19637013b092Sespie 196436bb23f1Svisa VOP_UNLOCK(tdnode->tn_vnode); 19657013b092Sespie return 0; 19667013b092Sespie } 19677013b092Sespie 19687013b092Sespie /* 19697013b092Sespie * Lock directories a and b, which must be distinct, and look up and 19707013b092Sespie * lock nodes a and b. Do a first and then b. Directory b may not be 19717013b092Sespie * an ancestor of directory a, although directory a may be an ancestor 19727013b092Sespie * of directory b. Fail with overlap_error if node a is directory b. 19737013b092Sespie * Neither componentname may be `.' or `..'. 19747013b092Sespie * 19757013b092Sespie * a_dvp and b_dvp must be referenced. 19767013b092Sespie * 19777013b092Sespie * On entry, a_dvp and b_dvp are unlocked. 19787013b092Sespie * 19797013b092Sespie * On success, 19807013b092Sespie * . a_dvp and b_dvp are locked, 19817013b092Sespie * . *a_dirent_ret is filled with a directory entry whose node is 19827013b092Sespie * locked and referenced, 19837013b092Sespie * . *b_vp_ret is filled with the corresponding vnode, 19847013b092Sespie * . *b_dirent_ret is filled either with null or with a directory entry 19857013b092Sespie * whose node is locked and referenced, 19867013b092Sespie * . *b_vp is filled either with null or with the corresponding vnode, 19877013b092Sespie * and 19887013b092Sespie * . the only pair of vnodes that may be identical is a_vp and b_vp. 19897013b092Sespie * 19907013b092Sespie * On failure, a_dvp and b_dvp are left unlocked, and *a_dirent_ret, 19917013b092Sespie * *a_vp, *b_dirent_ret, and *b_vp are left alone. 19927013b092Sespie */ 19937013b092Sespie int 19947013b092Sespie tmpfs_rename_lock(struct mount *mount, struct ucred *cred, int overlap_error, 19957013b092Sespie struct vnode *a_dvp, struct tmpfs_node *a_dnode, 19967013b092Sespie struct componentname *a_cnp, int a_missing_ok, 19977013b092Sespie struct tmpfs_dirent **a_dirent_ret, struct vnode **a_vp_ret, 19987013b092Sespie struct vnode *b_dvp, struct tmpfs_node *b_dnode, 19997013b092Sespie struct componentname *b_cnp, int b_missing_ok, 20007013b092Sespie struct tmpfs_dirent **b_dirent_ret, struct vnode **b_vp_ret) 20017013b092Sespie { 20027013b092Sespie struct tmpfs_dirent *a_dirent, *b_dirent; 20037013b092Sespie struct vnode *a_vp, *b_vp; 20047013b092Sespie int error; 20057013b092Sespie 20067013b092Sespie KASSERT(a_dvp != NULL); 20077013b092Sespie KASSERT(a_dnode != NULL); 20087013b092Sespie KASSERT(a_cnp != NULL); 20097013b092Sespie KASSERT(a_dirent_ret != NULL); 20107013b092Sespie KASSERT(a_vp_ret != NULL); 20117013b092Sespie KASSERT(b_dvp != NULL); 20127013b092Sespie KASSERT(b_dnode != NULL); 20137013b092Sespie KASSERT(b_cnp != NULL); 20147013b092Sespie KASSERT(b_dirent_ret != NULL); 20157013b092Sespie KASSERT(b_vp_ret != NULL); 20167013b092Sespie KASSERT(a_dvp != b_dvp); 20177013b092Sespie KASSERT(a_dnode != b_dnode); 20187013b092Sespie KASSERT(a_dnode->tn_vnode == a_dvp); 20197013b092Sespie KASSERT(b_dnode->tn_vnode == b_dvp); 20207013b092Sespie KASSERT(a_dnode->tn_type == VDIR); 20217013b092Sespie KASSERT(b_dnode->tn_type == VDIR); 20227013b092Sespie KASSERT(a_missing_ok != b_missing_ok); 20237013b092Sespie 20247013b092Sespie error = tmpfs_rename_lock_directory(a_dvp, a_dnode); 20257013b092Sespie if (error) 20267013b092Sespie goto fail0; 20277013b092Sespie 20287013b092Sespie /* Did we lose a race with mount? */ 20297013b092Sespie if (a_dvp->v_mountedhere != NULL) { 20307013b092Sespie error = EBUSY; 20317013b092Sespie goto fail1; 20327013b092Sespie } 20337013b092Sespie 20347013b092Sespie /* Make sure the caller may read the directory. */ 20357013b092Sespie error = VOP_ACCESS(a_dvp, VEXEC, cred, curproc); 20367013b092Sespie if (error) 20377013b092Sespie goto fail1; 20387013b092Sespie 20397013b092Sespie a_dirent = tmpfs_dir_lookup(a_dnode, a_cnp); 20407013b092Sespie if (a_dirent != NULL) { 20417013b092Sespie KASSERT(a_dirent->td_node != NULL); 20427013b092Sespie /* We ruled out `.' earlier. */ 20437013b092Sespie KASSERT(a_dirent->td_node != a_dnode); 20447013b092Sespie /* We ruled out `..' earlier. */ 20457013b092Sespie KASSERT(a_dirent->td_node != 20467013b092Sespie a_dnode->tn_spec.tn_dir.tn_parent); 20477013b092Sespie if (a_dirent->td_node == b_dnode) { 20487013b092Sespie error = overlap_error; 20497013b092Sespie goto fail1; 20507013b092Sespie } 20517013b092Sespie rw_enter_write(&a_dirent->td_node->tn_nlock); 20527013b092Sespie error = tmpfs_vnode_get(mount, a_dirent->td_node, &a_vp); 20537013b092Sespie if (error) 20547013b092Sespie goto fail1; 20557013b092Sespie KASSERT(a_vp->v_mount == mount); 20567013b092Sespie /* Refuse to rename (over) a mount point. */ 20577013b092Sespie if ((a_vp->v_type == VDIR) && (a_vp->v_mountedhere != NULL)) { 20587013b092Sespie error = EBUSY; 20597013b092Sespie goto fail2; 20607013b092Sespie } 20617013b092Sespie } else if (!a_missing_ok) { 20627013b092Sespie error = ENOENT; 20637013b092Sespie goto fail1; 20647013b092Sespie } else { 20657013b092Sespie a_vp = NULL; 20667013b092Sespie } 20677013b092Sespie KASSERT(a_vp != a_dvp); 20687013b092Sespie KASSERT(a_vp != b_dvp); 20697013b092Sespie 20707013b092Sespie error = tmpfs_rename_lock_directory(b_dvp, b_dnode); 20717013b092Sespie if (error) 20727013b092Sespie goto fail2; 20737013b092Sespie 20747013b092Sespie /* Did we lose a race with mount? */ 20757013b092Sespie if (b_dvp->v_mountedhere != NULL) { 20767013b092Sespie error = EBUSY; 20777013b092Sespie goto fail3; 20787013b092Sespie } 20797013b092Sespie 20807013b092Sespie /* Make sure the caller may read the directory. */ 20817013b092Sespie error = VOP_ACCESS(b_dvp, VEXEC, cred, curproc); 20827013b092Sespie if (error) 20837013b092Sespie goto fail3; 20847013b092Sespie 20857013b092Sespie b_dirent = tmpfs_dir_lookup(b_dnode, b_cnp); 20867013b092Sespie if (b_dirent != NULL) { 20877013b092Sespie KASSERT(b_dirent->td_node != NULL); 20887013b092Sespie /* We ruled out `.' earlier. */ 20897013b092Sespie KASSERT(b_dirent->td_node != b_dnode); 20907013b092Sespie /* We ruled out `..' earlier. */ 20917013b092Sespie KASSERT(b_dirent->td_node != 20927013b092Sespie b_dnode->tn_spec.tn_dir.tn_parent); 20937013b092Sespie /* b is not an ancestor of a. */ 20947013b092Sespie KASSERT(b_dirent->td_node != a_dnode); 20957013b092Sespie /* But the source and target nodes might be the same. */ 20967013b092Sespie if ((a_dirent == NULL) || 20977013b092Sespie (a_dirent->td_node != b_dirent->td_node)) { 20987013b092Sespie rw_enter_write(&b_dirent->td_node->tn_nlock); 20997013b092Sespie error = tmpfs_vnode_get(mount, b_dirent->td_node, 21007013b092Sespie &b_vp); 21017013b092Sespie if (error) 21027013b092Sespie goto fail3; 21037013b092Sespie KASSERT(b_vp->v_mount == mount); 21047013b092Sespie KASSERT(a_vp != b_vp); 21057013b092Sespie /* Refuse to rename (over) a mount point. */ 21067013b092Sespie if ((b_vp->v_type == VDIR) && 21077013b092Sespie (b_vp->v_mountedhere != NULL)) { 21087013b092Sespie error = EBUSY; 21097013b092Sespie goto fail4; 21107013b092Sespie } 21117013b092Sespie } else { 21127013b092Sespie b_vp = a_vp; 21137013b092Sespie vref(b_vp); 21147013b092Sespie } 21157013b092Sespie } else if (!b_missing_ok) { 21167013b092Sespie error = ENOENT; 21177013b092Sespie goto fail3; 21187013b092Sespie } else { 21197013b092Sespie b_vp = NULL; 21207013b092Sespie } 21217013b092Sespie KASSERT(b_vp != a_dvp); 21227013b092Sespie KASSERT(b_vp != b_dvp); 21237013b092Sespie 21247013b092Sespie KASSERT(VOP_ISLOCKED(a_dvp) == LK_EXCLUSIVE); 21257013b092Sespie KASSERT(VOP_ISLOCKED(b_dvp) == LK_EXCLUSIVE); 21267013b092Sespie KASSERT(a_missing_ok || (a_dirent != NULL)); 21277013b092Sespie KASSERT(a_missing_ok || (a_dirent->td_node != NULL)); 21287013b092Sespie KASSERT(b_missing_ok || (b_dirent != NULL)); 21297013b092Sespie KASSERT(b_missing_ok || (b_dirent->td_node != NULL)); 21307013b092Sespie KASSERT((a_dirent == NULL) || (a_dirent->td_node != NULL)); 21317013b092Sespie KASSERT((a_dirent == NULL) || (a_dirent->td_node->tn_vnode == a_vp)); 21327013b092Sespie KASSERT((b_dirent == NULL) || (b_dirent->td_node != NULL)); 21337013b092Sespie KASSERT((b_dirent == NULL) || (b_dirent->td_node->tn_vnode == b_vp)); 21347013b092Sespie KASSERT((a_vp == NULL) || (VOP_ISLOCKED(a_vp) == LK_EXCLUSIVE)); 21357013b092Sespie KASSERT((b_vp == NULL) || (VOP_ISLOCKED(b_vp) == LK_EXCLUSIVE)); 21367013b092Sespie 21377013b092Sespie *a_dirent_ret = a_dirent; 21387013b092Sespie *b_dirent_ret = b_dirent; 21397013b092Sespie *a_vp_ret = a_vp; 21407013b092Sespie *b_vp_ret = b_vp; 21417013b092Sespie return 0; 21427013b092Sespie 21437013b092Sespie fail4: if (b_vp != NULL) { 21447013b092Sespie KASSERT(VOP_ISLOCKED(b_vp) == LK_EXCLUSIVE); 21457013b092Sespie if (b_vp != a_vp) 21467013b092Sespie vput(b_vp); 21477013b092Sespie else 21487013b092Sespie vrele(a_vp); 21497013b092Sespie } 21507013b092Sespie 21517013b092Sespie fail3: KASSERT(VOP_ISLOCKED(b_dvp) == LK_EXCLUSIVE); 215236bb23f1Svisa VOP_UNLOCK(b_dvp); 21537013b092Sespie 21547013b092Sespie fail2: if (a_vp != NULL) { 21557013b092Sespie KASSERT(VOP_ISLOCKED(a_vp) == LK_EXCLUSIVE); 21567013b092Sespie vput(a_vp); 21577013b092Sespie } 21587013b092Sespie 21597013b092Sespie fail1: KASSERT(VOP_ISLOCKED(a_dvp) == LK_EXCLUSIVE); 216036bb23f1Svisa VOP_UNLOCK(a_dvp); 21617013b092Sespie 21627013b092Sespie fail0: /* KASSERT(VOP_ISLOCKED(a_dvp) != LK_EXCLUSIVE); */ 21637013b092Sespie /* KASSERT(VOP_ISLOCKED(b_dvp) != LK_EXCLUSIVE); */ 21647013b092Sespie /* KASSERT((a_vp == NULL) || (VOP_ISLOCKED(a_vp) != LK_EXCLUSIVE)); */ 21657013b092Sespie /* KASSERT((b_vp == NULL) || (VOP_ISLOCKED(b_vp) != LK_EXCLUSIVE)); */ 21667013b092Sespie return error; 21677013b092Sespie } 21687013b092Sespie 21697013b092Sespie /* 21707013b092Sespie * Shuffle the directory entries to move fvp from the directory fdvp 21717013b092Sespie * into the directory tdvp. fde is fvp's directory entry in fdvp. If 21727013b092Sespie * we are overwriting a target node, it is tvp, and tde is its 21737013b092Sespie * directory entry in tdvp. 21747013b092Sespie * 21757013b092Sespie * fdvp, fvp, tdvp, and tvp must all be locked and referenced. 21767013b092Sespie */ 21777013b092Sespie void 21787013b092Sespie tmpfs_rename_attachdetach(struct tmpfs_mount *tmpfs, 21797013b092Sespie struct vnode *fdvp, struct tmpfs_dirent *fde, struct vnode *fvp, 21807013b092Sespie struct vnode *tdvp, struct tmpfs_dirent *tde, struct vnode *tvp) 21817013b092Sespie { 21827013b092Sespie 21837013b092Sespie KASSERT(tmpfs != NULL); 21847013b092Sespie KASSERT(fdvp != NULL); 21857013b092Sespie KASSERT(fde != NULL); 21867013b092Sespie KASSERT(fvp != NULL); 21877013b092Sespie KASSERT(tdvp != NULL); 21887013b092Sespie KASSERT(fde->td_node != NULL); 21897013b092Sespie KASSERT(fde->td_node->tn_vnode == fvp); 21907013b092Sespie KASSERT((tde == NULL) == (tvp == NULL)); 21917013b092Sespie KASSERT((tde == NULL) || (tde->td_node != NULL)); 21927013b092Sespie KASSERT((tde == NULL) || (tde->td_node->tn_vnode == tvp)); 21937013b092Sespie KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); 21947013b092Sespie KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 21957013b092Sespie KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); 21967013b092Sespie KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 21977013b092Sespie 21987013b092Sespie /* 21997013b092Sespie * If we are moving from one directory to another, detach the 22007013b092Sespie * source entry and reattach it to the target directory. 22017013b092Sespie */ 22027013b092Sespie if (fdvp != tdvp) { 22037013b092Sespie /* tmpfs_dir_detach clobbers fde->td_node, so save it. */ 22047013b092Sespie struct tmpfs_node *fnode = fde->td_node; 220557331246Sespie tmpfs_node_t *fdnode = VP_TO_TMPFS_DIR(fdvp); 220657331246Sespie tmpfs_node_t *tdnode = VP_TO_TMPFS_DIR(tdvp); 220757331246Sespie tmpfs_dir_detach(fdnode, fde); 220857331246Sespie tmpfs_dir_attach(tdnode, fde, fnode); 22097013b092Sespie } else if (tvp == NULL) { 22107013b092Sespie /* 22117013b092Sespie * We are changing the directory. tmpfs_dir_attach and 22127013b092Sespie * tmpfs_dir_detach note the events for us, but for 22137013b092Sespie * this case we don't call them, so we must note the 22147013b092Sespie * event explicitly. 22157013b092Sespie */ 22167013b092Sespie VN_KNOTE(fdvp, NOTE_WRITE); 22177013b092Sespie } 22187013b092Sespie 22197013b092Sespie /* 22207013b092Sespie * If we are replacing an existing target entry, delete it. 22217013b092Sespie */ 22227013b092Sespie if (tde != NULL) { 222357331246Sespie tmpfs_node_t *tdnode = VP_TO_TMPFS_DIR(tdvp); 22247013b092Sespie KASSERT(tvp != NULL); 22257013b092Sespie KASSERT(tde->td_node != NULL); 22267013b092Sespie KASSERT((fvp->v_type == VDIR) == (tvp->v_type == VDIR)); 22277013b092Sespie if (tde->td_node->tn_type == VDIR) { 22287013b092Sespie KASSERT(tde->td_node->tn_size == 0); 22297013b092Sespie KASSERT(tde->td_node->tn_links == 2); 22307013b092Sespie /* Decrement the extra link count for `.' so 22317013b092Sespie * the vnode will be recycled when released. */ 22327013b092Sespie tde->td_node->tn_links--; 22337013b092Sespie } 223457331246Sespie tmpfs_dir_detach(tdnode, tde); 22357013b092Sespie tmpfs_free_dirent(tmpfs, tde); 22367013b092Sespie } 22377013b092Sespie } 22387013b092Sespie 22397013b092Sespie /* 22407013b092Sespie * Remove the entry de for the non-directory vp from the directory dvp. 22417013b092Sespie * 22427013b092Sespie * Everything must be locked and referenced. 22437013b092Sespie */ 22447013b092Sespie int 22457013b092Sespie tmpfs_do_remove(struct tmpfs_mount *tmpfs, struct vnode *dvp, 22467013b092Sespie struct tmpfs_node *dnode, struct tmpfs_dirent *de, struct vnode *vp, 22477013b092Sespie struct ucred *cred) 22487013b092Sespie { 22497013b092Sespie int error; 22507013b092Sespie 22517013b092Sespie KASSERT(tmpfs != NULL); 22527013b092Sespie KASSERT(dvp != NULL); 22537013b092Sespie KASSERT(dnode != NULL); 22547013b092Sespie KASSERT(de != NULL); 22557013b092Sespie KASSERT(vp != NULL); 22567013b092Sespie KASSERT(dnode->tn_vnode == dvp); 22577013b092Sespie KASSERT(de->td_node != NULL); 22587013b092Sespie KASSERT(de->td_node->tn_vnode == vp); 22597013b092Sespie KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 22607013b092Sespie KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 22617013b092Sespie 22627013b092Sespie error = tmpfs_remove_check_possible(dnode, de->td_node); 22637013b092Sespie if (error) 22647013b092Sespie return error; 22657013b092Sespie 22667013b092Sespie error = tmpfs_remove_check_permitted(cred, dnode, de->td_node); 22677013b092Sespie if (error) 22687013b092Sespie return error; 22697013b092Sespie 22707013b092Sespie /* 22717013b092Sespie * If not root and directory is sticky, check for permission on 22727013b092Sespie * directory or on file. This implements append-only directories. 22737013b092Sespie */ 22747013b092Sespie if ((dnode->tn_mode & S_ISTXT) != 0) 22757013b092Sespie if (cred->cr_uid != 0 && cred->cr_uid != dnode->tn_uid && 22767013b092Sespie cred->cr_uid != de->td_node->tn_uid) 22777013b092Sespie return EPERM; 22787013b092Sespie 227957331246Sespie tmpfs_dir_detach(dnode, de); 22807013b092Sespie tmpfs_free_dirent(tmpfs, de); 22817013b092Sespie 22827013b092Sespie return 0; 22837013b092Sespie } 22847013b092Sespie 22857013b092Sespie /* 22867013b092Sespie * Check whether a rename is possible independent of credentials. 22877013b092Sespie * 22887013b092Sespie * Everything must be locked and referenced. 22897013b092Sespie */ 22907013b092Sespie int 22917013b092Sespie tmpfs_rename_check_possible( 22927013b092Sespie struct tmpfs_node *fdnode, struct tmpfs_node *fnode, 22937013b092Sespie struct tmpfs_node *tdnode, struct tmpfs_node *tnode) 22947013b092Sespie { 22957013b092Sespie 22967013b092Sespie KASSERT(fdnode != NULL); 22977013b092Sespie KASSERT(fnode != NULL); 22987013b092Sespie KASSERT(tdnode != NULL); 22997013b092Sespie KASSERT(fdnode != fnode); 23007013b092Sespie KASSERT(tdnode != tnode); 23017013b092Sespie KASSERT(fnode != tnode); 23027013b092Sespie KASSERT(fdnode->tn_vnode != NULL); 23037013b092Sespie KASSERT(fnode->tn_vnode != NULL); 23047013b092Sespie KASSERT(tdnode->tn_vnode != NULL); 23057013b092Sespie KASSERT((tnode == NULL) || (tnode->tn_vnode != NULL)); 23067013b092Sespie KASSERT(VOP_ISLOCKED(fdnode->tn_vnode) == LK_EXCLUSIVE); 23077013b092Sespie KASSERT(VOP_ISLOCKED(fnode->tn_vnode) == LK_EXCLUSIVE); 23087013b092Sespie KASSERT(VOP_ISLOCKED(tdnode->tn_vnode) == LK_EXCLUSIVE); 23097013b092Sespie KASSERT((tnode == NULL) || 23107013b092Sespie (VOP_ISLOCKED(tnode->tn_vnode) == LK_EXCLUSIVE)); 23117013b092Sespie 23127013b092Sespie /* 23137013b092Sespie * If fdnode is immutable, we can't write to it. If fdnode is 23147013b092Sespie * append-only, the only change we can make is to add entries 23157013b092Sespie * to it. If fnode is immutable, we can't change the links to 23167013b092Sespie * it. If fnode is append-only...well, this is what UFS does. 23177013b092Sespie */ 23187013b092Sespie if ((fdnode->tn_flags | fnode->tn_flags) & (IMMUTABLE | APPEND)) 23197013b092Sespie return EPERM; 23207013b092Sespie 23217013b092Sespie /* 23227013b092Sespie * If tdnode is immutable, we can't write to it. If tdnode is 23237013b092Sespie * append-only, we can add entries, but we can't change 23247013b092Sespie * existing entries. 23257013b092Sespie */ 23267013b092Sespie if (tdnode->tn_flags & (IMMUTABLE | (tnode? APPEND : 0))) 23277013b092Sespie return EPERM; 23287013b092Sespie 23297013b092Sespie /* 23307013b092Sespie * If tnode is immutable, we can't replace links to it. If 23317013b092Sespie * tnode is append-only...well, this is what UFS does. 23327013b092Sespie */ 23337013b092Sespie if (tnode != NULL) { 23347013b092Sespie KASSERT(tnode != NULL); 23357013b092Sespie if ((tnode->tn_flags & (IMMUTABLE | APPEND)) != 0) 23367013b092Sespie return EPERM; 23377013b092Sespie } 23387013b092Sespie 23397013b092Sespie return 0; 23407013b092Sespie } 23417013b092Sespie 23427013b092Sespie /* 23437013b092Sespie * Check whether a rename is permitted given our credentials. 23447013b092Sespie * 23457013b092Sespie * Everything must be locked and referenced. 23467013b092Sespie */ 23477013b092Sespie int 23487013b092Sespie tmpfs_rename_check_permitted(struct ucred *cred, 23497013b092Sespie struct tmpfs_node *fdnode, struct tmpfs_node *fnode, 23507013b092Sespie struct tmpfs_node *tdnode, struct tmpfs_node *tnode) 23517013b092Sespie { 23527013b092Sespie int error; 23537013b092Sespie 23547013b092Sespie KASSERT(fdnode != NULL); 23557013b092Sespie KASSERT(fnode != NULL); 23567013b092Sespie KASSERT(tdnode != NULL); 23577013b092Sespie KASSERT(fdnode != fnode); 23587013b092Sespie KASSERT(tdnode != tnode); 23597013b092Sespie KASSERT(fnode != tnode); 23607013b092Sespie KASSERT(fdnode->tn_vnode != NULL); 23617013b092Sespie KASSERT(fnode->tn_vnode != NULL); 23627013b092Sespie KASSERT(tdnode->tn_vnode != NULL); 23637013b092Sespie KASSERT((tnode == NULL) || (tnode->tn_vnode != NULL)); 23647013b092Sespie KASSERT(VOP_ISLOCKED(fdnode->tn_vnode) == LK_EXCLUSIVE); 23657013b092Sespie KASSERT(VOP_ISLOCKED(fnode->tn_vnode) == LK_EXCLUSIVE); 23667013b092Sespie KASSERT(VOP_ISLOCKED(tdnode->tn_vnode) == LK_EXCLUSIVE); 23677013b092Sespie KASSERT((tnode == NULL) || 23687013b092Sespie (VOP_ISLOCKED(tnode->tn_vnode) == LK_EXCLUSIVE)); 23697013b092Sespie 23707013b092Sespie /* 23717013b092Sespie * We need to remove or change an entry in the source directory. 23727013b092Sespie */ 23737013b092Sespie error = VOP_ACCESS(fdnode->tn_vnode, VWRITE, cred, curproc); 23747013b092Sespie if (error) 23757013b092Sespie return error; 23767013b092Sespie 23777013b092Sespie /* 23787013b092Sespie * If we are changing directories, then we need to write to the 23797013b092Sespie * target directory to add or change an entry. Also, if fnode 23807013b092Sespie * is a directory, we need to write to it to change its `..' 23817013b092Sespie * entry. 23827013b092Sespie */ 23837013b092Sespie if (fdnode != tdnode) { 23847013b092Sespie error = VOP_ACCESS(tdnode->tn_vnode, VWRITE, cred, curproc); 23857013b092Sespie if (error) 23867013b092Sespie return error; 23877013b092Sespie if (fnode->tn_type == VDIR) { 23887013b092Sespie error = VOP_ACCESS(fnode->tn_vnode, VWRITE, cred, 23897013b092Sespie curproc); 23907013b092Sespie if (error) 23917013b092Sespie return error; 23927013b092Sespie } 23937013b092Sespie } 23947013b092Sespie 23957013b092Sespie error = tmpfs_check_sticky(cred, fdnode, fnode); 23967013b092Sespie if (error) 23977013b092Sespie return error; 23987013b092Sespie 239957331246Sespie if (TMPFS_DIRSEQ_FULL(tdnode)) 240057331246Sespie return (ENOSPC); 240157331246Sespie 24027013b092Sespie error = tmpfs_check_sticky(cred, tdnode, tnode); 24037013b092Sespie if (error) 24047013b092Sespie return error; 24057013b092Sespie 24067013b092Sespie return 0; 24077013b092Sespie } 24087013b092Sespie 24097013b092Sespie /* 24107013b092Sespie * Check whether removing node's entry in dnode is possible independent 24117013b092Sespie * of credentials. 24127013b092Sespie * 24137013b092Sespie * Everything must be locked and referenced. 24147013b092Sespie */ 24157013b092Sespie int 24167013b092Sespie tmpfs_remove_check_possible(struct tmpfs_node *dnode, struct tmpfs_node *node) 24177013b092Sespie { 24187013b092Sespie 24197013b092Sespie KASSERT(dnode != NULL); 24207013b092Sespie KASSERT(dnode->tn_vnode != NULL); 24217013b092Sespie KASSERT(node != NULL); 24227013b092Sespie KASSERT(dnode != node); 24237013b092Sespie KASSERT(VOP_ISLOCKED(dnode->tn_vnode) == LK_EXCLUSIVE); 24247013b092Sespie KASSERT(VOP_ISLOCKED(node->tn_vnode) == LK_EXCLUSIVE); 24257013b092Sespie 24267013b092Sespie /* 24277013b092Sespie * We want to delete the entry. If dnode is immutable, we 24287013b092Sespie * can't write to it to delete the entry. If dnode is 24297013b092Sespie * append-only, the only change we can make is to add entries, 24307013b092Sespie * so we can't delete entries. If node is immutable, we can't 24317013b092Sespie * change the links to it, so we can't delete the entry. If 24327013b092Sespie * node is append-only...well, this is what UFS does. 24337013b092Sespie */ 24347013b092Sespie if ((dnode->tn_flags | node->tn_flags) & (IMMUTABLE | APPEND)) 24357013b092Sespie return EPERM; 24367013b092Sespie 24377013b092Sespie return 0; 24387013b092Sespie } 24397013b092Sespie 24407013b092Sespie /* 24417013b092Sespie * Check whether removing node's entry in dnode is permitted given our 24427013b092Sespie * credentials. 24437013b092Sespie * 24447013b092Sespie * Everything must be locked and referenced. 24457013b092Sespie */ 24467013b092Sespie int 24477013b092Sespie tmpfs_remove_check_permitted(struct ucred *cred, 24487013b092Sespie struct tmpfs_node *dnode, struct tmpfs_node *node) 24497013b092Sespie { 24507013b092Sespie int error; 24517013b092Sespie 24527013b092Sespie KASSERT(dnode != NULL); 24537013b092Sespie KASSERT(dnode->tn_vnode != NULL); 24547013b092Sespie KASSERT(node != NULL); 24557013b092Sespie KASSERT(dnode != node); 24567013b092Sespie KASSERT(VOP_ISLOCKED(dnode->tn_vnode) == LK_EXCLUSIVE); 24577013b092Sespie KASSERT(VOP_ISLOCKED(node->tn_vnode) == LK_EXCLUSIVE); 24587013b092Sespie 24597013b092Sespie /* 24607013b092Sespie * Check whether we are permitted to write to the source 24617013b092Sespie * directory in order to delete an entry from it. 24627013b092Sespie */ 24637013b092Sespie error = VOP_ACCESS(dnode->tn_vnode, VWRITE, cred, curproc); 24647013b092Sespie if (error) 24657013b092Sespie return error; 24667013b092Sespie 24677013b092Sespie error = tmpfs_check_sticky(cred, dnode, node); 24687013b092Sespie if (error) 24697013b092Sespie return error; 24707013b092Sespie 24717013b092Sespie return 0; 24727013b092Sespie } 24737013b092Sespie 24747013b092Sespie /* 24757013b092Sespie * Check whether we may change an entry in a sticky directory. If the 24767013b092Sespie * directory is sticky, the user must own either the directory or, if 24777013b092Sespie * it exists, the node, in order to change the entry. 24787013b092Sespie * 24797013b092Sespie * Everything must be locked and referenced. 24807013b092Sespie */ 24817013b092Sespie int 24827013b092Sespie tmpfs_check_sticky(struct ucred *cred, 24837013b092Sespie struct tmpfs_node *dnode, struct tmpfs_node *node) 24847013b092Sespie { 24857013b092Sespie 24867013b092Sespie KASSERT(dnode != NULL); 24877013b092Sespie KASSERT(dnode->tn_vnode != NULL); 24887013b092Sespie KASSERT(VOP_ISLOCKED(dnode->tn_vnode) == LK_EXCLUSIVE); 24897013b092Sespie KASSERT((node == NULL) || (node->tn_vnode != NULL)); 24907013b092Sespie KASSERT((node == NULL) || 24917013b092Sespie (VOP_ISLOCKED(dnode->tn_vnode) == LK_EXCLUSIVE)); 24927013b092Sespie 24937013b092Sespie if (node == NULL) 24947013b092Sespie return 0; 24957013b092Sespie 24967013b092Sespie if (dnode->tn_mode & S_ISTXT) { 24977013b092Sespie if (cred->cr_uid != 0 && 24987013b092Sespie cred->cr_uid != dnode->tn_uid && 24997013b092Sespie cred->cr_uid != node->tn_uid) 25007013b092Sespie return EPERM; 25017013b092Sespie } 25027013b092Sespie 25037013b092Sespie return 0; 25047013b092Sespie } 25057013b092Sespie 25067013b092Sespie void 25077013b092Sespie tmpfs_rename_cache_purge(struct vnode *fdvp, struct vnode *fvp, 25087013b092Sespie struct vnode *tdvp, struct vnode *tvp) 25097013b092Sespie { 25107013b092Sespie 25117013b092Sespie KASSERT(fdvp != NULL); 25127013b092Sespie KASSERT(fvp != NULL); 25137013b092Sespie KASSERT(tdvp != NULL); 25147013b092Sespie KASSERT(fdvp != fvp); 25157013b092Sespie KASSERT(fdvp != tvp); 25167013b092Sespie KASSERT(tdvp != fvp); 25177013b092Sespie KASSERT(tdvp != tvp); 25187013b092Sespie KASSERT(fvp != tvp); 25197013b092Sespie KASSERT(fdvp->v_type == VDIR); 25207013b092Sespie KASSERT(tdvp->v_type == VDIR); 25217013b092Sespie 25227013b092Sespie /* 25237013b092Sespie * XXX What actually needs to be purged? 25247013b092Sespie */ 25257013b092Sespie 25267013b092Sespie cache_purge(fdvp); 25277013b092Sespie 25287013b092Sespie if (fvp->v_type == VDIR) 25297013b092Sespie cache_purge(fvp); 25307013b092Sespie 25317013b092Sespie if (tdvp != fdvp) 25327013b092Sespie cache_purge(tdvp); 25337013b092Sespie 25347013b092Sespie if ((tvp != NULL) && (tvp->v_type == VDIR)) 25357013b092Sespie cache_purge(tvp); 25367013b092Sespie } 253746905cb9Sespie 253846905cb9Sespie void 253946905cb9Sespie tmpfs_rename_abort(void *v) 254046905cb9Sespie { 254146905cb9Sespie struct vop_rename_args *ap = v; 254246905cb9Sespie struct vnode *fdvp = ap->a_fdvp; 254346905cb9Sespie struct vnode *fvp = ap->a_fvp; 254446905cb9Sespie struct componentname *fcnp = ap->a_fcnp; 254546905cb9Sespie struct vnode *tdvp = ap->a_tdvp; 254646905cb9Sespie struct vnode *tvp = ap->a_tvp; 254746905cb9Sespie struct componentname *tcnp = ap->a_tcnp; 254846905cb9Sespie 254946905cb9Sespie VOP_ABORTOP(tdvp, tcnp); 255046905cb9Sespie if (tdvp == tvp) 255146905cb9Sespie vrele(tdvp); 255246905cb9Sespie else 255346905cb9Sespie vput(tdvp); 255446905cb9Sespie if (tvp != NULL) 255546905cb9Sespie vput(tvp); 255646905cb9Sespie VOP_ABORTOP(fdvp, fcnp); 255746905cb9Sespie vrele(fdvp); 255846905cb9Sespie vrele(fvp); 255946905cb9Sespie } 2560f9ac7274Stedu 2561f9ac7274Stedu void filt_tmpfsdetach(struct knote *kn); 2562f9ac7274Stedu int filt_tmpfsread(struct knote *kn, long hint); 2563f9ac7274Stedu int filt_tmpfswrite(struct knote *kn, long hint); 2564f9ac7274Stedu int filt_tmpfsvnode(struct knote *kn, long hint); 2565f9ac7274Stedu 256694321eb4Svisa const struct filterops tmpfsread_filtops = { 2567b8213689Svisa .f_flags = FILTEROP_ISFD, 256894321eb4Svisa .f_attach = NULL, 256994321eb4Svisa .f_detach = filt_tmpfsdetach, 257094321eb4Svisa .f_event = filt_tmpfsread, 257194321eb4Svisa }; 257294321eb4Svisa 257394321eb4Svisa const struct filterops tmpfswrite_filtops = { 2574b8213689Svisa .f_flags = FILTEROP_ISFD, 257594321eb4Svisa .f_attach = NULL, 257694321eb4Svisa .f_detach = filt_tmpfsdetach, 257794321eb4Svisa .f_event = filt_tmpfswrite, 257894321eb4Svisa }; 257994321eb4Svisa 258094321eb4Svisa const struct filterops tmpfsvnode_filtops = { 2581b8213689Svisa .f_flags = FILTEROP_ISFD, 258294321eb4Svisa .f_attach = NULL, 258394321eb4Svisa .f_detach = filt_tmpfsdetach, 258494321eb4Svisa .f_event = filt_tmpfsvnode, 258594321eb4Svisa }; 2586f9ac7274Stedu 2587f9ac7274Stedu int 2588f9ac7274Stedu tmpfs_kqfilter(void *v) 2589f9ac7274Stedu { 2590f9ac7274Stedu struct vop_kqfilter_args *ap = v; 2591f9ac7274Stedu struct vnode *vp = ap->a_vp; 2592f9ac7274Stedu struct knote *kn = ap->a_kn; 2593f9ac7274Stedu 2594f9ac7274Stedu switch (kn->kn_filter) { 2595f9ac7274Stedu case EVFILT_READ: 2596f9ac7274Stedu kn->kn_fop = &tmpfsread_filtops; 2597f9ac7274Stedu break; 2598f9ac7274Stedu case EVFILT_WRITE: 2599f9ac7274Stedu kn->kn_fop = &tmpfswrite_filtops; 2600f9ac7274Stedu break; 2601f9ac7274Stedu case EVFILT_VNODE: 2602f9ac7274Stedu kn->kn_fop = &tmpfsvnode_filtops; 2603f9ac7274Stedu break; 2604f9ac7274Stedu default: 2605f9ac7274Stedu return (EINVAL); 2606f9ac7274Stedu } 2607f9ac7274Stedu 2608f9ac7274Stedu kn->kn_hook = (caddr_t)vp; 2609f9ac7274Stedu 2610cc53a24cSmvs klist_insert_locked(&vp->v_klist, kn); 2611f9ac7274Stedu 2612f9ac7274Stedu return (0); 2613f9ac7274Stedu } 2614f9ac7274Stedu 2615f9ac7274Stedu void 2616f9ac7274Stedu filt_tmpfsdetach(struct knote *kn) 2617f9ac7274Stedu { 2618f9ac7274Stedu struct vnode *vp = (struct vnode *)kn->kn_hook; 2619f9ac7274Stedu 2620cc53a24cSmvs klist_remove_locked(&vp->v_klist, kn); 2621f9ac7274Stedu } 2622f9ac7274Stedu 2623f9ac7274Stedu int 2624f9ac7274Stedu filt_tmpfsread(struct knote *kn, long hint) 2625f9ac7274Stedu { 2626f9ac7274Stedu struct vnode *vp = (struct vnode *)kn->kn_hook; 2627f9ac7274Stedu tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); 2628f9ac7274Stedu 2629f9ac7274Stedu /* 2630f9ac7274Stedu * filesystem is gone, so set the EOF flag and schedule 2631f9ac7274Stedu * the knote for deletion. 2632f9ac7274Stedu */ 2633f9ac7274Stedu if (hint == NOTE_REVOKE) { 2634f9ac7274Stedu kn->kn_flags |= (EV_EOF | EV_ONESHOT); 2635f9ac7274Stedu return (1); 2636f9ac7274Stedu } 2637f9ac7274Stedu 2638836f297bSanton kn->kn_data = node->tn_size - foffset(kn->kn_fp); 2639f9ac7274Stedu if (kn->kn_data == 0 && kn->kn_sfflags & NOTE_EOF) { 2640f9ac7274Stedu kn->kn_fflags |= NOTE_EOF; 2641f9ac7274Stedu return (1); 2642f9ac7274Stedu } 2643f9ac7274Stedu 26446ecc0d7fSvisa if (kn->kn_flags & (__EV_POLL | __EV_SELECT)) 26456e29a944Smpi return (1); 26466e29a944Smpi 2647f9ac7274Stedu return (kn->kn_data != 0); 2648f9ac7274Stedu } 2649f9ac7274Stedu 2650f9ac7274Stedu int 2651f9ac7274Stedu filt_tmpfswrite(struct knote *kn, long hint) 2652f9ac7274Stedu { 2653f9ac7274Stedu /* 2654f9ac7274Stedu * filesystem is gone, so set the EOF flag and schedule 2655f9ac7274Stedu * the knote for deletion. 2656f9ac7274Stedu */ 2657f9ac7274Stedu if (hint == NOTE_REVOKE) { 2658f9ac7274Stedu kn->kn_flags |= (EV_EOF | EV_ONESHOT); 2659f9ac7274Stedu return (1); 2660f9ac7274Stedu } 2661f9ac7274Stedu 2662f9ac7274Stedu kn->kn_data = 0; 2663f9ac7274Stedu return (1); 2664f9ac7274Stedu } 2665f9ac7274Stedu 2666f9ac7274Stedu int 2667f9ac7274Stedu filt_tmpfsvnode(struct knote *kn, long hint) 2668f9ac7274Stedu { 2669f9ac7274Stedu if (kn->kn_sfflags & hint) 2670f9ac7274Stedu kn->kn_fflags |= hint; 2671f9ac7274Stedu if (hint == NOTE_REVOKE) { 2672f9ac7274Stedu kn->kn_flags |= EV_EOF; 2673f9ac7274Stedu return (1); 2674f9ac7274Stedu } 2675f9ac7274Stedu return (kn->kn_fflags != 0); 2676f9ac7274Stedu } 2677