1df8bae1dSRodney W. Grimes /* 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. 5df8bae1dSRodney W. Grimes * 6df8bae1dSRodney W. Grimes * This code is derived from software contributed to Berkeley by 7df8bae1dSRodney W. Grimes * Jan-Simon Pendry. 8df8bae1dSRodney W. Grimes * 9df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 10df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 11df8bae1dSRodney W. Grimes * are met: 12df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 13df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 14df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 15df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 16df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 17df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 18df8bae1dSRodney W. Grimes * must display the following acknowledgement: 19df8bae1dSRodney W. Grimes * This product includes software developed by the University of 20df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 21df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 22df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 23df8bae1dSRodney W. Grimes * without specific prior written permission. 24df8bae1dSRodney W. Grimes * 25df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35df8bae1dSRodney W. Grimes * SUCH DAMAGE. 36df8bae1dSRodney W. Grimes * 37996c772fSJohn Dyson * @(#)union_subr.c 8.20 (Berkeley) 5/20/95 381130b656SJordan K. Hubbard * $FreeBSD$ 39df8bae1dSRodney W. Grimes */ 40df8bae1dSRodney W. Grimes 41df8bae1dSRodney W. Grimes #include <sys/param.h> 42df8bae1dSRodney W. Grimes #include <sys/systm.h> 43df8bae1dSRodney W. Grimes #include <sys/time.h> 44df8bae1dSRodney W. Grimes #include <sys/kernel.h> 45df8bae1dSRodney W. Grimes #include <sys/vnode.h> 46df8bae1dSRodney W. Grimes #include <sys/namei.h> 47df8bae1dSRodney W. Grimes #include <sys/malloc.h> 48df8bae1dSRodney W. Grimes #include <sys/file.h> 49df8bae1dSRodney W. Grimes #include <sys/filedesc.h> 50df8bae1dSRodney W. Grimes #include <sys/queue.h> 51996c772fSJohn Dyson #include <sys/mount.h> 52996c772fSJohn Dyson #include <sys/stat.h> 53996c772fSJohn Dyson #include <vm/vm.h> /* for vnode_pager_setsize */ 54df8bae1dSRodney W. Grimes #include <miscfs/union/union.h> 55df8bae1dSRodney W. Grimes 56df8bae1dSRodney W. Grimes #include <sys/proc.h> 57df8bae1dSRodney W. Grimes 589b5e8b3aSBruce Evans extern int union_init __P((void)); 599b5e8b3aSBruce Evans 60df8bae1dSRodney W. Grimes /* must be power of two, otherwise change UNION_HASH() */ 61df8bae1dSRodney W. Grimes #define NHASH 32 62df8bae1dSRodney W. Grimes 63df8bae1dSRodney W. Grimes /* unsigned int ... */ 64df8bae1dSRodney W. Grimes #define UNION_HASH(u, l) \ 65df8bae1dSRodney W. Grimes (((((unsigned long) (u)) + ((unsigned long) l)) >> 8) & (NHASH-1)) 66df8bae1dSRodney W. Grimes 67df8bae1dSRodney W. Grimes static LIST_HEAD(unhead, union_node) unhead[NHASH]; 68df8bae1dSRodney W. Grimes static int unvplock[NHASH]; 69df8bae1dSRodney W. Grimes 709b5e8b3aSBruce Evans static int union_list_lock __P((int ix)); 719b5e8b3aSBruce Evans static void union_list_unlock __P((int ix)); 729b5e8b3aSBruce Evans extern void union_updatevp __P((struct union_node *un, 739b5e8b3aSBruce Evans struct vnode *uppervp, 749b5e8b3aSBruce Evans struct vnode *lowervp)); 759b5e8b3aSBruce Evans 76df8bae1dSRodney W. Grimes int 77df8bae1dSRodney W. Grimes union_init() 78df8bae1dSRodney W. Grimes { 79df8bae1dSRodney W. Grimes int i; 80df8bae1dSRodney W. Grimes 81df8bae1dSRodney W. Grimes for (i = 0; i < NHASH; i++) 82df8bae1dSRodney W. Grimes LIST_INIT(&unhead[i]); 83df8bae1dSRodney W. Grimes bzero((caddr_t) unvplock, sizeof(unvplock)); 8426f9a767SRodney W. Grimes return (0); 85df8bae1dSRodney W. Grimes } 86df8bae1dSRodney W. Grimes 87df8bae1dSRodney W. Grimes static int 88df8bae1dSRodney W. Grimes union_list_lock(ix) 89df8bae1dSRodney W. Grimes int ix; 90df8bae1dSRodney W. Grimes { 91df8bae1dSRodney W. Grimes 92df8bae1dSRodney W. Grimes if (unvplock[ix] & UN_LOCKED) { 93df8bae1dSRodney W. Grimes unvplock[ix] |= UN_WANT; 9482478919SDavid Greenman (void) tsleep((caddr_t) &unvplock[ix], PINOD, "unllck", 0); 95df8bae1dSRodney W. Grimes return (1); 96df8bae1dSRodney W. Grimes } 97df8bae1dSRodney W. Grimes 98df8bae1dSRodney W. Grimes unvplock[ix] |= UN_LOCKED; 99df8bae1dSRodney W. Grimes 100df8bae1dSRodney W. Grimes return (0); 101df8bae1dSRodney W. Grimes } 102df8bae1dSRodney W. Grimes 103df8bae1dSRodney W. Grimes static void 104df8bae1dSRodney W. Grimes union_list_unlock(ix) 105df8bae1dSRodney W. Grimes int ix; 106df8bae1dSRodney W. Grimes { 107df8bae1dSRodney W. Grimes 108df8bae1dSRodney W. Grimes unvplock[ix] &= ~UN_LOCKED; 109df8bae1dSRodney W. Grimes 110df8bae1dSRodney W. Grimes if (unvplock[ix] & UN_WANT) { 111df8bae1dSRodney W. Grimes unvplock[ix] &= ~UN_WANT; 112df8bae1dSRodney W. Grimes wakeup((caddr_t) &unvplock[ix]); 113df8bae1dSRodney W. Grimes } 114df8bae1dSRodney W. Grimes } 115df8bae1dSRodney W. Grimes 116df8bae1dSRodney W. Grimes void 117df8bae1dSRodney W. Grimes union_updatevp(un, uppervp, lowervp) 118df8bae1dSRodney W. Grimes struct union_node *un; 119df8bae1dSRodney W. Grimes struct vnode *uppervp; 120df8bae1dSRodney W. Grimes struct vnode *lowervp; 121df8bae1dSRodney W. Grimes { 122df8bae1dSRodney W. Grimes int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp); 123df8bae1dSRodney W. Grimes int nhash = UNION_HASH(uppervp, lowervp); 124996c772fSJohn Dyson int docache = (lowervp != NULLVP || uppervp != NULLVP); 125996c772fSJohn Dyson int lhash, hhash, uhash; 126df8bae1dSRodney W. Grimes 127df8bae1dSRodney W. Grimes /* 128df8bae1dSRodney W. Grimes * Ensure locking is ordered from lower to higher 129df8bae1dSRodney W. Grimes * to avoid deadlocks. 130df8bae1dSRodney W. Grimes */ 131df8bae1dSRodney W. Grimes if (nhash < ohash) { 132996c772fSJohn Dyson lhash = nhash; 133996c772fSJohn Dyson uhash = ohash; 134df8bae1dSRodney W. Grimes } else { 135996c772fSJohn Dyson lhash = ohash; 136996c772fSJohn Dyson uhash = nhash; 137df8bae1dSRodney W. Grimes } 138df8bae1dSRodney W. Grimes 139996c772fSJohn Dyson if (lhash != uhash) 140996c772fSJohn Dyson while (union_list_lock(lhash)) 141996c772fSJohn Dyson continue; 142996c772fSJohn Dyson 143996c772fSJohn Dyson while (union_list_lock(uhash)) 144996c772fSJohn Dyson continue; 145996c772fSJohn Dyson 146996c772fSJohn Dyson if (ohash != nhash || !docache) { 147996c772fSJohn Dyson if (un->un_flags & UN_CACHED) { 148996c772fSJohn Dyson un->un_flags &= ~UN_CACHED; 149996c772fSJohn Dyson LIST_REMOVE(un, un_cache); 150996c772fSJohn Dyson } 151996c772fSJohn Dyson } 152996c772fSJohn Dyson 153996c772fSJohn Dyson if (ohash != nhash) 154996c772fSJohn Dyson union_list_unlock(ohash); 155996c772fSJohn Dyson 156df8bae1dSRodney W. Grimes if (un->un_lowervp != lowervp) { 157df8bae1dSRodney W. Grimes if (un->un_lowervp) { 158df8bae1dSRodney W. Grimes vrele(un->un_lowervp); 159df8bae1dSRodney W. Grimes if (un->un_path) { 160df8bae1dSRodney W. Grimes free(un->un_path, M_TEMP); 161df8bae1dSRodney W. Grimes un->un_path = 0; 162df8bae1dSRodney W. Grimes } 163df8bae1dSRodney W. Grimes if (un->un_dirvp) { 164df8bae1dSRodney W. Grimes vrele(un->un_dirvp); 165df8bae1dSRodney W. Grimes un->un_dirvp = NULLVP; 166df8bae1dSRodney W. Grimes } 167df8bae1dSRodney W. Grimes } 168df8bae1dSRodney W. Grimes un->un_lowervp = lowervp; 169996c772fSJohn Dyson un->un_lowersz = VNOVAL; 170df8bae1dSRodney W. Grimes } 171df8bae1dSRodney W. Grimes 172df8bae1dSRodney W. Grimes if (un->un_uppervp != uppervp) { 173df8bae1dSRodney W. Grimes if (un->un_uppervp) 174df8bae1dSRodney W. Grimes vrele(un->un_uppervp); 175df8bae1dSRodney W. Grimes 176df8bae1dSRodney W. Grimes un->un_uppervp = uppervp; 177996c772fSJohn Dyson un->un_uppersz = VNOVAL; 178df8bae1dSRodney W. Grimes } 179df8bae1dSRodney W. Grimes 180996c772fSJohn Dyson if (docache && (ohash != nhash)) { 181df8bae1dSRodney W. Grimes LIST_INSERT_HEAD(&unhead[nhash], un, un_cache); 182996c772fSJohn Dyson un->un_flags |= UN_CACHED; 183996c772fSJohn Dyson } 184df8bae1dSRodney W. Grimes 185df8bae1dSRodney W. Grimes union_list_unlock(nhash); 186df8bae1dSRodney W. Grimes } 187df8bae1dSRodney W. Grimes 188df8bae1dSRodney W. Grimes void 189df8bae1dSRodney W. Grimes union_newlower(un, lowervp) 190df8bae1dSRodney W. Grimes struct union_node *un; 191df8bae1dSRodney W. Grimes struct vnode *lowervp; 192df8bae1dSRodney W. Grimes { 193df8bae1dSRodney W. Grimes 194df8bae1dSRodney W. Grimes union_updatevp(un, un->un_uppervp, lowervp); 195df8bae1dSRodney W. Grimes } 196df8bae1dSRodney W. Grimes 197df8bae1dSRodney W. Grimes void 198df8bae1dSRodney W. Grimes union_newupper(un, uppervp) 199df8bae1dSRodney W. Grimes struct union_node *un; 200df8bae1dSRodney W. Grimes struct vnode *uppervp; 201df8bae1dSRodney W. Grimes { 202df8bae1dSRodney W. Grimes 203df8bae1dSRodney W. Grimes union_updatevp(un, uppervp, un->un_lowervp); 204df8bae1dSRodney W. Grimes } 205df8bae1dSRodney W. Grimes 206df8bae1dSRodney W. Grimes /* 207996c772fSJohn Dyson * Keep track of size changes in the underlying vnodes. 208996c772fSJohn Dyson * If the size changes, then callback to the vm layer 209996c772fSJohn Dyson * giving priority to the upper layer size. 210996c772fSJohn Dyson */ 211996c772fSJohn Dyson void 212996c772fSJohn Dyson union_newsize(vp, uppersz, lowersz) 213996c772fSJohn Dyson struct vnode *vp; 214996c772fSJohn Dyson off_t uppersz, lowersz; 215996c772fSJohn Dyson { 216996c772fSJohn Dyson struct union_node *un; 217996c772fSJohn Dyson off_t sz; 218996c772fSJohn Dyson 219996c772fSJohn Dyson /* only interested in regular files */ 220996c772fSJohn Dyson if (vp->v_type != VREG) 221996c772fSJohn Dyson return; 222996c772fSJohn Dyson 223996c772fSJohn Dyson un = VTOUNION(vp); 224996c772fSJohn Dyson sz = VNOVAL; 225996c772fSJohn Dyson 226996c772fSJohn Dyson if ((uppersz != VNOVAL) && (un->un_uppersz != uppersz)) { 227996c772fSJohn Dyson un->un_uppersz = uppersz; 228996c772fSJohn Dyson if (sz == VNOVAL) 229996c772fSJohn Dyson sz = un->un_uppersz; 230996c772fSJohn Dyson } 231996c772fSJohn Dyson 232996c772fSJohn Dyson if ((lowersz != VNOVAL) && (un->un_lowersz != lowersz)) { 233996c772fSJohn Dyson un->un_lowersz = lowersz; 234996c772fSJohn Dyson if (sz == VNOVAL) 235996c772fSJohn Dyson sz = un->un_lowersz; 236996c772fSJohn Dyson } 237996c772fSJohn Dyson 238996c772fSJohn Dyson if (sz != VNOVAL) { 239996c772fSJohn Dyson #ifdef UNION_DIAGNOSTIC 240996c772fSJohn Dyson printf("union: %s size now %ld\n", 241996c772fSJohn Dyson uppersz != VNOVAL ? "upper" : "lower", (long) sz); 242996c772fSJohn Dyson #endif 243996c772fSJohn Dyson vnode_pager_setsize(vp, sz); 244996c772fSJohn Dyson } 245996c772fSJohn Dyson } 246996c772fSJohn Dyson 247996c772fSJohn Dyson /* 248df8bae1dSRodney W. Grimes * allocate a union_node/vnode pair. the vnode is 249df8bae1dSRodney W. Grimes * referenced and locked. the new vnode is returned 250df8bae1dSRodney W. Grimes * via (vpp). (mp) is the mountpoint of the union filesystem, 251df8bae1dSRodney W. Grimes * (dvp) is the parent directory where the upper layer object 252df8bae1dSRodney W. Grimes * should exist (but doesn't) and (cnp) is the componentname 253df8bae1dSRodney W. Grimes * information which is partially copied to allow the upper 254df8bae1dSRodney W. Grimes * layer object to be created at a later time. (uppervp) 255df8bae1dSRodney W. Grimes * and (lowervp) reference the upper and lower layer objects 256df8bae1dSRodney W. Grimes * being mapped. either, but not both, can be nil. 257df8bae1dSRodney W. Grimes * if supplied, (uppervp) is locked. 258df8bae1dSRodney W. Grimes * the reference is either maintained in the new union_node 259df8bae1dSRodney W. Grimes * object which is allocated, or they are vrele'd. 260df8bae1dSRodney W. Grimes * 261df8bae1dSRodney W. Grimes * all union_nodes are maintained on a singly-linked 262df8bae1dSRodney W. Grimes * list. new nodes are only allocated when they cannot 263df8bae1dSRodney W. Grimes * be found on this list. entries on the list are 264df8bae1dSRodney W. Grimes * removed when the vfs reclaim entry is called. 265df8bae1dSRodney W. Grimes * 266df8bae1dSRodney W. Grimes * a single lock is kept for the entire list. this is 267df8bae1dSRodney W. Grimes * needed because the getnewvnode() function can block 268df8bae1dSRodney W. Grimes * waiting for a vnode to become free, in which case there 269df8bae1dSRodney W. Grimes * may be more than one process trying to get the same 270df8bae1dSRodney W. Grimes * vnode. this lock is only taken if we are going to 271df8bae1dSRodney W. Grimes * call getnewvnode, since the kernel itself is single-threaded. 272df8bae1dSRodney W. Grimes * 273df8bae1dSRodney W. Grimes * if an entry is found on the list, then call vget() to 274df8bae1dSRodney W. Grimes * take a reference. this is done because there may be 275df8bae1dSRodney W. Grimes * zero references to it and so it needs to removed from 276df8bae1dSRodney W. Grimes * the vnode free list. 277df8bae1dSRodney W. Grimes */ 278df8bae1dSRodney W. Grimes int 279996c772fSJohn Dyson union_allocvp(vpp, mp, undvp, dvp, cnp, uppervp, lowervp, docache) 280df8bae1dSRodney W. Grimes struct vnode **vpp; 281df8bae1dSRodney W. Grimes struct mount *mp; 282996c772fSJohn Dyson struct vnode *undvp; /* parent union vnode */ 283df8bae1dSRodney W. Grimes struct vnode *dvp; /* may be null */ 284df8bae1dSRodney W. Grimes struct componentname *cnp; /* may be null */ 285df8bae1dSRodney W. Grimes struct vnode *uppervp; /* may be null */ 286df8bae1dSRodney W. Grimes struct vnode *lowervp; /* may be null */ 287996c772fSJohn Dyson int docache; 288df8bae1dSRodney W. Grimes { 289df8bae1dSRodney W. Grimes int error; 29026f9a767SRodney W. Grimes struct union_node *un = 0; 291df8bae1dSRodney W. Grimes struct vnode *xlowervp = NULLVP; 292996c772fSJohn Dyson struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 293996c772fSJohn Dyson int hash; 294996c772fSJohn Dyson int vflag; 295df8bae1dSRodney W. Grimes int try; 296df8bae1dSRodney W. Grimes 297df8bae1dSRodney W. Grimes if (uppervp == NULLVP && lowervp == NULLVP) 298df8bae1dSRodney W. Grimes panic("union: unidentifiable allocation"); 299df8bae1dSRodney W. Grimes 300df8bae1dSRodney W. Grimes if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) { 301df8bae1dSRodney W. Grimes xlowervp = lowervp; 302df8bae1dSRodney W. Grimes lowervp = NULLVP; 303df8bae1dSRodney W. Grimes } 304df8bae1dSRodney W. Grimes 305996c772fSJohn Dyson /* detect the root vnode (and aliases) */ 306996c772fSJohn Dyson vflag = 0; 307996c772fSJohn Dyson if ((uppervp == um->um_uppervp) && 308996c772fSJohn Dyson ((lowervp == NULLVP) || lowervp == um->um_lowervp)) { 309996c772fSJohn Dyson if (lowervp == NULLVP) { 310996c772fSJohn Dyson lowervp = um->um_lowervp; 311996c772fSJohn Dyson if (lowervp != NULLVP) 312996c772fSJohn Dyson VREF(lowervp); 313996c772fSJohn Dyson } 314996c772fSJohn Dyson vflag = VROOT; 315996c772fSJohn Dyson } 316996c772fSJohn Dyson 317df8bae1dSRodney W. Grimes loop: 318996c772fSJohn Dyson if (!docache) { 319996c772fSJohn Dyson un = 0; 320996c772fSJohn Dyson } else for (try = 0; try < 3; try++) { 321df8bae1dSRodney W. Grimes switch (try) { 322df8bae1dSRodney W. Grimes case 0: 323df8bae1dSRodney W. Grimes if (lowervp == NULLVP) 324df8bae1dSRodney W. Grimes continue; 325df8bae1dSRodney W. Grimes hash = UNION_HASH(uppervp, lowervp); 326df8bae1dSRodney W. Grimes break; 327df8bae1dSRodney W. Grimes 328df8bae1dSRodney W. Grimes case 1: 329df8bae1dSRodney W. Grimes if (uppervp == NULLVP) 330df8bae1dSRodney W. Grimes continue; 331df8bae1dSRodney W. Grimes hash = UNION_HASH(uppervp, NULLVP); 332df8bae1dSRodney W. Grimes break; 333df8bae1dSRodney W. Grimes 334df8bae1dSRodney W. Grimes case 2: 335df8bae1dSRodney W. Grimes if (lowervp == NULLVP) 336df8bae1dSRodney W. Grimes continue; 337df8bae1dSRodney W. Grimes hash = UNION_HASH(NULLVP, lowervp); 338df8bae1dSRodney W. Grimes break; 339df8bae1dSRodney W. Grimes } 340df8bae1dSRodney W. Grimes 341df8bae1dSRodney W. Grimes while (union_list_lock(hash)) 342df8bae1dSRodney W. Grimes continue; 343df8bae1dSRodney W. Grimes 344df8bae1dSRodney W. Grimes for (un = unhead[hash].lh_first; un != 0; 345df8bae1dSRodney W. Grimes un = un->un_cache.le_next) { 346df8bae1dSRodney W. Grimes if ((un->un_lowervp == lowervp || 347df8bae1dSRodney W. Grimes un->un_lowervp == NULLVP) && 348df8bae1dSRodney W. Grimes (un->un_uppervp == uppervp || 349df8bae1dSRodney W. Grimes un->un_uppervp == NULLVP) && 350df8bae1dSRodney W. Grimes (UNIONTOV(un)->v_mount == mp)) { 351996c772fSJohn Dyson if (vget(UNIONTOV(un), 0, 352996c772fSJohn Dyson cnp ? cnp->cn_proc : NULL)) { 353df8bae1dSRodney W. Grimes union_list_unlock(hash); 354df8bae1dSRodney W. Grimes goto loop; 355df8bae1dSRodney W. Grimes } 356df8bae1dSRodney W. Grimes break; 357df8bae1dSRodney W. Grimes } 358df8bae1dSRodney W. Grimes } 359df8bae1dSRodney W. Grimes 360df8bae1dSRodney W. Grimes union_list_unlock(hash); 361df8bae1dSRodney W. Grimes 362df8bae1dSRodney W. Grimes if (un) 363df8bae1dSRodney W. Grimes break; 364df8bae1dSRodney W. Grimes } 365df8bae1dSRodney W. Grimes 366df8bae1dSRodney W. Grimes if (un) { 367df8bae1dSRodney W. Grimes /* 368df8bae1dSRodney W. Grimes * Obtain a lock on the union_node. 369df8bae1dSRodney W. Grimes * uppervp is locked, though un->un_uppervp 370df8bae1dSRodney W. Grimes * may not be. this doesn't break the locking 371df8bae1dSRodney W. Grimes * hierarchy since in the case that un->un_uppervp 372df8bae1dSRodney W. Grimes * is not yet locked it will be vrele'd and replaced 373df8bae1dSRodney W. Grimes * with uppervp. 374df8bae1dSRodney W. Grimes */ 375df8bae1dSRodney W. Grimes 376df8bae1dSRodney W. Grimes if ((dvp != NULLVP) && (uppervp == dvp)) { 377df8bae1dSRodney W. Grimes /* 378df8bae1dSRodney W. Grimes * Access ``.'', so (un) will already 379df8bae1dSRodney W. Grimes * be locked. Since this process has 380df8bae1dSRodney W. Grimes * the lock on (uppervp) no other 381df8bae1dSRodney W. Grimes * process can hold the lock on (un). 382df8bae1dSRodney W. Grimes */ 383df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 384df8bae1dSRodney W. Grimes if ((un->un_flags & UN_LOCKED) == 0) 385df8bae1dSRodney W. Grimes panic("union: . not locked"); 386df8bae1dSRodney W. Grimes else if (curproc && un->un_pid != curproc->p_pid && 387df8bae1dSRodney W. Grimes un->un_pid > -1 && curproc->p_pid > -1) 388df8bae1dSRodney W. Grimes panic("union: allocvp not lock owner"); 389df8bae1dSRodney W. Grimes #endif 390df8bae1dSRodney W. Grimes } else { 391df8bae1dSRodney W. Grimes if (un->un_flags & UN_LOCKED) { 392df8bae1dSRodney W. Grimes vrele(UNIONTOV(un)); 393df8bae1dSRodney W. Grimes un->un_flags |= UN_WANT; 39482478919SDavid Greenman (void) tsleep((caddr_t) &un->un_flags, PINOD, "unalvp", 0); 395df8bae1dSRodney W. Grimes goto loop; 396df8bae1dSRodney W. Grimes } 397df8bae1dSRodney W. Grimes un->un_flags |= UN_LOCKED; 398df8bae1dSRodney W. Grimes 399df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 400df8bae1dSRodney W. Grimes if (curproc) 401df8bae1dSRodney W. Grimes un->un_pid = curproc->p_pid; 402df8bae1dSRodney W. Grimes else 403df8bae1dSRodney W. Grimes un->un_pid = -1; 404df8bae1dSRodney W. Grimes #endif 405df8bae1dSRodney W. Grimes } 406df8bae1dSRodney W. Grimes 407df8bae1dSRodney W. Grimes /* 408df8bae1dSRodney W. Grimes * At this point, the union_node is locked, 409df8bae1dSRodney W. Grimes * un->un_uppervp may not be locked, and uppervp 410df8bae1dSRodney W. Grimes * is locked or nil. 411df8bae1dSRodney W. Grimes */ 412df8bae1dSRodney W. Grimes 413df8bae1dSRodney W. Grimes /* 414df8bae1dSRodney W. Grimes * Save information about the upper layer. 415df8bae1dSRodney W. Grimes */ 416df8bae1dSRodney W. Grimes if (uppervp != un->un_uppervp) { 417df8bae1dSRodney W. Grimes union_newupper(un, uppervp); 418df8bae1dSRodney W. Grimes } else if (uppervp) { 419df8bae1dSRodney W. Grimes vrele(uppervp); 420df8bae1dSRodney W. Grimes } 421df8bae1dSRodney W. Grimes 422df8bae1dSRodney W. Grimes if (un->un_uppervp) { 423df8bae1dSRodney W. Grimes un->un_flags |= UN_ULOCK; 424df8bae1dSRodney W. Grimes un->un_flags &= ~UN_KLOCK; 425df8bae1dSRodney W. Grimes } 426df8bae1dSRodney W. Grimes 427df8bae1dSRodney W. Grimes /* 428df8bae1dSRodney W. Grimes * Save information about the lower layer. 429df8bae1dSRodney W. Grimes * This needs to keep track of pathname 430df8bae1dSRodney W. Grimes * and directory information which union_vn_create 431df8bae1dSRodney W. Grimes * might need. 432df8bae1dSRodney W. Grimes */ 433df8bae1dSRodney W. Grimes if (lowervp != un->un_lowervp) { 434df8bae1dSRodney W. Grimes union_newlower(un, lowervp); 435996c772fSJohn Dyson if (cnp && (lowervp != NULLVP)) { 436df8bae1dSRodney W. Grimes un->un_hash = cnp->cn_hash; 437df8bae1dSRodney W. Grimes un->un_path = malloc(cnp->cn_namelen+1, 438df8bae1dSRodney W. Grimes M_TEMP, M_WAITOK); 439df8bae1dSRodney W. Grimes bcopy(cnp->cn_nameptr, un->un_path, 440df8bae1dSRodney W. Grimes cnp->cn_namelen); 441df8bae1dSRodney W. Grimes un->un_path[cnp->cn_namelen] = '\0'; 442df8bae1dSRodney W. Grimes VREF(dvp); 443df8bae1dSRodney W. Grimes un->un_dirvp = dvp; 444df8bae1dSRodney W. Grimes } 445df8bae1dSRodney W. Grimes } else if (lowervp) { 446df8bae1dSRodney W. Grimes vrele(lowervp); 447df8bae1dSRodney W. Grimes } 448df8bae1dSRodney W. Grimes *vpp = UNIONTOV(un); 449df8bae1dSRodney W. Grimes return (0); 450df8bae1dSRodney W. Grimes } 451df8bae1dSRodney W. Grimes 452996c772fSJohn Dyson if (docache) { 453df8bae1dSRodney W. Grimes /* 454df8bae1dSRodney W. Grimes * otherwise lock the vp list while we call getnewvnode 455df8bae1dSRodney W. Grimes * since that can block. 456df8bae1dSRodney W. Grimes */ 457df8bae1dSRodney W. Grimes hash = UNION_HASH(uppervp, lowervp); 458df8bae1dSRodney W. Grimes 459df8bae1dSRodney W. Grimes if (union_list_lock(hash)) 460df8bae1dSRodney W. Grimes goto loop; 461996c772fSJohn Dyson } 462df8bae1dSRodney W. Grimes 463df8bae1dSRodney W. Grimes error = getnewvnode(VT_UNION, mp, union_vnodeop_p, vpp); 464df8bae1dSRodney W. Grimes if (error) { 465df8bae1dSRodney W. Grimes if (uppervp) { 466df8bae1dSRodney W. Grimes if (dvp == uppervp) 467df8bae1dSRodney W. Grimes vrele(uppervp); 468df8bae1dSRodney W. Grimes else 469df8bae1dSRodney W. Grimes vput(uppervp); 470df8bae1dSRodney W. Grimes } 471df8bae1dSRodney W. Grimes if (lowervp) 472df8bae1dSRodney W. Grimes vrele(lowervp); 473df8bae1dSRodney W. Grimes 474df8bae1dSRodney W. Grimes goto out; 475df8bae1dSRodney W. Grimes } 476df8bae1dSRodney W. Grimes 477df8bae1dSRodney W. Grimes MALLOC((*vpp)->v_data, void *, sizeof(struct union_node), 478df8bae1dSRodney W. Grimes M_TEMP, M_WAITOK); 479df8bae1dSRodney W. Grimes 480996c772fSJohn Dyson (*vpp)->v_flag |= vflag; 481df8bae1dSRodney W. Grimes if (uppervp) 482df8bae1dSRodney W. Grimes (*vpp)->v_type = uppervp->v_type; 483df8bae1dSRodney W. Grimes else 484df8bae1dSRodney W. Grimes (*vpp)->v_type = lowervp->v_type; 485df8bae1dSRodney W. Grimes un = VTOUNION(*vpp); 486df8bae1dSRodney W. Grimes un->un_vnode = *vpp; 487df8bae1dSRodney W. Grimes un->un_uppervp = uppervp; 488996c772fSJohn Dyson un->un_uppersz = VNOVAL; 489df8bae1dSRodney W. Grimes un->un_lowervp = lowervp; 490996c772fSJohn Dyson un->un_lowersz = VNOVAL; 491996c772fSJohn Dyson un->un_pvp = undvp; 492996c772fSJohn Dyson if (undvp != NULLVP) 493996c772fSJohn Dyson VREF(undvp); 494996c772fSJohn Dyson un->un_dircache = 0; 495df8bae1dSRodney W. Grimes un->un_openl = 0; 496df8bae1dSRodney W. Grimes un->un_flags = UN_LOCKED; 497df8bae1dSRodney W. Grimes if (un->un_uppervp) 498df8bae1dSRodney W. Grimes un->un_flags |= UN_ULOCK; 499df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 500df8bae1dSRodney W. Grimes if (curproc) 501df8bae1dSRodney W. Grimes un->un_pid = curproc->p_pid; 502df8bae1dSRodney W. Grimes else 503df8bae1dSRodney W. Grimes un->un_pid = -1; 504df8bae1dSRodney W. Grimes #endif 505996c772fSJohn Dyson if (cnp && (lowervp != NULLVP)) { 506df8bae1dSRodney W. Grimes un->un_hash = cnp->cn_hash; 507df8bae1dSRodney W. Grimes un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK); 508df8bae1dSRodney W. Grimes bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen); 509df8bae1dSRodney W. Grimes un->un_path[cnp->cn_namelen] = '\0'; 510df8bae1dSRodney W. Grimes VREF(dvp); 511df8bae1dSRodney W. Grimes un->un_dirvp = dvp; 512df8bae1dSRodney W. Grimes } else { 513df8bae1dSRodney W. Grimes un->un_hash = 0; 514df8bae1dSRodney W. Grimes un->un_path = 0; 515df8bae1dSRodney W. Grimes un->un_dirvp = 0; 516df8bae1dSRodney W. Grimes } 517df8bae1dSRodney W. Grimes 518996c772fSJohn Dyson if (docache) { 519df8bae1dSRodney W. Grimes LIST_INSERT_HEAD(&unhead[hash], un, un_cache); 520996c772fSJohn Dyson un->un_flags |= UN_CACHED; 521996c772fSJohn Dyson } 522df8bae1dSRodney W. Grimes 523df8bae1dSRodney W. Grimes if (xlowervp) 524df8bae1dSRodney W. Grimes vrele(xlowervp); 525df8bae1dSRodney W. Grimes 526df8bae1dSRodney W. Grimes out: 527996c772fSJohn Dyson if (docache) 528df8bae1dSRodney W. Grimes union_list_unlock(hash); 529df8bae1dSRodney W. Grimes 530df8bae1dSRodney W. Grimes return (error); 531df8bae1dSRodney W. Grimes } 532df8bae1dSRodney W. Grimes 533df8bae1dSRodney W. Grimes int 534df8bae1dSRodney W. Grimes union_freevp(vp) 535df8bae1dSRodney W. Grimes struct vnode *vp; 536df8bae1dSRodney W. Grimes { 537df8bae1dSRodney W. Grimes struct union_node *un = VTOUNION(vp); 538df8bae1dSRodney W. Grimes 539996c772fSJohn Dyson if (un->un_flags & UN_CACHED) { 540996c772fSJohn Dyson un->un_flags &= ~UN_CACHED; 541df8bae1dSRodney W. Grimes LIST_REMOVE(un, un_cache); 542996c772fSJohn Dyson } 543df8bae1dSRodney W. Grimes 544996c772fSJohn Dyson if (un->un_pvp != NULLVP) 545996c772fSJohn Dyson vrele(un->un_pvp); 546996c772fSJohn Dyson if (un->un_uppervp != NULLVP) 547df8bae1dSRodney W. Grimes vrele(un->un_uppervp); 548996c772fSJohn Dyson if (un->un_lowervp != NULLVP) 549df8bae1dSRodney W. Grimes vrele(un->un_lowervp); 550996c772fSJohn Dyson if (un->un_dirvp != NULLVP) 551df8bae1dSRodney W. Grimes vrele(un->un_dirvp); 552df8bae1dSRodney W. Grimes if (un->un_path) 553df8bae1dSRodney W. Grimes free(un->un_path, M_TEMP); 554df8bae1dSRodney W. Grimes 555df8bae1dSRodney W. Grimes FREE(vp->v_data, M_TEMP); 556df8bae1dSRodney W. Grimes vp->v_data = 0; 557df8bae1dSRodney W. Grimes 558df8bae1dSRodney W. Grimes return (0); 559df8bae1dSRodney W. Grimes } 560df8bae1dSRodney W. Grimes 561df8bae1dSRodney W. Grimes /* 562df8bae1dSRodney W. Grimes * copyfile. copy the vnode (fvp) to the vnode (tvp) 563df8bae1dSRodney W. Grimes * using a sequence of reads and writes. both (fvp) 564df8bae1dSRodney W. Grimes * and (tvp) are locked on entry and exit. 565df8bae1dSRodney W. Grimes */ 566df8bae1dSRodney W. Grimes int 567996c772fSJohn Dyson union_copyfile(fvp, tvp, cred, p) 568df8bae1dSRodney W. Grimes struct vnode *fvp; 569df8bae1dSRodney W. Grimes struct vnode *tvp; 570996c772fSJohn Dyson struct ucred *cred; 571996c772fSJohn Dyson struct proc *p; 572df8bae1dSRodney W. Grimes { 573df8bae1dSRodney W. Grimes char *buf; 574df8bae1dSRodney W. Grimes struct uio uio; 575df8bae1dSRodney W. Grimes struct iovec iov; 576df8bae1dSRodney W. Grimes int error = 0; 577df8bae1dSRodney W. Grimes 578df8bae1dSRodney W. Grimes /* 579df8bae1dSRodney W. Grimes * strategy: 580df8bae1dSRodney W. Grimes * allocate a buffer of size MAXBSIZE. 581df8bae1dSRodney W. Grimes * loop doing reads and writes, keeping track 582df8bae1dSRodney W. Grimes * of the current uio offset. 583df8bae1dSRodney W. Grimes * give up at the first sign of trouble. 584df8bae1dSRodney W. Grimes */ 585df8bae1dSRodney W. Grimes 586df8bae1dSRodney W. Grimes uio.uio_procp = p; 587df8bae1dSRodney W. Grimes uio.uio_segflg = UIO_SYSSPACE; 588df8bae1dSRodney W. Grimes uio.uio_offset = 0; 589df8bae1dSRodney W. Grimes 590996c772fSJohn Dyson VOP_UNLOCK(fvp, 0, p); /* XXX */ 591996c772fSJohn Dyson VOP_LEASE(fvp, p, cred, LEASE_READ); 592996c772fSJohn Dyson vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */ 593996c772fSJohn Dyson VOP_UNLOCK(tvp, 0, p); /* XXX */ 594996c772fSJohn Dyson VOP_LEASE(tvp, p, cred, LEASE_WRITE); 595996c772fSJohn Dyson vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */ 596df8bae1dSRodney W. Grimes 597df8bae1dSRodney W. Grimes buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK); 598df8bae1dSRodney W. Grimes 599df8bae1dSRodney W. Grimes /* ugly loop follows... */ 600df8bae1dSRodney W. Grimes do { 601df8bae1dSRodney W. Grimes off_t offset = uio.uio_offset; 602df8bae1dSRodney W. Grimes 603df8bae1dSRodney W. Grimes uio.uio_iov = &iov; 604df8bae1dSRodney W. Grimes uio.uio_iovcnt = 1; 605df8bae1dSRodney W. Grimes iov.iov_base = buf; 606df8bae1dSRodney W. Grimes iov.iov_len = MAXBSIZE; 607df8bae1dSRodney W. Grimes uio.uio_resid = iov.iov_len; 608df8bae1dSRodney W. Grimes uio.uio_rw = UIO_READ; 609df8bae1dSRodney W. Grimes error = VOP_READ(fvp, &uio, 0, cred); 610df8bae1dSRodney W. Grimes 611df8bae1dSRodney W. Grimes if (error == 0) { 612df8bae1dSRodney W. Grimes uio.uio_iov = &iov; 613df8bae1dSRodney W. Grimes uio.uio_iovcnt = 1; 614df8bae1dSRodney W. Grimes iov.iov_base = buf; 615df8bae1dSRodney W. Grimes iov.iov_len = MAXBSIZE - uio.uio_resid; 616df8bae1dSRodney W. Grimes uio.uio_offset = offset; 617df8bae1dSRodney W. Grimes uio.uio_rw = UIO_WRITE; 618df8bae1dSRodney W. Grimes uio.uio_resid = iov.iov_len; 619df8bae1dSRodney W. Grimes 620df8bae1dSRodney W. Grimes if (uio.uio_resid == 0) 621df8bae1dSRodney W. Grimes break; 622df8bae1dSRodney W. Grimes 623df8bae1dSRodney W. Grimes do { 624df8bae1dSRodney W. Grimes error = VOP_WRITE(tvp, &uio, 0, cred); 625df8bae1dSRodney W. Grimes } while ((uio.uio_resid > 0) && (error == 0)); 626df8bae1dSRodney W. Grimes } 627df8bae1dSRodney W. Grimes 628df8bae1dSRodney W. Grimes } while (error == 0); 629df8bae1dSRodney W. Grimes 630df8bae1dSRodney W. Grimes free(buf, M_TEMP); 631df8bae1dSRodney W. Grimes return (error); 632df8bae1dSRodney W. Grimes } 633df8bae1dSRodney W. Grimes 634df8bae1dSRodney W. Grimes /* 635996c772fSJohn Dyson * (un) is assumed to be locked on entry and remains 636996c772fSJohn Dyson * locked on exit. 637996c772fSJohn Dyson */ 638996c772fSJohn Dyson int 639996c772fSJohn Dyson union_copyup(un, docopy, cred, p) 640996c772fSJohn Dyson struct union_node *un; 641996c772fSJohn Dyson int docopy; 642996c772fSJohn Dyson struct ucred *cred; 643996c772fSJohn Dyson struct proc *p; 644996c772fSJohn Dyson { 645996c772fSJohn Dyson int error; 646996c772fSJohn Dyson struct vnode *lvp, *uvp; 647996c772fSJohn Dyson 648996c772fSJohn Dyson error = union_vn_create(&uvp, un, p); 649996c772fSJohn Dyson if (error) 650996c772fSJohn Dyson return (error); 651996c772fSJohn Dyson 652996c772fSJohn Dyson /* at this point, uppervp is locked */ 653996c772fSJohn Dyson union_newupper(un, uvp); 654996c772fSJohn Dyson un->un_flags |= UN_ULOCK; 655996c772fSJohn Dyson 656996c772fSJohn Dyson lvp = un->un_lowervp; 657996c772fSJohn Dyson 658996c772fSJohn Dyson if (docopy) { 659996c772fSJohn Dyson /* 660996c772fSJohn Dyson * XX - should not ignore errors 661996c772fSJohn Dyson * from VOP_CLOSE 662996c772fSJohn Dyson */ 663996c772fSJohn Dyson vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p); 664996c772fSJohn Dyson error = VOP_OPEN(lvp, FREAD, cred, p); 665996c772fSJohn Dyson if (error == 0) { 666996c772fSJohn Dyson error = union_copyfile(lvp, uvp, cred, p); 667996c772fSJohn Dyson VOP_UNLOCK(lvp, 0, p); 668996c772fSJohn Dyson (void) VOP_CLOSE(lvp, FREAD, cred, p); 669996c772fSJohn Dyson } 670996c772fSJohn Dyson #ifdef UNION_DIAGNOSTIC 671996c772fSJohn Dyson if (error == 0) 672996c772fSJohn Dyson uprintf("union: copied up %s\n", un->un_path); 673996c772fSJohn Dyson #endif 674996c772fSJohn Dyson 675996c772fSJohn Dyson } 676996c772fSJohn Dyson un->un_flags &= ~UN_ULOCK; 677996c772fSJohn Dyson VOP_UNLOCK(uvp, 0, p); 678996c772fSJohn Dyson union_vn_close(uvp, FWRITE, cred, p); 679996c772fSJohn Dyson vn_lock(uvp, LK_EXCLUSIVE | LK_RETRY, p); 680996c772fSJohn Dyson un->un_flags |= UN_ULOCK; 681996c772fSJohn Dyson 682996c772fSJohn Dyson /* 683996c772fSJohn Dyson * Subsequent IOs will go to the top layer, so 684996c772fSJohn Dyson * call close on the lower vnode and open on the 685996c772fSJohn Dyson * upper vnode to ensure that the filesystem keeps 686996c772fSJohn Dyson * its references counts right. This doesn't do 687996c772fSJohn Dyson * the right thing with (cred) and (FREAD) though. 688996c772fSJohn Dyson * Ignoring error returns is not right, either. 689996c772fSJohn Dyson */ 690996c772fSJohn Dyson if (error == 0) { 691996c772fSJohn Dyson int i; 692996c772fSJohn Dyson 693996c772fSJohn Dyson for (i = 0; i < un->un_openl; i++) { 694996c772fSJohn Dyson (void) VOP_CLOSE(lvp, FREAD, cred, p); 695996c772fSJohn Dyson (void) VOP_OPEN(uvp, FREAD, cred, p); 696996c772fSJohn Dyson } 697996c772fSJohn Dyson un->un_openl = 0; 698996c772fSJohn Dyson } 699996c772fSJohn Dyson 700996c772fSJohn Dyson return (error); 701996c772fSJohn Dyson 702996c772fSJohn Dyson } 703996c772fSJohn Dyson 704996c772fSJohn Dyson static int 705996c772fSJohn Dyson union_relookup(um, dvp, vpp, cnp, cn, path, pathlen) 706996c772fSJohn Dyson struct union_mount *um; 707996c772fSJohn Dyson struct vnode *dvp; 708996c772fSJohn Dyson struct vnode **vpp; 709996c772fSJohn Dyson struct componentname *cnp; 710996c772fSJohn Dyson struct componentname *cn; 711996c772fSJohn Dyson char *path; 712996c772fSJohn Dyson int pathlen; 713996c772fSJohn Dyson { 714996c772fSJohn Dyson int error; 715996c772fSJohn Dyson 716996c772fSJohn Dyson /* 717996c772fSJohn Dyson * A new componentname structure must be faked up because 718996c772fSJohn Dyson * there is no way to know where the upper level cnp came 719996c772fSJohn Dyson * from or what it is being used for. This must duplicate 720996c772fSJohn Dyson * some of the work done by NDINIT, some of the work done 721996c772fSJohn Dyson * by namei, some of the work done by lookup and some of 722996c772fSJohn Dyson * the work done by VOP_LOOKUP when given a CREATE flag. 723996c772fSJohn Dyson * Conclusion: Horrible. 724996c772fSJohn Dyson * 725996c772fSJohn Dyson * The pathname buffer will be FREEed by VOP_MKDIR. 726996c772fSJohn Dyson */ 727996c772fSJohn Dyson cn->cn_namelen = pathlen; 728996c772fSJohn Dyson cn->cn_pnbuf = malloc(cn->cn_namelen+1, M_NAMEI, M_WAITOK); 729996c772fSJohn Dyson bcopy(path, cn->cn_pnbuf, cn->cn_namelen); 730996c772fSJohn Dyson cn->cn_pnbuf[cn->cn_namelen] = '\0'; 731996c772fSJohn Dyson 732996c772fSJohn Dyson cn->cn_nameiop = CREATE; 733996c772fSJohn Dyson cn->cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN); 734996c772fSJohn Dyson cn->cn_proc = cnp->cn_proc; 735996c772fSJohn Dyson if (um->um_op == UNMNT_ABOVE) 736996c772fSJohn Dyson cn->cn_cred = cnp->cn_cred; 737996c772fSJohn Dyson else 738996c772fSJohn Dyson cn->cn_cred = um->um_cred; 739996c772fSJohn Dyson cn->cn_nameptr = cn->cn_pnbuf; 740996c772fSJohn Dyson cn->cn_hash = cnp->cn_hash; 741996c772fSJohn Dyson cn->cn_consume = cnp->cn_consume; 742996c772fSJohn Dyson 743996c772fSJohn Dyson VREF(dvp); 744996c772fSJohn Dyson error = relookup(dvp, vpp, cn); 745996c772fSJohn Dyson if (!error) 746996c772fSJohn Dyson vrele(dvp); 747996c772fSJohn Dyson 748996c772fSJohn Dyson return (error); 749996c772fSJohn Dyson } 750996c772fSJohn Dyson 751996c772fSJohn Dyson /* 752df8bae1dSRodney W. Grimes * Create a shadow directory in the upper layer. 753df8bae1dSRodney W. Grimes * The new vnode is returned locked. 754df8bae1dSRodney W. Grimes * 755df8bae1dSRodney W. Grimes * (um) points to the union mount structure for access to the 756df8bae1dSRodney W. Grimes * the mounting process's credentials. 757df8bae1dSRodney W. Grimes * (dvp) is the directory in which to create the shadow directory. 758df8bae1dSRodney W. Grimes * it is unlocked on entry and exit. 759df8bae1dSRodney W. Grimes * (cnp) is the componentname to be created. 760df8bae1dSRodney W. Grimes * (vpp) is the returned newly created shadow directory, which 761df8bae1dSRodney W. Grimes * is returned locked. 762df8bae1dSRodney W. Grimes */ 763df8bae1dSRodney W. Grimes int 764df8bae1dSRodney W. Grimes union_mkshadow(um, dvp, cnp, vpp) 765df8bae1dSRodney W. Grimes struct union_mount *um; 766df8bae1dSRodney W. Grimes struct vnode *dvp; 767df8bae1dSRodney W. Grimes struct componentname *cnp; 768df8bae1dSRodney W. Grimes struct vnode **vpp; 769df8bae1dSRodney W. Grimes { 770df8bae1dSRodney W. Grimes int error; 771df8bae1dSRodney W. Grimes struct vattr va; 772df8bae1dSRodney W. Grimes struct proc *p = cnp->cn_proc; 773df8bae1dSRodney W. Grimes struct componentname cn; 774df8bae1dSRodney W. Grimes 775996c772fSJohn Dyson error = union_relookup(um, dvp, vpp, cnp, &cn, 776996c772fSJohn Dyson cnp->cn_nameptr, cnp->cn_namelen); 777996c772fSJohn Dyson if (error) 778996c772fSJohn Dyson return (error); 779996c772fSJohn Dyson 780996c772fSJohn Dyson if (*vpp) { 781996c772fSJohn Dyson VOP_ABORTOP(dvp, &cn); 782996c772fSJohn Dyson VOP_UNLOCK(dvp, 0, p); 783996c772fSJohn Dyson vrele(*vpp); 784996c772fSJohn Dyson *vpp = NULLVP; 785996c772fSJohn Dyson return (EEXIST); 786996c772fSJohn Dyson } 787996c772fSJohn Dyson 788df8bae1dSRodney W. Grimes /* 789df8bae1dSRodney W. Grimes * policy: when creating the shadow directory in the 790df8bae1dSRodney W. Grimes * upper layer, create it owned by the user who did 791df8bae1dSRodney W. Grimes * the mount, group from parent directory, and mode 792df8bae1dSRodney W. Grimes * 777 modified by umask (ie mostly identical to the 793df8bae1dSRodney W. Grimes * mkdir syscall). (jsp, kb) 794df8bae1dSRodney W. Grimes */ 795df8bae1dSRodney W. Grimes 796df8bae1dSRodney W. Grimes VATTR_NULL(&va); 797df8bae1dSRodney W. Grimes va.va_type = VDIR; 798df8bae1dSRodney W. Grimes va.va_mode = um->um_cmode; 799df8bae1dSRodney W. Grimes 800996c772fSJohn Dyson /* VOP_LEASE: dvp is locked */ 801996c772fSJohn Dyson VOP_LEASE(dvp, p, cn.cn_cred, LEASE_WRITE); 802df8bae1dSRodney W. Grimes 803df8bae1dSRodney W. Grimes error = VOP_MKDIR(dvp, vpp, &cn, &va); 804df8bae1dSRodney W. Grimes return (error); 805df8bae1dSRodney W. Grimes } 806df8bae1dSRodney W. Grimes 807df8bae1dSRodney W. Grimes /* 808996c772fSJohn Dyson * Create a whiteout entry in the upper layer. 809996c772fSJohn Dyson * 810996c772fSJohn Dyson * (um) points to the union mount structure for access to the 811996c772fSJohn Dyson * the mounting process's credentials. 812996c772fSJohn Dyson * (dvp) is the directory in which to create the whiteout. 813996c772fSJohn Dyson * it is locked on entry and exit. 814996c772fSJohn Dyson * (cnp) is the componentname to be created. 815996c772fSJohn Dyson */ 816996c772fSJohn Dyson int 817996c772fSJohn Dyson union_mkwhiteout(um, dvp, cnp, path) 818996c772fSJohn Dyson struct union_mount *um; 819996c772fSJohn Dyson struct vnode *dvp; 820996c772fSJohn Dyson struct componentname *cnp; 821996c772fSJohn Dyson char *path; 822996c772fSJohn Dyson { 823996c772fSJohn Dyson int error; 824996c772fSJohn Dyson struct vattr va; 825996c772fSJohn Dyson struct proc *p = cnp->cn_proc; 826996c772fSJohn Dyson struct vnode *wvp; 827996c772fSJohn Dyson struct componentname cn; 828996c772fSJohn Dyson 829996c772fSJohn Dyson VOP_UNLOCK(dvp, 0, p); 830996c772fSJohn Dyson error = union_relookup(um, dvp, &wvp, cnp, &cn, path, strlen(path)); 831996c772fSJohn Dyson if (error) { 832996c772fSJohn Dyson vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); 833996c772fSJohn Dyson return (error); 834996c772fSJohn Dyson } 835996c772fSJohn Dyson 836996c772fSJohn Dyson if (wvp) { 837996c772fSJohn Dyson VOP_ABORTOP(dvp, &cn); 838996c772fSJohn Dyson vrele(dvp); 839996c772fSJohn Dyson vrele(wvp); 840996c772fSJohn Dyson return (EEXIST); 841996c772fSJohn Dyson } 842996c772fSJohn Dyson 843996c772fSJohn Dyson /* VOP_LEASE: dvp is locked */ 844996c772fSJohn Dyson VOP_LEASE(dvp, p, p->p_ucred, LEASE_WRITE); 845996c772fSJohn Dyson 846996c772fSJohn Dyson error = VOP_WHITEOUT(dvp, &cn, CREATE); 847996c772fSJohn Dyson if (error) 848996c772fSJohn Dyson VOP_ABORTOP(dvp, &cn); 849996c772fSJohn Dyson 850996c772fSJohn Dyson vrele(dvp); 851996c772fSJohn Dyson 852996c772fSJohn Dyson return (error); 853996c772fSJohn Dyson } 854996c772fSJohn Dyson 855996c772fSJohn Dyson /* 856df8bae1dSRodney W. Grimes * union_vn_create: creates and opens a new shadow file 857df8bae1dSRodney W. Grimes * on the upper union layer. this function is similar 858df8bae1dSRodney W. Grimes * in spirit to calling vn_open but it avoids calling namei(). 859df8bae1dSRodney W. Grimes * the problem with calling namei is that a) it locks too many 860df8bae1dSRodney W. Grimes * things, and b) it doesn't start at the "right" directory, 861df8bae1dSRodney W. Grimes * whereas relookup is told where to start. 862df8bae1dSRodney W. Grimes */ 863df8bae1dSRodney W. Grimes int 864df8bae1dSRodney W. Grimes union_vn_create(vpp, un, p) 865df8bae1dSRodney W. Grimes struct vnode **vpp; 866df8bae1dSRodney W. Grimes struct union_node *un; 867df8bae1dSRodney W. Grimes struct proc *p; 868df8bae1dSRodney W. Grimes { 869df8bae1dSRodney W. Grimes struct vnode *vp; 870df8bae1dSRodney W. Grimes struct ucred *cred = p->p_ucred; 871df8bae1dSRodney W. Grimes struct vattr vat; 872df8bae1dSRodney W. Grimes struct vattr *vap = &vat; 873df8bae1dSRodney W. Grimes int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL); 874df8bae1dSRodney W. Grimes int error; 875df8bae1dSRodney W. Grimes int cmode = UN_FILEMODE & ~p->p_fd->fd_cmask; 876df8bae1dSRodney W. Grimes struct componentname cn; 877df8bae1dSRodney W. Grimes 878df8bae1dSRodney W. Grimes *vpp = NULLVP; 879df8bae1dSRodney W. Grimes 880df8bae1dSRodney W. Grimes /* 881df8bae1dSRodney W. Grimes * Build a new componentname structure (for the same 882df8bae1dSRodney W. Grimes * reasons outlines in union_mkshadow). 883df8bae1dSRodney W. Grimes * The difference here is that the file is owned by 884df8bae1dSRodney W. Grimes * the current user, rather than by the person who 885df8bae1dSRodney W. Grimes * did the mount, since the current user needs to be 886df8bae1dSRodney W. Grimes * able to write the file (that's why it is being 887df8bae1dSRodney W. Grimes * copied in the first place). 888df8bae1dSRodney W. Grimes */ 889df8bae1dSRodney W. Grimes cn.cn_namelen = strlen(un->un_path); 890df8bae1dSRodney W. Grimes cn.cn_pnbuf = (caddr_t) malloc(cn.cn_namelen, M_NAMEI, M_WAITOK); 891df8bae1dSRodney W. Grimes bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1); 892df8bae1dSRodney W. Grimes cn.cn_nameiop = CREATE; 893df8bae1dSRodney W. Grimes cn.cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN); 894df8bae1dSRodney W. Grimes cn.cn_proc = p; 895df8bae1dSRodney W. Grimes cn.cn_cred = p->p_ucred; 896df8bae1dSRodney W. Grimes cn.cn_nameptr = cn.cn_pnbuf; 897df8bae1dSRodney W. Grimes cn.cn_hash = un->un_hash; 898df8bae1dSRodney W. Grimes cn.cn_consume = 0; 899df8bae1dSRodney W. Grimes 900df8bae1dSRodney W. Grimes VREF(un->un_dirvp); 9013a773ad0SPoul-Henning Kamp error = relookup(un->un_dirvp, &vp, &cn); 9023a773ad0SPoul-Henning Kamp if (error) 903df8bae1dSRodney W. Grimes return (error); 904df8bae1dSRodney W. Grimes vrele(un->un_dirvp); 905df8bae1dSRodney W. Grimes 906df8bae1dSRodney W. Grimes if (vp) { 907df8bae1dSRodney W. Grimes VOP_ABORTOP(un->un_dirvp, &cn); 908df8bae1dSRodney W. Grimes if (un->un_dirvp == vp) 909df8bae1dSRodney W. Grimes vrele(un->un_dirvp); 910df8bae1dSRodney W. Grimes else 911df8bae1dSRodney W. Grimes vput(un->un_dirvp); 912df8bae1dSRodney W. Grimes vrele(vp); 913df8bae1dSRodney W. Grimes return (EEXIST); 914df8bae1dSRodney W. Grimes } 915df8bae1dSRodney W. Grimes 916df8bae1dSRodney W. Grimes /* 917df8bae1dSRodney W. Grimes * Good - there was no race to create the file 918df8bae1dSRodney W. Grimes * so go ahead and create it. The permissions 919df8bae1dSRodney W. Grimes * on the file will be 0666 modified by the 920df8bae1dSRodney W. Grimes * current user's umask. Access to the file, while 921df8bae1dSRodney W. Grimes * it is unioned, will require access to the top *and* 922df8bae1dSRodney W. Grimes * bottom files. Access when not unioned will simply 923df8bae1dSRodney W. Grimes * require access to the top-level file. 924df8bae1dSRodney W. Grimes * TODO: confirm choice of access permissions. 925df8bae1dSRodney W. Grimes */ 926df8bae1dSRodney W. Grimes VATTR_NULL(vap); 927df8bae1dSRodney W. Grimes vap->va_type = VREG; 928df8bae1dSRodney W. Grimes vap->va_mode = cmode; 929996c772fSJohn Dyson VOP_LEASE(un->un_dirvp, p, cred, LEASE_WRITE); 930996c772fSJohn Dyson if (error = VOP_CREATE(un->un_dirvp, &vp, &cn, vap)) 931df8bae1dSRodney W. Grimes return (error); 932df8bae1dSRodney W. Grimes 9333a773ad0SPoul-Henning Kamp error = VOP_OPEN(vp, fmode, cred, p); 9343a773ad0SPoul-Henning Kamp if (error) { 935df8bae1dSRodney W. Grimes vput(vp); 936df8bae1dSRodney W. Grimes return (error); 937df8bae1dSRodney W. Grimes } 938df8bae1dSRodney W. Grimes 939df8bae1dSRodney W. Grimes vp->v_writecount++; 940df8bae1dSRodney W. Grimes *vpp = vp; 941df8bae1dSRodney W. Grimes return (0); 942df8bae1dSRodney W. Grimes } 943df8bae1dSRodney W. Grimes 944df8bae1dSRodney W. Grimes int 945df8bae1dSRodney W. Grimes union_vn_close(vp, fmode, cred, p) 946df8bae1dSRodney W. Grimes struct vnode *vp; 947df8bae1dSRodney W. Grimes int fmode; 948df8bae1dSRodney W. Grimes struct ucred *cred; 949df8bae1dSRodney W. Grimes struct proc *p; 950df8bae1dSRodney W. Grimes { 951996c772fSJohn Dyson 952df8bae1dSRodney W. Grimes if (fmode & FWRITE) 953df8bae1dSRodney W. Grimes --vp->v_writecount; 954cf2455a3SBruce Evans return (VOP_CLOSE(vp, fmode, cred, p)); 955df8bae1dSRodney W. Grimes } 956df8bae1dSRodney W. Grimes 957df8bae1dSRodney W. Grimes void 958df8bae1dSRodney W. Grimes union_removed_upper(un) 959df8bae1dSRodney W. Grimes struct union_node *un; 960df8bae1dSRodney W. Grimes { 961996c772fSJohn Dyson struct proc *p = curproc; /* XXX */ 962df8bae1dSRodney W. Grimes 963df8bae1dSRodney W. Grimes union_newupper(un, NULLVP); 964996c772fSJohn Dyson if (un->un_flags & UN_CACHED) { 965996c772fSJohn Dyson un->un_flags &= ~UN_CACHED; 966996c772fSJohn Dyson LIST_REMOVE(un, un_cache); 967df8bae1dSRodney W. Grimes } 968df8bae1dSRodney W. Grimes 969996c772fSJohn Dyson if (un->un_flags & UN_ULOCK) { 970996c772fSJohn Dyson un->un_flags &= ~UN_ULOCK; 971996c772fSJohn Dyson VOP_UNLOCK(un->un_uppervp, 0, p); 972996c772fSJohn Dyson } 973996c772fSJohn Dyson } 974996c772fSJohn Dyson 975996c772fSJohn Dyson #if 0 976df8bae1dSRodney W. Grimes struct vnode * 977df8bae1dSRodney W. Grimes union_lowervp(vp) 978df8bae1dSRodney W. Grimes struct vnode *vp; 979df8bae1dSRodney W. Grimes { 980df8bae1dSRodney W. Grimes struct union_node *un = VTOUNION(vp); 981df8bae1dSRodney W. Grimes 982996c772fSJohn Dyson if ((un->un_lowervp != NULLVP) && 983996c772fSJohn Dyson (vp->v_type == un->un_lowervp->v_type)) { 984996c772fSJohn Dyson if (vget(un->un_lowervp, 0) == 0) 985996c772fSJohn Dyson return (un->un_lowervp); 986df8bae1dSRodney W. Grimes } 987df8bae1dSRodney W. Grimes 988996c772fSJohn Dyson return (NULLVP); 989996c772fSJohn Dyson } 990996c772fSJohn Dyson #endif 991996c772fSJohn Dyson 992996c772fSJohn Dyson /* 993996c772fSJohn Dyson * determine whether a whiteout is needed 994996c772fSJohn Dyson * during a remove/rmdir operation. 995996c772fSJohn Dyson */ 996996c772fSJohn Dyson int 997996c772fSJohn Dyson union_dowhiteout(un, cred, p) 998996c772fSJohn Dyson struct union_node *un; 999996c772fSJohn Dyson struct ucred *cred; 1000996c772fSJohn Dyson struct proc *p; 1001996c772fSJohn Dyson { 1002996c772fSJohn Dyson struct vattr va; 1003996c772fSJohn Dyson 1004996c772fSJohn Dyson if (un->un_lowervp != NULLVP) 1005996c772fSJohn Dyson return (1); 1006996c772fSJohn Dyson 1007996c772fSJohn Dyson if (VOP_GETATTR(un->un_uppervp, &va, cred, p) == 0 && 1008996c772fSJohn Dyson (va.va_flags & OPAQUE)) 1009996c772fSJohn Dyson return (1); 1010996c772fSJohn Dyson 1011996c772fSJohn Dyson return (0); 1012996c772fSJohn Dyson } 1013996c772fSJohn Dyson 1014996c772fSJohn Dyson static void 1015996c772fSJohn Dyson union_dircache_r(vp, vppp, cntp) 1016996c772fSJohn Dyson struct vnode *vp; 1017996c772fSJohn Dyson struct vnode ***vppp; 1018996c772fSJohn Dyson int *cntp; 1019996c772fSJohn Dyson { 1020996c772fSJohn Dyson struct union_node *un; 1021996c772fSJohn Dyson 1022996c772fSJohn Dyson if (vp->v_op != union_vnodeop_p) { 1023996c772fSJohn Dyson if (vppp) { 1024996c772fSJohn Dyson VREF(vp); 1025996c772fSJohn Dyson *(*vppp)++ = vp; 1026996c772fSJohn Dyson if (--(*cntp) == 0) 1027996c772fSJohn Dyson panic("union: dircache table too small"); 1028996c772fSJohn Dyson } else { 1029996c772fSJohn Dyson (*cntp)++; 1030996c772fSJohn Dyson } 1031996c772fSJohn Dyson 1032996c772fSJohn Dyson return; 1033996c772fSJohn Dyson } 1034996c772fSJohn Dyson 1035996c772fSJohn Dyson un = VTOUNION(vp); 1036996c772fSJohn Dyson if (un->un_uppervp != NULLVP) 1037996c772fSJohn Dyson union_dircache_r(un->un_uppervp, vppp, cntp); 1038996c772fSJohn Dyson if (un->un_lowervp != NULLVP) 1039996c772fSJohn Dyson union_dircache_r(un->un_lowervp, vppp, cntp); 1040996c772fSJohn Dyson } 1041996c772fSJohn Dyson 1042996c772fSJohn Dyson struct vnode * 1043996c772fSJohn Dyson union_dircache(vp, p) 1044996c772fSJohn Dyson struct vnode *vp; 1045996c772fSJohn Dyson struct proc *p; 1046996c772fSJohn Dyson { 1047996c772fSJohn Dyson int cnt; 1048996c772fSJohn Dyson struct vnode *nvp; 1049996c772fSJohn Dyson struct vnode **vpp; 1050996c772fSJohn Dyson struct vnode **dircache; 1051996c772fSJohn Dyson struct union_node *un; 1052996c772fSJohn Dyson int error; 1053996c772fSJohn Dyson 1054996c772fSJohn Dyson vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); 1055996c772fSJohn Dyson dircache = VTOUNION(vp)->un_dircache; 1056996c772fSJohn Dyson 1057996c772fSJohn Dyson nvp = NULLVP; 1058996c772fSJohn Dyson 1059996c772fSJohn Dyson if (dircache == 0) { 1060996c772fSJohn Dyson cnt = 0; 1061996c772fSJohn Dyson union_dircache_r(vp, 0, &cnt); 1062996c772fSJohn Dyson cnt++; 1063996c772fSJohn Dyson dircache = (struct vnode **) 1064996c772fSJohn Dyson malloc(cnt * sizeof(struct vnode *), 1065996c772fSJohn Dyson M_TEMP, M_WAITOK); 1066996c772fSJohn Dyson vpp = dircache; 1067996c772fSJohn Dyson union_dircache_r(vp, &vpp, &cnt); 1068996c772fSJohn Dyson *vpp = NULLVP; 1069996c772fSJohn Dyson vpp = dircache + 1; 1070996c772fSJohn Dyson } else { 1071996c772fSJohn Dyson vpp = dircache; 1072996c772fSJohn Dyson do { 1073996c772fSJohn Dyson if (*vpp++ == VTOUNION(vp)->un_uppervp) 1074996c772fSJohn Dyson break; 1075996c772fSJohn Dyson } while (*vpp != NULLVP); 1076996c772fSJohn Dyson } 1077996c772fSJohn Dyson 1078996c772fSJohn Dyson if (*vpp == NULLVP) 1079996c772fSJohn Dyson goto out; 1080996c772fSJohn Dyson 1081996c772fSJohn Dyson vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY, p); 1082996c772fSJohn Dyson VREF(*vpp); 1083996c772fSJohn Dyson error = union_allocvp(&nvp, vp->v_mount, NULLVP, NULLVP, 0, *vpp, NULLVP, 0); 1084996c772fSJohn Dyson if (error) 1085996c772fSJohn Dyson goto out; 1086996c772fSJohn Dyson 1087996c772fSJohn Dyson VTOUNION(vp)->un_dircache = 0; 1088996c772fSJohn Dyson un = VTOUNION(nvp); 1089996c772fSJohn Dyson un->un_dircache = dircache; 1090996c772fSJohn Dyson 1091996c772fSJohn Dyson out: 1092996c772fSJohn Dyson VOP_UNLOCK(vp, 0, p); 1093996c772fSJohn Dyson return (nvp); 1094df8bae1dSRodney W. Grimes } 1095