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 63a9b794ffSDaichi GOTO #define NUNIONFSNODECACHE 16 64a9b794ffSDaichi GOTO 65a9b794ffSDaichi GOTO static MALLOC_DEFINE(M_UNIONFSHASH, "UNIONFS hash", "UNIONFS hash table"); 66d00947d8SCraig Rodrigues MALLOC_DEFINE(M_UNIONFSNODE, "UNIONFS node", "UNIONFS vnode private part"); 67d00947d8SCraig Rodrigues MALLOC_DEFINE(M_UNIONFSPATH, "UNIONFS path", "UNIONFS path private part"); 68df8bae1dSRodney W. Grimes 69d00947d8SCraig Rodrigues /* 70dc2dd185SDaichi GOTO * Initialize 71d00947d8SCraig Rodrigues */ 72df8bae1dSRodney W. Grimes int 73d00947d8SCraig Rodrigues unionfs_init(struct vfsconf *vfsp) 74df8bae1dSRodney W. Grimes { 75d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_init\n"); /* printed during system boot */ 7626f9a767SRodney W. Grimes return (0); 77df8bae1dSRodney W. Grimes } 78df8bae1dSRodney W. Grimes 79d00947d8SCraig Rodrigues /* 80dc2dd185SDaichi GOTO * Uninitialize 81d00947d8SCraig Rodrigues */ 82d00947d8SCraig Rodrigues int 83d00947d8SCraig Rodrigues unionfs_uninit(struct vfsconf *vfsp) 84df8bae1dSRodney W. Grimes { 85df8bae1dSRodney W. Grimes return (0); 86df8bae1dSRodney W. Grimes } 87df8bae1dSRodney W. Grimes 88a9b794ffSDaichi GOTO static struct unionfs_node_hashhead * 89a9b794ffSDaichi GOTO unionfs_get_hashhead(struct vnode *dvp, char *path) 90a9b794ffSDaichi GOTO { 91a9b794ffSDaichi GOTO int count; 92a9b794ffSDaichi GOTO char hash; 93a9b794ffSDaichi GOTO struct unionfs_node *unp; 94a9b794ffSDaichi GOTO 95a9b794ffSDaichi GOTO hash = 0; 96a9b794ffSDaichi GOTO unp = VTOUNIONFS(dvp); 97a9b794ffSDaichi GOTO if (path != NULL) { 98a9b794ffSDaichi GOTO for (count = 0; path[count]; count++) 99a9b794ffSDaichi GOTO hash += path[count]; 100a9b794ffSDaichi GOTO } 101a9b794ffSDaichi GOTO 102a9b794ffSDaichi GOTO return (&(unp->un_hashtbl[hash & (unp->un_hashmask)])); 103a9b794ffSDaichi GOTO } 104a9b794ffSDaichi GOTO 105a9b794ffSDaichi GOTO /* 106a9b794ffSDaichi GOTO * Get the cached vnode. (only VDIR) 107a9b794ffSDaichi GOTO */ 108a9b794ffSDaichi GOTO static struct vnode * 109a9b794ffSDaichi GOTO unionfs_get_cached_vdir(struct vnode *uvp, struct vnode *lvp, 110a9b794ffSDaichi GOTO struct vnode *dvp, char *path) 111a9b794ffSDaichi GOTO { 112a9b794ffSDaichi GOTO struct unionfs_node_hashhead *hd; 113a9b794ffSDaichi GOTO struct unionfs_node *unp; 114a9b794ffSDaichi GOTO struct vnode *vp; 115a9b794ffSDaichi GOTO 116a9b794ffSDaichi GOTO KASSERT((uvp == NULLVP || uvp->v_type == VDIR), 117a9b794ffSDaichi GOTO ("unionfs_get_cached_vdir: v_type != VDIR")); 118a9b794ffSDaichi GOTO KASSERT((lvp == NULLVP || lvp->v_type == VDIR), 119a9b794ffSDaichi GOTO ("unionfs_get_cached_vdir: v_type != VDIR")); 120a9b794ffSDaichi GOTO 121a9b794ffSDaichi GOTO VI_LOCK(dvp); 122a9b794ffSDaichi GOTO hd = unionfs_get_hashhead(dvp, path); 123a9b794ffSDaichi GOTO LIST_FOREACH(unp, hd, un_hash) { 124a9b794ffSDaichi GOTO if (!strcmp(unp->un_path, path)) { 125a9b794ffSDaichi GOTO vp = UNIONFSTOV(unp); 126a9b794ffSDaichi GOTO VI_LOCK_FLAGS(vp, MTX_DUPOK); 127a9b794ffSDaichi GOTO VI_UNLOCK(dvp); 128a9b794ffSDaichi GOTO vp->v_iflag &= ~VI_OWEINACT; 129a9b794ffSDaichi GOTO if ((vp->v_iflag & (VI_DOOMED | VI_DOINGINACT)) != 0) { 130a9b794ffSDaichi GOTO VI_UNLOCK(vp); 131a9b794ffSDaichi GOTO vp = NULLVP; 132a9b794ffSDaichi GOTO } else 133a9b794ffSDaichi GOTO VI_UNLOCK(vp); 134a9b794ffSDaichi GOTO return (vp); 135a9b794ffSDaichi GOTO } 136a9b794ffSDaichi GOTO } 137a9b794ffSDaichi GOTO VI_UNLOCK(dvp); 138a9b794ffSDaichi GOTO 139a9b794ffSDaichi GOTO return (NULLVP); 140a9b794ffSDaichi GOTO } 141a9b794ffSDaichi GOTO 142a9b794ffSDaichi GOTO /* 143a9b794ffSDaichi GOTO * Add the new vnode into cache. (only VDIR) 144a9b794ffSDaichi GOTO */ 145a9b794ffSDaichi GOTO static struct vnode * 146a9b794ffSDaichi GOTO unionfs_ins_cached_vdir(struct unionfs_node *uncp, 147a9b794ffSDaichi GOTO struct vnode *dvp, char *path) 148a9b794ffSDaichi GOTO { 149a9b794ffSDaichi GOTO struct unionfs_node_hashhead *hd; 150a9b794ffSDaichi GOTO struct unionfs_node *unp; 151a9b794ffSDaichi GOTO struct vnode *vp; 152a9b794ffSDaichi GOTO 153a9b794ffSDaichi GOTO KASSERT((uncp->un_uppervp==NULLVP || uncp->un_uppervp->v_type==VDIR), 154a9b794ffSDaichi GOTO ("unionfs_ins_cached_vdir: v_type != VDIR")); 155a9b794ffSDaichi GOTO KASSERT((uncp->un_lowervp==NULLVP || uncp->un_lowervp->v_type==VDIR), 156a9b794ffSDaichi GOTO ("unionfs_ins_cached_vdir: v_type != VDIR")); 157a9b794ffSDaichi GOTO 158a9b794ffSDaichi GOTO VI_LOCK(dvp); 159a9b794ffSDaichi GOTO hd = unionfs_get_hashhead(dvp, path); 160a9b794ffSDaichi GOTO LIST_FOREACH(unp, hd, un_hash) { 161a9b794ffSDaichi GOTO if (!strcmp(unp->un_path, path)) { 162a9b794ffSDaichi GOTO vp = UNIONFSTOV(unp); 163a9b794ffSDaichi GOTO VI_LOCK_FLAGS(vp, MTX_DUPOK); 164a9b794ffSDaichi GOTO vp->v_iflag &= ~VI_OWEINACT; 165a9b794ffSDaichi GOTO if ((vp->v_iflag & (VI_DOOMED | VI_DOINGINACT)) != 0) { 166a9b794ffSDaichi GOTO LIST_INSERT_HEAD(hd, uncp, un_hash); 167a9b794ffSDaichi GOTO VI_UNLOCK(vp); 168a9b794ffSDaichi GOTO vp = NULLVP; 169a9b794ffSDaichi GOTO } else 170a9b794ffSDaichi GOTO VI_UNLOCK(vp); 171a9b794ffSDaichi GOTO VI_UNLOCK(dvp); 172a9b794ffSDaichi GOTO return (vp); 173a9b794ffSDaichi GOTO } 174a9b794ffSDaichi GOTO } 175a9b794ffSDaichi GOTO 176a9b794ffSDaichi GOTO LIST_INSERT_HEAD(hd, uncp, un_hash); 177a9b794ffSDaichi GOTO VI_UNLOCK(dvp); 178a9b794ffSDaichi GOTO 179a9b794ffSDaichi GOTO return (NULLVP); 180a9b794ffSDaichi GOTO } 181a9b794ffSDaichi GOTO 182a9b794ffSDaichi GOTO /* 183a9b794ffSDaichi GOTO * Remove the vnode. (only VDIR) 184a9b794ffSDaichi GOTO */ 185a9b794ffSDaichi GOTO static void 186a9b794ffSDaichi GOTO unionfs_rem_cached_vdir(struct unionfs_node *unp, struct vnode *dvp) 187a9b794ffSDaichi GOTO { 188a9b794ffSDaichi GOTO KASSERT((unp != NULL), ("unionfs_rem_cached_vdir: null node")); 189a9b794ffSDaichi GOTO KASSERT((dvp != NULLVP), 190a9b794ffSDaichi GOTO ("unionfs_rem_cached_vdir: null parent vnode")); 191a9b794ffSDaichi GOTO KASSERT((unp->un_hash.le_prev != NULL), 192a9b794ffSDaichi GOTO ("unionfs_rem_cached_vdir: null hash")); 193a9b794ffSDaichi GOTO 194a9b794ffSDaichi GOTO VI_LOCK(dvp); 195a9b794ffSDaichi GOTO LIST_REMOVE(unp, un_hash); 196a9b794ffSDaichi GOTO VI_UNLOCK(dvp); 197a9b794ffSDaichi GOTO } 198a9b794ffSDaichi GOTO 199d00947d8SCraig Rodrigues /* 200d00947d8SCraig Rodrigues * Make a new or get existing unionfs node. 2012a31267eSMatthew Dillon * 202d00947d8SCraig Rodrigues * uppervp and lowervp should be unlocked. Because if new unionfs vnode is 203d00947d8SCraig Rodrigues * locked, uppervp or lowervp is locked too. In order to prevent dead lock, 204d00947d8SCraig Rodrigues * you should not lock plurality simultaneously. 205df8bae1dSRodney W. Grimes */ 206d00947d8SCraig Rodrigues int 207d00947d8SCraig Rodrigues unionfs_nodeget(struct mount *mp, struct vnode *uppervp, 208d00947d8SCraig Rodrigues struct vnode *lowervp, struct vnode *dvp, 209d00947d8SCraig Rodrigues struct vnode **vpp, struct componentname *cnp, 210d00947d8SCraig Rodrigues struct thread *td) 211d00947d8SCraig Rodrigues { 212d00947d8SCraig Rodrigues struct unionfs_mount *ump; 213d00947d8SCraig Rodrigues struct unionfs_node *unp; 214d00947d8SCraig Rodrigues struct vnode *vp; 215d00947d8SCraig Rodrigues int error; 216d00947d8SCraig Rodrigues int lkflags; 217a9b794ffSDaichi GOTO enum vtype vt; 218d00947d8SCraig Rodrigues char *path; 219df8bae1dSRodney W. Grimes 220d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 221d00947d8SCraig Rodrigues lkflags = (cnp ? cnp->cn_lkflags : 0); 222dc2dd185SDaichi GOTO path = (cnp ? cnp->cn_nameptr : NULL); 223a9b794ffSDaichi GOTO *vpp = NULLVP; 2242a31267eSMatthew Dillon 225d00947d8SCraig Rodrigues if (uppervp == NULLVP && lowervp == NULLVP) 226d00947d8SCraig Rodrigues panic("unionfs_nodeget: upper and lower is null"); 227d00947d8SCraig Rodrigues 228a9b794ffSDaichi GOTO vt = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type); 229a9b794ffSDaichi GOTO 230d00947d8SCraig Rodrigues /* If it has no ISLASTCN flag, path check is skipped. */ 231dc2dd185SDaichi GOTO if (cnp && !(cnp->cn_flags & ISLASTCN)) 232d00947d8SCraig Rodrigues path = NULL; 233d00947d8SCraig Rodrigues 234a9b794ffSDaichi GOTO /* check the vdir cache */ 235a9b794ffSDaichi GOTO if (path != NULL && dvp != NULLVP && vt == VDIR) { 236a9b794ffSDaichi GOTO vp = unionfs_get_cached_vdir(uppervp, lowervp, dvp, path); 237a9b794ffSDaichi GOTO if (vp != NULLVP) { 238a9b794ffSDaichi GOTO vref(vp); 239a9b794ffSDaichi GOTO *vpp = vp; 240a9b794ffSDaichi GOTO goto unionfs_nodeget_out; 241a9b794ffSDaichi GOTO } 242a9b794ffSDaichi GOTO } 243a9b794ffSDaichi GOTO 244d00947d8SCraig Rodrigues if ((uppervp == NULLVP || ump->um_uppervp != uppervp) || 245d00947d8SCraig Rodrigues (lowervp == NULLVP || ump->um_lowervp != lowervp)) { 246a9b794ffSDaichi GOTO /* dvp will be NULLVP only in case of root vnode. */ 247d00947d8SCraig Rodrigues if (dvp == NULLVP) 248d00947d8SCraig Rodrigues return (EINVAL); 249d00947d8SCraig Rodrigues } 250d00947d8SCraig Rodrigues 251df8bae1dSRodney W. Grimes /* 252d00947d8SCraig Rodrigues * Do the MALLOC before the getnewvnode since doing so afterward 253d00947d8SCraig Rodrigues * might cause a bogus v_data pointer to get dereferenced elsewhere 254d00947d8SCraig Rodrigues * if MALLOC should block. 255df8bae1dSRodney W. Grimes */ 256d00947d8SCraig Rodrigues MALLOC(unp, struct unionfs_node *, sizeof(struct unionfs_node), 257d00947d8SCraig Rodrigues M_UNIONFSNODE, M_WAITOK | M_ZERO); 258d00947d8SCraig Rodrigues 259d00947d8SCraig Rodrigues error = getnewvnode("unionfs", mp, &unionfs_vnodeops, &vp); 260dc2dd185SDaichi GOTO if (error != 0) { 261d00947d8SCraig Rodrigues FREE(unp, M_UNIONFSNODE); 262d00947d8SCraig Rodrigues return (error); 263d00947d8SCraig Rodrigues } 26461b9d89fSTor Egge error = insmntque(vp, mp); /* XXX: Too early for mpsafe fs */ 26561b9d89fSTor Egge if (error != 0) { 26661b9d89fSTor Egge FREE(unp, M_UNIONFSNODE); 26761b9d89fSTor Egge return (error); 26861b9d89fSTor Egge } 269d00947d8SCraig Rodrigues if (dvp != NULLVP) 270d00947d8SCraig Rodrigues vref(dvp); 271d00947d8SCraig Rodrigues if (uppervp != NULLVP) 272d00947d8SCraig Rodrigues vref(uppervp); 273d00947d8SCraig Rodrigues if (lowervp != NULLVP) 274d00947d8SCraig Rodrigues vref(lowervp); 275d00947d8SCraig Rodrigues 276a9b794ffSDaichi GOTO if (vt == VDIR) 277a9b794ffSDaichi GOTO unp->un_hashtbl = hashinit(NUNIONFSNODECACHE, M_UNIONFSHASH, 278a9b794ffSDaichi GOTO &(unp->un_hashmask)); 279a9b794ffSDaichi GOTO 280d00947d8SCraig Rodrigues unp->un_vnode = vp; 281d00947d8SCraig Rodrigues unp->un_uppervp = uppervp; 282d00947d8SCraig Rodrigues unp->un_lowervp = lowervp; 283d00947d8SCraig Rodrigues unp->un_dvp = dvp; 284d00947d8SCraig Rodrigues if (uppervp != NULLVP) 285d00947d8SCraig Rodrigues vp->v_vnlock = uppervp->v_vnlock; 286d00947d8SCraig Rodrigues else 287d00947d8SCraig Rodrigues vp->v_vnlock = lowervp->v_vnlock; 288d00947d8SCraig Rodrigues 289dc2dd185SDaichi GOTO if (path != NULL) { 290d00947d8SCraig Rodrigues unp->un_path = (char *) 291d00947d8SCraig Rodrigues malloc(cnp->cn_namelen +1, M_UNIONFSPATH, M_WAITOK|M_ZERO); 292d00947d8SCraig Rodrigues bcopy(cnp->cn_nameptr, unp->un_path, cnp->cn_namelen); 293d00947d8SCraig Rodrigues unp->un_path[cnp->cn_namelen] = '\0'; 294d00947d8SCraig Rodrigues } 295a9b794ffSDaichi GOTO vp->v_type = vt; 296d00947d8SCraig Rodrigues vp->v_data = unp; 297d00947d8SCraig Rodrigues 298d00947d8SCraig Rodrigues if ((uppervp != NULLVP && ump->um_uppervp == uppervp) && 299d00947d8SCraig Rodrigues (lowervp != NULLVP && ump->um_lowervp == lowervp)) 300d00947d8SCraig Rodrigues vp->v_vflag |= VV_ROOT; 301d00947d8SCraig Rodrigues 302a9b794ffSDaichi GOTO if (path != NULL && dvp != NULLVP && vt == VDIR) 303a9b794ffSDaichi GOTO *vpp = unionfs_ins_cached_vdir(unp, dvp, path); 304a9b794ffSDaichi GOTO if ((*vpp) != NULLVP) { 305a9b794ffSDaichi GOTO if (dvp != NULLVP) 306a9b794ffSDaichi GOTO vrele(dvp); 307a9b794ffSDaichi GOTO if (uppervp != NULLVP) 308a9b794ffSDaichi GOTO vrele(uppervp); 309a9b794ffSDaichi GOTO if (lowervp != NULLVP) 310a9b794ffSDaichi GOTO vrele(lowervp); 311a9b794ffSDaichi GOTO 312a9b794ffSDaichi GOTO unp->un_uppervp = NULLVP; 313a9b794ffSDaichi GOTO unp->un_lowervp = NULLVP; 314a9b794ffSDaichi GOTO unp->un_dvp = NULLVP; 315a9b794ffSDaichi GOTO vrele(vp); 316a9b794ffSDaichi GOTO vp = *vpp; 317a9b794ffSDaichi GOTO vref(vp); 318a9b794ffSDaichi GOTO } else 319a9b794ffSDaichi GOTO *vpp = vp; 320a9b794ffSDaichi GOTO 321a9b794ffSDaichi GOTO unionfs_nodeget_out: 322d00947d8SCraig Rodrigues if (lkflags & LK_TYPE_MASK) 323cb05b60aSAttilio Rao vn_lock(vp, lkflags | LK_RETRY); 324df8bae1dSRodney W. Grimes 325d00947d8SCraig Rodrigues return (0); 326996c772fSJohn Dyson } 327df8bae1dSRodney W. Grimes 3282a31267eSMatthew Dillon /* 329dc2dd185SDaichi GOTO * Clean up the unionfs node. 3302a31267eSMatthew Dillon */ 331d00947d8SCraig Rodrigues void 332dc2dd185SDaichi GOTO unionfs_noderem(struct vnode *vp, struct thread *td) 333d00947d8SCraig Rodrigues { 334d00947d8SCraig Rodrigues int vfslocked; 335d00947d8SCraig Rodrigues struct unionfs_node *unp; 336acc4bab1SCraig Rodrigues struct unionfs_node_status *unsp, *unsp_tmp; 337d00947d8SCraig Rodrigues struct vnode *lvp; 338d00947d8SCraig Rodrigues struct vnode *uvp; 339a9b794ffSDaichi GOTO struct vnode *dvp; 3402a31267eSMatthew Dillon 3412a31267eSMatthew Dillon /* 342d00947d8SCraig Rodrigues * Use the interlock to protect the clearing of v_data to 343d00947d8SCraig Rodrigues * prevent faults in unionfs_lock(). 3442a31267eSMatthew Dillon */ 345d00947d8SCraig Rodrigues VI_LOCK(vp); 346d00947d8SCraig Rodrigues unp = VTOUNIONFS(vp); 347d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 348d00947d8SCraig Rodrigues uvp = unp->un_uppervp; 349a9b794ffSDaichi GOTO dvp = unp->un_dvp; 350d00947d8SCraig Rodrigues unp->un_lowervp = unp->un_uppervp = NULLVP; 351d00947d8SCraig Rodrigues 352d00947d8SCraig Rodrigues vp->v_vnlock = &(vp->v_lock); 353d00947d8SCraig Rodrigues vp->v_data = NULL; 3540e9eb108SAttilio Rao lockmgr(vp->v_vnlock, LK_EXCLUSIVE | LK_INTERLOCK, VI_MTX(vp)); 355d00947d8SCraig Rodrigues if (lvp != NULLVP) 35622db15c0SAttilio Rao VOP_UNLOCK(lvp, 0); 357d00947d8SCraig Rodrigues if (uvp != NULLVP) 35822db15c0SAttilio Rao VOP_UNLOCK(uvp, 0); 359d00947d8SCraig Rodrigues vp->v_object = NULL; 360d00947d8SCraig Rodrigues 361a9b794ffSDaichi GOTO if (unp->un_path != NULL && dvp != NULLVP && vp->v_type == VDIR) 362a9b794ffSDaichi GOTO unionfs_rem_cached_vdir(unp, dvp); 363a9b794ffSDaichi GOTO 364d00947d8SCraig Rodrigues if (lvp != NULLVP) { 365d00947d8SCraig Rodrigues vfslocked = VFS_LOCK_GIANT(lvp->v_mount); 366d00947d8SCraig Rodrigues vrele(lvp); 367d00947d8SCraig Rodrigues VFS_UNLOCK_GIANT(vfslocked); 368d00947d8SCraig Rodrigues } 369d00947d8SCraig Rodrigues if (uvp != NULLVP) { 370d00947d8SCraig Rodrigues vfslocked = VFS_LOCK_GIANT(uvp->v_mount); 371d00947d8SCraig Rodrigues vrele(uvp); 372d00947d8SCraig Rodrigues VFS_UNLOCK_GIANT(vfslocked); 373d00947d8SCraig Rodrigues } 374a9b794ffSDaichi GOTO if (dvp != NULLVP) { 375a9b794ffSDaichi GOTO vfslocked = VFS_LOCK_GIANT(dvp->v_mount); 376a9b794ffSDaichi GOTO vrele(dvp); 377d00947d8SCraig Rodrigues VFS_UNLOCK_GIANT(vfslocked); 378d00947d8SCraig Rodrigues unp->un_dvp = NULLVP; 379d00947d8SCraig Rodrigues } 380a9b794ffSDaichi GOTO if (unp->un_path != NULL) { 381d00947d8SCraig Rodrigues free(unp->un_path, M_UNIONFSPATH); 382d00947d8SCraig Rodrigues unp->un_path = NULL; 383d00947d8SCraig Rodrigues } 384acc4bab1SCraig Rodrigues 385a9b794ffSDaichi GOTO if (unp->un_hashtbl != NULL) 386a9b794ffSDaichi GOTO hashdestroy(unp->un_hashtbl, M_UNIONFSHASH, unp->un_hashmask); 387a9b794ffSDaichi GOTO 388acc4bab1SCraig Rodrigues LIST_FOREACH_SAFE(unsp, &(unp->un_unshead), uns_list, unsp_tmp) { 389d00947d8SCraig Rodrigues LIST_REMOVE(unsp, uns_list); 390d00947d8SCraig Rodrigues free(unsp, M_TEMP); 391d00947d8SCraig Rodrigues } 392d00947d8SCraig Rodrigues FREE(unp, M_UNIONFSNODE); 393df8bae1dSRodney W. Grimes } 394df8bae1dSRodney W. Grimes 395d00947d8SCraig Rodrigues /* 396d00947d8SCraig Rodrigues * Get the unionfs node status. 397d00947d8SCraig Rodrigues * You need exclusive lock this vnode. 398d00947d8SCraig Rodrigues */ 399d00947d8SCraig Rodrigues void 400d00947d8SCraig Rodrigues unionfs_get_node_status(struct unionfs_node *unp, struct thread *td, 401d00947d8SCraig Rodrigues struct unionfs_node_status **unspp) 402d00947d8SCraig Rodrigues { 403d00947d8SCraig Rodrigues struct unionfs_node_status *unsp; 404df8bae1dSRodney W. Grimes 405d00947d8SCraig Rodrigues KASSERT(NULL != unspp, ("null pointer")); 406d00947d8SCraig Rodrigues ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status"); 4072a31267eSMatthew Dillon 408d00947d8SCraig Rodrigues LIST_FOREACH(unsp, &(unp->un_unshead), uns_list) { 409d00947d8SCraig Rodrigues if (unsp->uns_tid == td->td_tid) { 410d00947d8SCraig Rodrigues *unspp = unsp; 411d00947d8SCraig Rodrigues return; 412d00947d8SCraig Rodrigues } 413d00947d8SCraig Rodrigues } 4142a31267eSMatthew Dillon 415d00947d8SCraig Rodrigues /* create a new unionfs node status */ 416d00947d8SCraig Rodrigues MALLOC(unsp, struct unionfs_node_status *, 417d00947d8SCraig Rodrigues sizeof(struct unionfs_node_status), M_TEMP, M_WAITOK | M_ZERO); 4182a31267eSMatthew Dillon 419d00947d8SCraig Rodrigues unsp->uns_tid = td->td_tid; 420d00947d8SCraig Rodrigues LIST_INSERT_HEAD(&(unp->un_unshead), unsp, uns_list); 4212a31267eSMatthew Dillon 422d00947d8SCraig Rodrigues *unspp = unsp; 423d00947d8SCraig Rodrigues } 424d00947d8SCraig Rodrigues 425d00947d8SCraig Rodrigues /* 426d00947d8SCraig Rodrigues * Remove the unionfs node status, if you can. 427d00947d8SCraig Rodrigues * You need exclusive lock this vnode. 428d00947d8SCraig Rodrigues */ 429d00947d8SCraig Rodrigues void 430d00947d8SCraig Rodrigues unionfs_tryrem_node_status(struct unionfs_node *unp, struct thread *td, 431d00947d8SCraig Rodrigues struct unionfs_node_status *unsp) 432d00947d8SCraig Rodrigues { 433d00947d8SCraig Rodrigues KASSERT(NULL != unsp, ("null pointer")); 434d00947d8SCraig Rodrigues ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status"); 435d00947d8SCraig Rodrigues 436d00947d8SCraig Rodrigues if (0 < unsp->uns_lower_opencnt || 0 < unsp->uns_upper_opencnt) 437d00947d8SCraig Rodrigues return; 438d00947d8SCraig Rodrigues 439d00947d8SCraig Rodrigues LIST_REMOVE(unsp, uns_list); 440d00947d8SCraig Rodrigues free(unsp, M_TEMP); 441d00947d8SCraig Rodrigues } 442d00947d8SCraig Rodrigues 443d00947d8SCraig Rodrigues /* 444d00947d8SCraig Rodrigues * Create upper node attr. 445d00947d8SCraig Rodrigues */ 446d00947d8SCraig Rodrigues void 447d00947d8SCraig Rodrigues unionfs_create_uppervattr_core(struct unionfs_mount *ump, 448d00947d8SCraig Rodrigues struct vattr *lva, 449d00947d8SCraig Rodrigues struct vattr *uva, 450d00947d8SCraig Rodrigues struct thread *td) 451d00947d8SCraig Rodrigues { 452d00947d8SCraig Rodrigues VATTR_NULL(uva); 453d00947d8SCraig Rodrigues uva->va_type = lva->va_type; 454d00947d8SCraig Rodrigues uva->va_atime = lva->va_atime; 455d00947d8SCraig Rodrigues uva->va_mtime = lva->va_mtime; 456d00947d8SCraig Rodrigues uva->va_ctime = lva->va_ctime; 457d00947d8SCraig Rodrigues 458d00947d8SCraig Rodrigues switch (ump->um_copymode) { 459d00947d8SCraig Rodrigues case UNIONFS_TRANSPARENT: 460d00947d8SCraig Rodrigues uva->va_mode = lva->va_mode; 461d00947d8SCraig Rodrigues uva->va_uid = lva->va_uid; 462d00947d8SCraig Rodrigues uva->va_gid = lva->va_gid; 463d00947d8SCraig Rodrigues break; 464d00947d8SCraig Rodrigues case UNIONFS_MASQUERADE: 465d00947d8SCraig Rodrigues if (ump->um_uid == lva->va_uid) { 466d00947d8SCraig Rodrigues uva->va_mode = lva->va_mode & 077077; 467d00947d8SCraig Rodrigues uva->va_mode |= (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile) & 0700; 468d00947d8SCraig Rodrigues uva->va_uid = lva->va_uid; 469d00947d8SCraig Rodrigues uva->va_gid = lva->va_gid; 470df8bae1dSRodney W. Grimes } else { 471d00947d8SCraig Rodrigues uva->va_mode = (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile); 472d00947d8SCraig Rodrigues uva->va_uid = ump->um_uid; 473d00947d8SCraig Rodrigues uva->va_gid = ump->um_gid; 474d00947d8SCraig Rodrigues } 475d00947d8SCraig Rodrigues break; 476d00947d8SCraig Rodrigues default: /* UNIONFS_TRADITIONAL */ 4775e3f7694SRobert Watson FILEDESC_SLOCK(td->td_proc->p_fd); 478d00947d8SCraig Rodrigues uva->va_mode = 0777 & ~td->td_proc->p_fd->fd_cmask; 4795e3f7694SRobert Watson FILEDESC_SUNLOCK(td->td_proc->p_fd); 480d00947d8SCraig Rodrigues uva->va_uid = ump->um_uid; 481d00947d8SCraig Rodrigues uva->va_gid = ump->um_gid; 482d00947d8SCraig Rodrigues break; 483d00947d8SCraig Rodrigues } 484df8bae1dSRodney W. Grimes } 485df8bae1dSRodney W. Grimes 486d00947d8SCraig Rodrigues /* 487d00947d8SCraig Rodrigues * Create upper node attr. 488d00947d8SCraig Rodrigues */ 489d00947d8SCraig Rodrigues int 490d00947d8SCraig Rodrigues unionfs_create_uppervattr(struct unionfs_mount *ump, 491d00947d8SCraig Rodrigues struct vnode *lvp, 492d00947d8SCraig Rodrigues struct vattr *uva, 493d00947d8SCraig Rodrigues struct ucred *cred, 494d00947d8SCraig Rodrigues struct thread *td) 495d00947d8SCraig Rodrigues { 496d00947d8SCraig Rodrigues int error; 497d00947d8SCraig Rodrigues struct vattr lva; 498df8bae1dSRodney W. Grimes 499d00947d8SCraig Rodrigues if ((error = VOP_GETATTR(lvp, &lva, cred, td))) 500d00947d8SCraig Rodrigues return (error); 501d00947d8SCraig Rodrigues 502d00947d8SCraig Rodrigues unionfs_create_uppervattr_core(ump, &lva, uva, td); 503df8bae1dSRodney W. Grimes 504df8bae1dSRodney W. Grimes return (error); 505df8bae1dSRodney W. Grimes } 506df8bae1dSRodney W. Grimes 507d00947d8SCraig Rodrigues /* 508d00947d8SCraig Rodrigues * relookup 509d00947d8SCraig Rodrigues * 510d00947d8SCraig Rodrigues * dvp should be locked on entry and will be locked on return. 511d00947d8SCraig Rodrigues * 512d00947d8SCraig Rodrigues * If an error is returned, *vpp will be invalid, otherwise it will hold a 513d00947d8SCraig Rodrigues * locked, referenced vnode. If *vpp == dvp then remember that only one 514d00947d8SCraig Rodrigues * LK_EXCLUSIVE lock is held. 515d00947d8SCraig Rodrigues */ 516d00947d8SCraig Rodrigues static int 517d00947d8SCraig Rodrigues unionfs_relookup(struct vnode *dvp, struct vnode **vpp, 518d00947d8SCraig Rodrigues struct componentname *cnp, struct componentname *cn, 519d00947d8SCraig Rodrigues struct thread *td, char *path, int pathlen, u_long nameiop) 520df8bae1dSRodney W. Grimes { 521d00947d8SCraig Rodrigues int error; 522df8bae1dSRodney W. Grimes 523d00947d8SCraig Rodrigues cn->cn_namelen = pathlen; 524d00947d8SCraig Rodrigues cn->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); 525d00947d8SCraig Rodrigues bcopy(path, cn->cn_pnbuf, pathlen); 526d00947d8SCraig Rodrigues cn->cn_pnbuf[pathlen] = '\0'; 527df8bae1dSRodney W. Grimes 528d00947d8SCraig Rodrigues cn->cn_nameiop = nameiop; 529d00947d8SCraig Rodrigues cn->cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN); 530d00947d8SCraig Rodrigues cn->cn_lkflags = LK_EXCLUSIVE; 531d00947d8SCraig Rodrigues cn->cn_thread = td; 532d00947d8SCraig Rodrigues cn->cn_cred = cnp->cn_cred; 533df8bae1dSRodney W. Grimes 534d00947d8SCraig Rodrigues cn->cn_nameptr = cn->cn_pnbuf; 535d00947d8SCraig Rodrigues cn->cn_consume = cnp->cn_consume; 536df8bae1dSRodney W. Grimes 537d00947d8SCraig Rodrigues if (nameiop == DELETE) 538d00947d8SCraig Rodrigues cn->cn_flags |= (cnp->cn_flags & (DOWHITEOUT | SAVESTART)); 539d00947d8SCraig Rodrigues else if (RENAME == nameiop) 540d00947d8SCraig Rodrigues cn->cn_flags |= (cnp->cn_flags & SAVESTART); 541d00947d8SCraig Rodrigues 542d00947d8SCraig Rodrigues vref(dvp); 54322db15c0SAttilio Rao VOP_UNLOCK(dvp, 0); 544d00947d8SCraig Rodrigues 545d00947d8SCraig Rodrigues if ((error = relookup(dvp, vpp, cn))) { 546d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn->cn_pnbuf); 547d00947d8SCraig Rodrigues cn->cn_flags &= ~HASBUF; 548cb05b60aSAttilio Rao vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); 549d00947d8SCraig Rodrigues } else 550d00947d8SCraig Rodrigues vrele(dvp); 551d00947d8SCraig Rodrigues 552d00947d8SCraig Rodrigues return (error); 553df8bae1dSRodney W. Grimes } 554df8bae1dSRodney W. Grimes 555df8bae1dSRodney W. Grimes /* 556d00947d8SCraig Rodrigues * relookup for CREATE namei operation. 5572a31267eSMatthew Dillon * 558d00947d8SCraig Rodrigues * dvp is unionfs vnode. dvp should be locked. 559d00947d8SCraig Rodrigues * 560d00947d8SCraig Rodrigues * If it called 'unionfs_copyfile' function by unionfs_link etc, 561d00947d8SCraig Rodrigues * VOP_LOOKUP information is broken. 562d00947d8SCraig Rodrigues * So it need relookup in order to create link etc. 563d00947d8SCraig Rodrigues */ 564d00947d8SCraig Rodrigues int 565d00947d8SCraig Rodrigues unionfs_relookup_for_create(struct vnode *dvp, struct componentname *cnp, 566d00947d8SCraig Rodrigues struct thread *td) 567d00947d8SCraig Rodrigues { 568d00947d8SCraig Rodrigues int error; 569d00947d8SCraig Rodrigues struct vnode *udvp; 570d00947d8SCraig Rodrigues struct vnode *vp; 571d00947d8SCraig Rodrigues struct componentname cn; 572d00947d8SCraig Rodrigues 573d00947d8SCraig Rodrigues udvp = UNIONFSVPTOUPPERVP(dvp); 574d00947d8SCraig Rodrigues vp = NULLVP; 575d00947d8SCraig Rodrigues 576d00947d8SCraig Rodrigues error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr, 577d00947d8SCraig Rodrigues strlen(cnp->cn_nameptr), CREATE); 578d00947d8SCraig Rodrigues if (error) 579d00947d8SCraig Rodrigues return (error); 580d00947d8SCraig Rodrigues 581d00947d8SCraig Rodrigues if (vp != NULLVP) { 582d00947d8SCraig Rodrigues if (udvp == vp) 583d00947d8SCraig Rodrigues vrele(vp); 584d00947d8SCraig Rodrigues else 585d00947d8SCraig Rodrigues vput(vp); 586d00947d8SCraig Rodrigues 587d00947d8SCraig Rodrigues error = EEXIST; 588d00947d8SCraig Rodrigues } 589d00947d8SCraig Rodrigues 590d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 591d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 592d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 593d00947d8SCraig Rodrigues } 594d00947d8SCraig Rodrigues 595d00947d8SCraig Rodrigues if (!error) { 596d00947d8SCraig Rodrigues cn.cn_flags |= (cnp->cn_flags & HASBUF); 597d00947d8SCraig Rodrigues cnp->cn_flags = cn.cn_flags; 598d00947d8SCraig Rodrigues } 599d00947d8SCraig Rodrigues 600d00947d8SCraig Rodrigues return (error); 601d00947d8SCraig Rodrigues } 602d00947d8SCraig Rodrigues 603d00947d8SCraig Rodrigues /* 604d00947d8SCraig Rodrigues * relookup for DELETE namei operation. 605d00947d8SCraig Rodrigues * 606d00947d8SCraig Rodrigues * dvp is unionfs vnode. dvp should be locked. 607d00947d8SCraig Rodrigues */ 608d00947d8SCraig Rodrigues int 609d00947d8SCraig Rodrigues unionfs_relookup_for_delete(struct vnode *dvp, struct componentname *cnp, 610d00947d8SCraig Rodrigues struct thread *td) 611d00947d8SCraig Rodrigues { 612d00947d8SCraig Rodrigues int error; 613d00947d8SCraig Rodrigues struct vnode *udvp; 614d00947d8SCraig Rodrigues struct vnode *vp; 615d00947d8SCraig Rodrigues struct componentname cn; 616d00947d8SCraig Rodrigues 617d00947d8SCraig Rodrigues udvp = UNIONFSVPTOUPPERVP(dvp); 618d00947d8SCraig Rodrigues vp = NULLVP; 619d00947d8SCraig Rodrigues 620d00947d8SCraig Rodrigues error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr, 621d00947d8SCraig Rodrigues strlen(cnp->cn_nameptr), DELETE); 622d00947d8SCraig Rodrigues if (error) 623d00947d8SCraig Rodrigues return (error); 624d00947d8SCraig Rodrigues 625d00947d8SCraig Rodrigues if (vp == NULLVP) 626d00947d8SCraig Rodrigues error = ENOENT; 627d00947d8SCraig Rodrigues else { 628d00947d8SCraig Rodrigues if (udvp == vp) 629d00947d8SCraig Rodrigues vrele(vp); 630d00947d8SCraig Rodrigues else 631d00947d8SCraig Rodrigues vput(vp); 632d00947d8SCraig Rodrigues } 633d00947d8SCraig Rodrigues 634d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 635d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 636d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 637d00947d8SCraig Rodrigues } 638d00947d8SCraig Rodrigues 639d00947d8SCraig Rodrigues if (!error) { 640d00947d8SCraig Rodrigues cn.cn_flags |= (cnp->cn_flags & HASBUF); 641d00947d8SCraig Rodrigues cnp->cn_flags = cn.cn_flags; 642d00947d8SCraig Rodrigues } 643d00947d8SCraig Rodrigues 644d00947d8SCraig Rodrigues return (error); 645d00947d8SCraig Rodrigues } 646d00947d8SCraig Rodrigues 647d00947d8SCraig Rodrigues /* 648d00947d8SCraig Rodrigues * relookup for RENAME namei operation. 649d00947d8SCraig Rodrigues * 650d00947d8SCraig Rodrigues * dvp is unionfs vnode. dvp should be locked. 651d00947d8SCraig Rodrigues */ 652d00947d8SCraig Rodrigues int 653d00947d8SCraig Rodrigues unionfs_relookup_for_rename(struct vnode *dvp, struct componentname *cnp, 654d00947d8SCraig Rodrigues struct thread *td) 655d00947d8SCraig Rodrigues { 656d00947d8SCraig Rodrigues int error; 657d00947d8SCraig Rodrigues struct vnode *udvp; 658d00947d8SCraig Rodrigues struct vnode *vp; 659d00947d8SCraig Rodrigues struct componentname cn; 660d00947d8SCraig Rodrigues 661d00947d8SCraig Rodrigues udvp = UNIONFSVPTOUPPERVP(dvp); 662d00947d8SCraig Rodrigues vp = NULLVP; 663d00947d8SCraig Rodrigues 664d00947d8SCraig Rodrigues error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr, 665d00947d8SCraig Rodrigues strlen(cnp->cn_nameptr), RENAME); 666d00947d8SCraig Rodrigues if (error) 667d00947d8SCraig Rodrigues return (error); 668d00947d8SCraig Rodrigues 669d00947d8SCraig Rodrigues if (vp != NULLVP) { 670d00947d8SCraig Rodrigues if (udvp == vp) 671d00947d8SCraig Rodrigues vrele(vp); 672d00947d8SCraig Rodrigues else 673d00947d8SCraig Rodrigues vput(vp); 674d00947d8SCraig Rodrigues } 675d00947d8SCraig Rodrigues 676d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 677d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 678d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 679d00947d8SCraig Rodrigues } 680d00947d8SCraig Rodrigues 681d00947d8SCraig Rodrigues if (!error) { 682d00947d8SCraig Rodrigues cn.cn_flags |= (cnp->cn_flags & HASBUF); 683d00947d8SCraig Rodrigues cnp->cn_flags = cn.cn_flags; 684d00947d8SCraig Rodrigues } 685d00947d8SCraig Rodrigues 686d00947d8SCraig Rodrigues return (error); 687d00947d8SCraig Rodrigues 688d00947d8SCraig Rodrigues } 689d00947d8SCraig Rodrigues 690d00947d8SCraig Rodrigues /* 691d00947d8SCraig Rodrigues * Update the unionfs_node. 692d00947d8SCraig Rodrigues * 693d00947d8SCraig Rodrigues * uvp is new locked upper vnode. unionfs vnode's lock will be exchanged to the 694d00947d8SCraig Rodrigues * uvp's lock and lower's lock will be unlocked. 695d00947d8SCraig Rodrigues */ 696d00947d8SCraig Rodrigues static void 697d00947d8SCraig Rodrigues unionfs_node_update(struct unionfs_node *unp, struct vnode *uvp, 698d00947d8SCraig Rodrigues struct thread *td) 699d00947d8SCraig Rodrigues { 700047dd67eSAttilio Rao unsigned count, lockrec; 701d00947d8SCraig Rodrigues struct vnode *vp; 702d00947d8SCraig Rodrigues struct vnode *lvp; 703a9b794ffSDaichi GOTO struct vnode *dvp; 704d00947d8SCraig Rodrigues 705d00947d8SCraig Rodrigues vp = UNIONFSTOV(unp); 706d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 707047dd67eSAttilio Rao ASSERT_VOP_ELOCKED(lvp, "unionfs_node_update"); 708a9b794ffSDaichi GOTO dvp = unp->un_dvp; 709d00947d8SCraig Rodrigues 710d00947d8SCraig Rodrigues /* 711d00947d8SCraig Rodrigues * lock update 712d00947d8SCraig Rodrigues */ 713d00947d8SCraig Rodrigues VI_LOCK(vp); 714d00947d8SCraig Rodrigues unp->un_uppervp = uvp; 715d00947d8SCraig Rodrigues vp->v_vnlock = uvp->v_vnlock; 716d00947d8SCraig Rodrigues VI_UNLOCK(vp); 717047dd67eSAttilio Rao lockrec = lvp->v_vnlock->lk_recurse; 718047dd67eSAttilio Rao for (count = 0; count < lockrec; count++) 719cb05b60aSAttilio Rao vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY); 720a9b794ffSDaichi GOTO 721a9b794ffSDaichi GOTO /* 722a9b794ffSDaichi GOTO * cache update 723a9b794ffSDaichi GOTO */ 724a9b794ffSDaichi GOTO if (unp->un_path != NULL && dvp != NULLVP && vp->v_type == VDIR) { 725a9b794ffSDaichi GOTO static struct unionfs_node_hashhead *hd; 726a9b794ffSDaichi GOTO 727a9b794ffSDaichi GOTO VI_LOCK(dvp); 728a9b794ffSDaichi GOTO hd = unionfs_get_hashhead(dvp, unp->un_path); 729a9b794ffSDaichi GOTO LIST_REMOVE(unp, un_hash); 730a9b794ffSDaichi GOTO LIST_INSERT_HEAD(hd, unp, un_hash); 731a9b794ffSDaichi GOTO VI_UNLOCK(dvp); 732a9b794ffSDaichi GOTO } 733d00947d8SCraig Rodrigues } 734d00947d8SCraig Rodrigues 735d00947d8SCraig Rodrigues /* 736d00947d8SCraig Rodrigues * Create a new shadow dir. 737d00947d8SCraig Rodrigues * 738d00947d8SCraig Rodrigues * udvp should be locked on entry and will be locked on return. 739d00947d8SCraig Rodrigues * 740d00947d8SCraig Rodrigues * If no error returned, unp will be updated. 741d00947d8SCraig Rodrigues */ 742d00947d8SCraig Rodrigues int 743d00947d8SCraig Rodrigues unionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *udvp, 744d00947d8SCraig Rodrigues struct unionfs_node *unp, struct componentname *cnp, 745d00947d8SCraig Rodrigues struct thread *td) 746d00947d8SCraig Rodrigues { 747d00947d8SCraig Rodrigues int error; 748d00947d8SCraig Rodrigues struct vnode *lvp; 749d00947d8SCraig Rodrigues struct vnode *uvp; 750d00947d8SCraig Rodrigues struct vattr va; 751d00947d8SCraig Rodrigues struct vattr lva; 752d00947d8SCraig Rodrigues struct componentname cn; 753d00947d8SCraig Rodrigues struct mount *mp; 754d00947d8SCraig Rodrigues struct ucred *cred; 755d00947d8SCraig Rodrigues struct ucred *credbk; 756d00947d8SCraig Rodrigues struct uidinfo *rootinfo; 757d00947d8SCraig Rodrigues 758d00947d8SCraig Rodrigues if (unp->un_uppervp != NULLVP) 759d00947d8SCraig Rodrigues return (EEXIST); 760d00947d8SCraig Rodrigues 761d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 762d00947d8SCraig Rodrigues uvp = NULLVP; 763d00947d8SCraig Rodrigues credbk = cnp->cn_cred; 764d00947d8SCraig Rodrigues 765d00947d8SCraig Rodrigues /* Authority change to root */ 766d00947d8SCraig Rodrigues rootinfo = uifind((uid_t)0); 767d00947d8SCraig Rodrigues cred = crdup(cnp->cn_cred); 768d00947d8SCraig Rodrigues chgproccnt(cred->cr_ruidinfo, 1, 0); 769d00947d8SCraig Rodrigues change_euid(cred, rootinfo); 770d00947d8SCraig Rodrigues change_ruid(cred, rootinfo); 771d00947d8SCraig Rodrigues change_svuid(cred, (uid_t)0); 772d00947d8SCraig Rodrigues uifree(rootinfo); 773d00947d8SCraig Rodrigues cnp->cn_cred = cred; 774d00947d8SCraig Rodrigues 775d00947d8SCraig Rodrigues memset(&cn, 0, sizeof(cn)); 776d00947d8SCraig Rodrigues 777d00947d8SCraig Rodrigues if ((error = VOP_GETATTR(lvp, &lva, cnp->cn_cred, td))) 778d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_abort; 779d00947d8SCraig Rodrigues 780d00947d8SCraig Rodrigues if ((error = unionfs_relookup(udvp, &uvp, cnp, &cn, td, cnp->cn_nameptr, cnp->cn_namelen, CREATE))) 781d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_abort; 782d00947d8SCraig Rodrigues if (uvp != NULLVP) { 783d00947d8SCraig Rodrigues if (udvp == uvp) 784d00947d8SCraig Rodrigues vrele(uvp); 785d00947d8SCraig Rodrigues else 786d00947d8SCraig Rodrigues vput(uvp); 787d00947d8SCraig Rodrigues 788d00947d8SCraig Rodrigues error = EEXIST; 789d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_free_out; 790d00947d8SCraig Rodrigues } 791d00947d8SCraig Rodrigues 792d00947d8SCraig Rodrigues if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH))) 793d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_free_out; 794d00947d8SCraig Rodrigues if ((error = VOP_LEASE(udvp, td, cn.cn_cred, LEASE_WRITE))) { 795d00947d8SCraig Rodrigues vn_finished_write(mp); 796d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_free_out; 797d00947d8SCraig Rodrigues } 798d00947d8SCraig Rodrigues unionfs_create_uppervattr_core(ump, &lva, &va, td); 799d00947d8SCraig Rodrigues 800d00947d8SCraig Rodrigues error = VOP_MKDIR(udvp, &uvp, &cn, &va); 801d00947d8SCraig Rodrigues 802d00947d8SCraig Rodrigues if (!error) { 803d00947d8SCraig Rodrigues unionfs_node_update(unp, uvp, td); 804d00947d8SCraig Rodrigues 805d00947d8SCraig Rodrigues /* 806d00947d8SCraig Rodrigues * XXX The bug which cannot set uid/gid was corrected. 807d00947d8SCraig Rodrigues * Ignore errors. 808d00947d8SCraig Rodrigues */ 809d00947d8SCraig Rodrigues va.va_type = VNON; 810d00947d8SCraig Rodrigues VOP_SETATTR(uvp, &va, cn.cn_cred, td); 811d00947d8SCraig Rodrigues } 812d00947d8SCraig Rodrigues vn_finished_write(mp); 813d00947d8SCraig Rodrigues 814d00947d8SCraig Rodrigues unionfs_mkshadowdir_free_out: 815d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 816d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 817d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 818d00947d8SCraig Rodrigues } 819d00947d8SCraig Rodrigues 820d00947d8SCraig Rodrigues unionfs_mkshadowdir_abort: 821d00947d8SCraig Rodrigues cnp->cn_cred = credbk; 822d00947d8SCraig Rodrigues chgproccnt(cred->cr_ruidinfo, -1, 0); 823d00947d8SCraig Rodrigues crfree(cred); 824d00947d8SCraig Rodrigues 825d00947d8SCraig Rodrigues return (error); 826d00947d8SCraig Rodrigues } 827d00947d8SCraig Rodrigues 828d00947d8SCraig Rodrigues /* 829d00947d8SCraig Rodrigues * Create a new whiteout. 830d00947d8SCraig Rodrigues * 831d00947d8SCraig Rodrigues * dvp should be locked on entry and will be locked on return. 832d00947d8SCraig Rodrigues */ 833d00947d8SCraig Rodrigues int 834d00947d8SCraig Rodrigues unionfs_mkwhiteout(struct vnode *dvp, struct componentname *cnp, 835d00947d8SCraig Rodrigues struct thread *td, char *path) 836d00947d8SCraig Rodrigues { 837d00947d8SCraig Rodrigues int error; 838d00947d8SCraig Rodrigues struct vnode *wvp; 839d00947d8SCraig Rodrigues struct componentname cn; 840d00947d8SCraig Rodrigues struct mount *mp; 841d00947d8SCraig Rodrigues 842d00947d8SCraig Rodrigues if (path == NULL) 843d00947d8SCraig Rodrigues path = cnp->cn_nameptr; 844d00947d8SCraig Rodrigues 845d00947d8SCraig Rodrigues wvp = NULLVP; 846d00947d8SCraig Rodrigues if ((error = unionfs_relookup(dvp, &wvp, cnp, &cn, td, path, strlen(path), CREATE))) 847d00947d8SCraig Rodrigues return (error); 848d00947d8SCraig Rodrigues if (wvp != NULLVP) { 849d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 850d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 851d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 852d00947d8SCraig Rodrigues } 853d00947d8SCraig Rodrigues if (dvp == wvp) 854d00947d8SCraig Rodrigues vrele(wvp); 855d00947d8SCraig Rodrigues else 856d00947d8SCraig Rodrigues vput(wvp); 857d00947d8SCraig Rodrigues 858d00947d8SCraig Rodrigues return (EEXIST); 859d00947d8SCraig Rodrigues } 860d00947d8SCraig Rodrigues 861d00947d8SCraig Rodrigues if ((error = vn_start_write(dvp, &mp, V_WAIT | PCATCH))) 862d00947d8SCraig Rodrigues goto unionfs_mkwhiteout_free_out; 863d00947d8SCraig Rodrigues if (!(error = VOP_LEASE(dvp, td, td->td_ucred, LEASE_WRITE))) 864d00947d8SCraig Rodrigues error = VOP_WHITEOUT(dvp, &cn, CREATE); 865d00947d8SCraig Rodrigues 866d00947d8SCraig Rodrigues vn_finished_write(mp); 867d00947d8SCraig Rodrigues 868d00947d8SCraig Rodrigues unionfs_mkwhiteout_free_out: 869d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 870d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 871d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 872d00947d8SCraig Rodrigues } 873d00947d8SCraig Rodrigues 874d00947d8SCraig Rodrigues return (error); 875d00947d8SCraig Rodrigues } 876d00947d8SCraig Rodrigues 877d00947d8SCraig Rodrigues /* 878d00947d8SCraig Rodrigues * Create a new vnode for create a new shadow file. 879d00947d8SCraig Rodrigues * 880d00947d8SCraig Rodrigues * If an error is returned, *vpp will be invalid, otherwise it will hold a 881d00947d8SCraig Rodrigues * locked, referenced and opened vnode. 882d00947d8SCraig Rodrigues * 883d00947d8SCraig Rodrigues * unp is never updated. 884df8bae1dSRodney W. Grimes */ 88580b301c3SPoul-Henning Kamp static int 886d00947d8SCraig Rodrigues unionfs_vn_create_on_upper(struct vnode **vpp, struct vnode *udvp, 887d00947d8SCraig Rodrigues struct unionfs_node *unp, struct vattr *uvap, 888d00947d8SCraig Rodrigues struct thread *td) 889df8bae1dSRodney W. Grimes { 890d00947d8SCraig Rodrigues struct unionfs_mount *ump; 891d00947d8SCraig Rodrigues struct vnode *vp; 892d00947d8SCraig Rodrigues struct vnode *lvp; 893d00947d8SCraig Rodrigues struct ucred *cred; 894d00947d8SCraig Rodrigues struct vattr lva; 895d00947d8SCraig Rodrigues int fmode; 896d00947d8SCraig Rodrigues int error; 897d00947d8SCraig Rodrigues struct componentname cn; 898d00947d8SCraig Rodrigues 899d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(UNIONFSTOV(unp)->v_mount); 900d00947d8SCraig Rodrigues vp = NULLVP; 901d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 902d00947d8SCraig Rodrigues cred = td->td_ucred; 903d00947d8SCraig Rodrigues fmode = FFLAGS(O_WRONLY | O_CREAT | O_TRUNC | O_EXCL); 904d00947d8SCraig Rodrigues error = 0; 905d00947d8SCraig Rodrigues 906d00947d8SCraig Rodrigues if ((error = VOP_GETATTR(lvp, &lva, cred, td)) != 0) 907d00947d8SCraig Rodrigues return (error); 908d00947d8SCraig Rodrigues unionfs_create_uppervattr_core(ump, &lva, uvap, td); 909d00947d8SCraig Rodrigues 910d00947d8SCraig Rodrigues if (unp->un_path == NULL) 911d00947d8SCraig Rodrigues panic("unionfs: un_path is null"); 912d00947d8SCraig Rodrigues 913d00947d8SCraig Rodrigues cn.cn_namelen = strlen(unp->un_path); 914d00947d8SCraig Rodrigues cn.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); 915d00947d8SCraig Rodrigues bcopy(unp->un_path, cn.cn_pnbuf, cn.cn_namelen + 1); 916d00947d8SCraig Rodrigues cn.cn_nameiop = CREATE; 917d00947d8SCraig Rodrigues cn.cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN); 918d00947d8SCraig Rodrigues cn.cn_lkflags = LK_EXCLUSIVE; 919d00947d8SCraig Rodrigues cn.cn_thread = td; 920d00947d8SCraig Rodrigues cn.cn_cred = cred; 921d00947d8SCraig Rodrigues cn.cn_nameptr = cn.cn_pnbuf; 922d00947d8SCraig Rodrigues cn.cn_consume = 0; 923d00947d8SCraig Rodrigues 924d00947d8SCraig Rodrigues vref(udvp); 925d00947d8SCraig Rodrigues if ((error = relookup(udvp, &vp, &cn)) != 0) 926d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out2; 927d00947d8SCraig Rodrigues vrele(udvp); 928d00947d8SCraig Rodrigues 929d00947d8SCraig Rodrigues if (vp != NULLVP) { 930d00947d8SCraig Rodrigues if (vp == udvp) 931d00947d8SCraig Rodrigues vrele(vp); 932d00947d8SCraig Rodrigues else 933d00947d8SCraig Rodrigues vput(vp); 934d00947d8SCraig Rodrigues error = EEXIST; 935d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out1; 936d00947d8SCraig Rodrigues } 937d00947d8SCraig Rodrigues 938d00947d8SCraig Rodrigues if ((error = VOP_LEASE(udvp, td, cred, LEASE_WRITE)) != 0) 939d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out1; 940d00947d8SCraig Rodrigues 941d00947d8SCraig Rodrigues if ((error = VOP_CREATE(udvp, &vp, &cn, uvap)) != 0) 942d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out1; 943d00947d8SCraig Rodrigues 9449e223287SKonstantin Belousov if ((error = VOP_OPEN(vp, fmode, cred, td, NULL)) != 0) { 945d00947d8SCraig Rodrigues vput(vp); 946d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out1; 947d00947d8SCraig Rodrigues } 948d00947d8SCraig Rodrigues vp->v_writecount++; 949d00947d8SCraig Rodrigues *vpp = vp; 950d00947d8SCraig Rodrigues 951d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out1: 95222db15c0SAttilio Rao VOP_UNLOCK(udvp, 0); 953d00947d8SCraig Rodrigues 954d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out2: 955d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 956d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 957d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 958d00947d8SCraig Rodrigues } 959d00947d8SCraig Rodrigues 960d00947d8SCraig Rodrigues return (error); 961d00947d8SCraig Rodrigues } 962d00947d8SCraig Rodrigues 963d00947d8SCraig Rodrigues /* 964d00947d8SCraig Rodrigues * Copy from lvp to uvp. 965d00947d8SCraig Rodrigues * 966d00947d8SCraig Rodrigues * lvp and uvp should be locked and opened on entry and will be locked and 967d00947d8SCraig Rodrigues * opened on return. 968d00947d8SCraig Rodrigues */ 969d00947d8SCraig Rodrigues static int 970d00947d8SCraig Rodrigues unionfs_copyfile_core(struct vnode *lvp, struct vnode *uvp, 971d00947d8SCraig Rodrigues struct ucred *cred, struct thread *td) 972d00947d8SCraig Rodrigues { 973d00947d8SCraig Rodrigues int error; 974d00947d8SCraig Rodrigues off_t offset; 975d00947d8SCraig Rodrigues int count; 976d00947d8SCraig Rodrigues int bufoffset; 977df8bae1dSRodney W. Grimes char *buf; 978df8bae1dSRodney W. Grimes struct uio uio; 979df8bae1dSRodney W. Grimes struct iovec iov; 980df8bae1dSRodney W. Grimes 981d00947d8SCraig Rodrigues error = 0; 982d00947d8SCraig Rodrigues memset(&uio, 0, sizeof(uio)); 9832a31267eSMatthew Dillon 984b40ce416SJulian Elischer uio.uio_td = td; 985df8bae1dSRodney W. Grimes uio.uio_segflg = UIO_SYSSPACE; 986df8bae1dSRodney W. Grimes uio.uio_offset = 0; 987df8bae1dSRodney W. Grimes 988d00947d8SCraig Rodrigues if ((error = VOP_LEASE(lvp, td, cred, LEASE_READ)) != 0) 989d00947d8SCraig Rodrigues return (error); 990d00947d8SCraig Rodrigues if ((error = VOP_LEASE(uvp, td, cred, LEASE_WRITE)) != 0) 991d00947d8SCraig Rodrigues return (error); 992a163d034SWarner Losh buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK); 993df8bae1dSRodney W. Grimes 994d00947d8SCraig Rodrigues while (error == 0) { 995d00947d8SCraig Rodrigues offset = uio.uio_offset; 996df8bae1dSRodney W. Grimes 997df8bae1dSRodney W. Grimes uio.uio_iov = &iov; 998df8bae1dSRodney W. Grimes uio.uio_iovcnt = 1; 999df8bae1dSRodney W. Grimes iov.iov_base = buf; 1000df8bae1dSRodney W. Grimes iov.iov_len = MAXBSIZE; 1001df8bae1dSRodney W. Grimes uio.uio_resid = iov.iov_len; 1002df8bae1dSRodney W. Grimes uio.uio_rw = UIO_READ; 1003df8bae1dSRodney W. Grimes 1004d00947d8SCraig Rodrigues if ((error = VOP_READ(lvp, &uio, 0, cred)) != 0) 10052a31267eSMatthew Dillon break; 10062a31267eSMatthew Dillon if ((count = MAXBSIZE - uio.uio_resid) == 0) 10072a31267eSMatthew Dillon break; 10082a31267eSMatthew Dillon 1009d00947d8SCraig Rodrigues bufoffset = 0; 10102a31267eSMatthew Dillon while (bufoffset < count) { 1011df8bae1dSRodney W. Grimes uio.uio_iov = &iov; 1012df8bae1dSRodney W. Grimes uio.uio_iovcnt = 1; 10132a31267eSMatthew Dillon iov.iov_base = buf + bufoffset; 10142a31267eSMatthew Dillon iov.iov_len = count - bufoffset; 10152a31267eSMatthew Dillon uio.uio_offset = offset + bufoffset; 1016df8bae1dSRodney W. Grimes uio.uio_resid = iov.iov_len; 1017d00947d8SCraig Rodrigues uio.uio_rw = UIO_WRITE; 1018df8bae1dSRodney W. Grimes 1019d00947d8SCraig Rodrigues if ((error = VOP_WRITE(uvp, &uio, 0, cred)) != 0) 1020df8bae1dSRodney W. Grimes break; 1021d00947d8SCraig Rodrigues 10222a31267eSMatthew Dillon bufoffset += (count - bufoffset) - uio.uio_resid; 1023df8bae1dSRodney W. Grimes } 1024d00947d8SCraig Rodrigues 10252a31267eSMatthew Dillon uio.uio_offset = offset + bufoffset; 1026d00947d8SCraig Rodrigues } 1027df8bae1dSRodney W. Grimes 1028df8bae1dSRodney W. Grimes free(buf, M_TEMP); 1029d00947d8SCraig Rodrigues 1030df8bae1dSRodney W. Grimes return (error); 1031df8bae1dSRodney W. Grimes } 1032df8bae1dSRodney W. Grimes 1033df8bae1dSRodney W. Grimes /* 1034d00947d8SCraig Rodrigues * Copy file from lower to upper. 10352a31267eSMatthew Dillon * 1036d00947d8SCraig Rodrigues * If you need copy of the contents, set 1 to docopy. Otherwise, set 0 to 1037d00947d8SCraig Rodrigues * docopy. 1038d00947d8SCraig Rodrigues * 1039d00947d8SCraig Rodrigues * If no error returned, unp will be updated. 1040996c772fSJohn Dyson */ 1041996c772fSJohn Dyson int 1042d00947d8SCraig Rodrigues unionfs_copyfile(struct unionfs_node *unp, int docopy, struct ucred *cred, 1043d00947d8SCraig Rodrigues struct thread *td) 1044996c772fSJohn Dyson { 1045996c772fSJohn Dyson int error; 1046f2a2857bSKirk McKusick struct mount *mp; 1047d00947d8SCraig Rodrigues struct vnode *udvp; 1048d00947d8SCraig Rodrigues struct vnode *lvp; 1049d00947d8SCraig Rodrigues struct vnode *uvp; 1050d00947d8SCraig Rodrigues struct vattr uva; 1051996c772fSJohn Dyson 1052d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 1053d00947d8SCraig Rodrigues uvp = NULLVP; 1054d00947d8SCraig Rodrigues 1055d00947d8SCraig Rodrigues if ((UNIONFSTOV(unp)->v_mount->mnt_flag & MNT_RDONLY)) 1056d00947d8SCraig Rodrigues return (EROFS); 1057d00947d8SCraig Rodrigues if (unp->un_dvp == NULLVP) 1058d00947d8SCraig Rodrigues return (EINVAL); 1059d00947d8SCraig Rodrigues if (unp->un_uppervp != NULLVP) 1060d00947d8SCraig Rodrigues return (EEXIST); 1061d00947d8SCraig Rodrigues udvp = VTOUNIONFS(unp->un_dvp)->un_uppervp; 1062d00947d8SCraig Rodrigues if (udvp == NULLVP) 1063d00947d8SCraig Rodrigues return (EROFS); 1064d00947d8SCraig Rodrigues if ((udvp->v_mount->mnt_flag & MNT_RDONLY)) 1065d00947d8SCraig Rodrigues return (EROFS); 1066d00947d8SCraig Rodrigues 1067d00947d8SCraig Rodrigues error = VOP_ACCESS(lvp, VREAD, cred, td); 1068d00947d8SCraig Rodrigues if (error != 0) 10695842d4e5SKATO Takenori return (error); 10705842d4e5SKATO Takenori 1071d00947d8SCraig Rodrigues if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH)) != 0) 1072996c772fSJohn Dyson return (error); 1073d00947d8SCraig Rodrigues error = unionfs_vn_create_on_upper(&uvp, udvp, unp, &uva, td); 1074d00947d8SCraig Rodrigues if (error != 0) { 1075f2a2857bSKirk McKusick vn_finished_write(mp); 1076f2a2857bSKirk McKusick return (error); 1077f2a2857bSKirk McKusick } 1078996c772fSJohn Dyson 1079d00947d8SCraig Rodrigues if (docopy != 0) { 10809e223287SKonstantin Belousov error = VOP_OPEN(lvp, FREAD, cred, td, NULL); 1081996c772fSJohn Dyson if (error == 0) { 1082d00947d8SCraig Rodrigues error = unionfs_copyfile_core(lvp, uvp, cred, td); 1083d00947d8SCraig Rodrigues VOP_CLOSE(lvp, FREAD, cred, td); 1084996c772fSJohn Dyson } 1085d00947d8SCraig Rodrigues } 1086d00947d8SCraig Rodrigues VOP_CLOSE(uvp, FWRITE, cred, td); 1087d00947d8SCraig Rodrigues uvp->v_writecount--; 1088996c772fSJohn Dyson 1089f2a2857bSKirk McKusick vn_finished_write(mp); 1090d00947d8SCraig Rodrigues 1091996c772fSJohn Dyson if (error == 0) { 1092d00947d8SCraig Rodrigues /* Reset the attributes. Ignore errors. */ 1093d00947d8SCraig Rodrigues uva.va_type = VNON; 1094d00947d8SCraig Rodrigues VOP_SETATTR(uvp, &uva, cred, td); 1095996c772fSJohn Dyson } 1096996c772fSJohn Dyson 1097d00947d8SCraig Rodrigues unionfs_node_update(unp, uvp, td); 1098996c772fSJohn Dyson 10992a31267eSMatthew Dillon return (error); 1100b422956cSPoul-Henning Kamp } 1101996c772fSJohn Dyson 11022a31267eSMatthew Dillon /* 1103d00947d8SCraig Rodrigues * It checks whether vp can rmdir. (check empty) 11042a31267eSMatthew Dillon * 1105d00947d8SCraig Rodrigues * vp is unionfs vnode. 1106d00947d8SCraig Rodrigues * vp should be locked. 1107df8bae1dSRodney W. Grimes */ 1108df8bae1dSRodney W. Grimes int 1109d00947d8SCraig Rodrigues unionfs_check_rmdir(struct vnode *vp, struct ucred *cred, struct thread *td) 1110df8bae1dSRodney W. Grimes { 1111df8bae1dSRodney W. Grimes int error; 1112d00947d8SCraig Rodrigues int eofflag; 1113d00947d8SCraig Rodrigues int lookuperr; 1114d00947d8SCraig Rodrigues struct vnode *uvp; 1115d00947d8SCraig Rodrigues struct vnode *lvp; 1116d00947d8SCraig Rodrigues struct vnode *tvp; 1117df8bae1dSRodney W. Grimes struct vattr va; 1118df8bae1dSRodney W. Grimes struct componentname cn; 1119df8bae1dSRodney W. Grimes /* 1120d00947d8SCraig Rodrigues * The size of buf needs to be larger than DIRBLKSIZ. 1121df8bae1dSRodney W. Grimes */ 1122d00947d8SCraig Rodrigues char buf[256 * 6]; 1123d00947d8SCraig Rodrigues struct dirent *dp; 1124d00947d8SCraig Rodrigues struct dirent *edp; 1125d00947d8SCraig Rodrigues struct uio uio; 1126d00947d8SCraig Rodrigues struct iovec iov; 1127df8bae1dSRodney W. Grimes 1128d00947d8SCraig Rodrigues ASSERT_VOP_ELOCKED(vp, "unionfs_check_rmdir"); 1129df8bae1dSRodney W. Grimes 1130d00947d8SCraig Rodrigues eofflag = 0; 1131d00947d8SCraig Rodrigues uvp = UNIONFSVPTOUPPERVP(vp); 1132d00947d8SCraig Rodrigues lvp = UNIONFSVPTOLOWERVP(vp); 1133df8bae1dSRodney W. Grimes 1134d00947d8SCraig Rodrigues /* check opaque */ 1135d00947d8SCraig Rodrigues if ((error = VOP_GETATTR(uvp, &va, cred, td)) != 0) 1136df8bae1dSRodney W. Grimes return (error); 1137d00947d8SCraig Rodrigues if (va.va_flags & OPAQUE) 1138d00947d8SCraig Rodrigues return (0); 1139df8bae1dSRodney W. Grimes 1140d00947d8SCraig Rodrigues /* open vnode */ 11413282e2c4SDaichi GOTO #ifdef MAC 114230d239bcSRobert Watson if ((error = mac_vnode_check_open(cred, vp, VEXEC|VREAD)) != 0) 11433282e2c4SDaichi GOTO return (error); 11443282e2c4SDaichi GOTO #endif 11453282e2c4SDaichi GOTO if ((error = VOP_ACCESS(vp, VEXEC|VREAD, cred, td)) != 0) 11463282e2c4SDaichi GOTO return (error); 11479e223287SKonstantin Belousov if ((error = VOP_OPEN(vp, FREAD, cred, td, NULL)) != 0) 1148996c772fSJohn Dyson return (error); 1149996c772fSJohn Dyson 1150d00947d8SCraig Rodrigues uio.uio_rw = UIO_READ; 1151d00947d8SCraig Rodrigues uio.uio_segflg = UIO_SYSSPACE; 1152d00947d8SCraig Rodrigues uio.uio_td = td; 1153d00947d8SCraig Rodrigues uio.uio_offset = 0; 1154996c772fSJohn Dyson 1155d00947d8SCraig Rodrigues #ifdef MAC 115630d239bcSRobert Watson error = mac_vnode_check_readdir(td->td_ucred, lvp); 1157d00947d8SCraig Rodrigues #endif 1158d00947d8SCraig Rodrigues while (!error && !eofflag) { 1159d00947d8SCraig Rodrigues iov.iov_base = buf; 1160d00947d8SCraig Rodrigues iov.iov_len = sizeof(buf); 1161d00947d8SCraig Rodrigues uio.uio_iov = &iov; 1162d00947d8SCraig Rodrigues uio.uio_iovcnt = 1; 1163d00947d8SCraig Rodrigues uio.uio_resid = iov.iov_len; 1164996c772fSJohn Dyson 1165d00947d8SCraig Rodrigues error = VOP_READDIR(lvp, &uio, cred, &eofflag, NULL, NULL); 1166a68ae31cSDaichi GOTO if (error != 0) 1167d00947d8SCraig Rodrigues break; 1168a68ae31cSDaichi GOTO if (eofflag == 0 && uio.uio_resid == sizeof(buf)) { 1169a68ae31cSDaichi GOTO #ifdef DIAGNOSTIC 1170a68ae31cSDaichi GOTO panic("bad readdir response from lower FS."); 1171a68ae31cSDaichi GOTO #endif 1172a68ae31cSDaichi GOTO break; 1173a68ae31cSDaichi GOTO } 1174996c772fSJohn Dyson 1175d00947d8SCraig Rodrigues edp = (struct dirent*)&buf[sizeof(buf) - uio.uio_resid]; 1176d00947d8SCraig Rodrigues for (dp = (struct dirent*)buf; !error && dp < edp; 1177d00947d8SCraig Rodrigues dp = (struct dirent*)((caddr_t)dp + dp->d_reclen)) { 1178d00947d8SCraig Rodrigues if (dp->d_type == DT_WHT || 1179d00947d8SCraig Rodrigues (dp->d_namlen == 1 && dp->d_name[0] == '.') || 1180d00947d8SCraig Rodrigues (dp->d_namlen == 2 && !bcmp(dp->d_name, "..", 2))) 1181d00947d8SCraig Rodrigues continue; 1182df8bae1dSRodney W. Grimes 1183d00947d8SCraig Rodrigues cn.cn_namelen = dp->d_namlen; 1184d00947d8SCraig Rodrigues cn.cn_pnbuf = NULL; 1185d00947d8SCraig Rodrigues cn.cn_nameptr = dp->d_name; 1186d00947d8SCraig Rodrigues cn.cn_nameiop = LOOKUP; 1187d00947d8SCraig Rodrigues cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN); 1188d00947d8SCraig Rodrigues cn.cn_lkflags = LK_EXCLUSIVE; 1189b40ce416SJulian Elischer cn.cn_thread = td; 1190d00947d8SCraig Rodrigues cn.cn_cred = cred; 1191df8bae1dSRodney W. Grimes cn.cn_consume = 0; 1192df8bae1dSRodney W. Grimes 11932a31267eSMatthew Dillon /* 1194d00947d8SCraig Rodrigues * check entry in lower. 1195d00947d8SCraig Rodrigues * Sometimes, readdir function returns 1196d00947d8SCraig Rodrigues * wrong entry. 11972a31267eSMatthew Dillon */ 1198d00947d8SCraig Rodrigues lookuperr = VOP_LOOKUP(lvp, &tvp, &cn); 1199df8bae1dSRodney W. Grimes 1200d00947d8SCraig Rodrigues if (!lookuperr) 1201d00947d8SCraig Rodrigues vput(tvp); 12022a31267eSMatthew Dillon else 1203d00947d8SCraig Rodrigues continue; /* skip entry */ 1204df8bae1dSRodney W. Grimes 1205df8bae1dSRodney W. Grimes /* 1206d00947d8SCraig Rodrigues * check entry 1207d00947d8SCraig Rodrigues * If it has no exist/whiteout entry in upper, 1208d00947d8SCraig Rodrigues * directory is not empty. 1209df8bae1dSRodney W. Grimes */ 1210d00947d8SCraig Rodrigues cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN); 1211d00947d8SCraig Rodrigues lookuperr = VOP_LOOKUP(uvp, &tvp, &cn); 1212df8bae1dSRodney W. Grimes 1213d00947d8SCraig Rodrigues if (!lookuperr) 1214d00947d8SCraig Rodrigues vput(tvp); 1215df8bae1dSRodney W. Grimes 1216d00947d8SCraig Rodrigues /* ignore exist or whiteout entry */ 1217d00947d8SCraig Rodrigues if (!lookuperr || 1218d00947d8SCraig Rodrigues (lookuperr == ENOENT && (cn.cn_flags & ISWHITEOUT))) 121901634480SBrian Feldman continue; 1220d00947d8SCraig Rodrigues 1221d00947d8SCraig Rodrigues error = ENOTEMPTY; 1222df8bae1dSRodney W. Grimes } 1223996c772fSJohn Dyson } 1224996c772fSJohn Dyson 1225d00947d8SCraig Rodrigues /* close vnode */ 1226d00947d8SCraig Rodrigues VOP_CLOSE(vp, FREAD, cred, td); 1227d00947d8SCraig Rodrigues 1228d00947d8SCraig Rodrigues return (error); 1229d00947d8SCraig Rodrigues } 1230d00947d8SCraig Rodrigues 1231d00947d8SCraig Rodrigues #ifdef DIAGNOSTIC 1232d00947d8SCraig Rodrigues 1233d00947d8SCraig Rodrigues struct vnode * 1234d00947d8SCraig Rodrigues unionfs_checkuppervp(struct vnode *vp, char *fil, int lno) 1235996c772fSJohn Dyson { 1236d00947d8SCraig Rodrigues struct unionfs_node *unp; 1237996c772fSJohn Dyson 1238d00947d8SCraig Rodrigues unp = VTOUNIONFS(vp); 1239996c772fSJohn Dyson 1240d00947d8SCraig Rodrigues #ifdef notyet 1241d00947d8SCraig Rodrigues if (vp->v_op != unionfs_vnodeop_p) { 1242d00947d8SCraig Rodrigues printf("unionfs_checkuppervp: on non-unionfs-node.\n"); 1243d00947d8SCraig Rodrigues #ifdef KDB 12443de213ccSRobert Watson kdb_enter(KDB_WHY_UNIONFS, 12453de213ccSRobert Watson "unionfs_checkuppervp: on non-unionfs-node.\n"); 1246d00947d8SCraig Rodrigues #endif 1247d00947d8SCraig Rodrigues panic("unionfs_checkuppervp"); 1248d00947d8SCraig Rodrigues }; 1249d00947d8SCraig Rodrigues #endif 1250d00947d8SCraig Rodrigues return (unp->un_uppervp); 1251d8c6e674SDavid Schultz } 1252996c772fSJohn Dyson 1253996c772fSJohn Dyson struct vnode * 1254d00947d8SCraig Rodrigues unionfs_checklowervp(struct vnode *vp, char *fil, int lno) 1255996c772fSJohn Dyson { 1256d00947d8SCraig Rodrigues struct unionfs_node *unp; 1257996c772fSJohn Dyson 1258d00947d8SCraig Rodrigues unp = VTOUNIONFS(vp); 1259996c772fSJohn Dyson 1260d00947d8SCraig Rodrigues #ifdef notyet 1261d00947d8SCraig Rodrigues if (vp->v_op != unionfs_vnodeop_p) { 1262d00947d8SCraig Rodrigues printf("unionfs_checklowervp: on non-unionfs-node.\n"); 1263d00947d8SCraig Rodrigues #ifdef KDB 12643de213ccSRobert Watson kdb_enter(KDB_WHY_UNIONFS, 12653de213ccSRobert Watson "unionfs_checklowervp: on non-unionfs-node.\n"); 1266d00947d8SCraig Rodrigues #endif 1267d00947d8SCraig Rodrigues panic("unionfs_checklowervp"); 12688c14bf40SPeter Wemm }; 1269d00947d8SCraig Rodrigues #endif 1270d00947d8SCraig Rodrigues return (unp->un_lowervp); 1271d00947d8SCraig Rodrigues } 1272d00947d8SCraig Rodrigues #endif 1273