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