xref: /csrg-svn/sys/miscfs/union/union_vnops.c (revision 69588)
165935Spendry /*
268590Spendry  * Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry.
368590Spendry  * Copyright (c) 1992, 1993, 1994, 1995
468590Spendry  *	The Regents of the University of California.  All rights reserved.
565935Spendry  *
665935Spendry  * This code is derived from software contributed to Berkeley by
765963Spendry  * Jan-Simon Pendry.
865935Spendry  *
965935Spendry  * %sccs.include.redist.c%
1065935Spendry  *
11*69588Spendry  *	@(#)union_vnops.c	8.29 (Berkeley) 05/20/95
1265935Spendry  */
1365935Spendry 
1465935Spendry #include <sys/param.h>
1565935Spendry #include <sys/systm.h>
1665935Spendry #include <sys/proc.h>
1765935Spendry #include <sys/file.h>
1865935Spendry #include <sys/time.h>
1967575Spendry #include <sys/stat.h>
2065935Spendry #include <sys/types.h>
2165935Spendry #include <sys/vnode.h>
2265935Spendry #include <sys/mount.h>
2365935Spendry #include <sys/namei.h>
2465935Spendry #include <sys/malloc.h>
2565935Spendry #include <sys/buf.h>
2666053Spendry #include <sys/queue.h>
2769447Smckusick #include <sys/lock.h>
2866055Spendry #include <miscfs/union/union.h>
2965935Spendry 
3069447Smckusick #define FIXUP(un, p) { \
3166152Spendry 	if (((un)->un_flags & UN_ULOCK) == 0) { \
3269447Smckusick 		union_fixup(un, p); \
3366152Spendry 	} \
3466152Spendry }
3566152Spendry 
3666152Spendry static void
3769447Smckusick union_fixup(un, p)
3866152Spendry 	struct union_node *un;
3969447Smckusick 	struct proc *p;
4066152Spendry {
4166152Spendry 
4269447Smckusick 	vn_lock(un->un_uppervp, LK_EXCLUSIVE | LK_RETRY, p);
4366152Spendry 	un->un_flags |= UN_ULOCK;
4466152Spendry }
4566152Spendry 
4665935Spendry static int
4767064Spendry union_lookup1(udvp, dvpp, vpp, cnp)
4865989Spendry 	struct vnode *udvp;
4967064Spendry 	struct vnode **dvpp;
5065935Spendry 	struct vnode **vpp;
5165935Spendry 	struct componentname *cnp;
5265935Spendry {
5365935Spendry 	int error;
5469447Smckusick 	struct proc *p = cnp->cn_proc;
5565935Spendry 	struct vnode *tdvp;
5667064Spendry 	struct vnode *dvp;
5765935Spendry 	struct mount *mp;
5865935Spendry 
5967064Spendry 	dvp = *dvpp;
6067064Spendry 
6165994Spendry 	/*
6265994Spendry 	 * If stepping up the directory tree, check for going
6365994Spendry 	 * back across the mount point, in which case do what
6465994Spendry 	 * lookup would do by stepping back down the mount
6565994Spendry 	 * hierarchy.
6665994Spendry 	 */
6765935Spendry 	if (cnp->cn_flags & ISDOTDOT) {
6867064Spendry 		while ((dvp != udvp) && (dvp->v_flag & VROOT)) {
6966034Spendry 			/*
7066034Spendry 			 * Don't do the NOCROSSMOUNT check
7166034Spendry 			 * at this level.  By definition,
7266034Spendry 			 * union fs deals with namespaces, not
7366034Spendry 			 * filesystems.
7466034Spendry 			 */
7565935Spendry 			tdvp = dvp;
7667064Spendry 			*dvpp = dvp = dvp->v_mount->mnt_vnodecovered;
7765935Spendry 			vput(tdvp);
7865935Spendry 			VREF(dvp);
7969447Smckusick 			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
8065935Spendry 		}
8165935Spendry 	}
8266027Spendry 
8365935Spendry         error = VOP_LOOKUP(dvp, &tdvp, cnp);
8465935Spendry 	if (error)
8565935Spendry 		return (error);
8665935Spendry 
8765994Spendry 	/*
8866027Spendry 	 * The parent directory will have been unlocked, unless lookup
8966027Spendry 	 * found the last component.  In which case, re-lock the node
9066027Spendry 	 * here to allow it to be unlocked again (phew) in union_lookup.
9165994Spendry 	 */
9266027Spendry 	if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN))
9369447Smckusick 		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
9465994Spendry 
9565935Spendry 	dvp = tdvp;
9665994Spendry 
9765994Spendry 	/*
9865994Spendry 	 * Lastly check if the current node is a mount point in
9966034Spendry 	 * which case walk up the mount hierarchy making sure not to
10065994Spendry 	 * bump into the root of the mount tree (ie. dvp != udvp).
10165994Spendry 	 */
10265989Spendry 	while (dvp != udvp && (dvp->v_type == VDIR) &&
10366034Spendry 	       (mp = dvp->v_mountedhere)) {
10465935Spendry 
10569572Smckusick 		if (vfs_busy(mp, 0, 0, p))
10665935Spendry 			continue;
10765935Spendry 
10869572Smckusick 		error = VFS_ROOT(mp, &tdvp);
10969572Smckusick 		vfs_unbusy(mp, p);
11069572Smckusick 		if (error) {
11165935Spendry 			vput(dvp);
11265935Spendry 			return (error);
11365935Spendry 		}
11465935Spendry 
11565965Spendry 		vput(dvp);
11665935Spendry 		dvp = tdvp;
11765935Spendry 	}
11865935Spendry 
11965935Spendry 	*vpp = dvp;
12065935Spendry 	return (0);
12165935Spendry }
12265935Spendry 
12365935Spendry int
12465935Spendry union_lookup(ap)
12565935Spendry 	struct vop_lookup_args /* {
12665935Spendry 		struct vnodeop_desc *a_desc;
12765935Spendry 		struct vnode *a_dvp;
12865935Spendry 		struct vnode **a_vpp;
12965935Spendry 		struct componentname *a_cnp;
13065935Spendry 	} */ *ap;
13165935Spendry {
13265965Spendry 	int error;
13365935Spendry 	int uerror, lerror;
13465935Spendry 	struct vnode *uppervp, *lowervp;
13565935Spendry 	struct vnode *upperdvp, *lowerdvp;
13665935Spendry 	struct vnode *dvp = ap->a_dvp;
13765989Spendry 	struct union_node *dun = VTOUNION(dvp);
13865935Spendry 	struct componentname *cnp = ap->a_cnp;
13969447Smckusick 	struct proc *p = cnp->cn_proc;
14065935Spendry 	int lockparent = cnp->cn_flags & LOCKPARENT;
14165994Spendry 	int rdonly = cnp->cn_flags & RDONLY;
14265997Spendry 	struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
14366152Spendry 	struct ucred *saved_cred;
14467575Spendry 	int iswhiteout;
14567575Spendry 	struct vattr va;
14665935Spendry 
14767446Spendry #ifdef notyet
14867446Spendry 	if (cnp->cn_namelen == 3 &&
14967446Spendry 			cnp->cn_nameptr[2] == '.' &&
15067446Spendry 			cnp->cn_nameptr[1] == '.' &&
15167446Spendry 			cnp->cn_nameptr[0] == '.') {
15267446Spendry 		dvp = *ap->a_vpp = LOWERVP(ap->a_dvp);
15367446Spendry 		if (dvp == NULLVP)
15467446Spendry 			return (ENOENT);
15567446Spendry 		VREF(dvp);
15669447Smckusick 		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
15767446Spendry 		if (!lockparent || !(cnp->cn_flags & ISLASTCN))
15869447Smckusick 			VOP_UNLOCK(ap->a_dvp, 0, p);
15967446Spendry 		return (0);
16067446Spendry 	}
16167446Spendry #endif
16267446Spendry 
16365965Spendry 	cnp->cn_flags |= LOCKPARENT;
16465965Spendry 
16565935Spendry 	upperdvp = dun->un_uppervp;
16665935Spendry 	lowerdvp = dun->un_lowervp;
16765997Spendry 	uppervp = NULLVP;
16865997Spendry 	lowervp = NULLVP;
16967575Spendry 	iswhiteout = 0;
17065935Spendry 
17165935Spendry 	/*
17265935Spendry 	 * do the lookup in the upper level.
17365935Spendry 	 * if that level comsumes additional pathnames,
17465935Spendry 	 * then assume that something special is going
17565935Spendry 	 * on and just return that vnode.
17665935Spendry 	 */
17767076Spendry 	if (upperdvp != NULLVP) {
17869447Smckusick 		FIXUP(dun, p);
17967064Spendry 		uerror = union_lookup1(um->um_uppervp, &upperdvp,
18065997Spendry 					&uppervp, cnp);
18166051Spendry 		/*if (uppervp == upperdvp)
18266051Spendry 			dun->un_flags |= UN_KLOCK;*/
18365965Spendry 
18465935Spendry 		if (cnp->cn_consume != 0) {
18565935Spendry 			*ap->a_vpp = uppervp;
18665965Spendry 			if (!lockparent)
18765965Spendry 				cnp->cn_flags &= ~LOCKPARENT;
18865935Spendry 			return (uerror);
18965935Spendry 		}
19067575Spendry 		if (uerror == ENOENT || uerror == EJUSTRETURN) {
19167575Spendry 			if (cnp->cn_flags & ISWHITEOUT) {
19267575Spendry 				iswhiteout = 1;
19367575Spendry 			} else if (lowerdvp != NULLVP) {
19467575Spendry 				lerror = VOP_GETATTR(upperdvp, &va,
19567575Spendry 					cnp->cn_cred, cnp->cn_proc);
19667575Spendry 				if (lerror == 0 && (va.va_flags & OPAQUE))
19767575Spendry 					iswhiteout = 1;
19867575Spendry 			}
19967575Spendry 		}
20065935Spendry 	} else {
20165935Spendry 		uerror = ENOENT;
20265935Spendry 	}
20365935Spendry 
20465935Spendry 	/*
20565935Spendry 	 * in a similar way to the upper layer, do the lookup
20665935Spendry 	 * in the lower layer.   this time, if there is some
20765935Spendry 	 * component magic going on, then vput whatever we got
20865935Spendry 	 * back from the upper layer and return the lower vnode
20965935Spendry 	 * instead.
21065935Spendry 	 */
21167575Spendry 	if (lowerdvp != NULLVP && !iswhiteout) {
21266051Spendry 		int nameiop;
21366051Spendry 
21469447Smckusick 		vn_lock(lowerdvp, LK_EXCLUSIVE | LK_RETRY, p);
21566051Spendry 
21666051Spendry 		/*
21766051Spendry 		 * Only do a LOOKUP on the bottom node, since
21866051Spendry 		 * we won't be making changes to it anyway.
21966051Spendry 		 */
22066051Spendry 		nameiop = cnp->cn_nameiop;
22166051Spendry 		cnp->cn_nameiop = LOOKUP;
22266152Spendry 		if (um->um_op == UNMNT_BELOW) {
22366152Spendry 			saved_cred = cnp->cn_cred;
22466152Spendry 			cnp->cn_cred = um->um_cred;
22566152Spendry 		}
22667064Spendry 		lerror = union_lookup1(um->um_lowervp, &lowerdvp,
22766051Spendry 				&lowervp, cnp);
22866152Spendry 		if (um->um_op == UNMNT_BELOW)
22966152Spendry 			cnp->cn_cred = saved_cred;
23066051Spendry 		cnp->cn_nameiop = nameiop;
23166051Spendry 
23265989Spendry 		if (lowervp != lowerdvp)
23369447Smckusick 			VOP_UNLOCK(lowerdvp, 0, p);
23465965Spendry 
23565935Spendry 		if (cnp->cn_consume != 0) {
23667076Spendry 			if (uppervp != NULLVP) {
23766051Spendry 				if (uppervp == upperdvp)
23866051Spendry 					vrele(uppervp);
23966051Spendry 				else
24066051Spendry 					vput(uppervp);
24165997Spendry 				uppervp = NULLVP;
24265935Spendry 			}
24365935Spendry 			*ap->a_vpp = lowervp;
24465965Spendry 			if (!lockparent)
24565965Spendry 				cnp->cn_flags &= ~LOCKPARENT;
24665935Spendry 			return (lerror);
24765935Spendry 		}
24865935Spendry 	} else {
24965935Spendry 		lerror = ENOENT;
25067416Spendry 		if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) {
25167416Spendry 			lowervp = LOWERVP(dun->un_pvp);
25267416Spendry 			if (lowervp != NULLVP) {
25367416Spendry 				VREF(lowervp);
25469447Smckusick 				vn_lock(lowervp, LK_EXCLUSIVE | LK_RETRY, p);
25567416Spendry 				lerror = 0;
25667416Spendry 			}
25767416Spendry 		}
25865935Spendry 	}
25965935Spendry 
26065965Spendry 	if (!lockparent)
26165965Spendry 		cnp->cn_flags &= ~LOCKPARENT;
26265965Spendry 
26365935Spendry 	/*
26465935Spendry 	 * at this point, we have uerror and lerror indicating
26565935Spendry 	 * possible errors with the lookups in the upper and lower
26665935Spendry 	 * layers.  additionally, uppervp and lowervp are (locked)
26765935Spendry 	 * references to existing vnodes in the upper and lower layers.
26865935Spendry 	 *
26965935Spendry 	 * there are now three cases to consider.
27065935Spendry 	 * 1. if both layers returned an error, then return whatever
27165935Spendry 	 *    error the upper layer generated.
27265935Spendry 	 *
27365935Spendry 	 * 2. if the top layer failed and the bottom layer succeeded
27465935Spendry 	 *    then two subcases occur.
27565935Spendry 	 *    a.  the bottom vnode is not a directory, in which
27665935Spendry 	 *	  case just return a new union vnode referencing
27765935Spendry 	 *	  an empty top layer and the existing bottom layer.
27865935Spendry 	 *    b.  the bottom vnode is a directory, in which case
27965935Spendry 	 *	  create a new directory in the top-level and
28065935Spendry 	 *	  continue as in case 3.
28165935Spendry 	 *
28265935Spendry 	 * 3. if the top layer succeeded then return a new union
28365935Spendry 	 *    vnode referencing whatever the new top layer and
28465935Spendry 	 *    whatever the bottom layer returned.
28565935Spendry 	 */
28665935Spendry 
28766027Spendry 	*ap->a_vpp = NULLVP;
28866027Spendry 
28965935Spendry 	/* case 1. */
29065935Spendry 	if ((uerror != 0) && (lerror != 0)) {
29165935Spendry 		return (uerror);
29265935Spendry 	}
29365935Spendry 
29465935Spendry 	/* case 2. */
29565935Spendry 	if (uerror != 0 /* && (lerror == 0) */ ) {
29665935Spendry 		if (lowervp->v_type == VDIR) { /* case 2b. */
29766051Spendry 			dun->un_flags &= ~UN_ULOCK;
29869447Smckusick 			VOP_UNLOCK(upperdvp, 0, p);
29965997Spendry 			uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
30069447Smckusick 			vn_lock(upperdvp, LK_EXCLUSIVE | LK_RETRY, p);
30166051Spendry 			dun->un_flags |= UN_ULOCK;
30266051Spendry 
30365935Spendry 			if (uerror) {
30467076Spendry 				if (lowervp != NULLVP) {
30565935Spendry 					vput(lowervp);
30665997Spendry 					lowervp = NULLVP;
30765935Spendry 				}
30865935Spendry 				return (uerror);
30965935Spendry 			}
31065935Spendry 		}
31165935Spendry 	}
31265935Spendry 
31367076Spendry 	if (lowervp != NULLVP)
31469447Smckusick 		VOP_UNLOCK(lowervp, 0, p);
31565965Spendry 
31665997Spendry 	error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
31768078Spendry 			      uppervp, lowervp, 1);
31865997Spendry 
31965965Spendry 	if (error) {
32067076Spendry 		if (uppervp != NULLVP)
32166051Spendry 			vput(uppervp);
32267076Spendry 		if (lowervp != NULLVP)
32365965Spendry 			vrele(lowervp);
32465965Spendry 	} else {
32565994Spendry 		if (*ap->a_vpp != dvp)
32665994Spendry 			if (!lockparent || !(cnp->cn_flags & ISLASTCN))
32769447Smckusick 				VOP_UNLOCK(dvp, 0, p);
32865965Spendry 	}
32965965Spendry 
33065965Spendry 	return (error);
33165935Spendry }
33265935Spendry 
33365963Spendry int
33465963Spendry union_create(ap)
33565963Spendry 	struct vop_create_args /* {
33665963Spendry 		struct vnode *a_dvp;
33765963Spendry 		struct vnode **a_vpp;
33865963Spendry 		struct componentname *a_cnp;
33965963Spendry 		struct vattr *a_vap;
34065963Spendry 	} */ *ap;
34165963Spendry {
34265963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
34365963Spendry 	struct vnode *dvp = un->un_uppervp;
34469447Smckusick 	struct componentname *cnp = ap->a_cnp;
34569447Smckusick 	struct proc *p = cnp->cn_proc;
34665963Spendry 
34767076Spendry 	if (dvp != NULLVP) {
34865963Spendry 		int error;
34965963Spendry 		struct vnode *vp;
35068078Spendry 		struct mount *mp;
35165963Spendry 
35269447Smckusick 		FIXUP(un, p);
35366152Spendry 
35465963Spendry 		VREF(dvp);
35566051Spendry 		un->un_flags |= UN_KLOCK;
35668078Spendry 		mp = ap->a_dvp->v_mount;
35765963Spendry 		vput(ap->a_dvp);
35869447Smckusick 		error = VOP_CREATE(dvp, &vp, cnp, ap->a_vap);
35965963Spendry 		if (error)
36065963Spendry 			return (error);
36165963Spendry 
36269447Smckusick 		error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP, cnp, vp,
36369447Smckusick 				NULLVP, 1);
36465965Spendry 		if (error)
36566051Spendry 			vput(vp);
36665963Spendry 		return (error);
36765963Spendry 	}
36865963Spendry 
36965963Spendry 	vput(ap->a_dvp);
37065963Spendry 	return (EROFS);
37165963Spendry }
37265963Spendry 
37365963Spendry int
37467575Spendry union_whiteout(ap)
37567575Spendry 	struct vop_whiteout_args /* {
37667575Spendry 		struct vnode *a_dvp;
37767575Spendry 		struct componentname *a_cnp;
37867575Spendry 		int a_flags;
37967575Spendry 	} */ *ap;
38067575Spendry {
38167575Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
38269447Smckusick 	struct componentname *cnp = ap->a_cnp;
38369447Smckusick 	struct proc *p = cnp->cn_proc;
38467575Spendry 
38567575Spendry 	if (un->un_uppervp == NULLVP)
38667575Spendry 		return (EOPNOTSUPP);
38767575Spendry 
38869447Smckusick 	FIXUP(un, p);
38969447Smckusick 	return (VOP_WHITEOUT(un->un_uppervp, cnp, ap->a_flags));
39067575Spendry }
39167575Spendry 
39267575Spendry int
39365963Spendry union_mknod(ap)
39465963Spendry 	struct vop_mknod_args /* {
39565963Spendry 		struct vnode *a_dvp;
39665963Spendry 		struct vnode **a_vpp;
39765963Spendry 		struct componentname *a_cnp;
39865963Spendry 		struct vattr *a_vap;
39965963Spendry 	} */ *ap;
40065963Spendry {
40165963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
40265963Spendry 	struct vnode *dvp = un->un_uppervp;
40369447Smckusick 	struct componentname *cnp = ap->a_cnp;
40469447Smckusick 	struct proc *p = cnp->cn_proc;
40565963Spendry 
40667076Spendry 	if (dvp != NULLVP) {
40765963Spendry 		int error;
40865963Spendry 		struct vnode *vp;
40968078Spendry 		struct mount *mp;
41065963Spendry 
41169447Smckusick 		FIXUP(un, p);
41266152Spendry 
41365963Spendry 		VREF(dvp);
41466051Spendry 		un->un_flags |= UN_KLOCK;
41568078Spendry 		mp = ap->a_dvp->v_mount;
41665963Spendry 		vput(ap->a_dvp);
41769447Smckusick 		error = VOP_MKNOD(dvp, &vp, cnp, ap->a_vap);
41865963Spendry 		if (error)
41965963Spendry 			return (error);
42065963Spendry 
42167076Spendry 		if (vp != NULLVP) {
42269447Smckusick 			error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP,
42369447Smckusick 					cnp, vp, NULLVP, 1);
42465965Spendry 			if (error)
42566051Spendry 				vput(vp);
42665965Spendry 		}
42765963Spendry 		return (error);
42865963Spendry 	}
42965963Spendry 
43065963Spendry 	vput(ap->a_dvp);
43165963Spendry 	return (EROFS);
43265963Spendry }
43365963Spendry 
43465935Spendry int
43565935Spendry union_open(ap)
43665935Spendry 	struct vop_open_args /* {
43765935Spendry 		struct vnodeop_desc *a_desc;
43865935Spendry 		struct vnode *a_vp;
43965935Spendry 		int a_mode;
44065935Spendry 		struct ucred *a_cred;
44165935Spendry 		struct proc *a_p;
44265935Spendry 	} */ *ap;
44365935Spendry {
44465935Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
44565965Spendry 	struct vnode *tvp;
44665935Spendry 	int mode = ap->a_mode;
44765935Spendry 	struct ucred *cred = ap->a_cred;
44865935Spendry 	struct proc *p = ap->a_p;
44965965Spendry 	int error;
45065935Spendry 
45165935Spendry 	/*
45265935Spendry 	 * If there is an existing upper vp then simply open that.
45365935Spendry 	 */
45465965Spendry 	tvp = un->un_uppervp;
45565965Spendry 	if (tvp == NULLVP) {
45665935Spendry 		/*
45765965Spendry 		 * If the lower vnode is being opened for writing, then
45865965Spendry 		 * copy the file contents to the upper vnode and open that,
45965965Spendry 		 * otherwise can simply open the lower vnode.
46065935Spendry 		 */
46165965Spendry 		tvp = un->un_lowervp;
46265965Spendry 		if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
46367169Spendry 			error = union_copyup(un, (mode&O_TRUNC) == 0, cred, p);
46465965Spendry 			if (error == 0)
46565965Spendry 				error = VOP_OPEN(un->un_uppervp, mode, cred, p);
46665965Spendry 			return (error);
46765935Spendry 		}
46866051Spendry 
46966051Spendry 		/*
47066051Spendry 		 * Just open the lower vnode
47166051Spendry 		 */
47266027Spendry 		un->un_openl++;
47369447Smckusick 		vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p);
47466051Spendry 		error = VOP_OPEN(tvp, mode, cred, p);
47569447Smckusick 		VOP_UNLOCK(tvp, 0, p);
47666051Spendry 
47766051Spendry 		return (error);
47865935Spendry 	}
47965935Spendry 
48069447Smckusick 	FIXUP(un, p);
48166152Spendry 
48265965Spendry 	error = VOP_OPEN(tvp, mode, cred, p);
48365965Spendry 
48465965Spendry 	return (error);
48565935Spendry }
48665935Spendry 
48765963Spendry int
48865963Spendry union_close(ap)
48965963Spendry 	struct vop_close_args /* {
49065963Spendry 		struct vnode *a_vp;
49165963Spendry 		int  a_fflag;
49265963Spendry 		struct ucred *a_cred;
49365963Spendry 		struct proc *a_p;
49465963Spendry 	} */ *ap;
49565963Spendry {
49665997Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
49765997Spendry 	struct vnode *vp;
49865963Spendry 
49967076Spendry 	if (un->un_uppervp != NULLVP) {
50065997Spendry 		vp = un->un_uppervp;
50165997Spendry 	} else {
50266027Spendry #ifdef UNION_DIAGNOSTIC
50366027Spendry 		if (un->un_openl <= 0)
50466027Spendry 			panic("union: un_openl cnt");
50565997Spendry #endif
50666027Spendry 		--un->un_openl;
50765997Spendry 		vp = un->un_lowervp;
50865997Spendry 	}
50966027Spendry 
51065997Spendry 	return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p));
51165963Spendry }
51265963Spendry 
51365935Spendry /*
51465963Spendry  * Check access permission on the union vnode.
51565963Spendry  * The access check being enforced is to check
51665963Spendry  * against both the underlying vnode, and any
51765963Spendry  * copied vnode.  This ensures that no additional
51865963Spendry  * file permissions are given away simply because
51965963Spendry  * the user caused an implicit file copy.
52065963Spendry  */
52165963Spendry int
52265963Spendry union_access(ap)
52365963Spendry 	struct vop_access_args /* {
52465963Spendry 		struct vnodeop_desc *a_desc;
52565963Spendry 		struct vnode *a_vp;
52665963Spendry 		int a_mode;
52765963Spendry 		struct ucred *a_cred;
52865963Spendry 		struct proc *a_p;
52965963Spendry 	} */ *ap;
53065963Spendry {
53165963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
53269447Smckusick 	struct proc *p = ap->a_p;
53366149Spendry 	int error = EACCES;
53465963Spendry 	struct vnode *vp;
53565963Spendry 
53667076Spendry 	if ((vp = un->un_uppervp) != NULLVP) {
53769447Smckusick 		FIXUP(un, p);
53869447Smckusick 		return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, p));
53966152Spendry 	}
54066152Spendry 
54167076Spendry 	if ((vp = un->un_lowervp) != NULLVP) {
54269447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
54369447Smckusick 		error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, p);
54466152Spendry 		if (error == 0) {
54566152Spendry 			struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
54666152Spendry 
54766152Spendry 			if (um->um_op == UNMNT_BELOW)
54866152Spendry 				error = VOP_ACCESS(vp, ap->a_mode,
54969447Smckusick 						um->um_cred, p);
55066152Spendry 		}
55169447Smckusick 		VOP_UNLOCK(vp, 0, p);
55265963Spendry 		if (error)
55365963Spendry 			return (error);
55465963Spendry 	}
55565963Spendry 
55665965Spendry 	return (error);
55765963Spendry }
55865963Spendry 
55965963Spendry /*
56067109Spendry  * We handle getattr only to change the fsid and
56167109Spendry  * track object sizes
56265935Spendry  */
56365935Spendry int
56465935Spendry union_getattr(ap)
56565935Spendry 	struct vop_getattr_args /* {
56665935Spendry 		struct vnode *a_vp;
56765935Spendry 		struct vattr *a_vap;
56865935Spendry 		struct ucred *a_cred;
56965935Spendry 		struct proc *a_p;
57065935Spendry 	} */ *ap;
57165935Spendry {
57265935Spendry 	int error;
57366062Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
57466062Spendry 	struct vnode *vp = un->un_uppervp;
57569447Smckusick 	struct proc *p = ap->a_p;
57666062Spendry 	struct vattr *vap;
57766062Spendry 	struct vattr va;
57865935Spendry 
57966062Spendry 
58066062Spendry 	/*
58166062Spendry 	 * Some programs walk the filesystem hierarchy by counting
58266062Spendry 	 * links to directories to avoid stat'ing all the time.
58366062Spendry 	 * This means the link count on directories needs to be "correct".
58466062Spendry 	 * The only way to do that is to call getattr on both layers
58566062Spendry 	 * and fix up the link count.  The link count will not necessarily
58666062Spendry 	 * be accurate but will be large enough to defeat the tree walkers.
58766062Spendry 	 */
58866062Spendry 
58966062Spendry 	vap = ap->a_vap;
59066062Spendry 
59166062Spendry 	vp = un->un_uppervp;
59266062Spendry 	if (vp != NULLVP) {
59367073Spendry 		/*
59467073Spendry 		 * It's not clear whether VOP_GETATTR is to be
59567073Spendry 		 * called with the vnode locked or not.  stat() calls
59667073Spendry 		 * it with (vp) locked, and fstat calls it with
59767073Spendry 		 * (vp) unlocked.
59867073Spendry 		 * In the mean time, compensate here by checking
59967073Spendry 		 * the union_node's lock flag.
60067073Spendry 		 */
60167073Spendry 		if (un->un_flags & UN_LOCKED)
60269447Smckusick 			FIXUP(un, p);
60367073Spendry 
60466062Spendry 		error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
60566062Spendry 		if (error)
60666062Spendry 			return (error);
60767109Spendry 		union_newsize(ap->a_vp, vap->va_size, VNOVAL);
60866062Spendry 	}
60966062Spendry 
61066062Spendry 	if (vp == NULLVP) {
61166062Spendry 		vp = un->un_lowervp;
61266062Spendry 	} else if (vp->v_type == VDIR) {
61366062Spendry 		vp = un->un_lowervp;
61466062Spendry 		vap = &va;
61566062Spendry 	} else {
61666062Spendry 		vp = NULLVP;
61766062Spendry 	}
61866062Spendry 
61966062Spendry 	if (vp != NULLVP) {
62066062Spendry 		error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
62166062Spendry 		if (error)
62266062Spendry 			return (error);
62367109Spendry 		union_newsize(ap->a_vp, VNOVAL, vap->va_size);
62466062Spendry 	}
62565965Spendry 
62666062Spendry 	if ((vap != ap->a_vap) && (vap->va_type == VDIR))
62766062Spendry 		ap->a_vap->va_nlink += vap->va_nlink;
62866062Spendry 
62967400Spendry 	ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
63065935Spendry 	return (0);
63165935Spendry }
63265935Spendry 
63365963Spendry int
63465965Spendry union_setattr(ap)
63565963Spendry 	struct vop_setattr_args /* {
63665963Spendry 		struct vnode *a_vp;
63765963Spendry 		struct vattr *a_vap;
63865963Spendry 		struct ucred *a_cred;
63965963Spendry 		struct proc *a_p;
64065963Spendry 	} */ *ap;
64165963Spendry {
64265963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
64369447Smckusick 	struct proc *p = ap->a_p;
64465963Spendry 	int error;
64565963Spendry 
64666057Spendry 	/*
64766057Spendry 	 * Handle case of truncating lower object to zero size,
64866057Spendry 	 * by creating a zero length upper object.  This is to
64966057Spendry 	 * handle the case of open with O_TRUNC and O_CREAT.
65066057Spendry 	 */
65166057Spendry 	if ((un->un_uppervp == NULLVP) &&
65266057Spendry 	    /* assert(un->un_lowervp != NULLVP) */
65367575Spendry 	    (un->un_lowervp->v_type == VREG)) {
65467575Spendry 		error = union_copyup(un, (ap->a_vap->va_size != 0),
65567575Spendry 						ap->a_cred, ap->a_p);
65666057Spendry 		if (error)
65766057Spendry 			return (error);
65866057Spendry 	}
65966057Spendry 
66066057Spendry 	/*
66166057Spendry 	 * Try to set attributes in upper layer,
66266057Spendry 	 * otherwise return read-only filesystem error.
66366057Spendry 	 */
66466057Spendry 	if (un->un_uppervp != NULLVP) {
66569447Smckusick 		FIXUP(un, p);
66665963Spendry 		error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
66765963Spendry 					ap->a_cred, ap->a_p);
66867109Spendry 		if ((error == 0) && (ap->a_vap->va_size != VNOVAL))
66967109Spendry 			union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL);
67065963Spendry 	} else {
67165963Spendry 		error = EROFS;
67265963Spendry 	}
67365963Spendry 
67465963Spendry 	return (error);
67565963Spendry }
67665963Spendry 
67765963Spendry int
67865963Spendry union_read(ap)
67965963Spendry 	struct vop_read_args /* {
68065963Spendry 		struct vnode *a_vp;
68165963Spendry 		struct uio *a_uio;
68265963Spendry 		int  a_ioflag;
68365963Spendry 		struct ucred *a_cred;
68465963Spendry 	} */ *ap;
68565963Spendry {
68665963Spendry 	int error;
68769447Smckusick 	struct proc *p = ap->a_uio->uio_procp;
68865963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
68966051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
69065963Spendry 
69166051Spendry 	if (dolock)
69269447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
69366152Spendry 	else
69469447Smckusick 		FIXUP(VTOUNION(ap->a_vp), p);
69565963Spendry 	error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
69666051Spendry 	if (dolock)
69769447Smckusick 		VOP_UNLOCK(vp, 0, p);
69865963Spendry 
69967109Spendry 	/*
70067109Spendry 	 * XXX
70167109Spendry 	 * perhaps the size of the underlying object has changed under
70267109Spendry 	 * our feet.  take advantage of the offset information present
70367109Spendry 	 * in the uio structure.
70467109Spendry 	 */
70567109Spendry 	if (error == 0) {
70667109Spendry 		struct union_node *un = VTOUNION(ap->a_vp);
70767109Spendry 		off_t cur = ap->a_uio->uio_offset;
70867109Spendry 
70967109Spendry 		if (vp == un->un_uppervp) {
71067109Spendry 			if (cur > un->un_uppersz)
71167109Spendry 				union_newsize(ap->a_vp, cur, VNOVAL);
71267109Spendry 		} else {
71367109Spendry 			if (cur > un->un_lowersz)
71467109Spendry 				union_newsize(ap->a_vp, VNOVAL, cur);
71567109Spendry 		}
71667109Spendry 	}
71767109Spendry 
71865963Spendry 	return (error);
71965963Spendry }
72065963Spendry 
72165963Spendry int
72265963Spendry union_write(ap)
72365963Spendry 	struct vop_read_args /* {
72465963Spendry 		struct vnode *a_vp;
72565963Spendry 		struct uio *a_uio;
72665963Spendry 		int  a_ioflag;
72765963Spendry 		struct ucred *a_cred;
72865963Spendry 	} */ *ap;
72965963Spendry {
73065963Spendry 	int error;
73167575Spendry 	struct vnode *vp;
73267575Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
73369447Smckusick 	struct proc *p = ap->a_uio->uio_procp;
73465963Spendry 
73567575Spendry 	vp = UPPERVP(ap->a_vp);
73667575Spendry 	if (vp == NULLVP)
73767575Spendry 		panic("union: missing upper layer in write");
73867575Spendry 
73969447Smckusick 	FIXUP(un, p);
74065963Spendry 	error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
74165963Spendry 
74267109Spendry 	/*
74367109Spendry 	 * the size of the underlying object may be changed by the
74467109Spendry 	 * write.
74567109Spendry 	 */
74667109Spendry 	if (error == 0) {
74767109Spendry 		off_t cur = ap->a_uio->uio_offset;
74867109Spendry 
74967575Spendry 		if (cur > un->un_uppersz)
75067575Spendry 			union_newsize(ap->a_vp, cur, VNOVAL);
75167109Spendry 	}
75267109Spendry 
75365963Spendry 	return (error);
75465963Spendry }
75565963Spendry 
75667751Spendry union_lease(ap)
75767751Spendry 	struct vop_lease_args /* {
75867751Spendry 		struct vnode *a_vp;
75967751Spendry 		struct proc *a_p;
76067751Spendry 		struct ucred *a_cred;
76167751Spendry 		int a_flag;
76267751Spendry 	} */ *ap;
76367751Spendry {
76467751Spendry 
76567751Spendry 	return (VOP_LEASE(OTHERVP(ap->a_vp), ap->a_p, ap->a_cred, ap->a_flag));
76667751Spendry }
76767751Spendry 
76865963Spendry int
76965963Spendry union_ioctl(ap)
77065963Spendry 	struct vop_ioctl_args /* {
77165963Spendry 		struct vnode *a_vp;
77265963Spendry 		int  a_command;
77365963Spendry 		caddr_t  a_data;
77465963Spendry 		int  a_fflag;
77565963Spendry 		struct ucred *a_cred;
77665963Spendry 		struct proc *a_p;
77765963Spendry 	} */ *ap;
77865963Spendry {
77965963Spendry 
78065963Spendry 	return (VOP_IOCTL(OTHERVP(ap->a_vp), ap->a_command, ap->a_data,
78165963Spendry 				ap->a_fflag, ap->a_cred, ap->a_p));
78265963Spendry }
78365963Spendry 
78465963Spendry int
78565963Spendry union_select(ap)
78665963Spendry 	struct vop_select_args /* {
78765963Spendry 		struct vnode *a_vp;
78865963Spendry 		int  a_which;
78965963Spendry 		int  a_fflags;
79065963Spendry 		struct ucred *a_cred;
79165963Spendry 		struct proc *a_p;
79265963Spendry 	} */ *ap;
79365963Spendry {
79465963Spendry 
79565963Spendry 	return (VOP_SELECT(OTHERVP(ap->a_vp), ap->a_which, ap->a_fflags,
79665963Spendry 				ap->a_cred, ap->a_p));
79765963Spendry }
79865963Spendry 
79965963Spendry int
80069389Spendry union_revoke(ap)
80169389Spendry 	struct vop_revoke_args /* {
80269389Spendry 		struct vnode *a_vp;
80369389Spendry 		int a_flags;
80469447Smckusick 		struct proc *a_p;
80569389Spendry 	} */ *ap;
80669389Spendry {
80769389Spendry 	struct vnode *vp = ap->a_vp;
80869389Spendry 
80969389Spendry 	if (UPPERVP(vp))
81069389Spendry 		VOP_REVOKE(UPPERVP(vp), ap->a_flags);
81169389Spendry 	if (LOWERVP(vp))
81269447Smckusick 		VOP_REVOKE(LOWERVP(vp), ap->a_flags);
81369389Spendry 	vgone(vp);
81469389Spendry }
81569389Spendry 
81669389Spendry int
81765963Spendry union_mmap(ap)
81865963Spendry 	struct vop_mmap_args /* {
81965963Spendry 		struct vnode *a_vp;
82065963Spendry 		int  a_fflags;
82165963Spendry 		struct ucred *a_cred;
82265963Spendry 		struct proc *a_p;
82365963Spendry 	} */ *ap;
82465963Spendry {
82565963Spendry 
82665963Spendry 	return (VOP_MMAP(OTHERVP(ap->a_vp), ap->a_fflags,
82765963Spendry 				ap->a_cred, ap->a_p));
82865963Spendry }
82965963Spendry 
83065963Spendry int
83165963Spendry union_fsync(ap)
83265963Spendry 	struct vop_fsync_args /* {
83365963Spendry 		struct vnode *a_vp;
83465963Spendry 		struct ucred *a_cred;
83565963Spendry 		int  a_waitfor;
83665963Spendry 		struct proc *a_p;
83765963Spendry 	} */ *ap;
83865963Spendry {
83965963Spendry 	int error = 0;
84069447Smckusick 	struct proc *p = ap->a_p;
84165963Spendry 	struct vnode *targetvp = OTHERVP(ap->a_vp);
84265963Spendry 
84367076Spendry 	if (targetvp != NULLVP) {
84466051Spendry 		int dolock = (targetvp == LOWERVP(ap->a_vp));
84566051Spendry 
84666051Spendry 		if (dolock)
84769447Smckusick 			vn_lock(targetvp, LK_EXCLUSIVE | LK_RETRY, p);
84866152Spendry 		else
84969447Smckusick 			FIXUP(VTOUNION(ap->a_vp), p);
85069447Smckusick 		error = VOP_FSYNC(targetvp, ap->a_cred, ap->a_waitfor, p);
85166051Spendry 		if (dolock)
85269447Smckusick 			VOP_UNLOCK(targetvp, 0, p);
85365963Spendry 	}
85465963Spendry 
85565963Spendry 	return (error);
85665963Spendry }
85765963Spendry 
85865963Spendry int
85965963Spendry union_seek(ap)
86065963Spendry 	struct vop_seek_args /* {
86165963Spendry 		struct vnode *a_vp;
86265963Spendry 		off_t  a_oldoff;
86365963Spendry 		off_t  a_newoff;
86465963Spendry 		struct ucred *a_cred;
86565963Spendry 	} */ *ap;
86665963Spendry {
86765963Spendry 
86865963Spendry 	return (VOP_SEEK(OTHERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred));
86965963Spendry }
87065963Spendry 
87165963Spendry int
87265963Spendry union_remove(ap)
87365963Spendry 	struct vop_remove_args /* {
87465963Spendry 		struct vnode *a_dvp;
87565963Spendry 		struct vnode *a_vp;
87665963Spendry 		struct componentname *a_cnp;
87765963Spendry 	} */ *ap;
87865963Spendry {
87965963Spendry 	int error;
88065963Spendry 	struct union_node *dun = VTOUNION(ap->a_dvp);
88165963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
88269447Smckusick 	struct componentname *cnp = ap->a_cnp;
88369447Smckusick 	struct proc *p = cnp->cn_proc;
88465963Spendry 
88567575Spendry 	if (dun->un_uppervp == NULLVP)
88667575Spendry 		panic("union remove: null upper vnode");
88767575Spendry 
88867575Spendry 	if (un->un_uppervp != NULLVP) {
88965963Spendry 		struct vnode *dvp = dun->un_uppervp;
89065963Spendry 		struct vnode *vp = un->un_uppervp;
89165963Spendry 
89269447Smckusick 		FIXUP(dun, p);
89365963Spendry 		VREF(dvp);
89466051Spendry 		dun->un_flags |= UN_KLOCK;
89565963Spendry 		vput(ap->a_dvp);
89669447Smckusick 		FIXUP(un, p);
89765963Spendry 		VREF(vp);
89866051Spendry 		un->un_flags |= UN_KLOCK;
89965963Spendry 		vput(ap->a_vp);
90065963Spendry 
90167784Spendry 		if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
90267784Spendry 			cnp->cn_flags |= DOWHITEOUT;
90367784Spendry 		error = VOP_REMOVE(dvp, vp, cnp);
90466027Spendry 		if (!error)
90566027Spendry 			union_removed_upper(un);
90665963Spendry 	} else {
90769447Smckusick 		FIXUP(dun, p);
90867575Spendry 		error = union_mkwhiteout(
90967575Spendry 			MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
91067575Spendry 			dun->un_uppervp, ap->a_cnp, un->un_path);
91165963Spendry 		vput(ap->a_dvp);
91265963Spendry 		vput(ap->a_vp);
91365963Spendry 	}
91465963Spendry 
91565963Spendry 	return (error);
91665963Spendry }
91765963Spendry 
91865963Spendry int
91965963Spendry union_link(ap)
92065963Spendry 	struct vop_link_args /* {
92165963Spendry 		struct vnode *a_vp;
92265963Spendry 		struct vnode *a_tdvp;
92365963Spendry 		struct componentname *a_cnp;
92465963Spendry 	} */ *ap;
92565963Spendry {
92667169Spendry 	int error = 0;
92769447Smckusick 	struct componentname *cnp = ap->a_cnp;
92869447Smckusick 	struct proc *p = cnp->cn_proc;
92967169Spendry 	struct union_node *un;
93067169Spendry 	struct vnode *vp;
93167169Spendry 	struct vnode *tdvp;
93265963Spendry 
93368535Smckusick 	un = VTOUNION(ap->a_tdvp);
93465963Spendry 
93568535Smckusick 	if (ap->a_tdvp->v_op != ap->a_vp->v_op) {
93668535Smckusick 		vp = ap->a_vp;
93767169Spendry 	} else {
93868535Smckusick 		struct union_node *tun = VTOUNION(ap->a_vp);
93968535Smckusick 		if (tun->un_uppervp == NULLVP) {
94069447Smckusick 			vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p);
94168535Smckusick 			if (un->un_uppervp == tun->un_dirvp) {
94267169Spendry 				un->un_flags &= ~UN_ULOCK;
94369447Smckusick 				VOP_UNLOCK(un->un_uppervp, 0, p);
94467169Spendry 			}
94569447Smckusick 			error = union_copyup(tun, 1, cnp->cn_cred, p);
94668535Smckusick 			if (un->un_uppervp == tun->un_dirvp) {
94769447Smckusick 				vn_lock(un->un_uppervp,
94869447Smckusick 						LK_EXCLUSIVE | LK_RETRY, p);
94967169Spendry 				un->un_flags |= UN_ULOCK;
95067169Spendry 			}
95169447Smckusick 			VOP_UNLOCK(ap->a_vp, 0, p);
95267169Spendry 		}
95368535Smckusick 		vp = tun->un_uppervp;
95467169Spendry 	}
95565963Spendry 
95668535Smckusick 	tdvp = un->un_uppervp;
95768535Smckusick 	if (tdvp == NULLVP)
95867169Spendry 		error = EROFS;
95967169Spendry 
96067169Spendry 	if (error) {
96168535Smckusick 		vput(ap->a_tdvp);
96267169Spendry 		return (error);
96365963Spendry 	}
96465963Spendry 
96569447Smckusick 	FIXUP(un, p);
96668535Smckusick 	VREF(tdvp);
96767169Spendry 	un->un_flags |= UN_KLOCK;
96868535Smckusick 	vput(ap->a_tdvp);
96967169Spendry 
97069447Smckusick 	return (VOP_LINK(vp, tdvp, cnp));
97165963Spendry }
97265963Spendry 
97365963Spendry int
97465963Spendry union_rename(ap)
97565963Spendry 	struct vop_rename_args  /* {
97665963Spendry 		struct vnode *a_fdvp;
97765963Spendry 		struct vnode *a_fvp;
97865963Spendry 		struct componentname *a_fcnp;
97965963Spendry 		struct vnode *a_tdvp;
98065963Spendry 		struct vnode *a_tvp;
98165963Spendry 		struct componentname *a_tcnp;
98265963Spendry 	} */ *ap;
98365963Spendry {
98465963Spendry 	int error;
98565963Spendry 
98665963Spendry 	struct vnode *fdvp = ap->a_fdvp;
98765963Spendry 	struct vnode *fvp = ap->a_fvp;
98865963Spendry 	struct vnode *tdvp = ap->a_tdvp;
98965963Spendry 	struct vnode *tvp = ap->a_tvp;
99065963Spendry 
99165963Spendry 	if (fdvp->v_op == union_vnodeop_p) {	/* always true */
99265963Spendry 		struct union_node *un = VTOUNION(fdvp);
99365997Spendry 		if (un->un_uppervp == NULLVP) {
99467575Spendry 			/*
99567575Spendry 			 * this should never happen in normal
99667575Spendry 			 * operation but might if there was
99767575Spendry 			 * a problem creating the top-level shadow
99867575Spendry 			 * directory.
99967575Spendry 			 */
100067575Spendry 			error = EXDEV;
100165963Spendry 			goto bad;
100265963Spendry 		}
100365963Spendry 
100465963Spendry 		fdvp = un->un_uppervp;
100565963Spendry 		VREF(fdvp);
100665963Spendry 		vrele(ap->a_fdvp);
100765963Spendry 	}
100865963Spendry 
100965963Spendry 	if (fvp->v_op == union_vnodeop_p) {	/* always true */
101065963Spendry 		struct union_node *un = VTOUNION(fvp);
101165997Spendry 		if (un->un_uppervp == NULLVP) {
101267575Spendry 			/* XXX: should do a copyup */
101367575Spendry 			error = EXDEV;
101465963Spendry 			goto bad;
101565963Spendry 		}
101665963Spendry 
101767575Spendry 		if (un->un_lowervp != NULLVP)
101867575Spendry 			ap->a_fcnp->cn_flags |= DOWHITEOUT;
101967575Spendry 
102065963Spendry 		fvp = un->un_uppervp;
102165963Spendry 		VREF(fvp);
102265963Spendry 		vrele(ap->a_fvp);
102365963Spendry 	}
102465963Spendry 
102565963Spendry 	if (tdvp->v_op == union_vnodeop_p) {
102665963Spendry 		struct union_node *un = VTOUNION(tdvp);
102765997Spendry 		if (un->un_uppervp == NULLVP) {
102867076Spendry 			/*
102967076Spendry 			 * this should never happen in normal
103067076Spendry 			 * operation but might if there was
103167076Spendry 			 * a problem creating the top-level shadow
103267076Spendry 			 * directory.
103367076Spendry 			 */
103467575Spendry 			error = EXDEV;
103565963Spendry 			goto bad;
103665963Spendry 		}
103765963Spendry 
103865963Spendry 		tdvp = un->un_uppervp;
103965963Spendry 		VREF(tdvp);
104066051Spendry 		un->un_flags |= UN_KLOCK;
104165997Spendry 		vput(ap->a_tdvp);
104265963Spendry 	}
104365963Spendry 
104467076Spendry 	if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) {
104565963Spendry 		struct union_node *un = VTOUNION(tvp);
104665963Spendry 
104765963Spendry 		tvp = un->un_uppervp;
104867076Spendry 		if (tvp != NULLVP) {
104967076Spendry 			VREF(tvp);
105067076Spendry 			un->un_flags |= UN_KLOCK;
105167076Spendry 		}
105265963Spendry 		vput(ap->a_tvp);
105365963Spendry 	}
105465963Spendry 
105565963Spendry 	return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));
105665963Spendry 
105765963Spendry bad:
105865963Spendry 	vrele(fdvp);
105965963Spendry 	vrele(fvp);
106065963Spendry 	vput(tdvp);
106167076Spendry 	if (tvp != NULLVP)
106265963Spendry 		vput(tvp);
106365963Spendry 
106465963Spendry 	return (error);
106565963Spendry }
106665963Spendry 
106765963Spendry int
106865963Spendry union_mkdir(ap)
106965963Spendry 	struct vop_mkdir_args /* {
107065963Spendry 		struct vnode *a_dvp;
107165963Spendry 		struct vnode **a_vpp;
107265963Spendry 		struct componentname *a_cnp;
107365963Spendry 		struct vattr *a_vap;
107465963Spendry 	} */ *ap;
107565963Spendry {
107665963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
107765963Spendry 	struct vnode *dvp = un->un_uppervp;
107869447Smckusick 	struct componentname *cnp = ap->a_cnp;
107969447Smckusick 	struct proc *p = cnp->cn_proc;
108065963Spendry 
108167076Spendry 	if (dvp != NULLVP) {
108265963Spendry 		int error;
108365963Spendry 		struct vnode *vp;
108465963Spendry 
108569447Smckusick 		FIXUP(un, p);
108665963Spendry 		VREF(dvp);
108766051Spendry 		un->un_flags |= UN_KLOCK;
108869447Smckusick 		VOP_UNLOCK(ap->a_dvp, 0, p);
108969447Smckusick 		error = VOP_MKDIR(dvp, &vp, cnp, ap->a_vap);
109068078Spendry 		if (error) {
109168078Spendry 			vrele(ap->a_dvp);
109265963Spendry 			return (error);
109368078Spendry 		}
109465963Spendry 
109569447Smckusick 		error = union_allocvp(ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp,
109669447Smckusick 				NULLVP, cnp, vp, NULLVP, 1);
109768078Spendry 		vrele(ap->a_dvp);
109865965Spendry 		if (error)
109966051Spendry 			vput(vp);
110065963Spendry 		return (error);
110165963Spendry 	}
110265963Spendry 
110365963Spendry 	vput(ap->a_dvp);
110465963Spendry 	return (EROFS);
110565963Spendry }
110665963Spendry 
110765963Spendry int
110865963Spendry union_rmdir(ap)
110965963Spendry 	struct vop_rmdir_args /* {
111065963Spendry 		struct vnode *a_dvp;
111165963Spendry 		struct vnode *a_vp;
111265963Spendry 		struct componentname *a_cnp;
111365963Spendry 	} */ *ap;
111465963Spendry {
111565963Spendry 	int error;
111665963Spendry 	struct union_node *dun = VTOUNION(ap->a_dvp);
111765963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
111869447Smckusick 	struct componentname *cnp = ap->a_cnp;
111969447Smckusick 	struct proc *p = cnp->cn_proc;
112065963Spendry 
112167575Spendry 	if (dun->un_uppervp == NULLVP)
112267575Spendry 		panic("union rmdir: null upper vnode");
112367575Spendry 
112467575Spendry 	if (un->un_uppervp != NULLVP) {
112565963Spendry 		struct vnode *dvp = dun->un_uppervp;
112665963Spendry 		struct vnode *vp = un->un_uppervp;
112765963Spendry 
112869447Smckusick 		FIXUP(dun, p);
112965963Spendry 		VREF(dvp);
113066051Spendry 		dun->un_flags |= UN_KLOCK;
113165963Spendry 		vput(ap->a_dvp);
113269447Smckusick 		FIXUP(un, p);
113365963Spendry 		VREF(vp);
113466051Spendry 		un->un_flags |= UN_KLOCK;
113565963Spendry 		vput(ap->a_vp);
113665963Spendry 
113767784Spendry 		if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
113867784Spendry 			cnp->cn_flags |= DOWHITEOUT;
113966051Spendry 		error = VOP_RMDIR(dvp, vp, ap->a_cnp);
114066027Spendry 		if (!error)
114166027Spendry 			union_removed_upper(un);
114265963Spendry 	} else {
114369447Smckusick 		FIXUP(dun, p);
114467575Spendry 		error = union_mkwhiteout(
114567575Spendry 			MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
114667575Spendry 			dun->un_uppervp, ap->a_cnp, un->un_path);
114765963Spendry 		vput(ap->a_dvp);
114865963Spendry 		vput(ap->a_vp);
114965963Spendry 	}
115065963Spendry 
115165963Spendry 	return (error);
115265963Spendry }
115365963Spendry 
115465963Spendry int
115565963Spendry union_symlink(ap)
115665963Spendry 	struct vop_symlink_args /* {
115765963Spendry 		struct vnode *a_dvp;
115865963Spendry 		struct vnode **a_vpp;
115965963Spendry 		struct componentname *a_cnp;
116065963Spendry 		struct vattr *a_vap;
116165963Spendry 		char *a_target;
116265963Spendry 	} */ *ap;
116365963Spendry {
116465963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
116565963Spendry 	struct vnode *dvp = un->un_uppervp;
116669447Smckusick 	struct componentname *cnp = ap->a_cnp;
116769447Smckusick 	struct proc *p = cnp->cn_proc;
116865963Spendry 
116967076Spendry 	if (dvp != NULLVP) {
117065963Spendry 		int error;
117165963Spendry 		struct vnode *vp;
117265963Spendry 		struct mount *mp = ap->a_dvp->v_mount;
117365963Spendry 
117469447Smckusick 		FIXUP(un, p);
117565963Spendry 		VREF(dvp);
117666051Spendry 		un->un_flags |= UN_KLOCK;
117765963Spendry 		vput(ap->a_dvp);
117869447Smckusick 		error = VOP_SYMLINK(dvp, &vp, cnp, ap->a_vap, ap->a_target);
117965997Spendry 		*ap->a_vpp = NULLVP;
118065963Spendry 		return (error);
118165963Spendry 	}
118265963Spendry 
118365963Spendry 	vput(ap->a_dvp);
118465963Spendry 	return (EROFS);
118565963Spendry }
118665963Spendry 
118765935Spendry /*
118865935Spendry  * union_readdir works in concert with getdirentries and
118965935Spendry  * readdir(3) to provide a list of entries in the unioned
119065935Spendry  * directories.  getdirentries is responsible for walking
119165935Spendry  * down the union stack.  readdir(3) is responsible for
119265935Spendry  * eliminating duplicate names from the returned data stream.
119365935Spendry  */
119465935Spendry int
119565935Spendry union_readdir(ap)
119665935Spendry 	struct vop_readdir_args /* {
119765935Spendry 		struct vnodeop_desc *a_desc;
119865935Spendry 		struct vnode *a_vp;
119965935Spendry 		struct uio *a_uio;
120065935Spendry 		struct ucred *a_cred;
120167369Smckusick 		int *a_eofflag;
120267369Smckusick 		u_long *a_cookies;
120367369Smckusick 		int a_ncookies;
120465935Spendry 	} */ *ap;
120565935Spendry {
120669447Smckusick 	struct union_node *un = VTOUNION(ap->a_vp);
120769447Smckusick 	struct vnode *uvp = un->un_uppervp;
120869447Smckusick 	struct proc *p = ap->a_uio->uio_procp;
120965935Spendry 
121067369Smckusick 	if (uvp == NULLVP)
121167369Smckusick 		return (0);
121265935Spendry 
121369447Smckusick 	FIXUP(un, p);
121467369Smckusick 	ap->a_vp = uvp;
121567369Smckusick 	return (VOCALL(uvp->v_op, VOFFSET(vop_readdir), ap));
121665935Spendry }
121765935Spendry 
121865935Spendry int
121965963Spendry union_readlink(ap)
122065963Spendry 	struct vop_readlink_args /* {
122165963Spendry 		struct vnode *a_vp;
122265963Spendry 		struct uio *a_uio;
122365963Spendry 		struct ucred *a_cred;
122465963Spendry 	} */ *ap;
122565963Spendry {
122665963Spendry 	int error;
122769447Smckusick 	struct uio *uio = ap->a_uio;
122869447Smckusick 	struct proc *p = uio->uio_procp;
122965963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
123066051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
123165963Spendry 
123266051Spendry 	if (dolock)
123369447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
123466152Spendry 	else
123569447Smckusick 		FIXUP(VTOUNION(ap->a_vp), p);
123669447Smckusick 	error = VOP_READLINK(vp, uio, ap->a_cred);
123766051Spendry 	if (dolock)
123869447Smckusick 		VOP_UNLOCK(vp, 0, p);
123965963Spendry 
124065963Spendry 	return (error);
124165963Spendry }
124265963Spendry 
124365963Spendry int
124465963Spendry union_abortop(ap)
124565963Spendry 	struct vop_abortop_args /* {
124665963Spendry 		struct vnode *a_dvp;
124765963Spendry 		struct componentname *a_cnp;
124865963Spendry 	} */ *ap;
124965963Spendry {
125065963Spendry 	int error;
125169447Smckusick 	struct componentname *cnp = ap->a_cnp;
125269447Smckusick 	struct proc *p = cnp->cn_proc;
125365965Spendry 	struct vnode *vp = OTHERVP(ap->a_dvp);
125465963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
125565963Spendry 	int islocked = un->un_flags & UN_LOCKED;
125666051Spendry 	int dolock = (vp == LOWERVP(ap->a_dvp));
125765963Spendry 
125866152Spendry 	if (islocked) {
125966152Spendry 		if (dolock)
126069447Smckusick 			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
126166152Spendry 		else
126269447Smckusick 			FIXUP(VTOUNION(ap->a_dvp), p);
126366152Spendry 	}
126469447Smckusick 	error = VOP_ABORTOP(vp, cnp);
126566051Spendry 	if (islocked && dolock)
126669447Smckusick 		VOP_UNLOCK(vp, 0, p);
126765963Spendry 
126865963Spendry 	return (error);
126965963Spendry }
127065963Spendry 
127165963Spendry int
127265935Spendry union_inactive(ap)
127365935Spendry 	struct vop_inactive_args /* {
127465935Spendry 		struct vnode *a_vp;
127569447Smckusick 		struct proc *a_p;
127665935Spendry 	} */ *ap;
127765935Spendry {
1278*69588Spendry 	struct vnode *vp = ap->a_vp;
1279*69588Spendry 	struct proc *p = ap->a_p;
1280*69588Spendry 	struct union_node *un = VTOUNION(vp);
128168078Spendry 	struct vnode **vpp;
128265935Spendry 
128365935Spendry 	/*
128465935Spendry 	 * Do nothing (and _don't_ bypass).
128565935Spendry 	 * Wait to vrele lowervp until reclaim,
128665935Spendry 	 * so that until then our union_node is in the
128765935Spendry 	 * cache and reusable.
128865935Spendry 	 *
128965935Spendry 	 * NEEDSWORK: Someday, consider inactive'ing
129065935Spendry 	 * the lowervp and then trying to reactivate it
129165935Spendry 	 * with capabilities (v_id)
129265935Spendry 	 * like they do in the name lookup cache code.
129365935Spendry 	 * That's too much work for now.
129465935Spendry 	 */
129565989Spendry 
129668078Spendry 	if (un->un_dircache != 0) {
129768078Spendry 		for (vpp = un->un_dircache; *vpp != NULLVP; vpp++)
129868078Spendry 			vrele(*vpp);
129968078Spendry 		free(un->un_dircache, M_TEMP);
130068078Spendry 		un->un_dircache = 0;
130168078Spendry 	}
130268078Spendry 
1303*69588Spendry 	VOP_UNLOCK(vp, 0, p);
1304*69588Spendry 
130567073Spendry 	if ((un->un_flags & UN_CACHED) == 0)
1306*69588Spendry 		vgone(vp);
130767073Spendry 
130865935Spendry 	return (0);
130965935Spendry }
131065935Spendry 
131165935Spendry int
131265935Spendry union_reclaim(ap)
131365935Spendry 	struct vop_reclaim_args /* {
131465935Spendry 		struct vnode *a_vp;
131565935Spendry 	} */ *ap;
131665935Spendry {
131765935Spendry 
131866053Spendry 	union_freevp(ap->a_vp);
131966053Spendry 
132065935Spendry 	return (0);
132165935Spendry }
132265935Spendry 
132365963Spendry int
132465963Spendry union_lock(ap)
132565963Spendry 	struct vop_lock_args *ap;
132665963Spendry {
132766149Spendry 	struct vnode *vp = ap->a_vp;
132869447Smckusick 	struct proc *p = ap->a_p;
132969447Smckusick 	int flags = ap->a_flags;
133066149Spendry 	struct union_node *un;
1331*69588Spendry 	int error;
133265935Spendry 
133366149Spendry start:
1334*69588Spendry 
1335*69588Spendry 	if (flags & LK_INTERLOCK) {
133669447Smckusick 		simple_unlock(&vp->v_interlock);
1337*69588Spendry 		flags &= ~LK_INTERLOCK;
133866149Spendry 	}
133966149Spendry 
134066149Spendry 	un = VTOUNION(vp);
134166149Spendry 
134267076Spendry 	if (un->un_uppervp != NULLVP) {
134367230Spendry 		if (((un->un_flags & UN_ULOCK) == 0) &&
134467230Spendry 		    (vp->v_usecount != 0)) {
1345*69588Spendry 			error = vn_lock(un->un_uppervp, flags, p);
1346*69588Spendry 			if (error)
1347*69588Spendry 				return (error);
134866149Spendry 			un->un_flags |= UN_ULOCK;
134966051Spendry 		}
135066051Spendry #ifdef DIAGNOSTIC
135169389Spendry 		if (un->un_flags & UN_KLOCK) {
135268590Spendry 			vprint("union: dangling klock", vp);
135368590Spendry 			panic("union: dangling upper lock (%lx)", vp);
135469389Spendry 		}
135566051Spendry #endif
135666051Spendry 	}
135766051Spendry 
135866149Spendry 	if (un->un_flags & UN_LOCKED) {
135965963Spendry #ifdef DIAGNOSTIC
136065989Spendry 		if (curproc && un->un_pid == curproc->p_pid &&
136165989Spendry 			    un->un_pid > -1 && curproc->p_pid > -1)
136265989Spendry 			panic("union: locking against myself");
136365963Spendry #endif
136465963Spendry 		un->un_flags |= UN_WANT;
136569447Smckusick 		tsleep((caddr_t)&un->un_flags, PINOD, "unionlk2", 0);
136666149Spendry 		goto start;
136765963Spendry 	}
136865989Spendry 
136965963Spendry #ifdef DIAGNOSTIC
137065989Spendry 	if (curproc)
137165989Spendry 		un->un_pid = curproc->p_pid;
137265989Spendry 	else
137365989Spendry 		un->un_pid = -1;
137465963Spendry #endif
137566028Spendry 
137666149Spendry 	un->un_flags |= UN_LOCKED;
137766028Spendry 	return (0);
137865963Spendry }
137965963Spendry 
138068590Spendry /*
138168590Spendry  * When operations want to vput() a union node yet retain a lock on
138268590Spendry  * the upper vnode (say, to do some further operations like link(),
138368590Spendry  * mkdir(), ...), they set UN_KLOCK on the union node, then call
138468590Spendry  * vput() which calls VOP_UNLOCK() and comes here.  union_unlock()
138568590Spendry  * unlocks the union node (leaving the upper vnode alone), clears the
138668590Spendry  * KLOCK flag, and then returns to vput().  The caller then does whatever
138768590Spendry  * is left to do with the upper vnode, and ensures that it gets unlocked.
138868590Spendry  *
138968590Spendry  * If UN_KLOCK isn't set, then the upper vnode is unlocked here.
139068590Spendry  */
139165935Spendry int
139265963Spendry union_unlock(ap)
1393*69588Spendry 	struct vop_unlock_args /* {
1394*69588Spendry 		struct vnode *a_vp;
1395*69588Spendry 		int a_flags;
1396*69588Spendry 		struct proc *a_p;
1397*69588Spendry 	} */ *ap;
139865963Spendry {
139965963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
140069447Smckusick 	struct proc *p = ap->a_p;
140165963Spendry 
140265963Spendry #ifdef DIAGNOSTIC
140365965Spendry 	if ((un->un_flags & UN_LOCKED) == 0)
140465965Spendry 		panic("union: unlock unlocked node");
140565989Spendry 	if (curproc && un->un_pid != curproc->p_pid &&
140665989Spendry 			curproc->p_pid > -1 && un->un_pid > -1)
140765963Spendry 		panic("union: unlocking other process's union node");
140865963Spendry #endif
140965963Spendry 
141065963Spendry 	un->un_flags &= ~UN_LOCKED;
141166051Spendry 
141266051Spendry 	if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK)
141369447Smckusick 		VOP_UNLOCK(un->un_uppervp, 0, p);
141466051Spendry 
141566051Spendry 	un->un_flags &= ~(UN_ULOCK|UN_KLOCK);
141666051Spendry 
141765963Spendry 	if (un->un_flags & UN_WANT) {
141865963Spendry 		un->un_flags &= ~UN_WANT;
141965963Spendry 		wakeup((caddr_t) &un->un_flags);
142065963Spendry 	}
142165963Spendry 
142265963Spendry #ifdef DIAGNOSTIC
142365963Spendry 	un->un_pid = 0;
142465963Spendry #endif
142566028Spendry 
142666028Spendry 	return (0);
142765963Spendry }
142865963Spendry 
142965963Spendry int
143065963Spendry union_bmap(ap)
143165963Spendry 	struct vop_bmap_args /* {
143265963Spendry 		struct vnode *a_vp;
143365963Spendry 		daddr_t  a_bn;
143465963Spendry 		struct vnode **a_vpp;
143565963Spendry 		daddr_t *a_bnp;
143665963Spendry 		int *a_runp;
143765963Spendry 	} */ *ap;
143865963Spendry {
143965963Spendry 	int error;
144069447Smckusick 	struct proc *p = curproc;		/* XXX */
144165963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
144266051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
144365963Spendry 
144466051Spendry 	if (dolock)
144569447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
144666152Spendry 	else
144769447Smckusick 		FIXUP(VTOUNION(ap->a_vp), p);
144865963Spendry 	error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp);
144966051Spendry 	if (dolock)
145069447Smckusick 		VOP_UNLOCK(vp, 0, p);
145165963Spendry 
145265963Spendry 	return (error);
145365963Spendry }
145465963Spendry 
145565963Spendry int
145665935Spendry union_print(ap)
145765935Spendry 	struct vop_print_args /* {
145865935Spendry 		struct vnode *a_vp;
145965935Spendry 	} */ *ap;
146065935Spendry {
146165935Spendry 	struct vnode *vp = ap->a_vp;
146265935Spendry 
146365935Spendry 	printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n",
146465935Spendry 			vp, UPPERVP(vp), LOWERVP(vp));
146568590Spendry 	if (UPPERVP(vp) != NULLVP)
146668590Spendry 		vprint("union: upper", UPPERVP(vp));
146768590Spendry 	if (LOWERVP(vp) != NULLVP)
146868590Spendry 		vprint("union: lower", LOWERVP(vp));
146968590Spendry 
147065935Spendry 	return (0);
147165935Spendry }
147265935Spendry 
147365963Spendry int
147465963Spendry union_islocked(ap)
147565963Spendry 	struct vop_islocked_args /* {
147665963Spendry 		struct vnode *a_vp;
147765963Spendry 	} */ *ap;
147865963Spendry {
147965935Spendry 
148065963Spendry 	return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0);
148165963Spendry }
148265963Spendry 
148365935Spendry int
148465963Spendry union_pathconf(ap)
148565963Spendry 	struct vop_pathconf_args /* {
148665963Spendry 		struct vnode *a_vp;
148765963Spendry 		int a_name;
148865963Spendry 		int *a_retval;
148965935Spendry 	} */ *ap;
149065935Spendry {
149165935Spendry 	int error;
149269447Smckusick 	struct proc *p = curproc;		/* XXX */
149365963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
149466051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
149565935Spendry 
149666051Spendry 	if (dolock)
149769447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
149866152Spendry 	else
149969447Smckusick 		FIXUP(VTOUNION(ap->a_vp), p);
150065963Spendry 	error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
150166051Spendry 	if (dolock)
150269447Smckusick 		VOP_UNLOCK(vp, 0, p);
150365935Spendry 
150465963Spendry 	return (error);
150565963Spendry }
150665935Spendry 
150765963Spendry int
150865963Spendry union_advlock(ap)
150965963Spendry 	struct vop_advlock_args /* {
151065963Spendry 		struct vnode *a_vp;
151165963Spendry 		caddr_t  a_id;
151265963Spendry 		int  a_op;
151365963Spendry 		struct flock *a_fl;
151465963Spendry 		int  a_flags;
151565963Spendry 	} */ *ap;
151665963Spendry {
151765935Spendry 
151865963Spendry 	return (VOP_ADVLOCK(OTHERVP(ap->a_vp), ap->a_id, ap->a_op,
151965963Spendry 				ap->a_fl, ap->a_flags));
152065935Spendry }
152165935Spendry 
152265935Spendry 
152365935Spendry /*
152465963Spendry  * XXX - vop_strategy must be hand coded because it has no
152565935Spendry  * vnode in its arguments.
152665935Spendry  * This goes away with a merged VM/buffer cache.
152765935Spendry  */
152865935Spendry int
152965963Spendry union_strategy(ap)
153065963Spendry 	struct vop_strategy_args /* {
153165935Spendry 		struct buf *a_bp;
153265935Spendry 	} */ *ap;
153365935Spendry {
153465935Spendry 	struct buf *bp = ap->a_bp;
153565935Spendry 	int error;
153665935Spendry 	struct vnode *savedvp;
153765935Spendry 
153865935Spendry 	savedvp = bp->b_vp;
153965963Spendry 	bp->b_vp = OTHERVP(bp->b_vp);
154065935Spendry 
154165935Spendry #ifdef DIAGNOSTIC
154265997Spendry 	if (bp->b_vp == NULLVP)
154365963Spendry 		panic("union_strategy: nil vp");
154465963Spendry 	if (((bp->b_flags & B_READ) == 0) &&
154565963Spendry 	    (bp->b_vp == LOWERVP(savedvp)))
154665963Spendry 		panic("union_strategy: writing to lowervp");
154765935Spendry #endif
154865935Spendry 
154965963Spendry 	error = VOP_STRATEGY(bp);
155065935Spendry 	bp->b_vp = savedvp;
155165935Spendry 
155265935Spendry 	return (error);
155365935Spendry }
155465935Spendry 
155565935Spendry /*
155665935Spendry  * Global vfs data structures
155765935Spendry  */
155865935Spendry int (**union_vnodeop_p)();
155965965Spendry struct vnodeopv_entry_desc union_vnodeop_entries[] = {
156065963Spendry 	{ &vop_default_desc, vn_default_error },
156165963Spendry 	{ &vop_lookup_desc, union_lookup },		/* lookup */
156265963Spendry 	{ &vop_create_desc, union_create },		/* create */
156367575Spendry 	{ &vop_whiteout_desc, union_whiteout },		/* whiteout */
156465963Spendry 	{ &vop_mknod_desc, union_mknod },		/* mknod */
156565963Spendry 	{ &vop_open_desc, union_open },			/* open */
156665963Spendry 	{ &vop_close_desc, union_close },		/* close */
156765963Spendry 	{ &vop_access_desc, union_access },		/* access */
156865963Spendry 	{ &vop_getattr_desc, union_getattr },		/* getattr */
156965963Spendry 	{ &vop_setattr_desc, union_setattr },		/* setattr */
157065963Spendry 	{ &vop_read_desc, union_read },			/* read */
157165963Spendry 	{ &vop_write_desc, union_write },		/* write */
157267751Spendry 	{ &vop_lease_desc, union_lease },		/* lease */
157365963Spendry 	{ &vop_ioctl_desc, union_ioctl },		/* ioctl */
157465963Spendry 	{ &vop_select_desc, union_select },		/* select */
157569389Spendry 	{ &vop_revoke_desc, union_revoke },		/* revoke */
157665963Spendry 	{ &vop_mmap_desc, union_mmap },			/* mmap */
157765963Spendry 	{ &vop_fsync_desc, union_fsync },		/* fsync */
157865963Spendry 	{ &vop_seek_desc, union_seek },			/* seek */
157965963Spendry 	{ &vop_remove_desc, union_remove },		/* remove */
158065963Spendry 	{ &vop_link_desc, union_link },			/* link */
158165963Spendry 	{ &vop_rename_desc, union_rename },		/* rename */
158265963Spendry 	{ &vop_mkdir_desc, union_mkdir },		/* mkdir */
158365963Spendry 	{ &vop_rmdir_desc, union_rmdir },		/* rmdir */
158465963Spendry 	{ &vop_symlink_desc, union_symlink },		/* symlink */
158565963Spendry 	{ &vop_readdir_desc, union_readdir },		/* readdir */
158665963Spendry 	{ &vop_readlink_desc, union_readlink },		/* readlink */
158765963Spendry 	{ &vop_abortop_desc, union_abortop },		/* abortop */
158865963Spendry 	{ &vop_inactive_desc, union_inactive },		/* inactive */
158965963Spendry 	{ &vop_reclaim_desc, union_reclaim },		/* reclaim */
159065963Spendry 	{ &vop_lock_desc, union_lock },			/* lock */
159165963Spendry 	{ &vop_unlock_desc, union_unlock },		/* unlock */
159265963Spendry 	{ &vop_bmap_desc, union_bmap },			/* bmap */
159365963Spendry 	{ &vop_strategy_desc, union_strategy },		/* strategy */
159465963Spendry 	{ &vop_print_desc, union_print },		/* print */
159565963Spendry 	{ &vop_islocked_desc, union_islocked },		/* islocked */
159665963Spendry 	{ &vop_pathconf_desc, union_pathconf },		/* pathconf */
159765963Spendry 	{ &vop_advlock_desc, union_advlock },		/* advlock */
159865963Spendry #ifdef notdef
159965963Spendry 	{ &vop_blkatoff_desc, union_blkatoff },		/* blkatoff */
160065963Spendry 	{ &vop_valloc_desc, union_valloc },		/* valloc */
160165963Spendry 	{ &vop_vfree_desc, union_vfree },		/* vfree */
160265963Spendry 	{ &vop_truncate_desc, union_truncate },		/* truncate */
160365963Spendry 	{ &vop_update_desc, union_update },		/* update */
160465963Spendry 	{ &vop_bwrite_desc, union_bwrite },		/* bwrite */
160565963Spendry #endif
160665935Spendry 	{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
160765935Spendry };
160865935Spendry struct vnodeopv_desc union_vnodeop_opv_desc =
160965935Spendry 	{ &union_vnodeop_p, union_vnodeop_entries };
1610