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 38c3aac50fSPeter Wemm * $FreeBSD$ 39df8bae1dSRodney W. Grimes */ 40df8bae1dSRodney W. Grimes 41df8bae1dSRodney W. Grimes #include <sys/param.h> 42df8bae1dSRodney W. Grimes #include <sys/systm.h> 433ac4d1efSBruce Evans #include <sys/fcntl.h> 448c14bf40SPeter Wemm #include <sys/file.h> 45df8bae1dSRodney W. Grimes #include <sys/filedesc.h> 46fb919e4dSMark Murray #include <sys/kernel.h> 47fb919e4dSMark Murray #include <sys/lock.h> 48fb919e4dSMark Murray #include <sys/malloc.h> 498c14bf40SPeter Wemm #include <sys/module.h> 50996c772fSJohn Dyson #include <sys/mount.h> 512178ff8bSJohn Baldwin #include <sys/mutex.h> 52fb919e4dSMark Murray #include <sys/namei.h> 53996c772fSJohn Dyson #include <sys/stat.h> 54fb919e4dSMark Murray #include <sys/vnode.h> 55fb919e4dSMark Murray 56724ab195SMike Pritchard #include <vm/vm.h> 57724ab195SMike Pritchard #include <vm/vm_extern.h> /* for vnode_pager_setsize */ 58675ea6f0SBruce Evans #include <vm/vm_zone.h> 592a31267eSMatthew Dillon #include <vm/vm_object.h> /* for vm cache coherency */ 60fb919e4dSMark Murray 61df8bae1dSRodney W. Grimes #include <miscfs/union/union.h> 62df8bae1dSRodney W. Grimes 63df8bae1dSRodney W. Grimes #include <sys/proc.h> 64df8bae1dSRodney W. Grimes 659b5e8b3aSBruce Evans extern int union_init __P((void)); 669b5e8b3aSBruce Evans 67df8bae1dSRodney W. Grimes /* must be power of two, otherwise change UNION_HASH() */ 68df8bae1dSRodney W. Grimes #define NHASH 32 69df8bae1dSRodney W. Grimes 70df8bae1dSRodney W. Grimes /* unsigned int ... */ 71df8bae1dSRodney W. Grimes #define UNION_HASH(u, l) \ 7215c73825SBruce Evans (((((uintptr_t) (u)) + ((uintptr_t) l)) >> 8) & (NHASH-1)) 73df8bae1dSRodney W. Grimes 74e3975643SJake Burkholder static LIST_HEAD(unhead, union_node) unhead[NHASH]; 75df8bae1dSRodney W. Grimes static int unvplock[NHASH]; 76df8bae1dSRodney W. Grimes 77938958b9SBruce Evans static void union_dircache_r __P((struct vnode *vp, struct vnode ***vppp, 78938958b9SBruce Evans int *cntp)); 799b5e8b3aSBruce Evans static int union_list_lock __P((int ix)); 809b5e8b3aSBruce Evans static void union_list_unlock __P((int ix)); 81938958b9SBruce Evans static int union_relookup __P((struct union_mount *um, struct vnode *dvp, 82938958b9SBruce Evans struct vnode **vpp, 83938958b9SBruce Evans struct componentname *cnp, 84938958b9SBruce Evans struct componentname *cn, char *path, 85938958b9SBruce Evans int pathlen)); 8680b301c3SPoul-Henning Kamp static void union_updatevp __P((struct union_node *un, 879b5e8b3aSBruce Evans struct vnode *uppervp, 889b5e8b3aSBruce Evans struct vnode *lowervp)); 8980b301c3SPoul-Henning Kamp static void union_newlower __P((struct union_node *, struct vnode *)); 9080b301c3SPoul-Henning Kamp static void union_newupper __P((struct union_node *, struct vnode *)); 9180b301c3SPoul-Henning Kamp static int union_copyfile __P((struct vnode *, struct vnode *, 9280b301c3SPoul-Henning Kamp struct ucred *, struct proc *)); 9380b301c3SPoul-Henning Kamp static int union_vn_create __P((struct vnode **, struct union_node *, 9480b301c3SPoul-Henning Kamp struct proc *)); 9580b301c3SPoul-Henning Kamp static int union_vn_close __P((struct vnode *, int, struct ucred *, 9680b301c3SPoul-Henning Kamp struct proc *)); 979b5e8b3aSBruce Evans 98df8bae1dSRodney W. Grimes int 99df8bae1dSRodney W. Grimes union_init() 100df8bae1dSRodney W. Grimes { 101df8bae1dSRodney W. Grimes int i; 102df8bae1dSRodney W. Grimes 103df8bae1dSRodney W. Grimes for (i = 0; i < NHASH; i++) 104df8bae1dSRodney W. Grimes LIST_INIT(&unhead[i]); 105df8bae1dSRodney W. Grimes bzero((caddr_t)unvplock, sizeof(unvplock)); 10626f9a767SRodney W. Grimes return (0); 107df8bae1dSRodney W. Grimes } 108df8bae1dSRodney W. Grimes 109df8bae1dSRodney W. Grimes static int 110df8bae1dSRodney W. Grimes union_list_lock(ix) 111df8bae1dSRodney W. Grimes int ix; 112df8bae1dSRodney W. Grimes { 1132a31267eSMatthew Dillon if (unvplock[ix] & UNVP_LOCKED) { 1142a31267eSMatthew Dillon unvplock[ix] |= UNVP_WANT; 11582478919SDavid Greenman (void) tsleep((caddr_t) &unvplock[ix], PINOD, "unllck", 0); 116df8bae1dSRodney W. Grimes return (1); 117df8bae1dSRodney W. Grimes } 1182a31267eSMatthew Dillon unvplock[ix] |= UNVP_LOCKED; 119df8bae1dSRodney W. Grimes return (0); 120df8bae1dSRodney W. Grimes } 121df8bae1dSRodney W. Grimes 122df8bae1dSRodney W. Grimes static void 123df8bae1dSRodney W. Grimes union_list_unlock(ix) 124df8bae1dSRodney W. Grimes int ix; 125df8bae1dSRodney W. Grimes { 1262a31267eSMatthew Dillon unvplock[ix] &= ~UNVP_LOCKED; 127df8bae1dSRodney W. Grimes 1282a31267eSMatthew Dillon if (unvplock[ix] & UNVP_WANT) { 1292a31267eSMatthew Dillon unvplock[ix] &= ~UNVP_WANT; 130df8bae1dSRodney W. Grimes wakeup((caddr_t) &unvplock[ix]); 131df8bae1dSRodney W. Grimes } 132df8bae1dSRodney W. Grimes } 133df8bae1dSRodney W. Grimes 1342a31267eSMatthew Dillon /* 1352a31267eSMatthew Dillon * union_updatevp: 1362a31267eSMatthew Dillon * 1372a31267eSMatthew Dillon * The uppervp, if not NULL, must be referenced and not locked by us 1382a31267eSMatthew Dillon * The lowervp, if not NULL, must be referenced. 1392a31267eSMatthew Dillon * 1402a31267eSMatthew Dillon * if uppervp and lowervp match pointers already installed, nothing 1412a31267eSMatthew Dillon * happens. The passed vp's (when matching) are not adjusted. This 1422a31267eSMatthew Dillon * routine may only be called by union_newupper() and union_newlower(). 1432a31267eSMatthew Dillon */ 1442a31267eSMatthew Dillon 14580b301c3SPoul-Henning Kamp static void 146df8bae1dSRodney W. Grimes union_updatevp(un, uppervp, lowervp) 147df8bae1dSRodney W. Grimes struct union_node *un; 148df8bae1dSRodney W. Grimes struct vnode *uppervp; 149df8bae1dSRodney W. Grimes struct vnode *lowervp; 150df8bae1dSRodney W. Grimes { 151df8bae1dSRodney W. Grimes int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp); 152df8bae1dSRodney W. Grimes int nhash = UNION_HASH(uppervp, lowervp); 153996c772fSJohn Dyson int docache = (lowervp != NULLVP || uppervp != NULLVP); 15480b301c3SPoul-Henning Kamp int lhash, uhash; 155df8bae1dSRodney W. Grimes 156df8bae1dSRodney W. Grimes /* 157df8bae1dSRodney W. Grimes * Ensure locking is ordered from lower to higher 158df8bae1dSRodney W. Grimes * to avoid deadlocks. 159df8bae1dSRodney W. Grimes */ 160df8bae1dSRodney W. Grimes if (nhash < ohash) { 161996c772fSJohn Dyson lhash = nhash; 162996c772fSJohn Dyson uhash = ohash; 163df8bae1dSRodney W. Grimes } else { 164996c772fSJohn Dyson lhash = ohash; 165996c772fSJohn Dyson uhash = nhash; 166df8bae1dSRodney W. Grimes } 167df8bae1dSRodney W. Grimes 1682a31267eSMatthew Dillon if (lhash != uhash) { 169996c772fSJohn Dyson while (union_list_lock(lhash)) 170996c772fSJohn Dyson continue; 1712a31267eSMatthew Dillon } 172996c772fSJohn Dyson 173996c772fSJohn Dyson while (union_list_lock(uhash)) 174996c772fSJohn Dyson continue; 175996c772fSJohn Dyson 176996c772fSJohn Dyson if (ohash != nhash || !docache) { 177996c772fSJohn Dyson if (un->un_flags & UN_CACHED) { 178996c772fSJohn Dyson un->un_flags &= ~UN_CACHED; 179996c772fSJohn Dyson LIST_REMOVE(un, un_cache); 180996c772fSJohn Dyson } 181996c772fSJohn Dyson } 182996c772fSJohn Dyson 183996c772fSJohn Dyson if (ohash != nhash) 184996c772fSJohn Dyson union_list_unlock(ohash); 185996c772fSJohn Dyson 186df8bae1dSRodney W. Grimes if (un->un_lowervp != lowervp) { 187df8bae1dSRodney W. Grimes if (un->un_lowervp) { 188df8bae1dSRodney W. Grimes vrele(un->un_lowervp); 189df8bae1dSRodney W. Grimes if (un->un_path) { 190df8bae1dSRodney W. Grimes free(un->un_path, M_TEMP); 191df8bae1dSRodney W. Grimes un->un_path = 0; 192df8bae1dSRodney W. Grimes } 193df8bae1dSRodney W. Grimes } 194df8bae1dSRodney W. Grimes un->un_lowervp = lowervp; 195996c772fSJohn Dyson un->un_lowersz = VNOVAL; 196df8bae1dSRodney W. Grimes } 197df8bae1dSRodney W. Grimes 198df8bae1dSRodney W. Grimes if (un->un_uppervp != uppervp) { 199df8bae1dSRodney W. Grimes if (un->un_uppervp) 200df8bae1dSRodney W. Grimes vrele(un->un_uppervp); 201df8bae1dSRodney W. Grimes un->un_uppervp = uppervp; 202996c772fSJohn Dyson un->un_uppersz = VNOVAL; 203df8bae1dSRodney W. Grimes } 204df8bae1dSRodney W. Grimes 205996c772fSJohn Dyson if (docache && (ohash != nhash)) { 206df8bae1dSRodney W. Grimes LIST_INSERT_HEAD(&unhead[nhash], un, un_cache); 207996c772fSJohn Dyson un->un_flags |= UN_CACHED; 208996c772fSJohn Dyson } 209df8bae1dSRodney W. Grimes 210df8bae1dSRodney W. Grimes union_list_unlock(nhash); 211df8bae1dSRodney W. Grimes } 212df8bae1dSRodney W. Grimes 2132a31267eSMatthew Dillon /* 2142a31267eSMatthew Dillon * Set a new lowervp. The passed lowervp must be referenced and will be 2152a31267eSMatthew Dillon * stored in the vp in a referenced state. 2162a31267eSMatthew Dillon */ 2172a31267eSMatthew Dillon 21880b301c3SPoul-Henning Kamp static void 219df8bae1dSRodney W. Grimes union_newlower(un, lowervp) 220df8bae1dSRodney W. Grimes struct union_node *un; 221df8bae1dSRodney W. Grimes struct vnode *lowervp; 222df8bae1dSRodney W. Grimes { 223df8bae1dSRodney W. Grimes union_updatevp(un, un->un_uppervp, lowervp); 224df8bae1dSRodney W. Grimes } 225df8bae1dSRodney W. Grimes 2262a31267eSMatthew Dillon /* 2272a31267eSMatthew Dillon * Set a new uppervp. The passed uppervp must be locked and will be 2282a31267eSMatthew Dillon * stored in the vp in a locked state. The caller should not unlock 2292a31267eSMatthew Dillon * uppervp. 2302a31267eSMatthew Dillon */ 2312a31267eSMatthew Dillon 23280b301c3SPoul-Henning Kamp static void 233df8bae1dSRodney W. Grimes union_newupper(un, uppervp) 234df8bae1dSRodney W. Grimes struct union_node *un; 235df8bae1dSRodney W. Grimes struct vnode *uppervp; 236df8bae1dSRodney W. Grimes { 237df8bae1dSRodney W. Grimes union_updatevp(un, uppervp, un->un_lowervp); 238df8bae1dSRodney W. Grimes } 239df8bae1dSRodney W. Grimes 240df8bae1dSRodney W. Grimes /* 241996c772fSJohn Dyson * Keep track of size changes in the underlying vnodes. 242996c772fSJohn Dyson * If the size changes, then callback to the vm layer 243996c772fSJohn Dyson * giving priority to the upper layer size. 244996c772fSJohn Dyson */ 245996c772fSJohn Dyson void 246996c772fSJohn Dyson union_newsize(vp, uppersz, lowersz) 247996c772fSJohn Dyson struct vnode *vp; 248996c772fSJohn Dyson off_t uppersz, lowersz; 249996c772fSJohn Dyson { 250996c772fSJohn Dyson struct union_node *un; 251996c772fSJohn Dyson off_t sz; 252996c772fSJohn Dyson 253996c772fSJohn Dyson /* only interested in regular files */ 254996c772fSJohn Dyson if (vp->v_type != VREG) 255996c772fSJohn Dyson return; 256996c772fSJohn Dyson 257996c772fSJohn Dyson un = VTOUNION(vp); 258996c772fSJohn Dyson sz = VNOVAL; 259996c772fSJohn Dyson 260996c772fSJohn Dyson if ((uppersz != VNOVAL) && (un->un_uppersz != uppersz)) { 261996c772fSJohn Dyson un->un_uppersz = uppersz; 262996c772fSJohn Dyson if (sz == VNOVAL) 263996c772fSJohn Dyson sz = un->un_uppersz; 264996c772fSJohn Dyson } 265996c772fSJohn Dyson 266996c772fSJohn Dyson if ((lowersz != VNOVAL) && (un->un_lowersz != lowersz)) { 267996c772fSJohn Dyson un->un_lowersz = lowersz; 268996c772fSJohn Dyson if (sz == VNOVAL) 269996c772fSJohn Dyson sz = un->un_lowersz; 270996c772fSJohn Dyson } 271996c772fSJohn Dyson 272996c772fSJohn Dyson if (sz != VNOVAL) { 2732a31267eSMatthew Dillon UDEBUG(("union: %s size now %ld\n", 2742a31267eSMatthew Dillon (uppersz != VNOVAL ? "upper" : "lower"), (long)sz)); 2753413421bSBoris Popov /* 2763413421bSBoris Popov * There is no need to change size of non-existent object 2773413421bSBoris Popov */ 2783413421bSBoris Popov /* vnode_pager_setsize(vp, sz); */ 279996c772fSJohn Dyson } 280996c772fSJohn Dyson } 281996c772fSJohn Dyson 282996c772fSJohn Dyson /* 2832a31267eSMatthew Dillon * union_allocvp: allocate a union_node and associate it with a 2842a31267eSMatthew Dillon * parent union_node and one or two vnodes. 2852a31267eSMatthew Dillon * 2862a31267eSMatthew Dillon * vpp Holds the returned vnode locked and referenced if no 2872a31267eSMatthew Dillon * error occurs. 2882a31267eSMatthew Dillon * 2892a31267eSMatthew Dillon * mp Holds the mount point. mp may or may not be busied. 2902a31267eSMatthew Dillon * allocvp makes no changes to mp. 2912a31267eSMatthew Dillon * 2922a31267eSMatthew Dillon * dvp Holds the parent union_node to the one we wish to create. 2932a31267eSMatthew Dillon * XXX may only be used to traverse an uncopied lowervp-based 2942a31267eSMatthew Dillon * tree? XXX 2952a31267eSMatthew Dillon * 2962a31267eSMatthew Dillon * dvp may or may not be locked. allocvp makes no changes 2972a31267eSMatthew Dillon * to dvp. 2982a31267eSMatthew Dillon * 2992a31267eSMatthew Dillon * upperdvp Holds the parent vnode to uppervp, generally used along 3002a31267eSMatthew Dillon * with path component information to create a shadow of 3012a31267eSMatthew Dillon * lowervp when uppervp does not exist. 3022a31267eSMatthew Dillon * 3032a31267eSMatthew Dillon * upperdvp is referenced but unlocked on entry, and will be 3042a31267eSMatthew Dillon * dereferenced on return. 3052a31267eSMatthew Dillon * 3062a31267eSMatthew Dillon * uppervp Holds the new uppervp vnode to be stored in the 3072a31267eSMatthew Dillon * union_node we are allocating. uppervp is referenced but 3082a31267eSMatthew Dillon * not locked, and will be dereferenced on return. 3092a31267eSMatthew Dillon * 3102a31267eSMatthew Dillon * lowervp Holds the new lowervp vnode to be stored in the 3119dbd7336SBoris Popov * union_node we are allocating. lowervp is referenced but 3122a31267eSMatthew Dillon * not locked, and will be dereferenced on return. 3132a31267eSMatthew Dillon * 3142a31267eSMatthew Dillon * cnp Holds path component information to be coupled with 3152a31267eSMatthew Dillon * lowervp and upperdvp to allow unionfs to create an uppervp 3162a31267eSMatthew Dillon * later on. Only used if lowervp is valid. The conents 3172a31267eSMatthew Dillon * of cnp is only valid for the duration of the call. 3182a31267eSMatthew Dillon * 3192a31267eSMatthew Dillon * docache Determine whether this node should be entered in the 3202a31267eSMatthew Dillon * cache or whether it should be destroyed as soon as possible. 321df8bae1dSRodney W. Grimes * 322df8bae1dSRodney W. Grimes * all union_nodes are maintained on a singly-linked 323df8bae1dSRodney W. Grimes * list. new nodes are only allocated when they cannot 324df8bae1dSRodney W. Grimes * be found on this list. entries on the list are 325df8bae1dSRodney W. Grimes * removed when the vfs reclaim entry is called. 326df8bae1dSRodney W. Grimes * 327df8bae1dSRodney W. Grimes * a single lock is kept for the entire list. this is 328df8bae1dSRodney W. Grimes * needed because the getnewvnode() function can block 329df8bae1dSRodney W. Grimes * waiting for a vnode to become free, in which case there 330df8bae1dSRodney W. Grimes * may be more than one process trying to get the same 331df8bae1dSRodney W. Grimes * vnode. this lock is only taken if we are going to 332df8bae1dSRodney W. Grimes * call getnewvnode, since the kernel itself is single-threaded. 333df8bae1dSRodney W. Grimes * 334df8bae1dSRodney W. Grimes * if an entry is found on the list, then call vget() to 335df8bae1dSRodney W. Grimes * take a reference. this is done because there may be 336df8bae1dSRodney W. Grimes * zero references to it and so it needs to removed from 337df8bae1dSRodney W. Grimes * the vnode free list. 338df8bae1dSRodney W. Grimes */ 3392a31267eSMatthew Dillon 340df8bae1dSRodney W. Grimes int 3412a31267eSMatthew Dillon union_allocvp(vpp, mp, dvp, upperdvp, cnp, uppervp, lowervp, docache) 342df8bae1dSRodney W. Grimes struct vnode **vpp; 343df8bae1dSRodney W. Grimes struct mount *mp; 3442a31267eSMatthew Dillon struct vnode *dvp; /* parent union vnode */ 3452a31267eSMatthew Dillon struct vnode *upperdvp; /* parent vnode of uppervp */ 346df8bae1dSRodney W. Grimes struct componentname *cnp; /* may be null */ 347df8bae1dSRodney W. Grimes struct vnode *uppervp; /* may be null */ 348df8bae1dSRodney W. Grimes struct vnode *lowervp; /* may be null */ 349996c772fSJohn Dyson int docache; 350df8bae1dSRodney W. Grimes { 351df8bae1dSRodney W. Grimes int error; 35226f9a767SRodney W. Grimes struct union_node *un = 0; 353996c772fSJohn Dyson struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 3542a31267eSMatthew Dillon struct proc *p = (cnp) ? cnp->cn_proc : curproc; 35527ed09c2SMatthew Dillon int hash = 0; 356996c772fSJohn Dyson int vflag; 357df8bae1dSRodney W. Grimes int try; 358df8bae1dSRodney W. Grimes 359df8bae1dSRodney W. Grimes if (uppervp == NULLVP && lowervp == NULLVP) 360df8bae1dSRodney W. Grimes panic("union: unidentifiable allocation"); 361df8bae1dSRodney W. Grimes 362df8bae1dSRodney W. Grimes if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) { 3639dbd7336SBoris Popov vrele(lowervp); 364df8bae1dSRodney W. Grimes lowervp = NULLVP; 365df8bae1dSRodney W. Grimes } 366df8bae1dSRodney W. Grimes 367996c772fSJohn Dyson /* detect the root vnode (and aliases) */ 368996c772fSJohn Dyson vflag = 0; 369996c772fSJohn Dyson if ((uppervp == um->um_uppervp) && 370996c772fSJohn Dyson ((lowervp == NULLVP) || lowervp == um->um_lowervp)) { 371996c772fSJohn Dyson if (lowervp == NULLVP) { 372996c772fSJohn Dyson lowervp = um->um_lowervp; 373996c772fSJohn Dyson if (lowervp != NULLVP) 374996c772fSJohn Dyson VREF(lowervp); 375996c772fSJohn Dyson } 376996c772fSJohn Dyson vflag = VROOT; 377996c772fSJohn Dyson } 378996c772fSJohn Dyson 379df8bae1dSRodney W. Grimes loop: 380996c772fSJohn Dyson if (!docache) { 381996c772fSJohn Dyson un = 0; 382996c772fSJohn Dyson } else for (try = 0; try < 3; try++) { 383df8bae1dSRodney W. Grimes switch (try) { 384df8bae1dSRodney W. Grimes case 0: 385df8bae1dSRodney W. Grimes if (lowervp == NULLVP) 386df8bae1dSRodney W. Grimes continue; 387df8bae1dSRodney W. Grimes hash = UNION_HASH(uppervp, lowervp); 388df8bae1dSRodney W. Grimes break; 389df8bae1dSRodney W. Grimes 390df8bae1dSRodney W. Grimes case 1: 391df8bae1dSRodney W. Grimes if (uppervp == NULLVP) 392df8bae1dSRodney W. Grimes continue; 393df8bae1dSRodney W. Grimes hash = UNION_HASH(uppervp, NULLVP); 394df8bae1dSRodney W. Grimes break; 395df8bae1dSRodney W. Grimes 396df8bae1dSRodney W. Grimes case 2: 397df8bae1dSRodney W. Grimes if (lowervp == NULLVP) 398df8bae1dSRodney W. Grimes continue; 399df8bae1dSRodney W. Grimes hash = UNION_HASH(NULLVP, lowervp); 400df8bae1dSRodney W. Grimes break; 401df8bae1dSRodney W. Grimes } 402df8bae1dSRodney W. Grimes 403df8bae1dSRodney W. Grimes while (union_list_lock(hash)) 404df8bae1dSRodney W. Grimes continue; 405df8bae1dSRodney W. Grimes 406ef9e85abSPoul-Henning Kamp LIST_FOREACH(un, &unhead[hash], un_cache) { 407df8bae1dSRodney W. Grimes if ((un->un_lowervp == lowervp || 408df8bae1dSRodney W. Grimes un->un_lowervp == NULLVP) && 409df8bae1dSRodney W. Grimes (un->un_uppervp == uppervp || 410df8bae1dSRodney W. Grimes un->un_uppervp == NULLVP) && 411df8bae1dSRodney W. Grimes (UNIONTOV(un)->v_mount == mp)) { 412996c772fSJohn Dyson if (vget(UNIONTOV(un), 0, 413996c772fSJohn Dyson cnp ? cnp->cn_proc : NULL)) { 414df8bae1dSRodney W. Grimes union_list_unlock(hash); 415df8bae1dSRodney W. Grimes goto loop; 416df8bae1dSRodney W. Grimes } 417df8bae1dSRodney W. Grimes break; 418df8bae1dSRodney W. Grimes } 419df8bae1dSRodney W. Grimes } 420df8bae1dSRodney W. Grimes 421df8bae1dSRodney W. Grimes union_list_unlock(hash); 422df8bae1dSRodney W. Grimes 423df8bae1dSRodney W. Grimes if (un) 424df8bae1dSRodney W. Grimes break; 425df8bae1dSRodney W. Grimes } 426df8bae1dSRodney W. Grimes 427df8bae1dSRodney W. Grimes if (un) { 428df8bae1dSRodney W. Grimes /* 4292a31267eSMatthew Dillon * Obtain a lock on the union_node. Everything is unlocked 4302a31267eSMatthew Dillon * except for dvp, so check that case. If they match, our 4312a31267eSMatthew Dillon * new un is already locked. Otherwise we have to lock our 4322a31267eSMatthew Dillon * new un. 4332a31267eSMatthew Dillon * 4342a31267eSMatthew Dillon * A potential deadlock situation occurs when we are holding 4352a31267eSMatthew Dillon * one lock while trying to get another. We must follow 4362a31267eSMatthew Dillon * strict ordering rules to avoid it. We try to locate dvp 4372a31267eSMatthew Dillon * by scanning up from un_vnode, since the most likely 4382a31267eSMatthew Dillon * scenario is un being under dvp. 439df8bae1dSRodney W. Grimes */ 440df8bae1dSRodney W. Grimes 4412a31267eSMatthew Dillon if (dvp && un->un_vnode != dvp) { 4422a31267eSMatthew Dillon struct vnode *scan = un->un_vnode; 4432a31267eSMatthew Dillon 4442a31267eSMatthew Dillon do { 4452a31267eSMatthew Dillon scan = VTOUNION(scan)->un_pvp; 4462a31267eSMatthew Dillon } while (scan && scan->v_tag == VT_UNION && scan != dvp); 4472a31267eSMatthew Dillon if (scan != dvp) { 448df8bae1dSRodney W. Grimes /* 4492a31267eSMatthew Dillon * our new un is above dvp (we never saw dvp 4502a31267eSMatthew Dillon * while moving up the tree). 451df8bae1dSRodney W. Grimes */ 4522a31267eSMatthew Dillon VREF(dvp); 4532a31267eSMatthew Dillon VOP_UNLOCK(dvp, 0, p); 4542a31267eSMatthew Dillon error = vn_lock(un->un_vnode, LK_EXCLUSIVE, p); 4552a31267eSMatthew Dillon vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); 4562a31267eSMatthew Dillon vrele(dvp); 457df8bae1dSRodney W. Grimes } else { 4582a31267eSMatthew Dillon /* 4592a31267eSMatthew Dillon * our new un is under dvp 4602a31267eSMatthew Dillon */ 4612a31267eSMatthew Dillon error = vn_lock(un->un_vnode, LK_EXCLUSIVE, p); 4622a31267eSMatthew Dillon } 4632a31267eSMatthew Dillon } else if (dvp == NULLVP) { 4642a31267eSMatthew Dillon /* 4652a31267eSMatthew Dillon * dvp is NULL, we need to lock un. 4662a31267eSMatthew Dillon */ 4672a31267eSMatthew Dillon error = vn_lock(un->un_vnode, LK_EXCLUSIVE, p); 4682a31267eSMatthew Dillon } else { 4692a31267eSMatthew Dillon /* 4702a31267eSMatthew Dillon * dvp == un->un_vnode, we are already locked. 4712a31267eSMatthew Dillon */ 4722a31267eSMatthew Dillon error = 0; 4732a31267eSMatthew Dillon } 4742a31267eSMatthew Dillon 4752a31267eSMatthew Dillon if (error) 476df8bae1dSRodney W. Grimes goto loop; 477df8bae1dSRodney W. Grimes 478df8bae1dSRodney W. Grimes /* 4792a31267eSMatthew Dillon * At this point, the union_node is locked and referenced. 4802a31267eSMatthew Dillon * 4812a31267eSMatthew Dillon * uppervp is locked and referenced or NULL, lowervp is 4822a31267eSMatthew Dillon * referenced or NULL. 483df8bae1dSRodney W. Grimes */ 4842a31267eSMatthew Dillon UDEBUG(("Modify existing un %p vn %p upper %p(refs %d) -> %p(refs %d)\n", 4852a31267eSMatthew Dillon un, un->un_vnode, un->un_uppervp, 4862a31267eSMatthew Dillon (un->un_uppervp ? un->un_uppervp->v_usecount : -99), 4872a31267eSMatthew Dillon uppervp, 4882a31267eSMatthew Dillon (uppervp ? uppervp->v_usecount : -99) 4892a31267eSMatthew Dillon )); 490df8bae1dSRodney W. Grimes 491df8bae1dSRodney W. Grimes if (uppervp != un->un_uppervp) { 4922a31267eSMatthew Dillon KASSERT(uppervp == NULL || uppervp->v_usecount > 0, ("union_allocvp: too few refs %d (at least 1 required) on uppervp", uppervp->v_usecount)); 493df8bae1dSRodney W. Grimes union_newupper(un, uppervp); 494df8bae1dSRodney W. Grimes } else if (uppervp) { 4952a31267eSMatthew Dillon KASSERT(uppervp->v_usecount > 1, ("union_allocvp: too few refs %d (at least 2 required) on uppervp", uppervp->v_usecount)); 496df8bae1dSRodney W. Grimes vrele(uppervp); 497df8bae1dSRodney W. Grimes } 498df8bae1dSRodney W. Grimes 499df8bae1dSRodney W. Grimes /* 500df8bae1dSRodney W. Grimes * Save information about the lower layer. 501df8bae1dSRodney W. Grimes * This needs to keep track of pathname 502df8bae1dSRodney W. Grimes * and directory information which union_vn_create 503df8bae1dSRodney W. Grimes * might need. 504df8bae1dSRodney W. Grimes */ 505df8bae1dSRodney W. Grimes if (lowervp != un->un_lowervp) { 506df8bae1dSRodney W. Grimes union_newlower(un, lowervp); 507996c772fSJohn Dyson if (cnp && (lowervp != NULLVP)) { 508df8bae1dSRodney W. Grimes un->un_path = malloc(cnp->cn_namelen+1, 509df8bae1dSRodney W. Grimes M_TEMP, M_WAITOK); 510df8bae1dSRodney W. Grimes bcopy(cnp->cn_nameptr, un->un_path, 511df8bae1dSRodney W. Grimes cnp->cn_namelen); 512df8bae1dSRodney W. Grimes un->un_path[cnp->cn_namelen] = '\0'; 513df8bae1dSRodney W. Grimes } 514df8bae1dSRodney W. Grimes } else if (lowervp) { 515df8bae1dSRodney W. Grimes vrele(lowervp); 516df8bae1dSRodney W. Grimes } 5172a31267eSMatthew Dillon 5182a31267eSMatthew Dillon /* 5192a31267eSMatthew Dillon * and upperdvp 5202a31267eSMatthew Dillon */ 5212a31267eSMatthew Dillon if (upperdvp != un->un_dirvp) { 5222a31267eSMatthew Dillon if (un->un_dirvp) 5232a31267eSMatthew Dillon vrele(un->un_dirvp); 5242a31267eSMatthew Dillon un->un_dirvp = upperdvp; 5252a31267eSMatthew Dillon } else if (upperdvp) { 5262a31267eSMatthew Dillon vrele(upperdvp); 5272a31267eSMatthew Dillon } 5282a31267eSMatthew Dillon 529df8bae1dSRodney W. Grimes *vpp = UNIONTOV(un); 530df8bae1dSRodney W. Grimes return (0); 531df8bae1dSRodney W. Grimes } 532df8bae1dSRodney W. Grimes 533996c772fSJohn Dyson if (docache) { 534df8bae1dSRodney W. Grimes /* 535df8bae1dSRodney W. Grimes * otherwise lock the vp list while we call getnewvnode 536df8bae1dSRodney W. Grimes * since that can block. 537df8bae1dSRodney W. Grimes */ 538df8bae1dSRodney W. Grimes hash = UNION_HASH(uppervp, lowervp); 539df8bae1dSRodney W. Grimes 540df8bae1dSRodney W. Grimes if (union_list_lock(hash)) 541df8bae1dSRodney W. Grimes goto loop; 542996c772fSJohn Dyson } 543df8bae1dSRodney W. Grimes 5442a31267eSMatthew Dillon /* 5452a31267eSMatthew Dillon * Create new node rather then replace old node 5462a31267eSMatthew Dillon */ 5472a31267eSMatthew Dillon 548df8bae1dSRodney W. Grimes error = getnewvnode(VT_UNION, mp, union_vnodeop_p, vpp); 549df8bae1dSRodney W. Grimes if (error) { 5502a31267eSMatthew Dillon /* 5512a31267eSMatthew Dillon * If an error occurs clear out vnodes. 5522a31267eSMatthew Dillon */ 553df8bae1dSRodney W. Grimes if (lowervp) 554df8bae1dSRodney W. Grimes vrele(lowervp); 5552a31267eSMatthew Dillon if (uppervp) 5562a31267eSMatthew Dillon vrele(uppervp); 5572a31267eSMatthew Dillon if (upperdvp) 5582a31267eSMatthew Dillon vrele(upperdvp); 5592a31267eSMatthew Dillon *vpp = NULL; 560df8bae1dSRodney W. Grimes goto out; 561df8bae1dSRodney W. Grimes } 562df8bae1dSRodney W. Grimes 563df8bae1dSRodney W. Grimes MALLOC((*vpp)->v_data, void *, sizeof(struct union_node), 564df8bae1dSRodney W. Grimes M_TEMP, M_WAITOK); 565df8bae1dSRodney W. Grimes 566996c772fSJohn Dyson (*vpp)->v_flag |= vflag; 567df8bae1dSRodney W. Grimes if (uppervp) 568df8bae1dSRodney W. Grimes (*vpp)->v_type = uppervp->v_type; 569df8bae1dSRodney W. Grimes else 570df8bae1dSRodney W. Grimes (*vpp)->v_type = lowervp->v_type; 5712a31267eSMatthew Dillon 572df8bae1dSRodney W. Grimes un = VTOUNION(*vpp); 5732a31267eSMatthew Dillon bzero(un, sizeof(*un)); 5742a31267eSMatthew Dillon 5752a31267eSMatthew Dillon lockinit(&un->un_lock, PVFS, "unlock", 0, 0); 5762a31267eSMatthew Dillon vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY, p); 5772a31267eSMatthew Dillon 578df8bae1dSRodney W. Grimes un->un_vnode = *vpp; 579df8bae1dSRodney W. Grimes un->un_uppervp = uppervp; 580996c772fSJohn Dyson un->un_uppersz = VNOVAL; 581df8bae1dSRodney W. Grimes un->un_lowervp = lowervp; 582996c772fSJohn Dyson un->un_lowersz = VNOVAL; 5832a31267eSMatthew Dillon un->un_dirvp = upperdvp; 5842a31267eSMatthew Dillon un->un_pvp = dvp; /* only parent dir in new allocation */ 5852a31267eSMatthew Dillon if (dvp != NULLVP) 5862a31267eSMatthew Dillon VREF(dvp); 587996c772fSJohn Dyson un->un_dircache = 0; 588df8bae1dSRodney W. Grimes un->un_openl = 0; 5892a31267eSMatthew Dillon 590996c772fSJohn Dyson if (cnp && (lowervp != NULLVP)) { 591df8bae1dSRodney W. Grimes un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK); 592df8bae1dSRodney W. Grimes bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen); 593df8bae1dSRodney W. Grimes un->un_path[cnp->cn_namelen] = '\0'; 594df8bae1dSRodney W. Grimes } else { 595df8bae1dSRodney W. Grimes un->un_path = 0; 5962a31267eSMatthew Dillon un->un_dirvp = NULL; 597df8bae1dSRodney W. Grimes } 598df8bae1dSRodney W. Grimes 599996c772fSJohn Dyson if (docache) { 600df8bae1dSRodney W. Grimes LIST_INSERT_HEAD(&unhead[hash], un, un_cache); 601996c772fSJohn Dyson un->un_flags |= UN_CACHED; 602996c772fSJohn Dyson } 603df8bae1dSRodney W. Grimes 6042a31267eSMatthew Dillon out: 605996c772fSJohn Dyson if (docache) 606df8bae1dSRodney W. Grimes union_list_unlock(hash); 607df8bae1dSRodney W. Grimes 608df8bae1dSRodney W. Grimes return (error); 609df8bae1dSRodney W. Grimes } 610df8bae1dSRodney W. Grimes 611df8bae1dSRodney W. Grimes int 612df8bae1dSRodney W. Grimes union_freevp(vp) 613df8bae1dSRodney W. Grimes struct vnode *vp; 614df8bae1dSRodney W. Grimes { 615df8bae1dSRodney W. Grimes struct union_node *un = VTOUNION(vp); 616df8bae1dSRodney W. Grimes 617996c772fSJohn Dyson if (un->un_flags & UN_CACHED) { 618996c772fSJohn Dyson un->un_flags &= ~UN_CACHED; 619df8bae1dSRodney W. Grimes LIST_REMOVE(un, un_cache); 620996c772fSJohn Dyson } 621df8bae1dSRodney W. Grimes 6222a31267eSMatthew Dillon if (un->un_pvp != NULLVP) { 623996c772fSJohn Dyson vrele(un->un_pvp); 6242a31267eSMatthew Dillon un->un_pvp = NULL; 6252a31267eSMatthew Dillon } 6262a31267eSMatthew Dillon if (un->un_uppervp != NULLVP) { 627df8bae1dSRodney W. Grimes vrele(un->un_uppervp); 6282a31267eSMatthew Dillon un->un_uppervp = NULL; 6292a31267eSMatthew Dillon } 6302a31267eSMatthew Dillon if (un->un_lowervp != NULLVP) { 631df8bae1dSRodney W. Grimes vrele(un->un_lowervp); 6322a31267eSMatthew Dillon un->un_lowervp = NULL; 6332a31267eSMatthew Dillon } 6342a31267eSMatthew Dillon if (un->un_dirvp != NULLVP) { 635df8bae1dSRodney W. Grimes vrele(un->un_dirvp); 6362a31267eSMatthew Dillon un->un_dirvp = NULL; 6372a31267eSMatthew Dillon } 6382a31267eSMatthew Dillon if (un->un_path) { 639df8bae1dSRodney W. Grimes free(un->un_path, M_TEMP); 6402a31267eSMatthew Dillon un->un_path = NULL; 6412a31267eSMatthew Dillon } 642a18b1f1dSJason Evans lockdestroy(&un->un_lock); 643df8bae1dSRodney W. Grimes 644df8bae1dSRodney W. Grimes FREE(vp->v_data, M_TEMP); 645df8bae1dSRodney W. Grimes vp->v_data = 0; 646df8bae1dSRodney W. Grimes 647df8bae1dSRodney W. Grimes return (0); 648df8bae1dSRodney W. Grimes } 649df8bae1dSRodney W. Grimes 650df8bae1dSRodney W. Grimes /* 651df8bae1dSRodney W. Grimes * copyfile. copy the vnode (fvp) to the vnode (tvp) 652df8bae1dSRodney W. Grimes * using a sequence of reads and writes. both (fvp) 653df8bae1dSRodney W. Grimes * and (tvp) are locked on entry and exit. 6542a31267eSMatthew Dillon * 6552a31267eSMatthew Dillon * fvp and tvp are both exclusive locked on call, but their refcount's 6562a31267eSMatthew Dillon * haven't been bumped at all. 657df8bae1dSRodney W. Grimes */ 65880b301c3SPoul-Henning Kamp static int 659996c772fSJohn Dyson union_copyfile(fvp, tvp, cred, p) 660df8bae1dSRodney W. Grimes struct vnode *fvp; 661df8bae1dSRodney W. Grimes struct vnode *tvp; 662996c772fSJohn Dyson struct ucred *cred; 663996c772fSJohn Dyson struct proc *p; 664df8bae1dSRodney W. Grimes { 665df8bae1dSRodney W. Grimes char *buf; 666df8bae1dSRodney W. Grimes struct uio uio; 667df8bae1dSRodney W. Grimes struct iovec iov; 668df8bae1dSRodney W. Grimes int error = 0; 669df8bae1dSRodney W. Grimes 670df8bae1dSRodney W. Grimes /* 671df8bae1dSRodney W. Grimes * strategy: 672df8bae1dSRodney W. Grimes * allocate a buffer of size MAXBSIZE. 673df8bae1dSRodney W. Grimes * loop doing reads and writes, keeping track 674df8bae1dSRodney W. Grimes * of the current uio offset. 675df8bae1dSRodney W. Grimes * give up at the first sign of trouble. 676df8bae1dSRodney W. Grimes */ 677df8bae1dSRodney W. Grimes 6782a31267eSMatthew Dillon bzero(&uio, sizeof(uio)); 6792a31267eSMatthew Dillon 680df8bae1dSRodney W. Grimes uio.uio_procp = p; 681df8bae1dSRodney W. Grimes uio.uio_segflg = UIO_SYSSPACE; 682df8bae1dSRodney W. Grimes uio.uio_offset = 0; 683df8bae1dSRodney W. Grimes 684996c772fSJohn Dyson VOP_LEASE(fvp, p, cred, LEASE_READ); 685996c772fSJohn Dyson VOP_LEASE(tvp, p, cred, LEASE_WRITE); 686df8bae1dSRodney W. Grimes 687df8bae1dSRodney W. Grimes buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK); 688df8bae1dSRodney W. Grimes 689df8bae1dSRodney W. Grimes /* ugly loop follows... */ 690df8bae1dSRodney W. Grimes do { 691df8bae1dSRodney W. Grimes off_t offset = uio.uio_offset; 6922a31267eSMatthew Dillon int count; 6932a31267eSMatthew Dillon int bufoffset; 694df8bae1dSRodney W. Grimes 6952a31267eSMatthew Dillon /* 6962a31267eSMatthew Dillon * Setup for big read 6972a31267eSMatthew Dillon */ 698df8bae1dSRodney W. Grimes uio.uio_iov = &iov; 699df8bae1dSRodney W. Grimes uio.uio_iovcnt = 1; 700df8bae1dSRodney W. Grimes iov.iov_base = buf; 701df8bae1dSRodney W. Grimes iov.iov_len = MAXBSIZE; 702df8bae1dSRodney W. Grimes uio.uio_resid = iov.iov_len; 703df8bae1dSRodney W. Grimes uio.uio_rw = UIO_READ; 704df8bae1dSRodney W. Grimes 7052a31267eSMatthew Dillon if ((error = VOP_READ(fvp, &uio, 0, cred)) != 0) 7062a31267eSMatthew Dillon break; 7072a31267eSMatthew Dillon 7082a31267eSMatthew Dillon /* 7092a31267eSMatthew Dillon * Get bytes read, handle read eof case and setup for 7102a31267eSMatthew Dillon * write loop 7112a31267eSMatthew Dillon */ 7122a31267eSMatthew Dillon if ((count = MAXBSIZE - uio.uio_resid) == 0) 7132a31267eSMatthew Dillon break; 7142a31267eSMatthew Dillon bufoffset = 0; 7152a31267eSMatthew Dillon 7162a31267eSMatthew Dillon /* 7172a31267eSMatthew Dillon * Write until an error occurs or our buffer has been 7182a31267eSMatthew Dillon * exhausted, then update the offset for the next read. 7192a31267eSMatthew Dillon */ 7202a31267eSMatthew Dillon while (bufoffset < count) { 721df8bae1dSRodney W. Grimes uio.uio_iov = &iov; 722df8bae1dSRodney W. Grimes uio.uio_iovcnt = 1; 7232a31267eSMatthew Dillon iov.iov_base = buf + bufoffset; 7242a31267eSMatthew Dillon iov.iov_len = count - bufoffset; 7252a31267eSMatthew Dillon uio.uio_offset = offset + bufoffset; 726df8bae1dSRodney W. Grimes uio.uio_rw = UIO_WRITE; 727df8bae1dSRodney W. Grimes uio.uio_resid = iov.iov_len; 728df8bae1dSRodney W. Grimes 7292a31267eSMatthew Dillon if ((error = VOP_WRITE(tvp, &uio, 0, cred)) != 0) 730df8bae1dSRodney W. Grimes break; 7312a31267eSMatthew Dillon bufoffset += (count - bufoffset) - uio.uio_resid; 732df8bae1dSRodney W. Grimes } 7332a31267eSMatthew Dillon uio.uio_offset = offset + bufoffset; 734df8bae1dSRodney W. Grimes } while (error == 0); 735df8bae1dSRodney W. Grimes 736df8bae1dSRodney W. Grimes free(buf, M_TEMP); 737df8bae1dSRodney W. Grimes return (error); 738df8bae1dSRodney W. Grimes } 739df8bae1dSRodney W. Grimes 740df8bae1dSRodney W. Grimes /* 7412a31267eSMatthew Dillon * 7422a31267eSMatthew Dillon * un's vnode is assumed to be locked on entry and remains locked on exit. 743996c772fSJohn Dyson */ 7442a31267eSMatthew Dillon 745996c772fSJohn Dyson int 746996c772fSJohn Dyson union_copyup(un, docopy, cred, p) 747996c772fSJohn Dyson struct union_node *un; 748996c772fSJohn Dyson int docopy; 749996c772fSJohn Dyson struct ucred *cred; 750996c772fSJohn Dyson struct proc *p; 751996c772fSJohn Dyson { 752996c772fSJohn Dyson int error; 753f2a2857bSKirk McKusick struct mount *mp; 754996c772fSJohn Dyson struct vnode *lvp, *uvp; 755996c772fSJohn Dyson 7565842d4e5SKATO Takenori /* 7575842d4e5SKATO Takenori * If the user does not have read permission, the vnode should not 7585842d4e5SKATO Takenori * be copied to upper layer. 7595842d4e5SKATO Takenori */ 7605842d4e5SKATO Takenori vn_lock(un->un_lowervp, LK_EXCLUSIVE | LK_RETRY, p); 7615842d4e5SKATO Takenori error = VOP_ACCESS(un->un_lowervp, VREAD, cred, p); 7625842d4e5SKATO Takenori VOP_UNLOCK(un->un_lowervp, 0, p); 7635842d4e5SKATO Takenori if (error) 7645842d4e5SKATO Takenori return (error); 7655842d4e5SKATO Takenori 766f2a2857bSKirk McKusick if ((error = vn_start_write(un->un_dirvp, &mp, V_WAIT | PCATCH)) != 0) 767996c772fSJohn Dyson return (error); 768f2a2857bSKirk McKusick if ((error = union_vn_create(&uvp, un, p)) != 0) { 769f2a2857bSKirk McKusick vn_finished_write(mp); 770f2a2857bSKirk McKusick return (error); 771f2a2857bSKirk McKusick } 772996c772fSJohn Dyson 773996c772fSJohn Dyson lvp = un->un_lowervp; 774996c772fSJohn Dyson 7752a31267eSMatthew Dillon KASSERT(uvp->v_usecount > 0, ("copy: uvp refcount 0: %d", uvp->v_usecount)); 776996c772fSJohn Dyson if (docopy) { 777996c772fSJohn Dyson /* 778996c772fSJohn Dyson * XX - should not ignore errors 779996c772fSJohn Dyson * from VOP_CLOSE 780996c772fSJohn Dyson */ 781996c772fSJohn Dyson vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p); 782996c772fSJohn Dyson error = VOP_OPEN(lvp, FREAD, cred, p); 7832a31267eSMatthew Dillon if (error == 0 && vn_canvmio(lvp) == TRUE) 7842a31267eSMatthew Dillon error = vfs_object_create(lvp, p, cred); 785996c772fSJohn Dyson if (error == 0) { 786996c772fSJohn Dyson error = union_copyfile(lvp, uvp, cred, p); 787996c772fSJohn Dyson VOP_UNLOCK(lvp, 0, p); 788996c772fSJohn Dyson (void) VOP_CLOSE(lvp, FREAD, cred, p); 789996c772fSJohn Dyson } 790996c772fSJohn Dyson if (error == 0) 7912a31267eSMatthew Dillon UDEBUG(("union: copied up %s\n", un->un_path)); 792996c772fSJohn Dyson 793996c772fSJohn Dyson } 794996c772fSJohn Dyson VOP_UNLOCK(uvp, 0, p); 795f2a2857bSKirk McKusick vn_finished_write(mp); 7962a31267eSMatthew Dillon union_newupper(un, uvp); 7972a31267eSMatthew Dillon KASSERT(uvp->v_usecount > 0, ("copy: uvp refcount 0: %d", uvp->v_usecount)); 798996c772fSJohn Dyson union_vn_close(uvp, FWRITE, cred, p); 7992a31267eSMatthew Dillon KASSERT(uvp->v_usecount > 0, ("copy: uvp refcount 0: %d", uvp->v_usecount)); 800996c772fSJohn Dyson /* 801996c772fSJohn Dyson * Subsequent IOs will go to the top layer, so 802996c772fSJohn Dyson * call close on the lower vnode and open on the 803996c772fSJohn Dyson * upper vnode to ensure that the filesystem keeps 804996c772fSJohn Dyson * its references counts right. This doesn't do 805996c772fSJohn Dyson * the right thing with (cred) and (FREAD) though. 806996c772fSJohn Dyson * Ignoring error returns is not right, either. 807996c772fSJohn Dyson */ 808996c772fSJohn Dyson if (error == 0) { 809996c772fSJohn Dyson int i; 810996c772fSJohn Dyson 811996c772fSJohn Dyson for (i = 0; i < un->un_openl; i++) { 812996c772fSJohn Dyson (void) VOP_CLOSE(lvp, FREAD, cred, p); 813996c772fSJohn Dyson (void) VOP_OPEN(uvp, FREAD, cred, p); 814996c772fSJohn Dyson } 815e3a285c7SMatthew Dillon if (un->un_openl) { 8162a31267eSMatthew Dillon if (vn_canvmio(uvp) == TRUE) 8172a31267eSMatthew Dillon error = vfs_object_create(uvp, p, cred); 818e3a285c7SMatthew Dillon } 819996c772fSJohn Dyson un->un_openl = 0; 820996c772fSJohn Dyson } 821996c772fSJohn Dyson 822996c772fSJohn Dyson return (error); 823996c772fSJohn Dyson 824996c772fSJohn Dyson } 825996c772fSJohn Dyson 8262a31267eSMatthew Dillon /* 8272a31267eSMatthew Dillon * union_relookup: 8282a31267eSMatthew Dillon * 8292a31267eSMatthew Dillon * dvp should be locked on entry and will be locked on return. No 8302a31267eSMatthew Dillon * net change in the ref count will occur. 8312a31267eSMatthew Dillon * 8322a31267eSMatthew Dillon * If an error is returned, *vpp will be invalid, otherwise it 8332a31267eSMatthew Dillon * will hold a locked, referenced vnode. If *vpp == dvp then 8342a31267eSMatthew Dillon * remember that only one exclusive lock is held. 8352a31267eSMatthew Dillon */ 8362a31267eSMatthew Dillon 837996c772fSJohn Dyson static int 838996c772fSJohn Dyson union_relookup(um, dvp, vpp, cnp, cn, path, pathlen) 839996c772fSJohn Dyson struct union_mount *um; 840996c772fSJohn Dyson struct vnode *dvp; 841996c772fSJohn Dyson struct vnode **vpp; 842996c772fSJohn Dyson struct componentname *cnp; 843996c772fSJohn Dyson struct componentname *cn; 844996c772fSJohn Dyson char *path; 845996c772fSJohn Dyson int pathlen; 846996c772fSJohn Dyson { 847996c772fSJohn Dyson int error; 848996c772fSJohn Dyson 849996c772fSJohn Dyson /* 850996c772fSJohn Dyson * A new componentname structure must be faked up because 851996c772fSJohn Dyson * there is no way to know where the upper level cnp came 852996c772fSJohn Dyson * from or what it is being used for. This must duplicate 853996c772fSJohn Dyson * some of the work done by NDINIT, some of the work done 854996c772fSJohn Dyson * by namei, some of the work done by lookup and some of 855996c772fSJohn Dyson * the work done by VOP_LOOKUP when given a CREATE flag. 856996c772fSJohn Dyson * Conclusion: Horrible. 857996c772fSJohn Dyson */ 858996c772fSJohn Dyson cn->cn_namelen = pathlen; 85999448ed1SJohn Dyson cn->cn_pnbuf = zalloc(namei_zone); 860996c772fSJohn Dyson bcopy(path, cn->cn_pnbuf, cn->cn_namelen); 861996c772fSJohn Dyson cn->cn_pnbuf[cn->cn_namelen] = '\0'; 862996c772fSJohn Dyson 863996c772fSJohn Dyson cn->cn_nameiop = CREATE; 8642a31267eSMatthew Dillon cn->cn_flags = (LOCKPARENT|LOCKLEAF|HASBUF|SAVENAME|ISLASTCN); 865996c772fSJohn Dyson cn->cn_proc = cnp->cn_proc; 866996c772fSJohn Dyson if (um->um_op == UNMNT_ABOVE) 867996c772fSJohn Dyson cn->cn_cred = cnp->cn_cred; 868996c772fSJohn Dyson else 869996c772fSJohn Dyson cn->cn_cred = um->um_cred; 870996c772fSJohn Dyson cn->cn_nameptr = cn->cn_pnbuf; 871996c772fSJohn Dyson cn->cn_consume = cnp->cn_consume; 872996c772fSJohn Dyson 873996c772fSJohn Dyson VREF(dvp); 8742a31267eSMatthew Dillon VOP_UNLOCK(dvp, 0, cnp->cn_proc); 8752a31267eSMatthew Dillon 8762a31267eSMatthew Dillon /* 8772a31267eSMatthew Dillon * Pass dvp unlocked and referenced on call to relookup(). 8782a31267eSMatthew Dillon * 8792a31267eSMatthew Dillon * If an error occurs, dvp will be returned unlocked and dereferenced. 8802a31267eSMatthew Dillon */ 8812a31267eSMatthew Dillon 8822a31267eSMatthew Dillon if ((error = relookup(dvp, vpp, cn)) != 0) { 8832a31267eSMatthew Dillon vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, cnp->cn_proc); 8842a31267eSMatthew Dillon return(error); 885b422956cSPoul-Henning Kamp } 886996c772fSJohn Dyson 8872a31267eSMatthew Dillon /* 8882a31267eSMatthew Dillon * If no error occurs, dvp will be returned locked with the reference 8892a31267eSMatthew Dillon * left as before, and vpp will be returned referenced and locked. 8902a31267eSMatthew Dillon * 8912a31267eSMatthew Dillon * We want to return with dvp as it was passed to us, so we get 8922a31267eSMatthew Dillon * rid of our reference. 8932a31267eSMatthew Dillon */ 8942a31267eSMatthew Dillon vrele(dvp); 8952a31267eSMatthew Dillon return (0); 896996c772fSJohn Dyson } 897996c772fSJohn Dyson 898996c772fSJohn Dyson /* 899df8bae1dSRodney W. Grimes * Create a shadow directory in the upper layer. 900df8bae1dSRodney W. Grimes * The new vnode is returned locked. 901df8bae1dSRodney W. Grimes * 902df8bae1dSRodney W. Grimes * (um) points to the union mount structure for access to the 903df8bae1dSRodney W. Grimes * the mounting process's credentials. 9042a31267eSMatthew Dillon * (dvp) is the directory in which to create the shadow directory, 9052a31267eSMatthew Dillon * it is locked (but not ref'd) on entry and return. 906df8bae1dSRodney W. Grimes * (cnp) is the componentname to be created. 907df8bae1dSRodney W. Grimes * (vpp) is the returned newly created shadow directory, which 9082a31267eSMatthew Dillon * is returned locked and ref'd 909df8bae1dSRodney W. Grimes */ 910df8bae1dSRodney W. Grimes int 911df8bae1dSRodney W. Grimes union_mkshadow(um, dvp, cnp, vpp) 912df8bae1dSRodney W. Grimes struct union_mount *um; 913df8bae1dSRodney W. Grimes struct vnode *dvp; 914df8bae1dSRodney W. Grimes struct componentname *cnp; 915df8bae1dSRodney W. Grimes struct vnode **vpp; 916df8bae1dSRodney W. Grimes { 917df8bae1dSRodney W. Grimes int error; 918df8bae1dSRodney W. Grimes struct vattr va; 919df8bae1dSRodney W. Grimes struct proc *p = cnp->cn_proc; 920df8bae1dSRodney W. Grimes struct componentname cn; 921f2a2857bSKirk McKusick struct mount *mp; 922df8bae1dSRodney W. Grimes 923f2a2857bSKirk McKusick if ((error = vn_start_write(dvp, &mp, V_WAIT | PCATCH)) != 0) 924996c772fSJohn Dyson return (error); 925f2a2857bSKirk McKusick if ((error = union_relookup(um, dvp, vpp, cnp, &cn, 926f2a2857bSKirk McKusick cnp->cn_nameptr, cnp->cn_namelen)) != 0) { 927f2a2857bSKirk McKusick vn_finished_write(mp); 928f2a2857bSKirk McKusick return (error); 929f2a2857bSKirk McKusick } 930996c772fSJohn Dyson 931996c772fSJohn Dyson if (*vpp) { 932762e6b85SEivind Eklund if (cn.cn_flags & HASBUF) { 933762e6b85SEivind Eklund zfree(namei_zone, cn.cn_pnbuf); 934762e6b85SEivind Eklund cn.cn_flags &= ~HASBUF; 935762e6b85SEivind Eklund } 9362a31267eSMatthew Dillon if (dvp == *vpp) 937996c772fSJohn Dyson vrele(*vpp); 9382a31267eSMatthew Dillon else 9392a31267eSMatthew Dillon vput(*vpp); 940f2a2857bSKirk McKusick vn_finished_write(mp); 941996c772fSJohn Dyson *vpp = NULLVP; 942996c772fSJohn Dyson return (EEXIST); 943996c772fSJohn Dyson } 944996c772fSJohn Dyson 945df8bae1dSRodney W. Grimes /* 946df8bae1dSRodney W. Grimes * policy: when creating the shadow directory in the 947df8bae1dSRodney W. Grimes * upper layer, create it owned by the user who did 948df8bae1dSRodney W. Grimes * the mount, group from parent directory, and mode 949df8bae1dSRodney W. Grimes * 777 modified by umask (ie mostly identical to the 950df8bae1dSRodney W. Grimes * mkdir syscall). (jsp, kb) 951df8bae1dSRodney W. Grimes */ 952df8bae1dSRodney W. Grimes 953df8bae1dSRodney W. Grimes VATTR_NULL(&va); 954df8bae1dSRodney W. Grimes va.va_type = VDIR; 955df8bae1dSRodney W. Grimes va.va_mode = um->um_cmode; 956df8bae1dSRodney W. Grimes 957996c772fSJohn Dyson /* VOP_LEASE: dvp is locked */ 958996c772fSJohn Dyson VOP_LEASE(dvp, p, cn.cn_cred, LEASE_WRITE); 959df8bae1dSRodney W. Grimes 960df8bae1dSRodney W. Grimes error = VOP_MKDIR(dvp, vpp, &cn, &va); 961762e6b85SEivind Eklund if (cn.cn_flags & HASBUF) { 962762e6b85SEivind Eklund zfree(namei_zone, cn.cn_pnbuf); 963762e6b85SEivind Eklund cn.cn_flags &= ~HASBUF; 964762e6b85SEivind Eklund } 9652a31267eSMatthew Dillon /*vput(dvp);*/ 966f2a2857bSKirk McKusick vn_finished_write(mp); 967df8bae1dSRodney W. Grimes return (error); 968df8bae1dSRodney W. Grimes } 969df8bae1dSRodney W. Grimes 970df8bae1dSRodney W. Grimes /* 971996c772fSJohn Dyson * Create a whiteout entry in the upper layer. 972996c772fSJohn Dyson * 973996c772fSJohn Dyson * (um) points to the union mount structure for access to the 974996c772fSJohn Dyson * the mounting process's credentials. 975996c772fSJohn Dyson * (dvp) is the directory in which to create the whiteout. 9762a31267eSMatthew Dillon * it is locked on entry and return. 977996c772fSJohn Dyson * (cnp) is the componentname to be created. 978996c772fSJohn Dyson */ 979996c772fSJohn Dyson int 980996c772fSJohn Dyson union_mkwhiteout(um, dvp, cnp, path) 981996c772fSJohn Dyson struct union_mount *um; 982996c772fSJohn Dyson struct vnode *dvp; 983996c772fSJohn Dyson struct componentname *cnp; 984996c772fSJohn Dyson char *path; 985996c772fSJohn Dyson { 986996c772fSJohn Dyson int error; 987996c772fSJohn Dyson struct proc *p = cnp->cn_proc; 988996c772fSJohn Dyson struct vnode *wvp; 989996c772fSJohn Dyson struct componentname cn; 990f2a2857bSKirk McKusick struct mount *mp; 991996c772fSJohn Dyson 992f2a2857bSKirk McKusick if ((error = vn_start_write(dvp, &mp, V_WAIT | PCATCH)) != 0) 993996c772fSJohn Dyson return (error); 994f2a2857bSKirk McKusick error = union_relookup(um, dvp, &wvp, cnp, &cn, path, strlen(path)); 995f2a2857bSKirk McKusick if (error) { 996f2a2857bSKirk McKusick vn_finished_write(mp); 997f2a2857bSKirk McKusick return (error); 998f2a2857bSKirk McKusick } 999996c772fSJohn Dyson 1000996c772fSJohn Dyson if (wvp) { 1001762e6b85SEivind Eklund if (cn.cn_flags & HASBUF) { 1002762e6b85SEivind Eklund zfree(namei_zone, cn.cn_pnbuf); 1003762e6b85SEivind Eklund cn.cn_flags &= ~HASBUF; 1004762e6b85SEivind Eklund } 10052a31267eSMatthew Dillon if (wvp == dvp) 1006996c772fSJohn Dyson vrele(wvp); 10072a31267eSMatthew Dillon else 10082a31267eSMatthew Dillon vput(wvp); 1009f2a2857bSKirk McKusick vn_finished_write(mp); 1010996c772fSJohn Dyson return (EEXIST); 1011996c772fSJohn Dyson } 1012996c772fSJohn Dyson 1013996c772fSJohn Dyson /* VOP_LEASE: dvp is locked */ 1014996c772fSJohn Dyson VOP_LEASE(dvp, p, p->p_ucred, LEASE_WRITE); 1015996c772fSJohn Dyson 1016996c772fSJohn Dyson error = VOP_WHITEOUT(dvp, &cn, CREATE); 1017762e6b85SEivind Eklund if (cn.cn_flags & HASBUF) { 1018762e6b85SEivind Eklund zfree(namei_zone, cn.cn_pnbuf); 1019762e6b85SEivind Eklund cn.cn_flags &= ~HASBUF; 1020762e6b85SEivind Eklund } 1021f2a2857bSKirk McKusick vn_finished_write(mp); 1022996c772fSJohn Dyson return (error); 1023996c772fSJohn Dyson } 1024996c772fSJohn Dyson 1025996c772fSJohn Dyson /* 1026df8bae1dSRodney W. Grimes * union_vn_create: creates and opens a new shadow file 1027df8bae1dSRodney W. Grimes * on the upper union layer. this function is similar 1028df8bae1dSRodney W. Grimes * in spirit to calling vn_open but it avoids calling namei(). 1029df8bae1dSRodney W. Grimes * the problem with calling namei is that a) it locks too many 1030df8bae1dSRodney W. Grimes * things, and b) it doesn't start at the "right" directory, 1031df8bae1dSRodney W. Grimes * whereas relookup is told where to start. 10322a31267eSMatthew Dillon * 10332a31267eSMatthew Dillon * On entry, the vnode associated with un is locked. It remains locked 10342a31267eSMatthew Dillon * on return. 10352a31267eSMatthew Dillon * 10362a31267eSMatthew Dillon * If no error occurs, *vpp contains a locked referenced vnode for your 10372a31267eSMatthew Dillon * use. If an error occurs *vpp iis undefined. 1038df8bae1dSRodney W. Grimes */ 103980b301c3SPoul-Henning Kamp static int 1040df8bae1dSRodney W. Grimes union_vn_create(vpp, un, p) 1041df8bae1dSRodney W. Grimes struct vnode **vpp; 1042df8bae1dSRodney W. Grimes struct union_node *un; 1043df8bae1dSRodney W. Grimes struct proc *p; 1044df8bae1dSRodney W. Grimes { 1045df8bae1dSRodney W. Grimes struct vnode *vp; 1046df8bae1dSRodney W. Grimes struct ucred *cred = p->p_ucred; 1047df8bae1dSRodney W. Grimes struct vattr vat; 1048df8bae1dSRodney W. Grimes struct vattr *vap = &vat; 1049df8bae1dSRodney W. Grimes int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL); 1050df8bae1dSRodney W. Grimes int error; 1051df8bae1dSRodney W. Grimes int cmode = UN_FILEMODE & ~p->p_fd->fd_cmask; 1052df8bae1dSRodney W. Grimes struct componentname cn; 1053df8bae1dSRodney W. Grimes 1054df8bae1dSRodney W. Grimes *vpp = NULLVP; 1055df8bae1dSRodney W. Grimes 1056df8bae1dSRodney W. Grimes /* 1057df8bae1dSRodney W. Grimes * Build a new componentname structure (for the same 1058df8bae1dSRodney W. Grimes * reasons outlines in union_mkshadow). 1059df8bae1dSRodney W. Grimes * The difference here is that the file is owned by 1060df8bae1dSRodney W. Grimes * the current user, rather than by the person who 1061df8bae1dSRodney W. Grimes * did the mount, since the current user needs to be 1062df8bae1dSRodney W. Grimes * able to write the file (that's why it is being 1063df8bae1dSRodney W. Grimes * copied in the first place). 1064df8bae1dSRodney W. Grimes */ 1065df8bae1dSRodney W. Grimes cn.cn_namelen = strlen(un->un_path); 106699448ed1SJohn Dyson cn.cn_pnbuf = zalloc(namei_zone); 1067df8bae1dSRodney W. Grimes bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1); 1068df8bae1dSRodney W. Grimes cn.cn_nameiop = CREATE; 10692a31267eSMatthew Dillon cn.cn_flags = (LOCKPARENT|LOCKLEAF|HASBUF|SAVENAME|ISLASTCN); 1070df8bae1dSRodney W. Grimes cn.cn_proc = p; 1071df8bae1dSRodney W. Grimes cn.cn_cred = p->p_ucred; 1072df8bae1dSRodney W. Grimes cn.cn_nameptr = cn.cn_pnbuf; 1073df8bae1dSRodney W. Grimes cn.cn_consume = 0; 1074df8bae1dSRodney W. Grimes 10752a31267eSMatthew Dillon /* 10762a31267eSMatthew Dillon * Pass dvp unlocked and referenced on call to relookup(). 10772a31267eSMatthew Dillon * 10782a31267eSMatthew Dillon * If an error occurs, dvp will be returned unlocked and dereferenced. 10792a31267eSMatthew Dillon */ 1080df8bae1dSRodney W. Grimes VREF(un->un_dirvp); 10813a773ad0SPoul-Henning Kamp error = relookup(un->un_dirvp, &vp, &cn); 10823a773ad0SPoul-Henning Kamp if (error) 1083df8bae1dSRodney W. Grimes return (error); 1084df8bae1dSRodney W. Grimes 10852a31267eSMatthew Dillon /* 10862a31267eSMatthew Dillon * If no error occurs, dvp will be returned locked with the reference 10872a31267eSMatthew Dillon * left as before, and vpp will be returned referenced and locked. 10882a31267eSMatthew Dillon */ 1089df8bae1dSRodney W. Grimes if (vp) { 1090df8bae1dSRodney W. Grimes vput(un->un_dirvp); 1091762e6b85SEivind Eklund if (cn.cn_flags & HASBUF) { 1092762e6b85SEivind Eklund zfree(namei_zone, cn.cn_pnbuf); 1093762e6b85SEivind Eklund cn.cn_flags &= ~HASBUF; 1094762e6b85SEivind Eklund } 10952a31267eSMatthew Dillon if (vp == un->un_dirvp) 1096df8bae1dSRodney W. Grimes vrele(vp); 10972a31267eSMatthew Dillon else 10982a31267eSMatthew Dillon vput(vp); 1099df8bae1dSRodney W. Grimes return (EEXIST); 1100df8bae1dSRodney W. Grimes } 1101df8bae1dSRodney W. Grimes 1102df8bae1dSRodney W. Grimes /* 1103df8bae1dSRodney W. Grimes * Good - there was no race to create the file 1104df8bae1dSRodney W. Grimes * so go ahead and create it. The permissions 1105df8bae1dSRodney W. Grimes * on the file will be 0666 modified by the 1106df8bae1dSRodney W. Grimes * current user's umask. Access to the file, while 1107df8bae1dSRodney W. Grimes * it is unioned, will require access to the top *and* 1108df8bae1dSRodney W. Grimes * bottom files. Access when not unioned will simply 1109df8bae1dSRodney W. Grimes * require access to the top-level file. 1110df8bae1dSRodney W. Grimes * TODO: confirm choice of access permissions. 1111df8bae1dSRodney W. Grimes */ 1112df8bae1dSRodney W. Grimes VATTR_NULL(vap); 1113df8bae1dSRodney W. Grimes vap->va_type = VREG; 1114df8bae1dSRodney W. Grimes vap->va_mode = cmode; 1115996c772fSJohn Dyson VOP_LEASE(un->un_dirvp, p, cred, LEASE_WRITE); 11167be2d300SMike Smith error = VOP_CREATE(un->un_dirvp, &vp, &cn, vap); 1117762e6b85SEivind Eklund if (cn.cn_flags & HASBUF) { 1118762e6b85SEivind Eklund zfree(namei_zone, cn.cn_pnbuf); 1119762e6b85SEivind Eklund cn.cn_flags &= ~HASBUF; 1120762e6b85SEivind Eklund } 11217be2d300SMike Smith vput(un->un_dirvp); 11227be2d300SMike Smith if (error) 1123df8bae1dSRodney W. Grimes return (error); 1124df8bae1dSRodney W. Grimes 11253a773ad0SPoul-Henning Kamp error = VOP_OPEN(vp, fmode, cred, p); 11262a31267eSMatthew Dillon if (error == 0 && vn_canvmio(vp) == TRUE) 11272a31267eSMatthew Dillon error = vfs_object_create(vp, p, cred); 11283a773ad0SPoul-Henning Kamp if (error) { 1129df8bae1dSRodney W. Grimes vput(vp); 1130df8bae1dSRodney W. Grimes return (error); 1131df8bae1dSRodney W. Grimes } 1132df8bae1dSRodney W. Grimes vp->v_writecount++; 1133df8bae1dSRodney W. Grimes *vpp = vp; 1134df8bae1dSRodney W. Grimes return (0); 1135df8bae1dSRodney W. Grimes } 1136df8bae1dSRodney W. Grimes 113780b301c3SPoul-Henning Kamp static int 1138df8bae1dSRodney W. Grimes union_vn_close(vp, fmode, cred, p) 1139df8bae1dSRodney W. Grimes struct vnode *vp; 1140df8bae1dSRodney W. Grimes int fmode; 1141df8bae1dSRodney W. Grimes struct ucred *cred; 1142df8bae1dSRodney W. Grimes struct proc *p; 1143df8bae1dSRodney W. Grimes { 1144996c772fSJohn Dyson 1145df8bae1dSRodney W. Grimes if (fmode & FWRITE) 1146df8bae1dSRodney W. Grimes --vp->v_writecount; 1147cf2455a3SBruce Evans return (VOP_CLOSE(vp, fmode, cred, p)); 1148df8bae1dSRodney W. Grimes } 1149df8bae1dSRodney W. Grimes 11502a31267eSMatthew Dillon #if 0 11512a31267eSMatthew Dillon 11522a31267eSMatthew Dillon /* 11532a31267eSMatthew Dillon * union_removed_upper: 11542a31267eSMatthew Dillon * 11552a31267eSMatthew Dillon * called with union_node unlocked. XXX 11562a31267eSMatthew Dillon */ 11572a31267eSMatthew Dillon 1158df8bae1dSRodney W. Grimes void 1159df8bae1dSRodney W. Grimes union_removed_upper(un) 1160df8bae1dSRodney W. Grimes struct union_node *un; 1161df8bae1dSRodney W. Grimes { 1162996c772fSJohn Dyson struct proc *p = curproc; /* XXX */ 1163b69aa7f1SKATO Takenori struct vnode **vpp; 1164df8bae1dSRodney W. Grimes 1165b69aa7f1SKATO Takenori /* 1166b69aa7f1SKATO Takenori * Do not set the uppervp to NULLVP. If lowervp is NULLVP, 11679758931dSKATO Takenori * union node will have neither uppervp nor lowervp. We remove 1168b69aa7f1SKATO Takenori * the union node from cache, so that it will not be referrenced. 1169b69aa7f1SKATO Takenori */ 1170df8bae1dSRodney W. Grimes union_newupper(un, NULLVP); 1171b69aa7f1SKATO Takenori if (un->un_dircache != 0) { 1172b69aa7f1SKATO Takenori for (vpp = un->un_dircache; *vpp != NULLVP; vpp++) 1173b69aa7f1SKATO Takenori vrele(*vpp); 1174b69aa7f1SKATO Takenori free(un->un_dircache, M_TEMP); 1175b69aa7f1SKATO Takenori un->un_dircache = 0; 1176b69aa7f1SKATO Takenori } 1177b69aa7f1SKATO Takenori 1178996c772fSJohn Dyson if (un->un_flags & UN_CACHED) { 1179996c772fSJohn Dyson un->un_flags &= ~UN_CACHED; 1180996c772fSJohn Dyson LIST_REMOVE(un, un_cache); 1181df8bae1dSRodney W. Grimes } 1182996c772fSJohn Dyson } 1183996c772fSJohn Dyson 1184996c772fSJohn Dyson #endif 1185996c772fSJohn Dyson 1186996c772fSJohn Dyson /* 1187996c772fSJohn Dyson * determine whether a whiteout is needed 1188996c772fSJohn Dyson * during a remove/rmdir operation. 1189996c772fSJohn Dyson */ 1190996c772fSJohn Dyson int 1191996c772fSJohn Dyson union_dowhiteout(un, cred, p) 1192996c772fSJohn Dyson struct union_node *un; 1193996c772fSJohn Dyson struct ucred *cred; 1194996c772fSJohn Dyson struct proc *p; 1195996c772fSJohn Dyson { 1196996c772fSJohn Dyson struct vattr va; 1197996c772fSJohn Dyson 1198996c772fSJohn Dyson if (un->un_lowervp != NULLVP) 1199996c772fSJohn Dyson return (1); 1200996c772fSJohn Dyson 1201996c772fSJohn Dyson if (VOP_GETATTR(un->un_uppervp, &va, cred, p) == 0 && 1202996c772fSJohn Dyson (va.va_flags & OPAQUE)) 1203996c772fSJohn Dyson return (1); 1204996c772fSJohn Dyson 1205996c772fSJohn Dyson return (0); 1206996c772fSJohn Dyson } 1207996c772fSJohn Dyson 1208996c772fSJohn Dyson static void 1209996c772fSJohn Dyson union_dircache_r(vp, vppp, cntp) 1210996c772fSJohn Dyson struct vnode *vp; 1211996c772fSJohn Dyson struct vnode ***vppp; 1212996c772fSJohn Dyson int *cntp; 1213996c772fSJohn Dyson { 1214996c772fSJohn Dyson struct union_node *un; 1215996c772fSJohn Dyson 1216996c772fSJohn Dyson if (vp->v_op != union_vnodeop_p) { 1217996c772fSJohn Dyson if (vppp) { 1218996c772fSJohn Dyson VREF(vp); 1219996c772fSJohn Dyson *(*vppp)++ = vp; 1220996c772fSJohn Dyson if (--(*cntp) == 0) 1221996c772fSJohn Dyson panic("union: dircache table too small"); 1222996c772fSJohn Dyson } else { 1223996c772fSJohn Dyson (*cntp)++; 1224996c772fSJohn Dyson } 1225996c772fSJohn Dyson 1226996c772fSJohn Dyson return; 1227996c772fSJohn Dyson } 1228996c772fSJohn Dyson 1229996c772fSJohn Dyson un = VTOUNION(vp); 1230996c772fSJohn Dyson if (un->un_uppervp != NULLVP) 1231996c772fSJohn Dyson union_dircache_r(un->un_uppervp, vppp, cntp); 1232996c772fSJohn Dyson if (un->un_lowervp != NULLVP) 1233996c772fSJohn Dyson union_dircache_r(un->un_lowervp, vppp, cntp); 1234996c772fSJohn Dyson } 1235996c772fSJohn Dyson 1236996c772fSJohn Dyson struct vnode * 1237996c772fSJohn Dyson union_dircache(vp, p) 1238996c772fSJohn Dyson struct vnode *vp; 1239996c772fSJohn Dyson struct proc *p; 1240996c772fSJohn Dyson { 1241996c772fSJohn Dyson int cnt; 1242996c772fSJohn Dyson struct vnode *nvp; 1243996c772fSJohn Dyson struct vnode **vpp; 1244996c772fSJohn Dyson struct vnode **dircache; 1245996c772fSJohn Dyson struct union_node *un; 1246996c772fSJohn Dyson int error; 1247996c772fSJohn Dyson 1248996c772fSJohn Dyson vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); 1249996c772fSJohn Dyson dircache = VTOUNION(vp)->un_dircache; 1250996c772fSJohn Dyson 1251996c772fSJohn Dyson nvp = NULLVP; 1252996c772fSJohn Dyson 12532a31267eSMatthew Dillon if (dircache == NULL) { 1254996c772fSJohn Dyson cnt = 0; 1255996c772fSJohn Dyson union_dircache_r(vp, 0, &cnt); 1256996c772fSJohn Dyson cnt++; 12572a31267eSMatthew Dillon dircache = malloc(cnt * sizeof(struct vnode *), 1258996c772fSJohn Dyson M_TEMP, M_WAITOK); 1259996c772fSJohn Dyson vpp = dircache; 1260996c772fSJohn Dyson union_dircache_r(vp, &vpp, &cnt); 1261996c772fSJohn Dyson *vpp = NULLVP; 1262996c772fSJohn Dyson vpp = dircache + 1; 1263996c772fSJohn Dyson } else { 1264996c772fSJohn Dyson vpp = dircache; 1265996c772fSJohn Dyson do { 1266996c772fSJohn Dyson if (*vpp++ == VTOUNION(vp)->un_uppervp) 1267996c772fSJohn Dyson break; 1268996c772fSJohn Dyson } while (*vpp != NULLVP); 1269996c772fSJohn Dyson } 1270996c772fSJohn Dyson 1271996c772fSJohn Dyson if (*vpp == NULLVP) 1272996c772fSJohn Dyson goto out; 1273996c772fSJohn Dyson 12742a31267eSMatthew Dillon /*vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY, p);*/ 12752a31267eSMatthew Dillon UDEBUG(("ALLOCVP-3 %p ref %d\n", *vpp, (*vpp ? (*vpp)->v_usecount : -99))); 1276996c772fSJohn Dyson VREF(*vpp); 12772a31267eSMatthew Dillon error = union_allocvp(&nvp, vp->v_mount, NULLVP, NULLVP, NULL, *vpp, NULLVP, 0); 12782a31267eSMatthew Dillon UDEBUG(("ALLOCVP-3B %p ref %d\n", nvp, (*vpp ? (*vpp)->v_usecount : -99))); 1279996c772fSJohn Dyson if (error) 1280996c772fSJohn Dyson goto out; 1281996c772fSJohn Dyson 1282996c772fSJohn Dyson VTOUNION(vp)->un_dircache = 0; 1283996c772fSJohn Dyson un = VTOUNION(nvp); 1284996c772fSJohn Dyson un->un_dircache = dircache; 1285996c772fSJohn Dyson 1286996c772fSJohn Dyson out: 1287996c772fSJohn Dyson VOP_UNLOCK(vp, 0, p); 1288996c772fSJohn Dyson return (nvp); 1289df8bae1dSRodney W. Grimes } 12908c14bf40SPeter Wemm 12918c14bf40SPeter Wemm /* 12928c14bf40SPeter Wemm * Module glue to remove #ifdef UNION from vfs_syscalls.c 12938c14bf40SPeter Wemm */ 12948c14bf40SPeter Wemm static int 12958c14bf40SPeter Wemm union_dircheck(struct proc *p, struct vnode **vp, struct file *fp) 12968c14bf40SPeter Wemm { 12978c14bf40SPeter Wemm int error = 0; 12988c14bf40SPeter Wemm 12998c14bf40SPeter Wemm if ((*vp)->v_op == union_vnodeop_p) { 13008c14bf40SPeter Wemm struct vnode *lvp; 13018c14bf40SPeter Wemm 13028c14bf40SPeter Wemm lvp = union_dircache(*vp, p); 13038c14bf40SPeter Wemm if (lvp != NULLVP) { 13048c14bf40SPeter Wemm struct vattr va; 13058c14bf40SPeter Wemm 13068c14bf40SPeter Wemm /* 13078c14bf40SPeter Wemm * If the directory is opaque, 13088c14bf40SPeter Wemm * then don't show lower entries 13098c14bf40SPeter Wemm */ 13108c14bf40SPeter Wemm error = VOP_GETATTR(*vp, &va, fp->f_cred, p); 13118c14bf40SPeter Wemm if (va.va_flags & OPAQUE) { 13128c14bf40SPeter Wemm vput(lvp); 13138c14bf40SPeter Wemm lvp = NULL; 13148c14bf40SPeter Wemm } 13158c14bf40SPeter Wemm } 13168c14bf40SPeter Wemm 13178c14bf40SPeter Wemm if (lvp != NULLVP) { 13188c14bf40SPeter Wemm error = VOP_OPEN(lvp, FREAD, fp->f_cred, p); 13192a31267eSMatthew Dillon if (error == 0 && vn_canvmio(lvp) == TRUE) 13202a31267eSMatthew Dillon error = vfs_object_create(lvp, p, fp->f_cred); 13218c14bf40SPeter Wemm if (error) { 13228c14bf40SPeter Wemm vput(lvp); 13238c14bf40SPeter Wemm return (error); 13248c14bf40SPeter Wemm } 13258c14bf40SPeter Wemm VOP_UNLOCK(lvp, 0, p); 13268c14bf40SPeter Wemm fp->f_data = (caddr_t) lvp; 13278c14bf40SPeter Wemm fp->f_offset = 0; 13288c14bf40SPeter Wemm error = vn_close(*vp, FREAD, fp->f_cred, p); 13298c14bf40SPeter Wemm if (error) 13308c14bf40SPeter Wemm return (error); 13318c14bf40SPeter Wemm *vp = lvp; 13328c14bf40SPeter Wemm return -1; /* goto unionread */ 13338c14bf40SPeter Wemm } 13348c14bf40SPeter Wemm } 13358c14bf40SPeter Wemm return error; 13368c14bf40SPeter Wemm } 13378c14bf40SPeter Wemm 1338c25ded31SBruce Evans static int 1339c25ded31SBruce Evans union_modevent(module_t mod, int type, void *data) 13408c14bf40SPeter Wemm { 13418c14bf40SPeter Wemm switch (type) { 13428c14bf40SPeter Wemm case MOD_LOAD: 13438c14bf40SPeter Wemm union_dircheckp = union_dircheck; 13448c14bf40SPeter Wemm break; 13458c14bf40SPeter Wemm case MOD_UNLOAD: 13468c14bf40SPeter Wemm union_dircheckp = NULL; 13478c14bf40SPeter Wemm break; 13488c14bf40SPeter Wemm default: 13498c14bf40SPeter Wemm break; 13508c14bf40SPeter Wemm } 13518c14bf40SPeter Wemm return 0; 13528c14bf40SPeter Wemm } 13532a31267eSMatthew Dillon 13548c14bf40SPeter Wemm static moduledata_t union_mod = { 13558c14bf40SPeter Wemm "union_dircheck", 13568c14bf40SPeter Wemm union_modevent, 13578c14bf40SPeter Wemm NULL 13588c14bf40SPeter Wemm }; 13592a31267eSMatthew Dillon 13608c14bf40SPeter Wemm DECLARE_MODULE(union_dircheck, union_mod, SI_SUB_VFS, SI_ORDER_ANY); 1361