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 116938161d6SDaichi GOTO KASSERT((uvp == NULLVP || uvp->v_type == VDIR || uvp->v_type == VSOCK), 117938161d6SDaichi GOTO ("unionfs_get_cached_vdir: v_type != VDIR/VSOCK")); 118938161d6SDaichi GOTO KASSERT((lvp == NULLVP || lvp->v_type == VDIR || lvp->v_type == VSOCK), 119938161d6SDaichi GOTO ("unionfs_get_cached_vdir: v_type != VDIR/VSOCK")); 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 153938161d6SDaichi GOTO KASSERT((uncp->un_uppervp==NULLVP || uncp->un_uppervp->v_type==VDIR || 154938161d6SDaichi GOTO uncp->un_uppervp->v_type==VSOCK), 155938161d6SDaichi GOTO ("unionfs_ins_cached_vdir: v_type != VDIR/VSOCK")); 156938161d6SDaichi GOTO KASSERT((uncp->un_lowervp==NULLVP || uncp->un_lowervp->v_type==VDIR || 157938161d6SDaichi GOTO uncp->un_lowervp->v_type==VSOCK), 158938161d6SDaichi GOTO ("unionfs_ins_cached_vdir: v_type != VDIR/VSOCK")); 159a9b794ffSDaichi GOTO 160a9b794ffSDaichi GOTO VI_LOCK(dvp); 161a9b794ffSDaichi GOTO hd = unionfs_get_hashhead(dvp, path); 162a9b794ffSDaichi GOTO LIST_FOREACH(unp, hd, un_hash) { 163a9b794ffSDaichi GOTO if (!strcmp(unp->un_path, path)) { 164a9b794ffSDaichi GOTO vp = UNIONFSTOV(unp); 165a9b794ffSDaichi GOTO VI_LOCK_FLAGS(vp, MTX_DUPOK); 166a9b794ffSDaichi GOTO vp->v_iflag &= ~VI_OWEINACT; 167a9b794ffSDaichi GOTO if ((vp->v_iflag & (VI_DOOMED | VI_DOINGINACT)) != 0) { 168a9b794ffSDaichi GOTO LIST_INSERT_HEAD(hd, uncp, un_hash); 169a9b794ffSDaichi GOTO VI_UNLOCK(vp); 170a9b794ffSDaichi GOTO vp = NULLVP; 171a9b794ffSDaichi GOTO } else 172a9b794ffSDaichi GOTO VI_UNLOCK(vp); 173a9b794ffSDaichi GOTO VI_UNLOCK(dvp); 174a9b794ffSDaichi GOTO return (vp); 175a9b794ffSDaichi GOTO } 176a9b794ffSDaichi GOTO } 177a9b794ffSDaichi GOTO 178a9b794ffSDaichi GOTO LIST_INSERT_HEAD(hd, uncp, un_hash); 179a9b794ffSDaichi GOTO VI_UNLOCK(dvp); 180a9b794ffSDaichi GOTO 181a9b794ffSDaichi GOTO return (NULLVP); 182a9b794ffSDaichi GOTO } 183a9b794ffSDaichi GOTO 184a9b794ffSDaichi GOTO /* 185a9b794ffSDaichi GOTO * Remove the vnode. (only VDIR) 186a9b794ffSDaichi GOTO */ 187a9b794ffSDaichi GOTO static void 188a9b794ffSDaichi GOTO unionfs_rem_cached_vdir(struct unionfs_node *unp, struct vnode *dvp) 189a9b794ffSDaichi GOTO { 190a9b794ffSDaichi GOTO KASSERT((unp != NULL), ("unionfs_rem_cached_vdir: null node")); 191a9b794ffSDaichi GOTO KASSERT((dvp != NULLVP), 192a9b794ffSDaichi GOTO ("unionfs_rem_cached_vdir: null parent vnode")); 193a9b794ffSDaichi GOTO KASSERT((unp->un_hash.le_prev != NULL), 194a9b794ffSDaichi GOTO ("unionfs_rem_cached_vdir: null hash")); 195a9b794ffSDaichi GOTO 196a9b794ffSDaichi GOTO VI_LOCK(dvp); 197a9b794ffSDaichi GOTO LIST_REMOVE(unp, un_hash); 198a9b794ffSDaichi GOTO VI_UNLOCK(dvp); 199a9b794ffSDaichi GOTO } 200a9b794ffSDaichi GOTO 201d00947d8SCraig Rodrigues /* 202d00947d8SCraig Rodrigues * Make a new or get existing unionfs node. 2032a31267eSMatthew Dillon * 204d00947d8SCraig Rodrigues * uppervp and lowervp should be unlocked. Because if new unionfs vnode is 205d00947d8SCraig Rodrigues * locked, uppervp or lowervp is locked too. In order to prevent dead lock, 206d00947d8SCraig Rodrigues * you should not lock plurality simultaneously. 207df8bae1dSRodney W. Grimes */ 208d00947d8SCraig Rodrigues int 209d00947d8SCraig Rodrigues unionfs_nodeget(struct mount *mp, struct vnode *uppervp, 210d00947d8SCraig Rodrigues struct vnode *lowervp, struct vnode *dvp, 211d00947d8SCraig Rodrigues struct vnode **vpp, struct componentname *cnp, 212d00947d8SCraig Rodrigues struct thread *td) 213d00947d8SCraig Rodrigues { 214d00947d8SCraig Rodrigues struct unionfs_mount *ump; 215d00947d8SCraig Rodrigues struct unionfs_node *unp; 216d00947d8SCraig Rodrigues struct vnode *vp; 217d00947d8SCraig Rodrigues int error; 218d00947d8SCraig Rodrigues int lkflags; 219a9b794ffSDaichi GOTO enum vtype vt; 220d00947d8SCraig Rodrigues char *path; 221df8bae1dSRodney W. Grimes 222d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 223d00947d8SCraig Rodrigues lkflags = (cnp ? cnp->cn_lkflags : 0); 224dc2dd185SDaichi GOTO path = (cnp ? cnp->cn_nameptr : NULL); 225a9b794ffSDaichi GOTO *vpp = NULLVP; 2262a31267eSMatthew Dillon 227d00947d8SCraig Rodrigues if (uppervp == NULLVP && lowervp == NULLVP) 228d00947d8SCraig Rodrigues panic("unionfs_nodeget: upper and lower is null"); 229d00947d8SCraig Rodrigues 230a9b794ffSDaichi GOTO vt = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type); 231a9b794ffSDaichi GOTO 232d00947d8SCraig Rodrigues /* If it has no ISLASTCN flag, path check is skipped. */ 233dc2dd185SDaichi GOTO if (cnp && !(cnp->cn_flags & ISLASTCN)) 234d00947d8SCraig Rodrigues path = NULL; 235d00947d8SCraig Rodrigues 236a9b794ffSDaichi GOTO /* check the vdir cache */ 237938161d6SDaichi GOTO if (path != NULL && dvp != NULLVP && (vt == VDIR || vt == VSOCK)) { 238a9b794ffSDaichi GOTO vp = unionfs_get_cached_vdir(uppervp, lowervp, dvp, path); 239a9b794ffSDaichi GOTO if (vp != NULLVP) { 240a9b794ffSDaichi GOTO vref(vp); 241a9b794ffSDaichi GOTO *vpp = vp; 242a9b794ffSDaichi GOTO goto unionfs_nodeget_out; 243a9b794ffSDaichi GOTO } 244a9b794ffSDaichi GOTO } 245a9b794ffSDaichi GOTO 246d00947d8SCraig Rodrigues if ((uppervp == NULLVP || ump->um_uppervp != uppervp) || 247d00947d8SCraig Rodrigues (lowervp == NULLVP || ump->um_lowervp != lowervp)) { 248a9b794ffSDaichi GOTO /* dvp will be NULLVP only in case of root vnode. */ 249d00947d8SCraig Rodrigues if (dvp == NULLVP) 250d00947d8SCraig Rodrigues return (EINVAL); 251d00947d8SCraig Rodrigues } 252d00947d8SCraig Rodrigues 253df8bae1dSRodney W. Grimes /* 254d00947d8SCraig Rodrigues * Do the MALLOC before the getnewvnode since doing so afterward 255d00947d8SCraig Rodrigues * might cause a bogus v_data pointer to get dereferenced elsewhere 256d00947d8SCraig Rodrigues * if MALLOC should block. 257df8bae1dSRodney W. Grimes */ 258d00947d8SCraig Rodrigues MALLOC(unp, struct unionfs_node *, sizeof(struct unionfs_node), 259d00947d8SCraig Rodrigues M_UNIONFSNODE, M_WAITOK | M_ZERO); 260d00947d8SCraig Rodrigues 261d00947d8SCraig Rodrigues error = getnewvnode("unionfs", mp, &unionfs_vnodeops, &vp); 262dc2dd185SDaichi GOTO if (error != 0) { 263d00947d8SCraig Rodrigues FREE(unp, M_UNIONFSNODE); 264d00947d8SCraig Rodrigues return (error); 265d00947d8SCraig Rodrigues } 26661b9d89fSTor Egge error = insmntque(vp, mp); /* XXX: Too early for mpsafe fs */ 26761b9d89fSTor Egge if (error != 0) { 26861b9d89fSTor Egge FREE(unp, M_UNIONFSNODE); 26961b9d89fSTor Egge return (error); 27061b9d89fSTor Egge } 271d00947d8SCraig Rodrigues if (dvp != NULLVP) 272d00947d8SCraig Rodrigues vref(dvp); 273d00947d8SCraig Rodrigues if (uppervp != NULLVP) 274d00947d8SCraig Rodrigues vref(uppervp); 275d00947d8SCraig Rodrigues if (lowervp != NULLVP) 276d00947d8SCraig Rodrigues vref(lowervp); 277d00947d8SCraig Rodrigues 278938161d6SDaichi GOTO switch (vt) { 279938161d6SDaichi GOTO case VDIR: 280a9b794ffSDaichi GOTO unp->un_hashtbl = hashinit(NUNIONFSNODECACHE, M_UNIONFSHASH, 281a9b794ffSDaichi GOTO &(unp->un_hashmask)); 282938161d6SDaichi GOTO break; 283938161d6SDaichi GOTO case VSOCK: 284938161d6SDaichi GOTO if (uppervp != NULLVP) 285938161d6SDaichi GOTO vp->v_socket = uppervp->v_socket; 286938161d6SDaichi GOTO else 287938161d6SDaichi GOTO vp->v_socket = lowervp->v_socket; 288938161d6SDaichi GOTO break; 289938161d6SDaichi GOTO default: 290938161d6SDaichi GOTO break; 291938161d6SDaichi GOTO } 292a9b794ffSDaichi GOTO 293d00947d8SCraig Rodrigues unp->un_vnode = vp; 294d00947d8SCraig Rodrigues unp->un_uppervp = uppervp; 295d00947d8SCraig Rodrigues unp->un_lowervp = lowervp; 296d00947d8SCraig Rodrigues unp->un_dvp = dvp; 297d00947d8SCraig Rodrigues if (uppervp != NULLVP) 298d00947d8SCraig Rodrigues vp->v_vnlock = uppervp->v_vnlock; 299d00947d8SCraig Rodrigues else 300d00947d8SCraig Rodrigues vp->v_vnlock = lowervp->v_vnlock; 301d00947d8SCraig Rodrigues 302dc2dd185SDaichi GOTO if (path != NULL) { 303d00947d8SCraig Rodrigues unp->un_path = (char *) 304d00947d8SCraig Rodrigues malloc(cnp->cn_namelen +1, M_UNIONFSPATH, M_WAITOK|M_ZERO); 305d00947d8SCraig Rodrigues bcopy(cnp->cn_nameptr, unp->un_path, cnp->cn_namelen); 306d00947d8SCraig Rodrigues unp->un_path[cnp->cn_namelen] = '\0'; 307d00947d8SCraig Rodrigues } 308a9b794ffSDaichi GOTO vp->v_type = vt; 309d00947d8SCraig Rodrigues vp->v_data = unp; 310d00947d8SCraig Rodrigues 311d00947d8SCraig Rodrigues if ((uppervp != NULLVP && ump->um_uppervp == uppervp) && 312d00947d8SCraig Rodrigues (lowervp != NULLVP && ump->um_lowervp == lowervp)) 313d00947d8SCraig Rodrigues vp->v_vflag |= VV_ROOT; 314d00947d8SCraig Rodrigues 315938161d6SDaichi GOTO if (path != NULL && dvp != NULLVP && (vt == VDIR || vt == VSOCK)) 316a9b794ffSDaichi GOTO *vpp = unionfs_ins_cached_vdir(unp, dvp, path); 317a9b794ffSDaichi GOTO if ((*vpp) != NULLVP) { 318a9b794ffSDaichi GOTO if (dvp != NULLVP) 319a9b794ffSDaichi GOTO vrele(dvp); 320a9b794ffSDaichi GOTO if (uppervp != NULLVP) 321a9b794ffSDaichi GOTO vrele(uppervp); 322a9b794ffSDaichi GOTO if (lowervp != NULLVP) 323a9b794ffSDaichi GOTO vrele(lowervp); 324a9b794ffSDaichi GOTO 325a9b794ffSDaichi GOTO unp->un_uppervp = NULLVP; 326a9b794ffSDaichi GOTO unp->un_lowervp = NULLVP; 327a9b794ffSDaichi GOTO unp->un_dvp = NULLVP; 328a9b794ffSDaichi GOTO vrele(vp); 329a9b794ffSDaichi GOTO vp = *vpp; 330a9b794ffSDaichi GOTO vref(vp); 331a9b794ffSDaichi GOTO } else 332a9b794ffSDaichi GOTO *vpp = vp; 333a9b794ffSDaichi GOTO 334a9b794ffSDaichi GOTO unionfs_nodeget_out: 335d00947d8SCraig Rodrigues if (lkflags & LK_TYPE_MASK) 336cb05b60aSAttilio Rao vn_lock(vp, lkflags | LK_RETRY); 337df8bae1dSRodney W. Grimes 338d00947d8SCraig Rodrigues return (0); 339996c772fSJohn Dyson } 340df8bae1dSRodney W. Grimes 3412a31267eSMatthew Dillon /* 342dc2dd185SDaichi GOTO * Clean up the unionfs node. 3432a31267eSMatthew Dillon */ 344d00947d8SCraig Rodrigues void 345dc2dd185SDaichi GOTO unionfs_noderem(struct vnode *vp, struct thread *td) 346d00947d8SCraig Rodrigues { 347d00947d8SCraig Rodrigues int vfslocked; 348d00947d8SCraig Rodrigues struct unionfs_node *unp; 349acc4bab1SCraig Rodrigues struct unionfs_node_status *unsp, *unsp_tmp; 350d00947d8SCraig Rodrigues struct vnode *lvp; 351d00947d8SCraig Rodrigues struct vnode *uvp; 352a9b794ffSDaichi GOTO struct vnode *dvp; 3532a31267eSMatthew Dillon 3542a31267eSMatthew Dillon /* 355d00947d8SCraig Rodrigues * Use the interlock to protect the clearing of v_data to 356d00947d8SCraig Rodrigues * prevent faults in unionfs_lock(). 3572a31267eSMatthew Dillon */ 358d00947d8SCraig Rodrigues VI_LOCK(vp); 359d00947d8SCraig Rodrigues unp = VTOUNIONFS(vp); 360d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 361d00947d8SCraig Rodrigues uvp = unp->un_uppervp; 362a9b794ffSDaichi GOTO dvp = unp->un_dvp; 363d00947d8SCraig Rodrigues unp->un_lowervp = unp->un_uppervp = NULLVP; 364d00947d8SCraig Rodrigues 365d00947d8SCraig Rodrigues vp->v_vnlock = &(vp->v_lock); 366d00947d8SCraig Rodrigues vp->v_data = NULL; 3670e9eb108SAttilio Rao lockmgr(vp->v_vnlock, LK_EXCLUSIVE | LK_INTERLOCK, VI_MTX(vp)); 368d00947d8SCraig Rodrigues if (lvp != NULLVP) 36922db15c0SAttilio Rao VOP_UNLOCK(lvp, 0); 370d00947d8SCraig Rodrigues if (uvp != NULLVP) 37122db15c0SAttilio Rao VOP_UNLOCK(uvp, 0); 372d00947d8SCraig Rodrigues vp->v_object = NULL; 373d00947d8SCraig Rodrigues 374938161d6SDaichi GOTO if (unp->un_path != NULL && dvp != NULLVP && 375938161d6SDaichi GOTO (vp->v_type == VDIR || vp->v_type == VSOCK)) 376a9b794ffSDaichi GOTO unionfs_rem_cached_vdir(unp, dvp); 377a9b794ffSDaichi GOTO 378d00947d8SCraig Rodrigues if (lvp != NULLVP) { 379d00947d8SCraig Rodrigues vfslocked = VFS_LOCK_GIANT(lvp->v_mount); 380d00947d8SCraig Rodrigues vrele(lvp); 381d00947d8SCraig Rodrigues VFS_UNLOCK_GIANT(vfslocked); 382d00947d8SCraig Rodrigues } 383d00947d8SCraig Rodrigues if (uvp != NULLVP) { 384d00947d8SCraig Rodrigues vfslocked = VFS_LOCK_GIANT(uvp->v_mount); 385d00947d8SCraig Rodrigues vrele(uvp); 386d00947d8SCraig Rodrigues VFS_UNLOCK_GIANT(vfslocked); 387d00947d8SCraig Rodrigues } 388a9b794ffSDaichi GOTO if (dvp != NULLVP) { 389a9b794ffSDaichi GOTO vfslocked = VFS_LOCK_GIANT(dvp->v_mount); 390a9b794ffSDaichi GOTO vrele(dvp); 391d00947d8SCraig Rodrigues VFS_UNLOCK_GIANT(vfslocked); 392d00947d8SCraig Rodrigues unp->un_dvp = NULLVP; 393d00947d8SCraig Rodrigues } 394a9b794ffSDaichi GOTO if (unp->un_path != NULL) { 395d00947d8SCraig Rodrigues free(unp->un_path, M_UNIONFSPATH); 396d00947d8SCraig Rodrigues unp->un_path = NULL; 397d00947d8SCraig Rodrigues } 398acc4bab1SCraig Rodrigues 399a9b794ffSDaichi GOTO if (unp->un_hashtbl != NULL) 400a9b794ffSDaichi GOTO hashdestroy(unp->un_hashtbl, M_UNIONFSHASH, unp->un_hashmask); 401a9b794ffSDaichi GOTO 402acc4bab1SCraig Rodrigues LIST_FOREACH_SAFE(unsp, &(unp->un_unshead), uns_list, unsp_tmp) { 403d00947d8SCraig Rodrigues LIST_REMOVE(unsp, uns_list); 404d00947d8SCraig Rodrigues free(unsp, M_TEMP); 405d00947d8SCraig Rodrigues } 406d00947d8SCraig Rodrigues FREE(unp, M_UNIONFSNODE); 407df8bae1dSRodney W. Grimes } 408df8bae1dSRodney W. Grimes 409d00947d8SCraig Rodrigues /* 410d00947d8SCraig Rodrigues * Get the unionfs node status. 411d00947d8SCraig Rodrigues * You need exclusive lock this vnode. 412d00947d8SCraig Rodrigues */ 413d00947d8SCraig Rodrigues void 414d00947d8SCraig Rodrigues unionfs_get_node_status(struct unionfs_node *unp, struct thread *td, 415d00947d8SCraig Rodrigues struct unionfs_node_status **unspp) 416d00947d8SCraig Rodrigues { 417d00947d8SCraig Rodrigues struct unionfs_node_status *unsp; 418fe5f08cdSDaichi GOTO pid_t pid = td->td_proc->p_pid; 419df8bae1dSRodney W. Grimes 420d00947d8SCraig Rodrigues KASSERT(NULL != unspp, ("null pointer")); 421d00947d8SCraig Rodrigues ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status"); 4222a31267eSMatthew Dillon 423d00947d8SCraig Rodrigues LIST_FOREACH(unsp, &(unp->un_unshead), uns_list) { 424fe5f08cdSDaichi GOTO if (unsp->uns_pid == pid) { 425d00947d8SCraig Rodrigues *unspp = unsp; 426d00947d8SCraig Rodrigues return; 427d00947d8SCraig Rodrigues } 428d00947d8SCraig Rodrigues } 4292a31267eSMatthew Dillon 430d00947d8SCraig Rodrigues /* create a new unionfs node status */ 431d00947d8SCraig Rodrigues MALLOC(unsp, struct unionfs_node_status *, 432d00947d8SCraig Rodrigues sizeof(struct unionfs_node_status), M_TEMP, M_WAITOK | M_ZERO); 4332a31267eSMatthew Dillon 434fe5f08cdSDaichi GOTO unsp->uns_pid = pid; 435d00947d8SCraig Rodrigues LIST_INSERT_HEAD(&(unp->un_unshead), unsp, uns_list); 4362a31267eSMatthew Dillon 437d00947d8SCraig Rodrigues *unspp = unsp; 438d00947d8SCraig Rodrigues } 439d00947d8SCraig Rodrigues 440d00947d8SCraig Rodrigues /* 441d00947d8SCraig Rodrigues * Remove the unionfs node status, if you can. 442d00947d8SCraig Rodrigues * You need exclusive lock this vnode. 443d00947d8SCraig Rodrigues */ 444d00947d8SCraig Rodrigues void 445fe5f08cdSDaichi GOTO unionfs_tryrem_node_status(struct unionfs_node *unp, 446d00947d8SCraig Rodrigues struct unionfs_node_status *unsp) 447d00947d8SCraig Rodrigues { 448d00947d8SCraig Rodrigues KASSERT(NULL != unsp, ("null pointer")); 449d00947d8SCraig Rodrigues ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status"); 450d00947d8SCraig Rodrigues 451d00947d8SCraig Rodrigues if (0 < unsp->uns_lower_opencnt || 0 < unsp->uns_upper_opencnt) 452d00947d8SCraig Rodrigues return; 453d00947d8SCraig Rodrigues 454d00947d8SCraig Rodrigues LIST_REMOVE(unsp, uns_list); 455d00947d8SCraig Rodrigues free(unsp, M_TEMP); 456d00947d8SCraig Rodrigues } 457d00947d8SCraig Rodrigues 458d00947d8SCraig Rodrigues /* 459d00947d8SCraig Rodrigues * Create upper node attr. 460d00947d8SCraig Rodrigues */ 461d00947d8SCraig Rodrigues void 462d00947d8SCraig Rodrigues unionfs_create_uppervattr_core(struct unionfs_mount *ump, 463d00947d8SCraig Rodrigues struct vattr *lva, 464d00947d8SCraig Rodrigues struct vattr *uva, 465d00947d8SCraig Rodrigues struct thread *td) 466d00947d8SCraig Rodrigues { 467d00947d8SCraig Rodrigues VATTR_NULL(uva); 468d00947d8SCraig Rodrigues uva->va_type = lva->va_type; 469d00947d8SCraig Rodrigues uva->va_atime = lva->va_atime; 470d00947d8SCraig Rodrigues uva->va_mtime = lva->va_mtime; 471d00947d8SCraig Rodrigues uva->va_ctime = lva->va_ctime; 472d00947d8SCraig Rodrigues 473d00947d8SCraig Rodrigues switch (ump->um_copymode) { 474d00947d8SCraig Rodrigues case UNIONFS_TRANSPARENT: 475d00947d8SCraig Rodrigues uva->va_mode = lva->va_mode; 476d00947d8SCraig Rodrigues uva->va_uid = lva->va_uid; 477d00947d8SCraig Rodrigues uva->va_gid = lva->va_gid; 478d00947d8SCraig Rodrigues break; 479d00947d8SCraig Rodrigues case UNIONFS_MASQUERADE: 480d00947d8SCraig Rodrigues if (ump->um_uid == lva->va_uid) { 481d00947d8SCraig Rodrigues uva->va_mode = lva->va_mode & 077077; 482d00947d8SCraig Rodrigues uva->va_mode |= (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile) & 0700; 483d00947d8SCraig Rodrigues uva->va_uid = lva->va_uid; 484d00947d8SCraig Rodrigues uva->va_gid = lva->va_gid; 485df8bae1dSRodney W. Grimes } else { 486d00947d8SCraig Rodrigues uva->va_mode = (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile); 487d00947d8SCraig Rodrigues uva->va_uid = ump->um_uid; 488d00947d8SCraig Rodrigues uva->va_gid = ump->um_gid; 489d00947d8SCraig Rodrigues } 490d00947d8SCraig Rodrigues break; 491d00947d8SCraig Rodrigues default: /* UNIONFS_TRADITIONAL */ 4925e3f7694SRobert Watson FILEDESC_SLOCK(td->td_proc->p_fd); 493d00947d8SCraig Rodrigues uva->va_mode = 0777 & ~td->td_proc->p_fd->fd_cmask; 4945e3f7694SRobert Watson FILEDESC_SUNLOCK(td->td_proc->p_fd); 495d00947d8SCraig Rodrigues uva->va_uid = ump->um_uid; 496d00947d8SCraig Rodrigues uva->va_gid = ump->um_gid; 497d00947d8SCraig Rodrigues break; 498d00947d8SCraig Rodrigues } 499df8bae1dSRodney W. Grimes } 500df8bae1dSRodney W. Grimes 501d00947d8SCraig Rodrigues /* 502d00947d8SCraig Rodrigues * Create upper node attr. 503d00947d8SCraig Rodrigues */ 504d00947d8SCraig Rodrigues int 505d00947d8SCraig Rodrigues unionfs_create_uppervattr(struct unionfs_mount *ump, 506d00947d8SCraig Rodrigues struct vnode *lvp, 507d00947d8SCraig Rodrigues struct vattr *uva, 508d00947d8SCraig Rodrigues struct ucred *cred, 509d00947d8SCraig Rodrigues struct thread *td) 510d00947d8SCraig Rodrigues { 511d00947d8SCraig Rodrigues int error; 512d00947d8SCraig Rodrigues struct vattr lva; 513df8bae1dSRodney W. Grimes 514d00947d8SCraig Rodrigues if ((error = VOP_GETATTR(lvp, &lva, cred, td))) 515d00947d8SCraig Rodrigues return (error); 516d00947d8SCraig Rodrigues 517d00947d8SCraig Rodrigues unionfs_create_uppervattr_core(ump, &lva, uva, td); 518df8bae1dSRodney W. Grimes 519df8bae1dSRodney W. Grimes return (error); 520df8bae1dSRodney W. Grimes } 521df8bae1dSRodney W. Grimes 522d00947d8SCraig Rodrigues /* 523d00947d8SCraig Rodrigues * relookup 524d00947d8SCraig Rodrigues * 525d00947d8SCraig Rodrigues * dvp should be locked on entry and will be locked on return. 526d00947d8SCraig Rodrigues * 527d00947d8SCraig Rodrigues * If an error is returned, *vpp will be invalid, otherwise it will hold a 528d00947d8SCraig Rodrigues * locked, referenced vnode. If *vpp == dvp then remember that only one 529d00947d8SCraig Rodrigues * LK_EXCLUSIVE lock is held. 530d00947d8SCraig Rodrigues */ 531d00947d8SCraig Rodrigues static int 532d00947d8SCraig Rodrigues unionfs_relookup(struct vnode *dvp, struct vnode **vpp, 533d00947d8SCraig Rodrigues struct componentname *cnp, struct componentname *cn, 534d00947d8SCraig Rodrigues struct thread *td, char *path, int pathlen, u_long nameiop) 535df8bae1dSRodney W. Grimes { 536d00947d8SCraig Rodrigues int error; 537df8bae1dSRodney W. Grimes 538d00947d8SCraig Rodrigues cn->cn_namelen = pathlen; 539d00947d8SCraig Rodrigues cn->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); 540d00947d8SCraig Rodrigues bcopy(path, cn->cn_pnbuf, pathlen); 541d00947d8SCraig Rodrigues cn->cn_pnbuf[pathlen] = '\0'; 542df8bae1dSRodney W. Grimes 543d00947d8SCraig Rodrigues cn->cn_nameiop = nameiop; 544d00947d8SCraig Rodrigues cn->cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN); 545d00947d8SCraig Rodrigues cn->cn_lkflags = LK_EXCLUSIVE; 546d00947d8SCraig Rodrigues cn->cn_thread = td; 547d00947d8SCraig Rodrigues cn->cn_cred = cnp->cn_cred; 548df8bae1dSRodney W. Grimes 549d00947d8SCraig Rodrigues cn->cn_nameptr = cn->cn_pnbuf; 550d00947d8SCraig Rodrigues cn->cn_consume = cnp->cn_consume; 551df8bae1dSRodney W. Grimes 552d00947d8SCraig Rodrigues if (nameiop == DELETE) 553d00947d8SCraig Rodrigues cn->cn_flags |= (cnp->cn_flags & (DOWHITEOUT | SAVESTART)); 554d00947d8SCraig Rodrigues else if (RENAME == nameiop) 555d00947d8SCraig Rodrigues cn->cn_flags |= (cnp->cn_flags & SAVESTART); 556d00947d8SCraig Rodrigues 557d00947d8SCraig Rodrigues vref(dvp); 55822db15c0SAttilio Rao VOP_UNLOCK(dvp, 0); 559d00947d8SCraig Rodrigues 560d00947d8SCraig Rodrigues if ((error = relookup(dvp, vpp, cn))) { 561d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn->cn_pnbuf); 562d00947d8SCraig Rodrigues cn->cn_flags &= ~HASBUF; 563cb05b60aSAttilio Rao vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); 564d00947d8SCraig Rodrigues } else 565d00947d8SCraig Rodrigues vrele(dvp); 566d00947d8SCraig Rodrigues 567d00947d8SCraig Rodrigues return (error); 568df8bae1dSRodney W. Grimes } 569df8bae1dSRodney W. Grimes 570df8bae1dSRodney W. Grimes /* 571d00947d8SCraig Rodrigues * relookup for CREATE namei operation. 5722a31267eSMatthew Dillon * 573d00947d8SCraig Rodrigues * dvp is unionfs vnode. dvp should be locked. 574d00947d8SCraig Rodrigues * 575d00947d8SCraig Rodrigues * If it called 'unionfs_copyfile' function by unionfs_link etc, 576d00947d8SCraig Rodrigues * VOP_LOOKUP information is broken. 577d00947d8SCraig Rodrigues * So it need relookup in order to create link etc. 578d00947d8SCraig Rodrigues */ 579d00947d8SCraig Rodrigues int 580d00947d8SCraig Rodrigues unionfs_relookup_for_create(struct vnode *dvp, struct componentname *cnp, 581d00947d8SCraig Rodrigues struct thread *td) 582d00947d8SCraig Rodrigues { 583d00947d8SCraig Rodrigues int error; 584d00947d8SCraig Rodrigues struct vnode *udvp; 585d00947d8SCraig Rodrigues struct vnode *vp; 586d00947d8SCraig Rodrigues struct componentname cn; 587d00947d8SCraig Rodrigues 588d00947d8SCraig Rodrigues udvp = UNIONFSVPTOUPPERVP(dvp); 589d00947d8SCraig Rodrigues vp = NULLVP; 590d00947d8SCraig Rodrigues 591d00947d8SCraig Rodrigues error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr, 592d00947d8SCraig Rodrigues strlen(cnp->cn_nameptr), CREATE); 593d00947d8SCraig Rodrigues if (error) 594d00947d8SCraig Rodrigues return (error); 595d00947d8SCraig Rodrigues 596d00947d8SCraig Rodrigues if (vp != NULLVP) { 597d00947d8SCraig Rodrigues if (udvp == vp) 598d00947d8SCraig Rodrigues vrele(vp); 599d00947d8SCraig Rodrigues else 600d00947d8SCraig Rodrigues vput(vp); 601d00947d8SCraig Rodrigues 602d00947d8SCraig Rodrigues error = EEXIST; 603d00947d8SCraig Rodrigues } 604d00947d8SCraig Rodrigues 605d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 606d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 607d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 608d00947d8SCraig Rodrigues } 609d00947d8SCraig Rodrigues 610d00947d8SCraig Rodrigues if (!error) { 611d00947d8SCraig Rodrigues cn.cn_flags |= (cnp->cn_flags & HASBUF); 612d00947d8SCraig Rodrigues cnp->cn_flags = cn.cn_flags; 613d00947d8SCraig Rodrigues } 614d00947d8SCraig Rodrigues 615d00947d8SCraig Rodrigues return (error); 616d00947d8SCraig Rodrigues } 617d00947d8SCraig Rodrigues 618d00947d8SCraig Rodrigues /* 619d00947d8SCraig Rodrigues * relookup for DELETE namei operation. 620d00947d8SCraig Rodrigues * 621d00947d8SCraig Rodrigues * dvp is unionfs vnode. dvp should be locked. 622d00947d8SCraig Rodrigues */ 623d00947d8SCraig Rodrigues int 624d00947d8SCraig Rodrigues unionfs_relookup_for_delete(struct vnode *dvp, struct componentname *cnp, 625d00947d8SCraig Rodrigues struct thread *td) 626d00947d8SCraig Rodrigues { 627d00947d8SCraig Rodrigues int error; 628d00947d8SCraig Rodrigues struct vnode *udvp; 629d00947d8SCraig Rodrigues struct vnode *vp; 630d00947d8SCraig Rodrigues struct componentname cn; 631d00947d8SCraig Rodrigues 632d00947d8SCraig Rodrigues udvp = UNIONFSVPTOUPPERVP(dvp); 633d00947d8SCraig Rodrigues vp = NULLVP; 634d00947d8SCraig Rodrigues 635d00947d8SCraig Rodrigues error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr, 636d00947d8SCraig Rodrigues strlen(cnp->cn_nameptr), DELETE); 637d00947d8SCraig Rodrigues if (error) 638d00947d8SCraig Rodrigues return (error); 639d00947d8SCraig Rodrigues 640d00947d8SCraig Rodrigues if (vp == NULLVP) 641d00947d8SCraig Rodrigues error = ENOENT; 642d00947d8SCraig Rodrigues else { 643d00947d8SCraig Rodrigues if (udvp == vp) 644d00947d8SCraig Rodrigues vrele(vp); 645d00947d8SCraig Rodrigues else 646d00947d8SCraig Rodrigues vput(vp); 647d00947d8SCraig Rodrigues } 648d00947d8SCraig Rodrigues 649d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 650d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 651d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 652d00947d8SCraig Rodrigues } 653d00947d8SCraig Rodrigues 654d00947d8SCraig Rodrigues if (!error) { 655d00947d8SCraig Rodrigues cn.cn_flags |= (cnp->cn_flags & HASBUF); 656d00947d8SCraig Rodrigues cnp->cn_flags = cn.cn_flags; 657d00947d8SCraig Rodrigues } 658d00947d8SCraig Rodrigues 659d00947d8SCraig Rodrigues return (error); 660d00947d8SCraig Rodrigues } 661d00947d8SCraig Rodrigues 662d00947d8SCraig Rodrigues /* 663d00947d8SCraig Rodrigues * relookup for RENAME namei operation. 664d00947d8SCraig Rodrigues * 665d00947d8SCraig Rodrigues * dvp is unionfs vnode. dvp should be locked. 666d00947d8SCraig Rodrigues */ 667d00947d8SCraig Rodrigues int 668d00947d8SCraig Rodrigues unionfs_relookup_for_rename(struct vnode *dvp, struct componentname *cnp, 669d00947d8SCraig Rodrigues struct thread *td) 670d00947d8SCraig Rodrigues { 671d00947d8SCraig Rodrigues int error; 672d00947d8SCraig Rodrigues struct vnode *udvp; 673d00947d8SCraig Rodrigues struct vnode *vp; 674d00947d8SCraig Rodrigues struct componentname cn; 675d00947d8SCraig Rodrigues 676d00947d8SCraig Rodrigues udvp = UNIONFSVPTOUPPERVP(dvp); 677d00947d8SCraig Rodrigues vp = NULLVP; 678d00947d8SCraig Rodrigues 679d00947d8SCraig Rodrigues error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr, 680d00947d8SCraig Rodrigues strlen(cnp->cn_nameptr), RENAME); 681d00947d8SCraig Rodrigues if (error) 682d00947d8SCraig Rodrigues return (error); 683d00947d8SCraig Rodrigues 684d00947d8SCraig Rodrigues if (vp != NULLVP) { 685d00947d8SCraig Rodrigues if (udvp == vp) 686d00947d8SCraig Rodrigues vrele(vp); 687d00947d8SCraig Rodrigues else 688d00947d8SCraig Rodrigues vput(vp); 689d00947d8SCraig Rodrigues } 690d00947d8SCraig Rodrigues 691d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 692d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 693d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 694d00947d8SCraig Rodrigues } 695d00947d8SCraig Rodrigues 696d00947d8SCraig Rodrigues if (!error) { 697d00947d8SCraig Rodrigues cn.cn_flags |= (cnp->cn_flags & HASBUF); 698d00947d8SCraig Rodrigues cnp->cn_flags = cn.cn_flags; 699d00947d8SCraig Rodrigues } 700d00947d8SCraig Rodrigues 701d00947d8SCraig Rodrigues return (error); 702d00947d8SCraig Rodrigues 703d00947d8SCraig Rodrigues } 704d00947d8SCraig Rodrigues 705d00947d8SCraig Rodrigues /* 706d00947d8SCraig Rodrigues * Update the unionfs_node. 707d00947d8SCraig Rodrigues * 708d00947d8SCraig Rodrigues * uvp is new locked upper vnode. unionfs vnode's lock will be exchanged to the 709d00947d8SCraig Rodrigues * uvp's lock and lower's lock will be unlocked. 710d00947d8SCraig Rodrigues */ 711d00947d8SCraig Rodrigues static void 712d00947d8SCraig Rodrigues unionfs_node_update(struct unionfs_node *unp, struct vnode *uvp, 713d00947d8SCraig Rodrigues struct thread *td) 714d00947d8SCraig Rodrigues { 715047dd67eSAttilio Rao unsigned count, lockrec; 716d00947d8SCraig Rodrigues struct vnode *vp; 717d00947d8SCraig Rodrigues struct vnode *lvp; 718a9b794ffSDaichi GOTO struct vnode *dvp; 719d00947d8SCraig Rodrigues 720d00947d8SCraig Rodrigues vp = UNIONFSTOV(unp); 721d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 722047dd67eSAttilio Rao ASSERT_VOP_ELOCKED(lvp, "unionfs_node_update"); 723a9b794ffSDaichi GOTO dvp = unp->un_dvp; 724d00947d8SCraig Rodrigues 725d00947d8SCraig Rodrigues /* 726d00947d8SCraig Rodrigues * lock update 727d00947d8SCraig Rodrigues */ 728d00947d8SCraig Rodrigues VI_LOCK(vp); 729d00947d8SCraig Rodrigues unp->un_uppervp = uvp; 730d00947d8SCraig Rodrigues vp->v_vnlock = uvp->v_vnlock; 731d00947d8SCraig Rodrigues VI_UNLOCK(vp); 732047dd67eSAttilio Rao lockrec = lvp->v_vnlock->lk_recurse; 733047dd67eSAttilio Rao for (count = 0; count < lockrec; count++) 734cb05b60aSAttilio Rao vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY); 735a9b794ffSDaichi GOTO 736a9b794ffSDaichi GOTO /* 737a9b794ffSDaichi GOTO * cache update 738a9b794ffSDaichi GOTO */ 739938161d6SDaichi GOTO if (unp->un_path != NULL && dvp != NULLVP && 740938161d6SDaichi GOTO (vp->v_type == VDIR || vp->v_type == VSOCK)) { 741a9b794ffSDaichi GOTO static struct unionfs_node_hashhead *hd; 742a9b794ffSDaichi GOTO 743a9b794ffSDaichi GOTO VI_LOCK(dvp); 744a9b794ffSDaichi GOTO hd = unionfs_get_hashhead(dvp, unp->un_path); 745a9b794ffSDaichi GOTO LIST_REMOVE(unp, un_hash); 746a9b794ffSDaichi GOTO LIST_INSERT_HEAD(hd, unp, un_hash); 747a9b794ffSDaichi GOTO VI_UNLOCK(dvp); 748a9b794ffSDaichi GOTO } 749d00947d8SCraig Rodrigues } 750d00947d8SCraig Rodrigues 751d00947d8SCraig Rodrigues /* 752d00947d8SCraig Rodrigues * Create a new shadow dir. 753d00947d8SCraig Rodrigues * 754d00947d8SCraig Rodrigues * udvp should be locked on entry and will be locked on return. 755d00947d8SCraig Rodrigues * 756d00947d8SCraig Rodrigues * If no error returned, unp will be updated. 757d00947d8SCraig Rodrigues */ 758d00947d8SCraig Rodrigues int 759d00947d8SCraig Rodrigues unionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *udvp, 760d00947d8SCraig Rodrigues struct unionfs_node *unp, struct componentname *cnp, 761d00947d8SCraig Rodrigues struct thread *td) 762d00947d8SCraig Rodrigues { 763d00947d8SCraig Rodrigues int error; 764d00947d8SCraig Rodrigues struct vnode *lvp; 765d00947d8SCraig Rodrigues struct vnode *uvp; 766d00947d8SCraig Rodrigues struct vattr va; 767d00947d8SCraig Rodrigues struct vattr lva; 768d00947d8SCraig Rodrigues struct componentname cn; 769d00947d8SCraig Rodrigues struct mount *mp; 770d00947d8SCraig Rodrigues struct ucred *cred; 771d00947d8SCraig Rodrigues struct ucred *credbk; 772d00947d8SCraig Rodrigues struct uidinfo *rootinfo; 773d00947d8SCraig Rodrigues 774d00947d8SCraig Rodrigues if (unp->un_uppervp != NULLVP) 775d00947d8SCraig Rodrigues return (EEXIST); 776d00947d8SCraig Rodrigues 777d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 778d00947d8SCraig Rodrigues uvp = NULLVP; 779d00947d8SCraig Rodrigues credbk = cnp->cn_cred; 780d00947d8SCraig Rodrigues 781d00947d8SCraig Rodrigues /* Authority change to root */ 782d00947d8SCraig Rodrigues rootinfo = uifind((uid_t)0); 783d00947d8SCraig Rodrigues cred = crdup(cnp->cn_cred); 784d00947d8SCraig Rodrigues chgproccnt(cred->cr_ruidinfo, 1, 0); 785d00947d8SCraig Rodrigues change_euid(cred, rootinfo); 786d00947d8SCraig Rodrigues change_ruid(cred, rootinfo); 787d00947d8SCraig Rodrigues change_svuid(cred, (uid_t)0); 788d00947d8SCraig Rodrigues uifree(rootinfo); 789d00947d8SCraig Rodrigues cnp->cn_cred = cred; 790d00947d8SCraig Rodrigues 791d00947d8SCraig Rodrigues memset(&cn, 0, sizeof(cn)); 792d00947d8SCraig Rodrigues 793d00947d8SCraig Rodrigues if ((error = VOP_GETATTR(lvp, &lva, cnp->cn_cred, td))) 794d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_abort; 795d00947d8SCraig Rodrigues 796d00947d8SCraig Rodrigues if ((error = unionfs_relookup(udvp, &uvp, cnp, &cn, td, cnp->cn_nameptr, cnp->cn_namelen, CREATE))) 797d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_abort; 798d00947d8SCraig Rodrigues if (uvp != NULLVP) { 799d00947d8SCraig Rodrigues if (udvp == uvp) 800d00947d8SCraig Rodrigues vrele(uvp); 801d00947d8SCraig Rodrigues else 802d00947d8SCraig Rodrigues vput(uvp); 803d00947d8SCraig Rodrigues 804d00947d8SCraig Rodrigues error = EEXIST; 805d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_free_out; 806d00947d8SCraig Rodrigues } 807d00947d8SCraig Rodrigues 808d00947d8SCraig Rodrigues if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH))) 809d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_free_out; 810d00947d8SCraig Rodrigues if ((error = VOP_LEASE(udvp, td, cn.cn_cred, LEASE_WRITE))) { 811d00947d8SCraig Rodrigues vn_finished_write(mp); 812d00947d8SCraig Rodrigues goto unionfs_mkshadowdir_free_out; 813d00947d8SCraig Rodrigues } 814d00947d8SCraig Rodrigues unionfs_create_uppervattr_core(ump, &lva, &va, td); 815d00947d8SCraig Rodrigues 816d00947d8SCraig Rodrigues error = VOP_MKDIR(udvp, &uvp, &cn, &va); 817d00947d8SCraig Rodrigues 818d00947d8SCraig Rodrigues if (!error) { 819d00947d8SCraig Rodrigues unionfs_node_update(unp, uvp, td); 820d00947d8SCraig Rodrigues 821d00947d8SCraig Rodrigues /* 822d00947d8SCraig Rodrigues * XXX The bug which cannot set uid/gid was corrected. 823d00947d8SCraig Rodrigues * Ignore errors. 824d00947d8SCraig Rodrigues */ 825d00947d8SCraig Rodrigues va.va_type = VNON; 826d00947d8SCraig Rodrigues VOP_SETATTR(uvp, &va, cn.cn_cred, td); 827d00947d8SCraig Rodrigues } 828d00947d8SCraig Rodrigues vn_finished_write(mp); 829d00947d8SCraig Rodrigues 830d00947d8SCraig Rodrigues unionfs_mkshadowdir_free_out: 831d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 832d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 833d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 834d00947d8SCraig Rodrigues } 835d00947d8SCraig Rodrigues 836d00947d8SCraig Rodrigues unionfs_mkshadowdir_abort: 837d00947d8SCraig Rodrigues cnp->cn_cred = credbk; 838d00947d8SCraig Rodrigues chgproccnt(cred->cr_ruidinfo, -1, 0); 839d00947d8SCraig Rodrigues crfree(cred); 840d00947d8SCraig Rodrigues 841d00947d8SCraig Rodrigues return (error); 842d00947d8SCraig Rodrigues } 843d00947d8SCraig Rodrigues 844d00947d8SCraig Rodrigues /* 845d00947d8SCraig Rodrigues * Create a new whiteout. 846d00947d8SCraig Rodrigues * 847d00947d8SCraig Rodrigues * dvp should be locked on entry and will be locked on return. 848d00947d8SCraig Rodrigues */ 849d00947d8SCraig Rodrigues int 850d00947d8SCraig Rodrigues unionfs_mkwhiteout(struct vnode *dvp, struct componentname *cnp, 851d00947d8SCraig Rodrigues struct thread *td, char *path) 852d00947d8SCraig Rodrigues { 853d00947d8SCraig Rodrigues int error; 854d00947d8SCraig Rodrigues struct vnode *wvp; 855d00947d8SCraig Rodrigues struct componentname cn; 856d00947d8SCraig Rodrigues struct mount *mp; 857d00947d8SCraig Rodrigues 858d00947d8SCraig Rodrigues if (path == NULL) 859d00947d8SCraig Rodrigues path = cnp->cn_nameptr; 860d00947d8SCraig Rodrigues 861d00947d8SCraig Rodrigues wvp = NULLVP; 862d00947d8SCraig Rodrigues if ((error = unionfs_relookup(dvp, &wvp, cnp, &cn, td, path, strlen(path), CREATE))) 863d00947d8SCraig Rodrigues return (error); 864d00947d8SCraig Rodrigues if (wvp != NULLVP) { 865d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 866d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 867d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 868d00947d8SCraig Rodrigues } 869d00947d8SCraig Rodrigues if (dvp == wvp) 870d00947d8SCraig Rodrigues vrele(wvp); 871d00947d8SCraig Rodrigues else 872d00947d8SCraig Rodrigues vput(wvp); 873d00947d8SCraig Rodrigues 874d00947d8SCraig Rodrigues return (EEXIST); 875d00947d8SCraig Rodrigues } 876d00947d8SCraig Rodrigues 877d00947d8SCraig Rodrigues if ((error = vn_start_write(dvp, &mp, V_WAIT | PCATCH))) 878d00947d8SCraig Rodrigues goto unionfs_mkwhiteout_free_out; 879d00947d8SCraig Rodrigues if (!(error = VOP_LEASE(dvp, td, td->td_ucred, LEASE_WRITE))) 880d00947d8SCraig Rodrigues error = VOP_WHITEOUT(dvp, &cn, CREATE); 881d00947d8SCraig Rodrigues 882d00947d8SCraig Rodrigues vn_finished_write(mp); 883d00947d8SCraig Rodrigues 884d00947d8SCraig Rodrigues unionfs_mkwhiteout_free_out: 885d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 886d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 887d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 888d00947d8SCraig Rodrigues } 889d00947d8SCraig Rodrigues 890d00947d8SCraig Rodrigues return (error); 891d00947d8SCraig Rodrigues } 892d00947d8SCraig Rodrigues 893d00947d8SCraig Rodrigues /* 894d00947d8SCraig Rodrigues * Create a new vnode for create a new shadow file. 895d00947d8SCraig Rodrigues * 896d00947d8SCraig Rodrigues * If an error is returned, *vpp will be invalid, otherwise it will hold a 897d00947d8SCraig Rodrigues * locked, referenced and opened vnode. 898d00947d8SCraig Rodrigues * 899d00947d8SCraig Rodrigues * unp is never updated. 900df8bae1dSRodney W. Grimes */ 90180b301c3SPoul-Henning Kamp static int 902d00947d8SCraig Rodrigues unionfs_vn_create_on_upper(struct vnode **vpp, struct vnode *udvp, 903d00947d8SCraig Rodrigues struct unionfs_node *unp, struct vattr *uvap, 904d00947d8SCraig Rodrigues struct thread *td) 905df8bae1dSRodney W. Grimes { 906d00947d8SCraig Rodrigues struct unionfs_mount *ump; 907d00947d8SCraig Rodrigues struct vnode *vp; 908d00947d8SCraig Rodrigues struct vnode *lvp; 909d00947d8SCraig Rodrigues struct ucred *cred; 910d00947d8SCraig Rodrigues struct vattr lva; 911d00947d8SCraig Rodrigues int fmode; 912d00947d8SCraig Rodrigues int error; 913d00947d8SCraig Rodrigues struct componentname cn; 914d00947d8SCraig Rodrigues 915d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(UNIONFSTOV(unp)->v_mount); 916d00947d8SCraig Rodrigues vp = NULLVP; 917d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 918d00947d8SCraig Rodrigues cred = td->td_ucred; 919d00947d8SCraig Rodrigues fmode = FFLAGS(O_WRONLY | O_CREAT | O_TRUNC | O_EXCL); 920d00947d8SCraig Rodrigues error = 0; 921d00947d8SCraig Rodrigues 922d00947d8SCraig Rodrigues if ((error = VOP_GETATTR(lvp, &lva, cred, td)) != 0) 923d00947d8SCraig Rodrigues return (error); 924d00947d8SCraig Rodrigues unionfs_create_uppervattr_core(ump, &lva, uvap, td); 925d00947d8SCraig Rodrigues 926d00947d8SCraig Rodrigues if (unp->un_path == NULL) 927d00947d8SCraig Rodrigues panic("unionfs: un_path is null"); 928d00947d8SCraig Rodrigues 929d00947d8SCraig Rodrigues cn.cn_namelen = strlen(unp->un_path); 930d00947d8SCraig Rodrigues cn.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); 931d00947d8SCraig Rodrigues bcopy(unp->un_path, cn.cn_pnbuf, cn.cn_namelen + 1); 932d00947d8SCraig Rodrigues cn.cn_nameiop = CREATE; 933d00947d8SCraig Rodrigues cn.cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN); 934d00947d8SCraig Rodrigues cn.cn_lkflags = LK_EXCLUSIVE; 935d00947d8SCraig Rodrigues cn.cn_thread = td; 936d00947d8SCraig Rodrigues cn.cn_cred = cred; 937d00947d8SCraig Rodrigues cn.cn_nameptr = cn.cn_pnbuf; 938d00947d8SCraig Rodrigues cn.cn_consume = 0; 939d00947d8SCraig Rodrigues 940d00947d8SCraig Rodrigues vref(udvp); 941d00947d8SCraig Rodrigues if ((error = relookup(udvp, &vp, &cn)) != 0) 942d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out2; 943d00947d8SCraig Rodrigues vrele(udvp); 944d00947d8SCraig Rodrigues 945d00947d8SCraig Rodrigues if (vp != NULLVP) { 946d00947d8SCraig Rodrigues if (vp == udvp) 947d00947d8SCraig Rodrigues vrele(vp); 948d00947d8SCraig Rodrigues else 949d00947d8SCraig Rodrigues vput(vp); 950d00947d8SCraig Rodrigues error = EEXIST; 951d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out1; 952d00947d8SCraig Rodrigues } 953d00947d8SCraig Rodrigues 954d00947d8SCraig Rodrigues if ((error = VOP_LEASE(udvp, td, cred, LEASE_WRITE)) != 0) 955d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out1; 956d00947d8SCraig Rodrigues 957d00947d8SCraig Rodrigues if ((error = VOP_CREATE(udvp, &vp, &cn, uvap)) != 0) 958d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out1; 959d00947d8SCraig Rodrigues 9609e223287SKonstantin Belousov if ((error = VOP_OPEN(vp, fmode, cred, td, NULL)) != 0) { 961d00947d8SCraig Rodrigues vput(vp); 962d00947d8SCraig Rodrigues goto unionfs_vn_create_on_upper_free_out1; 963d00947d8SCraig Rodrigues } 964d00947d8SCraig Rodrigues vp->v_writecount++; 965d00947d8SCraig Rodrigues *vpp = vp; 966d00947d8SCraig Rodrigues 967d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out1: 96822db15c0SAttilio Rao VOP_UNLOCK(udvp, 0); 969d00947d8SCraig Rodrigues 970d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out2: 971d00947d8SCraig Rodrigues if (cn.cn_flags & HASBUF) { 972d00947d8SCraig Rodrigues uma_zfree(namei_zone, cn.cn_pnbuf); 973d00947d8SCraig Rodrigues cn.cn_flags &= ~HASBUF; 974d00947d8SCraig Rodrigues } 975d00947d8SCraig Rodrigues 976d00947d8SCraig Rodrigues return (error); 977d00947d8SCraig Rodrigues } 978d00947d8SCraig Rodrigues 979d00947d8SCraig Rodrigues /* 980d00947d8SCraig Rodrigues * Copy from lvp to uvp. 981d00947d8SCraig Rodrigues * 982d00947d8SCraig Rodrigues * lvp and uvp should be locked and opened on entry and will be locked and 983d00947d8SCraig Rodrigues * opened on return. 984d00947d8SCraig Rodrigues */ 985d00947d8SCraig Rodrigues static int 986d00947d8SCraig Rodrigues unionfs_copyfile_core(struct vnode *lvp, struct vnode *uvp, 987d00947d8SCraig Rodrigues struct ucred *cred, struct thread *td) 988d00947d8SCraig Rodrigues { 989d00947d8SCraig Rodrigues int error; 990d00947d8SCraig Rodrigues off_t offset; 991d00947d8SCraig Rodrigues int count; 992d00947d8SCraig Rodrigues int bufoffset; 993df8bae1dSRodney W. Grimes char *buf; 994df8bae1dSRodney W. Grimes struct uio uio; 995df8bae1dSRodney W. Grimes struct iovec iov; 996df8bae1dSRodney W. Grimes 997d00947d8SCraig Rodrigues error = 0; 998d00947d8SCraig Rodrigues memset(&uio, 0, sizeof(uio)); 9992a31267eSMatthew Dillon 1000b40ce416SJulian Elischer uio.uio_td = td; 1001df8bae1dSRodney W. Grimes uio.uio_segflg = UIO_SYSSPACE; 1002df8bae1dSRodney W. Grimes uio.uio_offset = 0; 1003df8bae1dSRodney W. Grimes 1004d00947d8SCraig Rodrigues if ((error = VOP_LEASE(lvp, td, cred, LEASE_READ)) != 0) 1005d00947d8SCraig Rodrigues return (error); 1006d00947d8SCraig Rodrigues if ((error = VOP_LEASE(uvp, td, cred, LEASE_WRITE)) != 0) 1007d00947d8SCraig Rodrigues return (error); 1008a163d034SWarner Losh buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK); 1009df8bae1dSRodney W. Grimes 1010d00947d8SCraig Rodrigues while (error == 0) { 1011d00947d8SCraig Rodrigues offset = uio.uio_offset; 1012df8bae1dSRodney W. Grimes 1013df8bae1dSRodney W. Grimes uio.uio_iov = &iov; 1014df8bae1dSRodney W. Grimes uio.uio_iovcnt = 1; 1015df8bae1dSRodney W. Grimes iov.iov_base = buf; 1016df8bae1dSRodney W. Grimes iov.iov_len = MAXBSIZE; 1017df8bae1dSRodney W. Grimes uio.uio_resid = iov.iov_len; 1018df8bae1dSRodney W. Grimes uio.uio_rw = UIO_READ; 1019df8bae1dSRodney W. Grimes 1020d00947d8SCraig Rodrigues if ((error = VOP_READ(lvp, &uio, 0, cred)) != 0) 10212a31267eSMatthew Dillon break; 10222a31267eSMatthew Dillon if ((count = MAXBSIZE - uio.uio_resid) == 0) 10232a31267eSMatthew Dillon break; 10242a31267eSMatthew Dillon 1025d00947d8SCraig Rodrigues bufoffset = 0; 10262a31267eSMatthew Dillon while (bufoffset < count) { 1027df8bae1dSRodney W. Grimes uio.uio_iov = &iov; 1028df8bae1dSRodney W. Grimes uio.uio_iovcnt = 1; 10292a31267eSMatthew Dillon iov.iov_base = buf + bufoffset; 10302a31267eSMatthew Dillon iov.iov_len = count - bufoffset; 10312a31267eSMatthew Dillon uio.uio_offset = offset + bufoffset; 1032df8bae1dSRodney W. Grimes uio.uio_resid = iov.iov_len; 1033d00947d8SCraig Rodrigues uio.uio_rw = UIO_WRITE; 1034df8bae1dSRodney W. Grimes 1035d00947d8SCraig Rodrigues if ((error = VOP_WRITE(uvp, &uio, 0, cred)) != 0) 1036df8bae1dSRodney W. Grimes break; 1037d00947d8SCraig Rodrigues 10382a31267eSMatthew Dillon bufoffset += (count - bufoffset) - uio.uio_resid; 1039df8bae1dSRodney W. Grimes } 1040d00947d8SCraig Rodrigues 10412a31267eSMatthew Dillon uio.uio_offset = offset + bufoffset; 1042d00947d8SCraig Rodrigues } 1043df8bae1dSRodney W. Grimes 1044df8bae1dSRodney W. Grimes free(buf, M_TEMP); 1045d00947d8SCraig Rodrigues 1046df8bae1dSRodney W. Grimes return (error); 1047df8bae1dSRodney W. Grimes } 1048df8bae1dSRodney W. Grimes 1049df8bae1dSRodney W. Grimes /* 1050d00947d8SCraig Rodrigues * Copy file from lower to upper. 10512a31267eSMatthew Dillon * 1052d00947d8SCraig Rodrigues * If you need copy of the contents, set 1 to docopy. Otherwise, set 0 to 1053d00947d8SCraig Rodrigues * docopy. 1054d00947d8SCraig Rodrigues * 1055d00947d8SCraig Rodrigues * If no error returned, unp will be updated. 1056996c772fSJohn Dyson */ 1057996c772fSJohn Dyson int 1058d00947d8SCraig Rodrigues unionfs_copyfile(struct unionfs_node *unp, int docopy, struct ucred *cred, 1059d00947d8SCraig Rodrigues struct thread *td) 1060996c772fSJohn Dyson { 1061996c772fSJohn Dyson int error; 1062f2a2857bSKirk McKusick struct mount *mp; 1063d00947d8SCraig Rodrigues struct vnode *udvp; 1064d00947d8SCraig Rodrigues struct vnode *lvp; 1065d00947d8SCraig Rodrigues struct vnode *uvp; 1066d00947d8SCraig Rodrigues struct vattr uva; 1067996c772fSJohn Dyson 1068d00947d8SCraig Rodrigues lvp = unp->un_lowervp; 1069d00947d8SCraig Rodrigues uvp = NULLVP; 1070d00947d8SCraig Rodrigues 1071d00947d8SCraig Rodrigues if ((UNIONFSTOV(unp)->v_mount->mnt_flag & MNT_RDONLY)) 1072d00947d8SCraig Rodrigues return (EROFS); 1073d00947d8SCraig Rodrigues if (unp->un_dvp == NULLVP) 1074d00947d8SCraig Rodrigues return (EINVAL); 1075d00947d8SCraig Rodrigues if (unp->un_uppervp != NULLVP) 1076d00947d8SCraig Rodrigues return (EEXIST); 1077d00947d8SCraig Rodrigues udvp = VTOUNIONFS(unp->un_dvp)->un_uppervp; 1078d00947d8SCraig Rodrigues if (udvp == NULLVP) 1079d00947d8SCraig Rodrigues return (EROFS); 1080d00947d8SCraig Rodrigues if ((udvp->v_mount->mnt_flag & MNT_RDONLY)) 1081d00947d8SCraig Rodrigues return (EROFS); 1082d00947d8SCraig Rodrigues 1083d00947d8SCraig Rodrigues error = VOP_ACCESS(lvp, VREAD, cred, td); 1084d00947d8SCraig Rodrigues if (error != 0) 10855842d4e5SKATO Takenori return (error); 10865842d4e5SKATO Takenori 1087d00947d8SCraig Rodrigues if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH)) != 0) 1088996c772fSJohn Dyson return (error); 1089d00947d8SCraig Rodrigues error = unionfs_vn_create_on_upper(&uvp, udvp, unp, &uva, td); 1090d00947d8SCraig Rodrigues if (error != 0) { 1091f2a2857bSKirk McKusick vn_finished_write(mp); 1092f2a2857bSKirk McKusick return (error); 1093f2a2857bSKirk McKusick } 1094996c772fSJohn Dyson 1095d00947d8SCraig Rodrigues if (docopy != 0) { 10969e223287SKonstantin Belousov error = VOP_OPEN(lvp, FREAD, cred, td, NULL); 1097996c772fSJohn Dyson if (error == 0) { 1098d00947d8SCraig Rodrigues error = unionfs_copyfile_core(lvp, uvp, cred, td); 1099d00947d8SCraig Rodrigues VOP_CLOSE(lvp, FREAD, cred, td); 1100996c772fSJohn Dyson } 1101d00947d8SCraig Rodrigues } 1102d00947d8SCraig Rodrigues VOP_CLOSE(uvp, FWRITE, cred, td); 1103d00947d8SCraig Rodrigues uvp->v_writecount--; 1104996c772fSJohn Dyson 1105f2a2857bSKirk McKusick vn_finished_write(mp); 1106d00947d8SCraig Rodrigues 1107996c772fSJohn Dyson if (error == 0) { 1108d00947d8SCraig Rodrigues /* Reset the attributes. Ignore errors. */ 1109d00947d8SCraig Rodrigues uva.va_type = VNON; 1110d00947d8SCraig Rodrigues VOP_SETATTR(uvp, &uva, cred, td); 1111996c772fSJohn Dyson } 1112996c772fSJohn Dyson 1113d00947d8SCraig Rodrigues unionfs_node_update(unp, uvp, td); 1114996c772fSJohn Dyson 11152a31267eSMatthew Dillon return (error); 1116b422956cSPoul-Henning Kamp } 1117996c772fSJohn Dyson 11182a31267eSMatthew Dillon /* 1119d00947d8SCraig Rodrigues * It checks whether vp can rmdir. (check empty) 11202a31267eSMatthew Dillon * 1121d00947d8SCraig Rodrigues * vp is unionfs vnode. 1122d00947d8SCraig Rodrigues * vp should be locked. 1123df8bae1dSRodney W. Grimes */ 1124df8bae1dSRodney W. Grimes int 1125d00947d8SCraig Rodrigues unionfs_check_rmdir(struct vnode *vp, struct ucred *cred, struct thread *td) 1126df8bae1dSRodney W. Grimes { 1127df8bae1dSRodney W. Grimes int error; 1128d00947d8SCraig Rodrigues int eofflag; 1129d00947d8SCraig Rodrigues int lookuperr; 1130d00947d8SCraig Rodrigues struct vnode *uvp; 1131d00947d8SCraig Rodrigues struct vnode *lvp; 1132d00947d8SCraig Rodrigues struct vnode *tvp; 1133df8bae1dSRodney W. Grimes struct vattr va; 1134df8bae1dSRodney W. Grimes struct componentname cn; 1135df8bae1dSRodney W. Grimes /* 1136d00947d8SCraig Rodrigues * The size of buf needs to be larger than DIRBLKSIZ. 1137df8bae1dSRodney W. Grimes */ 1138d00947d8SCraig Rodrigues char buf[256 * 6]; 1139d00947d8SCraig Rodrigues struct dirent *dp; 1140d00947d8SCraig Rodrigues struct dirent *edp; 1141d00947d8SCraig Rodrigues struct uio uio; 1142d00947d8SCraig Rodrigues struct iovec iov; 1143df8bae1dSRodney W. Grimes 1144d00947d8SCraig Rodrigues ASSERT_VOP_ELOCKED(vp, "unionfs_check_rmdir"); 1145df8bae1dSRodney W. Grimes 1146d00947d8SCraig Rodrigues eofflag = 0; 1147d00947d8SCraig Rodrigues uvp = UNIONFSVPTOUPPERVP(vp); 1148d00947d8SCraig Rodrigues lvp = UNIONFSVPTOLOWERVP(vp); 1149df8bae1dSRodney W. Grimes 1150d00947d8SCraig Rodrigues /* check opaque */ 1151d00947d8SCraig Rodrigues if ((error = VOP_GETATTR(uvp, &va, cred, td)) != 0) 1152df8bae1dSRodney W. Grimes return (error); 1153d00947d8SCraig Rodrigues if (va.va_flags & OPAQUE) 1154d00947d8SCraig Rodrigues return (0); 1155df8bae1dSRodney W. Grimes 1156d00947d8SCraig Rodrigues /* open vnode */ 11573282e2c4SDaichi GOTO #ifdef MAC 115830d239bcSRobert Watson if ((error = mac_vnode_check_open(cred, vp, VEXEC|VREAD)) != 0) 11593282e2c4SDaichi GOTO return (error); 11603282e2c4SDaichi GOTO #endif 11613282e2c4SDaichi GOTO if ((error = VOP_ACCESS(vp, VEXEC|VREAD, cred, td)) != 0) 11623282e2c4SDaichi GOTO return (error); 11639e223287SKonstantin Belousov if ((error = VOP_OPEN(vp, FREAD, cred, td, NULL)) != 0) 1164996c772fSJohn Dyson return (error); 1165996c772fSJohn Dyson 1166d00947d8SCraig Rodrigues uio.uio_rw = UIO_READ; 1167d00947d8SCraig Rodrigues uio.uio_segflg = UIO_SYSSPACE; 1168d00947d8SCraig Rodrigues uio.uio_td = td; 1169d00947d8SCraig Rodrigues uio.uio_offset = 0; 1170996c772fSJohn Dyson 1171d00947d8SCraig Rodrigues #ifdef MAC 117230d239bcSRobert Watson error = mac_vnode_check_readdir(td->td_ucred, lvp); 1173d00947d8SCraig Rodrigues #endif 1174d00947d8SCraig Rodrigues while (!error && !eofflag) { 1175d00947d8SCraig Rodrigues iov.iov_base = buf; 1176d00947d8SCraig Rodrigues iov.iov_len = sizeof(buf); 1177d00947d8SCraig Rodrigues uio.uio_iov = &iov; 1178d00947d8SCraig Rodrigues uio.uio_iovcnt = 1; 1179d00947d8SCraig Rodrigues uio.uio_resid = iov.iov_len; 1180996c772fSJohn Dyson 1181d00947d8SCraig Rodrigues error = VOP_READDIR(lvp, &uio, cred, &eofflag, NULL, NULL); 1182a68ae31cSDaichi GOTO if (error != 0) 1183d00947d8SCraig Rodrigues break; 1184a68ae31cSDaichi GOTO if (eofflag == 0 && uio.uio_resid == sizeof(buf)) { 1185a68ae31cSDaichi GOTO #ifdef DIAGNOSTIC 1186a68ae31cSDaichi GOTO panic("bad readdir response from lower FS."); 1187a68ae31cSDaichi GOTO #endif 1188a68ae31cSDaichi GOTO break; 1189a68ae31cSDaichi GOTO } 1190996c772fSJohn Dyson 1191d00947d8SCraig Rodrigues edp = (struct dirent*)&buf[sizeof(buf) - uio.uio_resid]; 1192d00947d8SCraig Rodrigues for (dp = (struct dirent*)buf; !error && dp < edp; 1193d00947d8SCraig Rodrigues dp = (struct dirent*)((caddr_t)dp + dp->d_reclen)) { 1194d00947d8SCraig Rodrigues if (dp->d_type == DT_WHT || 1195d00947d8SCraig Rodrigues (dp->d_namlen == 1 && dp->d_name[0] == '.') || 1196d00947d8SCraig Rodrigues (dp->d_namlen == 2 && !bcmp(dp->d_name, "..", 2))) 1197d00947d8SCraig Rodrigues continue; 1198df8bae1dSRodney W. Grimes 1199d00947d8SCraig Rodrigues cn.cn_namelen = dp->d_namlen; 1200d00947d8SCraig Rodrigues cn.cn_pnbuf = NULL; 1201d00947d8SCraig Rodrigues cn.cn_nameptr = dp->d_name; 1202d00947d8SCraig Rodrigues cn.cn_nameiop = LOOKUP; 1203d00947d8SCraig Rodrigues cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN); 1204d00947d8SCraig Rodrigues cn.cn_lkflags = LK_EXCLUSIVE; 1205b40ce416SJulian Elischer cn.cn_thread = td; 1206d00947d8SCraig Rodrigues cn.cn_cred = cred; 1207df8bae1dSRodney W. Grimes cn.cn_consume = 0; 1208df8bae1dSRodney W. Grimes 12092a31267eSMatthew Dillon /* 1210d00947d8SCraig Rodrigues * check entry in lower. 1211d00947d8SCraig Rodrigues * Sometimes, readdir function returns 1212d00947d8SCraig Rodrigues * wrong entry. 12132a31267eSMatthew Dillon */ 1214d00947d8SCraig Rodrigues lookuperr = VOP_LOOKUP(lvp, &tvp, &cn); 1215df8bae1dSRodney W. Grimes 1216d00947d8SCraig Rodrigues if (!lookuperr) 1217d00947d8SCraig Rodrigues vput(tvp); 12182a31267eSMatthew Dillon else 1219d00947d8SCraig Rodrigues continue; /* skip entry */ 1220df8bae1dSRodney W. Grimes 1221df8bae1dSRodney W. Grimes /* 1222d00947d8SCraig Rodrigues * check entry 1223d00947d8SCraig Rodrigues * If it has no exist/whiteout entry in upper, 1224d00947d8SCraig Rodrigues * directory is not empty. 1225df8bae1dSRodney W. Grimes */ 1226d00947d8SCraig Rodrigues cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN); 1227d00947d8SCraig Rodrigues lookuperr = VOP_LOOKUP(uvp, &tvp, &cn); 1228df8bae1dSRodney W. Grimes 1229d00947d8SCraig Rodrigues if (!lookuperr) 1230d00947d8SCraig Rodrigues vput(tvp); 1231df8bae1dSRodney W. Grimes 1232d00947d8SCraig Rodrigues /* ignore exist or whiteout entry */ 1233d00947d8SCraig Rodrigues if (!lookuperr || 1234d00947d8SCraig Rodrigues (lookuperr == ENOENT && (cn.cn_flags & ISWHITEOUT))) 123501634480SBrian Feldman continue; 1236d00947d8SCraig Rodrigues 1237d00947d8SCraig Rodrigues error = ENOTEMPTY; 1238df8bae1dSRodney W. Grimes } 1239996c772fSJohn Dyson } 1240996c772fSJohn Dyson 1241d00947d8SCraig Rodrigues /* close vnode */ 1242d00947d8SCraig Rodrigues VOP_CLOSE(vp, FREAD, cred, td); 1243d00947d8SCraig Rodrigues 1244d00947d8SCraig Rodrigues return (error); 1245d00947d8SCraig Rodrigues } 1246d00947d8SCraig Rodrigues 1247d00947d8SCraig Rodrigues #ifdef DIAGNOSTIC 1248d00947d8SCraig Rodrigues 1249d00947d8SCraig Rodrigues struct vnode * 1250d00947d8SCraig Rodrigues unionfs_checkuppervp(struct vnode *vp, char *fil, int lno) 1251996c772fSJohn Dyson { 1252d00947d8SCraig Rodrigues struct unionfs_node *unp; 1253996c772fSJohn Dyson 1254d00947d8SCraig Rodrigues unp = VTOUNIONFS(vp); 1255996c772fSJohn Dyson 1256d00947d8SCraig Rodrigues #ifdef notyet 1257d00947d8SCraig Rodrigues if (vp->v_op != unionfs_vnodeop_p) { 1258d00947d8SCraig Rodrigues printf("unionfs_checkuppervp: on non-unionfs-node.\n"); 1259d00947d8SCraig Rodrigues #ifdef KDB 12603de213ccSRobert Watson kdb_enter(KDB_WHY_UNIONFS, 12613de213ccSRobert Watson "unionfs_checkuppervp: on non-unionfs-node.\n"); 1262d00947d8SCraig Rodrigues #endif 1263d00947d8SCraig Rodrigues panic("unionfs_checkuppervp"); 1264d00947d8SCraig Rodrigues }; 1265d00947d8SCraig Rodrigues #endif 1266d00947d8SCraig Rodrigues return (unp->un_uppervp); 1267d8c6e674SDavid Schultz } 1268996c772fSJohn Dyson 1269996c772fSJohn Dyson struct vnode * 1270d00947d8SCraig Rodrigues unionfs_checklowervp(struct vnode *vp, char *fil, int lno) 1271996c772fSJohn Dyson { 1272d00947d8SCraig Rodrigues struct unionfs_node *unp; 1273996c772fSJohn Dyson 1274d00947d8SCraig Rodrigues unp = VTOUNIONFS(vp); 1275996c772fSJohn Dyson 1276d00947d8SCraig Rodrigues #ifdef notyet 1277d00947d8SCraig Rodrigues if (vp->v_op != unionfs_vnodeop_p) { 1278d00947d8SCraig Rodrigues printf("unionfs_checklowervp: on non-unionfs-node.\n"); 1279d00947d8SCraig Rodrigues #ifdef KDB 12803de213ccSRobert Watson kdb_enter(KDB_WHY_UNIONFS, 12813de213ccSRobert Watson "unionfs_checklowervp: on non-unionfs-node.\n"); 1282d00947d8SCraig Rodrigues #endif 1283d00947d8SCraig Rodrigues panic("unionfs_checklowervp"); 12848c14bf40SPeter Wemm }; 1285d00947d8SCraig Rodrigues #endif 1286d00947d8SCraig Rodrigues return (unp->un_lowervp); 1287d00947d8SCraig Rodrigues } 1288d00947d8SCraig Rodrigues #endif 1289