xref: /original-bsd/sys/miscfs/union/union_subr.c (revision a753c0a3)
1bfade366Spendry /*
2bfade366Spendry  * Copyright (c) 1994 Jan-Simon Pendry
3bfade366Spendry  * Copyright (c) 1994
4bfade366Spendry  *	The Regents of the University of California.  All rights reserved.
5bfade366Spendry  *
6bfade366Spendry  * This code is derived from software contributed to Berkeley by
7bfade366Spendry  * Jan-Simon Pendry.
8bfade366Spendry  *
9bfade366Spendry  * %sccs.include.redist.c%
10bfade366Spendry  *
11*a753c0a3Spendry  *	@(#)union_subr.c	8.20 (Berkeley) 05/20/95
12bfade366Spendry  */
13bfade366Spendry 
14bfade366Spendry #include <sys/param.h>
15bfade366Spendry #include <sys/systm.h>
16bfade366Spendry #include <sys/time.h>
17bfade366Spendry #include <sys/kernel.h>
18bfade366Spendry #include <sys/vnode.h>
19bfade366Spendry #include <sys/namei.h>
20bfade366Spendry #include <sys/malloc.h>
219979a570Spendry #include <sys/file.h>
22ab1360c4Spendry #include <sys/filedesc.h>
2313cf9c7cSpendry #include <sys/queue.h>
24ea3beb6cSpendry #include <sys/mount.h>
2590a04b0bSpendry #include <sys/stat.h>
26e2214d42Spendry #include <vm/vm.h>		/* for vnode_pager_setsize */
27f6d85c23Spendry #include <miscfs/union/union.h>
28bfade366Spendry 
29165faeebSpendry #ifdef DIAGNOSTIC
30165faeebSpendry #include <sys/proc.h>
31165faeebSpendry #endif
32165faeebSpendry 
3313cf9c7cSpendry /* must be power of two, otherwise change UNION_HASH() */
3413cf9c7cSpendry #define NHASH 32
3513cf9c7cSpendry 
3613cf9c7cSpendry /* unsigned int ... */
3713cf9c7cSpendry #define UNION_HASH(u, l) \
3813cf9c7cSpendry 	(((((unsigned long) (u)) + ((unsigned long) l)) >> 8) & (NHASH-1))
3913cf9c7cSpendry 
LIST_HEAD(unhead,union_node)4013cf9c7cSpendry static LIST_HEAD(unhead, union_node) unhead[NHASH];
4113cf9c7cSpendry static int unvplock[NHASH];
42bfade366Spendry 
43bfade366Spendry int
44bfade366Spendry union_init()
45bfade366Spendry {
4613cf9c7cSpendry 	int i;
47bfade366Spendry 
4813cf9c7cSpendry 	for (i = 0; i < NHASH; i++)
4913cf9c7cSpendry 		LIST_INIT(&unhead[i]);
5013cf9c7cSpendry 	bzero((caddr_t) unvplock, sizeof(unvplock));
5113cf9c7cSpendry }
5213cf9c7cSpendry 
5313cf9c7cSpendry static int
union_list_lock(ix)5413cf9c7cSpendry union_list_lock(ix)
5513cf9c7cSpendry 	int ix;
5613cf9c7cSpendry {
5713cf9c7cSpendry 
5813cf9c7cSpendry 	if (unvplock[ix] & UN_LOCKED) {
5913cf9c7cSpendry 		unvplock[ix] |= UN_WANT;
6013cf9c7cSpendry 		sleep((caddr_t) &unvplock[ix], PINOD);
6113cf9c7cSpendry 		return (1);
6213cf9c7cSpendry 	}
6313cf9c7cSpendry 
6413cf9c7cSpendry 	unvplock[ix] |= UN_LOCKED;
6513cf9c7cSpendry 
6613cf9c7cSpendry 	return (0);
67bfade366Spendry }
68bfade366Spendry 
69957f32cdSpendry static void
union_list_unlock(ix)7013cf9c7cSpendry union_list_unlock(ix)
7113cf9c7cSpendry 	int ix;
72957f32cdSpendry {
73957f32cdSpendry 
7413cf9c7cSpendry 	unvplock[ix] &= ~UN_LOCKED;
7513cf9c7cSpendry 
7613cf9c7cSpendry 	if (unvplock[ix] & UN_WANT) {
7713cf9c7cSpendry 		unvplock[ix] &= ~UN_WANT;
7813cf9c7cSpendry 		wakeup((caddr_t) &unvplock[ix]);
79957f32cdSpendry 	}
80957f32cdSpendry }
8113cf9c7cSpendry 
8213cf9c7cSpendry void
union_updatevp(un,uppervp,lowervp)8313cf9c7cSpendry union_updatevp(un, uppervp, lowervp)
8413cf9c7cSpendry 	struct union_node *un;
8513cf9c7cSpendry 	struct vnode *uppervp;
8613cf9c7cSpendry 	struct vnode *lowervp;
8713cf9c7cSpendry {
8813cf9c7cSpendry 	int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);
8913cf9c7cSpendry 	int nhash = UNION_HASH(uppervp, lowervp);
900b526f5eSpendry 	int docache = (lowervp != NULLVP || uppervp != NULLVP);
9177514422Spendry 	int lhash, hhash, uhash;
9213cf9c7cSpendry 
9313cf9c7cSpendry 	/*
9413cf9c7cSpendry 	 * Ensure locking is ordered from lower to higher
9513cf9c7cSpendry 	 * to avoid deadlocks.
9613cf9c7cSpendry 	 */
9713cf9c7cSpendry 	if (nhash < ohash) {
9877514422Spendry 		lhash = nhash;
9977514422Spendry 		uhash = ohash;
10077514422Spendry 	} else {
10177514422Spendry 		lhash = ohash;
10277514422Spendry 		uhash = nhash;
10313cf9c7cSpendry 	}
10413cf9c7cSpendry 
10577514422Spendry 	if (lhash != uhash)
10677514422Spendry 		while (union_list_lock(lhash))
10713cf9c7cSpendry 			continue;
10813cf9c7cSpendry 
10977514422Spendry 	while (union_list_lock(uhash))
11013cf9c7cSpendry 		continue;
11113cf9c7cSpendry 
1120b526f5eSpendry 	if (ohash != nhash || !docache) {
1130b526f5eSpendry 		if (un->un_flags & UN_CACHED) {
1140b526f5eSpendry 			un->un_flags &= ~UN_CACHED;
1151be9b708Spendry 			LIST_REMOVE(un, un_cache);
11613cf9c7cSpendry 		}
1170b526f5eSpendry 	}
1180b526f5eSpendry 
1190b526f5eSpendry 	if (ohash != nhash)
1200b526f5eSpendry 		union_list_unlock(ohash);
12113cf9c7cSpendry 
12213cf9c7cSpendry 	if (un->un_lowervp != lowervp) {
12313cf9c7cSpendry 		if (un->un_lowervp) {
12413cf9c7cSpendry 			vrele(un->un_lowervp);
12513cf9c7cSpendry 			if (un->un_path) {
12613cf9c7cSpendry 				free(un->un_path, M_TEMP);
12713cf9c7cSpendry 				un->un_path = 0;
12813cf9c7cSpendry 			}
12913cf9c7cSpendry 			if (un->un_dirvp) {
13013cf9c7cSpendry 				vrele(un->un_dirvp);
13113cf9c7cSpendry 				un->un_dirvp = NULLVP;
13213cf9c7cSpendry 			}
13313cf9c7cSpendry 		}
13413cf9c7cSpendry 		un->un_lowervp = lowervp;
135e2214d42Spendry 		un->un_lowersz = VNOVAL;
13613cf9c7cSpendry 	}
13713cf9c7cSpendry 
13813cf9c7cSpendry 	if (un->un_uppervp != uppervp) {
13913cf9c7cSpendry 		if (un->un_uppervp)
14013cf9c7cSpendry 			vrele(un->un_uppervp);
14113cf9c7cSpendry 
14213cf9c7cSpendry 		un->un_uppervp = uppervp;
143e2214d42Spendry 		un->un_uppersz = VNOVAL;
14413cf9c7cSpendry 	}
14513cf9c7cSpendry 
1460b526f5eSpendry 	if (docache && (ohash != nhash)) {
14713cf9c7cSpendry 		LIST_INSERT_HEAD(&unhead[nhash], un, un_cache);
1480b526f5eSpendry 		un->un_flags |= UN_CACHED;
1490b526f5eSpendry 	}
15013cf9c7cSpendry 
15113cf9c7cSpendry 	union_list_unlock(nhash);
15213cf9c7cSpendry }
15313cf9c7cSpendry 
15413cf9c7cSpendry void
union_newlower(un,lowervp)15513cf9c7cSpendry union_newlower(un, lowervp)
15613cf9c7cSpendry 	struct union_node *un;
15713cf9c7cSpendry 	struct vnode *lowervp;
15813cf9c7cSpendry {
15913cf9c7cSpendry 
16013cf9c7cSpendry 	union_updatevp(un, un->un_uppervp, lowervp);
16113cf9c7cSpendry }
16213cf9c7cSpendry 
16313cf9c7cSpendry void
union_newupper(un,uppervp)16413cf9c7cSpendry union_newupper(un, uppervp)
16513cf9c7cSpendry 	struct union_node *un;
16613cf9c7cSpendry 	struct vnode *uppervp;
16713cf9c7cSpendry {
16813cf9c7cSpendry 
16913cf9c7cSpendry 	union_updatevp(un, uppervp, un->un_lowervp);
170957f32cdSpendry }
171957f32cdSpendry 
172bfade366Spendry /*
173e2214d42Spendry  * Keep track of size changes in the underlying vnodes.
174e2214d42Spendry  * If the size changes, then callback to the vm layer
175e2214d42Spendry  * giving priority to the upper layer size.
176e2214d42Spendry  */
177e2214d42Spendry void
union_newsize(vp,uppersz,lowersz)178e2214d42Spendry union_newsize(vp, uppersz, lowersz)
179e2214d42Spendry 	struct vnode *vp;
180e2214d42Spendry 	off_t uppersz, lowersz;
181e2214d42Spendry {
182e2214d42Spendry 	struct union_node *un;
183e2214d42Spendry 	off_t sz;
184e2214d42Spendry 
185e2214d42Spendry 	/* only interested in regular files */
186e2214d42Spendry 	if (vp->v_type != VREG)
187e2214d42Spendry 		return;
188e2214d42Spendry 
189e2214d42Spendry 	un = VTOUNION(vp);
190e2214d42Spendry 	sz = VNOVAL;
191e2214d42Spendry 
192e2214d42Spendry 	if ((uppersz != VNOVAL) && (un->un_uppersz != uppersz)) {
193e2214d42Spendry 		un->un_uppersz = uppersz;
194e2214d42Spendry 		if (sz == VNOVAL)
195e2214d42Spendry 			sz = un->un_uppersz;
196e2214d42Spendry 	}
197e2214d42Spendry 
198e2214d42Spendry 	if ((lowersz != VNOVAL) && (un->un_lowersz != lowersz)) {
199e2214d42Spendry 		un->un_lowersz = lowersz;
200e2214d42Spendry 		if (sz == VNOVAL)
201e2214d42Spendry 			sz = un->un_lowersz;
202e2214d42Spendry 	}
203e2214d42Spendry 
204e2214d42Spendry 	if (sz != VNOVAL) {
205e2214d42Spendry #ifdef UNION_DIAGNOSTIC
206e2214d42Spendry 		printf("union: %s size now %ld\n",
207e2214d42Spendry 			uppersz != VNOVAL ? "upper" : "lower", (long) sz);
208e2214d42Spendry #endif
209e2214d42Spendry 		vnode_pager_setsize(vp, sz);
210e2214d42Spendry 	}
211e2214d42Spendry }
212e2214d42Spendry 
213e2214d42Spendry /*
214bfade366Spendry  * allocate a union_node/vnode pair.  the vnode is
2154fa8e650Spendry  * referenced and locked.  the new vnode is returned
2164fa8e650Spendry  * via (vpp).  (mp) is the mountpoint of the union filesystem,
2174fa8e650Spendry  * (dvp) is the parent directory where the upper layer object
2184fa8e650Spendry  * should exist (but doesn't) and (cnp) is the componentname
2194fa8e650Spendry  * information which is partially copied to allow the upper
2204fa8e650Spendry  * layer object to be created at a later time.  (uppervp)
2214fa8e650Spendry  * and (lowervp) reference the upper and lower layer objects
2224fa8e650Spendry  * being mapped.  either, but not both, can be nil.
223957f32cdSpendry  * if supplied, (uppervp) is locked.
224ab1360c4Spendry  * the reference is either maintained in the new union_node
225ab1360c4Spendry  * object which is allocated, or they are vrele'd.
226bfade366Spendry  *
227bfade366Spendry  * all union_nodes are maintained on a singly-linked
228bfade366Spendry  * list.  new nodes are only allocated when they cannot
229bfade366Spendry  * be found on this list.  entries on the list are
230bfade366Spendry  * removed when the vfs reclaim entry is called.
231bfade366Spendry  *
232bfade366Spendry  * a single lock is kept for the entire list.  this is
233bfade366Spendry  * needed because the getnewvnode() function can block
234bfade366Spendry  * waiting for a vnode to become free, in which case there
235bfade366Spendry  * may be more than one process trying to get the same
236bfade366Spendry  * vnode.  this lock is only taken if we are going to
237bfade366Spendry  * call getnewvnode, since the kernel itself is single-threaded.
238bfade366Spendry  *
239bfade366Spendry  * if an entry is found on the list, then call vget() to
240bfade366Spendry  * take a reference.  this is done because there may be
241bfade366Spendry  * zero references to it and so it needs to removed from
242bfade366Spendry  * the vnode free list.
243bfade366Spendry  */
244bfade366Spendry int
union_allocvp(vpp,mp,undvp,dvp,cnp,uppervp,lowervp,docache)245b6a23b57Spendry union_allocvp(vpp, mp, undvp, dvp, cnp, uppervp, lowervp, docache)
246bfade366Spendry 	struct vnode **vpp;
247bfade366Spendry 	struct mount *mp;
24850230300Spendry 	struct vnode *undvp;		/* parent union vnode */
249bfade366Spendry 	struct vnode *dvp;		/* may be null */
250bfade366Spendry 	struct componentname *cnp;	/* may be null */
251bfade366Spendry 	struct vnode *uppervp;		/* may be null */
252bfade366Spendry 	struct vnode *lowervp;		/* may be null */
253b6a23b57Spendry 	int docache;
254bfade366Spendry {
255bfade366Spendry 	int error;
256bfade366Spendry 	struct union_node *un;
257bfade366Spendry 	struct union_node **pp;
25813cf9c7cSpendry 	struct vnode *xlowervp = NULLVP;
259ea3beb6cSpendry 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
26013cf9c7cSpendry 	int hash;
261ea3beb6cSpendry 	int vflag;
26213cf9c7cSpendry 	int try;
2634fa8e650Spendry 
26413cf9c7cSpendry 	if (uppervp == NULLVP && lowervp == NULLVP)
2654fa8e650Spendry 		panic("union: unidentifiable allocation");
2664fa8e650Spendry 
2674fa8e650Spendry 	if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) {
2684fa8e650Spendry 		xlowervp = lowervp;
26913cf9c7cSpendry 		lowervp = NULLVP;
2704fa8e650Spendry 	}
271bfade366Spendry 
272ea3beb6cSpendry 	/* detect the root vnode (and aliases) */
273ea3beb6cSpendry 	vflag = 0;
274ea3beb6cSpendry 	if ((uppervp == um->um_uppervp) &&
275ea3beb6cSpendry 	    ((lowervp == NULLVP) || lowervp == um->um_lowervp)) {
276ea3beb6cSpendry 		if (lowervp == NULLVP) {
277ea3beb6cSpendry 			lowervp = um->um_lowervp;
278d351e58bSpendry 			if (lowervp != NULLVP)
279ea3beb6cSpendry 				VREF(lowervp);
280ea3beb6cSpendry 		}
281ea3beb6cSpendry 		vflag = VROOT;
282ea3beb6cSpendry 	}
283ea3beb6cSpendry 
284bfade366Spendry loop:
285b6a23b57Spendry 	if (!docache) {
286b6a23b57Spendry 		un = 0;
287b6a23b57Spendry 	} else for (try = 0; try < 3; try++) {
28813cf9c7cSpendry 		switch (try) {
28913cf9c7cSpendry 		case 0:
29013cf9c7cSpendry 			if (lowervp == NULLVP)
29113cf9c7cSpendry 				continue;
29213cf9c7cSpendry 			hash = UNION_HASH(uppervp, lowervp);
29313cf9c7cSpendry 			break;
29413cf9c7cSpendry 
29513cf9c7cSpendry 		case 1:
29613cf9c7cSpendry 			if (uppervp == NULLVP)
29713cf9c7cSpendry 				continue;
29813cf9c7cSpendry 			hash = UNION_HASH(uppervp, NULLVP);
29913cf9c7cSpendry 			break;
30013cf9c7cSpendry 
30113cf9c7cSpendry 		case 2:
30213cf9c7cSpendry 			if (lowervp == NULLVP)
30313cf9c7cSpendry 				continue;
30413cf9c7cSpendry 			hash = UNION_HASH(NULLVP, lowervp);
305957f32cdSpendry 			break;
306957f32cdSpendry 		}
30713cf9c7cSpendry 
30813cf9c7cSpendry 		while (union_list_lock(hash))
30913cf9c7cSpendry 			continue;
31013cf9c7cSpendry 
31113cf9c7cSpendry 		for (un = unhead[hash].lh_first; un != 0;
31213cf9c7cSpendry 					un = un->un_cache.le_next) {
31313cf9c7cSpendry 			if ((un->un_lowervp == lowervp ||
31413cf9c7cSpendry 			     un->un_lowervp == NULLVP) &&
31513cf9c7cSpendry 			    (un->un_uppervp == uppervp ||
31613cf9c7cSpendry 			     un->un_uppervp == NULLVP) &&
31713cf9c7cSpendry 			    (UNIONTOV(un)->v_mount == mp)) {
31828a12a76Smckusick 				if (vget(UNIONTOV(un), 0,
31928a12a76Smckusick 				    cnp ? cnp->cn_proc : NULL)) {
32013cf9c7cSpendry 					union_list_unlock(hash);
32113cf9c7cSpendry 					goto loop;
32213cf9c7cSpendry 				}
32313cf9c7cSpendry 				break;
32413cf9c7cSpendry 			}
32513cf9c7cSpendry 		}
32613cf9c7cSpendry 
32713cf9c7cSpendry 		union_list_unlock(hash);
32813cf9c7cSpendry 
32913cf9c7cSpendry 		if (un)
33013cf9c7cSpendry 			break;
331957f32cdSpendry 	}
332957f32cdSpendry 
333957f32cdSpendry 	if (un) {
334957f32cdSpendry 		/*
335957f32cdSpendry 		 * Obtain a lock on the union_node.
336957f32cdSpendry 		 * uppervp is locked, though un->un_uppervp
337957f32cdSpendry 		 * may not be.  this doesn't break the locking
338957f32cdSpendry 		 * hierarchy since in the case that un->un_uppervp
339957f32cdSpendry 		 * is not yet locked it will be vrele'd and replaced
340957f32cdSpendry 		 * with uppervp.
341957f32cdSpendry 		 */
342957f32cdSpendry 
343957f32cdSpendry 		if ((dvp != NULLVP) && (uppervp == dvp)) {
344957f32cdSpendry 			/*
345957f32cdSpendry 			 * Access ``.'', so (un) will already
346957f32cdSpendry 			 * be locked.  Since this process has
347957f32cdSpendry 			 * the lock on (uppervp) no other
348957f32cdSpendry 			 * process can hold the lock on (un).
349957f32cdSpendry 			 */
350957f32cdSpendry #ifdef DIAGNOSTIC
351957f32cdSpendry 			if ((un->un_flags & UN_LOCKED) == 0)
352957f32cdSpendry 				panic("union: . not locked");
353957f32cdSpendry 			else if (curproc && un->un_pid != curproc->p_pid &&
354957f32cdSpendry 				    un->un_pid > -1 && curproc->p_pid > -1)
355957f32cdSpendry 				panic("union: allocvp not lock owner");
356957f32cdSpendry #endif
357957f32cdSpendry 		} else {
358957f32cdSpendry 			if (un->un_flags & UN_LOCKED) {
359957f32cdSpendry 				vrele(UNIONTOV(un));
360957f32cdSpendry 				un->un_flags |= UN_WANT;
361957f32cdSpendry 				sleep((caddr_t) &un->un_flags, PINOD);
362957f32cdSpendry 				goto loop;
363957f32cdSpendry 			}
364957f32cdSpendry 			un->un_flags |= UN_LOCKED;
365957f32cdSpendry 
366957f32cdSpendry #ifdef DIAGNOSTIC
367957f32cdSpendry 			if (curproc)
368957f32cdSpendry 				un->un_pid = curproc->p_pid;
369957f32cdSpendry 			else
370957f32cdSpendry 				un->un_pid = -1;
371957f32cdSpendry #endif
372957f32cdSpendry 		}
373957f32cdSpendry 
374957f32cdSpendry 		/*
375957f32cdSpendry 		 * At this point, the union_node is locked,
376957f32cdSpendry 		 * un->un_uppervp may not be locked, and uppervp
377957f32cdSpendry 		 * is locked or nil.
378957f32cdSpendry 		 */
3794f59f7deSpendry 
3804f59f7deSpendry 		/*
3814f59f7deSpendry 		 * Save information about the upper layer.
3824f59f7deSpendry 		 */
383165faeebSpendry 		if (uppervp != un->un_uppervp) {
38413cf9c7cSpendry 			union_newupper(un, uppervp);
385ab1360c4Spendry 		} else if (uppervp) {
386ab1360c4Spendry 			vrele(uppervp);
387165faeebSpendry 		}
3884f59f7deSpendry 
389957f32cdSpendry 		if (un->un_uppervp) {
390957f32cdSpendry 			un->un_flags |= UN_ULOCK;
391957f32cdSpendry 			un->un_flags &= ~UN_KLOCK;
392957f32cdSpendry 		}
393957f32cdSpendry 
3944f59f7deSpendry 		/*
3954f59f7deSpendry 		 * Save information about the lower layer.
3964f59f7deSpendry 		 * This needs to keep track of pathname
3974f59f7deSpendry 		 * and directory information which union_vn_create
3984f59f7deSpendry 		 * might need.
3994f59f7deSpendry 		 */
400165faeebSpendry 		if (lowervp != un->un_lowervp) {
40113cf9c7cSpendry 			union_newlower(un, lowervp);
4024ba124f7Spendry 			if (cnp && (lowervp != NULLVP)) {
4034f59f7deSpendry 				un->un_hash = cnp->cn_hash;
4044f59f7deSpendry 				un->un_path = malloc(cnp->cn_namelen+1,
4054f59f7deSpendry 						M_TEMP, M_WAITOK);
4064f59f7deSpendry 				bcopy(cnp->cn_nameptr, un->un_path,
4074f59f7deSpendry 						cnp->cn_namelen);
4084f59f7deSpendry 				un->un_path[cnp->cn_namelen] = '\0';
4094f59f7deSpendry 				VREF(dvp);
4104f59f7deSpendry 				un->un_dirvp = dvp;
4114f59f7deSpendry 			}
412ab1360c4Spendry 		} else if (lowervp) {
413ab1360c4Spendry 			vrele(lowervp);
414165faeebSpendry 		}
415165faeebSpendry 		*vpp = UNIONTOV(un);
416bfade366Spendry 		return (0);
417bfade366Spendry 	}
418bfade366Spendry 
419b6a23b57Spendry 	if (docache) {
420bfade366Spendry 		/*
421bfade366Spendry 		 * otherwise lock the vp list while we call getnewvnode
422bfade366Spendry 		 * since that can block.
423bfade366Spendry 		 */
42413cf9c7cSpendry 		hash = UNION_HASH(uppervp, lowervp);
42513cf9c7cSpendry 
42613cf9c7cSpendry 		if (union_list_lock(hash))
427bfade366Spendry 			goto loop;
428b6a23b57Spendry 	}
429bfade366Spendry 
430bfade366Spendry 	error = getnewvnode(VT_UNION, mp, union_vnodeop_p, vpp);
431957f32cdSpendry 	if (error) {
432957f32cdSpendry 		if (uppervp) {
433957f32cdSpendry 			if (dvp == uppervp)
434957f32cdSpendry 				vrele(uppervp);
435957f32cdSpendry 			else
436957f32cdSpendry 				vput(uppervp);
437957f32cdSpendry 		}
438957f32cdSpendry 		if (lowervp)
439957f32cdSpendry 			vrele(lowervp);
440957f32cdSpendry 
441bfade366Spendry 		goto out;
442957f32cdSpendry 	}
443bfade366Spendry 
444bfade366Spendry 	MALLOC((*vpp)->v_data, void *, sizeof(struct union_node),
445bfade366Spendry 		M_TEMP, M_WAITOK);
446bfade366Spendry 
447ea3beb6cSpendry 	(*vpp)->v_flag |= vflag;
4484fa8e650Spendry 	if (uppervp)
4494fa8e650Spendry 		(*vpp)->v_type = uppervp->v_type;
4504fa8e650Spendry 	else
4514fa8e650Spendry 		(*vpp)->v_type = lowervp->v_type;
452bfade366Spendry 	un = VTOUNION(*vpp);
453165faeebSpendry 	un->un_vnode = *vpp;
454bfade366Spendry 	un->un_uppervp = uppervp;
455e2214d42Spendry 	un->un_uppersz = VNOVAL;
456bfade366Spendry 	un->un_lowervp = lowervp;
457e2214d42Spendry 	un->un_lowersz = VNOVAL;
45850230300Spendry 	un->un_pvp = undvp;
45950230300Spendry 	if (undvp != NULLVP)
46050230300Spendry 		VREF(undvp);
461b6a23b57Spendry 	un->un_dircache = 0;
462376f0aceSpendry 	un->un_openl = 0;
463957f32cdSpendry 	un->un_flags = UN_LOCKED;
464957f32cdSpendry 	if (un->un_uppervp)
465957f32cdSpendry 		un->un_flags |= UN_ULOCK;
466957f32cdSpendry #ifdef DIAGNOSTIC
467957f32cdSpendry 	if (curproc)
468957f32cdSpendry 		un->un_pid = curproc->p_pid;
469957f32cdSpendry 	else
470957f32cdSpendry 		un->un_pid = -1;
471957f32cdSpendry #endif
4724ba124f7Spendry 	if (cnp && (lowervp != NULLVP)) {
4734f59f7deSpendry 		un->un_hash = cnp->cn_hash;
474bfade366Spendry 		un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK);
475bfade366Spendry 		bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen);
476bfade366Spendry 		un->un_path[cnp->cn_namelen] = '\0';
4774fa8e650Spendry 		VREF(dvp);
4784fa8e650Spendry 		un->un_dirvp = dvp;
479bfade366Spendry 	} else {
4804f59f7deSpendry 		un->un_hash = 0;
481bfade366Spendry 		un->un_path = 0;
4824fa8e650Spendry 		un->un_dirvp = 0;
483bfade366Spendry 	}
484bfade366Spendry 
485b6a23b57Spendry 	if (docache) {
48613cf9c7cSpendry 		LIST_INSERT_HEAD(&unhead[hash], un, un_cache);
4870b526f5eSpendry 		un->un_flags |= UN_CACHED;
488b6a23b57Spendry 	}
489bfade366Spendry 
4904fa8e650Spendry 	if (xlowervp)
4914fa8e650Spendry 		vrele(xlowervp);
4924fa8e650Spendry 
493bfade366Spendry out:
494b6a23b57Spendry 	if (docache)
49513cf9c7cSpendry 		union_list_unlock(hash);
496bfade366Spendry 
497bfade366Spendry 	return (error);
498bfade366Spendry }
499bfade366Spendry 
500bfade366Spendry int
union_freevp(vp)501bfade366Spendry union_freevp(vp)
502bfade366Spendry 	struct vnode *vp;
503bfade366Spendry {
504bfade366Spendry 	struct union_node *un = VTOUNION(vp);
505bfade366Spendry 
5060b526f5eSpendry 	if (un->un_flags & UN_CACHED) {
5070b526f5eSpendry 		un->un_flags &= ~UN_CACHED;
5081be9b708Spendry 		LIST_REMOVE(un, un_cache);
5090b526f5eSpendry 	}
51013cf9c7cSpendry 
51150230300Spendry 	if (un->un_pvp != NULLVP)
51250230300Spendry 		vrele(un->un_pvp);
5130b526f5eSpendry 	if (un->un_uppervp != NULLVP)
51413cf9c7cSpendry 		vrele(un->un_uppervp);
5150b526f5eSpendry 	if (un->un_lowervp != NULLVP)
51613cf9c7cSpendry 		vrele(un->un_lowervp);
5170b526f5eSpendry 	if (un->un_dirvp != NULLVP)
51813cf9c7cSpendry 		vrele(un->un_dirvp);
51913cf9c7cSpendry 	if (un->un_path)
52013cf9c7cSpendry 		free(un->un_path, M_TEMP);
521bfade366Spendry 
522bfade366Spendry 	FREE(vp->v_data, M_TEMP);
523bfade366Spendry 	vp->v_data = 0;
52413cf9c7cSpendry 
525bfade366Spendry 	return (0);
526bfade366Spendry }
5279979a570Spendry 
5289979a570Spendry /*
5299979a570Spendry  * copyfile.  copy the vnode (fvp) to the vnode (tvp)
5309979a570Spendry  * using a sequence of reads and writes.  both (fvp)
5319979a570Spendry  * and (tvp) are locked on entry and exit.
5329979a570Spendry  */
5339979a570Spendry int
union_copyfile(fvp,tvp,cred,p)534e88f5f84Spendry union_copyfile(fvp, tvp, cred, p)
5359979a570Spendry 	struct vnode *fvp;
5369979a570Spendry 	struct vnode *tvp;
537e88f5f84Spendry 	struct ucred *cred;
538e88f5f84Spendry 	struct proc *p;
5399979a570Spendry {
5409979a570Spendry 	char *buf;
5419979a570Spendry 	struct uio uio;
5429979a570Spendry 	struct iovec iov;
5439979a570Spendry 	int error = 0;
5449979a570Spendry 
5459979a570Spendry 	/*
5469979a570Spendry 	 * strategy:
5479979a570Spendry 	 * allocate a buffer of size MAXBSIZE.
5489979a570Spendry 	 * loop doing reads and writes, keeping track
5499979a570Spendry 	 * of the current uio offset.
5509979a570Spendry 	 * give up at the first sign of trouble.
5519979a570Spendry 	 */
5529979a570Spendry 
5539979a570Spendry 	uio.uio_procp = p;
5549979a570Spendry 	uio.uio_segflg = UIO_SYSSPACE;
5559979a570Spendry 	uio.uio_offset = 0;
5569979a570Spendry 
55728a12a76Smckusick 	VOP_UNLOCK(fvp, 0, p);				/* XXX */
55816549c44Spendry 	VOP_LEASE(fvp, p, cred, LEASE_READ);
55928a12a76Smckusick 	vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p);	/* XXX */
56028a12a76Smckusick 	VOP_UNLOCK(tvp, 0, p);				/* XXX */
56116549c44Spendry 	VOP_LEASE(tvp, p, cred, LEASE_WRITE);
56228a12a76Smckusick 	vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p);	/* XXX */
5639979a570Spendry 
5649979a570Spendry 	buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
5659979a570Spendry 
5669979a570Spendry 	/* ugly loop follows... */
5679979a570Spendry 	do {
5689979a570Spendry 		off_t offset = uio.uio_offset;
5699979a570Spendry 
5709979a570Spendry 		uio.uio_iov = &iov;
5719979a570Spendry 		uio.uio_iovcnt = 1;
5729979a570Spendry 		iov.iov_base = buf;
5739979a570Spendry 		iov.iov_len = MAXBSIZE;
5749979a570Spendry 		uio.uio_resid = iov.iov_len;
5759979a570Spendry 		uio.uio_rw = UIO_READ;
5769979a570Spendry 		error = VOP_READ(fvp, &uio, 0, cred);
5779979a570Spendry 
5789979a570Spendry 		if (error == 0) {
5799979a570Spendry 			uio.uio_iov = &iov;
5809979a570Spendry 			uio.uio_iovcnt = 1;
5819979a570Spendry 			iov.iov_base = buf;
5829979a570Spendry 			iov.iov_len = MAXBSIZE - uio.uio_resid;
5839979a570Spendry 			uio.uio_offset = offset;
5849979a570Spendry 			uio.uio_rw = UIO_WRITE;
5859979a570Spendry 			uio.uio_resid = iov.iov_len;
5869979a570Spendry 
5879979a570Spendry 			if (uio.uio_resid == 0)
5889979a570Spendry 				break;
5899979a570Spendry 
5909979a570Spendry 			do {
5919979a570Spendry 				error = VOP_WRITE(tvp, &uio, 0, cred);
5929979a570Spendry 			} while ((uio.uio_resid > 0) && (error == 0));
5939979a570Spendry 		}
5949979a570Spendry 
5959979a570Spendry 	} while (error == 0);
5969979a570Spendry 
5979979a570Spendry 	free(buf, M_TEMP);
5989979a570Spendry 	return (error);
5999979a570Spendry }
6009979a570Spendry 
6019979a570Spendry /*
602e88f5f84Spendry  * (un) is assumed to be locked on entry and remains
603e88f5f84Spendry  * locked on exit.
604e88f5f84Spendry  */
605e88f5f84Spendry int
union_copyup(un,docopy,cred,p)606e88f5f84Spendry union_copyup(un, docopy, cred, p)
607e88f5f84Spendry 	struct union_node *un;
608e88f5f84Spendry 	int docopy;
609e88f5f84Spendry 	struct ucred *cred;
610e88f5f84Spendry 	struct proc *p;
611e88f5f84Spendry {
612e88f5f84Spendry 	int error;
613e88f5f84Spendry 	struct vnode *lvp, *uvp;
614e88f5f84Spendry 
615e88f5f84Spendry 	error = union_vn_create(&uvp, un, p);
616e88f5f84Spendry 	if (error)
617e88f5f84Spendry 		return (error);
618e88f5f84Spendry 
619e88f5f84Spendry 	/* at this point, uppervp is locked */
620e88f5f84Spendry 	union_newupper(un, uvp);
621e88f5f84Spendry 	un->un_flags |= UN_ULOCK;
622e88f5f84Spendry 
623e88f5f84Spendry 	lvp = un->un_lowervp;
624e88f5f84Spendry 
625e88f5f84Spendry 	if (docopy) {
626e88f5f84Spendry 		/*
627e88f5f84Spendry 		 * XX - should not ignore errors
628e88f5f84Spendry 		 * from VOP_CLOSE
629e88f5f84Spendry 		 */
63028a12a76Smckusick 		vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
631e88f5f84Spendry 		error = VOP_OPEN(lvp, FREAD, cred, p);
632e88f5f84Spendry 		if (error == 0) {
633e88f5f84Spendry 			error = union_copyfile(lvp, uvp, cred, p);
63428a12a76Smckusick 			VOP_UNLOCK(lvp, 0, p);
635c2dd7187Smckusick 			(void) VOP_CLOSE(lvp, FREAD, cred, p);
636e88f5f84Spendry 		}
637e88f5f84Spendry #ifdef UNION_DIAGNOSTIC
638e88f5f84Spendry 		if (error == 0)
639e88f5f84Spendry 			uprintf("union: copied up %s\n", un->un_path);
640e88f5f84Spendry #endif
641e88f5f84Spendry 
642e88f5f84Spendry 	}
643e88f5f84Spendry 	un->un_flags &= ~UN_ULOCK;
64428a12a76Smckusick 	VOP_UNLOCK(uvp, 0, p);
645e88f5f84Spendry 	union_vn_close(uvp, FWRITE, cred, p);
64628a12a76Smckusick 	vn_lock(uvp, LK_EXCLUSIVE | LK_RETRY, p);
647e88f5f84Spendry 	un->un_flags |= UN_ULOCK;
648e88f5f84Spendry 
649e88f5f84Spendry 	/*
650e88f5f84Spendry 	 * Subsequent IOs will go to the top layer, so
651e88f5f84Spendry 	 * call close on the lower vnode and open on the
652e88f5f84Spendry 	 * upper vnode to ensure that the filesystem keeps
653e88f5f84Spendry 	 * its references counts right.  This doesn't do
654e88f5f84Spendry 	 * the right thing with (cred) and (FREAD) though.
655e88f5f84Spendry 	 * Ignoring error returns is not right, either.
656e88f5f84Spendry 	 */
657e88f5f84Spendry 	if (error == 0) {
658e88f5f84Spendry 		int i;
659e88f5f84Spendry 
660e88f5f84Spendry 		for (i = 0; i < un->un_openl; i++) {
661c2dd7187Smckusick 			(void) VOP_CLOSE(lvp, FREAD, cred, p);
662e88f5f84Spendry 			(void) VOP_OPEN(uvp, FREAD, cred, p);
663e88f5f84Spendry 		}
664e88f5f84Spendry 		un->un_openl = 0;
665e88f5f84Spendry 	}
666e88f5f84Spendry 
667e88f5f84Spendry 	return (error);
668e88f5f84Spendry 
669e88f5f84Spendry }
670e88f5f84Spendry 
6714ba124f7Spendry static int
union_relookup(um,dvp,vpp,cnp,cn,path,pathlen)67216549c44Spendry union_relookup(um, dvp, vpp, cnp, cn, path, pathlen)
6734ba124f7Spendry 	struct union_mount *um;
6744ba124f7Spendry 	struct vnode *dvp;
6754ba124f7Spendry 	struct vnode **vpp;
6764ba124f7Spendry 	struct componentname *cnp;
6774ba124f7Spendry 	struct componentname *cn;
6784ba124f7Spendry 	char *path;
67916549c44Spendry 	int pathlen;
6804ba124f7Spendry {
6814ba124f7Spendry 	int error;
6824ba124f7Spendry 
6834ba124f7Spendry 	/*
6844ba124f7Spendry 	 * A new componentname structure must be faked up because
6854ba124f7Spendry 	 * there is no way to know where the upper level cnp came
6864ba124f7Spendry 	 * from or what it is being used for.  This must duplicate
6874ba124f7Spendry 	 * some of the work done by NDINIT, some of the work done
6884ba124f7Spendry 	 * by namei, some of the work done by lookup and some of
6894ba124f7Spendry 	 * the work done by VOP_LOOKUP when given a CREATE flag.
6904ba124f7Spendry 	 * Conclusion: Horrible.
6914ba124f7Spendry 	 *
6924ba124f7Spendry 	 * The pathname buffer will be FREEed by VOP_MKDIR.
6934ba124f7Spendry 	 */
69416549c44Spendry 	cn->cn_namelen = pathlen;
6954ba124f7Spendry 	cn->cn_pnbuf = malloc(cn->cn_namelen+1, M_NAMEI, M_WAITOK);
6964ba124f7Spendry 	bcopy(path, cn->cn_pnbuf, cn->cn_namelen);
6974ba124f7Spendry 	cn->cn_pnbuf[cn->cn_namelen] = '\0';
6984ba124f7Spendry 
6994ba124f7Spendry 	cn->cn_nameiop = CREATE;
7004ba124f7Spendry 	cn->cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
7014ba124f7Spendry 	cn->cn_proc = cnp->cn_proc;
7024ba124f7Spendry 	if (um->um_op == UNMNT_ABOVE)
7034ba124f7Spendry 		cn->cn_cred = cnp->cn_cred;
7044ba124f7Spendry 	else
7054ba124f7Spendry 		cn->cn_cred = um->um_cred;
7064ba124f7Spendry 	cn->cn_nameptr = cn->cn_pnbuf;
7074ba124f7Spendry 	cn->cn_hash = cnp->cn_hash;
7084ba124f7Spendry 	cn->cn_consume = cnp->cn_consume;
7094ba124f7Spendry 
7104ba124f7Spendry 	VREF(dvp);
7114ba124f7Spendry 	error = relookup(dvp, vpp, cn);
7124ba124f7Spendry 	if (!error)
7134ba124f7Spendry 		vrele(dvp);
7144ba124f7Spendry 
7154ba124f7Spendry 	return (error);
7164ba124f7Spendry }
7174ba124f7Spendry 
718e88f5f84Spendry /*
719ab1360c4Spendry  * Create a shadow directory in the upper layer.
720ab1360c4Spendry  * The new vnode is returned locked.
721ab1360c4Spendry  *
722ab1360c4Spendry  * (um) points to the union mount structure for access to the
723ab1360c4Spendry  * the mounting process's credentials.
724ab1360c4Spendry  * (dvp) is the directory in which to create the shadow directory.
725ab1360c4Spendry  * it is unlocked on entry and exit.
726ab1360c4Spendry  * (cnp) is the componentname to be created.
727ab1360c4Spendry  * (vpp) is the returned newly created shadow directory, which
728ab1360c4Spendry  * is returned locked.
729ab1360c4Spendry  */
730ab1360c4Spendry int
union_mkshadow(um,dvp,cnp,vpp)731ab1360c4Spendry union_mkshadow(um, dvp, cnp, vpp)
732ab1360c4Spendry 	struct union_mount *um;
733ab1360c4Spendry 	struct vnode *dvp;
734ab1360c4Spendry 	struct componentname *cnp;
735ab1360c4Spendry 	struct vnode **vpp;
736ab1360c4Spendry {
737ab1360c4Spendry 	int error;
738ab1360c4Spendry 	struct vattr va;
739ab1360c4Spendry 	struct proc *p = cnp->cn_proc;
740ab1360c4Spendry 	struct componentname cn;
741ab1360c4Spendry 
74216549c44Spendry 	error = union_relookup(um, dvp, vpp, cnp, &cn,
74316549c44Spendry 			cnp->cn_nameptr, cnp->cn_namelen);
7444ba124f7Spendry 	if (error)
745ab1360c4Spendry 		return (error);
746ab1360c4Spendry 
747ab1360c4Spendry 	if (*vpp) {
748ab1360c4Spendry 		VOP_ABORTOP(dvp, &cn);
74928a12a76Smckusick 		VOP_UNLOCK(dvp, 0, p);
750ab1360c4Spendry 		vrele(*vpp);
751ab1360c4Spendry 		*vpp = NULLVP;
752ab1360c4Spendry 		return (EEXIST);
753ab1360c4Spendry 	}
754ab1360c4Spendry 
7554ba124f7Spendry 	/*
7564ba124f7Spendry 	 * policy: when creating the shadow directory in the
7574ba124f7Spendry 	 * upper layer, create it owned by the user who did
7584ba124f7Spendry 	 * the mount, group from parent directory, and mode
7594ba124f7Spendry 	 * 777 modified by umask (ie mostly identical to the
7604ba124f7Spendry 	 * mkdir syscall).  (jsp, kb)
7614ba124f7Spendry 	 */
7624ba124f7Spendry 
763ab1360c4Spendry 	VATTR_NULL(&va);
764ab1360c4Spendry 	va.va_type = VDIR;
7656d0f5438Spendry 	va.va_mode = um->um_cmode;
766ab1360c4Spendry 
76716549c44Spendry 	/* VOP_LEASE: dvp is locked */
76816549c44Spendry 	VOP_LEASE(dvp, p, cn.cn_cred, LEASE_WRITE);
769ab1360c4Spendry 
770ab1360c4Spendry 	error = VOP_MKDIR(dvp, vpp, &cn, &va);
771ab1360c4Spendry 	return (error);
772ab1360c4Spendry }
773ab1360c4Spendry 
774ab1360c4Spendry /*
7754ba124f7Spendry  * Create a whiteout entry in the upper layer.
7764ba124f7Spendry  *
7774ba124f7Spendry  * (um) points to the union mount structure for access to the
7784ba124f7Spendry  * the mounting process's credentials.
7794ba124f7Spendry  * (dvp) is the directory in which to create the whiteout.
7804ba124f7Spendry  * it is locked on entry and exit.
7814ba124f7Spendry  * (cnp) is the componentname to be created.
7824ba124f7Spendry  */
7834ba124f7Spendry int
union_mkwhiteout(um,dvp,cnp,path)7844ba124f7Spendry union_mkwhiteout(um, dvp, cnp, path)
7854ba124f7Spendry 	struct union_mount *um;
7864ba124f7Spendry 	struct vnode *dvp;
7874ba124f7Spendry 	struct componentname *cnp;
7884ba124f7Spendry 	char *path;
7894ba124f7Spendry {
7904ba124f7Spendry 	int error;
7914ba124f7Spendry 	struct vattr va;
7924ba124f7Spendry 	struct proc *p = cnp->cn_proc;
793d21f5d14Spendry 	struct vnode *wvp;
7944ba124f7Spendry 	struct componentname cn;
7954ba124f7Spendry 
79628a12a76Smckusick 	VOP_UNLOCK(dvp, 0, p);
797d21f5d14Spendry 	error = union_relookup(um, dvp, &wvp, cnp, &cn, path, strlen(path));
79816549c44Spendry 	if (error) {
79928a12a76Smckusick 		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
8004ba124f7Spendry 		return (error);
80116549c44Spendry 	}
8024ba124f7Spendry 
803d21f5d14Spendry 	if (wvp) {
8044ba124f7Spendry 		VOP_ABORTOP(dvp, &cn);
8054ba124f7Spendry 		vrele(dvp);
806d21f5d14Spendry 		vrele(wvp);
8074ba124f7Spendry 		return (EEXIST);
8084ba124f7Spendry 	}
8094ba124f7Spendry 
81016549c44Spendry 	/* VOP_LEASE: dvp is locked */
81116549c44Spendry 	VOP_LEASE(dvp, p, p->p_ucred, LEASE_WRITE);
8124ba124f7Spendry 
8134ba124f7Spendry 	error = VOP_WHITEOUT(dvp, &cn, CREATE);
81416549c44Spendry 	if (error)
8154ba124f7Spendry 		VOP_ABORTOP(dvp, &cn);
8164ba124f7Spendry 
8174ba124f7Spendry 	vrele(dvp);
8184ba124f7Spendry 
8194ba124f7Spendry 	return (error);
8204ba124f7Spendry }
8214ba124f7Spendry 
8224ba124f7Spendry /*
8239979a570Spendry  * union_vn_create: creates and opens a new shadow file
8249979a570Spendry  * on the upper union layer.  this function is similar
8259979a570Spendry  * in spirit to calling vn_open but it avoids calling namei().
8269979a570Spendry  * the problem with calling namei is that a) it locks too many
8279979a570Spendry  * things, and b) it doesn't start at the "right" directory,
8289979a570Spendry  * whereas relookup is told where to start.
8299979a570Spendry  */
8309979a570Spendry int
union_vn_create(vpp,un,p)831ab1360c4Spendry union_vn_create(vpp, un, p)
8329979a570Spendry 	struct vnode **vpp;
8339979a570Spendry 	struct union_node *un;
8349979a570Spendry 	struct proc *p;
8359979a570Spendry {
8369979a570Spendry 	struct vnode *vp;
8379979a570Spendry 	struct ucred *cred = p->p_ucred;
8389979a570Spendry 	struct vattr vat;
8399979a570Spendry 	struct vattr *vap = &vat;
8409979a570Spendry 	int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL);
8419979a570Spendry 	int error;
842ab1360c4Spendry 	int cmode = UN_FILEMODE & ~p->p_fd->fd_cmask;
8439979a570Spendry 	char *cp;
8449979a570Spendry 	struct componentname cn;
8459979a570Spendry 
8469979a570Spendry 	*vpp = NULLVP;
8479979a570Spendry 
8486d0f5438Spendry 	/*
8496d0f5438Spendry 	 * Build a new componentname structure (for the same
8506d0f5438Spendry 	 * reasons outlines in union_mkshadow).
8516d0f5438Spendry 	 * The difference here is that the file is owned by
8526d0f5438Spendry 	 * the current user, rather than by the person who
8536d0f5438Spendry 	 * did the mount, since the current user needs to be
8546d0f5438Spendry 	 * able to write the file (that's why it is being
8556d0f5438Spendry 	 * copied in the first place).
8566d0f5438Spendry 	 */
8579979a570Spendry 	cn.cn_namelen = strlen(un->un_path);
8589979a570Spendry 	cn.cn_pnbuf = (caddr_t) malloc(cn.cn_namelen, M_NAMEI, M_WAITOK);
8599979a570Spendry 	bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1);
8609979a570Spendry 	cn.cn_nameiop = CREATE;
861f0e5a953Spendry 	cn.cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
8629979a570Spendry 	cn.cn_proc = p;
8639979a570Spendry 	cn.cn_cred = p->p_ucred;
8649979a570Spendry 	cn.cn_nameptr = cn.cn_pnbuf;
8654f59f7deSpendry 	cn.cn_hash = un->un_hash;
8669979a570Spendry 	cn.cn_consume = 0;
8679979a570Spendry 
8684f59f7deSpendry 	VREF(un->un_dirvp);
8699979a570Spendry 	if (error = relookup(un->un_dirvp, &vp, &cn))
8709979a570Spendry 		return (error);
8714f59f7deSpendry 	vrele(un->un_dirvp);
8724f59f7deSpendry 
873f0e5a953Spendry 	if (vp) {
874f0e5a953Spendry 		VOP_ABORTOP(un->un_dirvp, &cn);
875f0e5a953Spendry 		if (un->un_dirvp == vp)
876f0e5a953Spendry 			vrele(un->un_dirvp);
877f0e5a953Spendry 		else
878f0e5a953Spendry 			vput(un->un_dirvp);
879f0e5a953Spendry 		vrele(vp);
880f0e5a953Spendry 		return (EEXIST);
881f0e5a953Spendry 	}
882f0e5a953Spendry 
8836d0f5438Spendry 	/*
8846d0f5438Spendry 	 * Good - there was no race to create the file
8856d0f5438Spendry 	 * so go ahead and create it.  The permissions
8866d0f5438Spendry 	 * on the file will be 0666 modified by the
8876d0f5438Spendry 	 * current user's umask.  Access to the file, while
8886d0f5438Spendry 	 * it is unioned, will require access to the top *and*
8896d0f5438Spendry 	 * bottom files.  Access when not unioned will simply
8906d0f5438Spendry 	 * require access to the top-level file.
8916d0f5438Spendry 	 * TODO: confirm choice of access permissions.
8926d0f5438Spendry 	 */
8939979a570Spendry 	VATTR_NULL(vap);
8949979a570Spendry 	vap->va_type = VREG;
8959979a570Spendry 	vap->va_mode = cmode;
89616549c44Spendry 	VOP_LEASE(un->un_dirvp, p, cred, LEASE_WRITE);
897f0e5a953Spendry 	if (error = VOP_CREATE(un->un_dirvp, &vp, &cn, vap))
8989979a570Spendry 		return (error);
899f0e5a953Spendry 
900f0e5a953Spendry 	if (error = VOP_OPEN(vp, fmode, cred, p)) {
9019979a570Spendry 		vput(vp);
902f0e5a953Spendry 		return (error);
9039979a570Spendry 	}
9049979a570Spendry 
9059979a570Spendry 	vp->v_writecount++;
9069979a570Spendry 	*vpp = vp;
9079979a570Spendry 	return (0);
9089979a570Spendry }
9094f59f7deSpendry 
9104f59f7deSpendry int
union_vn_close(vp,fmode,cred,p)911376f0aceSpendry union_vn_close(vp, fmode, cred, p)
9124f59f7deSpendry 	struct vnode *vp;
9134f59f7deSpendry 	int fmode;
914376f0aceSpendry 	struct ucred *cred;
915376f0aceSpendry 	struct proc *p;
9164f59f7deSpendry {
9174ba124f7Spendry 
9184f59f7deSpendry 	if (fmode & FWRITE)
9194f59f7deSpendry 		--vp->v_writecount;
920c2dd7187Smckusick 	return (VOP_CLOSE(vp, fmode, cred, p));
9214f59f7deSpendry }
9224f59f7deSpendry 
9234f59f7deSpendry void
union_removed_upper(un)9244f59f7deSpendry union_removed_upper(un)
9254f59f7deSpendry 	struct union_node *un;
9264f59f7deSpendry {
92728a12a76Smckusick 	struct proc *p = curproc;	/* XXX */
9281be9b708Spendry 
92977514422Spendry 	union_newupper(un, NULLVP);
9301be9b708Spendry 	if (un->un_flags & UN_CACHED) {
9311be9b708Spendry 		un->un_flags &= ~UN_CACHED;
9321be9b708Spendry 		LIST_REMOVE(un, un_cache);
9331be9b708Spendry 	}
93477514422Spendry 
93577514422Spendry 	if (un->un_flags & UN_ULOCK) {
93677514422Spendry 		un->un_flags &= ~UN_ULOCK;
93728a12a76Smckusick 		VOP_UNLOCK(un->un_uppervp, 0, p);
93877514422Spendry 	}
9394f59f7deSpendry }
940376f0aceSpendry 
941b6a23b57Spendry #if 0
942376f0aceSpendry struct vnode *
943376f0aceSpendry union_lowervp(vp)
944376f0aceSpendry 	struct vnode *vp;
945376f0aceSpendry {
946376f0aceSpendry 	struct union_node *un = VTOUNION(vp);
947376f0aceSpendry 
948e88f5f84Spendry 	if ((un->un_lowervp != NULLVP) &&
949e88f5f84Spendry 	    (vp->v_type == un->un_lowervp->v_type)) {
950e88f5f84Spendry 		if (vget(un->un_lowervp, 0) == 0)
951e88f5f84Spendry 			return (un->un_lowervp);
952376f0aceSpendry 	}
953376f0aceSpendry 
954e88f5f84Spendry 	return (NULLVP);
955376f0aceSpendry }
956b6a23b57Spendry #endif
95790a04b0bSpendry 
95890a04b0bSpendry /*
95990a04b0bSpendry  * determine whether a whiteout is needed
96090a04b0bSpendry  * during a remove/rmdir operation.
96190a04b0bSpendry  */
96290a04b0bSpendry int
union_dowhiteout(un,cred,p)96390a04b0bSpendry union_dowhiteout(un, cred, p)
96490a04b0bSpendry 	struct union_node *un;
96590a04b0bSpendry 	struct ucred *cred;
96690a04b0bSpendry 	struct proc *p;
96790a04b0bSpendry {
96890a04b0bSpendry 	struct vattr va;
96990a04b0bSpendry 
97090a04b0bSpendry 	if (un->un_lowervp != NULLVP)
97190a04b0bSpendry 		return (1);
97290a04b0bSpendry 
97390a04b0bSpendry 	if (VOP_GETATTR(un->un_uppervp, &va, cred, p) == 0 &&
97490a04b0bSpendry 	    (va.va_flags & OPAQUE))
97590a04b0bSpendry 		return (1);
97690a04b0bSpendry 
97790a04b0bSpendry 	return (0);
97890a04b0bSpendry }
979b6a23b57Spendry 
980b6a23b57Spendry static void
union_dircache_r(vp,vppp,cntp)981b6a23b57Spendry union_dircache_r(vp, vppp, cntp)
982b6a23b57Spendry 	struct vnode *vp;
983b6a23b57Spendry 	struct vnode ***vppp;
984b6a23b57Spendry 	int *cntp;
985b6a23b57Spendry {
986b6a23b57Spendry 	struct union_node *un;
987b6a23b57Spendry 
988b6a23b57Spendry 	if (vp->v_op != union_vnodeop_p) {
989b6a23b57Spendry 		if (vppp) {
990b6a23b57Spendry 			VREF(vp);
991b6a23b57Spendry 			*(*vppp)++ = vp;
992b6a23b57Spendry 			if (--(*cntp) == 0)
993b6a23b57Spendry 				panic("union: dircache table too small");
994b6a23b57Spendry 		} else {
995b6a23b57Spendry 			(*cntp)++;
996b6a23b57Spendry 		}
997b6a23b57Spendry 
998b6a23b57Spendry 		return;
999b6a23b57Spendry 	}
1000b6a23b57Spendry 
1001b6a23b57Spendry 	un = VTOUNION(vp);
1002b6a23b57Spendry 	if (un->un_uppervp != NULLVP)
1003b6a23b57Spendry 		union_dircache_r(un->un_uppervp, vppp, cntp);
1004b6a23b57Spendry 	if (un->un_lowervp != NULLVP)
1005b6a23b57Spendry 		union_dircache_r(un->un_lowervp, vppp, cntp);
1006b6a23b57Spendry }
1007b6a23b57Spendry 
1008b6a23b57Spendry struct vnode *
union_dircache(vp,p)100928a12a76Smckusick union_dircache(vp, p)
1010b6a23b57Spendry 	struct vnode *vp;
101128a12a76Smckusick 	struct proc *p;
1012b6a23b57Spendry {
1013b6a23b57Spendry 	int cnt;
1014b6a23b57Spendry 	struct vnode *nvp;
1015b6a23b57Spendry 	struct vnode **vpp;
1016*a753c0a3Spendry 	struct vnode **dircache;
1017b6a23b57Spendry 	struct union_node *un;
1018b6a23b57Spendry 	int error;
1019b6a23b57Spendry 
1020*a753c0a3Spendry 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
1021*a753c0a3Spendry 	dircache = VTOUNION(vp)->un_dircache;
1022*a753c0a3Spendry 
1023*a753c0a3Spendry 	nvp = NULLVP;
1024*a753c0a3Spendry 
1025b6a23b57Spendry 	if (dircache == 0) {
1026b6a23b57Spendry 		cnt = 0;
1027b6a23b57Spendry 		union_dircache_r(vp, 0, &cnt);
1028b6a23b57Spendry 		cnt++;
1029b6a23b57Spendry 		dircache = (struct vnode **)
1030b6a23b57Spendry 				malloc(cnt * sizeof(struct vnode *),
1031b6a23b57Spendry 					M_TEMP, M_WAITOK);
1032b6a23b57Spendry 		vpp = dircache;
1033b6a23b57Spendry 		union_dircache_r(vp, &vpp, &cnt);
1034b6a23b57Spendry 		*vpp = NULLVP;
1035b6a23b57Spendry 		vpp = dircache + 1;
1036b6a23b57Spendry 	} else {
1037b6a23b57Spendry 		vpp = dircache;
1038b6a23b57Spendry 		do {
1039b6a23b57Spendry 			if (*vpp++ == VTOUNION(vp)->un_uppervp)
1040b6a23b57Spendry 				break;
1041b6a23b57Spendry 		} while (*vpp != NULLVP);
1042b6a23b57Spendry 	}
1043b6a23b57Spendry 
1044b6a23b57Spendry 	if (*vpp == NULLVP)
1045*a753c0a3Spendry 		goto out;
1046b6a23b57Spendry 
104728a12a76Smckusick 	vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY, p);
1048b6a23b57Spendry 	VREF(*vpp);
1049b6a23b57Spendry 	error = union_allocvp(&nvp, vp->v_mount, NULLVP, NULLVP, 0, *vpp, NULLVP, 0);
1050b6a23b57Spendry 	if (error)
1051*a753c0a3Spendry 		goto out;
1052*a753c0a3Spendry 
1053b6a23b57Spendry 	VTOUNION(vp)->un_dircache = 0;
1054b6a23b57Spendry 	un = VTOUNION(nvp);
1055b6a23b57Spendry 	un->un_dircache = dircache;
1056b6a23b57Spendry 
1057*a753c0a3Spendry out:
1058*a753c0a3Spendry 	VOP_UNLOCK(vp, 0, p);
1059b6a23b57Spendry 	return (nvp);
1060b6a23b57Spendry }
1061