xref: /csrg-svn/sys/miscfs/union/union_vnops.c (revision 68590)
165935Spendry /*
2*68590Spendry  * Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry.
3*68590Spendry  * Copyright (c) 1992, 1993, 1994, 1995
4*68590Spendry  *	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*68590Spendry  *	@(#)union_vnops.c	8.25 (Berkeley) 03/24/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>
2766055Spendry #include <miscfs/union/union.h>
2865935Spendry 
2966152Spendry #define FIXUP(un) { \
3066152Spendry 	if (((un)->un_flags & UN_ULOCK) == 0) { \
3166152Spendry 		union_fixup(un); \
3266152Spendry 	} \
3366152Spendry }
3466152Spendry 
3566152Spendry static void
3666152Spendry union_fixup(un)
3766152Spendry 	struct union_node *un;
3866152Spendry {
3966152Spendry 
4066152Spendry 	VOP_LOCK(un->un_uppervp);
4166152Spendry 	un->un_flags |= UN_ULOCK;
4266152Spendry }
4366152Spendry 
4465935Spendry static int
4567064Spendry union_lookup1(udvp, dvpp, vpp, cnp)
4665989Spendry 	struct vnode *udvp;
4767064Spendry 	struct vnode **dvpp;
4865935Spendry 	struct vnode **vpp;
4965935Spendry 	struct componentname *cnp;
5065935Spendry {
5165935Spendry 	int error;
5265935Spendry 	struct vnode *tdvp;
5367064Spendry 	struct vnode *dvp;
5465935Spendry 	struct mount *mp;
5565935Spendry 
5667064Spendry 	dvp = *dvpp;
5767064Spendry 
5865994Spendry 	/*
5965994Spendry 	 * If stepping up the directory tree, check for going
6065994Spendry 	 * back across the mount point, in which case do what
6165994Spendry 	 * lookup would do by stepping back down the mount
6265994Spendry 	 * hierarchy.
6365994Spendry 	 */
6465935Spendry 	if (cnp->cn_flags & ISDOTDOT) {
6567064Spendry 		while ((dvp != udvp) && (dvp->v_flag & VROOT)) {
6666034Spendry 			/*
6766034Spendry 			 * Don't do the NOCROSSMOUNT check
6866034Spendry 			 * at this level.  By definition,
6966034Spendry 			 * union fs deals with namespaces, not
7066034Spendry 			 * filesystems.
7166034Spendry 			 */
7265935Spendry 			tdvp = dvp;
7367064Spendry 			*dvpp = dvp = dvp->v_mount->mnt_vnodecovered;
7465935Spendry 			vput(tdvp);
7565935Spendry 			VREF(dvp);
7665935Spendry 			VOP_LOCK(dvp);
7765935Spendry 		}
7865935Spendry 	}
7966027Spendry 
8065935Spendry         error = VOP_LOOKUP(dvp, &tdvp, cnp);
8165935Spendry 	if (error)
8265935Spendry 		return (error);
8365935Spendry 
8465994Spendry 	/*
8566027Spendry 	 * The parent directory will have been unlocked, unless lookup
8666027Spendry 	 * found the last component.  In which case, re-lock the node
8766027Spendry 	 * here to allow it to be unlocked again (phew) in union_lookup.
8865994Spendry 	 */
8966027Spendry 	if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN))
9065994Spendry 		VOP_LOCK(dvp);
9165994Spendry 
9265935Spendry 	dvp = tdvp;
9365994Spendry 
9465994Spendry 	/*
9565994Spendry 	 * Lastly check if the current node is a mount point in
9666034Spendry 	 * which case walk up the mount hierarchy making sure not to
9765994Spendry 	 * bump into the root of the mount tree (ie. dvp != udvp).
9865994Spendry 	 */
9965989Spendry 	while (dvp != udvp && (dvp->v_type == VDIR) &&
10066034Spendry 	       (mp = dvp->v_mountedhere)) {
10165935Spendry 
10265935Spendry 		if (mp->mnt_flag & MNT_MLOCK) {
10365935Spendry 			mp->mnt_flag |= MNT_MWAIT;
10465935Spendry 			sleep((caddr_t) mp, PVFS);
10565935Spendry 			continue;
10665935Spendry 		}
10765935Spendry 
10865935Spendry 		if (error = VFS_ROOT(mp, &tdvp)) {
10965935Spendry 			vput(dvp);
11065935Spendry 			return (error);
11165935Spendry 		}
11265935Spendry 
11365965Spendry 		vput(dvp);
11465935Spendry 		dvp = tdvp;
11565935Spendry 	}
11665935Spendry 
11765935Spendry 	*vpp = dvp;
11865935Spendry 	return (0);
11965935Spendry }
12065935Spendry 
12165935Spendry int
12265935Spendry union_lookup(ap)
12365935Spendry 	struct vop_lookup_args /* {
12465935Spendry 		struct vnodeop_desc *a_desc;
12565935Spendry 		struct vnode *a_dvp;
12665935Spendry 		struct vnode **a_vpp;
12765935Spendry 		struct componentname *a_cnp;
12865935Spendry 	} */ *ap;
12965935Spendry {
13065965Spendry 	int error;
13165935Spendry 	int uerror, lerror;
13265935Spendry 	struct vnode *uppervp, *lowervp;
13365935Spendry 	struct vnode *upperdvp, *lowerdvp;
13465935Spendry 	struct vnode *dvp = ap->a_dvp;
13565989Spendry 	struct union_node *dun = VTOUNION(dvp);
13665935Spendry 	struct componentname *cnp = ap->a_cnp;
13765935Spendry 	int lockparent = cnp->cn_flags & LOCKPARENT;
13865994Spendry 	int rdonly = cnp->cn_flags & RDONLY;
13965997Spendry 	struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
14066152Spendry 	struct ucred *saved_cred;
14167575Spendry 	int iswhiteout;
14267575Spendry 	struct vattr va;
14365935Spendry 
14467446Spendry #ifdef notyet
14567446Spendry 	if (cnp->cn_namelen == 3 &&
14667446Spendry 			cnp->cn_nameptr[2] == '.' &&
14767446Spendry 			cnp->cn_nameptr[1] == '.' &&
14867446Spendry 			cnp->cn_nameptr[0] == '.') {
14967446Spendry 		dvp = *ap->a_vpp = LOWERVP(ap->a_dvp);
15067446Spendry 		if (dvp == NULLVP)
15167446Spendry 			return (ENOENT);
15267446Spendry 		VREF(dvp);
15367446Spendry 		VOP_LOCK(dvp);
15467446Spendry 		if (!lockparent || !(cnp->cn_flags & ISLASTCN))
15567446Spendry 			VOP_UNLOCK(ap->a_dvp);
15667446Spendry 		return (0);
15767446Spendry 	}
15867446Spendry #endif
15967446Spendry 
16065965Spendry 	cnp->cn_flags |= LOCKPARENT;
16165965Spendry 
16265935Spendry 	upperdvp = dun->un_uppervp;
16365935Spendry 	lowerdvp = dun->un_lowervp;
16465997Spendry 	uppervp = NULLVP;
16565997Spendry 	lowervp = NULLVP;
16667575Spendry 	iswhiteout = 0;
16765935Spendry 
16865935Spendry 	/*
16965935Spendry 	 * do the lookup in the upper level.
17065935Spendry 	 * if that level comsumes additional pathnames,
17165935Spendry 	 * then assume that something special is going
17265935Spendry 	 * on and just return that vnode.
17365935Spendry 	 */
17467076Spendry 	if (upperdvp != NULLVP) {
17566152Spendry 		FIXUP(dun);
17667064Spendry 		uerror = union_lookup1(um->um_uppervp, &upperdvp,
17765997Spendry 					&uppervp, cnp);
17866051Spendry 		/*if (uppervp == upperdvp)
17966051Spendry 			dun->un_flags |= UN_KLOCK;*/
18065965Spendry 
18165935Spendry 		if (cnp->cn_consume != 0) {
18265935Spendry 			*ap->a_vpp = uppervp;
18365965Spendry 			if (!lockparent)
18465965Spendry 				cnp->cn_flags &= ~LOCKPARENT;
18565935Spendry 			return (uerror);
18665935Spendry 		}
18767575Spendry 		if (uerror == ENOENT || uerror == EJUSTRETURN) {
18867575Spendry 			if (cnp->cn_flags & ISWHITEOUT) {
18967575Spendry 				iswhiteout = 1;
19067575Spendry 			} else if (lowerdvp != NULLVP) {
19167575Spendry 				lerror = VOP_GETATTR(upperdvp, &va,
19267575Spendry 					cnp->cn_cred, cnp->cn_proc);
19367575Spendry 				if (lerror == 0 && (va.va_flags & OPAQUE))
19467575Spendry 					iswhiteout = 1;
19567575Spendry 			}
19667575Spendry 		}
19765935Spendry 	} else {
19865935Spendry 		uerror = ENOENT;
19965935Spendry 	}
20065935Spendry 
20165935Spendry 	/*
20265935Spendry 	 * in a similar way to the upper layer, do the lookup
20365935Spendry 	 * in the lower layer.   this time, if there is some
20465935Spendry 	 * component magic going on, then vput whatever we got
20565935Spendry 	 * back from the upper layer and return the lower vnode
20665935Spendry 	 * instead.
20765935Spendry 	 */
20867575Spendry 	if (lowerdvp != NULLVP && !iswhiteout) {
20966051Spendry 		int nameiop;
21066051Spendry 
21165965Spendry 		VOP_LOCK(lowerdvp);
21266051Spendry 
21366051Spendry 		/*
21466051Spendry 		 * Only do a LOOKUP on the bottom node, since
21566051Spendry 		 * we won't be making changes to it anyway.
21666051Spendry 		 */
21766051Spendry 		nameiop = cnp->cn_nameiop;
21866051Spendry 		cnp->cn_nameiop = LOOKUP;
21966152Spendry 		if (um->um_op == UNMNT_BELOW) {
22066152Spendry 			saved_cred = cnp->cn_cred;
22166152Spendry 			cnp->cn_cred = um->um_cred;
22266152Spendry 		}
22367064Spendry 		lerror = union_lookup1(um->um_lowervp, &lowerdvp,
22466051Spendry 				&lowervp, cnp);
22566152Spendry 		if (um->um_op == UNMNT_BELOW)
22666152Spendry 			cnp->cn_cred = saved_cred;
22766051Spendry 		cnp->cn_nameiop = nameiop;
22866051Spendry 
22965989Spendry 		if (lowervp != lowerdvp)
23065989Spendry 			VOP_UNLOCK(lowerdvp);
23165965Spendry 
23265935Spendry 		if (cnp->cn_consume != 0) {
23367076Spendry 			if (uppervp != NULLVP) {
23466051Spendry 				if (uppervp == upperdvp)
23566051Spendry 					vrele(uppervp);
23666051Spendry 				else
23766051Spendry 					vput(uppervp);
23865997Spendry 				uppervp = NULLVP;
23965935Spendry 			}
24065935Spendry 			*ap->a_vpp = lowervp;
24165965Spendry 			if (!lockparent)
24265965Spendry 				cnp->cn_flags &= ~LOCKPARENT;
24365935Spendry 			return (lerror);
24465935Spendry 		}
24565935Spendry 	} else {
24665935Spendry 		lerror = ENOENT;
24767416Spendry 		if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) {
24867416Spendry 			lowervp = LOWERVP(dun->un_pvp);
24967416Spendry 			if (lowervp != NULLVP) {
25067416Spendry 				VREF(lowervp);
25167416Spendry 				VOP_LOCK(lowervp);
25267416Spendry 				lerror = 0;
25367416Spendry 			}
25467416Spendry 		}
25565935Spendry 	}
25665935Spendry 
25765965Spendry 	if (!lockparent)
25865965Spendry 		cnp->cn_flags &= ~LOCKPARENT;
25965965Spendry 
26065935Spendry 	/*
26165935Spendry 	 * at this point, we have uerror and lerror indicating
26265935Spendry 	 * possible errors with the lookups in the upper and lower
26365935Spendry 	 * layers.  additionally, uppervp and lowervp are (locked)
26465935Spendry 	 * references to existing vnodes in the upper and lower layers.
26565935Spendry 	 *
26665935Spendry 	 * there are now three cases to consider.
26765935Spendry 	 * 1. if both layers returned an error, then return whatever
26865935Spendry 	 *    error the upper layer generated.
26965935Spendry 	 *
27065935Spendry 	 * 2. if the top layer failed and the bottom layer succeeded
27165935Spendry 	 *    then two subcases occur.
27265935Spendry 	 *    a.  the bottom vnode is not a directory, in which
27365935Spendry 	 *	  case just return a new union vnode referencing
27465935Spendry 	 *	  an empty top layer and the existing bottom layer.
27565935Spendry 	 *    b.  the bottom vnode is a directory, in which case
27665935Spendry 	 *	  create a new directory in the top-level and
27765935Spendry 	 *	  continue as in case 3.
27865935Spendry 	 *
27965935Spendry 	 * 3. if the top layer succeeded then return a new union
28065935Spendry 	 *    vnode referencing whatever the new top layer and
28165935Spendry 	 *    whatever the bottom layer returned.
28265935Spendry 	 */
28365935Spendry 
28466027Spendry 	*ap->a_vpp = NULLVP;
28566027Spendry 
28665935Spendry 	/* case 1. */
28765935Spendry 	if ((uerror != 0) && (lerror != 0)) {
28865935Spendry 		return (uerror);
28965935Spendry 	}
29065935Spendry 
29165935Spendry 	/* case 2. */
29265935Spendry 	if (uerror != 0 /* && (lerror == 0) */ ) {
29365935Spendry 		if (lowervp->v_type == VDIR) { /* case 2b. */
29466051Spendry 			dun->un_flags &= ~UN_ULOCK;
29566051Spendry 			VOP_UNLOCK(upperdvp);
29665997Spendry 			uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
29766051Spendry 			VOP_LOCK(upperdvp);
29866051Spendry 			dun->un_flags |= UN_ULOCK;
29966051Spendry 
30065935Spendry 			if (uerror) {
30167076Spendry 				if (lowervp != NULLVP) {
30265935Spendry 					vput(lowervp);
30365997Spendry 					lowervp = NULLVP;
30465935Spendry 				}
30565935Spendry 				return (uerror);
30665935Spendry 			}
30765935Spendry 		}
30865935Spendry 	}
30965935Spendry 
31067076Spendry 	if (lowervp != NULLVP)
31165965Spendry 		VOP_UNLOCK(lowervp);
31265965Spendry 
31365997Spendry 	error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
31468078Spendry 			      uppervp, lowervp, 1);
31565997Spendry 
31665965Spendry 	if (error) {
31767076Spendry 		if (uppervp != NULLVP)
31866051Spendry 			vput(uppervp);
31967076Spendry 		if (lowervp != NULLVP)
32065965Spendry 			vrele(lowervp);
32165965Spendry 	} else {
32265994Spendry 		if (*ap->a_vpp != dvp)
32365994Spendry 			if (!lockparent || !(cnp->cn_flags & ISLASTCN))
32465994Spendry 				VOP_UNLOCK(dvp);
32565965Spendry 	}
32665965Spendry 
32765965Spendry 	return (error);
32865935Spendry }
32965935Spendry 
33065963Spendry int
33165963Spendry union_create(ap)
33265963Spendry 	struct vop_create_args /* {
33365963Spendry 		struct vnode *a_dvp;
33465963Spendry 		struct vnode **a_vpp;
33565963Spendry 		struct componentname *a_cnp;
33665963Spendry 		struct vattr *a_vap;
33765963Spendry 	} */ *ap;
33865963Spendry {
33965963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
34065963Spendry 	struct vnode *dvp = un->un_uppervp;
34165963Spendry 
34267076Spendry 	if (dvp != NULLVP) {
34365963Spendry 		int error;
34465963Spendry 		struct vnode *vp;
34568078Spendry 		struct mount *mp;
34665963Spendry 
34766152Spendry 		FIXUP(un);
34866152Spendry 
34965963Spendry 		VREF(dvp);
35066051Spendry 		un->un_flags |= UN_KLOCK;
35168078Spendry 		mp = ap->a_dvp->v_mount;
35265963Spendry 		vput(ap->a_dvp);
35365963Spendry 		error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap);
35465963Spendry 		if (error)
35565963Spendry 			return (error);
35665963Spendry 
35765963Spendry 		error = union_allocvp(
35865963Spendry 				ap->a_vpp,
35968078Spendry 				mp,
36065965Spendry 				NULLVP,
36168078Spendry 				NULLVP,
36265963Spendry 				ap->a_cnp,
36365963Spendry 				vp,
36468078Spendry 				NULLVP,
36568078Spendry 				1);
36665965Spendry 		if (error)
36766051Spendry 			vput(vp);
36865963Spendry 		return (error);
36965963Spendry 	}
37065963Spendry 
37165963Spendry 	vput(ap->a_dvp);
37265963Spendry 	return (EROFS);
37365963Spendry }
37465963Spendry 
37565963Spendry int
37667575Spendry union_whiteout(ap)
37767575Spendry 	struct vop_whiteout_args /* {
37867575Spendry 		struct vnode *a_dvp;
37967575Spendry 		struct componentname *a_cnp;
38067575Spendry 		int a_flags;
38167575Spendry 	} */ *ap;
38267575Spendry {
38367575Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
38467575Spendry 
38567575Spendry 	if (un->un_uppervp == NULLVP)
38667575Spendry 		return (EOPNOTSUPP);
38767575Spendry 
38867575Spendry 	FIXUP(un);
38967575Spendry 	return (VOP_WHITEOUT(un->un_uppervp, ap->a_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;
40365963Spendry 
40467076Spendry 	if (dvp != NULLVP) {
40565963Spendry 		int error;
40665963Spendry 		struct vnode *vp;
40768078Spendry 		struct mount *mp;
40865963Spendry 
40966152Spendry 		FIXUP(un);
41066152Spendry 
41165963Spendry 		VREF(dvp);
41266051Spendry 		un->un_flags |= UN_KLOCK;
41368078Spendry 		mp = ap->a_dvp->v_mount;
41465963Spendry 		vput(ap->a_dvp);
41565963Spendry 		error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap);
41665963Spendry 		if (error)
41765963Spendry 			return (error);
41865963Spendry 
41967076Spendry 		if (vp != NULLVP) {
42065965Spendry 			error = union_allocvp(
42165965Spendry 					ap->a_vpp,
42268078Spendry 					mp,
42365965Spendry 					NULLVP,
42468078Spendry 					NULLVP,
42565965Spendry 					ap->a_cnp,
42665965Spendry 					vp,
42768078Spendry 					NULLVP,
42868078Spendry 					1);
42965965Spendry 			if (error)
43066051Spendry 				vput(vp);
43165965Spendry 		}
43265963Spendry 		return (error);
43365963Spendry 	}
43465963Spendry 
43565963Spendry 	vput(ap->a_dvp);
43665963Spendry 	return (EROFS);
43765963Spendry }
43865963Spendry 
43965935Spendry int
44065935Spendry union_open(ap)
44165935Spendry 	struct vop_open_args /* {
44265935Spendry 		struct vnodeop_desc *a_desc;
44365935Spendry 		struct vnode *a_vp;
44465935Spendry 		int a_mode;
44565935Spendry 		struct ucred *a_cred;
44665935Spendry 		struct proc *a_p;
44765935Spendry 	} */ *ap;
44865935Spendry {
44965935Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
45065965Spendry 	struct vnode *tvp;
45165935Spendry 	int mode = ap->a_mode;
45265935Spendry 	struct ucred *cred = ap->a_cred;
45365935Spendry 	struct proc *p = ap->a_p;
45465965Spendry 	int error;
45565935Spendry 
45665935Spendry 	/*
45765935Spendry 	 * If there is an existing upper vp then simply open that.
45865935Spendry 	 */
45965965Spendry 	tvp = un->un_uppervp;
46065965Spendry 	if (tvp == NULLVP) {
46165935Spendry 		/*
46265965Spendry 		 * If the lower vnode is being opened for writing, then
46365965Spendry 		 * copy the file contents to the upper vnode and open that,
46465965Spendry 		 * otherwise can simply open the lower vnode.
46565935Spendry 		 */
46665965Spendry 		tvp = un->un_lowervp;
46765965Spendry 		if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
46867169Spendry 			error = union_copyup(un, (mode&O_TRUNC) == 0, cred, p);
46965965Spendry 			if (error == 0)
47065965Spendry 				error = VOP_OPEN(un->un_uppervp, mode, cred, p);
47165965Spendry 			return (error);
47265935Spendry 		}
47366051Spendry 
47466051Spendry 		/*
47566051Spendry 		 * Just open the lower vnode
47666051Spendry 		 */
47766027Spendry 		un->un_openl++;
47866051Spendry 		VOP_LOCK(tvp);
47966051Spendry 		error = VOP_OPEN(tvp, mode, cred, p);
48066051Spendry 		VOP_UNLOCK(tvp);
48166051Spendry 
48266051Spendry 		return (error);
48365935Spendry 	}
48465935Spendry 
48566152Spendry 	FIXUP(un);
48666152Spendry 
48765965Spendry 	error = VOP_OPEN(tvp, mode, cred, p);
48865965Spendry 
48965965Spendry 	return (error);
49065935Spendry }
49165935Spendry 
49265963Spendry int
49365963Spendry union_close(ap)
49465963Spendry 	struct vop_close_args /* {
49565963Spendry 		struct vnode *a_vp;
49665963Spendry 		int  a_fflag;
49765963Spendry 		struct ucred *a_cred;
49865963Spendry 		struct proc *a_p;
49965963Spendry 	} */ *ap;
50065963Spendry {
50165997Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
50265997Spendry 	struct vnode *vp;
50365963Spendry 
50467076Spendry 	if (un->un_uppervp != NULLVP) {
50565997Spendry 		vp = un->un_uppervp;
50665997Spendry 	} else {
50766027Spendry #ifdef UNION_DIAGNOSTIC
50866027Spendry 		if (un->un_openl <= 0)
50966027Spendry 			panic("union: un_openl cnt");
51065997Spendry #endif
51166027Spendry 		--un->un_openl;
51265997Spendry 		vp = un->un_lowervp;
51365997Spendry 	}
51466027Spendry 
51565997Spendry 	return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p));
51665963Spendry }
51765963Spendry 
51865935Spendry /*
51965963Spendry  * Check access permission on the union vnode.
52065963Spendry  * The access check being enforced is to check
52165963Spendry  * against both the underlying vnode, and any
52265963Spendry  * copied vnode.  This ensures that no additional
52365963Spendry  * file permissions are given away simply because
52465963Spendry  * the user caused an implicit file copy.
52565963Spendry  */
52665963Spendry int
52765963Spendry union_access(ap)
52865963Spendry 	struct vop_access_args /* {
52965963Spendry 		struct vnodeop_desc *a_desc;
53065963Spendry 		struct vnode *a_vp;
53165963Spendry 		int a_mode;
53265963Spendry 		struct ucred *a_cred;
53365963Spendry 		struct proc *a_p;
53465963Spendry 	} */ *ap;
53565963Spendry {
53665963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
53766149Spendry 	int error = EACCES;
53865963Spendry 	struct vnode *vp;
53965963Spendry 
54067076Spendry 	if ((vp = un->un_uppervp) != NULLVP) {
54166152Spendry 		FIXUP(un);
54266152Spendry 		return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p));
54366152Spendry 	}
54466152Spendry 
54567076Spendry 	if ((vp = un->un_lowervp) != NULLVP) {
54665965Spendry 		VOP_LOCK(vp);
54765963Spendry 		error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
54866152Spendry 		if (error == 0) {
54966152Spendry 			struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
55066152Spendry 
55166152Spendry 			if (um->um_op == UNMNT_BELOW)
55266152Spendry 				error = VOP_ACCESS(vp, ap->a_mode,
55366152Spendry 						um->um_cred, ap->a_p);
55466152Spendry 		}
55565965Spendry 		VOP_UNLOCK(vp);
55665963Spendry 		if (error)
55765963Spendry 			return (error);
55865963Spendry 	}
55965963Spendry 
56065965Spendry 	return (error);
56165963Spendry }
56265963Spendry 
56365963Spendry /*
56467109Spendry  * We handle getattr only to change the fsid and
56567109Spendry  * track object sizes
56665935Spendry  */
56765935Spendry int
56865935Spendry union_getattr(ap)
56965935Spendry 	struct vop_getattr_args /* {
57065935Spendry 		struct vnode *a_vp;
57165935Spendry 		struct vattr *a_vap;
57265935Spendry 		struct ucred *a_cred;
57365935Spendry 		struct proc *a_p;
57465935Spendry 	} */ *ap;
57565935Spendry {
57665935Spendry 	int error;
57766062Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
57866062Spendry 	struct vnode *vp = un->un_uppervp;
57966062Spendry 	struct vattr *vap;
58066062Spendry 	struct vattr va;
58165935Spendry 
58266062Spendry 
58366062Spendry 	/*
58466062Spendry 	 * Some programs walk the filesystem hierarchy by counting
58566062Spendry 	 * links to directories to avoid stat'ing all the time.
58666062Spendry 	 * This means the link count on directories needs to be "correct".
58766062Spendry 	 * The only way to do that is to call getattr on both layers
58866062Spendry 	 * and fix up the link count.  The link count will not necessarily
58966062Spendry 	 * be accurate but will be large enough to defeat the tree walkers.
59066062Spendry 	 */
59166062Spendry 
59266062Spendry 	vap = ap->a_vap;
59366062Spendry 
59466062Spendry 	vp = un->un_uppervp;
59566062Spendry 	if (vp != NULLVP) {
59667073Spendry 		/*
59767073Spendry 		 * It's not clear whether VOP_GETATTR is to be
59867073Spendry 		 * called with the vnode locked or not.  stat() calls
59967073Spendry 		 * it with (vp) locked, and fstat calls it with
60067073Spendry 		 * (vp) unlocked.
60167073Spendry 		 * In the mean time, compensate here by checking
60267073Spendry 		 * the union_node's lock flag.
60367073Spendry 		 */
60467073Spendry 		if (un->un_flags & UN_LOCKED)
60567073Spendry 			FIXUP(un);
60667073Spendry 
60766062Spendry 		error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
60866062Spendry 		if (error)
60966062Spendry 			return (error);
61067109Spendry 		union_newsize(ap->a_vp, vap->va_size, VNOVAL);
61166062Spendry 	}
61266062Spendry 
61366062Spendry 	if (vp == NULLVP) {
61466062Spendry 		vp = un->un_lowervp;
61566062Spendry 	} else if (vp->v_type == VDIR) {
61666062Spendry 		vp = un->un_lowervp;
61766062Spendry 		vap = &va;
61866062Spendry 	} else {
61966062Spendry 		vp = NULLVP;
62066062Spendry 	}
62166062Spendry 
62266062Spendry 	if (vp != NULLVP) {
62366062Spendry 		error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
62466062Spendry 		if (error)
62566062Spendry 			return (error);
62667109Spendry 		union_newsize(ap->a_vp, VNOVAL, vap->va_size);
62766062Spendry 	}
62865965Spendry 
62966062Spendry 	if ((vap != ap->a_vap) && (vap->va_type == VDIR))
63066062Spendry 		ap->a_vap->va_nlink += vap->va_nlink;
63166062Spendry 
63267400Spendry 	ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
63365935Spendry 	return (0);
63465935Spendry }
63565935Spendry 
63665963Spendry int
63765965Spendry union_setattr(ap)
63865963Spendry 	struct vop_setattr_args /* {
63965963Spendry 		struct vnode *a_vp;
64065963Spendry 		struct vattr *a_vap;
64165963Spendry 		struct ucred *a_cred;
64265963Spendry 		struct proc *a_p;
64365963Spendry 	} */ *ap;
64465963Spendry {
64565963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
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) {
66766152Spendry 		FIXUP(un);
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
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;
68965963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
69066051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
69165963Spendry 
69266051Spendry 	if (dolock)
69366051Spendry 		VOP_LOCK(vp);
69466152Spendry 	else
69566152Spendry 		FIXUP(VTOUNION(ap->a_vp));
69665963Spendry 	error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
69766051Spendry 	if (dolock)
69866051Spendry 		VOP_UNLOCK(vp);
69965963Spendry 
70067109Spendry 	/*
70167109Spendry 	 * XXX
70267109Spendry 	 * perhaps the size of the underlying object has changed under
70367109Spendry 	 * our feet.  take advantage of the offset information present
70467109Spendry 	 * in the uio structure.
70567109Spendry 	 */
70667109Spendry 	if (error == 0) {
70767109Spendry 		struct union_node *un = VTOUNION(ap->a_vp);
70867109Spendry 		off_t cur = ap->a_uio->uio_offset;
70967109Spendry 
71067109Spendry 		if (vp == un->un_uppervp) {
71167109Spendry 			if (cur > un->un_uppersz)
71267109Spendry 				union_newsize(ap->a_vp, cur, VNOVAL);
71367109Spendry 		} else {
71467109Spendry 			if (cur > un->un_lowersz)
71567109Spendry 				union_newsize(ap->a_vp, VNOVAL, cur);
71667109Spendry 		}
71767109Spendry 	}
71867109Spendry 
71965963Spendry 	return (error);
72065963Spendry }
72165963Spendry 
72265963Spendry int
72365963Spendry union_write(ap)
72465963Spendry 	struct vop_read_args /* {
72565963Spendry 		struct vnode *a_vp;
72665963Spendry 		struct uio *a_uio;
72765963Spendry 		int  a_ioflag;
72865963Spendry 		struct ucred *a_cred;
72965963Spendry 	} */ *ap;
73065963Spendry {
73165963Spendry 	int error;
73267575Spendry 	struct vnode *vp;
73367575Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
73465963Spendry 
73567575Spendry 	vp = UPPERVP(ap->a_vp);
73667575Spendry 	if (vp == NULLVP)
73767575Spendry 		panic("union: missing upper layer in write");
73867575Spendry 
73967575Spendry 	FIXUP(un);
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
80065963Spendry union_mmap(ap)
80165963Spendry 	struct vop_mmap_args /* {
80265963Spendry 		struct vnode *a_vp;
80365963Spendry 		int  a_fflags;
80465963Spendry 		struct ucred *a_cred;
80565963Spendry 		struct proc *a_p;
80665963Spendry 	} */ *ap;
80765963Spendry {
80865963Spendry 
80965963Spendry 	return (VOP_MMAP(OTHERVP(ap->a_vp), ap->a_fflags,
81065963Spendry 				ap->a_cred, ap->a_p));
81165963Spendry }
81265963Spendry 
81365963Spendry int
81465963Spendry union_fsync(ap)
81565963Spendry 	struct vop_fsync_args /* {
81665963Spendry 		struct vnode *a_vp;
81765963Spendry 		struct ucred *a_cred;
81865963Spendry 		int  a_waitfor;
81965963Spendry 		struct proc *a_p;
82065963Spendry 	} */ *ap;
82165963Spendry {
82265963Spendry 	int error = 0;
82365963Spendry 	struct vnode *targetvp = OTHERVP(ap->a_vp);
82465963Spendry 
82567076Spendry 	if (targetvp != NULLVP) {
82666051Spendry 		int dolock = (targetvp == LOWERVP(ap->a_vp));
82766051Spendry 
82866051Spendry 		if (dolock)
82966051Spendry 			VOP_LOCK(targetvp);
83066152Spendry 		else
83166152Spendry 			FIXUP(VTOUNION(ap->a_vp));
83265963Spendry 		error = VOP_FSYNC(targetvp, ap->a_cred,
83365963Spendry 					ap->a_waitfor, ap->a_p);
83466051Spendry 		if (dolock)
83566051Spendry 			VOP_UNLOCK(targetvp);
83665963Spendry 	}
83765963Spendry 
83865963Spendry 	return (error);
83965963Spendry }
84065963Spendry 
84165963Spendry int
84265963Spendry union_seek(ap)
84365963Spendry 	struct vop_seek_args /* {
84465963Spendry 		struct vnode *a_vp;
84565963Spendry 		off_t  a_oldoff;
84665963Spendry 		off_t  a_newoff;
84765963Spendry 		struct ucred *a_cred;
84865963Spendry 	} */ *ap;
84965963Spendry {
85065963Spendry 
85165963Spendry 	return (VOP_SEEK(OTHERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred));
85265963Spendry }
85365963Spendry 
85465963Spendry int
85565963Spendry union_remove(ap)
85665963Spendry 	struct vop_remove_args /* {
85765963Spendry 		struct vnode *a_dvp;
85865963Spendry 		struct vnode *a_vp;
85965963Spendry 		struct componentname *a_cnp;
86065963Spendry 	} */ *ap;
86165963Spendry {
86265963Spendry 	int error;
86365963Spendry 	struct union_node *dun = VTOUNION(ap->a_dvp);
86465963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
86565963Spendry 
86667575Spendry 	if (dun->un_uppervp == NULLVP)
86767575Spendry 		panic("union remove: null upper vnode");
86867575Spendry 
86967575Spendry 	if (un->un_uppervp != NULLVP) {
87065963Spendry 		struct vnode *dvp = dun->un_uppervp;
87165963Spendry 		struct vnode *vp = un->un_uppervp;
87267784Spendry 		struct componentname *cnp = ap->a_cnp;
87365963Spendry 
87466152Spendry 		FIXUP(dun);
87565963Spendry 		VREF(dvp);
87666051Spendry 		dun->un_flags |= UN_KLOCK;
87765963Spendry 		vput(ap->a_dvp);
87866152Spendry 		FIXUP(un);
87965963Spendry 		VREF(vp);
88066051Spendry 		un->un_flags |= UN_KLOCK;
88165963Spendry 		vput(ap->a_vp);
88265963Spendry 
88367784Spendry 		if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
88467784Spendry 			cnp->cn_flags |= DOWHITEOUT;
88567784Spendry 		error = VOP_REMOVE(dvp, vp, cnp);
88666027Spendry 		if (!error)
88766027Spendry 			union_removed_upper(un);
88865963Spendry 	} else {
88967575Spendry 		FIXUP(dun);
89067575Spendry 		error = union_mkwhiteout(
89167575Spendry 			MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
89267575Spendry 			dun->un_uppervp, ap->a_cnp, un->un_path);
89365963Spendry 		vput(ap->a_dvp);
89465963Spendry 		vput(ap->a_vp);
89565963Spendry 	}
89665963Spendry 
89765963Spendry 	return (error);
89865963Spendry }
89965963Spendry 
90065963Spendry int
90165963Spendry union_link(ap)
90265963Spendry 	struct vop_link_args /* {
90365963Spendry 		struct vnode *a_vp;
90465963Spendry 		struct vnode *a_tdvp;
90565963Spendry 		struct componentname *a_cnp;
90665963Spendry 	} */ *ap;
90765963Spendry {
90867169Spendry 	int error = 0;
90967169Spendry 	struct union_node *un;
91067169Spendry 	struct vnode *vp;
91167169Spendry 	struct vnode *tdvp;
91265963Spendry 
91368535Smckusick 	un = VTOUNION(ap->a_tdvp);
91465963Spendry 
91568535Smckusick 	if (ap->a_tdvp->v_op != ap->a_vp->v_op) {
91668535Smckusick 		vp = ap->a_vp;
91767169Spendry 	} else {
91868535Smckusick 		struct union_node *tun = VTOUNION(ap->a_vp);
91968535Smckusick 		if (tun->un_uppervp == NULLVP) {
92068535Smckusick 			VOP_LOCK(ap->a_vp);
92168535Smckusick 			if (un->un_uppervp == tun->un_dirvp) {
92267169Spendry 				un->un_flags &= ~UN_ULOCK;
92367169Spendry 				VOP_UNLOCK(un->un_uppervp);
92467169Spendry 			}
92568535Smckusick 			error = union_copyup(tun, 1, ap->a_cnp->cn_cred,
92667169Spendry 						ap->a_cnp->cn_proc);
92768535Smckusick 			if (un->un_uppervp == tun->un_dirvp) {
92867169Spendry 				VOP_LOCK(un->un_uppervp);
92967169Spendry 				un->un_flags |= UN_ULOCK;
93067169Spendry 			}
93168535Smckusick 			VOP_UNLOCK(ap->a_vp);
93267169Spendry 		}
93368535Smckusick 		vp = tun->un_uppervp;
93467169Spendry 	}
93565963Spendry 
93668535Smckusick 	tdvp = un->un_uppervp;
93768535Smckusick 	if (tdvp == NULLVP)
93867169Spendry 		error = EROFS;
93967169Spendry 
94067169Spendry 	if (error) {
94168535Smckusick 		vput(ap->a_tdvp);
94267169Spendry 		return (error);
94365963Spendry 	}
94465963Spendry 
94567169Spendry 	FIXUP(un);
94668535Smckusick 	VREF(tdvp);
94767169Spendry 	un->un_flags |= UN_KLOCK;
94868535Smckusick 	vput(ap->a_tdvp);
94967169Spendry 
95067169Spendry 	return (VOP_LINK(vp, tdvp, ap->a_cnp));
95165963Spendry }
95265963Spendry 
95365963Spendry int
95465963Spendry union_rename(ap)
95565963Spendry 	struct vop_rename_args  /* {
95665963Spendry 		struct vnode *a_fdvp;
95765963Spendry 		struct vnode *a_fvp;
95865963Spendry 		struct componentname *a_fcnp;
95965963Spendry 		struct vnode *a_tdvp;
96065963Spendry 		struct vnode *a_tvp;
96165963Spendry 		struct componentname *a_tcnp;
96265963Spendry 	} */ *ap;
96365963Spendry {
96465963Spendry 	int error;
96565963Spendry 
96665963Spendry 	struct vnode *fdvp = ap->a_fdvp;
96765963Spendry 	struct vnode *fvp = ap->a_fvp;
96865963Spendry 	struct vnode *tdvp = ap->a_tdvp;
96965963Spendry 	struct vnode *tvp = ap->a_tvp;
97065963Spendry 
97165963Spendry 	if (fdvp->v_op == union_vnodeop_p) {	/* always true */
97265963Spendry 		struct union_node *un = VTOUNION(fdvp);
97365997Spendry 		if (un->un_uppervp == NULLVP) {
97467575Spendry 			/*
97567575Spendry 			 * this should never happen in normal
97667575Spendry 			 * operation but might if there was
97767575Spendry 			 * a problem creating the top-level shadow
97867575Spendry 			 * directory.
97967575Spendry 			 */
98067575Spendry 			error = EXDEV;
98165963Spendry 			goto bad;
98265963Spendry 		}
98365963Spendry 
98465963Spendry 		fdvp = un->un_uppervp;
98565963Spendry 		VREF(fdvp);
98665963Spendry 		vrele(ap->a_fdvp);
98765963Spendry 	}
98865963Spendry 
98965963Spendry 	if (fvp->v_op == union_vnodeop_p) {	/* always true */
99065963Spendry 		struct union_node *un = VTOUNION(fvp);
99165997Spendry 		if (un->un_uppervp == NULLVP) {
99267575Spendry 			/* XXX: should do a copyup */
99367575Spendry 			error = EXDEV;
99465963Spendry 			goto bad;
99565963Spendry 		}
99665963Spendry 
99767575Spendry 		if (un->un_lowervp != NULLVP)
99867575Spendry 			ap->a_fcnp->cn_flags |= DOWHITEOUT;
99967575Spendry 
100065963Spendry 		fvp = un->un_uppervp;
100165963Spendry 		VREF(fvp);
100265963Spendry 		vrele(ap->a_fvp);
100365963Spendry 	}
100465963Spendry 
100565963Spendry 	if (tdvp->v_op == union_vnodeop_p) {
100665963Spendry 		struct union_node *un = VTOUNION(tdvp);
100765997Spendry 		if (un->un_uppervp == NULLVP) {
100867076Spendry 			/*
100967076Spendry 			 * this should never happen in normal
101067076Spendry 			 * operation but might if there was
101167076Spendry 			 * a problem creating the top-level shadow
101267076Spendry 			 * directory.
101367076Spendry 			 */
101467575Spendry 			error = EXDEV;
101565963Spendry 			goto bad;
101665963Spendry 		}
101765963Spendry 
101865963Spendry 		tdvp = un->un_uppervp;
101965963Spendry 		VREF(tdvp);
102066051Spendry 		un->un_flags |= UN_KLOCK;
102165997Spendry 		vput(ap->a_tdvp);
102265963Spendry 	}
102365963Spendry 
102467076Spendry 	if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) {
102565963Spendry 		struct union_node *un = VTOUNION(tvp);
102665963Spendry 
102765963Spendry 		tvp = un->un_uppervp;
102867076Spendry 		if (tvp != NULLVP) {
102967076Spendry 			VREF(tvp);
103067076Spendry 			un->un_flags |= UN_KLOCK;
103167076Spendry 		}
103265963Spendry 		vput(ap->a_tvp);
103365963Spendry 	}
103465963Spendry 
103565963Spendry 	return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));
103665963Spendry 
103765963Spendry bad:
103865963Spendry 	vrele(fdvp);
103965963Spendry 	vrele(fvp);
104065963Spendry 	vput(tdvp);
104167076Spendry 	if (tvp != NULLVP)
104265963Spendry 		vput(tvp);
104365963Spendry 
104465963Spendry 	return (error);
104565963Spendry }
104665963Spendry 
104765963Spendry int
104865963Spendry union_mkdir(ap)
104965963Spendry 	struct vop_mkdir_args /* {
105065963Spendry 		struct vnode *a_dvp;
105165963Spendry 		struct vnode **a_vpp;
105265963Spendry 		struct componentname *a_cnp;
105365963Spendry 		struct vattr *a_vap;
105465963Spendry 	} */ *ap;
105565963Spendry {
105665963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
105765963Spendry 	struct vnode *dvp = un->un_uppervp;
105865963Spendry 
105967076Spendry 	if (dvp != NULLVP) {
106065963Spendry 		int error;
106165963Spendry 		struct vnode *vp;
106265963Spendry 
106366152Spendry 		FIXUP(un);
106465963Spendry 		VREF(dvp);
106566051Spendry 		un->un_flags |= UN_KLOCK;
106668078Spendry 		VOP_UNLOCK(ap->a_dvp);
106765963Spendry 		error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap);
106868078Spendry 		if (error) {
106968078Spendry 			vrele(ap->a_dvp);
107065963Spendry 			return (error);
107168078Spendry 		}
107265963Spendry 
107365963Spendry 		error = union_allocvp(
107465963Spendry 				ap->a_vpp,
107565989Spendry 				ap->a_dvp->v_mount,
107665989Spendry 				ap->a_dvp,
107765965Spendry 				NULLVP,
107865963Spendry 				ap->a_cnp,
107965963Spendry 				vp,
108068078Spendry 				NULLVP,
108168078Spendry 				1);
108268078Spendry 		vrele(ap->a_dvp);
108365965Spendry 		if (error)
108466051Spendry 			vput(vp);
108565963Spendry 		return (error);
108665963Spendry 	}
108765963Spendry 
108865963Spendry 	vput(ap->a_dvp);
108965963Spendry 	return (EROFS);
109065963Spendry }
109165963Spendry 
109265963Spendry int
109365963Spendry union_rmdir(ap)
109465963Spendry 	struct vop_rmdir_args /* {
109565963Spendry 		struct vnode *a_dvp;
109665963Spendry 		struct vnode *a_vp;
109765963Spendry 		struct componentname *a_cnp;
109865963Spendry 	} */ *ap;
109965963Spendry {
110065963Spendry 	int error;
110165963Spendry 	struct union_node *dun = VTOUNION(ap->a_dvp);
110265963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
110365963Spendry 
110467575Spendry 	if (dun->un_uppervp == NULLVP)
110567575Spendry 		panic("union rmdir: null upper vnode");
110667575Spendry 
110767575Spendry 	if (un->un_uppervp != NULLVP) {
110865963Spendry 		struct vnode *dvp = dun->un_uppervp;
110965963Spendry 		struct vnode *vp = un->un_uppervp;
111067784Spendry 		struct componentname *cnp = ap->a_cnp;
111165963Spendry 
111266152Spendry 		FIXUP(dun);
111365963Spendry 		VREF(dvp);
111466051Spendry 		dun->un_flags |= UN_KLOCK;
111565963Spendry 		vput(ap->a_dvp);
111666152Spendry 		FIXUP(un);
111765963Spendry 		VREF(vp);
111866051Spendry 		un->un_flags |= UN_KLOCK;
111965963Spendry 		vput(ap->a_vp);
112065963Spendry 
112167784Spendry 		if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
112267784Spendry 			cnp->cn_flags |= DOWHITEOUT;
112366051Spendry 		error = VOP_RMDIR(dvp, vp, ap->a_cnp);
112466027Spendry 		if (!error)
112566027Spendry 			union_removed_upper(un);
112665963Spendry 	} else {
112767575Spendry 		FIXUP(dun);
112867575Spendry 		error = union_mkwhiteout(
112967575Spendry 			MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
113067575Spendry 			dun->un_uppervp, ap->a_cnp, un->un_path);
113165963Spendry 		vput(ap->a_dvp);
113265963Spendry 		vput(ap->a_vp);
113365963Spendry 	}
113465963Spendry 
113565963Spendry 	return (error);
113665963Spendry }
113765963Spendry 
113865963Spendry int
113965963Spendry union_symlink(ap)
114065963Spendry 	struct vop_symlink_args /* {
114165963Spendry 		struct vnode *a_dvp;
114265963Spendry 		struct vnode **a_vpp;
114365963Spendry 		struct componentname *a_cnp;
114465963Spendry 		struct vattr *a_vap;
114565963Spendry 		char *a_target;
114665963Spendry 	} */ *ap;
114765963Spendry {
114865963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
114965963Spendry 	struct vnode *dvp = un->un_uppervp;
115065963Spendry 
115167076Spendry 	if (dvp != NULLVP) {
115265963Spendry 		int error;
115365963Spendry 		struct vnode *vp;
115465963Spendry 		struct mount *mp = ap->a_dvp->v_mount;
115565963Spendry 
115666152Spendry 		FIXUP(un);
115765963Spendry 		VREF(dvp);
115866051Spendry 		un->un_flags |= UN_KLOCK;
115965963Spendry 		vput(ap->a_dvp);
116065963Spendry 		error = VOP_SYMLINK(dvp, &vp, ap->a_cnp,
116165963Spendry 					ap->a_vap, ap->a_target);
116265997Spendry 		*ap->a_vpp = NULLVP;
116365963Spendry 		return (error);
116465963Spendry 	}
116565963Spendry 
116665963Spendry 	vput(ap->a_dvp);
116765963Spendry 	return (EROFS);
116865963Spendry }
116965963Spendry 
117065935Spendry /*
117165935Spendry  * union_readdir works in concert with getdirentries and
117265935Spendry  * readdir(3) to provide a list of entries in the unioned
117365935Spendry  * directories.  getdirentries is responsible for walking
117465935Spendry  * down the union stack.  readdir(3) is responsible for
117565935Spendry  * eliminating duplicate names from the returned data stream.
117665935Spendry  */
117765935Spendry int
117865935Spendry union_readdir(ap)
117965935Spendry 	struct vop_readdir_args /* {
118065935Spendry 		struct vnodeop_desc *a_desc;
118165935Spendry 		struct vnode *a_vp;
118265935Spendry 		struct uio *a_uio;
118365935Spendry 		struct ucred *a_cred;
118467369Smckusick 		int *a_eofflag;
118567369Smckusick 		u_long *a_cookies;
118667369Smckusick 		int a_ncookies;
118765935Spendry 	} */ *ap;
118865935Spendry {
118967369Smckusick 	register struct union_node *un = VTOUNION(ap->a_vp);
119067369Smckusick 	register struct vnode *uvp = un->un_uppervp;
119165935Spendry 
119267369Smckusick 	if (uvp == NULLVP)
119367369Smckusick 		return (0);
119465935Spendry 
119567369Smckusick 	FIXUP(un);
119667369Smckusick 	ap->a_vp = uvp;
119767369Smckusick 	return (VOCALL(uvp->v_op, VOFFSET(vop_readdir), ap));
119865935Spendry }
119965935Spendry 
120065935Spendry int
120165963Spendry union_readlink(ap)
120265963Spendry 	struct vop_readlink_args /* {
120365963Spendry 		struct vnode *a_vp;
120465963Spendry 		struct uio *a_uio;
120565963Spendry 		struct ucred *a_cred;
120665963Spendry 	} */ *ap;
120765963Spendry {
120865963Spendry 	int error;
120965963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
121066051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
121165963Spendry 
121266051Spendry 	if (dolock)
121366051Spendry 		VOP_LOCK(vp);
121466152Spendry 	else
121566152Spendry 		FIXUP(VTOUNION(ap->a_vp));
121665963Spendry 	error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
121766051Spendry 	if (dolock)
121866051Spendry 		VOP_UNLOCK(vp);
121965963Spendry 
122065963Spendry 	return (error);
122165963Spendry }
122265963Spendry 
122365963Spendry int
122465963Spendry union_abortop(ap)
122565963Spendry 	struct vop_abortop_args /* {
122665963Spendry 		struct vnode *a_dvp;
122765963Spendry 		struct componentname *a_cnp;
122865963Spendry 	} */ *ap;
122965963Spendry {
123065963Spendry 	int error;
123165965Spendry 	struct vnode *vp = OTHERVP(ap->a_dvp);
123265963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
123365963Spendry 	int islocked = un->un_flags & UN_LOCKED;
123466051Spendry 	int dolock = (vp == LOWERVP(ap->a_dvp));
123565963Spendry 
123666152Spendry 	if (islocked) {
123766152Spendry 		if (dolock)
123866152Spendry 			VOP_LOCK(vp);
123966152Spendry 		else
124066152Spendry 			FIXUP(VTOUNION(ap->a_dvp));
124166152Spendry 	}
124265963Spendry 	error = VOP_ABORTOP(vp, ap->a_cnp);
124366051Spendry 	if (islocked && dolock)
124465963Spendry 		VOP_UNLOCK(vp);
124565963Spendry 
124665963Spendry 	return (error);
124765963Spendry }
124865963Spendry 
124965963Spendry int
125065935Spendry union_inactive(ap)
125165935Spendry 	struct vop_inactive_args /* {
125265935Spendry 		struct vnode *a_vp;
125365935Spendry 	} */ *ap;
125465935Spendry {
125567073Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
125668078Spendry 	struct vnode **vpp;
125765935Spendry 
125865935Spendry 	/*
125965935Spendry 	 * Do nothing (and _don't_ bypass).
126065935Spendry 	 * Wait to vrele lowervp until reclaim,
126165935Spendry 	 * so that until then our union_node is in the
126265935Spendry 	 * cache and reusable.
126365935Spendry 	 *
126465935Spendry 	 * NEEDSWORK: Someday, consider inactive'ing
126565935Spendry 	 * the lowervp and then trying to reactivate it
126665935Spendry 	 * with capabilities (v_id)
126765935Spendry 	 * like they do in the name lookup cache code.
126865935Spendry 	 * That's too much work for now.
126965935Spendry 	 */
127065989Spendry 
127166027Spendry #ifdef UNION_DIAGNOSTIC
127265989Spendry 	if (un->un_flags & UN_LOCKED)
127365989Spendry 		panic("union: inactivating locked node");
127467073Spendry 	if (un->un_flags & UN_ULOCK)
127567073Spendry 		panic("union: inactivating w/locked upper node");
127665989Spendry #endif
127765989Spendry 
127868078Spendry 	if (un->un_dircache != 0) {
127968078Spendry 		for (vpp = un->un_dircache; *vpp != NULLVP; vpp++)
128068078Spendry 			vrele(*vpp);
128168078Spendry 		free(un->un_dircache, M_TEMP);
128268078Spendry 		un->un_dircache = 0;
128368078Spendry 	}
128468078Spendry 
128567073Spendry 	if ((un->un_flags & UN_CACHED) == 0)
128668408Smckusick 		VOP_REVOKE(ap->a_vp, 0);
128767073Spendry 
128865935Spendry 	return (0);
128965935Spendry }
129065935Spendry 
129165935Spendry int
129265935Spendry union_reclaim(ap)
129365935Spendry 	struct vop_reclaim_args /* {
129465935Spendry 		struct vnode *a_vp;
129565935Spendry 	} */ *ap;
129665935Spendry {
129765935Spendry 
129866053Spendry 	union_freevp(ap->a_vp);
129966053Spendry 
130065935Spendry 	return (0);
130165935Spendry }
130265935Spendry 
130365963Spendry int
130465963Spendry union_lock(ap)
130565963Spendry 	struct vop_lock_args *ap;
130665963Spendry {
130766149Spendry 	struct vnode *vp = ap->a_vp;
130866149Spendry 	struct union_node *un;
130965935Spendry 
131066149Spendry start:
131166149Spendry 	while (vp->v_flag & VXLOCK) {
131266149Spendry 		vp->v_flag |= VXWANT;
131366149Spendry 		sleep((caddr_t)vp, PINOD);
131466149Spendry 	}
131566149Spendry 
131666149Spendry 	un = VTOUNION(vp);
131766149Spendry 
131867076Spendry 	if (un->un_uppervp != NULLVP) {
131967230Spendry 		if (((un->un_flags & UN_ULOCK) == 0) &&
132067230Spendry 		    (vp->v_usecount != 0)) {
1321*68590Spendry 			VOP_LOCK(un->un_uppervp);
132266149Spendry 			un->un_flags |= UN_ULOCK;
132366051Spendry 		}
132466051Spendry #ifdef DIAGNOSTIC
132566051Spendry 		if (un->un_flags & UN_KLOCK)
1326*68590Spendry 			vprint("union: dangling klock", vp);
1327*68590Spendry 			panic("union: dangling upper lock (%lx)", vp);
132866051Spendry #endif
132966051Spendry 	}
133066051Spendry 
133166149Spendry 	if (un->un_flags & UN_LOCKED) {
133265963Spendry #ifdef DIAGNOSTIC
133365989Spendry 		if (curproc && un->un_pid == curproc->p_pid &&
133465989Spendry 			    un->un_pid > -1 && curproc->p_pid > -1)
133565989Spendry 			panic("union: locking against myself");
133665963Spendry #endif
133765963Spendry 		un->un_flags |= UN_WANT;
133865963Spendry 		sleep((caddr_t) &un->un_flags, PINOD);
133966149Spendry 		goto start;
134065963Spendry 	}
134165989Spendry 
134265963Spendry #ifdef DIAGNOSTIC
134365989Spendry 	if (curproc)
134465989Spendry 		un->un_pid = curproc->p_pid;
134565989Spendry 	else
134665989Spendry 		un->un_pid = -1;
134765963Spendry #endif
134866028Spendry 
134966149Spendry 	un->un_flags |= UN_LOCKED;
135066028Spendry 	return (0);
135165963Spendry }
135265963Spendry 
1353*68590Spendry /*
1354*68590Spendry  * When operations want to vput() a union node yet retain a lock on
1355*68590Spendry  * the upper vnode (say, to do some further operations like link(),
1356*68590Spendry  * mkdir(), ...), they set UN_KLOCK on the union node, then call
1357*68590Spendry  * vput() which calls VOP_UNLOCK() and comes here.  union_unlock()
1358*68590Spendry  * unlocks the union node (leaving the upper vnode alone), clears the
1359*68590Spendry  * KLOCK flag, and then returns to vput().  The caller then does whatever
1360*68590Spendry  * is left to do with the upper vnode, and ensures that it gets unlocked.
1361*68590Spendry  *
1362*68590Spendry  * If UN_KLOCK isn't set, then the upper vnode is unlocked here.
1363*68590Spendry  */
136465935Spendry int
136565963Spendry union_unlock(ap)
136665963Spendry 	struct vop_lock_args *ap;
136765963Spendry {
136865963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
136965963Spendry 
137065963Spendry #ifdef DIAGNOSTIC
137165965Spendry 	if ((un->un_flags & UN_LOCKED) == 0)
137265965Spendry 		panic("union: unlock unlocked node");
137365989Spendry 	if (curproc && un->un_pid != curproc->p_pid &&
137465989Spendry 			curproc->p_pid > -1 && un->un_pid > -1)
137565963Spendry 		panic("union: unlocking other process's union node");
137665963Spendry #endif
137765963Spendry 
137865963Spendry 	un->un_flags &= ~UN_LOCKED;
137966051Spendry 
138066051Spendry 	if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK)
138166051Spendry 		VOP_UNLOCK(un->un_uppervp);
138266051Spendry 
138366051Spendry 	un->un_flags &= ~(UN_ULOCK|UN_KLOCK);
138466051Spendry 
138565963Spendry 	if (un->un_flags & UN_WANT) {
138665963Spendry 		un->un_flags &= ~UN_WANT;
138765963Spendry 		wakeup((caddr_t) &un->un_flags);
138865963Spendry 	}
138965963Spendry 
139065963Spendry #ifdef DIAGNOSTIC
139165963Spendry 	un->un_pid = 0;
139265963Spendry #endif
139366028Spendry 
139466028Spendry 	return (0);
139565963Spendry }
139665963Spendry 
139765963Spendry int
139865963Spendry union_bmap(ap)
139965963Spendry 	struct vop_bmap_args /* {
140065963Spendry 		struct vnode *a_vp;
140165963Spendry 		daddr_t  a_bn;
140265963Spendry 		struct vnode **a_vpp;
140365963Spendry 		daddr_t *a_bnp;
140465963Spendry 		int *a_runp;
140565963Spendry 	} */ *ap;
140665963Spendry {
140765963Spendry 	int error;
140865963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
140966051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
141065963Spendry 
141166051Spendry 	if (dolock)
141266051Spendry 		VOP_LOCK(vp);
141366152Spendry 	else
141466152Spendry 		FIXUP(VTOUNION(ap->a_vp));
141565963Spendry 	error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp);
141666051Spendry 	if (dolock)
141766051Spendry 		VOP_UNLOCK(vp);
141865963Spendry 
141965963Spendry 	return (error);
142065963Spendry }
142165963Spendry 
142265963Spendry int
142365935Spendry union_print(ap)
142465935Spendry 	struct vop_print_args /* {
142565935Spendry 		struct vnode *a_vp;
142665935Spendry 	} */ *ap;
142765935Spendry {
142865935Spendry 	struct vnode *vp = ap->a_vp;
142965935Spendry 
143065935Spendry 	printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n",
143165935Spendry 			vp, UPPERVP(vp), LOWERVP(vp));
1432*68590Spendry 	if (UPPERVP(vp) != NULLVP)
1433*68590Spendry 		vprint("union: upper", UPPERVP(vp));
1434*68590Spendry 	if (LOWERVP(vp) != NULLVP)
1435*68590Spendry 		vprint("union: lower", LOWERVP(vp));
1436*68590Spendry 
143765935Spendry 	return (0);
143865935Spendry }
143965935Spendry 
144065963Spendry int
144165963Spendry union_islocked(ap)
144265963Spendry 	struct vop_islocked_args /* {
144365963Spendry 		struct vnode *a_vp;
144465963Spendry 	} */ *ap;
144565963Spendry {
144665935Spendry 
144765963Spendry 	return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0);
144865963Spendry }
144965963Spendry 
145065935Spendry int
145165963Spendry union_pathconf(ap)
145265963Spendry 	struct vop_pathconf_args /* {
145365963Spendry 		struct vnode *a_vp;
145465963Spendry 		int a_name;
145565963Spendry 		int *a_retval;
145665935Spendry 	} */ *ap;
145765935Spendry {
145865935Spendry 	int error;
145965963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
146066051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
146165935Spendry 
146266051Spendry 	if (dolock)
146366051Spendry 		VOP_LOCK(vp);
146466152Spendry 	else
146566152Spendry 		FIXUP(VTOUNION(ap->a_vp));
146665963Spendry 	error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
146766051Spendry 	if (dolock)
146866051Spendry 		VOP_UNLOCK(vp);
146965935Spendry 
147065963Spendry 	return (error);
147165963Spendry }
147265935Spendry 
147365963Spendry int
147465963Spendry union_advlock(ap)
147565963Spendry 	struct vop_advlock_args /* {
147665963Spendry 		struct vnode *a_vp;
147765963Spendry 		caddr_t  a_id;
147865963Spendry 		int  a_op;
147965963Spendry 		struct flock *a_fl;
148065963Spendry 		int  a_flags;
148165963Spendry 	} */ *ap;
148265963Spendry {
148365935Spendry 
148465963Spendry 	return (VOP_ADVLOCK(OTHERVP(ap->a_vp), ap->a_id, ap->a_op,
148565963Spendry 				ap->a_fl, ap->a_flags));
148665935Spendry }
148765935Spendry 
148865935Spendry 
148965935Spendry /*
149065963Spendry  * XXX - vop_strategy must be hand coded because it has no
149165935Spendry  * vnode in its arguments.
149265935Spendry  * This goes away with a merged VM/buffer cache.
149365935Spendry  */
149465935Spendry int
149565963Spendry union_strategy(ap)
149665963Spendry 	struct vop_strategy_args /* {
149765935Spendry 		struct buf *a_bp;
149865935Spendry 	} */ *ap;
149965935Spendry {
150065935Spendry 	struct buf *bp = ap->a_bp;
150165935Spendry 	int error;
150265935Spendry 	struct vnode *savedvp;
150365935Spendry 
150465935Spendry 	savedvp = bp->b_vp;
150565963Spendry 	bp->b_vp = OTHERVP(bp->b_vp);
150665935Spendry 
150765935Spendry #ifdef DIAGNOSTIC
150865997Spendry 	if (bp->b_vp == NULLVP)
150965963Spendry 		panic("union_strategy: nil vp");
151065963Spendry 	if (((bp->b_flags & B_READ) == 0) &&
151165963Spendry 	    (bp->b_vp == LOWERVP(savedvp)))
151265963Spendry 		panic("union_strategy: writing to lowervp");
151365935Spendry #endif
151465935Spendry 
151565963Spendry 	error = VOP_STRATEGY(bp);
151665935Spendry 	bp->b_vp = savedvp;
151765935Spendry 
151865935Spendry 	return (error);
151965935Spendry }
152065935Spendry 
152165935Spendry /*
152265935Spendry  * Global vfs data structures
152365935Spendry  */
152465935Spendry int (**union_vnodeop_p)();
152565965Spendry struct vnodeopv_entry_desc union_vnodeop_entries[] = {
152665963Spendry 	{ &vop_default_desc, vn_default_error },
152765963Spendry 	{ &vop_lookup_desc, union_lookup },		/* lookup */
152865963Spendry 	{ &vop_create_desc, union_create },		/* create */
152967575Spendry 	{ &vop_whiteout_desc, union_whiteout },		/* whiteout */
153065963Spendry 	{ &vop_mknod_desc, union_mknod },		/* mknod */
153165963Spendry 	{ &vop_open_desc, union_open },			/* open */
153265963Spendry 	{ &vop_close_desc, union_close },		/* close */
153365963Spendry 	{ &vop_access_desc, union_access },		/* access */
153465963Spendry 	{ &vop_getattr_desc, union_getattr },		/* getattr */
153565963Spendry 	{ &vop_setattr_desc, union_setattr },		/* setattr */
153665963Spendry 	{ &vop_read_desc, union_read },			/* read */
153765963Spendry 	{ &vop_write_desc, union_write },		/* write */
153867751Spendry 	{ &vop_lease_desc, union_lease },		/* lease */
153965963Spendry 	{ &vop_ioctl_desc, union_ioctl },		/* ioctl */
154065963Spendry 	{ &vop_select_desc, union_select },		/* select */
154165963Spendry 	{ &vop_mmap_desc, union_mmap },			/* mmap */
154265963Spendry 	{ &vop_fsync_desc, union_fsync },		/* fsync */
154365963Spendry 	{ &vop_seek_desc, union_seek },			/* seek */
154465963Spendry 	{ &vop_remove_desc, union_remove },		/* remove */
154565963Spendry 	{ &vop_link_desc, union_link },			/* link */
154665963Spendry 	{ &vop_rename_desc, union_rename },		/* rename */
154765963Spendry 	{ &vop_mkdir_desc, union_mkdir },		/* mkdir */
154865963Spendry 	{ &vop_rmdir_desc, union_rmdir },		/* rmdir */
154965963Spendry 	{ &vop_symlink_desc, union_symlink },		/* symlink */
155065963Spendry 	{ &vop_readdir_desc, union_readdir },		/* readdir */
155165963Spendry 	{ &vop_readlink_desc, union_readlink },		/* readlink */
155265963Spendry 	{ &vop_abortop_desc, union_abortop },		/* abortop */
155365963Spendry 	{ &vop_inactive_desc, union_inactive },		/* inactive */
155465963Spendry 	{ &vop_reclaim_desc, union_reclaim },		/* reclaim */
155565963Spendry 	{ &vop_lock_desc, union_lock },			/* lock */
155665963Spendry 	{ &vop_unlock_desc, union_unlock },		/* unlock */
155765963Spendry 	{ &vop_bmap_desc, union_bmap },			/* bmap */
155865963Spendry 	{ &vop_strategy_desc, union_strategy },		/* strategy */
155965963Spendry 	{ &vop_print_desc, union_print },		/* print */
156065963Spendry 	{ &vop_islocked_desc, union_islocked },		/* islocked */
156165963Spendry 	{ &vop_pathconf_desc, union_pathconf },		/* pathconf */
156265963Spendry 	{ &vop_advlock_desc, union_advlock },		/* advlock */
156365963Spendry #ifdef notdef
156465963Spendry 	{ &vop_blkatoff_desc, union_blkatoff },		/* blkatoff */
156565963Spendry 	{ &vop_valloc_desc, union_valloc },		/* valloc */
156665963Spendry 	{ &vop_vfree_desc, union_vfree },		/* vfree */
156765963Spendry 	{ &vop_truncate_desc, union_truncate },		/* truncate */
156865963Spendry 	{ &vop_update_desc, union_update },		/* update */
156965963Spendry 	{ &vop_bwrite_desc, union_bwrite },		/* bwrite */
157065963Spendry #endif
157165935Spendry 	{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
157265935Spendry };
157365935Spendry struct vnodeopv_desc union_vnodeop_opv_desc =
157465935Spendry 	{ &union_vnodeop_p, union_vnodeop_entries };
1575