xref: /freebsd/sys/fs/unionfs/union_subr.c (revision 996c772f581f5624846dcd8470ca6860c1678b7c)
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