union_subr.c revision 276500
1290001Sglebius/*- 2290001Sglebius * Copyright (c) 1994 Jan-Simon Pendry 3290001Sglebius * Copyright (c) 1994 4290001Sglebius * The Regents of the University of California. All rights reserved. 5290001Sglebius * Copyright (c) 2005, 2006, 2012 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc. 6290001Sglebius * Copyright (c) 2006, 2012 Daichi Goto <daichi@freebsd.org> 7290001Sglebius * 8290001Sglebius * This code is derived from software contributed to Berkeley by 9290001Sglebius * Jan-Simon Pendry. 10290001Sglebius * 11290001Sglebius * Redistribution and use in source and binary forms, with or without 12290001Sglebius * modification, are permitted provided that the following conditions 13290001Sglebius * are met: 14290001Sglebius * 1. Redistributions of source code must retain the above copyright 15290001Sglebius * notice, this list of conditions and the following disclaimer. 16290001Sglebius * 2. Redistributions in binary form must reproduce the above copyright 17290001Sglebius * notice, this list of conditions and the following disclaimer in the 18290001Sglebius * documentation and/or other materials provided with the distribution. 19290001Sglebius * 4. Neither the name of the University nor the names of its contributors 20290001Sglebius * may be used to endorse or promote products derived from this software 21290001Sglebius * without specific prior written permission. 22290001Sglebius * 23290001Sglebius * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24290001Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25290001Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26290001Sglebius * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27290001Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28290001Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29290001Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30290001Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31290001Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32290001Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33290001Sglebius * SUCH DAMAGE. 34290001Sglebius * 35290001Sglebius * @(#)union_subr.c 8.20 (Berkeley) 5/20/95 36290001Sglebius * $FreeBSD: stable/10/sys/fs/unionfs/union_subr.c 276500 2015-01-01 10:44:20Z kib $ 37290001Sglebius */ 38290001Sglebius 39290001Sglebius#include <sys/param.h> 40290001Sglebius#include <sys/systm.h> 41290001Sglebius#include <sys/kernel.h> 42290001Sglebius#include <sys/lock.h> 43290001Sglebius#include <sys/mutex.h> 44290001Sglebius#include <sys/malloc.h> 45290001Sglebius#include <sys/mount.h> 46290001Sglebius#include <sys/namei.h> 47290001Sglebius#include <sys/proc.h> 48290001Sglebius#include <sys/vnode.h> 49290001Sglebius#include <sys/dirent.h> 50290001Sglebius#include <sys/fcntl.h> 51290001Sglebius#include <sys/filedesc.h> 52290001Sglebius#include <sys/stat.h> 53290001Sglebius#include <sys/resourcevar.h> 54290001Sglebius 55290001Sglebius#include <security/mac/mac_framework.h> 56290001Sglebius 57290001Sglebius#include <vm/uma.h> 58290001Sglebius 59290001Sglebius#include <fs/unionfs/union.h> 60290001Sglebius 61290001Sglebius#define NUNIONFSNODECACHE 16 62290001Sglebius 63290001Sglebiusstatic MALLOC_DEFINE(M_UNIONFSHASH, "UNIONFS hash", "UNIONFS hash table"); 64290001SglebiusMALLOC_DEFINE(M_UNIONFSNODE, "UNIONFS node", "UNIONFS vnode private part"); 65290001SglebiusMALLOC_DEFINE(M_UNIONFSPATH, "UNIONFS path", "UNIONFS path private part"); 66290001Sglebius 67290001Sglebius/* 68290001Sglebius * Initialize 69290001Sglebius */ 70290001Sglebiusint 71290001Sglebiusunionfs_init(struct vfsconf *vfsp) 72290001Sglebius{ 73290001Sglebius UNIONFSDEBUG("unionfs_init\n"); /* printed during system boot */ 74290001Sglebius return (0); 75290001Sglebius} 76290001Sglebius 77290001Sglebius/* 78290001Sglebius * Uninitialize 79290001Sglebius */ 80290001Sglebiusint 81290001Sglebiusunionfs_uninit(struct vfsconf *vfsp) 82290001Sglebius{ 83290001Sglebius return (0); 84290001Sglebius} 85290001Sglebius 86290001Sglebiusstatic struct unionfs_node_hashhead * 87290001Sglebiusunionfs_get_hashhead(struct vnode *dvp, char *path) 88290001Sglebius{ 89290001Sglebius int count; 90290001Sglebius char hash; 91290001Sglebius struct unionfs_node *unp; 92290001Sglebius 93290001Sglebius hash = 0; 94290001Sglebius unp = VTOUNIONFS(dvp); 95290001Sglebius if (path != NULL) { 96290001Sglebius for (count = 0; path[count]; count++) 97290001Sglebius hash += path[count]; 98290001Sglebius } 99290001Sglebius 100290001Sglebius return (&(unp->un_hashtbl[hash & (unp->un_hashmask)])); 101290001Sglebius} 102290001Sglebius 103290001Sglebius/* 104290001Sglebius * Get the cached vnode. 105290001Sglebius */ 106290001Sglebiusstatic struct vnode * 107290001Sglebiusunionfs_get_cached_vnode(struct vnode *uvp, struct vnode *lvp, 108290001Sglebius struct vnode *dvp, char *path) 109290001Sglebius{ 110290001Sglebius struct unionfs_node_hashhead *hd; 111290001Sglebius struct unionfs_node *unp; 112290001Sglebius struct vnode *vp; 113290001Sglebius 114290001Sglebius KASSERT((uvp == NULLVP || uvp->v_type == VDIR), 115290001Sglebius ("unionfs_get_cached_vnode: v_type != VDIR")); 116290001Sglebius KASSERT((lvp == NULLVP || lvp->v_type == VDIR), 117290001Sglebius ("unionfs_get_cached_vnode: v_type != VDIR")); 118290001Sglebius 119290001Sglebius VI_LOCK(dvp); 120290001Sglebius hd = unionfs_get_hashhead(dvp, path); 121290001Sglebius LIST_FOREACH(unp, hd, un_hash) { 122290001Sglebius if (!strcmp(unp->un_path, path)) { 123290001Sglebius vp = UNIONFSTOV(unp); 124290001Sglebius VI_LOCK_FLAGS(vp, MTX_DUPOK); 125290001Sglebius VI_UNLOCK(dvp); 126290001Sglebius vp->v_iflag &= ~VI_OWEINACT; 127290001Sglebius if ((vp->v_iflag & (VI_DOOMED | VI_DOINGINACT)) != 0) { 128290001Sglebius VI_UNLOCK(vp); 129290001Sglebius vp = NULLVP; 130290001Sglebius } else 131290001Sglebius VI_UNLOCK(vp); 132290001Sglebius return (vp); 133290001Sglebius } 134290001Sglebius } 135290001Sglebius VI_UNLOCK(dvp); 136290001Sglebius 137290001Sglebius return (NULLVP); 138290001Sglebius} 139290001Sglebius 140290001Sglebius/* 141290001Sglebius * Add the new vnode into cache. 142290001Sglebius */ 143290001Sglebiusstatic struct vnode * 144290001Sglebiusunionfs_ins_cached_vnode(struct unionfs_node *uncp, 145290001Sglebius struct vnode *dvp, char *path) 146290001Sglebius{ 147290001Sglebius struct unionfs_node_hashhead *hd; 148290001Sglebius struct unionfs_node *unp; 149290001Sglebius struct vnode *vp; 150290001Sglebius 151290001Sglebius KASSERT((uncp->un_uppervp==NULLVP || uncp->un_uppervp->v_type==VDIR), 152290001Sglebius ("unionfs_ins_cached_vnode: v_type != VDIR")); 153290001Sglebius KASSERT((uncp->un_lowervp==NULLVP || uncp->un_lowervp->v_type==VDIR), 154290001Sglebius ("unionfs_ins_cached_vnode: v_type != VDIR")); 155290001Sglebius 156290001Sglebius VI_LOCK(dvp); 157290001Sglebius hd = unionfs_get_hashhead(dvp, path); 158290001Sglebius LIST_FOREACH(unp, hd, un_hash) { 159290001Sglebius if (!strcmp(unp->un_path, path)) { 160290001Sglebius vp = UNIONFSTOV(unp); 161290001Sglebius VI_LOCK_FLAGS(vp, MTX_DUPOK); 162290001Sglebius vp->v_iflag &= ~VI_OWEINACT; 163290001Sglebius if ((vp->v_iflag & (VI_DOOMED | VI_DOINGINACT)) != 0) { 164290001Sglebius LIST_INSERT_HEAD(hd, uncp, un_hash); 165290001Sglebius VI_UNLOCK(vp); 166290001Sglebius vp = NULLVP; 167290001Sglebius } else 168290001Sglebius VI_UNLOCK(vp); 169290001Sglebius VI_UNLOCK(dvp); 170290001Sglebius return (vp); 171290001Sglebius } 172290001Sglebius } 173290001Sglebius 174290001Sglebius LIST_INSERT_HEAD(hd, uncp, un_hash); 175290001Sglebius VI_UNLOCK(dvp); 176290001Sglebius 177290001Sglebius return (NULLVP); 178290001Sglebius} 179290001Sglebius 180290001Sglebius/* 181290001Sglebius * Remove the vnode. 182290001Sglebius */ 183290001Sglebiusstatic void 184290001Sglebiusunionfs_rem_cached_vnode(struct unionfs_node *unp, struct vnode *dvp) 185290001Sglebius{ 186290001Sglebius KASSERT((unp != NULL), ("unionfs_rem_cached_vnode: null node")); 187290001Sglebius KASSERT((dvp != NULLVP), 188290001Sglebius ("unionfs_rem_cached_vnode: null parent vnode")); 189290001Sglebius KASSERT((unp->un_hash.le_prev != NULL), 190290001Sglebius ("unionfs_rem_cached_vnode: null hash")); 191290001Sglebius 192290001Sglebius VI_LOCK(dvp); 193290001Sglebius LIST_REMOVE(unp, un_hash); 194290001Sglebius unp->un_hash.le_next = NULL; 195290001Sglebius unp->un_hash.le_prev = NULL; 196290001Sglebius VI_UNLOCK(dvp); 197290001Sglebius} 198290001Sglebius 199290001Sglebius/* 200290001Sglebius * Make a new or get existing unionfs node. 201290001Sglebius * 202290001Sglebius * uppervp and lowervp should be unlocked. Because if new unionfs vnode is 203290001Sglebius * locked, uppervp or lowervp is locked too. In order to prevent dead lock, 204290001Sglebius * you should not lock plurality simultaneously. 205290001Sglebius */ 206290001Sglebiusint 207290001Sglebiusunionfs_nodeget(struct mount *mp, struct vnode *uppervp, 208290001Sglebius struct vnode *lowervp, struct vnode *dvp, 209290001Sglebius struct vnode **vpp, struct componentname *cnp, 210290001Sglebius struct thread *td) 211290001Sglebius{ 212290001Sglebius struct unionfs_mount *ump; 213290001Sglebius struct unionfs_node *unp; 214290001Sglebius struct vnode *vp; 215290001Sglebius int error; 216290001Sglebius int lkflags; 217290001Sglebius enum vtype vt; 218290001Sglebius char *path; 219290001Sglebius 220290001Sglebius ump = MOUNTTOUNIONFSMOUNT(mp); 221290001Sglebius lkflags = (cnp ? cnp->cn_lkflags : 0); 222290001Sglebius path = (cnp ? cnp->cn_nameptr : NULL); 223290001Sglebius *vpp = NULLVP; 224290001Sglebius 225290001Sglebius if (uppervp == NULLVP && lowervp == NULLVP) 226290001Sglebius panic("unionfs_nodeget: upper and lower is null"); 227290001Sglebius 228290001Sglebius vt = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type); 229290001Sglebius 230290001Sglebius /* If it has no ISLASTCN flag, path check is skipped. */ 231290001Sglebius if (cnp && !(cnp->cn_flags & ISLASTCN)) 232290001Sglebius path = NULL; 233290001Sglebius 234290001Sglebius /* check the cache */ 235290001Sglebius if (path != NULL && dvp != NULLVP && vt == VDIR) { 236290001Sglebius vp = unionfs_get_cached_vnode(uppervp, lowervp, dvp, path); 237290001Sglebius if (vp != NULLVP) { 238290001Sglebius vref(vp); 239290001Sglebius *vpp = vp; 240290001Sglebius goto unionfs_nodeget_out; 241290001Sglebius } 242290001Sglebius } 243290001Sglebius 244290001Sglebius if ((uppervp == NULLVP || ump->um_uppervp != uppervp) || 245290001Sglebius (lowervp == NULLVP || ump->um_lowervp != lowervp)) { 246290001Sglebius /* dvp will be NULLVP only in case of root vnode. */ 247290001Sglebius if (dvp == NULLVP) 248290001Sglebius return (EINVAL); 249290001Sglebius } 250290001Sglebius unp = malloc(sizeof(struct unionfs_node), 251290001Sglebius M_UNIONFSNODE, M_WAITOK | M_ZERO); 252290001Sglebius 253290001Sglebius error = getnewvnode("unionfs", mp, &unionfs_vnodeops, &vp); 254290001Sglebius if (error != 0) { 255290001Sglebius free(unp, M_UNIONFSNODE); 256290001Sglebius return (error); 257290001Sglebius } 258290001Sglebius error = insmntque(vp, mp); /* XXX: Too early for mpsafe fs */ 259290001Sglebius if (error != 0) { 260290001Sglebius free(unp, M_UNIONFSNODE); 261290001Sglebius return (error); 262290001Sglebius } 263290001Sglebius if (dvp != NULLVP) 264290001Sglebius vref(dvp); 265290001Sglebius if (uppervp != NULLVP) 266290001Sglebius vref(uppervp); 267290001Sglebius if (lowervp != NULLVP) 268290001Sglebius vref(lowervp); 269290001Sglebius 270290001Sglebius if (vt == VDIR) 271290001Sglebius unp->un_hashtbl = hashinit(NUNIONFSNODECACHE, M_UNIONFSHASH, 272290001Sglebius &(unp->un_hashmask)); 273290001Sglebius 274290001Sglebius unp->un_vnode = vp; 275290001Sglebius unp->un_uppervp = uppervp; 276290001Sglebius unp->un_lowervp = lowervp; 277290001Sglebius unp->un_dvp = dvp; 278290001Sglebius if (uppervp != NULLVP) 279290001Sglebius vp->v_vnlock = uppervp->v_vnlock; 280290001Sglebius else 281290001Sglebius vp->v_vnlock = lowervp->v_vnlock; 282290001Sglebius 283290001Sglebius if (path != NULL) { 284290001Sglebius unp->un_path = (char *) 285290001Sglebius malloc(cnp->cn_namelen +1, M_UNIONFSPATH, M_WAITOK|M_ZERO); 286290001Sglebius bcopy(cnp->cn_nameptr, unp->un_path, cnp->cn_namelen); 287290001Sglebius unp->un_path[cnp->cn_namelen] = '\0'; 288290001Sglebius } 289290001Sglebius vp->v_type = vt; 290290001Sglebius vp->v_data = unp; 291290001Sglebius 292290001Sglebius if ((uppervp != NULLVP && ump->um_uppervp == uppervp) && 293290001Sglebius (lowervp != NULLVP && ump->um_lowervp == lowervp)) 294290001Sglebius vp->v_vflag |= VV_ROOT; 295290001Sglebius 296290001Sglebius if (path != NULL && dvp != NULLVP && vt == VDIR) 297290001Sglebius *vpp = unionfs_ins_cached_vnode(unp, dvp, path); 298290001Sglebius if ((*vpp) != NULLVP) { 299290001Sglebius if (dvp != NULLVP) 300290001Sglebius vrele(dvp); 301290001Sglebius if (uppervp != NULLVP) 302290001Sglebius vrele(uppervp); 303290001Sglebius if (lowervp != NULLVP) 304290001Sglebius vrele(lowervp); 305290001Sglebius 306290001Sglebius unp->un_uppervp = NULLVP; 307290001Sglebius unp->un_lowervp = NULLVP; 308290001Sglebius unp->un_dvp = NULLVP; 309290001Sglebius vrele(vp); 310290001Sglebius vp = *vpp; 311290001Sglebius vref(vp); 312290001Sglebius } else 313290001Sglebius *vpp = vp; 314290001Sglebius 315290001Sglebiusunionfs_nodeget_out: 316290001Sglebius if (lkflags & LK_TYPE_MASK) 317290001Sglebius vn_lock(vp, lkflags | LK_RETRY); 318290001Sglebius 319290001Sglebius return (0); 320290001Sglebius} 321290001Sglebius 322290001Sglebius/* 323290001Sglebius * Clean up the unionfs node. 324290001Sglebius */ 325290001Sglebiusvoid 326290001Sglebiusunionfs_noderem(struct vnode *vp, struct thread *td) 327290001Sglebius{ 328290001Sglebius int count; 329290001Sglebius struct unionfs_node *unp, *unp_t1, *unp_t2; 330290001Sglebius struct unionfs_node_hashhead *hd; 331290001Sglebius struct unionfs_node_status *unsp, *unsp_tmp; 332290001Sglebius struct vnode *lvp; 333290001Sglebius struct vnode *uvp; 334290001Sglebius struct vnode *dvp; 335290001Sglebius 336290001Sglebius /* 337290001Sglebius * Use the interlock to protect the clearing of v_data to 338290001Sglebius * prevent faults in unionfs_lock(). 339290001Sglebius */ 340290001Sglebius VI_LOCK(vp); 341290001Sglebius unp = VTOUNIONFS(vp); 342290001Sglebius lvp = unp->un_lowervp; 343290001Sglebius uvp = unp->un_uppervp; 344290001Sglebius dvp = unp->un_dvp; 345290001Sglebius unp->un_lowervp = unp->un_uppervp = NULLVP; 346290001Sglebius vp->v_vnlock = &(vp->v_lock); 347290001Sglebius vp->v_data = NULL; 348290001Sglebius vp->v_object = NULL; 349290001Sglebius VI_UNLOCK(vp); 350290001Sglebius 351290001Sglebius if (lvp != NULLVP) 352290001Sglebius VOP_UNLOCK(lvp, LK_RELEASE); 353290001Sglebius if (uvp != NULLVP) 354290001Sglebius VOP_UNLOCK(uvp, LK_RELEASE); 355290001Sglebius 356290001Sglebius if (dvp != NULLVP && unp->un_hash.le_prev != NULL) 357290001Sglebius unionfs_rem_cached_vnode(unp, dvp); 358290001Sglebius 359290001Sglebius if (lockmgr(vp->v_vnlock, LK_EXCLUSIVE, VI_MTX(vp)) != 0) 360290001Sglebius panic("the lock for deletion is unacquirable."); 361290001Sglebius 362290001Sglebius if (lvp != NULLVP) 363290001Sglebius vrele(lvp); 364290001Sglebius if (uvp != NULLVP) 365290001Sglebius vrele(uvp); 366290001Sglebius if (dvp != NULLVP) { 367290001Sglebius vrele(dvp); 368290001Sglebius unp->un_dvp = NULLVP; 369290001Sglebius } 370290001Sglebius if (unp->un_path != NULL) { 371290001Sglebius free(unp->un_path, M_UNIONFSPATH); 372290001Sglebius unp->un_path = NULL; 373290001Sglebius } 374290001Sglebius 375290001Sglebius if (unp->un_hashtbl != NULL) { 376290001Sglebius for (count = 0; count <= unp->un_hashmask; count++) { 377290001Sglebius hd = unp->un_hashtbl + count; 378290001Sglebius LIST_FOREACH_SAFE(unp_t1, hd, un_hash, unp_t2) { 379290001Sglebius LIST_REMOVE(unp_t1, un_hash); 380290001Sglebius unp_t1->un_hash.le_next = NULL; 381290001Sglebius unp_t1->un_hash.le_prev = NULL; 382290001Sglebius } 383290001Sglebius } 384290001Sglebius hashdestroy(unp->un_hashtbl, M_UNIONFSHASH, unp->un_hashmask); 385290001Sglebius } 386290001Sglebius 387290001Sglebius LIST_FOREACH_SAFE(unsp, &(unp->un_unshead), uns_list, unsp_tmp) { 388290001Sglebius LIST_REMOVE(unsp, uns_list); 389290001Sglebius free(unsp, M_TEMP); 390290001Sglebius } 391290001Sglebius free(unp, M_UNIONFSNODE); 392290001Sglebius} 393290001Sglebius 394290001Sglebius/* 395290001Sglebius * Get the unionfs node status. 396290001Sglebius * You need exclusive lock this vnode. 397290001Sglebius */ 398290001Sglebiusvoid 399290001Sglebiusunionfs_get_node_status(struct unionfs_node *unp, struct thread *td, 400290001Sglebius struct unionfs_node_status **unspp) 401290001Sglebius{ 402290001Sglebius struct unionfs_node_status *unsp; 403290001Sglebius pid_t pid = td->td_proc->p_pid; 404290001Sglebius 405290001Sglebius KASSERT(NULL != unspp, ("null pointer")); 406290001Sglebius ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status"); 407290001Sglebius 408290001Sglebius LIST_FOREACH(unsp, &(unp->un_unshead), uns_list) { 409290001Sglebius if (unsp->uns_pid == pid) { 410290001Sglebius *unspp = unsp; 411290001Sglebius return; 412290001Sglebius } 413290001Sglebius } 414290001Sglebius 415290001Sglebius /* create a new unionfs node status */ 416290001Sglebius unsp = malloc(sizeof(struct unionfs_node_status), 417290001Sglebius M_TEMP, M_WAITOK | M_ZERO); 418290001Sglebius 419290001Sglebius unsp->uns_pid = pid; 420290001Sglebius LIST_INSERT_HEAD(&(unp->un_unshead), unsp, uns_list); 421290001Sglebius 422290001Sglebius *unspp = unsp; 423290001Sglebius} 424290001Sglebius 425290001Sglebius/* 426290001Sglebius * Remove the unionfs node status, if you can. 427290001Sglebius * You need exclusive lock this vnode. 428290001Sglebius */ 429290001Sglebiusvoid 430290001Sglebiusunionfs_tryrem_node_status(struct unionfs_node *unp, 431290001Sglebius struct unionfs_node_status *unsp) 432290001Sglebius{ 433290001Sglebius KASSERT(NULL != unsp, ("null pointer")); 434290001Sglebius ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status"); 435290001Sglebius 436290001Sglebius if (0 < unsp->uns_lower_opencnt || 0 < unsp->uns_upper_opencnt) 437290001Sglebius return; 438290001Sglebius 439290001Sglebius LIST_REMOVE(unsp, uns_list); 440290001Sglebius free(unsp, M_TEMP); 441290001Sglebius} 442290001Sglebius 443290001Sglebius/* 444290001Sglebius * Create upper node attr. 445290001Sglebius */ 446290001Sglebiusvoid 447290001Sglebiusunionfs_create_uppervattr_core(struct unionfs_mount *ump, 448290001Sglebius struct vattr *lva, 449290001Sglebius struct vattr *uva, 450290001Sglebius struct thread *td) 451290001Sglebius{ 452290001Sglebius VATTR_NULL(uva); 453290001Sglebius uva->va_type = lva->va_type; 454290001Sglebius uva->va_atime = lva->va_atime; 455290001Sglebius uva->va_mtime = lva->va_mtime; 456290001Sglebius uva->va_ctime = lva->va_ctime; 457290001Sglebius 458290001Sglebius switch (ump->um_copymode) { 459290001Sglebius case UNIONFS_TRANSPARENT: 460290001Sglebius uva->va_mode = lva->va_mode; 461290001Sglebius uva->va_uid = lva->va_uid; 462290001Sglebius uva->va_gid = lva->va_gid; 463290001Sglebius break; 464290001Sglebius case UNIONFS_MASQUERADE: 465290001Sglebius if (ump->um_uid == lva->va_uid) { 466290001Sglebius uva->va_mode = lva->va_mode & 077077; 467290001Sglebius uva->va_mode |= (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile) & 0700; 468290001Sglebius uva->va_uid = lva->va_uid; 469290001Sglebius uva->va_gid = lva->va_gid; 470290001Sglebius } else { 471290001Sglebius uva->va_mode = (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile); 472290001Sglebius uva->va_uid = ump->um_uid; 473290001Sglebius uva->va_gid = ump->um_gid; 474290001Sglebius } 475290001Sglebius break; 476290001Sglebius default: /* UNIONFS_TRADITIONAL */ 477290001Sglebius uva->va_mode = 0777 & ~td->td_proc->p_fd->fd_cmask; 478290001Sglebius uva->va_uid = ump->um_uid; 479290001Sglebius uva->va_gid = ump->um_gid; 480290001Sglebius break; 481290001Sglebius } 482290001Sglebius} 483290001Sglebius 484290001Sglebius/* 485290001Sglebius * Create upper node attr. 486290001Sglebius */ 487290001Sglebiusint 488290001Sglebiusunionfs_create_uppervattr(struct unionfs_mount *ump, 489290001Sglebius struct vnode *lvp, 490290001Sglebius struct vattr *uva, 491290001Sglebius struct ucred *cred, 492290001Sglebius struct thread *td) 493290001Sglebius{ 494290001Sglebius int error; 495290001Sglebius struct vattr lva; 496290001Sglebius 497290001Sglebius if ((error = VOP_GETATTR(lvp, &lva, cred))) 498290001Sglebius return (error); 499290001Sglebius 500290001Sglebius unionfs_create_uppervattr_core(ump, &lva, uva, td); 501290001Sglebius 502290001Sglebius return (error); 503290001Sglebius} 504290001Sglebius 505290001Sglebius/* 506290001Sglebius * relookup 507290001Sglebius * 508290001Sglebius * dvp should be locked on entry and will be locked on return. 509290001Sglebius * 510290001Sglebius * If an error is returned, *vpp will be invalid, otherwise it will hold a 511290001Sglebius * locked, referenced vnode. If *vpp == dvp then remember that only one 512290001Sglebius * LK_EXCLUSIVE lock is held. 513290001Sglebius */ 514290001Sglebiusint 515290001Sglebiusunionfs_relookup(struct vnode *dvp, struct vnode **vpp, 516290001Sglebius struct componentname *cnp, struct componentname *cn, 517290001Sglebius struct thread *td, char *path, int pathlen, u_long nameiop) 518290001Sglebius{ 519290001Sglebius int error; 520290001Sglebius 521290001Sglebius cn->cn_namelen = pathlen; 522290001Sglebius cn->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); 523290001Sglebius bcopy(path, cn->cn_pnbuf, pathlen); 524290001Sglebius cn->cn_pnbuf[pathlen] = '\0'; 525290001Sglebius 526290001Sglebius cn->cn_nameiop = nameiop; 527290001Sglebius cn->cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN); 528290001Sglebius cn->cn_lkflags = LK_EXCLUSIVE; 529290001Sglebius cn->cn_thread = td; 530290001Sglebius cn->cn_cred = cnp->cn_cred; 531290001Sglebius 532290001Sglebius cn->cn_nameptr = cn->cn_pnbuf; 533290001Sglebius cn->cn_consume = cnp->cn_consume; 534290001Sglebius 535290001Sglebius if (nameiop == DELETE) 536290001Sglebius cn->cn_flags |= (cnp->cn_flags & (DOWHITEOUT | SAVESTART)); 537290001Sglebius else if (RENAME == nameiop) 538290001Sglebius cn->cn_flags |= (cnp->cn_flags & SAVESTART); 539290001Sglebius else if (nameiop == CREATE) 540290001Sglebius cn->cn_flags |= NOCACHE; 541290001Sglebius 542290001Sglebius vref(dvp); 543290001Sglebius VOP_UNLOCK(dvp, LK_RELEASE); 544290001Sglebius 545290001Sglebius if ((error = relookup(dvp, vpp, cn))) { 546290001Sglebius uma_zfree(namei_zone, cn->cn_pnbuf); 547290001Sglebius cn->cn_flags &= ~HASBUF; 548290001Sglebius vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); 549290001Sglebius } else 550290001Sglebius vrele(dvp); 551290001Sglebius 552290001Sglebius return (error); 553290001Sglebius} 554290001Sglebius 555290001Sglebius/* 556290001Sglebius * relookup for CREATE namei operation. 557290001Sglebius * 558290001Sglebius * dvp is unionfs vnode. dvp should be locked. 559290001Sglebius * 560290001Sglebius * If it called 'unionfs_copyfile' function by unionfs_link etc, 561290001Sglebius * VOP_LOOKUP information is broken. 562290001Sglebius * So it need relookup in order to create link etc. 563290001Sglebius */ 564290001Sglebiusint 565290001Sglebiusunionfs_relookup_for_create(struct vnode *dvp, struct componentname *cnp, 566290001Sglebius struct thread *td) 567290001Sglebius{ 568290001Sglebius int error; 569290001Sglebius struct vnode *udvp; 570290001Sglebius struct vnode *vp; 571290001Sglebius struct componentname cn; 572290001Sglebius 573290001Sglebius udvp = UNIONFSVPTOUPPERVP(dvp); 574290001Sglebius vp = NULLVP; 575290001Sglebius 576290001Sglebius error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr, 577290001Sglebius strlen(cnp->cn_nameptr), CREATE); 578290001Sglebius if (error) 579290001Sglebius return (error); 580290001Sglebius 581290001Sglebius if (vp != NULLVP) { 582290001Sglebius if (udvp == vp) 583290001Sglebius vrele(vp); 584290001Sglebius else 585290001Sglebius vput(vp); 586290001Sglebius 587290001Sglebius error = EEXIST; 588290001Sglebius } 589290001Sglebius 590290001Sglebius if (cn.cn_flags & HASBUF) { 591290001Sglebius uma_zfree(namei_zone, cn.cn_pnbuf); 592290001Sglebius cn.cn_flags &= ~HASBUF; 593290001Sglebius } 594290001Sglebius 595290001Sglebius if (!error) { 596290001Sglebius cn.cn_flags |= (cnp->cn_flags & HASBUF); 597290001Sglebius cnp->cn_flags = cn.cn_flags; 598290001Sglebius } 599290001Sglebius 600290001Sglebius return (error); 601290001Sglebius} 602290001Sglebius 603290001Sglebius/* 604290001Sglebius * relookup for DELETE namei operation. 605290001Sglebius * 606290001Sglebius * dvp is unionfs vnode. dvp should be locked. 607290001Sglebius */ 608290001Sglebiusint 609290001Sglebiusunionfs_relookup_for_delete(struct vnode *dvp, struct componentname *cnp, 610290001Sglebius struct thread *td) 611290001Sglebius{ 612290001Sglebius int error; 613290001Sglebius struct vnode *udvp; 614290001Sglebius struct vnode *vp; 615290001Sglebius struct componentname cn; 616290001Sglebius 617290001Sglebius udvp = UNIONFSVPTOUPPERVP(dvp); 618290001Sglebius vp = NULLVP; 619290001Sglebius 620290001Sglebius error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr, 621290001Sglebius strlen(cnp->cn_nameptr), DELETE); 622290001Sglebius if (error) 623290001Sglebius return (error); 624290001Sglebius 625290001Sglebius if (vp == NULLVP) 626290001Sglebius error = ENOENT; 627290001Sglebius else { 628290001Sglebius if (udvp == vp) 629290001Sglebius vrele(vp); 630290001Sglebius else 631290001Sglebius vput(vp); 632290001Sglebius } 633290001Sglebius 634290001Sglebius if (cn.cn_flags & HASBUF) { 635290001Sglebius uma_zfree(namei_zone, cn.cn_pnbuf); 636290001Sglebius cn.cn_flags &= ~HASBUF; 637290001Sglebius } 638290001Sglebius 639290001Sglebius if (!error) { 640290001Sglebius cn.cn_flags |= (cnp->cn_flags & HASBUF); 641290001Sglebius cnp->cn_flags = cn.cn_flags; 642290001Sglebius } 643290001Sglebius 644290001Sglebius return (error); 645290001Sglebius} 646290001Sglebius 647290001Sglebius/* 648290001Sglebius * relookup for RENAME namei operation. 649290001Sglebius * 650290001Sglebius * dvp is unionfs vnode. dvp should be locked. 651290001Sglebius */ 652290001Sglebiusint 653290001Sglebiusunionfs_relookup_for_rename(struct vnode *dvp, struct componentname *cnp, 654290001Sglebius struct thread *td) 655290001Sglebius{ 656290001Sglebius int error; 657290001Sglebius struct vnode *udvp; 658290001Sglebius struct vnode *vp; 659290001Sglebius struct componentname cn; 660290001Sglebius 661290001Sglebius udvp = UNIONFSVPTOUPPERVP(dvp); 662290001Sglebius vp = NULLVP; 663290001Sglebius 664290001Sglebius error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr, 665290001Sglebius strlen(cnp->cn_nameptr), RENAME); 666290001Sglebius if (error) 667290001Sglebius return (error); 668290001Sglebius 669290001Sglebius if (vp != NULLVP) { 670290001Sglebius if (udvp == vp) 671290001Sglebius vrele(vp); 672290001Sglebius else 673290001Sglebius vput(vp); 674290001Sglebius } 675290001Sglebius 676290001Sglebius if (cn.cn_flags & HASBUF) { 677290001Sglebius uma_zfree(namei_zone, cn.cn_pnbuf); 678290001Sglebius cn.cn_flags &= ~HASBUF; 679290001Sglebius } 680290001Sglebius 681290001Sglebius if (!error) { 682290001Sglebius cn.cn_flags |= (cnp->cn_flags & HASBUF); 683290001Sglebius cnp->cn_flags = cn.cn_flags; 684290001Sglebius } 685290001Sglebius 686290001Sglebius return (error); 687290001Sglebius 688290001Sglebius} 689290001Sglebius 690290001Sglebius/* 691290001Sglebius * Update the unionfs_node. 692290001Sglebius * 693290001Sglebius * uvp is new locked upper vnode. unionfs vnode's lock will be exchanged to the 694290001Sglebius * uvp's lock and lower's lock will be unlocked. 695290001Sglebius */ 696290001Sglebiusstatic void 697290001Sglebiusunionfs_node_update(struct unionfs_node *unp, struct vnode *uvp, 698290001Sglebius struct thread *td) 699290001Sglebius{ 700290001Sglebius unsigned count, lockrec; 701290001Sglebius struct vnode *vp; 702290001Sglebius struct vnode *lvp; 703290001Sglebius struct vnode *dvp; 704290001Sglebius 705290001Sglebius vp = UNIONFSTOV(unp); 706290001Sglebius lvp = unp->un_lowervp; 707290001Sglebius ASSERT_VOP_ELOCKED(lvp, "unionfs_node_update"); 708290001Sglebius dvp = unp->un_dvp; 709290001Sglebius 710290001Sglebius /* 711290001Sglebius * lock update 712290001Sglebius */ 713290001Sglebius VI_LOCK(vp); 714290001Sglebius unp->un_uppervp = uvp; 715290001Sglebius vp->v_vnlock = uvp->v_vnlock; 716290001Sglebius VI_UNLOCK(vp); 717290001Sglebius lockrec = lvp->v_vnlock->lk_recurse; 718290001Sglebius for (count = 0; count < lockrec; count++) 719290001Sglebius vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY); 720290001Sglebius 721290001Sglebius /* 722290001Sglebius * cache update 723290001Sglebius */ 724290001Sglebius if (unp->un_path != NULL && dvp != NULLVP && vp->v_type == VDIR) { 725290001Sglebius static struct unionfs_node_hashhead *hd; 726290001Sglebius 727290001Sglebius VI_LOCK(dvp); 728290001Sglebius hd = unionfs_get_hashhead(dvp, unp->un_path); 729290001Sglebius LIST_REMOVE(unp, un_hash); 730290001Sglebius LIST_INSERT_HEAD(hd, unp, un_hash); 731290001Sglebius VI_UNLOCK(dvp); 732290001Sglebius } 733290001Sglebius} 734290001Sglebius 735290001Sglebius/* 736290001Sglebius * Create a new shadow dir. 737290001Sglebius * 738290001Sglebius * udvp should be locked on entry and will be locked on return. 739290001Sglebius * 740290001Sglebius * If no error returned, unp will be updated. 741290001Sglebius */ 742290001Sglebiusint 743290001Sglebiusunionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *udvp, 744290001Sglebius struct unionfs_node *unp, struct componentname *cnp, 745290001Sglebius struct thread *td) 746290001Sglebius{ 747290001Sglebius int error; 748290001Sglebius struct vnode *lvp; 749290001Sglebius struct vnode *uvp; 750290001Sglebius struct vattr va; 751290001Sglebius struct vattr lva; 752290001Sglebius struct componentname cn; 753290001Sglebius struct mount *mp; 754290001Sglebius struct ucred *cred; 755290001Sglebius struct ucred *credbk; 756290001Sglebius struct uidinfo *rootinfo; 757290001Sglebius 758290001Sglebius if (unp->un_uppervp != NULLVP) 759290001Sglebius return (EEXIST); 760290001Sglebius 761290001Sglebius lvp = unp->un_lowervp; 762290001Sglebius uvp = NULLVP; 763290001Sglebius credbk = cnp->cn_cred; 764290001Sglebius 765290001Sglebius /* Authority change to root */ 766290001Sglebius rootinfo = uifind((uid_t)0); 767290001Sglebius cred = crdup(cnp->cn_cred); 768290001Sglebius /* 769290001Sglebius * The calls to chgproccnt() are needed to compensate for change_ruid() 770290001Sglebius * calling chgproccnt(). 771290001Sglebius */ 772290001Sglebius chgproccnt(cred->cr_ruidinfo, 1, 0); 773290001Sglebius change_euid(cred, rootinfo); 774290001Sglebius change_ruid(cred, rootinfo); 775290001Sglebius change_svuid(cred, (uid_t)0); 776290001Sglebius uifree(rootinfo); 777290001Sglebius cnp->cn_cred = cred; 778290001Sglebius 779290001Sglebius memset(&cn, 0, sizeof(cn)); 780290001Sglebius 781290001Sglebius if ((error = VOP_GETATTR(lvp, &lva, cnp->cn_cred))) 782290001Sglebius goto unionfs_mkshadowdir_abort; 783290001Sglebius 784290001Sglebius if ((error = unionfs_relookup(udvp, &uvp, cnp, &cn, td, cnp->cn_nameptr, cnp->cn_namelen, CREATE))) 785290001Sglebius goto unionfs_mkshadowdir_abort; 786290001Sglebius if (uvp != NULLVP) { 787290001Sglebius if (udvp == uvp) 788290001Sglebius vrele(uvp); 789290001Sglebius else 790290001Sglebius vput(uvp); 791290001Sglebius 792290001Sglebius error = EEXIST; 793290001Sglebius goto unionfs_mkshadowdir_free_out; 794290001Sglebius } 795290001Sglebius 796290001Sglebius if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH))) 797290001Sglebius goto unionfs_mkshadowdir_free_out; 798290001Sglebius unionfs_create_uppervattr_core(ump, &lva, &va, td); 799290001Sglebius 800290001Sglebius error = VOP_MKDIR(udvp, &uvp, &cn, &va); 801290001Sglebius 802290001Sglebius if (!error) { 803290001Sglebius unionfs_node_update(unp, uvp, td); 804290001Sglebius 805290001Sglebius /* 806290001Sglebius * XXX The bug which cannot set uid/gid was corrected. 807290001Sglebius * Ignore errors. 808290001Sglebius */ 809290001Sglebius va.va_type = VNON; 810290001Sglebius VOP_SETATTR(uvp, &va, cn.cn_cred); 811290001Sglebius } 812290001Sglebius vn_finished_write(mp); 813290001Sglebius 814290001Sglebiusunionfs_mkshadowdir_free_out: 815290001Sglebius if (cn.cn_flags & HASBUF) { 816290001Sglebius uma_zfree(namei_zone, cn.cn_pnbuf); 817290001Sglebius cn.cn_flags &= ~HASBUF; 818290001Sglebius } 819290001Sglebius 820290001Sglebiusunionfs_mkshadowdir_abort: 821290001Sglebius cnp->cn_cred = credbk; 822290001Sglebius chgproccnt(cred->cr_ruidinfo, -1, 0); 823290001Sglebius crfree(cred); 824290001Sglebius 825290001Sglebius return (error); 826290001Sglebius} 827290001Sglebius 828290001Sglebius/* 829290001Sglebius * Create a new whiteout. 830290001Sglebius * 831290001Sglebius * dvp should be locked on entry and will be locked on return. 832290001Sglebius */ 833290001Sglebiusint 834290001Sglebiusunionfs_mkwhiteout(struct vnode *dvp, struct componentname *cnp, 835290001Sglebius struct thread *td, char *path) 836290001Sglebius{ 837290001Sglebius int error; 838290001Sglebius struct vnode *wvp; 839290001Sglebius struct componentname cn; 840290001Sglebius struct mount *mp; 841290001Sglebius 842290001Sglebius if (path == NULL) 843290001Sglebius path = cnp->cn_nameptr; 844290001Sglebius 845290001Sglebius wvp = NULLVP; 846290001Sglebius if ((error = unionfs_relookup(dvp, &wvp, cnp, &cn, td, path, strlen(path), CREATE))) 847290001Sglebius return (error); 848290001Sglebius if (wvp != NULLVP) { 849290001Sglebius if (cn.cn_flags & HASBUF) { 850290001Sglebius uma_zfree(namei_zone, cn.cn_pnbuf); 851290001Sglebius cn.cn_flags &= ~HASBUF; 852290001Sglebius } 853290001Sglebius if (dvp == wvp) 854290001Sglebius vrele(wvp); 855290001Sglebius else 856290001Sglebius vput(wvp); 857290001Sglebius 858290001Sglebius return (EEXIST); 859290001Sglebius } 860290001Sglebius 861290001Sglebius if ((error = vn_start_write(dvp, &mp, V_WAIT | PCATCH))) 862290001Sglebius goto unionfs_mkwhiteout_free_out; 863290001Sglebius error = VOP_WHITEOUT(dvp, &cn, CREATE); 864290001Sglebius 865290001Sglebius vn_finished_write(mp); 866290001Sglebius 867290001Sglebiusunionfs_mkwhiteout_free_out: 868290001Sglebius if (cn.cn_flags & HASBUF) { 869290001Sglebius uma_zfree(namei_zone, cn.cn_pnbuf); 870290001Sglebius cn.cn_flags &= ~HASBUF; 871290001Sglebius } 872290001Sglebius 873290001Sglebius return (error); 874290001Sglebius} 875290001Sglebius 876290001Sglebius/* 877290001Sglebius * Create a new vnode for create a new shadow file. 878290001Sglebius * 879290001Sglebius * If an error is returned, *vpp will be invalid, otherwise it will hold a 880290001Sglebius * locked, referenced and opened vnode. 881290001Sglebius * 882290001Sglebius * unp is never updated. 883290001Sglebius */ 884290001Sglebiusstatic int 885290001Sglebiusunionfs_vn_create_on_upper(struct vnode **vpp, struct vnode *udvp, 886290001Sglebius struct unionfs_node *unp, struct vattr *uvap, 887290001Sglebius struct thread *td) 888290001Sglebius{ 889290001Sglebius struct unionfs_mount *ump; 890290001Sglebius struct vnode *vp; 891290001Sglebius struct vnode *lvp; 892290001Sglebius struct ucred *cred; 893290001Sglebius struct vattr lva; 894290001Sglebius int fmode; 895290001Sglebius int error; 896290001Sglebius struct componentname cn; 897290001Sglebius 898290001Sglebius ump = MOUNTTOUNIONFSMOUNT(UNIONFSTOV(unp)->v_mount); 899290001Sglebius vp = NULLVP; 900290001Sglebius lvp = unp->un_lowervp; 901290001Sglebius cred = td->td_ucred; 902290001Sglebius fmode = FFLAGS(O_WRONLY | O_CREAT | O_TRUNC | O_EXCL); 903290001Sglebius error = 0; 904290001Sglebius 905290001Sglebius if ((error = VOP_GETATTR(lvp, &lva, cred)) != 0) 906290001Sglebius return (error); 907290001Sglebius unionfs_create_uppervattr_core(ump, &lva, uvap, td); 908290001Sglebius 909290001Sglebius if (unp->un_path == NULL) 910290001Sglebius panic("unionfs: un_path is null"); 911290001Sglebius 912290001Sglebius cn.cn_namelen = strlen(unp->un_path); 913290001Sglebius cn.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); 914290001Sglebius bcopy(unp->un_path, cn.cn_pnbuf, cn.cn_namelen + 1); 915290001Sglebius cn.cn_nameiop = CREATE; 916290001Sglebius cn.cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN); 917290001Sglebius cn.cn_lkflags = LK_EXCLUSIVE; 918290001Sglebius cn.cn_thread = td; 919290001Sglebius cn.cn_cred = cred; 920290001Sglebius cn.cn_nameptr = cn.cn_pnbuf; 921290001Sglebius cn.cn_consume = 0; 922290001Sglebius 923290001Sglebius vref(udvp); 924290001Sglebius if ((error = relookup(udvp, &vp, &cn)) != 0) 925290001Sglebius goto unionfs_vn_create_on_upper_free_out2; 926290001Sglebius vrele(udvp); 927290001Sglebius 928290001Sglebius if (vp != NULLVP) { 929290001Sglebius if (vp == udvp) 930290001Sglebius vrele(vp); 931290001Sglebius else 932290001Sglebius vput(vp); 933290001Sglebius error = EEXIST; 934290001Sglebius goto unionfs_vn_create_on_upper_free_out1; 935290001Sglebius } 936290001Sglebius 937290001Sglebius if ((error = VOP_CREATE(udvp, &vp, &cn, uvap)) != 0) 938290001Sglebius goto unionfs_vn_create_on_upper_free_out1; 939290001Sglebius 940290001Sglebius if ((error = VOP_OPEN(vp, fmode, cred, td, NULL)) != 0) { 941290001Sglebius vput(vp); 942290001Sglebius goto unionfs_vn_create_on_upper_free_out1; 943290001Sglebius } 944290001Sglebius VOP_ADD_WRITECOUNT(vp, 1); 945290001Sglebius CTR3(KTR_VFS, "%s: vp %p v_writecount increased to %d", __func__, vp, 946290001Sglebius vp->v_writecount); 947290001Sglebius *vpp = vp; 948290001Sglebius 949290001Sglebiusunionfs_vn_create_on_upper_free_out1: 950290001Sglebius VOP_UNLOCK(udvp, LK_RELEASE); 951290001Sglebius 952290001Sglebiusunionfs_vn_create_on_upper_free_out2: 953290001Sglebius if (cn.cn_flags & HASBUF) { 954290001Sglebius uma_zfree(namei_zone, cn.cn_pnbuf); 955290001Sglebius cn.cn_flags &= ~HASBUF; 956290001Sglebius } 957290001Sglebius 958290001Sglebius return (error); 959290001Sglebius} 960290001Sglebius 961290001Sglebius/* 962290001Sglebius * Copy from lvp to uvp. 963290001Sglebius * 964290001Sglebius * lvp and uvp should be locked and opened on entry and will be locked and 965290001Sglebius * opened on return. 966290001Sglebius */ 967290001Sglebiusstatic int 968290001Sglebiusunionfs_copyfile_core(struct vnode *lvp, struct vnode *uvp, 969290001Sglebius struct ucred *cred, struct thread *td) 970290001Sglebius{ 971290001Sglebius int error; 972290001Sglebius off_t offset; 973290001Sglebius int count; 974290001Sglebius int bufoffset; 975290001Sglebius char *buf; 976290001Sglebius struct uio uio; 977290001Sglebius struct iovec iov; 978290001Sglebius 979290001Sglebius error = 0; 980290001Sglebius memset(&uio, 0, sizeof(uio)); 981290001Sglebius 982290001Sglebius uio.uio_td = td; 983290001Sglebius uio.uio_segflg = UIO_SYSSPACE; 984290001Sglebius uio.uio_offset = 0; 985290001Sglebius 986290001Sglebius buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK); 987290001Sglebius 988290001Sglebius while (error == 0) { 989290001Sglebius offset = uio.uio_offset; 990290001Sglebius 991290001Sglebius uio.uio_iov = &iov; 992290001Sglebius uio.uio_iovcnt = 1; 993290001Sglebius iov.iov_base = buf; 994290001Sglebius iov.iov_len = MAXBSIZE; 995290001Sglebius uio.uio_resid = iov.iov_len; 996290001Sglebius uio.uio_rw = UIO_READ; 997290001Sglebius 998290001Sglebius if ((error = VOP_READ(lvp, &uio, 0, cred)) != 0) 999290001Sglebius break; 1000290001Sglebius if ((count = MAXBSIZE - uio.uio_resid) == 0) 1001290001Sglebius break; 1002290001Sglebius 1003290001Sglebius bufoffset = 0; 1004290001Sglebius while (bufoffset < count) { 1005290001Sglebius uio.uio_iov = &iov; 1006290001Sglebius uio.uio_iovcnt = 1; 1007290001Sglebius iov.iov_base = buf + bufoffset; 1008290001Sglebius iov.iov_len = count - bufoffset; 1009290001Sglebius uio.uio_offset = offset + bufoffset; 1010290001Sglebius uio.uio_resid = iov.iov_len; 1011290001Sglebius uio.uio_rw = UIO_WRITE; 1012290001Sglebius 1013290001Sglebius if ((error = VOP_WRITE(uvp, &uio, 0, cred)) != 0) 1014290001Sglebius break; 1015290001Sglebius 1016290001Sglebius bufoffset += (count - bufoffset) - uio.uio_resid; 1017290001Sglebius } 1018290001Sglebius 1019290001Sglebius uio.uio_offset = offset + bufoffset; 1020290001Sglebius } 1021290001Sglebius 1022290001Sglebius free(buf, M_TEMP); 1023290001Sglebius 1024290001Sglebius return (error); 1025290001Sglebius} 1026290001Sglebius 1027290001Sglebius/* 1028290001Sglebius * Copy file from lower to upper. 1029290001Sglebius * 1030290001Sglebius * If you need copy of the contents, set 1 to docopy. Otherwise, set 0 to 1031290001Sglebius * docopy. 1032290001Sglebius * 1033290001Sglebius * If no error returned, unp will be updated. 1034290001Sglebius */ 1035290001Sglebiusint 1036290001Sglebiusunionfs_copyfile(struct unionfs_node *unp, int docopy, struct ucred *cred, 1037290001Sglebius struct thread *td) 1038290001Sglebius{ 1039290001Sglebius int error; 1040290001Sglebius struct mount *mp; 1041290001Sglebius struct vnode *udvp; 1042290001Sglebius struct vnode *lvp; 1043290001Sglebius struct vnode *uvp; 1044290001Sglebius struct vattr uva; 1045290001Sglebius 1046290001Sglebius lvp = unp->un_lowervp; 1047290001Sglebius uvp = NULLVP; 1048290001Sglebius 1049290001Sglebius if ((UNIONFSTOV(unp)->v_mount->mnt_flag & MNT_RDONLY)) 1050290001Sglebius return (EROFS); 1051290001Sglebius if (unp->un_dvp == NULLVP) 1052290001Sglebius return (EINVAL); 1053290001Sglebius if (unp->un_uppervp != NULLVP) 1054290001Sglebius return (EEXIST); 1055290001Sglebius udvp = VTOUNIONFS(unp->un_dvp)->un_uppervp; 1056290001Sglebius if (udvp == NULLVP) 1057290001Sglebius return (EROFS); 1058290001Sglebius if ((udvp->v_mount->mnt_flag & MNT_RDONLY)) 1059290001Sglebius return (EROFS); 1060290001Sglebius 1061290001Sglebius error = VOP_ACCESS(lvp, VREAD, cred, td); 1062290001Sglebius if (error != 0) 1063290001Sglebius return (error); 1064290001Sglebius 1065290001Sglebius if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH)) != 0) 1066290001Sglebius return (error); 1067290001Sglebius error = unionfs_vn_create_on_upper(&uvp, udvp, unp, &uva, td); 1068290001Sglebius if (error != 0) { 1069290001Sglebius vn_finished_write(mp); 1070290001Sglebius return (error); 1071290001Sglebius } 1072290001Sglebius 1073290001Sglebius if (docopy != 0) { 1074290001Sglebius error = VOP_OPEN(lvp, FREAD, cred, td, NULL); 1075290001Sglebius if (error == 0) { 1076290001Sglebius error = unionfs_copyfile_core(lvp, uvp, cred, td); 1077290001Sglebius VOP_CLOSE(lvp, FREAD, cred, td); 1078290001Sglebius } 1079290001Sglebius } 1080290001Sglebius VOP_CLOSE(uvp, FWRITE, cred, td); 1081290001Sglebius VOP_ADD_WRITECOUNT(uvp, -1); 1082290001Sglebius CTR3(KTR_VFS, "%s: vp %p v_writecount decreased to %d", __func__, uvp, 1083290001Sglebius uvp->v_writecount); 1084290001Sglebius 1085290001Sglebius vn_finished_write(mp); 1086290001Sglebius 1087290001Sglebius if (error == 0) { 1088290001Sglebius /* Reset the attributes. Ignore errors. */ 1089290001Sglebius uva.va_type = VNON; 1090290001Sglebius VOP_SETATTR(uvp, &uva, cred); 1091290001Sglebius } 1092290001Sglebius 1093290001Sglebius unionfs_node_update(unp, uvp, td); 1094290001Sglebius 1095290001Sglebius return (error); 1096290001Sglebius} 1097290001Sglebius 1098290001Sglebius/* 1099290001Sglebius * It checks whether vp can rmdir. (check empty) 1100290001Sglebius * 1101290001Sglebius * vp is unionfs vnode. 1102290001Sglebius * vp should be locked. 1103290001Sglebius */ 1104290001Sglebiusint 1105290001Sglebiusunionfs_check_rmdir(struct vnode *vp, struct ucred *cred, struct thread *td) 1106290001Sglebius{ 1107290001Sglebius int error; 1108290001Sglebius int eofflag; 1109290001Sglebius int lookuperr; 1110290001Sglebius struct vnode *uvp; 1111290001Sglebius struct vnode *lvp; 1112290001Sglebius struct vnode *tvp; 1113290001Sglebius struct vattr va; 1114290001Sglebius struct componentname cn; 1115290001Sglebius /* 1116290001Sglebius * The size of buf needs to be larger than DIRBLKSIZ. 1117290001Sglebius */ 1118290001Sglebius char buf[256 * 6]; 1119290001Sglebius struct dirent *dp; 1120290001Sglebius struct dirent *edp; 1121290001Sglebius struct uio uio; 1122290001Sglebius struct iovec iov; 1123290001Sglebius 1124290001Sglebius ASSERT_VOP_ELOCKED(vp, "unionfs_check_rmdir"); 1125290001Sglebius 1126290001Sglebius eofflag = 0; 1127290001Sglebius uvp = UNIONFSVPTOUPPERVP(vp); 1128290001Sglebius lvp = UNIONFSVPTOLOWERVP(vp); 1129290001Sglebius 1130290001Sglebius /* check opaque */ 1131290001Sglebius if ((error = VOP_GETATTR(uvp, &va, cred)) != 0) 1132290001Sglebius return (error); 1133290001Sglebius if (va.va_flags & OPAQUE) 1134290001Sglebius return (0); 1135290001Sglebius 1136290001Sglebius /* open vnode */ 1137290001Sglebius#ifdef MAC 1138290001Sglebius if ((error = mac_vnode_check_open(cred, vp, VEXEC|VREAD)) != 0) 1139290001Sglebius return (error); 1140290001Sglebius#endif 1141290001Sglebius if ((error = VOP_ACCESS(vp, VEXEC|VREAD, cred, td)) != 0) 1142290001Sglebius return (error); 1143290001Sglebius if ((error = VOP_OPEN(vp, FREAD, cred, td, NULL)) != 0) 1144290001Sglebius return (error); 1145290001Sglebius 1146290001Sglebius uio.uio_rw = UIO_READ; 1147290001Sglebius uio.uio_segflg = UIO_SYSSPACE; 1148290001Sglebius uio.uio_td = td; 1149290001Sglebius uio.uio_offset = 0; 1150290001Sglebius 1151290001Sglebius#ifdef MAC 1152290001Sglebius error = mac_vnode_check_readdir(td->td_ucred, lvp); 1153290001Sglebius#endif 1154290001Sglebius while (!error && !eofflag) { 1155290001Sglebius iov.iov_base = buf; 1156290001Sglebius iov.iov_len = sizeof(buf); 1157290001Sglebius uio.uio_iov = &iov; 1158290001Sglebius uio.uio_iovcnt = 1; 1159290001Sglebius uio.uio_resid = iov.iov_len; 1160290001Sglebius 1161290001Sglebius error = VOP_READDIR(lvp, &uio, cred, &eofflag, NULL, NULL); 1162290001Sglebius if (error != 0) 1163290001Sglebius break; 1164290001Sglebius if (eofflag == 0 && uio.uio_resid == sizeof(buf)) { 1165290001Sglebius#ifdef DIAGNOSTIC 1166290001Sglebius panic("bad readdir response from lower FS."); 1167290001Sglebius#endif 1168290001Sglebius break; 1169290001Sglebius } 1170290001Sglebius 1171290001Sglebius edp = (struct dirent*)&buf[sizeof(buf) - uio.uio_resid]; 1172290001Sglebius for (dp = (struct dirent*)buf; !error && dp < edp; 1173290001Sglebius dp = (struct dirent*)((caddr_t)dp + dp->d_reclen)) { 1174290001Sglebius if (dp->d_type == DT_WHT || dp->d_fileno == 0 || 1175290001Sglebius (dp->d_namlen == 1 && dp->d_name[0] == '.') || 1176290001Sglebius (dp->d_namlen == 2 && !bcmp(dp->d_name, "..", 2))) 1177290001Sglebius continue; 1178290001Sglebius 1179290001Sglebius cn.cn_namelen = dp->d_namlen; 1180290001Sglebius cn.cn_pnbuf = NULL; 1181290001Sglebius cn.cn_nameptr = dp->d_name; 1182290001Sglebius cn.cn_nameiop = LOOKUP; 1183290001Sglebius cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN); 1184290001Sglebius cn.cn_lkflags = LK_EXCLUSIVE; 1185290001Sglebius cn.cn_thread = td; 1186290001Sglebius cn.cn_cred = cred; 1187290001Sglebius cn.cn_consume = 0; 1188290001Sglebius 1189290001Sglebius /* 1190290001Sglebius * check entry in lower. 1191290001Sglebius * Sometimes, readdir function returns 1192290001Sglebius * wrong entry. 1193290001Sglebius */ 1194290001Sglebius lookuperr = VOP_LOOKUP(lvp, &tvp, &cn); 1195290001Sglebius 1196290001Sglebius if (!lookuperr) 1197290001Sglebius vput(tvp); 1198290001Sglebius else 1199290001Sglebius continue; /* skip entry */ 1200290001Sglebius 1201290001Sglebius /* 1202290001Sglebius * check entry 1203290001Sglebius * If it has no exist/whiteout entry in upper, 1204290001Sglebius * directory is not empty. 1205290001Sglebius */ 1206290001Sglebius cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN); 1207290001Sglebius lookuperr = VOP_LOOKUP(uvp, &tvp, &cn); 1208290001Sglebius 1209290001Sglebius if (!lookuperr) 1210290001Sglebius vput(tvp); 1211290001Sglebius 1212290001Sglebius /* ignore exist or whiteout entry */ 1213290001Sglebius if (!lookuperr || 1214290001Sglebius (lookuperr == ENOENT && (cn.cn_flags & ISWHITEOUT))) 1215290001Sglebius continue; 1216290001Sglebius 1217290001Sglebius error = ENOTEMPTY; 1218290001Sglebius } 1219290001Sglebius } 1220290001Sglebius 1221290001Sglebius /* close vnode */ 1222290001Sglebius VOP_CLOSE(vp, FREAD, cred, td); 1223290001Sglebius 1224290001Sglebius return (error); 1225290001Sglebius} 1226290001Sglebius 1227290001Sglebius#ifdef DIAGNOSTIC 1228290001Sglebius 1229290001Sglebiusstruct vnode * 1230290001Sglebiusunionfs_checkuppervp(struct vnode *vp, char *fil, int lno) 1231290001Sglebius{ 1232290001Sglebius struct unionfs_node *unp; 1233290001Sglebius 1234290001Sglebius unp = VTOUNIONFS(vp); 1235290001Sglebius 1236290001Sglebius#ifdef notyet 1237290001Sglebius if (vp->v_op != unionfs_vnodeop_p) { 1238290001Sglebius printf("unionfs_checkuppervp: on non-unionfs-node.\n"); 1239290001Sglebius#ifdef KDB 1240290001Sglebius kdb_enter(KDB_WHY_UNIONFS, 1241290001Sglebius "unionfs_checkuppervp: on non-unionfs-node.\n"); 1242290001Sglebius#endif 1243290001Sglebius panic("unionfs_checkuppervp"); 1244290001Sglebius }; 1245290001Sglebius#endif 1246290001Sglebius return (unp->un_uppervp); 1247290001Sglebius} 1248290001Sglebius 1249290001Sglebiusstruct vnode * 1250290001Sglebiusunionfs_checklowervp(struct vnode *vp, char *fil, int lno) 1251290001Sglebius{ 1252290001Sglebius struct unionfs_node *unp; 1253290001Sglebius 1254290001Sglebius unp = VTOUNIONFS(vp); 1255290001Sglebius 1256290001Sglebius#ifdef notyet 1257290001Sglebius if (vp->v_op != unionfs_vnodeop_p) { 1258290001Sglebius printf("unionfs_checklowervp: on non-unionfs-node.\n"); 1259290001Sglebius#ifdef KDB 1260290001Sglebius kdb_enter(KDB_WHY_UNIONFS, 1261290001Sglebius "unionfs_checklowervp: on non-unionfs-node.\n"); 1262290001Sglebius#endif 1263290001Sglebius panic("unionfs_checklowervp"); 1264290001Sglebius }; 1265290001Sglebius#endif 1266290001Sglebius return (unp->un_lowervp); 1267290001Sglebius} 1268290001Sglebius#endif 1269290001Sglebius