xref: /csrg-svn/sys/miscfs/union/union_vnops.c (revision 69978)
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*69978Spendry  *	@(#)union_vnops.c	8.32 (Berkeley) 06/23/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
union_fixup(un,p)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
union_lookup1(udvp,dvpp,vpp,cnp)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
union_lookup(ap)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
union_create(ap)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
union_whiteout(ap)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
union_mknod(ap)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
union_open(ap)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
union_close(ap)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 
49969590Smckusick 	if ((vp = un->un_uppervp) == NULLVP) {
50066027Spendry #ifdef UNION_DIAGNOSTIC
50166027Spendry 		if (un->un_openl <= 0)
50266027Spendry 			panic("union: un_openl cnt");
50365997Spendry #endif
50466027Spendry 		--un->un_openl;
50565997Spendry 		vp = un->un_lowervp;
50665997Spendry 	}
50766027Spendry 
50869590Smckusick 	ap->a_vp = vp;
50969590Smckusick 	return (VCALL(vp, VOFFSET(vop_close), ap));
51065963Spendry }
51165963Spendry 
51265935Spendry /*
51365963Spendry  * Check access permission on the union vnode.
51465963Spendry  * The access check being enforced is to check
51565963Spendry  * against both the underlying vnode, and any
51665963Spendry  * copied vnode.  This ensures that no additional
51765963Spendry  * file permissions are given away simply because
51865963Spendry  * the user caused an implicit file copy.
51965963Spendry  */
52065963Spendry int
union_access(ap)52165963Spendry union_access(ap)
52265963Spendry 	struct vop_access_args /* {
52365963Spendry 		struct vnodeop_desc *a_desc;
52465963Spendry 		struct vnode *a_vp;
52565963Spendry 		int a_mode;
52665963Spendry 		struct ucred *a_cred;
52765963Spendry 		struct proc *a_p;
52865963Spendry 	} */ *ap;
52965963Spendry {
53065963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
53169447Smckusick 	struct proc *p = ap->a_p;
53266149Spendry 	int error = EACCES;
53365963Spendry 	struct vnode *vp;
53465963Spendry 
53567076Spendry 	if ((vp = un->un_uppervp) != NULLVP) {
53669447Smckusick 		FIXUP(un, p);
53769590Smckusick 		ap->a_vp = vp;
53869590Smckusick 		return (VCALL(vp, VOFFSET(vop_access), ap));
53966152Spendry 	}
54066152Spendry 
54167076Spendry 	if ((vp = un->un_lowervp) != NULLVP) {
54269447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
54369590Smckusick 		ap->a_vp = vp;
54469590Smckusick 		error = VCALL(vp, VOFFSET(vop_access), ap);
54566152Spendry 		if (error == 0) {
54666152Spendry 			struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
54766152Spendry 
54869590Smckusick 			if (um->um_op == UNMNT_BELOW) {
54969590Smckusick 				ap->a_cred = um->um_cred;
55069590Smckusick 				error = VCALL(vp, VOFFSET(vop_access), ap);
55169590Smckusick 			}
55266152Spendry 		}
55369447Smckusick 		VOP_UNLOCK(vp, 0, p);
55465963Spendry 		if (error)
55565963Spendry 			return (error);
55665963Spendry 	}
55765963Spendry 
55865965Spendry 	return (error);
55965963Spendry }
56065963Spendry 
56165963Spendry /*
56267109Spendry  * We handle getattr only to change the fsid and
56367109Spendry  * track object sizes
56465935Spendry  */
56565935Spendry int
union_getattr(ap)56665935Spendry union_getattr(ap)
56765935Spendry 	struct vop_getattr_args /* {
56865935Spendry 		struct vnode *a_vp;
56965935Spendry 		struct vattr *a_vap;
57065935Spendry 		struct ucred *a_cred;
57165935Spendry 		struct proc *a_p;
57265935Spendry 	} */ *ap;
57365935Spendry {
57465935Spendry 	int error;
57566062Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
57666062Spendry 	struct vnode *vp = un->un_uppervp;
57769447Smckusick 	struct proc *p = ap->a_p;
57866062Spendry 	struct vattr *vap;
57966062Spendry 	struct vattr va;
58065935Spendry 
58166062Spendry 
58266062Spendry 	/*
58366062Spendry 	 * Some programs walk the filesystem hierarchy by counting
58466062Spendry 	 * links to directories to avoid stat'ing all the time.
58566062Spendry 	 * This means the link count on directories needs to be "correct".
58666062Spendry 	 * The only way to do that is to call getattr on both layers
58766062Spendry 	 * and fix up the link count.  The link count will not necessarily
58866062Spendry 	 * be accurate but will be large enough to defeat the tree walkers.
58966062Spendry 	 */
59066062Spendry 
59166062Spendry 	vap = ap->a_vap;
59266062Spendry 
59366062Spendry 	vp = un->un_uppervp;
59466062Spendry 	if (vp != NULLVP) {
59567073Spendry 		/*
59667073Spendry 		 * It's not clear whether VOP_GETATTR is to be
59767073Spendry 		 * called with the vnode locked or not.  stat() calls
59867073Spendry 		 * it with (vp) locked, and fstat calls it with
59967073Spendry 		 * (vp) unlocked.
60067073Spendry 		 * In the mean time, compensate here by checking
60167073Spendry 		 * the union_node's lock flag.
60267073Spendry 		 */
60367073Spendry 		if (un->un_flags & UN_LOCKED)
60469447Smckusick 			FIXUP(un, p);
60567073Spendry 
60666062Spendry 		error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
60766062Spendry 		if (error)
60866062Spendry 			return (error);
60967109Spendry 		union_newsize(ap->a_vp, vap->va_size, VNOVAL);
61066062Spendry 	}
61166062Spendry 
61266062Spendry 	if (vp == NULLVP) {
61366062Spendry 		vp = un->un_lowervp;
61466062Spendry 	} else if (vp->v_type == VDIR) {
61566062Spendry 		vp = un->un_lowervp;
61666062Spendry 		vap = &va;
61766062Spendry 	} else {
61866062Spendry 		vp = NULLVP;
61966062Spendry 	}
62066062Spendry 
62166062Spendry 	if (vp != NULLVP) {
62266062Spendry 		error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
62366062Spendry 		if (error)
62466062Spendry 			return (error);
62567109Spendry 		union_newsize(ap->a_vp, VNOVAL, vap->va_size);
62666062Spendry 	}
62765965Spendry 
62866062Spendry 	if ((vap != ap->a_vap) && (vap->va_type == VDIR))
62966062Spendry 		ap->a_vap->va_nlink += vap->va_nlink;
63066062Spendry 
63167400Spendry 	ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
63265935Spendry 	return (0);
63365935Spendry }
63465935Spendry 
63565963Spendry int
union_setattr(ap)63665965Spendry union_setattr(ap)
63765963Spendry 	struct vop_setattr_args /* {
63865963Spendry 		struct vnode *a_vp;
63965963Spendry 		struct vattr *a_vap;
64065963Spendry 		struct ucred *a_cred;
64165963Spendry 		struct proc *a_p;
64265963Spendry 	} */ *ap;
64365963Spendry {
64465963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
64569447Smckusick 	struct proc *p = ap->a_p;
64665963Spendry 	int error;
64765963Spendry 
64866057Spendry 	/*
64966057Spendry 	 * Handle case of truncating lower object to zero size,
65066057Spendry 	 * by creating a zero length upper object.  This is to
65166057Spendry 	 * handle the case of open with O_TRUNC and O_CREAT.
65266057Spendry 	 */
65366057Spendry 	if ((un->un_uppervp == NULLVP) &&
65466057Spendry 	    /* assert(un->un_lowervp != NULLVP) */
65567575Spendry 	    (un->un_lowervp->v_type == VREG)) {
65667575Spendry 		error = union_copyup(un, (ap->a_vap->va_size != 0),
65767575Spendry 						ap->a_cred, ap->a_p);
65866057Spendry 		if (error)
65966057Spendry 			return (error);
66066057Spendry 	}
66166057Spendry 
66266057Spendry 	/*
66366057Spendry 	 * Try to set attributes in upper layer,
66466057Spendry 	 * otherwise return read-only filesystem error.
66566057Spendry 	 */
66666057Spendry 	if (un->un_uppervp != NULLVP) {
66769447Smckusick 		FIXUP(un, p);
66865963Spendry 		error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
66965963Spendry 					ap->a_cred, ap->a_p);
67067109Spendry 		if ((error == 0) && (ap->a_vap->va_size != VNOVAL))
67167109Spendry 			union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL);
67265963Spendry 	} else {
67365963Spendry 		error = EROFS;
67465963Spendry 	}
67565963Spendry 
67665963Spendry 	return (error);
67765963Spendry }
67865963Spendry 
67965963Spendry int
union_read(ap)68065963Spendry union_read(ap)
68165963Spendry 	struct vop_read_args /* {
68265963Spendry 		struct vnode *a_vp;
68365963Spendry 		struct uio *a_uio;
68465963Spendry 		int  a_ioflag;
68565963Spendry 		struct ucred *a_cred;
68665963Spendry 	} */ *ap;
68765963Spendry {
68865963Spendry 	int error;
68969447Smckusick 	struct proc *p = ap->a_uio->uio_procp;
69065963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
69166051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
69265963Spendry 
69366051Spendry 	if (dolock)
69469447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
69566152Spendry 	else
69669447Smckusick 		FIXUP(VTOUNION(ap->a_vp), p);
69765963Spendry 	error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
69866051Spendry 	if (dolock)
69969447Smckusick 		VOP_UNLOCK(vp, 0, p);
70065963Spendry 
70167109Spendry 	/*
70267109Spendry 	 * XXX
70367109Spendry 	 * perhaps the size of the underlying object has changed under
70467109Spendry 	 * our feet.  take advantage of the offset information present
70567109Spendry 	 * in the uio structure.
70667109Spendry 	 */
70767109Spendry 	if (error == 0) {
70867109Spendry 		struct union_node *un = VTOUNION(ap->a_vp);
70967109Spendry 		off_t cur = ap->a_uio->uio_offset;
71067109Spendry 
71167109Spendry 		if (vp == un->un_uppervp) {
71267109Spendry 			if (cur > un->un_uppersz)
71367109Spendry 				union_newsize(ap->a_vp, cur, VNOVAL);
71467109Spendry 		} else {
71567109Spendry 			if (cur > un->un_lowersz)
71667109Spendry 				union_newsize(ap->a_vp, VNOVAL, cur);
71767109Spendry 		}
71867109Spendry 	}
71967109Spendry 
72065963Spendry 	return (error);
72165963Spendry }
72265963Spendry 
72365963Spendry int
union_write(ap)72465963Spendry union_write(ap)
72565963Spendry 	struct vop_read_args /* {
72665963Spendry 		struct vnode *a_vp;
72765963Spendry 		struct uio *a_uio;
72865963Spendry 		int  a_ioflag;
72965963Spendry 		struct ucred *a_cred;
73065963Spendry 	} */ *ap;
73165963Spendry {
73265963Spendry 	int error;
73367575Spendry 	struct vnode *vp;
73467575Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
73569447Smckusick 	struct proc *p = ap->a_uio->uio_procp;
73665963Spendry 
73767575Spendry 	vp = UPPERVP(ap->a_vp);
73867575Spendry 	if (vp == NULLVP)
73967575Spendry 		panic("union: missing upper layer in write");
74067575Spendry 
74169447Smckusick 	FIXUP(un, p);
74265963Spendry 	error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
74365963Spendry 
74467109Spendry 	/*
74567109Spendry 	 * the size of the underlying object may be changed by the
74667109Spendry 	 * write.
74767109Spendry 	 */
74867109Spendry 	if (error == 0) {
74967109Spendry 		off_t cur = ap->a_uio->uio_offset;
75067109Spendry 
75167575Spendry 		if (cur > un->un_uppersz)
75267575Spendry 			union_newsize(ap->a_vp, cur, VNOVAL);
75367109Spendry 	}
75467109Spendry 
75565963Spendry 	return (error);
75665963Spendry }
75765963Spendry 
75867751Spendry union_lease(ap)
75967751Spendry 	struct vop_lease_args /* {
76067751Spendry 		struct vnode *a_vp;
76167751Spendry 		struct proc *a_p;
76267751Spendry 		struct ucred *a_cred;
76367751Spendry 		int a_flag;
76467751Spendry 	} */ *ap;
76567751Spendry {
76669590Smckusick 	register struct vnode *ovp = OTHERVP(ap->a_vp);
76767751Spendry 
76869590Smckusick 	ap->a_vp = ovp;
76969590Smckusick 	return (VCALL(ovp, VOFFSET(vop_lease), ap));
77067751Spendry }
77167751Spendry 
77265963Spendry int
union_ioctl(ap)77365963Spendry union_ioctl(ap)
77465963Spendry 	struct vop_ioctl_args /* {
77565963Spendry 		struct vnode *a_vp;
77665963Spendry 		int  a_command;
77765963Spendry 		caddr_t  a_data;
77865963Spendry 		int  a_fflag;
77965963Spendry 		struct ucred *a_cred;
78065963Spendry 		struct proc *a_p;
78165963Spendry 	} */ *ap;
78265963Spendry {
78369590Smckusick 	register struct vnode *ovp = OTHERVP(ap->a_vp);
78465963Spendry 
78569590Smckusick 	ap->a_vp = ovp;
78669590Smckusick 	return (VCALL(ovp, VOFFSET(vop_ioctl), ap));
78765963Spendry }
78865963Spendry 
78965963Spendry int
union_select(ap)79065963Spendry union_select(ap)
79165963Spendry 	struct vop_select_args /* {
79265963Spendry 		struct vnode *a_vp;
79365963Spendry 		int  a_which;
79465963Spendry 		int  a_fflags;
79565963Spendry 		struct ucred *a_cred;
79665963Spendry 		struct proc *a_p;
79765963Spendry 	} */ *ap;
79865963Spendry {
79969590Smckusick 	register struct vnode *ovp = OTHERVP(ap->a_vp);
80065963Spendry 
80169590Smckusick 	ap->a_vp = ovp;
80269590Smckusick 	return (VCALL(ovp, VOFFSET(vop_select), ap));
80365963Spendry }
80465963Spendry 
80565963Spendry int
union_revoke(ap)80669389Spendry union_revoke(ap)
80769389Spendry 	struct vop_revoke_args /* {
80869389Spendry 		struct vnode *a_vp;
80969389Spendry 		int a_flags;
81069447Smckusick 		struct proc *a_p;
81169389Spendry 	} */ *ap;
81269389Spendry {
81369389Spendry 	struct vnode *vp = ap->a_vp;
81469389Spendry 
81569389Spendry 	if (UPPERVP(vp))
81669389Spendry 		VOP_REVOKE(UPPERVP(vp), ap->a_flags);
81769389Spendry 	if (LOWERVP(vp))
81869447Smckusick 		VOP_REVOKE(LOWERVP(vp), ap->a_flags);
81969389Spendry 	vgone(vp);
82069389Spendry }
82169389Spendry 
82269389Spendry int
union_mmap(ap)82365963Spendry union_mmap(ap)
82465963Spendry 	struct vop_mmap_args /* {
82565963Spendry 		struct vnode *a_vp;
82665963Spendry 		int  a_fflags;
82765963Spendry 		struct ucred *a_cred;
82865963Spendry 		struct proc *a_p;
82965963Spendry 	} */ *ap;
83065963Spendry {
83169590Smckusick 	register struct vnode *ovp = OTHERVP(ap->a_vp);
83265963Spendry 
83369590Smckusick 	ap->a_vp = ovp;
83469590Smckusick 	return (VCALL(ovp, VOFFSET(vop_mmap), ap));
83565963Spendry }
83665963Spendry 
83765963Spendry int
union_fsync(ap)83865963Spendry union_fsync(ap)
83965963Spendry 	struct vop_fsync_args /* {
84065963Spendry 		struct vnode *a_vp;
84165963Spendry 		struct ucred *a_cred;
84265963Spendry 		int  a_waitfor;
84365963Spendry 		struct proc *a_p;
84465963Spendry 	} */ *ap;
84565963Spendry {
84665963Spendry 	int error = 0;
84769447Smckusick 	struct proc *p = ap->a_p;
84865963Spendry 	struct vnode *targetvp = OTHERVP(ap->a_vp);
84965963Spendry 
85067076Spendry 	if (targetvp != NULLVP) {
85166051Spendry 		int dolock = (targetvp == LOWERVP(ap->a_vp));
85266051Spendry 
85366051Spendry 		if (dolock)
85469447Smckusick 			vn_lock(targetvp, LK_EXCLUSIVE | LK_RETRY, p);
85566152Spendry 		else
85669447Smckusick 			FIXUP(VTOUNION(ap->a_vp), p);
85769447Smckusick 		error = VOP_FSYNC(targetvp, ap->a_cred, ap->a_waitfor, p);
85866051Spendry 		if (dolock)
85969447Smckusick 			VOP_UNLOCK(targetvp, 0, p);
86065963Spendry 	}
86165963Spendry 
86265963Spendry 	return (error);
86365963Spendry }
86465963Spendry 
86565963Spendry int
union_seek(ap)86665963Spendry union_seek(ap)
86765963Spendry 	struct vop_seek_args /* {
86865963Spendry 		struct vnode *a_vp;
86965963Spendry 		off_t  a_oldoff;
87065963Spendry 		off_t  a_newoff;
87165963Spendry 		struct ucred *a_cred;
87265963Spendry 	} */ *ap;
87365963Spendry {
87469590Smckusick 	register struct vnode *ovp = OTHERVP(ap->a_vp);
87565963Spendry 
87669590Smckusick 	ap->a_vp = ovp;
87769590Smckusick 	return (VCALL(ovp, VOFFSET(vop_seek), ap));
87865963Spendry }
87965963Spendry 
88065963Spendry int
union_remove(ap)88165963Spendry union_remove(ap)
88265963Spendry 	struct vop_remove_args /* {
88365963Spendry 		struct vnode *a_dvp;
88465963Spendry 		struct vnode *a_vp;
88565963Spendry 		struct componentname *a_cnp;
88665963Spendry 	} */ *ap;
88765963Spendry {
88865963Spendry 	int error;
88965963Spendry 	struct union_node *dun = VTOUNION(ap->a_dvp);
89065963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
89169447Smckusick 	struct componentname *cnp = ap->a_cnp;
89269447Smckusick 	struct proc *p = cnp->cn_proc;
89365963Spendry 
89467575Spendry 	if (dun->un_uppervp == NULLVP)
89567575Spendry 		panic("union remove: null upper vnode");
89667575Spendry 
89767575Spendry 	if (un->un_uppervp != NULLVP) {
89865963Spendry 		struct vnode *dvp = dun->un_uppervp;
89965963Spendry 		struct vnode *vp = un->un_uppervp;
90065963Spendry 
90169447Smckusick 		FIXUP(dun, p);
90265963Spendry 		VREF(dvp);
90366051Spendry 		dun->un_flags |= UN_KLOCK;
90465963Spendry 		vput(ap->a_dvp);
90569447Smckusick 		FIXUP(un, p);
90665963Spendry 		VREF(vp);
90766051Spendry 		un->un_flags |= UN_KLOCK;
90865963Spendry 		vput(ap->a_vp);
90965963Spendry 
91067784Spendry 		if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
91167784Spendry 			cnp->cn_flags |= DOWHITEOUT;
91267784Spendry 		error = VOP_REMOVE(dvp, vp, cnp);
91366027Spendry 		if (!error)
91466027Spendry 			union_removed_upper(un);
91565963Spendry 	} else {
91669447Smckusick 		FIXUP(dun, p);
91767575Spendry 		error = union_mkwhiteout(
91867575Spendry 			MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
91967575Spendry 			dun->un_uppervp, ap->a_cnp, un->un_path);
92065963Spendry 		vput(ap->a_dvp);
92165963Spendry 		vput(ap->a_vp);
92265963Spendry 	}
92365963Spendry 
92465963Spendry 	return (error);
92565963Spendry }
92665963Spendry 
92765963Spendry int
union_link(ap)92865963Spendry union_link(ap)
92965963Spendry 	struct vop_link_args /* {
93065963Spendry 		struct vnode *a_vp;
93165963Spendry 		struct vnode *a_tdvp;
93265963Spendry 		struct componentname *a_cnp;
93365963Spendry 	} */ *ap;
93465963Spendry {
93567169Spendry 	int error = 0;
93669447Smckusick 	struct componentname *cnp = ap->a_cnp;
93769447Smckusick 	struct proc *p = cnp->cn_proc;
93867169Spendry 	struct union_node *un;
93967169Spendry 	struct vnode *vp;
94067169Spendry 	struct vnode *tdvp;
94165963Spendry 
94268535Smckusick 	un = VTOUNION(ap->a_tdvp);
94365963Spendry 
94468535Smckusick 	if (ap->a_tdvp->v_op != ap->a_vp->v_op) {
94568535Smckusick 		vp = ap->a_vp;
94667169Spendry 	} else {
94768535Smckusick 		struct union_node *tun = VTOUNION(ap->a_vp);
94868535Smckusick 		if (tun->un_uppervp == NULLVP) {
94969447Smckusick 			vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p);
95068535Smckusick 			if (un->un_uppervp == tun->un_dirvp) {
95167169Spendry 				un->un_flags &= ~UN_ULOCK;
95269447Smckusick 				VOP_UNLOCK(un->un_uppervp, 0, p);
95367169Spendry 			}
95469447Smckusick 			error = union_copyup(tun, 1, cnp->cn_cred, p);
95568535Smckusick 			if (un->un_uppervp == tun->un_dirvp) {
95669447Smckusick 				vn_lock(un->un_uppervp,
95769447Smckusick 						LK_EXCLUSIVE | LK_RETRY, p);
95867169Spendry 				un->un_flags |= UN_ULOCK;
95967169Spendry 			}
96069447Smckusick 			VOP_UNLOCK(ap->a_vp, 0, p);
96167169Spendry 		}
96268535Smckusick 		vp = tun->un_uppervp;
96367169Spendry 	}
96465963Spendry 
96568535Smckusick 	tdvp = un->un_uppervp;
96668535Smckusick 	if (tdvp == NULLVP)
96767169Spendry 		error = EROFS;
96867169Spendry 
96967169Spendry 	if (error) {
97068535Smckusick 		vput(ap->a_tdvp);
97167169Spendry 		return (error);
97265963Spendry 	}
97365963Spendry 
97469447Smckusick 	FIXUP(un, p);
97568535Smckusick 	VREF(tdvp);
97667169Spendry 	un->un_flags |= UN_KLOCK;
97768535Smckusick 	vput(ap->a_tdvp);
97867169Spendry 
97969447Smckusick 	return (VOP_LINK(vp, tdvp, cnp));
98065963Spendry }
98165963Spendry 
98265963Spendry int
union_rename(ap)98365963Spendry union_rename(ap)
98465963Spendry 	struct vop_rename_args  /* {
98565963Spendry 		struct vnode *a_fdvp;
98665963Spendry 		struct vnode *a_fvp;
98765963Spendry 		struct componentname *a_fcnp;
98865963Spendry 		struct vnode *a_tdvp;
98965963Spendry 		struct vnode *a_tvp;
99065963Spendry 		struct componentname *a_tcnp;
99165963Spendry 	} */ *ap;
99265963Spendry {
99365963Spendry 	int error;
99465963Spendry 
99565963Spendry 	struct vnode *fdvp = ap->a_fdvp;
99665963Spendry 	struct vnode *fvp = ap->a_fvp;
99765963Spendry 	struct vnode *tdvp = ap->a_tdvp;
99865963Spendry 	struct vnode *tvp = ap->a_tvp;
99965963Spendry 
100065963Spendry 	if (fdvp->v_op == union_vnodeop_p) {	/* always true */
100165963Spendry 		struct union_node *un = VTOUNION(fdvp);
100265997Spendry 		if (un->un_uppervp == NULLVP) {
100367575Spendry 			/*
100467575Spendry 			 * this should never happen in normal
100567575Spendry 			 * operation but might if there was
100667575Spendry 			 * a problem creating the top-level shadow
100767575Spendry 			 * directory.
100867575Spendry 			 */
100967575Spendry 			error = EXDEV;
101065963Spendry 			goto bad;
101165963Spendry 		}
101265963Spendry 
101365963Spendry 		fdvp = un->un_uppervp;
101465963Spendry 		VREF(fdvp);
101565963Spendry 		vrele(ap->a_fdvp);
101665963Spendry 	}
101765963Spendry 
101865963Spendry 	if (fvp->v_op == union_vnodeop_p) {	/* always true */
101965963Spendry 		struct union_node *un = VTOUNION(fvp);
102065997Spendry 		if (un->un_uppervp == NULLVP) {
102167575Spendry 			/* XXX: should do a copyup */
102267575Spendry 			error = EXDEV;
102365963Spendry 			goto bad;
102465963Spendry 		}
102565963Spendry 
102667575Spendry 		if (un->un_lowervp != NULLVP)
102767575Spendry 			ap->a_fcnp->cn_flags |= DOWHITEOUT;
102867575Spendry 
102965963Spendry 		fvp = un->un_uppervp;
103065963Spendry 		VREF(fvp);
103165963Spendry 		vrele(ap->a_fvp);
103265963Spendry 	}
103365963Spendry 
103465963Spendry 	if (tdvp->v_op == union_vnodeop_p) {
103565963Spendry 		struct union_node *un = VTOUNION(tdvp);
103665997Spendry 		if (un->un_uppervp == NULLVP) {
103767076Spendry 			/*
103867076Spendry 			 * this should never happen in normal
103967076Spendry 			 * operation but might if there was
104067076Spendry 			 * a problem creating the top-level shadow
104167076Spendry 			 * directory.
104267076Spendry 			 */
104367575Spendry 			error = EXDEV;
104465963Spendry 			goto bad;
104565963Spendry 		}
104665963Spendry 
104765963Spendry 		tdvp = un->un_uppervp;
104865963Spendry 		VREF(tdvp);
104966051Spendry 		un->un_flags |= UN_KLOCK;
105065997Spendry 		vput(ap->a_tdvp);
105165963Spendry 	}
105265963Spendry 
105367076Spendry 	if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) {
105465963Spendry 		struct union_node *un = VTOUNION(tvp);
105565963Spendry 
105665963Spendry 		tvp = un->un_uppervp;
105767076Spendry 		if (tvp != NULLVP) {
105867076Spendry 			VREF(tvp);
105967076Spendry 			un->un_flags |= UN_KLOCK;
106067076Spendry 		}
106165963Spendry 		vput(ap->a_tvp);
106265963Spendry 	}
106365963Spendry 
106465963Spendry 	return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));
106565963Spendry 
106665963Spendry bad:
106765963Spendry 	vrele(fdvp);
106865963Spendry 	vrele(fvp);
106965963Spendry 	vput(tdvp);
107067076Spendry 	if (tvp != NULLVP)
107165963Spendry 		vput(tvp);
107265963Spendry 
107365963Spendry 	return (error);
107465963Spendry }
107565963Spendry 
107665963Spendry int
union_mkdir(ap)107765963Spendry union_mkdir(ap)
107865963Spendry 	struct vop_mkdir_args /* {
107965963Spendry 		struct vnode *a_dvp;
108065963Spendry 		struct vnode **a_vpp;
108165963Spendry 		struct componentname *a_cnp;
108265963Spendry 		struct vattr *a_vap;
108365963Spendry 	} */ *ap;
108465963Spendry {
108565963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
108665963Spendry 	struct vnode *dvp = un->un_uppervp;
108769447Smckusick 	struct componentname *cnp = ap->a_cnp;
108869447Smckusick 	struct proc *p = cnp->cn_proc;
108965963Spendry 
109067076Spendry 	if (dvp != NULLVP) {
109165963Spendry 		int error;
109265963Spendry 		struct vnode *vp;
109365963Spendry 
109469447Smckusick 		FIXUP(un, p);
109565963Spendry 		VREF(dvp);
109666051Spendry 		un->un_flags |= UN_KLOCK;
109769447Smckusick 		VOP_UNLOCK(ap->a_dvp, 0, p);
109869447Smckusick 		error = VOP_MKDIR(dvp, &vp, cnp, ap->a_vap);
109968078Spendry 		if (error) {
110068078Spendry 			vrele(ap->a_dvp);
110165963Spendry 			return (error);
110268078Spendry 		}
110365963Spendry 
110469447Smckusick 		error = union_allocvp(ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp,
110569447Smckusick 				NULLVP, cnp, vp, NULLVP, 1);
110668078Spendry 		vrele(ap->a_dvp);
110765965Spendry 		if (error)
110866051Spendry 			vput(vp);
110965963Spendry 		return (error);
111065963Spendry 	}
111165963Spendry 
111265963Spendry 	vput(ap->a_dvp);
111365963Spendry 	return (EROFS);
111465963Spendry }
111565963Spendry 
111665963Spendry int
union_rmdir(ap)111765963Spendry union_rmdir(ap)
111865963Spendry 	struct vop_rmdir_args /* {
111965963Spendry 		struct vnode *a_dvp;
112065963Spendry 		struct vnode *a_vp;
112165963Spendry 		struct componentname *a_cnp;
112265963Spendry 	} */ *ap;
112365963Spendry {
112465963Spendry 	int error;
112565963Spendry 	struct union_node *dun = VTOUNION(ap->a_dvp);
112665963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
112769447Smckusick 	struct componentname *cnp = ap->a_cnp;
112869447Smckusick 	struct proc *p = cnp->cn_proc;
112965963Spendry 
113067575Spendry 	if (dun->un_uppervp == NULLVP)
113167575Spendry 		panic("union rmdir: null upper vnode");
113267575Spendry 
113367575Spendry 	if (un->un_uppervp != NULLVP) {
113465963Spendry 		struct vnode *dvp = dun->un_uppervp;
113565963Spendry 		struct vnode *vp = un->un_uppervp;
113665963Spendry 
113769447Smckusick 		FIXUP(dun, p);
113865963Spendry 		VREF(dvp);
113966051Spendry 		dun->un_flags |= UN_KLOCK;
114065963Spendry 		vput(ap->a_dvp);
114169447Smckusick 		FIXUP(un, p);
114265963Spendry 		VREF(vp);
114366051Spendry 		un->un_flags |= UN_KLOCK;
114465963Spendry 		vput(ap->a_vp);
114565963Spendry 
114667784Spendry 		if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
114767784Spendry 			cnp->cn_flags |= DOWHITEOUT;
114866051Spendry 		error = VOP_RMDIR(dvp, vp, ap->a_cnp);
114966027Spendry 		if (!error)
115066027Spendry 			union_removed_upper(un);
115165963Spendry 	} else {
115269447Smckusick 		FIXUP(dun, p);
115367575Spendry 		error = union_mkwhiteout(
115467575Spendry 			MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
115567575Spendry 			dun->un_uppervp, ap->a_cnp, un->un_path);
115665963Spendry 		vput(ap->a_dvp);
115765963Spendry 		vput(ap->a_vp);
115865963Spendry 	}
115965963Spendry 
116065963Spendry 	return (error);
116165963Spendry }
116265963Spendry 
116365963Spendry int
union_symlink(ap)116465963Spendry union_symlink(ap)
116565963Spendry 	struct vop_symlink_args /* {
116665963Spendry 		struct vnode *a_dvp;
116765963Spendry 		struct vnode **a_vpp;
116865963Spendry 		struct componentname *a_cnp;
116965963Spendry 		struct vattr *a_vap;
117065963Spendry 		char *a_target;
117165963Spendry 	} */ *ap;
117265963Spendry {
117365963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
117465963Spendry 	struct vnode *dvp = un->un_uppervp;
117569447Smckusick 	struct componentname *cnp = ap->a_cnp;
117669447Smckusick 	struct proc *p = cnp->cn_proc;
117765963Spendry 
117867076Spendry 	if (dvp != NULLVP) {
117965963Spendry 		int error;
118065963Spendry 		struct vnode *vp;
118165963Spendry 		struct mount *mp = ap->a_dvp->v_mount;
118265963Spendry 
118369447Smckusick 		FIXUP(un, p);
118465963Spendry 		VREF(dvp);
118566051Spendry 		un->un_flags |= UN_KLOCK;
118665963Spendry 		vput(ap->a_dvp);
118769447Smckusick 		error = VOP_SYMLINK(dvp, &vp, cnp, ap->a_vap, ap->a_target);
118865997Spendry 		*ap->a_vpp = NULLVP;
118965963Spendry 		return (error);
119065963Spendry 	}
119165963Spendry 
119265963Spendry 	vput(ap->a_dvp);
119365963Spendry 	return (EROFS);
119465963Spendry }
119565963Spendry 
119665935Spendry /*
119765935Spendry  * union_readdir works in concert with getdirentries and
119865935Spendry  * readdir(3) to provide a list of entries in the unioned
119965935Spendry  * directories.  getdirentries is responsible for walking
120065935Spendry  * down the union stack.  readdir(3) is responsible for
120165935Spendry  * eliminating duplicate names from the returned data stream.
120265935Spendry  */
120365935Spendry int
union_readdir(ap)120465935Spendry union_readdir(ap)
120565935Spendry 	struct vop_readdir_args /* {
120665935Spendry 		struct vnodeop_desc *a_desc;
120765935Spendry 		struct vnode *a_vp;
120865935Spendry 		struct uio *a_uio;
120965935Spendry 		struct ucred *a_cred;
121067369Smckusick 		int *a_eofflag;
121167369Smckusick 		u_long *a_cookies;
121267369Smckusick 		int a_ncookies;
121365935Spendry 	} */ *ap;
121465935Spendry {
121569447Smckusick 	struct union_node *un = VTOUNION(ap->a_vp);
121669447Smckusick 	struct vnode *uvp = un->un_uppervp;
121769447Smckusick 	struct proc *p = ap->a_uio->uio_procp;
121865935Spendry 
121967369Smckusick 	if (uvp == NULLVP)
122067369Smckusick 		return (0);
122165935Spendry 
122269447Smckusick 	FIXUP(un, p);
122367369Smckusick 	ap->a_vp = uvp;
122469590Smckusick 	return (VCALL(uvp, VOFFSET(vop_readdir), ap));
122565935Spendry }
122665935Spendry 
122765935Spendry int
union_readlink(ap)122865963Spendry union_readlink(ap)
122965963Spendry 	struct vop_readlink_args /* {
123065963Spendry 		struct vnode *a_vp;
123165963Spendry 		struct uio *a_uio;
123265963Spendry 		struct ucred *a_cred;
123365963Spendry 	} */ *ap;
123465963Spendry {
123565963Spendry 	int error;
123669447Smckusick 	struct uio *uio = ap->a_uio;
123769447Smckusick 	struct proc *p = uio->uio_procp;
123865963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
123966051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
124065963Spendry 
124166051Spendry 	if (dolock)
124269447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
124366152Spendry 	else
124469447Smckusick 		FIXUP(VTOUNION(ap->a_vp), p);
124569590Smckusick 	ap->a_vp = vp;
124669590Smckusick 	error = VCALL(vp, VOFFSET(vop_readlink), ap);
124766051Spendry 	if (dolock)
124869447Smckusick 		VOP_UNLOCK(vp, 0, p);
124965963Spendry 
125065963Spendry 	return (error);
125165963Spendry }
125265963Spendry 
125365963Spendry int
union_abortop(ap)125465963Spendry union_abortop(ap)
125565963Spendry 	struct vop_abortop_args /* {
125665963Spendry 		struct vnode *a_dvp;
125765963Spendry 		struct componentname *a_cnp;
125865963Spendry 	} */ *ap;
125965963Spendry {
126065963Spendry 	int error;
126169447Smckusick 	struct componentname *cnp = ap->a_cnp;
126269447Smckusick 	struct proc *p = cnp->cn_proc;
126365965Spendry 	struct vnode *vp = OTHERVP(ap->a_dvp);
126465963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
126565963Spendry 	int islocked = un->un_flags & UN_LOCKED;
126666051Spendry 	int dolock = (vp == LOWERVP(ap->a_dvp));
126765963Spendry 
126866152Spendry 	if (islocked) {
126966152Spendry 		if (dolock)
127069447Smckusick 			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
127166152Spendry 		else
127269447Smckusick 			FIXUP(VTOUNION(ap->a_dvp), p);
127366152Spendry 	}
127469590Smckusick 	ap->a_dvp = vp;
127569590Smckusick 	error = VCALL(vp, VOFFSET(vop_abortop), ap);
127666051Spendry 	if (islocked && dolock)
127769447Smckusick 		VOP_UNLOCK(vp, 0, p);
127865963Spendry 
127965963Spendry 	return (error);
128065963Spendry }
128165963Spendry 
128265963Spendry int
union_inactive(ap)128365935Spendry union_inactive(ap)
128465935Spendry 	struct vop_inactive_args /* {
128565935Spendry 		struct vnode *a_vp;
128669447Smckusick 		struct proc *a_p;
128765935Spendry 	} */ *ap;
128865935Spendry {
128969588Spendry 	struct vnode *vp = ap->a_vp;
129069588Spendry 	struct proc *p = ap->a_p;
129169588Spendry 	struct union_node *un = VTOUNION(vp);
129268078Spendry 	struct vnode **vpp;
129365935Spendry 
129465935Spendry 	/*
129565935Spendry 	 * Do nothing (and _don't_ bypass).
129665935Spendry 	 * Wait to vrele lowervp until reclaim,
129765935Spendry 	 * so that until then our union_node is in the
129865935Spendry 	 * cache and reusable.
129965935Spendry 	 *
130065935Spendry 	 * NEEDSWORK: Someday, consider inactive'ing
130165935Spendry 	 * the lowervp and then trying to reactivate it
130265935Spendry 	 * with capabilities (v_id)
130365935Spendry 	 * like they do in the name lookup cache code.
130465935Spendry 	 * That's too much work for now.
130565935Spendry 	 */
130665989Spendry 
130768078Spendry 	if (un->un_dircache != 0) {
130868078Spendry 		for (vpp = un->un_dircache; *vpp != NULLVP; vpp++)
130968078Spendry 			vrele(*vpp);
131068078Spendry 		free(un->un_dircache, M_TEMP);
131168078Spendry 		un->un_dircache = 0;
131268078Spendry 	}
131368078Spendry 
131469588Spendry 	VOP_UNLOCK(vp, 0, p);
131569588Spendry 
131667073Spendry 	if ((un->un_flags & UN_CACHED) == 0)
131769588Spendry 		vgone(vp);
131867073Spendry 
131965935Spendry 	return (0);
132065935Spendry }
132165935Spendry 
132265935Spendry int
union_reclaim(ap)132365935Spendry union_reclaim(ap)
132465935Spendry 	struct vop_reclaim_args /* {
132565935Spendry 		struct vnode *a_vp;
132665935Spendry 	} */ *ap;
132765935Spendry {
132865935Spendry 
132966053Spendry 	union_freevp(ap->a_vp);
133066053Spendry 
133165935Spendry 	return (0);
133265935Spendry }
133365935Spendry 
133465963Spendry int
union_lock(ap)133565963Spendry union_lock(ap)
133665963Spendry 	struct vop_lock_args *ap;
133765963Spendry {
133866149Spendry 	struct vnode *vp = ap->a_vp;
133969447Smckusick 	struct proc *p = ap->a_p;
134069447Smckusick 	int flags = ap->a_flags;
134166149Spendry 	struct union_node *un;
134269588Spendry 	int error;
134365935Spendry 
134469588Spendry 
134569616Smckusick 	vop_nolock(ap);
1346*69978Spendry 	/*
1347*69978Spendry 	 * Need to do real lockmgr-style locking here.
1348*69978Spendry 	 * in the mean time, draining won't work quite right,
1349*69978Spendry 	 * which could lead to a few race conditions.
1350*69978Spendry 	 * the following test was here, but is not quite right, we
1351*69978Spendry 	 * still need to take the lock:
135269616Smckusick 	if ((flags & LK_TYPE_MASK) == LK_DRAIN)
135369616Smckusick 		return (0);
1354*69978Spendry 	 */
135569616Smckusick 	flags &= ~LK_INTERLOCK;
135666149Spendry 
135769616Smckusick start:
135866149Spendry 	un = VTOUNION(vp);
135966149Spendry 
136067076Spendry 	if (un->un_uppervp != NULLVP) {
136167230Spendry 		if (((un->un_flags & UN_ULOCK) == 0) &&
136267230Spendry 		    (vp->v_usecount != 0)) {
136369588Spendry 			error = vn_lock(un->un_uppervp, flags, p);
136469588Spendry 			if (error)
136569588Spendry 				return (error);
136666149Spendry 			un->un_flags |= UN_ULOCK;
136766051Spendry 		}
136866051Spendry #ifdef DIAGNOSTIC
136969389Spendry 		if (un->un_flags & UN_KLOCK) {
137068590Spendry 			vprint("union: dangling klock", vp);
137168590Spendry 			panic("union: dangling upper lock (%lx)", vp);
137269389Spendry 		}
137366051Spendry #endif
137466051Spendry 	}
137566051Spendry 
137666149Spendry 	if (un->un_flags & UN_LOCKED) {
137765963Spendry #ifdef DIAGNOSTIC
137865989Spendry 		if (curproc && un->un_pid == curproc->p_pid &&
137965989Spendry 			    un->un_pid > -1 && curproc->p_pid > -1)
138065989Spendry 			panic("union: locking against myself");
138165963Spendry #endif
138265963Spendry 		un->un_flags |= UN_WANT;
138369447Smckusick 		tsleep((caddr_t)&un->un_flags, PINOD, "unionlk2", 0);
138466149Spendry 		goto start;
138565963Spendry 	}
138665989Spendry 
138765963Spendry #ifdef DIAGNOSTIC
138865989Spendry 	if (curproc)
138965989Spendry 		un->un_pid = curproc->p_pid;
139065989Spendry 	else
139165989Spendry 		un->un_pid = -1;
139265963Spendry #endif
139366028Spendry 
139466149Spendry 	un->un_flags |= UN_LOCKED;
139566028Spendry 	return (0);
139665963Spendry }
139765963Spendry 
139868590Spendry /*
139968590Spendry  * When operations want to vput() a union node yet retain a lock on
140068590Spendry  * the upper vnode (say, to do some further operations like link(),
140168590Spendry  * mkdir(), ...), they set UN_KLOCK on the union node, then call
140268590Spendry  * vput() which calls VOP_UNLOCK() and comes here.  union_unlock()
140368590Spendry  * unlocks the union node (leaving the upper vnode alone), clears the
140468590Spendry  * KLOCK flag, and then returns to vput().  The caller then does whatever
140568590Spendry  * is left to do with the upper vnode, and ensures that it gets unlocked.
140668590Spendry  *
140768590Spendry  * If UN_KLOCK isn't set, then the upper vnode is unlocked here.
140868590Spendry  */
140965935Spendry int
union_unlock(ap)141065963Spendry union_unlock(ap)
141169588Spendry 	struct vop_unlock_args /* {
141269588Spendry 		struct vnode *a_vp;
141369588Spendry 		int a_flags;
141469588Spendry 		struct proc *a_p;
141569588Spendry 	} */ *ap;
141665963Spendry {
141765963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
141869447Smckusick 	struct proc *p = ap->a_p;
141965963Spendry 
142065963Spendry #ifdef DIAGNOSTIC
142165965Spendry 	if ((un->un_flags & UN_LOCKED) == 0)
142265965Spendry 		panic("union: unlock unlocked node");
142365989Spendry 	if (curproc && un->un_pid != curproc->p_pid &&
142465989Spendry 			curproc->p_pid > -1 && un->un_pid > -1)
142565963Spendry 		panic("union: unlocking other process's union node");
142665963Spendry #endif
142765963Spendry 
142865963Spendry 	un->un_flags &= ~UN_LOCKED;
142966051Spendry 
143066051Spendry 	if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK)
143169447Smckusick 		VOP_UNLOCK(un->un_uppervp, 0, p);
143266051Spendry 
143366051Spendry 	un->un_flags &= ~(UN_ULOCK|UN_KLOCK);
143466051Spendry 
143565963Spendry 	if (un->un_flags & UN_WANT) {
143665963Spendry 		un->un_flags &= ~UN_WANT;
143765963Spendry 		wakeup((caddr_t) &un->un_flags);
143865963Spendry 	}
143965963Spendry 
144065963Spendry #ifdef DIAGNOSTIC
144165963Spendry 	un->un_pid = 0;
144265963Spendry #endif
144369616Smckusick 	vop_nounlock(ap);
144466028Spendry 
144566028Spendry 	return (0);
144665963Spendry }
144765963Spendry 
144865963Spendry int
union_bmap(ap)144965963Spendry union_bmap(ap)
145065963Spendry 	struct vop_bmap_args /* {
145165963Spendry 		struct vnode *a_vp;
145265963Spendry 		daddr_t  a_bn;
145365963Spendry 		struct vnode **a_vpp;
145465963Spendry 		daddr_t *a_bnp;
145565963Spendry 		int *a_runp;
145665963Spendry 	} */ *ap;
145765963Spendry {
145865963Spendry 	int error;
145969447Smckusick 	struct proc *p = curproc;		/* XXX */
146065963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
146166051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
146265963Spendry 
146366051Spendry 	if (dolock)
146469447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
146566152Spendry 	else
146669447Smckusick 		FIXUP(VTOUNION(ap->a_vp), p);
146769590Smckusick 	ap->a_vp = vp;
146869590Smckusick 	error = VCALL(vp, VOFFSET(vop_bmap), ap);
146966051Spendry 	if (dolock)
147069447Smckusick 		VOP_UNLOCK(vp, 0, p);
147165963Spendry 
147265963Spendry 	return (error);
147365963Spendry }
147465963Spendry 
147565963Spendry int
union_print(ap)147665935Spendry union_print(ap)
147765935Spendry 	struct vop_print_args /* {
147865935Spendry 		struct vnode *a_vp;
147965935Spendry 	} */ *ap;
148065935Spendry {
148165935Spendry 	struct vnode *vp = ap->a_vp;
148265935Spendry 
148365935Spendry 	printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n",
148465935Spendry 			vp, UPPERVP(vp), LOWERVP(vp));
148568590Spendry 	if (UPPERVP(vp) != NULLVP)
148668590Spendry 		vprint("union: upper", UPPERVP(vp));
148768590Spendry 	if (LOWERVP(vp) != NULLVP)
148868590Spendry 		vprint("union: lower", LOWERVP(vp));
148968590Spendry 
149065935Spendry 	return (0);
149165935Spendry }
149265935Spendry 
149365963Spendry int
union_islocked(ap)149465963Spendry union_islocked(ap)
149565963Spendry 	struct vop_islocked_args /* {
149665963Spendry 		struct vnode *a_vp;
149765963Spendry 	} */ *ap;
149865963Spendry {
149965935Spendry 
150065963Spendry 	return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0);
150165963Spendry }
150265963Spendry 
150365935Spendry int
union_pathconf(ap)150465963Spendry union_pathconf(ap)
150565963Spendry 	struct vop_pathconf_args /* {
150665963Spendry 		struct vnode *a_vp;
150765963Spendry 		int a_name;
150865963Spendry 		int *a_retval;
150965935Spendry 	} */ *ap;
151065935Spendry {
151165935Spendry 	int error;
151269447Smckusick 	struct proc *p = curproc;		/* XXX */
151365963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
151466051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
151565935Spendry 
151666051Spendry 	if (dolock)
151769447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
151866152Spendry 	else
151969447Smckusick 		FIXUP(VTOUNION(ap->a_vp), p);
152069590Smckusick 	ap->a_vp = vp;
152169590Smckusick 	error = VCALL(vp, VOFFSET(vop_pathconf), ap);
152266051Spendry 	if (dolock)
152369447Smckusick 		VOP_UNLOCK(vp, 0, p);
152465935Spendry 
152565963Spendry 	return (error);
152665963Spendry }
152765935Spendry 
152865963Spendry int
union_advlock(ap)152965963Spendry union_advlock(ap)
153065963Spendry 	struct vop_advlock_args /* {
153165963Spendry 		struct vnode *a_vp;
153265963Spendry 		caddr_t  a_id;
153365963Spendry 		int  a_op;
153465963Spendry 		struct flock *a_fl;
153565963Spendry 		int  a_flags;
153665963Spendry 	} */ *ap;
153765963Spendry {
153869590Smckusick 	register struct vnode *ovp = OTHERVP(ap->a_vp);
153965935Spendry 
154069590Smckusick 	ap->a_vp = ovp;
154169590Smckusick 	return (VCALL(ovp, VOFFSET(vop_advlock), ap));
154265935Spendry }
154365935Spendry 
154465935Spendry 
154565935Spendry /*
154665963Spendry  * XXX - vop_strategy must be hand coded because it has no
154765935Spendry  * vnode in its arguments.
154865935Spendry  * This goes away with a merged VM/buffer cache.
154965935Spendry  */
155065935Spendry int
union_strategy(ap)155165963Spendry union_strategy(ap)
155265963Spendry 	struct vop_strategy_args /* {
155365935Spendry 		struct buf *a_bp;
155465935Spendry 	} */ *ap;
155565935Spendry {
155665935Spendry 	struct buf *bp = ap->a_bp;
155765935Spendry 	int error;
155865935Spendry 	struct vnode *savedvp;
155965935Spendry 
156065935Spendry 	savedvp = bp->b_vp;
156165963Spendry 	bp->b_vp = OTHERVP(bp->b_vp);
156265935Spendry 
156365935Spendry #ifdef DIAGNOSTIC
156465997Spendry 	if (bp->b_vp == NULLVP)
156565963Spendry 		panic("union_strategy: nil vp");
156665963Spendry 	if (((bp->b_flags & B_READ) == 0) &&
156765963Spendry 	    (bp->b_vp == LOWERVP(savedvp)))
156865963Spendry 		panic("union_strategy: writing to lowervp");
156965935Spendry #endif
157065935Spendry 
157165963Spendry 	error = VOP_STRATEGY(bp);
157265935Spendry 	bp->b_vp = savedvp;
157365935Spendry 
157465935Spendry 	return (error);
157565935Spendry }
157665935Spendry 
157765935Spendry /*
157865935Spendry  * Global vfs data structures
157965935Spendry  */
158065935Spendry int (**union_vnodeop_p)();
158165965Spendry struct vnodeopv_entry_desc union_vnodeop_entries[] = {
158265963Spendry 	{ &vop_default_desc, vn_default_error },
158365963Spendry 	{ &vop_lookup_desc, union_lookup },		/* lookup */
158465963Spendry 	{ &vop_create_desc, union_create },		/* create */
158567575Spendry 	{ &vop_whiteout_desc, union_whiteout },		/* whiteout */
158665963Spendry 	{ &vop_mknod_desc, union_mknod },		/* mknod */
158765963Spendry 	{ &vop_open_desc, union_open },			/* open */
158865963Spendry 	{ &vop_close_desc, union_close },		/* close */
158965963Spendry 	{ &vop_access_desc, union_access },		/* access */
159065963Spendry 	{ &vop_getattr_desc, union_getattr },		/* getattr */
159165963Spendry 	{ &vop_setattr_desc, union_setattr },		/* setattr */
159265963Spendry 	{ &vop_read_desc, union_read },			/* read */
159365963Spendry 	{ &vop_write_desc, union_write },		/* write */
159467751Spendry 	{ &vop_lease_desc, union_lease },		/* lease */
159565963Spendry 	{ &vop_ioctl_desc, union_ioctl },		/* ioctl */
159665963Spendry 	{ &vop_select_desc, union_select },		/* select */
159769389Spendry 	{ &vop_revoke_desc, union_revoke },		/* revoke */
159865963Spendry 	{ &vop_mmap_desc, union_mmap },			/* mmap */
159965963Spendry 	{ &vop_fsync_desc, union_fsync },		/* fsync */
160065963Spendry 	{ &vop_seek_desc, union_seek },			/* seek */
160165963Spendry 	{ &vop_remove_desc, union_remove },		/* remove */
160265963Spendry 	{ &vop_link_desc, union_link },			/* link */
160365963Spendry 	{ &vop_rename_desc, union_rename },		/* rename */
160465963Spendry 	{ &vop_mkdir_desc, union_mkdir },		/* mkdir */
160565963Spendry 	{ &vop_rmdir_desc, union_rmdir },		/* rmdir */
160665963Spendry 	{ &vop_symlink_desc, union_symlink },		/* symlink */
160765963Spendry 	{ &vop_readdir_desc, union_readdir },		/* readdir */
160865963Spendry 	{ &vop_readlink_desc, union_readlink },		/* readlink */
160965963Spendry 	{ &vop_abortop_desc, union_abortop },		/* abortop */
161065963Spendry 	{ &vop_inactive_desc, union_inactive },		/* inactive */
161165963Spendry 	{ &vop_reclaim_desc, union_reclaim },		/* reclaim */
161265963Spendry 	{ &vop_lock_desc, union_lock },			/* lock */
161365963Spendry 	{ &vop_unlock_desc, union_unlock },		/* unlock */
161465963Spendry 	{ &vop_bmap_desc, union_bmap },			/* bmap */
161565963Spendry 	{ &vop_strategy_desc, union_strategy },		/* strategy */
161665963Spendry 	{ &vop_print_desc, union_print },		/* print */
161765963Spendry 	{ &vop_islocked_desc, union_islocked },		/* islocked */
161865963Spendry 	{ &vop_pathconf_desc, union_pathconf },		/* pathconf */
161965963Spendry 	{ &vop_advlock_desc, union_advlock },		/* advlock */
162065963Spendry #ifdef notdef
162165963Spendry 	{ &vop_blkatoff_desc, union_blkatoff },		/* blkatoff */
162265963Spendry 	{ &vop_valloc_desc, union_valloc },		/* valloc */
162365963Spendry 	{ &vop_vfree_desc, union_vfree },		/* vfree */
162465963Spendry 	{ &vop_truncate_desc, union_truncate },		/* truncate */
162565963Spendry 	{ &vop_update_desc, union_update },		/* update */
162665963Spendry 	{ &vop_bwrite_desc, union_bwrite },		/* bwrite */
162765963Spendry #endif
162865935Spendry 	{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
162965935Spendry };
163065935Spendry struct vnodeopv_desc union_vnodeop_opv_desc =
163165935Spendry 	{ &union_vnodeop_p, union_vnodeop_entries };
1632