1d167cf6fSWarner Losh /*- 2df8bae1dSRodney W. Grimes * Copyright (c) 1994 Jan-Simon Pendry 3df8bae1dSRodney W. Grimes * Copyright (c) 1994 4df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 5d00947d8SCraig Rodrigues * Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc. 6d00947d8SCraig Rodrigues * Copyright (c) 2006 Daichi Goto <daichi@freebsd.org> 7df8bae1dSRodney W. Grimes * 8df8bae1dSRodney W. Grimes * This code is derived from software contributed to Berkeley by 9df8bae1dSRodney W. Grimes * Jan-Simon Pendry. 10df8bae1dSRodney W. Grimes * 11df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 12df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 13df8bae1dSRodney W. Grimes * are met: 14df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 15df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 16df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 17df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 18df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 19df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 20df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 21df8bae1dSRodney W. Grimes * without specific prior written permission. 22df8bae1dSRodney W. Grimes * 23df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33df8bae1dSRodney W. Grimes * SUCH DAMAGE. 34df8bae1dSRodney W. Grimes * 35996c772fSJohn Dyson * @(#)union_subr.c 8.20 (Berkeley) 5/20/95 36c3aac50fSPeter Wemm * $FreeBSD$ 37df8bae1dSRodney W. Grimes */ 38df8bae1dSRodney W. Grimes 39df8bae1dSRodney W. Grimes #include <sys/param.h> 40df8bae1dSRodney W. Grimes #include <sys/systm.h> 41fb919e4dSMark Murray #include <sys/kernel.h> 42fb919e4dSMark Murray #include <sys/lock.h> 432178ff8bSJohn Baldwin #include <sys/mutex.h> 44d00947d8SCraig Rodrigues #include <sys/malloc.h> 45d00947d8SCraig Rodrigues #include <sys/mount.h> 46fb919e4dSMark Murray #include <sys/namei.h> 47d00947d8SCraig Rodrigues #include <sys/proc.h> 48fb919e4dSMark Murray #include <sys/vnode.h> 49d00947d8SCraig Rodrigues #include <sys/dirent.h> 50d00947d8SCraig Rodrigues #include <sys/fcntl.h> 51d00947d8SCraig Rodrigues #include <sys/filedesc.h> 52d00947d8SCraig Rodrigues #include <sys/stat.h> 53d00947d8SCraig Rodrigues #include <sys/resourcevar.h> 54fb919e4dSMark Murray 55d00947d8SCraig Rodrigues #ifdef MAC 56d00947d8SCraig Rodrigues #include <sys/mac.h> 57d00947d8SCraig Rodrigues #endif 58d00947d8SCraig Rodrigues 598396dd9eSJeff Roberson #include <vm/uma.h> 60fb919e4dSMark Murray 6199d300a1SRuslan Ermilov #include <fs/unionfs/union.h> 62df8bae1dSRodney W. Grimes 63d00947d8SCraig Rodrigues #define NUNIONFSNODECACHE 32 64df8bae1dSRodney W. Grimes 65d00947d8SCraig Rodrigues #define UNIONFS_NHASH(upper, lower) \ 66d00947d8SCraig Rodrigues (&unionfs_node_hashtbl[(((uintptr_t)upper + (uintptr_t)lower) >> 8) & unionfs_node_hash]) 679b5e8b3aSBruce Evans 68d00947d8SCraig Rodrigues static LIST_HEAD(unionfs_node_hashhead, unionfs_node) *unionfs_node_hashtbl; 69d00947d8SCraig Rodrigues static u_long unionfs_node_hash; 70d00947d8SCraig Rodrigues struct mtx unionfs_hashmtx; 71df8bae1dSRodney W. Grimes 72d00947d8SCraig Rodrigues static MALLOC_DEFINE(M_UNIONFSHASH, "UNIONFS hash", "UNIONFS hash table"); 73d00947d8SCraig Rodrigues MALLOC_DEFINE(M_UNIONFSNODE, "UNIONFS node", "UNIONFS vnode private part"); 74d00947d8SCraig Rodrigues MALLOC_DEFINE(M_UNIONFSPATH, "UNIONFS path", "UNIONFS path private part"); 75df8bae1dSRodney W. Grimes 76d00947d8SCraig Rodrigues /* 77d00947d8SCraig Rodrigues * Initialize cache headers 78d00947d8SCraig Rodrigues */ 79df8bae1dSRodney W. Grimes int 80d00947d8SCraig Rodrigues unionfs_init(struct vfsconf *vfsp) 81df8bae1dSRodney W. Grimes { 82d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_init\n"); /* printed during system boot */ 83d00947d8SCraig Rodrigues unionfs_node_hashtbl = hashinit(NUNIONFSNODECACHE, M_UNIONFSHASH, &unionfs_node_hash); 84d00947d8SCraig Rodrigues mtx_init(&unionfs_hashmtx, "unionfs", NULL, MTX_DEF); 85df8bae1dSRodney W. Grimes 8626f9a767SRodney W. Grimes return (0); 87df8bae1dSRodney W. Grimes } 88df8bae1dSRodney W. Grimes 89d00947d8SCraig Rodrigues /* 90d00947d8SCraig Rodrigues * Destroy cache headers 91d00947d8SCraig Rodrigues */ 92d00947d8SCraig Rodrigues int 93d00947d8SCraig Rodrigues unionfs_uninit(struct vfsconf *vfsp) 94df8bae1dSRodney W. Grimes { 95d00947d8SCraig Rodrigues mtx_destroy(&unionfs_hashmtx); 96d00947d8SCraig Rodrigues free(unionfs_node_hashtbl, M_UNIONFSHASH); 97df8bae1dSRodney W. Grimes return (0); 98df8bae1dSRodney W. Grimes } 99df8bae1dSRodney W. Grimes 100d00947d8SCraig Rodrigues /* 101d00947d8SCraig Rodrigues * Return a VREF'ed alias for unionfs vnode if already exists, else 0. 102d00947d8SCraig Rodrigues */ 103d00947d8SCraig Rodrigues static struct vnode * 104d00947d8SCraig Rodrigues unionfs_hashget(struct mount *mp, struct vnode *uppervp, 105d00947d8SCraig Rodrigues struct vnode *lowervp, struct vnode *dvp, char *path, 106d00947d8SCraig Rodrigues int lkflags, struct thread *td) 107df8bae1dSRodney W. Grimes { 108d00947d8SCraig Rodrigues struct unionfs_node_hashhead *hd; 109d00947d8SCraig Rodrigues struct unionfs_node *unp; 110996c772fSJohn Dyson struct vnode *vp; 111996c772fSJohn Dyson 112d00947d8SCraig Rodrigues if (lkflags & LK_TYPE_MASK) 113d00947d8SCraig Rodrigues lkflags |= LK_RETRY; 114d00947d8SCraig Rodrigues hd = UNIONFS_NHASH(uppervp, lowervp); 115996c772fSJohn Dyson 116df8bae1dSRodney W. Grimes loop: 117d00947d8SCraig Rodrigues mtx_lock(&unionfs_hashmtx); 118d00947d8SCraig Rodrigues LIST_FOREACH(unp, hd, un_hash) { 119d00947d8SCraig Rodrigues if (unp->un_uppervp == uppervp && 120d00947d8SCraig Rodrigues unp->un_lowervp == lowervp && 121d00947d8SCraig Rodrigues unp->un_dvp == dvp && 122d00947d8SCraig Rodrigues UNIONFSTOV(unp)->v_mount == mp && 123d00947d8SCraig Rodrigues (!path || !(unp->un_path) || !strcmp(unp->un_path, path))) { 124d00947d8SCraig Rodrigues vp = UNIONFSTOV(unp); 125d00947d8SCraig Rodrigues VI_LOCK(vp); 126df8bae1dSRodney W. Grimes 127d00947d8SCraig Rodrigues /* 128d00947d8SCraig Rodrigues * If the unionfs node is being recycled we have to 129d00947d8SCraig Rodrigues * wait until it finishes prior to scanning again. 130d00947d8SCraig Rodrigues */ 131d00947d8SCraig Rodrigues mtx_unlock(&unionfs_hashmtx); 132d00947d8SCraig Rodrigues if (vp->v_iflag & VI_DOOMED) { 133d00947d8SCraig Rodrigues /* Wait for recycling to finish. */ 134d00947d8SCraig Rodrigues vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK, td); 135d00947d8SCraig Rodrigues VOP_UNLOCK(vp, 0, td); 136df8bae1dSRodney W. Grimes goto loop; 137df8bae1dSRodney W. Grimes } 138df8bae1dSRodney W. Grimes /* 139d00947d8SCraig Rodrigues * We need to clear the OWEINACT flag here as this 140d00947d8SCraig Rodrigues * may lead vget() to try to lock our vnode which is 141d00947d8SCraig Rodrigues * already locked via vp. 142d00947d8SCraig Rodrigues */ 143d00947d8SCraig Rodrigues vp->v_iflag &= ~VI_OWEINACT; 144d00947d8SCraig Rodrigues vget(vp, lkflags | LK_INTERLOCK, td); 145d00947d8SCraig Rodrigues 146d00947d8SCraig Rodrigues return (vp); 147d00947d8SCraig Rodrigues } 148d00947d8SCraig Rodrigues } 149d00947d8SCraig Rodrigues 150d00947d8SCraig Rodrigues mtx_unlock(&unionfs_hashmtx); 151d00947d8SCraig Rodrigues 152d00947d8SCraig Rodrigues return (NULLVP); 153d00947d8SCraig Rodrigues } 154d00947d8SCraig Rodrigues 155d00947d8SCraig Rodrigues /* 156d00947d8SCraig Rodrigues * Act like unionfs_hashget, but add passed unionfs_node to hash if no existing 157d00947d8SCraig Rodrigues * node found. 158d00947d8SCraig Rodrigues */ 159d00947d8SCraig Rodrigues static struct vnode * 160d00947d8SCraig Rodrigues unionfs_hashins(struct mount *mp, struct unionfs_node *uncp, 161d00947d8SCraig Rodrigues char *path, int lkflags, struct thread *td) 162d00947d8SCraig Rodrigues { 163d00947d8SCraig Rodrigues struct unionfs_node_hashhead *hd; 164d00947d8SCraig Rodrigues struct unionfs_node *unp; 165d00947d8SCraig Rodrigues struct vnode *vp; 166d00947d8SCraig Rodrigues 167d00947d8SCraig Rodrigues if (lkflags & LK_TYPE_MASK) 168d00947d8SCraig Rodrigues lkflags |= LK_RETRY; 169d00947d8SCraig Rodrigues hd = UNIONFS_NHASH(uncp->un_uppervp, uncp->un_lowervp); 170d00947d8SCraig Rodrigues 171d00947d8SCraig Rodrigues loop: 172d00947d8SCraig Rodrigues mtx_lock(&unionfs_hashmtx); 173d00947d8SCraig Rodrigues LIST_FOREACH(unp, hd, un_hash) { 174d00947d8SCraig Rodrigues if (unp->un_uppervp == uncp->un_uppervp && 175d00947d8SCraig Rodrigues unp->un_lowervp == uncp->un_lowervp && 176d00947d8SCraig Rodrigues unp->un_dvp == uncp->un_dvp && 177d00947d8SCraig Rodrigues UNIONFSTOV(unp)->v_mount == mp && 178d00947d8SCraig Rodrigues (!path || !(unp->un_path) || !strcmp(unp->un_path, path))) { 179d00947d8SCraig Rodrigues vp = UNIONFSTOV(unp); 180d00947d8SCraig Rodrigues VI_LOCK(vp); 181d00947d8SCraig Rodrigues 182d00947d8SCraig Rodrigues mtx_unlock(&unionfs_hashmtx); 183d00947d8SCraig Rodrigues if (vp->v_iflag & VI_DOOMED) { 184d00947d8SCraig Rodrigues /* Wait for recycling to finish. */ 185d00947d8SCraig Rodrigues vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK, td); 186d00947d8SCraig Rodrigues VOP_UNLOCK(vp, 0, td); 187d00947d8SCraig Rodrigues goto loop; 188d00947d8SCraig Rodrigues } 189d00947d8SCraig Rodrigues vp->v_iflag &= ~VI_OWEINACT; 190d00947d8SCraig Rodrigues vget(vp, lkflags | LK_INTERLOCK, td); 191d00947d8SCraig Rodrigues 192d00947d8SCraig Rodrigues return (vp); 193d00947d8SCraig Rodrigues } 194d00947d8SCraig Rodrigues } 195d00947d8SCraig Rodrigues 196d00947d8SCraig Rodrigues LIST_INSERT_HEAD(hd, uncp, un_hash); 197d00947d8SCraig Rodrigues uncp->un_flag |= UNIONFS_CACHED; 198d00947d8SCraig Rodrigues mtx_unlock(&unionfs_hashmtx); 199d00947d8SCraig Rodrigues 200d00947d8SCraig Rodrigues return (NULLVP); 201d00947d8SCraig Rodrigues } 202d00947d8SCraig Rodrigues 203d00947d8SCraig Rodrigues /* 204d00947d8SCraig Rodrigues * Make a new or get existing unionfs node. 2052a31267eSMatthew Dillon * 206d00947d8SCraig Rodrigues * uppervp and lowervp should be unlocked. Because if new unionfs vnode is 207d00947d8SCraig Rodrigues * locked, uppervp or lowervp is locked too. In order to prevent dead lock, 208d00947d8SCraig Rodrigues * you should not lock plurality simultaneously. 209df8bae1dSRodney W. Grimes */ 210d00947d8SCraig Rodrigues int 211d00947d8SCraig Rodrigues unionfs_nodeget(struct mount *mp, struct vnode *uppervp, 212d00947d8SCraig Rodrigues struct vnode *lowervp, struct vnode *dvp, 213d00947d8SCraig Rodrigues struct vnode **vpp, struct componentname *cnp, 214d00947d8SCraig Rodrigues struct thread *td) 215d00947d8SCraig Rodrigues { 216d00947d8SCraig Rodrigues struct unionfs_mount *ump; 217d00947d8SCraig Rodrigues struct unionfs_node *unp; 218d00947d8SCraig Rodrigues struct vnode *vp; 219d00947d8SCraig Rodrigues int error; 220d00947d8SCraig Rodrigues int lkflags; 221d00947d8SCraig Rodrigues char *path; 222df8bae1dSRodney W. Grimes 223d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 224d00947d8SCraig Rodrigues lkflags = (cnp ? cnp->cn_lkflags : 0); 225d00947d8SCraig Rodrigues path = (cnp ? cnp->cn_nameptr : ""); 2262a31267eSMatthew Dillon 227d00947d8SCraig Rodrigues if (uppervp == NULLVP && lowervp == NULLVP) 228d00947d8SCraig Rodrigues panic("unionfs_nodeget: upper and lower is null"); 229d00947d8SCraig Rodrigues 230d00947d8SCraig Rodrigues /* If it has no ISLASTCN flag, path check is skipped. */ 231d00947d8SCraig Rodrigues if (!cnp || !(cnp->cn_flags & ISLASTCN)) 232d00947d8SCraig Rodrigues path = NULL; 233d00947d8SCraig Rodrigues 234d00947d8SCraig Rodrigues /* Lookup the hash first. */ 235d00947d8SCraig Rodrigues *vpp = unionfs_hashget(mp, uppervp, lowervp, dvp, path, lkflags, td); 236d00947d8SCraig Rodrigues if (*vpp != NULLVP) 237d00947d8SCraig Rodrigues return (0); 238d00947d8SCraig Rodrigues 239d00947d8SCraig Rodrigues if ((uppervp == NULLVP || ump->um_uppervp != uppervp) || 240d00947d8SCraig Rodrigues (lowervp == NULLVP || ump->um_lowervp != lowervp)) { 241d00947d8SCraig Rodrigues if (dvp == NULLVP) 242d00947d8SCraig Rodrigues return (EINVAL); 243d00947d8SCraig Rodrigues } 244d00947d8SCraig Rodrigues 245df8bae1dSRodney W. Grimes /* 246d00947d8SCraig Rodrigues * Do the MALLOC before the getnewvnode since doing so afterward 247d00947d8SCraig Rodrigues * might cause a bogus v_data pointer to get dereferenced elsewhere 248d00947d8SCraig Rodrigues * if MALLOC should block. 249df8bae1dSRodney W. Grimes */ 250d00947d8SCraig Rodrigues MALLOC(unp, struct unionfs_node *, sizeof(struct unionfs_node), 251d00947d8SCraig Rodrigues M_UNIONFSNODE, M_WAITOK | M_ZERO); 252d00947d8SCraig Rodrigues 253d00947d8SCraig Rodrigues error = getnewvnode("unionfs", mp, &unionfs_vnodeops, &vp); 254d00947d8SCraig Rodrigues if (error) { 255d00947d8SCraig Rodrigues FREE(unp, M_UNIONFSNODE); 256d00947d8SCraig Rodrigues return (error); 257d00947d8SCraig Rodrigues } 258d00947d8SCraig Rodrigues if (dvp != NULLVP) 259d00947d8SCraig Rodrigues vref(dvp); 260d00947d8SCraig Rodrigues if (uppervp != NULLVP) 261d00947d8SCraig Rodrigues vref(uppervp); 262d00947d8SCraig Rodrigues if (lowervp != NULLVP) 263d00947d8SCraig Rodrigues vref(lowervp); 264d00947d8SCraig Rodrigues 265d00947d8SCraig Rodrigues unp->un_vnode = vp; 266d00947d8SCraig Rodrigues unp->un_uppervp = uppervp; 267d00947d8SCraig Rodrigues unp->un_lowervp = lowervp; 268d00947d8SCraig Rodrigues unp->un_dvp = dvp; 269d00947d8SCraig Rodrigues if (uppervp != NULLVP) 270d00947d8SCraig Rodrigues vp->v_vnlock = uppervp->v_vnlock; 271d00947d8SCraig Rodrigues else 272d00947d8SCraig Rodrigues vp->v_vnlock = lowervp->v_vnlock; 273d00947d8SCraig Rodrigues 274d00947d8SCraig Rodrigues if (cnp) { 275d00947d8SCraig Rodrigues unp->un_path = (char *) 276d00947d8SCraig Rodrigues malloc(cnp->cn_namelen +1, M_UNIONFSPATH, M_WAITOK | M_ZERO); 277d00947d8SCraig Rodrigues bcopy(cnp->cn_nameptr, unp->un_path, cnp->cn_namelen); 278d00947d8SCraig Rodrigues unp->un_path[cnp->cn_namelen] = '\0'; 279d00947d8SCraig Rodrigues } 280d00947d8SCraig Rodrigues vp->v_type = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type); 281d00947d8SCraig Rodrigues vp->v_data = unp; 282d00947d8SCraig Rodrigues 283d00947d8SCraig Rodrigues if ((uppervp != NULLVP && ump->um_uppervp == uppervp) && 284d00947d8SCraig Rodrigues (lowervp != NULLVP && ump->um_lowervp == lowervp)) 285d00947d8SCraig Rodrigues vp->v_vflag |= VV_ROOT; 286d00947d8SCraig Rodrigues 287d00947d8SCraig Rodrigues *vpp = unionfs_hashins(mp, unp, path, lkflags, td); 288d00947d8SCraig Rodrigues if (*vpp != NULLVP) { 289d00947d8SCraig Rodrigues if (dvp != NULLVP) 2902a31267eSMatthew Dillon vrele(dvp); 291d00947d8SCraig Rodrigues if (uppervp != NULLVP) 292df8bae1dSRodney W. Grimes vrele(uppervp); 293d00947d8SCraig Rodrigues if (lowervp != NULLVP) 294df8bae1dSRodney W. Grimes vrele(lowervp); 2952a31267eSMatthew Dillon 296d00947d8SCraig Rodrigues unp->un_uppervp = NULLVP; 297d00947d8SCraig Rodrigues unp->un_lowervp = NULLVP; 298d00947d8SCraig Rodrigues unp->un_dvp = NULLVP; 299d00947d8SCraig Rodrigues vrele(vp); 3002a31267eSMatthew Dillon 301df8bae1dSRodney W. Grimes return (0); 302df8bae1dSRodney W. Grimes } 303df8bae1dSRodney W. Grimes 304d00947d8SCraig Rodrigues if (lkflags & LK_TYPE_MASK) 305d00947d8SCraig Rodrigues vn_lock(vp, lkflags | LK_RETRY, td); 306df8bae1dSRodney W. Grimes 307d00947d8SCraig Rodrigues *vpp = vp; 308d00947d8SCraig Rodrigues 309d00947d8SCraig Rodrigues return (0); 310996c772fSJohn Dyson } 311df8bae1dSRodney W. Grimes 3122a31267eSMatthew Dillon /* 313d00947d8SCraig Rodrigues * Remove node from hash. 3142a31267eSMatthew Dillon */ 315d00947d8SCraig Rodrigues void 316d00947d8SCraig Rodrigues unionfs_hashrem(struct vnode *vp, struct thread *td) 317d00947d8SCraig Rodrigues { 318d00947d8SCraig Rodrigues int vfslocked; 319d00947d8SCraig Rodrigues struct unionfs_node *unp; 320d00947d8SCraig Rodrigues struct unionfs_node_status *unsp; 321d00947d8SCraig Rodrigues struct vnode *lvp; 322d00947d8SCraig Rodrigues struct vnode *uvp; 3232a31267eSMatthew Dillon 3242a31267eSMatthew Dillon /* 325d00947d8SCraig Rodrigues * Use the interlock to protect the clearing of v_data to 326d00947d8SCraig Rodrigues * prevent faults in unionfs_lock(). 3272a31267eSMatthew Dillon */ 328d00947d8SCraig Rodrigues VI_LOCK(vp); 329d00947d8SCraig Rodrigues unp = VTOUNIONFS(vp); 330d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 331d00947d8SCraig Rodrigues uvp = unp->un_uppervp; 332d00947d8SCraig Rodrigues unp->un_lowervp = unp->un_uppervp = NULLVP; 333d00947d8SCraig Rodrigues 334d00947d8SCraig Rodrigues vp->v_vnlock = &(vp->v_lock); 335d00947d8SCraig Rodrigues vp->v_data = NULL; 336d00947d8SCraig Rodrigues lockmgr(vp->v_vnlock, LK_EXCLUSIVE | LK_INTERLOCK, VI_MTX(vp), td); 337d00947d8SCraig Rodrigues if (lvp != NULLVP) 338d00947d8SCraig Rodrigues VOP_UNLOCK(lvp, 0, td); 339d00947d8SCraig Rodrigues if (uvp != NULLVP) 340d00947d8SCraig Rodrigues VOP_UNLOCK(uvp, 0, td); 341d00947d8SCraig Rodrigues 342d00947d8SCraig Rodrigues mtx_lock(&unionfs_hashmtx); 343d00947d8SCraig Rodrigues if (unp->un_flag & UNIONFS_CACHED) { 344d00947d8SCraig Rodrigues LIST_REMOVE(unp, un_hash); 345d00947d8SCraig Rodrigues unp->un_flag &= ~UNIONFS_CACHED; 346d00947d8SCraig Rodrigues } 347d00947d8SCraig Rodrigues mtx_unlock(&unionfs_hashmtx); 348d00947d8SCraig Rodrigues vp->v_object = NULL; 349d00947d8SCraig Rodrigues 350d00947d8SCraig Rodrigues if (lvp != NULLVP) { 351d00947d8SCraig Rodrigues vfslocked = VFS_LOCK_GIANT(lvp->v_mount); 352d00947d8SCraig Rodrigues vrele(lvp); 353d00947d8SCraig Rodrigues VFS_UNLOCK_GIANT(vfslocked); 354d00947d8SCraig Rodrigues } 355d00947d8SCraig Rodrigues if (uvp != NULLVP) { 356d00947d8SCraig Rodrigues vfslocked = VFS_LOCK_GIANT(uvp->v_mount); 357d00947d8SCraig Rodrigues vrele(uvp); 358d00947d8SCraig Rodrigues VFS_UNLOCK_GIANT(vfslocked); 359d00947d8SCraig Rodrigues } 360d00947d8SCraig Rodrigues if (unp->un_dvp != NULLVP) { 361d00947d8SCraig Rodrigues vfslocked = VFS_LOCK_GIANT(unp->un_dvp->v_mount); 362d00947d8SCraig Rodrigues vrele(unp->un_dvp); 363d00947d8SCraig Rodrigues VFS_UNLOCK_GIANT(vfslocked); 364d00947d8SCraig Rodrigues unp->un_dvp = NULLVP; 365d00947d8SCraig Rodrigues } 366d00947d8SCraig Rodrigues if (unp->un_path) { 367d00947d8SCraig Rodrigues free(unp->un_path, M_UNIONFSPATH); 368d00947d8SCraig Rodrigues unp->un_path = NULL; 369d00947d8SCraig Rodrigues } 370d00947d8SCraig Rodrigues while ((unsp = LIST_FIRST(&(unp->un_unshead))), NULL != unsp) { 371d00947d8SCraig Rodrigues LIST_REMOVE(unsp, uns_list); 372d00947d8SCraig Rodrigues free(unsp, M_TEMP); 373d00947d8SCraig Rodrigues } 374d00947d8SCraig Rodrigues FREE(unp, M_UNIONFSNODE); 375df8bae1dSRodney W. Grimes } 376df8bae1dSRodney W. Grimes 377d00947d8SCraig Rodrigues /* 378d00947d8SCraig Rodrigues * Get the unionfs node status. 379d00947d8SCraig Rodrigues * You need exclusive lock this vnode. 380d00947d8SCraig Rodrigues */ 381d00947d8SCraig Rodrigues void 382d00947d8SCraig Rodrigues unionfs_get_node_status(struct unionfs_node *unp, struct thread *td, 383d00947d8SCraig Rodrigues struct unionfs_node_status **unspp) 384d00947d8SCraig Rodrigues { 385d00947d8SCraig Rodrigues struct unionfs_node_status *unsp; 386df8bae1dSRodney W. Grimes 387d00947d8SCraig Rodrigues KASSERT(NULL != unspp, ("null pointer")); 388d00947d8SCraig Rodrigues ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status"); 3892a31267eSMatthew Dillon 390d00947d8SCraig Rodrigues LIST_FOREACH(unsp, &(unp->un_unshead), uns_list) { 391d00947d8SCraig Rodrigues if (unsp->uns_tid == td->td_tid) { 392d00947d8SCraig Rodrigues *unspp = unsp; 393d00947d8SCraig Rodrigues return; 394d00947d8SCraig Rodrigues } 395d00947d8SCraig Rodrigues } 3962a31267eSMatthew Dillon 397d00947d8SCraig Rodrigues /* create a new unionfs node status */ 398d00947d8SCraig Rodrigues MALLOC(unsp, struct unionfs_node_status *, 399d00947d8SCraig Rodrigues sizeof(struct unionfs_node_status), M_TEMP, M_WAITOK | M_ZERO); 4002a31267eSMatthew Dillon 401d00947d8SCraig Rodrigues unsp->uns_tid = td->td_tid; 402d00947d8SCraig Rodrigues LIST_INSERT_HEAD(&(unp->un_unshead), unsp, uns_list); 4032a31267eSMatthew Dillon 404d00947d8SCraig Rodrigues *unspp = unsp; 405d00947d8SCraig Rodrigues } 406d00947d8SCraig Rodrigues 407d00947d8SCraig Rodrigues /* 408d00947d8SCraig Rodrigues * Remove the unionfs node status, if you can. 409d00947d8SCraig Rodrigues * You need exclusive lock this vnode. 410d00947d8SCraig Rodrigues */ 411d00947d8SCraig Rodrigues void 412d00947d8SCraig Rodrigues unionfs_tryrem_node_status(struct unionfs_node *unp, struct thread *td, 413d00947d8SCraig Rodrigues struct unionfs_node_status *unsp) 414d00947d8SCraig Rodrigues { 415d00947d8SCraig Rodrigues KASSERT(NULL != unsp, ("null pointer")); 416d00947d8SCraig Rodrigues ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status"); 417d00947d8SCraig Rodrigues 418d00947d8SCraig Rodrigues if (0 < unsp->uns_lower_opencnt || 0 < unsp->uns_upper_opencnt) 419d00947d8SCraig Rodrigues return; 420d00947d8SCraig Rodrigues 421d00947d8SCraig Rodrigues LIST_REMOVE(unsp, uns_list); 422d00947d8SCraig Rodrigues free(unsp, M_TEMP); 423d00947d8SCraig Rodrigues } 424d00947d8SCraig Rodrigues 425d00947d8SCraig Rodrigues /* 426d00947d8SCraig Rodrigues * Create upper node attr. 427d00947d8SCraig Rodrigues */ 428d00947d8SCraig Rodrigues void 429d00947d8SCraig Rodrigues unionfs_create_uppervattr_core(struct unionfs_mount *ump, 430d00947d8SCraig Rodrigues struct vattr *lva, 431d00947d8SCraig Rodrigues struct vattr *uva, 432d00947d8SCraig Rodrigues struct thread *td) 433d00947d8SCraig Rodrigues { 434d00947d8SCraig Rodrigues VATTR_NULL(uva); 435d00947d8SCraig Rodrigues uva->va_type = lva->va_type; 436d00947d8SCraig Rodrigues uva->va_atime = lva->va_atime; 437d00947d8SCraig Rodrigues uva->va_mtime = lva->va_mtime; 438d00947d8SCraig Rodrigues uva->va_ctime = lva->va_ctime; 439d00947d8SCraig Rodrigues 440d00947d8SCraig Rodrigues switch (ump->um_copymode) { 441d00947d8SCraig Rodrigues case UNIONFS_TRANSPARENT: 442d00947d8SCraig Rodrigues uva->va_mode = lva->va_mode; 443d00947d8SCraig Rodrigues uva->va_uid = lva->va_uid; 444d00947d8SCraig Rodrigues uva->va_gid = lva->va_gid; 445d00947d8SCraig Rodrigues break; 446d00947d8SCraig Rodrigues case UNIONFS_MASQUERADE: 447d00947d8SCraig Rodrigues if (ump->um_uid == lva->va_uid) { 448d00947d8SCraig Rodrigues uva->va_mode = lva->va_mode & 077077; 449d00947d8SCraig Rodrigues uva->va_mode |= (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile) & 0700; 450d00947d8SCraig Rodrigues uva->va_uid = lva->va_uid; 451d00947d8SCraig Rodrigues uva->va_gid = lva->va_gid; 452df8bae1dSRodney W. Grimes } else { 453d00947d8SCraig Rodrigues uva->va_mode = (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile); 454d00947d8SCraig Rodrigues uva->va_uid = ump->um_uid; 455d00947d8SCraig Rodrigues uva->va_gid = ump->um_gid; 456d00947d8SCraig Rodrigues } 457d00947d8SCraig Rodrigues break; 458d00947d8SCraig Rodrigues default: /* UNIONFS_TRADITIONAL */ 459d00947d8SCraig Rodrigues FILEDESC_LOCK_FAST(td->td_proc->p_fd); 460d00947d8SCraig Rodrigues uva->va_mode = 0777 & ~td->td_proc->p_fd->fd_cmask; 461d00947d8SCraig Rodrigues FILEDESC_UNLOCK_FAST(td->td_proc->p_fd); 462d00947d8SCraig Rodrigues uva->va_uid = ump->um_uid; 463d00947d8SCraig Rodrigues uva->va_gid = ump->um_gid; 464d00947d8SCraig Rodrigues break; 465d00947d8SCraig Rodrigues } 466df8bae1dSRodney W. Grimes } 467df8bae1dSRodney W. Grimes 468d00947d8SCraig Rodrigues /* 469d00947d8SCraig Rodrigues * Create upper node attr. 470d00947d8SCraig Rodrigues */ 471d00947d8SCraig Rodrigues int 472d00947d8SCraig Rodrigues unionfs_create_uppervattr(struct unionfs_mount *ump, 473d00947d8SCraig Rodrigues struct vnode *lvp, 474d00947d8SCraig Rodrigues struct vattr *uva, 475d00947d8SCraig Rodrigues struct ucred *cred, 476d00947d8SCraig Rodrigues struct thread *td) 477d00947d8SCraig Rodrigues { 478d00947d8SCraig Rodrigues int error; 479d00947d8SCraig Rodrigues struct vattr lva; 480df8bae1dSRodney W. Grimes 481d00947d8SCraig Rodrigues if ((error = VOP_GETATTR(lvp, &lva, cred, td))) 482d00947d8SCraig Rodrigues return (error); 483d00947d8SCraig Rodrigues 484d00947d8SCraig Rodrigues unionfs_create_uppervattr_core(ump, &lva, uva, td); 485df8bae1dSRodney W. Grimes 486df8bae1dSRodney W. Grimes return (error); 487df8bae1dSRodney W. Grimes } 488df8bae1dSRodney W. Grimes 489d00947d8SCraig Rodrigues /* 490d00947d8SCraig Rodrigues * relookup 491d00947d8SCraig Rodrigues * 492d00947d8SCraig Rodrigues * dvp should be locked on entry and will be locked on return. 493d00947d8SCraig Rodrigues * 494d00947d8SCraig Rodrigues * If an error is returned, *vpp will be invalid, otherwise it will hold a 495d00947d8SCraig Rodrigues * locked, referenced vnode. If *vpp == dvp then remember that only one 496d00947d8SCraig Rodrigues * LK_EXCLUSIVE lock is held. 497d00947d8SCraig Rodrigues */ 498d00947d8SCraig Rodrigues static int 499d00947d8SCraig Rodrigues unionfs_relookup(struct vnode *dvp, struct vnode **vpp, 500d00947d8SCraig Rodrigues struct componentname *cnp, struct componentname *cn, 501d00947d8SCraig Rodrigues struct thread *td, char *path, int pathlen, u_long nameiop) 502df8bae1dSRodney W. Grimes { 503d00947d8SCraig Rodrigues int error; 504df8bae1dSRodney W. Grimes 505d00947d8SCraig Rodrigues cn->cn_namelen = pathlen; 506d00947d8SCraig Rodrigues cn->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); 507d00947d8SCraig Rodrigues bcopy(path, cn->cn_pnbuf, pathlen); 508d00947d8SCraig Rodrigues cn->cn_pnbuf[pathlen] = '\0'; 509df8bae1dSRodney W. Grimes 510d00947d8SCraig Rodrigues cn->cn_nameiop = nameiop; 511d00947d8SCraig Rodrigues cn->cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN); 512d00947d8SCraig Rodrigues cn->cn_lkflags = LK_EXCLUSIVE; 513d00947d8SCraig Rodrigues cn->cn_thread = td; 514d00947d8SCraig Rodrigues cn->cn_cred = cnp->cn_cred; 515df8bae1dSRodney W. Grimes 516d00947d8SCraig Rodrigues cn->cn_nameptr = cn->cn_pnbuf; 517d00947d8SCraig Rodrigues cn->cn_consume = cnp->cn_consume; 518df8bae1dSRodney W. Grimes 519d00947d8SCraig Rodrigues if (nameiop == DELETE) 520d00947d8SCraig Rodrigues cn->cn_flags |= (cnp->cn_flags & (DOWHITEOUT | SAVESTART)); 521d00947d8SCraig Rodrigues else if (RENAME == nameiop) 522d00947d8SCraig Rodrigues cn->cn_flags |= (cnp->cn_flags & SAVESTART); 523d00947d8SCraig Rodrigues 524d00947d8SCraig Rodrigues vref(dvp); 525d00947d8SCraig Rodrigues VOP_UNLOCK(dvp, 0, td); 526d00947d8SCraig Rodrigues 527d00947d8SCraig Rodrigues if ((error = relookup(dvp, vpp, cn))) { 528d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn->cn_pnbuf); 529d00947d8SCraig Rodrigues cn->cn_flags &= ~HASBUF; 530d00947d8SCraig Rodrigues vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); 531d00947d8SCraig Rodrigues } else 532d00947d8SCraig Rodrigues vrele(dvp); 533d00947d8SCraig Rodrigues 534d00947d8SCraig Rodrigues return (error); 535df8bae1dSRodney W. Grimes } 536df8bae1dSRodney W. Grimes 537df8bae1dSRodney W. Grimes /* 538d00947d8SCraig Rodrigues * relookup for CREATE namei operation. 5392a31267eSMatthew Dillon * 540d00947d8SCraig Rodrigues * dvp is unionfs vnode. dvp should be locked. 541d00947d8SCraig Rodrigues * 542d00947d8SCraig Rodrigues * If it called 'unionfs_copyfile' function by unionfs_link etc, 543d00947d8SCraig Rodrigues * VOP_LOOKUP information is broken. 544d00947d8SCraig Rodrigues * So it need relookup in order to create link etc. 545d00947d8SCraig Rodrigues */ 546d00947d8SCraig Rodrigues int 547d00947d8SCraig Rodrigues unionfs_relookup_for_create(struct vnode *dvp, struct componentname *cnp, 548d00947d8SCraig Rodrigues struct thread *td) 549d00947d8SCraig Rodrigues { 550d00947d8SCraig Rodrigues int error; 551d00947d8SCraig Rodrigues struct vnode *udvp; 552d00947d8SCraig Rodrigues struct vnode *vp; 553d00947d8SCraig Rodrigues struct componentname cn; 554d00947d8SCraig Rodrigues 555d00947d8SCraig Rodrigues udvp = UNIONFSVPTOUPPERVP(dvp); 556d00947d8SCraig Rodrigues vp = NULLVP; 557d00947d8SCraig Rodrigues 558d00947d8SCraig Rodrigues error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr, 559d00947d8SCraig Rodrigues strlen(cnp->cn_nameptr), CREATE); 560d00947d8SCraig Rodrigues if (error) 561d00947d8SCraig Rodrigues return (error); 562d00947d8SCraig Rodrigues 563d00947d8SCraig Rodrigues if (vp != NULLVP) { 564d00947d8SCraig Rodrigues if (udvp == vp) 565d00947d8SCraig Rodrigues vrele(vp); 566d00947d8SCraig Rodrigues else 567d00947d8SCraig Rodrigues vput(vp); 568d00947d8SCraig Rodrigues 569d00947d8SCraig Rodrigues error = EEXIST; 570d00947d8SCraig Rodrigues } 571d00947d8SCraig Rodrigues 572d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 573d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 574d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 575d00947d8SCraig Rodrigues } 576d00947d8SCraig Rodrigues 577d00947d8SCraig Rodrigues if (!error) { 578d00947d8SCraig Rodrigues cn.cn_flags |= (cnp->cn_flags & HASBUF); 579d00947d8SCraig Rodrigues cnp->cn_flags = cn.cn_flags; 580d00947d8SCraig Rodrigues } 581d00947d8SCraig Rodrigues 582d00947d8SCraig Rodrigues return (error); 583d00947d8SCraig Rodrigues } 584d00947d8SCraig Rodrigues 585d00947d8SCraig Rodrigues /* 586d00947d8SCraig Rodrigues * relookup for DELETE namei operation. 587d00947d8SCraig Rodrigues * 588d00947d8SCraig Rodrigues * dvp is unionfs vnode. dvp should be locked. 589d00947d8SCraig Rodrigues */ 590d00947d8SCraig Rodrigues int 591d00947d8SCraig Rodrigues unionfs_relookup_for_delete(struct vnode *dvp, struct componentname *cnp, 592d00947d8SCraig Rodrigues struct thread *td) 593d00947d8SCraig Rodrigues { 594d00947d8SCraig Rodrigues int error; 595d00947d8SCraig Rodrigues struct vnode *udvp; 596d00947d8SCraig Rodrigues struct vnode *vp; 597d00947d8SCraig Rodrigues struct componentname cn; 598d00947d8SCraig Rodrigues 599d00947d8SCraig Rodrigues udvp = UNIONFSVPTOUPPERVP(dvp); 600d00947d8SCraig Rodrigues vp = NULLVP; 601d00947d8SCraig Rodrigues 602d00947d8SCraig Rodrigues error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr, 603d00947d8SCraig Rodrigues strlen(cnp->cn_nameptr), DELETE); 604d00947d8SCraig Rodrigues if (error) 605d00947d8SCraig Rodrigues return (error); 606d00947d8SCraig Rodrigues 607d00947d8SCraig Rodrigues if (vp == NULLVP) 608d00947d8SCraig Rodrigues error = ENOENT; 609d00947d8SCraig Rodrigues else { 610d00947d8SCraig Rodrigues if (udvp == vp) 611d00947d8SCraig Rodrigues vrele(vp); 612d00947d8SCraig Rodrigues else 613d00947d8SCraig Rodrigues vput(vp); 614d00947d8SCraig Rodrigues } 615d00947d8SCraig Rodrigues 616d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 617d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 618d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 619d00947d8SCraig Rodrigues } 620d00947d8SCraig Rodrigues 621d00947d8SCraig Rodrigues if (!error) { 622d00947d8SCraig Rodrigues cn.cn_flags |= (cnp->cn_flags & HASBUF); 623d00947d8SCraig Rodrigues cnp->cn_flags = cn.cn_flags; 624d00947d8SCraig Rodrigues } 625d00947d8SCraig Rodrigues 626d00947d8SCraig Rodrigues return (error); 627d00947d8SCraig Rodrigues } 628d00947d8SCraig Rodrigues 629d00947d8SCraig Rodrigues /* 630d00947d8SCraig Rodrigues * relookup for RENAME namei operation. 631d00947d8SCraig Rodrigues * 632d00947d8SCraig Rodrigues * dvp is unionfs vnode. dvp should be locked. 633d00947d8SCraig Rodrigues */ 634d00947d8SCraig Rodrigues int 635d00947d8SCraig Rodrigues unionfs_relookup_for_rename(struct vnode *dvp, struct componentname *cnp, 636d00947d8SCraig Rodrigues struct thread *td) 637d00947d8SCraig Rodrigues { 638d00947d8SCraig Rodrigues int error; 639d00947d8SCraig Rodrigues struct vnode *udvp; 640d00947d8SCraig Rodrigues struct vnode *vp; 641d00947d8SCraig Rodrigues struct componentname cn; 642d00947d8SCraig Rodrigues 643d00947d8SCraig Rodrigues udvp = UNIONFSVPTOUPPERVP(dvp); 644d00947d8SCraig Rodrigues vp = NULLVP; 645d00947d8SCraig Rodrigues 646d00947d8SCraig Rodrigues error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr, 647d00947d8SCraig Rodrigues strlen(cnp->cn_nameptr), RENAME); 648d00947d8SCraig Rodrigues if (error) 649d00947d8SCraig Rodrigues return (error); 650d00947d8SCraig Rodrigues 651d00947d8SCraig Rodrigues if (vp != NULLVP) { 652d00947d8SCraig Rodrigues if (udvp == vp) 653d00947d8SCraig Rodrigues vrele(vp); 654d00947d8SCraig Rodrigues else 655d00947d8SCraig Rodrigues vput(vp); 656d00947d8SCraig Rodrigues } 657d00947d8SCraig Rodrigues 658d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 659d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 660d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 661d00947d8SCraig Rodrigues } 662d00947d8SCraig Rodrigues 663d00947d8SCraig Rodrigues if (!error) { 664d00947d8SCraig Rodrigues cn.cn_flags |= (cnp->cn_flags & HASBUF); 665d00947d8SCraig Rodrigues cnp->cn_flags = cn.cn_flags; 666d00947d8SCraig Rodrigues } 667d00947d8SCraig Rodrigues 668d00947d8SCraig Rodrigues return (error); 669d00947d8SCraig Rodrigues 670d00947d8SCraig Rodrigues } 671d00947d8SCraig Rodrigues 672d00947d8SCraig Rodrigues /* 673d00947d8SCraig Rodrigues * Update the unionfs_node. 674d00947d8SCraig Rodrigues * 675d00947d8SCraig Rodrigues * uvp is new locked upper vnode. unionfs vnode's lock will be exchanged to the 676d00947d8SCraig Rodrigues * uvp's lock and lower's lock will be unlocked. 677d00947d8SCraig Rodrigues */ 678d00947d8SCraig Rodrigues static void 679d00947d8SCraig Rodrigues unionfs_node_update(struct unionfs_node *unp, struct vnode *uvp, 680d00947d8SCraig Rodrigues struct thread *td) 681d00947d8SCraig Rodrigues { 682d00947d8SCraig Rodrigues int count, lockcnt; 683d00947d8SCraig Rodrigues struct vnode *vp; 684d00947d8SCraig Rodrigues struct vnode *lvp; 685d00947d8SCraig Rodrigues 686d00947d8SCraig Rodrigues vp = UNIONFSTOV(unp); 687d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 688d00947d8SCraig Rodrigues 689d00947d8SCraig Rodrigues /* 690d00947d8SCraig Rodrigues * lock update 691d00947d8SCraig Rodrigues */ 692d00947d8SCraig Rodrigues VI_LOCK(vp); 693d00947d8SCraig Rodrigues unp->un_uppervp = uvp; 694d00947d8SCraig Rodrigues vp->v_vnlock = uvp->v_vnlock; 695d00947d8SCraig Rodrigues lockcnt = lvp->v_vnlock->lk_exclusivecount; 696d00947d8SCraig Rodrigues if (lockcnt <= 0) 697d00947d8SCraig Rodrigues panic("unionfs: no exclusive lock"); 698d00947d8SCraig Rodrigues VI_UNLOCK(vp); 699d00947d8SCraig Rodrigues for (count = 1; count < lockcnt; count++) 700d00947d8SCraig Rodrigues vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY, td); 701d00947d8SCraig Rodrigues 702d00947d8SCraig Rodrigues /* 703d00947d8SCraig Rodrigues * cache update 704d00947d8SCraig Rodrigues */ 705d00947d8SCraig Rodrigues mtx_lock(&unionfs_hashmtx); 706d00947d8SCraig Rodrigues if (unp->un_flag & UNIONFS_CACHED) 707d00947d8SCraig Rodrigues LIST_REMOVE(unp, un_hash); 708d00947d8SCraig Rodrigues LIST_INSERT_HEAD(UNIONFS_NHASH(uvp, lvp), unp, un_hash); 709d00947d8SCraig Rodrigues unp->un_flag |= UNIONFS_CACHED; 710d00947d8SCraig Rodrigues mtx_unlock(&unionfs_hashmtx); 711d00947d8SCraig Rodrigues } 712d00947d8SCraig Rodrigues 713d00947d8SCraig Rodrigues /* 714d00947d8SCraig Rodrigues * Create a new shadow dir. 715d00947d8SCraig Rodrigues * 716d00947d8SCraig Rodrigues * udvp should be locked on entry and will be locked on return. 717d00947d8SCraig Rodrigues * 718d00947d8SCraig Rodrigues * If no error returned, unp will be updated. 719d00947d8SCraig Rodrigues */ 720d00947d8SCraig Rodrigues int 721d00947d8SCraig Rodrigues unionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *udvp, 722d00947d8SCraig Rodrigues struct unionfs_node *unp, struct componentname *cnp, 723d00947d8SCraig Rodrigues struct thread *td) 724d00947d8SCraig Rodrigues { 725d00947d8SCraig Rodrigues int error; 726d00947d8SCraig Rodrigues struct vnode *lvp; 727d00947d8SCraig Rodrigues struct vnode *uvp; 728d00947d8SCraig Rodrigues struct vattr va; 729d00947d8SCraig Rodrigues struct vattr lva; 730d00947d8SCraig Rodrigues struct componentname cn; 731d00947d8SCraig Rodrigues struct mount *mp; 732d00947d8SCraig Rodrigues struct ucred *cred; 733d00947d8SCraig Rodrigues struct ucred *credbk; 734d00947d8SCraig Rodrigues struct uidinfo *rootinfo; 735d00947d8SCraig Rodrigues 736d00947d8SCraig Rodrigues if (unp->un_uppervp != NULLVP) 737d00947d8SCraig Rodrigues return (EEXIST); 738d00947d8SCraig Rodrigues 739d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 740d00947d8SCraig Rodrigues uvp = NULLVP; 741d00947d8SCraig Rodrigues credbk = cnp->cn_cred; 742d00947d8SCraig Rodrigues 743d00947d8SCraig Rodrigues /* Authority change to root */ 744d00947d8SCraig Rodrigues rootinfo = uifind((uid_t)0); 745d00947d8SCraig Rodrigues cred = crdup(cnp->cn_cred); 746d00947d8SCraig Rodrigues chgproccnt(cred->cr_ruidinfo, 1, 0); 747d00947d8SCraig Rodrigues change_euid(cred, rootinfo); 748d00947d8SCraig Rodrigues change_ruid(cred, rootinfo); 749d00947d8SCraig Rodrigues change_svuid(cred, (uid_t)0); 750d00947d8SCraig Rodrigues uifree(rootinfo); 751d00947d8SCraig Rodrigues cnp->cn_cred = cred; 752d00947d8SCraig Rodrigues 753d00947d8SCraig Rodrigues memset(&cn, 0, sizeof(cn)); 754d00947d8SCraig Rodrigues 755d00947d8SCraig Rodrigues if ((error = VOP_GETATTR(lvp, &lva, cnp->cn_cred, td))) 756d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_abort; 757d00947d8SCraig Rodrigues 758d00947d8SCraig Rodrigues if ((error = unionfs_relookup(udvp, &uvp, cnp, &cn, td, cnp->cn_nameptr, cnp->cn_namelen, CREATE))) 759d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_abort; 760d00947d8SCraig Rodrigues if (uvp != NULLVP) { 761d00947d8SCraig Rodrigues if (udvp == uvp) 762d00947d8SCraig Rodrigues vrele(uvp); 763d00947d8SCraig Rodrigues else 764d00947d8SCraig Rodrigues vput(uvp); 765d00947d8SCraig Rodrigues 766d00947d8SCraig Rodrigues error = EEXIST; 767d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_free_out; 768d00947d8SCraig Rodrigues } 769d00947d8SCraig Rodrigues 770d00947d8SCraig Rodrigues if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH))) 771d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_free_out; 772d00947d8SCraig Rodrigues if ((error = VOP_LEASE(udvp, td, cn.cn_cred, LEASE_WRITE))) { 773d00947d8SCraig Rodrigues vn_finished_write(mp); 774d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_free_out; 775d00947d8SCraig Rodrigues } 776d00947d8SCraig Rodrigues unionfs_create_uppervattr_core(ump, &lva, &va, td); 777d00947d8SCraig Rodrigues 778d00947d8SCraig Rodrigues error = VOP_MKDIR(udvp, &uvp, &cn, &va); 779d00947d8SCraig Rodrigues 780d00947d8SCraig Rodrigues if (!error) { 781d00947d8SCraig Rodrigues unionfs_node_update(unp, uvp, td); 782d00947d8SCraig Rodrigues 783d00947d8SCraig Rodrigues /* 784d00947d8SCraig Rodrigues * XXX The bug which cannot set uid/gid was corrected. 785d00947d8SCraig Rodrigues * Ignore errors. 786d00947d8SCraig Rodrigues */ 787d00947d8SCraig Rodrigues va.va_type = VNON; 788d00947d8SCraig Rodrigues VOP_SETATTR(uvp, &va, cn.cn_cred, td); 789d00947d8SCraig Rodrigues } 790d00947d8SCraig Rodrigues vn_finished_write(mp); 791d00947d8SCraig Rodrigues 792d00947d8SCraig Rodrigues unionfs_mkshadowdir_free_out: 793d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 794d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 795d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 796d00947d8SCraig Rodrigues } 797d00947d8SCraig Rodrigues 798d00947d8SCraig Rodrigues unionfs_mkshadowdir_abort: 799d00947d8SCraig Rodrigues cnp->cn_cred = credbk; 800d00947d8SCraig Rodrigues chgproccnt(cred->cr_ruidinfo, -1, 0); 801d00947d8SCraig Rodrigues crfree(cred); 802d00947d8SCraig Rodrigues 803d00947d8SCraig Rodrigues return (error); 804d00947d8SCraig Rodrigues } 805d00947d8SCraig Rodrigues 806d00947d8SCraig Rodrigues /* 807d00947d8SCraig Rodrigues * Create a new whiteout. 808d00947d8SCraig Rodrigues * 809d00947d8SCraig Rodrigues * dvp should be locked on entry and will be locked on return. 810d00947d8SCraig Rodrigues */ 811d00947d8SCraig Rodrigues int 812d00947d8SCraig Rodrigues unionfs_mkwhiteout(struct vnode *dvp, struct componentname *cnp, 813d00947d8SCraig Rodrigues struct thread *td, char *path) 814d00947d8SCraig Rodrigues { 815d00947d8SCraig Rodrigues int error; 816d00947d8SCraig Rodrigues struct vnode *wvp; 817d00947d8SCraig Rodrigues struct componentname cn; 818d00947d8SCraig Rodrigues struct mount *mp; 819d00947d8SCraig Rodrigues 820d00947d8SCraig Rodrigues if (path == NULL) 821d00947d8SCraig Rodrigues path = cnp->cn_nameptr; 822d00947d8SCraig Rodrigues 823d00947d8SCraig Rodrigues wvp = NULLVP; 824d00947d8SCraig Rodrigues if ((error = unionfs_relookup(dvp, &wvp, cnp, &cn, td, path, strlen(path), CREATE))) 825d00947d8SCraig Rodrigues return (error); 826d00947d8SCraig Rodrigues if (wvp != NULLVP) { 827d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 828d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 829d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 830d00947d8SCraig Rodrigues } 831d00947d8SCraig Rodrigues if (dvp == wvp) 832d00947d8SCraig Rodrigues vrele(wvp); 833d00947d8SCraig Rodrigues else 834d00947d8SCraig Rodrigues vput(wvp); 835d00947d8SCraig Rodrigues 836d00947d8SCraig Rodrigues return (EEXIST); 837d00947d8SCraig Rodrigues } 838d00947d8SCraig Rodrigues 839d00947d8SCraig Rodrigues if ((error = vn_start_write(dvp, &mp, V_WAIT | PCATCH))) 840d00947d8SCraig Rodrigues goto unionfs_mkwhiteout_free_out; 841d00947d8SCraig Rodrigues if (!(error = VOP_LEASE(dvp, td, td->td_ucred, LEASE_WRITE))) 842d00947d8SCraig Rodrigues error = VOP_WHITEOUT(dvp, &cn, CREATE); 843d00947d8SCraig Rodrigues 844d00947d8SCraig Rodrigues vn_finished_write(mp); 845d00947d8SCraig Rodrigues 846d00947d8SCraig Rodrigues unionfs_mkwhiteout_free_out: 847d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 848d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 849d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 850d00947d8SCraig Rodrigues } 851d00947d8SCraig Rodrigues 852d00947d8SCraig Rodrigues return (error); 853d00947d8SCraig Rodrigues } 854d00947d8SCraig Rodrigues 855d00947d8SCraig Rodrigues /* 856d00947d8SCraig Rodrigues * Create a new vnode for create a new shadow file. 857d00947d8SCraig Rodrigues * 858d00947d8SCraig Rodrigues * If an error is returned, *vpp will be invalid, otherwise it will hold a 859d00947d8SCraig Rodrigues * locked, referenced and opened vnode. 860d00947d8SCraig Rodrigues * 861d00947d8SCraig Rodrigues * unp is never updated. 862df8bae1dSRodney W. Grimes */ 86380b301c3SPoul-Henning Kamp static int 864d00947d8SCraig Rodrigues unionfs_vn_create_on_upper(struct vnode **vpp, struct vnode *udvp, 865d00947d8SCraig Rodrigues struct unionfs_node *unp, struct vattr *uvap, 866d00947d8SCraig Rodrigues struct thread *td) 867df8bae1dSRodney W. Grimes { 868d00947d8SCraig Rodrigues struct unionfs_mount *ump; 869d00947d8SCraig Rodrigues struct vnode *vp; 870d00947d8SCraig Rodrigues struct vnode *lvp; 871d00947d8SCraig Rodrigues struct ucred *cred; 872d00947d8SCraig Rodrigues struct vattr lva; 873d00947d8SCraig Rodrigues int fmode; 874d00947d8SCraig Rodrigues int error; 875d00947d8SCraig Rodrigues struct componentname cn; 876d00947d8SCraig Rodrigues 877d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(UNIONFSTOV(unp)->v_mount); 878d00947d8SCraig Rodrigues vp = NULLVP; 879d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 880d00947d8SCraig Rodrigues cred = td->td_ucred; 881d00947d8SCraig Rodrigues fmode = FFLAGS(O_WRONLY | O_CREAT | O_TRUNC | O_EXCL); 882d00947d8SCraig Rodrigues error = 0; 883d00947d8SCraig Rodrigues 884d00947d8SCraig Rodrigues if ((error = VOP_GETATTR(lvp, &lva, cred, td)) != 0) 885d00947d8SCraig Rodrigues return (error); 886d00947d8SCraig Rodrigues unionfs_create_uppervattr_core(ump, &lva, uvap, td); 887d00947d8SCraig Rodrigues 888d00947d8SCraig Rodrigues if (unp->un_path == NULL) 889d00947d8SCraig Rodrigues panic("unionfs: un_path is null"); 890d00947d8SCraig Rodrigues 891d00947d8SCraig Rodrigues cn.cn_namelen = strlen(unp->un_path); 892d00947d8SCraig Rodrigues cn.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); 893d00947d8SCraig Rodrigues bcopy(unp->un_path, cn.cn_pnbuf, cn.cn_namelen + 1); 894d00947d8SCraig Rodrigues cn.cn_nameiop = CREATE; 895d00947d8SCraig Rodrigues cn.cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN); 896d00947d8SCraig Rodrigues cn.cn_lkflags = LK_EXCLUSIVE; 897d00947d8SCraig Rodrigues cn.cn_thread = td; 898d00947d8SCraig Rodrigues cn.cn_cred = cred; 899d00947d8SCraig Rodrigues cn.cn_nameptr = cn.cn_pnbuf; 900d00947d8SCraig Rodrigues cn.cn_consume = 0; 901d00947d8SCraig Rodrigues 902d00947d8SCraig Rodrigues vref(udvp); 903d00947d8SCraig Rodrigues if ((error = relookup(udvp, &vp, &cn)) != 0) 904d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out2; 905d00947d8SCraig Rodrigues vrele(udvp); 906d00947d8SCraig Rodrigues 907d00947d8SCraig Rodrigues if (vp != NULLVP) { 908d00947d8SCraig Rodrigues if (vp == udvp) 909d00947d8SCraig Rodrigues vrele(vp); 910d00947d8SCraig Rodrigues else 911d00947d8SCraig Rodrigues vput(vp); 912d00947d8SCraig Rodrigues error = EEXIST; 913d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out1; 914d00947d8SCraig Rodrigues } 915d00947d8SCraig Rodrigues 916d00947d8SCraig Rodrigues if ((error = VOP_LEASE(udvp, td, cred, LEASE_WRITE)) != 0) 917d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out1; 918d00947d8SCraig Rodrigues 919d00947d8SCraig Rodrigues if ((error = VOP_CREATE(udvp, &vp, &cn, uvap)) != 0) 920d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out1; 921d00947d8SCraig Rodrigues 922d00947d8SCraig Rodrigues if ((error = VOP_OPEN(vp, fmode, cred, td, -1)) != 0) { 923d00947d8SCraig Rodrigues vput(vp); 924d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out1; 925d00947d8SCraig Rodrigues } 926d00947d8SCraig Rodrigues vp->v_writecount++; 927d00947d8SCraig Rodrigues *vpp = vp; 928d00947d8SCraig Rodrigues 929d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out1: 930d00947d8SCraig Rodrigues VOP_UNLOCK(udvp, 0, td); 931d00947d8SCraig Rodrigues 932d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out2: 933d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 934d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 935d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 936d00947d8SCraig Rodrigues } 937d00947d8SCraig Rodrigues 938d00947d8SCraig Rodrigues return (error); 939d00947d8SCraig Rodrigues } 940d00947d8SCraig Rodrigues 941d00947d8SCraig Rodrigues /* 942d00947d8SCraig Rodrigues * Copy from lvp to uvp. 943d00947d8SCraig Rodrigues * 944d00947d8SCraig Rodrigues * lvp and uvp should be locked and opened on entry and will be locked and 945d00947d8SCraig Rodrigues * opened on return. 946d00947d8SCraig Rodrigues */ 947d00947d8SCraig Rodrigues static int 948d00947d8SCraig Rodrigues unionfs_copyfile_core(struct vnode *lvp, struct vnode *uvp, 949d00947d8SCraig Rodrigues struct ucred *cred, struct thread *td) 950d00947d8SCraig Rodrigues { 951d00947d8SCraig Rodrigues int error; 952d00947d8SCraig Rodrigues off_t offset; 953d00947d8SCraig Rodrigues int count; 954d00947d8SCraig Rodrigues int bufoffset; 955df8bae1dSRodney W. Grimes char *buf; 956df8bae1dSRodney W. Grimes struct uio uio; 957df8bae1dSRodney W. Grimes struct iovec iov; 958df8bae1dSRodney W. Grimes 959d00947d8SCraig Rodrigues error = 0; 960d00947d8SCraig Rodrigues memset(&uio, 0, sizeof(uio)); 9612a31267eSMatthew Dillon 962b40ce416SJulian Elischer uio.uio_td = td; 963df8bae1dSRodney W. Grimes uio.uio_segflg = UIO_SYSSPACE; 964df8bae1dSRodney W. Grimes uio.uio_offset = 0; 965df8bae1dSRodney W. Grimes 966d00947d8SCraig Rodrigues if ((error = VOP_LEASE(lvp, td, cred, LEASE_READ)) != 0) 967d00947d8SCraig Rodrigues return (error); 968d00947d8SCraig Rodrigues if ((error = VOP_LEASE(uvp, td, cred, LEASE_WRITE)) != 0) 969d00947d8SCraig Rodrigues return (error); 970a163d034SWarner Losh buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK); 971df8bae1dSRodney W. Grimes 972d00947d8SCraig Rodrigues while (error == 0) { 973d00947d8SCraig Rodrigues offset = uio.uio_offset; 974df8bae1dSRodney W. Grimes 975df8bae1dSRodney W. Grimes uio.uio_iov = &iov; 976df8bae1dSRodney W. Grimes uio.uio_iovcnt = 1; 977df8bae1dSRodney W. Grimes iov.iov_base = buf; 978df8bae1dSRodney W. Grimes iov.iov_len = MAXBSIZE; 979df8bae1dSRodney W. Grimes uio.uio_resid = iov.iov_len; 980df8bae1dSRodney W. Grimes uio.uio_rw = UIO_READ; 981df8bae1dSRodney W. Grimes 982d00947d8SCraig Rodrigues if ((error = VOP_READ(lvp, &uio, 0, cred)) != 0) 9832a31267eSMatthew Dillon break; 9842a31267eSMatthew Dillon if ((count = MAXBSIZE - uio.uio_resid) == 0) 9852a31267eSMatthew Dillon break; 9862a31267eSMatthew Dillon 987d00947d8SCraig Rodrigues bufoffset = 0; 9882a31267eSMatthew Dillon while (bufoffset < count) { 989df8bae1dSRodney W. Grimes uio.uio_iov = &iov; 990df8bae1dSRodney W. Grimes uio.uio_iovcnt = 1; 9912a31267eSMatthew Dillon iov.iov_base = buf + bufoffset; 9922a31267eSMatthew Dillon iov.iov_len = count - bufoffset; 9932a31267eSMatthew Dillon uio.uio_offset = offset + bufoffset; 994df8bae1dSRodney W. Grimes uio.uio_resid = iov.iov_len; 995d00947d8SCraig Rodrigues uio.uio_rw = UIO_WRITE; 996df8bae1dSRodney W. Grimes 997d00947d8SCraig Rodrigues if ((error = VOP_WRITE(uvp, &uio, 0, cred)) != 0) 998df8bae1dSRodney W. Grimes break; 999d00947d8SCraig Rodrigues 10002a31267eSMatthew Dillon bufoffset += (count - bufoffset) - uio.uio_resid; 1001df8bae1dSRodney W. Grimes } 1002d00947d8SCraig Rodrigues 10032a31267eSMatthew Dillon uio.uio_offset = offset + bufoffset; 1004d00947d8SCraig Rodrigues } 1005df8bae1dSRodney W. Grimes 1006df8bae1dSRodney W. Grimes free(buf, M_TEMP); 1007d00947d8SCraig Rodrigues 1008df8bae1dSRodney W. Grimes return (error); 1009df8bae1dSRodney W. Grimes } 1010df8bae1dSRodney W. Grimes 1011df8bae1dSRodney W. Grimes /* 1012d00947d8SCraig Rodrigues * Copy file from lower to upper. 10132a31267eSMatthew Dillon * 1014d00947d8SCraig Rodrigues * If you need copy of the contents, set 1 to docopy. Otherwise, set 0 to 1015d00947d8SCraig Rodrigues * docopy. 1016d00947d8SCraig Rodrigues * 1017d00947d8SCraig Rodrigues * If no error returned, unp will be updated. 1018996c772fSJohn Dyson */ 1019996c772fSJohn Dyson int 1020d00947d8SCraig Rodrigues unionfs_copyfile(struct unionfs_node *unp, int docopy, struct ucred *cred, 1021d00947d8SCraig Rodrigues struct thread *td) 1022996c772fSJohn Dyson { 1023996c772fSJohn Dyson int error; 1024f2a2857bSKirk McKusick struct mount *mp; 1025d00947d8SCraig Rodrigues struct vnode *udvp; 1026d00947d8SCraig Rodrigues struct vnode *lvp; 1027d00947d8SCraig Rodrigues struct vnode *uvp; 1028d00947d8SCraig Rodrigues struct vattr uva; 1029996c772fSJohn Dyson 1030d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 1031d00947d8SCraig Rodrigues uvp = NULLVP; 1032d00947d8SCraig Rodrigues 1033d00947d8SCraig Rodrigues if ((UNIONFSTOV(unp)->v_mount->mnt_flag & MNT_RDONLY)) 1034d00947d8SCraig Rodrigues return (EROFS); 1035d00947d8SCraig Rodrigues if (unp->un_dvp == NULLVP) 1036d00947d8SCraig Rodrigues return (EINVAL); 1037d00947d8SCraig Rodrigues if (unp->un_uppervp != NULLVP) 1038d00947d8SCraig Rodrigues return (EEXIST); 1039d00947d8SCraig Rodrigues udvp = VTOUNIONFS(unp->un_dvp)->un_uppervp; 1040d00947d8SCraig Rodrigues if (udvp == NULLVP) 1041d00947d8SCraig Rodrigues return (EROFS); 1042d00947d8SCraig Rodrigues if ((udvp->v_mount->mnt_flag & MNT_RDONLY)) 1043d00947d8SCraig Rodrigues return (EROFS); 1044d00947d8SCraig Rodrigues 1045d00947d8SCraig Rodrigues error = VOP_ACCESS(lvp, VREAD, cred, td); 1046d00947d8SCraig Rodrigues if (error != 0) 10475842d4e5SKATO Takenori return (error); 10485842d4e5SKATO Takenori 1049d00947d8SCraig Rodrigues if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH)) != 0) 1050996c772fSJohn Dyson return (error); 1051d00947d8SCraig Rodrigues error = unionfs_vn_create_on_upper(&uvp, udvp, unp, &uva, td); 1052d00947d8SCraig Rodrigues if (error != 0) { 1053f2a2857bSKirk McKusick vn_finished_write(mp); 1054f2a2857bSKirk McKusick return (error); 1055f2a2857bSKirk McKusick } 1056996c772fSJohn Dyson 1057d00947d8SCraig Rodrigues if (docopy != 0) { 1058a8d43c90SPoul-Henning Kamp error = VOP_OPEN(lvp, FREAD, cred, td, -1); 1059996c772fSJohn Dyson if (error == 0) { 1060d00947d8SCraig Rodrigues error = unionfs_copyfile_core(lvp, uvp, cred, td); 1061d00947d8SCraig Rodrigues VOP_CLOSE(lvp, FREAD, cred, td); 1062996c772fSJohn Dyson } 1063d00947d8SCraig Rodrigues } 1064d00947d8SCraig Rodrigues VOP_CLOSE(uvp, FWRITE, cred, td); 1065d00947d8SCraig Rodrigues uvp->v_writecount--; 1066996c772fSJohn Dyson 1067f2a2857bSKirk McKusick vn_finished_write(mp); 1068d00947d8SCraig Rodrigues 1069996c772fSJohn Dyson if (error == 0) { 1070d00947d8SCraig Rodrigues /* Reset the attributes. Ignore errors. */ 1071d00947d8SCraig Rodrigues uva.va_type = VNON; 1072d00947d8SCraig Rodrigues VOP_SETATTR(uvp, &uva, cred, td); 1073996c772fSJohn Dyson } 1074996c772fSJohn Dyson 1075d00947d8SCraig Rodrigues unionfs_node_update(unp, uvp, td); 1076996c772fSJohn Dyson 10772a31267eSMatthew Dillon return (error); 1078b422956cSPoul-Henning Kamp } 1079996c772fSJohn Dyson 10802a31267eSMatthew Dillon /* 1081d00947d8SCraig Rodrigues * It checks whether vp can rmdir. (check empty) 10822a31267eSMatthew Dillon * 1083d00947d8SCraig Rodrigues * vp is unionfs vnode. 1084d00947d8SCraig Rodrigues * vp should be locked. 1085df8bae1dSRodney W. Grimes */ 1086df8bae1dSRodney W. Grimes int 1087d00947d8SCraig Rodrigues unionfs_check_rmdir(struct vnode *vp, struct ucred *cred, struct thread *td) 1088df8bae1dSRodney W. Grimes { 1089df8bae1dSRodney W. Grimes int error; 1090d00947d8SCraig Rodrigues int eofflag; 1091d00947d8SCraig Rodrigues int lookuperr; 1092d00947d8SCraig Rodrigues struct vnode *uvp; 1093d00947d8SCraig Rodrigues struct vnode *lvp; 1094d00947d8SCraig Rodrigues struct vnode *tvp; 1095df8bae1dSRodney W. Grimes struct vattr va; 1096df8bae1dSRodney W. Grimes struct componentname cn; 1097df8bae1dSRodney W. Grimes /* 1098d00947d8SCraig Rodrigues * The size of buf needs to be larger than DIRBLKSIZ. 1099df8bae1dSRodney W. Grimes */ 1100d00947d8SCraig Rodrigues char buf[256 * 6]; 1101d00947d8SCraig Rodrigues struct dirent *dp; 1102d00947d8SCraig Rodrigues struct dirent *edp; 1103d00947d8SCraig Rodrigues struct uio uio; 1104d00947d8SCraig Rodrigues struct iovec iov; 1105df8bae1dSRodney W. Grimes 1106d00947d8SCraig Rodrigues ASSERT_VOP_ELOCKED(vp, "unionfs_check_rmdir"); 1107df8bae1dSRodney W. Grimes 1108d00947d8SCraig Rodrigues eofflag = 0; 1109d00947d8SCraig Rodrigues uvp = UNIONFSVPTOUPPERVP(vp); 1110d00947d8SCraig Rodrigues lvp = UNIONFSVPTOLOWERVP(vp); 1111df8bae1dSRodney W. Grimes 1112d00947d8SCraig Rodrigues /* check opaque */ 1113d00947d8SCraig Rodrigues if ((error = VOP_GETATTR(uvp, &va, cred, td)) != 0) 1114df8bae1dSRodney W. Grimes return (error); 1115d00947d8SCraig Rodrigues if (va.va_flags & OPAQUE) 1116d00947d8SCraig Rodrigues return (0); 1117df8bae1dSRodney W. Grimes 1118d00947d8SCraig Rodrigues /* open vnode */ 1119d00947d8SCraig Rodrigues if ((error = VOP_OPEN(vp, FREAD, cred, td, -1)) != 0) 1120996c772fSJohn Dyson return (error); 1121996c772fSJohn Dyson 1122d00947d8SCraig Rodrigues uio.uio_rw = UIO_READ; 1123d00947d8SCraig Rodrigues uio.uio_segflg = UIO_SYSSPACE; 1124d00947d8SCraig Rodrigues uio.uio_td = td; 1125d00947d8SCraig Rodrigues uio.uio_offset = 0; 1126996c772fSJohn Dyson 1127d00947d8SCraig Rodrigues #ifdef MAC 1128d00947d8SCraig Rodrigues error = mac_check_vnode_readdir(td->td_ucred, lvp); 1129d00947d8SCraig Rodrigues #endif 1130d00947d8SCraig Rodrigues while (!error && !eofflag) { 1131d00947d8SCraig Rodrigues iov.iov_base = buf; 1132d00947d8SCraig Rodrigues iov.iov_len = sizeof(buf); 1133d00947d8SCraig Rodrigues uio.uio_iov = &iov; 1134d00947d8SCraig Rodrigues uio.uio_iovcnt = 1; 1135d00947d8SCraig Rodrigues uio.uio_resid = iov.iov_len; 1136996c772fSJohn Dyson 1137d00947d8SCraig Rodrigues error = VOP_READDIR(lvp, &uio, cred, &eofflag, NULL, NULL); 1138d00947d8SCraig Rodrigues if (error) 1139d00947d8SCraig Rodrigues break; 1140996c772fSJohn Dyson 1141d00947d8SCraig Rodrigues edp = (struct dirent*)&buf[sizeof(buf) - uio.uio_resid]; 1142d00947d8SCraig Rodrigues for (dp = (struct dirent*)buf; !error && dp < edp; 1143d00947d8SCraig Rodrigues dp = (struct dirent*)((caddr_t)dp + dp->d_reclen)) { 1144d00947d8SCraig Rodrigues if (dp->d_type == DT_WHT || 1145d00947d8SCraig Rodrigues (dp->d_namlen == 1 && dp->d_name[0] == '.') || 1146d00947d8SCraig Rodrigues (dp->d_namlen == 2 && !bcmp(dp->d_name, "..", 2))) 1147d00947d8SCraig Rodrigues continue; 1148df8bae1dSRodney W. Grimes 1149d00947d8SCraig Rodrigues cn.cn_namelen = dp->d_namlen; 1150d00947d8SCraig Rodrigues cn.cn_pnbuf = NULL; 1151d00947d8SCraig Rodrigues cn.cn_nameptr = dp->d_name; 1152d00947d8SCraig Rodrigues cn.cn_nameiop = LOOKUP; 1153d00947d8SCraig Rodrigues cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN); 1154d00947d8SCraig Rodrigues cn.cn_lkflags = LK_EXCLUSIVE; 1155b40ce416SJulian Elischer cn.cn_thread = td; 1156d00947d8SCraig Rodrigues cn.cn_cred = cred; 1157df8bae1dSRodney W. Grimes cn.cn_consume = 0; 1158df8bae1dSRodney W. Grimes 11592a31267eSMatthew Dillon /* 1160d00947d8SCraig Rodrigues * check entry in lower. 1161d00947d8SCraig Rodrigues * Sometimes, readdir function returns 1162d00947d8SCraig Rodrigues * wrong entry. 11632a31267eSMatthew Dillon */ 1164d00947d8SCraig Rodrigues lookuperr = VOP_LOOKUP(lvp, &tvp, &cn); 1165df8bae1dSRodney W. Grimes 1166d00947d8SCraig Rodrigues if (!lookuperr) 1167d00947d8SCraig Rodrigues vput(tvp); 11682a31267eSMatthew Dillon else 1169d00947d8SCraig Rodrigues continue; /* skip entry */ 1170df8bae1dSRodney W. Grimes 1171df8bae1dSRodney W. Grimes /* 1172d00947d8SCraig Rodrigues * check entry 1173d00947d8SCraig Rodrigues * If it has no exist/whiteout entry in upper, 1174d00947d8SCraig Rodrigues * directory is not empty. 1175df8bae1dSRodney W. Grimes */ 1176d00947d8SCraig Rodrigues cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN); 1177d00947d8SCraig Rodrigues lookuperr = VOP_LOOKUP(uvp, &tvp, &cn); 1178df8bae1dSRodney W. Grimes 1179d00947d8SCraig Rodrigues if (!lookuperr) 1180d00947d8SCraig Rodrigues vput(tvp); 1181df8bae1dSRodney W. Grimes 1182d00947d8SCraig Rodrigues /* ignore exist or whiteout entry */ 1183d00947d8SCraig Rodrigues if (!lookuperr || 1184d00947d8SCraig Rodrigues (lookuperr == ENOENT && (cn.cn_flags & ISWHITEOUT))) 118501634480SBrian Feldman continue; 1186d00947d8SCraig Rodrigues 1187d00947d8SCraig Rodrigues error = ENOTEMPTY; 1188df8bae1dSRodney W. Grimes } 1189996c772fSJohn Dyson } 1190996c772fSJohn Dyson 1191d00947d8SCraig Rodrigues /* close vnode */ 1192d00947d8SCraig Rodrigues VOP_CLOSE(vp, FREAD, cred, td); 1193d00947d8SCraig Rodrigues 1194d00947d8SCraig Rodrigues return (error); 1195d00947d8SCraig Rodrigues } 1196d00947d8SCraig Rodrigues 1197d00947d8SCraig Rodrigues #ifdef DIAGNOSTIC 1198d00947d8SCraig Rodrigues 1199d00947d8SCraig Rodrigues struct vnode * 1200d00947d8SCraig Rodrigues unionfs_checkuppervp(struct vnode *vp, char *fil, int lno) 1201996c772fSJohn Dyson { 1202d00947d8SCraig Rodrigues struct unionfs_node *unp; 1203996c772fSJohn Dyson 1204d00947d8SCraig Rodrigues unp = VTOUNIONFS(vp); 1205996c772fSJohn Dyson 1206d00947d8SCraig Rodrigues #ifdef notyet 1207d00947d8SCraig Rodrigues if (vp->v_op != unionfs_vnodeop_p) { 1208d00947d8SCraig Rodrigues printf("unionfs_checkuppervp: on non-unionfs-node.\n"); 1209d00947d8SCraig Rodrigues #ifdef KDB 1210d00947d8SCraig Rodrigues kdb_enter("unionfs_checkuppervp: on non-unionfs-node.\n"); 1211d00947d8SCraig Rodrigues #endif 1212d00947d8SCraig Rodrigues panic("unionfs_checkuppervp"); 1213d00947d8SCraig Rodrigues }; 1214d00947d8SCraig Rodrigues #endif 1215d00947d8SCraig Rodrigues return (unp->un_uppervp); 1216d8c6e674SDavid Schultz } 1217996c772fSJohn Dyson 1218996c772fSJohn Dyson struct vnode * 1219d00947d8SCraig Rodrigues unionfs_checklowervp(struct vnode *vp, char *fil, int lno) 1220996c772fSJohn Dyson { 1221d00947d8SCraig Rodrigues struct unionfs_node *unp; 1222996c772fSJohn Dyson 1223d00947d8SCraig Rodrigues unp = VTOUNIONFS(vp); 1224996c772fSJohn Dyson 1225d00947d8SCraig Rodrigues #ifdef notyet 1226d00947d8SCraig Rodrigues if (vp->v_op != unionfs_vnodeop_p) { 1227d00947d8SCraig Rodrigues printf("unionfs_checklowervp: on non-unionfs-node.\n"); 1228d00947d8SCraig Rodrigues #ifdef KDB 1229d00947d8SCraig Rodrigues kdb_enter("unionfs_checklowervp: on non-unionfs-node.\n"); 1230d00947d8SCraig Rodrigues #endif 1231d00947d8SCraig Rodrigues panic("unionfs_checklowervp"); 12328c14bf40SPeter Wemm }; 1233d00947d8SCraig Rodrigues #endif 1234d00947d8SCraig Rodrigues return (unp->un_lowervp); 1235d00947d8SCraig Rodrigues } 1236d00947d8SCraig Rodrigues #endif 1237