xref: /csrg-svn/sys/miscfs/union/union_vnops.c (revision 69447)
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*69447Smckusick  *	@(#)union_vnops.c	8.27 (Berkeley) 05/14/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>
27*69447Smckusick #include <sys/lock.h>
2866055Spendry #include <miscfs/union/union.h>
2965935Spendry 
30*69447Smckusick #define FIXUP(un, p) { \
3166152Spendry 	if (((un)->un_flags & UN_ULOCK) == 0) { \
32*69447Smckusick 		union_fixup(un, p); \
3366152Spendry 	} \
3466152Spendry }
3566152Spendry 
3666152Spendry static void
37*69447Smckusick union_fixup(un, p)
3866152Spendry 	struct union_node *un;
39*69447Smckusick 	struct proc *p;
4066152Spendry {
4166152Spendry 
42*69447Smckusick 	vn_lock(un->un_uppervp, LK_EXCLUSIVE | LK_RETRY, p);
4366152Spendry 	un->un_flags |= UN_ULOCK;
4466152Spendry }
4566152Spendry 
4665935Spendry static int
4767064Spendry union_lookup1(udvp, dvpp, vpp, cnp)
4865989Spendry 	struct vnode *udvp;
4967064Spendry 	struct vnode **dvpp;
5065935Spendry 	struct vnode **vpp;
5165935Spendry 	struct componentname *cnp;
5265935Spendry {
5365935Spendry 	int error;
54*69447Smckusick 	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);
79*69447Smckusick 			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))
93*69447Smckusick 		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 
10565935Spendry 		if (mp->mnt_flag & MNT_MLOCK) {
10665935Spendry 			mp->mnt_flag |= MNT_MWAIT;
10765935Spendry 			sleep((caddr_t) mp, PVFS);
10865935Spendry 			continue;
10965935Spendry 		}
11065935Spendry 
11165935Spendry 		if (error = VFS_ROOT(mp, &tdvp)) {
11265935Spendry 			vput(dvp);
11365935Spendry 			return (error);
11465935Spendry 		}
11565935Spendry 
11665965Spendry 		vput(dvp);
11765935Spendry 		dvp = tdvp;
11865935Spendry 	}
11965935Spendry 
12065935Spendry 	*vpp = dvp;
12165935Spendry 	return (0);
12265935Spendry }
12365935Spendry 
12465935Spendry int
12565935Spendry union_lookup(ap)
12665935Spendry 	struct vop_lookup_args /* {
12765935Spendry 		struct vnodeop_desc *a_desc;
12865935Spendry 		struct vnode *a_dvp;
12965935Spendry 		struct vnode **a_vpp;
13065935Spendry 		struct componentname *a_cnp;
13165935Spendry 	} */ *ap;
13265935Spendry {
13365965Spendry 	int error;
13465935Spendry 	int uerror, lerror;
13565935Spendry 	struct vnode *uppervp, *lowervp;
13665935Spendry 	struct vnode *upperdvp, *lowerdvp;
13765935Spendry 	struct vnode *dvp = ap->a_dvp;
13865989Spendry 	struct union_node *dun = VTOUNION(dvp);
13965935Spendry 	struct componentname *cnp = ap->a_cnp;
140*69447Smckusick 	struct proc *p = cnp->cn_proc;
14165935Spendry 	int lockparent = cnp->cn_flags & LOCKPARENT;
14265994Spendry 	int rdonly = cnp->cn_flags & RDONLY;
14365997Spendry 	struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
14466152Spendry 	struct ucred *saved_cred;
14567575Spendry 	int iswhiteout;
14667575Spendry 	struct vattr va;
14765935Spendry 
14867446Spendry #ifdef notyet
14967446Spendry 	if (cnp->cn_namelen == 3 &&
15067446Spendry 			cnp->cn_nameptr[2] == '.' &&
15167446Spendry 			cnp->cn_nameptr[1] == '.' &&
15267446Spendry 			cnp->cn_nameptr[0] == '.') {
15367446Spendry 		dvp = *ap->a_vpp = LOWERVP(ap->a_dvp);
15467446Spendry 		if (dvp == NULLVP)
15567446Spendry 			return (ENOENT);
15667446Spendry 		VREF(dvp);
157*69447Smckusick 		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
15867446Spendry 		if (!lockparent || !(cnp->cn_flags & ISLASTCN))
159*69447Smckusick 			VOP_UNLOCK(ap->a_dvp, 0, p);
16067446Spendry 		return (0);
16167446Spendry 	}
16267446Spendry #endif
16367446Spendry 
16465965Spendry 	cnp->cn_flags |= LOCKPARENT;
16565965Spendry 
16665935Spendry 	upperdvp = dun->un_uppervp;
16765935Spendry 	lowerdvp = dun->un_lowervp;
16865997Spendry 	uppervp = NULLVP;
16965997Spendry 	lowervp = NULLVP;
17067575Spendry 	iswhiteout = 0;
17165935Spendry 
17265935Spendry 	/*
17365935Spendry 	 * do the lookup in the upper level.
17465935Spendry 	 * if that level comsumes additional pathnames,
17565935Spendry 	 * then assume that something special is going
17665935Spendry 	 * on and just return that vnode.
17765935Spendry 	 */
17867076Spendry 	if (upperdvp != NULLVP) {
179*69447Smckusick 		FIXUP(dun, p);
18067064Spendry 		uerror = union_lookup1(um->um_uppervp, &upperdvp,
18165997Spendry 					&uppervp, cnp);
18266051Spendry 		/*if (uppervp == upperdvp)
18366051Spendry 			dun->un_flags |= UN_KLOCK;*/
18465965Spendry 
18565935Spendry 		if (cnp->cn_consume != 0) {
18665935Spendry 			*ap->a_vpp = uppervp;
18765965Spendry 			if (!lockparent)
18865965Spendry 				cnp->cn_flags &= ~LOCKPARENT;
18965935Spendry 			return (uerror);
19065935Spendry 		}
19167575Spendry 		if (uerror == ENOENT || uerror == EJUSTRETURN) {
19267575Spendry 			if (cnp->cn_flags & ISWHITEOUT) {
19367575Spendry 				iswhiteout = 1;
19467575Spendry 			} else if (lowerdvp != NULLVP) {
19567575Spendry 				lerror = VOP_GETATTR(upperdvp, &va,
19667575Spendry 					cnp->cn_cred, cnp->cn_proc);
19767575Spendry 				if (lerror == 0 && (va.va_flags & OPAQUE))
19867575Spendry 					iswhiteout = 1;
19967575Spendry 			}
20067575Spendry 		}
20165935Spendry 	} else {
20265935Spendry 		uerror = ENOENT;
20365935Spendry 	}
20465935Spendry 
20565935Spendry 	/*
20665935Spendry 	 * in a similar way to the upper layer, do the lookup
20765935Spendry 	 * in the lower layer.   this time, if there is some
20865935Spendry 	 * component magic going on, then vput whatever we got
20965935Spendry 	 * back from the upper layer and return the lower vnode
21065935Spendry 	 * instead.
21165935Spendry 	 */
21267575Spendry 	if (lowerdvp != NULLVP && !iswhiteout) {
21366051Spendry 		int nameiop;
21466051Spendry 
215*69447Smckusick 		vn_lock(lowerdvp, LK_EXCLUSIVE | LK_RETRY, p);
21666051Spendry 
21766051Spendry 		/*
21866051Spendry 		 * Only do a LOOKUP on the bottom node, since
21966051Spendry 		 * we won't be making changes to it anyway.
22066051Spendry 		 */
22166051Spendry 		nameiop = cnp->cn_nameiop;
22266051Spendry 		cnp->cn_nameiop = LOOKUP;
22366152Spendry 		if (um->um_op == UNMNT_BELOW) {
22466152Spendry 			saved_cred = cnp->cn_cred;
22566152Spendry 			cnp->cn_cred = um->um_cred;
22666152Spendry 		}
22767064Spendry 		lerror = union_lookup1(um->um_lowervp, &lowerdvp,
22866051Spendry 				&lowervp, cnp);
22966152Spendry 		if (um->um_op == UNMNT_BELOW)
23066152Spendry 			cnp->cn_cred = saved_cred;
23166051Spendry 		cnp->cn_nameiop = nameiop;
23266051Spendry 
23365989Spendry 		if (lowervp != lowerdvp)
234*69447Smckusick 			VOP_UNLOCK(lowerdvp, 0, p);
23565965Spendry 
23665935Spendry 		if (cnp->cn_consume != 0) {
23767076Spendry 			if (uppervp != NULLVP) {
23866051Spendry 				if (uppervp == upperdvp)
23966051Spendry 					vrele(uppervp);
24066051Spendry 				else
24166051Spendry 					vput(uppervp);
24265997Spendry 				uppervp = NULLVP;
24365935Spendry 			}
24465935Spendry 			*ap->a_vpp = lowervp;
24565965Spendry 			if (!lockparent)
24665965Spendry 				cnp->cn_flags &= ~LOCKPARENT;
24765935Spendry 			return (lerror);
24865935Spendry 		}
24965935Spendry 	} else {
25065935Spendry 		lerror = ENOENT;
25167416Spendry 		if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) {
25267416Spendry 			lowervp = LOWERVP(dun->un_pvp);
25367416Spendry 			if (lowervp != NULLVP) {
25467416Spendry 				VREF(lowervp);
255*69447Smckusick 				vn_lock(lowervp, LK_EXCLUSIVE | LK_RETRY, p);
25667416Spendry 				lerror = 0;
25767416Spendry 			}
25867416Spendry 		}
25965935Spendry 	}
26065935Spendry 
26165965Spendry 	if (!lockparent)
26265965Spendry 		cnp->cn_flags &= ~LOCKPARENT;
26365965Spendry 
26465935Spendry 	/*
26565935Spendry 	 * at this point, we have uerror and lerror indicating
26665935Spendry 	 * possible errors with the lookups in the upper and lower
26765935Spendry 	 * layers.  additionally, uppervp and lowervp are (locked)
26865935Spendry 	 * references to existing vnodes in the upper and lower layers.
26965935Spendry 	 *
27065935Spendry 	 * there are now three cases to consider.
27165935Spendry 	 * 1. if both layers returned an error, then return whatever
27265935Spendry 	 *    error the upper layer generated.
27365935Spendry 	 *
27465935Spendry 	 * 2. if the top layer failed and the bottom layer succeeded
27565935Spendry 	 *    then two subcases occur.
27665935Spendry 	 *    a.  the bottom vnode is not a directory, in which
27765935Spendry 	 *	  case just return a new union vnode referencing
27865935Spendry 	 *	  an empty top layer and the existing bottom layer.
27965935Spendry 	 *    b.  the bottom vnode is a directory, in which case
28065935Spendry 	 *	  create a new directory in the top-level and
28165935Spendry 	 *	  continue as in case 3.
28265935Spendry 	 *
28365935Spendry 	 * 3. if the top layer succeeded then return a new union
28465935Spendry 	 *    vnode referencing whatever the new top layer and
28565935Spendry 	 *    whatever the bottom layer returned.
28665935Spendry 	 */
28765935Spendry 
28866027Spendry 	*ap->a_vpp = NULLVP;
28966027Spendry 
29065935Spendry 	/* case 1. */
29165935Spendry 	if ((uerror != 0) && (lerror != 0)) {
29265935Spendry 		return (uerror);
29365935Spendry 	}
29465935Spendry 
29565935Spendry 	/* case 2. */
29665935Spendry 	if (uerror != 0 /* && (lerror == 0) */ ) {
29765935Spendry 		if (lowervp->v_type == VDIR) { /* case 2b. */
29866051Spendry 			dun->un_flags &= ~UN_ULOCK;
299*69447Smckusick 			VOP_UNLOCK(upperdvp, 0, p);
30065997Spendry 			uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
301*69447Smckusick 			vn_lock(upperdvp, LK_EXCLUSIVE | LK_RETRY, p);
30266051Spendry 			dun->un_flags |= UN_ULOCK;
30366051Spendry 
30465935Spendry 			if (uerror) {
30567076Spendry 				if (lowervp != NULLVP) {
30665935Spendry 					vput(lowervp);
30765997Spendry 					lowervp = NULLVP;
30865935Spendry 				}
30965935Spendry 				return (uerror);
31065935Spendry 			}
31165935Spendry 		}
31265935Spendry 	}
31365935Spendry 
31467076Spendry 	if (lowervp != NULLVP)
315*69447Smckusick 		VOP_UNLOCK(lowervp, 0, p);
31665965Spendry 
31765997Spendry 	error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
31868078Spendry 			      uppervp, lowervp, 1);
31965997Spendry 
32065965Spendry 	if (error) {
32167076Spendry 		if (uppervp != NULLVP)
32266051Spendry 			vput(uppervp);
32367076Spendry 		if (lowervp != NULLVP)
32465965Spendry 			vrele(lowervp);
32565965Spendry 	} else {
32665994Spendry 		if (*ap->a_vpp != dvp)
32765994Spendry 			if (!lockparent || !(cnp->cn_flags & ISLASTCN))
328*69447Smckusick 				VOP_UNLOCK(dvp, 0, p);
32965965Spendry 	}
33065965Spendry 
33165965Spendry 	return (error);
33265935Spendry }
33365935Spendry 
33465963Spendry int
33565963Spendry union_create(ap)
33665963Spendry 	struct vop_create_args /* {
33765963Spendry 		struct vnode *a_dvp;
33865963Spendry 		struct vnode **a_vpp;
33965963Spendry 		struct componentname *a_cnp;
34065963Spendry 		struct vattr *a_vap;
34165963Spendry 	} */ *ap;
34265963Spendry {
34365963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
34465963Spendry 	struct vnode *dvp = un->un_uppervp;
345*69447Smckusick 	struct componentname *cnp = ap->a_cnp;
346*69447Smckusick 	struct proc *p = cnp->cn_proc;
34765963Spendry 
34867076Spendry 	if (dvp != NULLVP) {
34965963Spendry 		int error;
35065963Spendry 		struct vnode *vp;
35168078Spendry 		struct mount *mp;
35265963Spendry 
353*69447Smckusick 		FIXUP(un, p);
35466152Spendry 
35565963Spendry 		VREF(dvp);
35666051Spendry 		un->un_flags |= UN_KLOCK;
35768078Spendry 		mp = ap->a_dvp->v_mount;
35865963Spendry 		vput(ap->a_dvp);
359*69447Smckusick 		error = VOP_CREATE(dvp, &vp, cnp, ap->a_vap);
36065963Spendry 		if (error)
36165963Spendry 			return (error);
36265963Spendry 
363*69447Smckusick 		error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP, cnp, vp,
364*69447Smckusick 				NULLVP, 1);
36565965Spendry 		if (error)
36666051Spendry 			vput(vp);
36765963Spendry 		return (error);
36865963Spendry 	}
36965963Spendry 
37065963Spendry 	vput(ap->a_dvp);
37165963Spendry 	return (EROFS);
37265963Spendry }
37365963Spendry 
37465963Spendry int
37567575Spendry union_whiteout(ap)
37667575Spendry 	struct vop_whiteout_args /* {
37767575Spendry 		struct vnode *a_dvp;
37867575Spendry 		struct componentname *a_cnp;
37967575Spendry 		int a_flags;
38067575Spendry 	} */ *ap;
38167575Spendry {
38267575Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
383*69447Smckusick 	struct componentname *cnp = ap->a_cnp;
384*69447Smckusick 	struct proc *p = cnp->cn_proc;
38567575Spendry 
38667575Spendry 	if (un->un_uppervp == NULLVP)
38767575Spendry 		return (EOPNOTSUPP);
38867575Spendry 
389*69447Smckusick 	FIXUP(un, p);
390*69447Smckusick 	return (VOP_WHITEOUT(un->un_uppervp, cnp, ap->a_flags));
39167575Spendry }
39267575Spendry 
39367575Spendry int
39465963Spendry union_mknod(ap)
39565963Spendry 	struct vop_mknod_args /* {
39665963Spendry 		struct vnode *a_dvp;
39765963Spendry 		struct vnode **a_vpp;
39865963Spendry 		struct componentname *a_cnp;
39965963Spendry 		struct vattr *a_vap;
40065963Spendry 	} */ *ap;
40165963Spendry {
40265963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
40365963Spendry 	struct vnode *dvp = un->un_uppervp;
404*69447Smckusick 	struct componentname *cnp = ap->a_cnp;
405*69447Smckusick 	struct proc *p = cnp->cn_proc;
40665963Spendry 
40767076Spendry 	if (dvp != NULLVP) {
40865963Spendry 		int error;
40965963Spendry 		struct vnode *vp;
41068078Spendry 		struct mount *mp;
41165963Spendry 
412*69447Smckusick 		FIXUP(un, p);
41366152Spendry 
41465963Spendry 		VREF(dvp);
41566051Spendry 		un->un_flags |= UN_KLOCK;
41668078Spendry 		mp = ap->a_dvp->v_mount;
41765963Spendry 		vput(ap->a_dvp);
418*69447Smckusick 		error = VOP_MKNOD(dvp, &vp, cnp, ap->a_vap);
41965963Spendry 		if (error)
42065963Spendry 			return (error);
42165963Spendry 
42267076Spendry 		if (vp != NULLVP) {
423*69447Smckusick 			error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP,
424*69447Smckusick 					cnp, vp, NULLVP, 1);
42565965Spendry 			if (error)
42666051Spendry 				vput(vp);
42765965Spendry 		}
42865963Spendry 		return (error);
42965963Spendry 	}
43065963Spendry 
43165963Spendry 	vput(ap->a_dvp);
43265963Spendry 	return (EROFS);
43365963Spendry }
43465963Spendry 
43565935Spendry int
43665935Spendry union_open(ap)
43765935Spendry 	struct vop_open_args /* {
43865935Spendry 		struct vnodeop_desc *a_desc;
43965935Spendry 		struct vnode *a_vp;
44065935Spendry 		int a_mode;
44165935Spendry 		struct ucred *a_cred;
44265935Spendry 		struct proc *a_p;
44365935Spendry 	} */ *ap;
44465935Spendry {
44565935Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
44665965Spendry 	struct vnode *tvp;
44765935Spendry 	int mode = ap->a_mode;
44865935Spendry 	struct ucred *cred = ap->a_cred;
44965935Spendry 	struct proc *p = ap->a_p;
45065965Spendry 	int error;
45165935Spendry 
45265935Spendry 	/*
45365935Spendry 	 * If there is an existing upper vp then simply open that.
45465935Spendry 	 */
45565965Spendry 	tvp = un->un_uppervp;
45665965Spendry 	if (tvp == NULLVP) {
45765935Spendry 		/*
45865965Spendry 		 * If the lower vnode is being opened for writing, then
45965965Spendry 		 * copy the file contents to the upper vnode and open that,
46065965Spendry 		 * otherwise can simply open the lower vnode.
46165935Spendry 		 */
46265965Spendry 		tvp = un->un_lowervp;
46365965Spendry 		if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
46467169Spendry 			error = union_copyup(un, (mode&O_TRUNC) == 0, cred, p);
46565965Spendry 			if (error == 0)
46665965Spendry 				error = VOP_OPEN(un->un_uppervp, mode, cred, p);
46765965Spendry 			return (error);
46865935Spendry 		}
46966051Spendry 
47066051Spendry 		/*
47166051Spendry 		 * Just open the lower vnode
47266051Spendry 		 */
47366027Spendry 		un->un_openl++;
474*69447Smckusick 		vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p);
47566051Spendry 		error = VOP_OPEN(tvp, mode, cred, p);
476*69447Smckusick 		VOP_UNLOCK(tvp, 0, p);
47766051Spendry 
47866051Spendry 		return (error);
47965935Spendry 	}
48065935Spendry 
481*69447Smckusick 	FIXUP(un, p);
48266152Spendry 
48365965Spendry 	error = VOP_OPEN(tvp, mode, cred, p);
48465965Spendry 
48565965Spendry 	return (error);
48665935Spendry }
48765935Spendry 
48865963Spendry int
48965963Spendry union_close(ap)
49065963Spendry 	struct vop_close_args /* {
49165963Spendry 		struct vnode *a_vp;
49265963Spendry 		int  a_fflag;
49365963Spendry 		struct ucred *a_cred;
49465963Spendry 		struct proc *a_p;
49565963Spendry 	} */ *ap;
49665963Spendry {
49765997Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
49865997Spendry 	struct vnode *vp;
49965963Spendry 
50067076Spendry 	if (un->un_uppervp != NULLVP) {
50165997Spendry 		vp = un->un_uppervp;
50265997Spendry 	} else {
50366027Spendry #ifdef UNION_DIAGNOSTIC
50466027Spendry 		if (un->un_openl <= 0)
50566027Spendry 			panic("union: un_openl cnt");
50665997Spendry #endif
50766027Spendry 		--un->un_openl;
50865997Spendry 		vp = un->un_lowervp;
50965997Spendry 	}
51066027Spendry 
51165997Spendry 	return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p));
51265963Spendry }
51365963Spendry 
51465935Spendry /*
51565963Spendry  * Check access permission on the union vnode.
51665963Spendry  * The access check being enforced is to check
51765963Spendry  * against both the underlying vnode, and any
51865963Spendry  * copied vnode.  This ensures that no additional
51965963Spendry  * file permissions are given away simply because
52065963Spendry  * the user caused an implicit file copy.
52165963Spendry  */
52265963Spendry int
52365963Spendry union_access(ap)
52465963Spendry 	struct vop_access_args /* {
52565963Spendry 		struct vnodeop_desc *a_desc;
52665963Spendry 		struct vnode *a_vp;
52765963Spendry 		int a_mode;
52865963Spendry 		struct ucred *a_cred;
52965963Spendry 		struct proc *a_p;
53065963Spendry 	} */ *ap;
53165963Spendry {
53265963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
533*69447Smckusick 	struct proc *p = ap->a_p;
53466149Spendry 	int error = EACCES;
53565963Spendry 	struct vnode *vp;
53665963Spendry 
53767076Spendry 	if ((vp = un->un_uppervp) != NULLVP) {
538*69447Smckusick 		FIXUP(un, p);
539*69447Smckusick 		return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, p));
54066152Spendry 	}
54166152Spendry 
54267076Spendry 	if ((vp = un->un_lowervp) != NULLVP) {
543*69447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
544*69447Smckusick 		error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, p);
54566152Spendry 		if (error == 0) {
54666152Spendry 			struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
54766152Spendry 
54866152Spendry 			if (um->um_op == UNMNT_BELOW)
54966152Spendry 				error = VOP_ACCESS(vp, ap->a_mode,
550*69447Smckusick 						um->um_cred, p);
55166152Spendry 		}
552*69447Smckusick 		VOP_UNLOCK(vp, 0, p);
55365963Spendry 		if (error)
55465963Spendry 			return (error);
55565963Spendry 	}
55665963Spendry 
55765965Spendry 	return (error);
55865963Spendry }
55965963Spendry 
56065963Spendry /*
56167109Spendry  * We handle getattr only to change the fsid and
56267109Spendry  * track object sizes
56365935Spendry  */
56465935Spendry int
56565935Spendry union_getattr(ap)
56665935Spendry 	struct vop_getattr_args /* {
56765935Spendry 		struct vnode *a_vp;
56865935Spendry 		struct vattr *a_vap;
56965935Spendry 		struct ucred *a_cred;
57065935Spendry 		struct proc *a_p;
57165935Spendry 	} */ *ap;
57265935Spendry {
57365935Spendry 	int error;
57466062Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
57566062Spendry 	struct vnode *vp = un->un_uppervp;
576*69447Smckusick 	struct proc *p = ap->a_p;
57766062Spendry 	struct vattr *vap;
57866062Spendry 	struct vattr va;
57965935Spendry 
58066062Spendry 
58166062Spendry 	/*
58266062Spendry 	 * Some programs walk the filesystem hierarchy by counting
58366062Spendry 	 * links to directories to avoid stat'ing all the time.
58466062Spendry 	 * This means the link count on directories needs to be "correct".
58566062Spendry 	 * The only way to do that is to call getattr on both layers
58666062Spendry 	 * and fix up the link count.  The link count will not necessarily
58766062Spendry 	 * be accurate but will be large enough to defeat the tree walkers.
58866062Spendry 	 */
58966062Spendry 
59066062Spendry 	vap = ap->a_vap;
59166062Spendry 
59266062Spendry 	vp = un->un_uppervp;
59366062Spendry 	if (vp != NULLVP) {
59467073Spendry 		/*
59567073Spendry 		 * It's not clear whether VOP_GETATTR is to be
59667073Spendry 		 * called with the vnode locked or not.  stat() calls
59767073Spendry 		 * it with (vp) locked, and fstat calls it with
59867073Spendry 		 * (vp) unlocked.
59967073Spendry 		 * In the mean time, compensate here by checking
60067073Spendry 		 * the union_node's lock flag.
60167073Spendry 		 */
60267073Spendry 		if (un->un_flags & UN_LOCKED)
603*69447Smckusick 			FIXUP(un, p);
60467073Spendry 
60566062Spendry 		error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
60666062Spendry 		if (error)
60766062Spendry 			return (error);
60867109Spendry 		union_newsize(ap->a_vp, vap->va_size, VNOVAL);
60966062Spendry 	}
61066062Spendry 
61166062Spendry 	if (vp == NULLVP) {
61266062Spendry 		vp = un->un_lowervp;
61366062Spendry 	} else if (vp->v_type == VDIR) {
61466062Spendry 		vp = un->un_lowervp;
61566062Spendry 		vap = &va;
61666062Spendry 	} else {
61766062Spendry 		vp = NULLVP;
61866062Spendry 	}
61966062Spendry 
62066062Spendry 	if (vp != NULLVP) {
62166062Spendry 		error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
62266062Spendry 		if (error)
62366062Spendry 			return (error);
62467109Spendry 		union_newsize(ap->a_vp, VNOVAL, vap->va_size);
62566062Spendry 	}
62665965Spendry 
62766062Spendry 	if ((vap != ap->a_vap) && (vap->va_type == VDIR))
62866062Spendry 		ap->a_vap->va_nlink += vap->va_nlink;
62966062Spendry 
63067400Spendry 	ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
63165935Spendry 	return (0);
63265935Spendry }
63365935Spendry 
63465963Spendry int
63565965Spendry union_setattr(ap)
63665963Spendry 	struct vop_setattr_args /* {
63765963Spendry 		struct vnode *a_vp;
63865963Spendry 		struct vattr *a_vap;
63965963Spendry 		struct ucred *a_cred;
64065963Spendry 		struct proc *a_p;
64165963Spendry 	} */ *ap;
64265963Spendry {
64365963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
644*69447Smckusick 	struct proc *p = ap->a_p;
64565963Spendry 	int error;
64665963Spendry 
64766057Spendry 	/*
64866057Spendry 	 * Handle case of truncating lower object to zero size,
64966057Spendry 	 * by creating a zero length upper object.  This is to
65066057Spendry 	 * handle the case of open with O_TRUNC and O_CREAT.
65166057Spendry 	 */
65266057Spendry 	if ((un->un_uppervp == NULLVP) &&
65366057Spendry 	    /* assert(un->un_lowervp != NULLVP) */
65467575Spendry 	    (un->un_lowervp->v_type == VREG)) {
65567575Spendry 		error = union_copyup(un, (ap->a_vap->va_size != 0),
65667575Spendry 						ap->a_cred, ap->a_p);
65766057Spendry 		if (error)
65866057Spendry 			return (error);
65966057Spendry 	}
66066057Spendry 
66166057Spendry 	/*
66266057Spendry 	 * Try to set attributes in upper layer,
66366057Spendry 	 * otherwise return read-only filesystem error.
66466057Spendry 	 */
66566057Spendry 	if (un->un_uppervp != NULLVP) {
666*69447Smckusick 		FIXUP(un, p);
66765963Spendry 		error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
66865963Spendry 					ap->a_cred, ap->a_p);
66967109Spendry 		if ((error == 0) && (ap->a_vap->va_size != VNOVAL))
67067109Spendry 			union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL);
67165963Spendry 	} else {
67265963Spendry 		error = EROFS;
67365963Spendry 	}
67465963Spendry 
67565963Spendry 	return (error);
67665963Spendry }
67765963Spendry 
67865963Spendry int
67965963Spendry union_read(ap)
68065963Spendry 	struct vop_read_args /* {
68165963Spendry 		struct vnode *a_vp;
68265963Spendry 		struct uio *a_uio;
68365963Spendry 		int  a_ioflag;
68465963Spendry 		struct ucred *a_cred;
68565963Spendry 	} */ *ap;
68665963Spendry {
68765963Spendry 	int error;
688*69447Smckusick 	struct proc *p = ap->a_uio->uio_procp;
68965963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
69066051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
69165963Spendry 
69266051Spendry 	if (dolock)
693*69447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
69466152Spendry 	else
695*69447Smckusick 		FIXUP(VTOUNION(ap->a_vp), p);
69665963Spendry 	error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
69766051Spendry 	if (dolock)
698*69447Smckusick 		VOP_UNLOCK(vp, 0, p);
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);
734*69447Smckusick 	struct proc *p = ap->a_uio->uio_procp;
73565963Spendry 
73667575Spendry 	vp = UPPERVP(ap->a_vp);
73767575Spendry 	if (vp == NULLVP)
73867575Spendry 		panic("union: missing upper layer in write");
73967575Spendry 
740*69447Smckusick 	FIXUP(un, p);
74165963Spendry 	error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
74265963Spendry 
74367109Spendry 	/*
74467109Spendry 	 * the size of the underlying object may be changed by the
74567109Spendry 	 * write.
74667109Spendry 	 */
74767109Spendry 	if (error == 0) {
74867109Spendry 		off_t cur = ap->a_uio->uio_offset;
74967109Spendry 
75067575Spendry 		if (cur > un->un_uppersz)
75167575Spendry 			union_newsize(ap->a_vp, cur, VNOVAL);
75267109Spendry 	}
75367109Spendry 
75465963Spendry 	return (error);
75565963Spendry }
75665963Spendry 
75767751Spendry union_lease(ap)
75867751Spendry 	struct vop_lease_args /* {
75967751Spendry 		struct vnode *a_vp;
76067751Spendry 		struct proc *a_p;
76167751Spendry 		struct ucred *a_cred;
76267751Spendry 		int a_flag;
76367751Spendry 	} */ *ap;
76467751Spendry {
76567751Spendry 
76667751Spendry 	return (VOP_LEASE(OTHERVP(ap->a_vp), ap->a_p, ap->a_cred, ap->a_flag));
76767751Spendry }
76867751Spendry 
76965963Spendry int
77065963Spendry union_ioctl(ap)
77165963Spendry 	struct vop_ioctl_args /* {
77265963Spendry 		struct vnode *a_vp;
77365963Spendry 		int  a_command;
77465963Spendry 		caddr_t  a_data;
77565963Spendry 		int  a_fflag;
77665963Spendry 		struct ucred *a_cred;
77765963Spendry 		struct proc *a_p;
77865963Spendry 	} */ *ap;
77965963Spendry {
78065963Spendry 
78165963Spendry 	return (VOP_IOCTL(OTHERVP(ap->a_vp), ap->a_command, ap->a_data,
78265963Spendry 				ap->a_fflag, ap->a_cred, ap->a_p));
78365963Spendry }
78465963Spendry 
78565963Spendry int
78665963Spendry union_select(ap)
78765963Spendry 	struct vop_select_args /* {
78865963Spendry 		struct vnode *a_vp;
78965963Spendry 		int  a_which;
79065963Spendry 		int  a_fflags;
79165963Spendry 		struct ucred *a_cred;
79265963Spendry 		struct proc *a_p;
79365963Spendry 	} */ *ap;
79465963Spendry {
79565963Spendry 
79665963Spendry 	return (VOP_SELECT(OTHERVP(ap->a_vp), ap->a_which, ap->a_fflags,
79765963Spendry 				ap->a_cred, ap->a_p));
79865963Spendry }
79965963Spendry 
80065963Spendry int
80169389Spendry union_revoke(ap)
80269389Spendry 	struct vop_revoke_args /* {
80369389Spendry 		struct vnode *a_vp;
80469389Spendry 		int a_flags;
805*69447Smckusick 		struct proc *a_p;
80669389Spendry 	} */ *ap;
80769389Spendry {
80869389Spendry 	struct vnode *vp = ap->a_vp;
80969389Spendry 
81069389Spendry 	if (UPPERVP(vp))
81169389Spendry 		VOP_REVOKE(UPPERVP(vp), ap->a_flags);
81269389Spendry 	if (LOWERVP(vp))
813*69447Smckusick 		VOP_REVOKE(LOWERVP(vp), ap->a_flags);
81469389Spendry 	vgone(vp);
81569389Spendry }
81669389Spendry 
81769389Spendry int
81865963Spendry union_mmap(ap)
81965963Spendry 	struct vop_mmap_args /* {
82065963Spendry 		struct vnode *a_vp;
82165963Spendry 		int  a_fflags;
82265963Spendry 		struct ucred *a_cred;
82365963Spendry 		struct proc *a_p;
82465963Spendry 	} */ *ap;
82565963Spendry {
82665963Spendry 
82765963Spendry 	return (VOP_MMAP(OTHERVP(ap->a_vp), ap->a_fflags,
82865963Spendry 				ap->a_cred, ap->a_p));
82965963Spendry }
83065963Spendry 
83165963Spendry int
83265963Spendry union_fsync(ap)
83365963Spendry 	struct vop_fsync_args /* {
83465963Spendry 		struct vnode *a_vp;
83565963Spendry 		struct ucred *a_cred;
83665963Spendry 		int  a_waitfor;
83765963Spendry 		struct proc *a_p;
83865963Spendry 	} */ *ap;
83965963Spendry {
84065963Spendry 	int error = 0;
841*69447Smckusick 	struct proc *p = ap->a_p;
84265963Spendry 	struct vnode *targetvp = OTHERVP(ap->a_vp);
84365963Spendry 
84467076Spendry 	if (targetvp != NULLVP) {
84566051Spendry 		int dolock = (targetvp == LOWERVP(ap->a_vp));
84666051Spendry 
84766051Spendry 		if (dolock)
848*69447Smckusick 			vn_lock(targetvp, LK_EXCLUSIVE | LK_RETRY, p);
84966152Spendry 		else
850*69447Smckusick 			FIXUP(VTOUNION(ap->a_vp), p);
851*69447Smckusick 		error = VOP_FSYNC(targetvp, ap->a_cred, ap->a_waitfor, p);
85266051Spendry 		if (dolock)
853*69447Smckusick 			VOP_UNLOCK(targetvp, 0, p);
85465963Spendry 	}
85565963Spendry 
85665963Spendry 	return (error);
85765963Spendry }
85865963Spendry 
85965963Spendry int
86065963Spendry union_seek(ap)
86165963Spendry 	struct vop_seek_args /* {
86265963Spendry 		struct vnode *a_vp;
86365963Spendry 		off_t  a_oldoff;
86465963Spendry 		off_t  a_newoff;
86565963Spendry 		struct ucred *a_cred;
86665963Spendry 	} */ *ap;
86765963Spendry {
86865963Spendry 
86965963Spendry 	return (VOP_SEEK(OTHERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred));
87065963Spendry }
87165963Spendry 
87265963Spendry int
87365963Spendry union_remove(ap)
87465963Spendry 	struct vop_remove_args /* {
87565963Spendry 		struct vnode *a_dvp;
87665963Spendry 		struct vnode *a_vp;
87765963Spendry 		struct componentname *a_cnp;
87865963Spendry 	} */ *ap;
87965963Spendry {
88065963Spendry 	int error;
88165963Spendry 	struct union_node *dun = VTOUNION(ap->a_dvp);
88265963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
883*69447Smckusick 	struct componentname *cnp = ap->a_cnp;
884*69447Smckusick 	struct proc *p = cnp->cn_proc;
88565963Spendry 
88667575Spendry 	if (dun->un_uppervp == NULLVP)
88767575Spendry 		panic("union remove: null upper vnode");
88867575Spendry 
88967575Spendry 	if (un->un_uppervp != NULLVP) {
89065963Spendry 		struct vnode *dvp = dun->un_uppervp;
89165963Spendry 		struct vnode *vp = un->un_uppervp;
89265963Spendry 
893*69447Smckusick 		FIXUP(dun, p);
89465963Spendry 		VREF(dvp);
89566051Spendry 		dun->un_flags |= UN_KLOCK;
89665963Spendry 		vput(ap->a_dvp);
897*69447Smckusick 		FIXUP(un, p);
89865963Spendry 		VREF(vp);
89966051Spendry 		un->un_flags |= UN_KLOCK;
90065963Spendry 		vput(ap->a_vp);
90165963Spendry 
90267784Spendry 		if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
90367784Spendry 			cnp->cn_flags |= DOWHITEOUT;
90467784Spendry 		error = VOP_REMOVE(dvp, vp, cnp);
90566027Spendry 		if (!error)
90666027Spendry 			union_removed_upper(un);
90765963Spendry 	} else {
908*69447Smckusick 		FIXUP(dun, p);
90967575Spendry 		error = union_mkwhiteout(
91067575Spendry 			MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
91167575Spendry 			dun->un_uppervp, ap->a_cnp, un->un_path);
91265963Spendry 		vput(ap->a_dvp);
91365963Spendry 		vput(ap->a_vp);
91465963Spendry 	}
91565963Spendry 
91665963Spendry 	return (error);
91765963Spendry }
91865963Spendry 
91965963Spendry int
92065963Spendry union_link(ap)
92165963Spendry 	struct vop_link_args /* {
92265963Spendry 		struct vnode *a_vp;
92365963Spendry 		struct vnode *a_tdvp;
92465963Spendry 		struct componentname *a_cnp;
92565963Spendry 	} */ *ap;
92665963Spendry {
92767169Spendry 	int error = 0;
928*69447Smckusick 	struct componentname *cnp = ap->a_cnp;
929*69447Smckusick 	struct proc *p = cnp->cn_proc;
93067169Spendry 	struct union_node *un;
93167169Spendry 	struct vnode *vp;
93267169Spendry 	struct vnode *tdvp;
93365963Spendry 
93468535Smckusick 	un = VTOUNION(ap->a_tdvp);
93565963Spendry 
93668535Smckusick 	if (ap->a_tdvp->v_op != ap->a_vp->v_op) {
93768535Smckusick 		vp = ap->a_vp;
93867169Spendry 	} else {
93968535Smckusick 		struct union_node *tun = VTOUNION(ap->a_vp);
94068535Smckusick 		if (tun->un_uppervp == NULLVP) {
941*69447Smckusick 			vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p);
94268535Smckusick 			if (un->un_uppervp == tun->un_dirvp) {
94367169Spendry 				un->un_flags &= ~UN_ULOCK;
944*69447Smckusick 				VOP_UNLOCK(un->un_uppervp, 0, p);
94567169Spendry 			}
946*69447Smckusick 			error = union_copyup(tun, 1, cnp->cn_cred, p);
94768535Smckusick 			if (un->un_uppervp == tun->un_dirvp) {
948*69447Smckusick 				vn_lock(un->un_uppervp,
949*69447Smckusick 						LK_EXCLUSIVE | LK_RETRY, p);
95067169Spendry 				un->un_flags |= UN_ULOCK;
95167169Spendry 			}
952*69447Smckusick 			VOP_UNLOCK(ap->a_vp, 0, p);
95367169Spendry 		}
95468535Smckusick 		vp = tun->un_uppervp;
95567169Spendry 	}
95665963Spendry 
95768535Smckusick 	tdvp = un->un_uppervp;
95868535Smckusick 	if (tdvp == NULLVP)
95967169Spendry 		error = EROFS;
96067169Spendry 
96167169Spendry 	if (error) {
96268535Smckusick 		vput(ap->a_tdvp);
96367169Spendry 		return (error);
96465963Spendry 	}
96565963Spendry 
966*69447Smckusick 	FIXUP(un, p);
96768535Smckusick 	VREF(tdvp);
96867169Spendry 	un->un_flags |= UN_KLOCK;
96968535Smckusick 	vput(ap->a_tdvp);
97067169Spendry 
971*69447Smckusick 	return (VOP_LINK(vp, tdvp, cnp));
97265963Spendry }
97365963Spendry 
97465963Spendry int
97565963Spendry union_rename(ap)
97665963Spendry 	struct vop_rename_args  /* {
97765963Spendry 		struct vnode *a_fdvp;
97865963Spendry 		struct vnode *a_fvp;
97965963Spendry 		struct componentname *a_fcnp;
98065963Spendry 		struct vnode *a_tdvp;
98165963Spendry 		struct vnode *a_tvp;
98265963Spendry 		struct componentname *a_tcnp;
98365963Spendry 	} */ *ap;
98465963Spendry {
98565963Spendry 	int error;
98665963Spendry 
98765963Spendry 	struct vnode *fdvp = ap->a_fdvp;
98865963Spendry 	struct vnode *fvp = ap->a_fvp;
98965963Spendry 	struct vnode *tdvp = ap->a_tdvp;
99065963Spendry 	struct vnode *tvp = ap->a_tvp;
99165963Spendry 
99265963Spendry 	if (fdvp->v_op == union_vnodeop_p) {	/* always true */
99365963Spendry 		struct union_node *un = VTOUNION(fdvp);
99465997Spendry 		if (un->un_uppervp == NULLVP) {
99567575Spendry 			/*
99667575Spendry 			 * this should never happen in normal
99767575Spendry 			 * operation but might if there was
99867575Spendry 			 * a problem creating the top-level shadow
99967575Spendry 			 * directory.
100067575Spendry 			 */
100167575Spendry 			error = EXDEV;
100265963Spendry 			goto bad;
100365963Spendry 		}
100465963Spendry 
100565963Spendry 		fdvp = un->un_uppervp;
100665963Spendry 		VREF(fdvp);
100765963Spendry 		vrele(ap->a_fdvp);
100865963Spendry 	}
100965963Spendry 
101065963Spendry 	if (fvp->v_op == union_vnodeop_p) {	/* always true */
101165963Spendry 		struct union_node *un = VTOUNION(fvp);
101265997Spendry 		if (un->un_uppervp == NULLVP) {
101367575Spendry 			/* XXX: should do a copyup */
101467575Spendry 			error = EXDEV;
101565963Spendry 			goto bad;
101665963Spendry 		}
101765963Spendry 
101867575Spendry 		if (un->un_lowervp != NULLVP)
101967575Spendry 			ap->a_fcnp->cn_flags |= DOWHITEOUT;
102067575Spendry 
102165963Spendry 		fvp = un->un_uppervp;
102265963Spendry 		VREF(fvp);
102365963Spendry 		vrele(ap->a_fvp);
102465963Spendry 	}
102565963Spendry 
102665963Spendry 	if (tdvp->v_op == union_vnodeop_p) {
102765963Spendry 		struct union_node *un = VTOUNION(tdvp);
102865997Spendry 		if (un->un_uppervp == NULLVP) {
102967076Spendry 			/*
103067076Spendry 			 * this should never happen in normal
103167076Spendry 			 * operation but might if there was
103267076Spendry 			 * a problem creating the top-level shadow
103367076Spendry 			 * directory.
103467076Spendry 			 */
103567575Spendry 			error = EXDEV;
103665963Spendry 			goto bad;
103765963Spendry 		}
103865963Spendry 
103965963Spendry 		tdvp = un->un_uppervp;
104065963Spendry 		VREF(tdvp);
104166051Spendry 		un->un_flags |= UN_KLOCK;
104265997Spendry 		vput(ap->a_tdvp);
104365963Spendry 	}
104465963Spendry 
104567076Spendry 	if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) {
104665963Spendry 		struct union_node *un = VTOUNION(tvp);
104765963Spendry 
104865963Spendry 		tvp = un->un_uppervp;
104967076Spendry 		if (tvp != NULLVP) {
105067076Spendry 			VREF(tvp);
105167076Spendry 			un->un_flags |= UN_KLOCK;
105267076Spendry 		}
105365963Spendry 		vput(ap->a_tvp);
105465963Spendry 	}
105565963Spendry 
105665963Spendry 	return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));
105765963Spendry 
105865963Spendry bad:
105965963Spendry 	vrele(fdvp);
106065963Spendry 	vrele(fvp);
106165963Spendry 	vput(tdvp);
106267076Spendry 	if (tvp != NULLVP)
106365963Spendry 		vput(tvp);
106465963Spendry 
106565963Spendry 	return (error);
106665963Spendry }
106765963Spendry 
106865963Spendry int
106965963Spendry union_mkdir(ap)
107065963Spendry 	struct vop_mkdir_args /* {
107165963Spendry 		struct vnode *a_dvp;
107265963Spendry 		struct vnode **a_vpp;
107365963Spendry 		struct componentname *a_cnp;
107465963Spendry 		struct vattr *a_vap;
107565963Spendry 	} */ *ap;
107665963Spendry {
107765963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
107865963Spendry 	struct vnode *dvp = un->un_uppervp;
1079*69447Smckusick 	struct componentname *cnp = ap->a_cnp;
1080*69447Smckusick 	struct proc *p = cnp->cn_proc;
108165963Spendry 
108267076Spendry 	if (dvp != NULLVP) {
108365963Spendry 		int error;
108465963Spendry 		struct vnode *vp;
108565963Spendry 
1086*69447Smckusick 		FIXUP(un, p);
108765963Spendry 		VREF(dvp);
108866051Spendry 		un->un_flags |= UN_KLOCK;
1089*69447Smckusick 		VOP_UNLOCK(ap->a_dvp, 0, p);
1090*69447Smckusick 		error = VOP_MKDIR(dvp, &vp, cnp, ap->a_vap);
109168078Spendry 		if (error) {
109268078Spendry 			vrele(ap->a_dvp);
109365963Spendry 			return (error);
109468078Spendry 		}
109565963Spendry 
1096*69447Smckusick 		error = union_allocvp(ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp,
1097*69447Smckusick 				NULLVP, cnp, vp, NULLVP, 1);
109868078Spendry 		vrele(ap->a_dvp);
109965965Spendry 		if (error)
110066051Spendry 			vput(vp);
110165963Spendry 		return (error);
110265963Spendry 	}
110365963Spendry 
110465963Spendry 	vput(ap->a_dvp);
110565963Spendry 	return (EROFS);
110665963Spendry }
110765963Spendry 
110865963Spendry int
110965963Spendry union_rmdir(ap)
111065963Spendry 	struct vop_rmdir_args /* {
111165963Spendry 		struct vnode *a_dvp;
111265963Spendry 		struct vnode *a_vp;
111365963Spendry 		struct componentname *a_cnp;
111465963Spendry 	} */ *ap;
111565963Spendry {
111665963Spendry 	int error;
111765963Spendry 	struct union_node *dun = VTOUNION(ap->a_dvp);
111865963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
1119*69447Smckusick 	struct componentname *cnp = ap->a_cnp;
1120*69447Smckusick 	struct proc *p = cnp->cn_proc;
112165963Spendry 
112267575Spendry 	if (dun->un_uppervp == NULLVP)
112367575Spendry 		panic("union rmdir: null upper vnode");
112467575Spendry 
112567575Spendry 	if (un->un_uppervp != NULLVP) {
112665963Spendry 		struct vnode *dvp = dun->un_uppervp;
112765963Spendry 		struct vnode *vp = un->un_uppervp;
112865963Spendry 
1129*69447Smckusick 		FIXUP(dun, p);
113065963Spendry 		VREF(dvp);
113166051Spendry 		dun->un_flags |= UN_KLOCK;
113265963Spendry 		vput(ap->a_dvp);
1133*69447Smckusick 		FIXUP(un, p);
113465963Spendry 		VREF(vp);
113566051Spendry 		un->un_flags |= UN_KLOCK;
113665963Spendry 		vput(ap->a_vp);
113765963Spendry 
113867784Spendry 		if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
113967784Spendry 			cnp->cn_flags |= DOWHITEOUT;
114066051Spendry 		error = VOP_RMDIR(dvp, vp, ap->a_cnp);
114166027Spendry 		if (!error)
114266027Spendry 			union_removed_upper(un);
114365963Spendry 	} else {
1144*69447Smckusick 		FIXUP(dun, p);
114567575Spendry 		error = union_mkwhiteout(
114667575Spendry 			MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
114767575Spendry 			dun->un_uppervp, ap->a_cnp, un->un_path);
114865963Spendry 		vput(ap->a_dvp);
114965963Spendry 		vput(ap->a_vp);
115065963Spendry 	}
115165963Spendry 
115265963Spendry 	return (error);
115365963Spendry }
115465963Spendry 
115565963Spendry int
115665963Spendry union_symlink(ap)
115765963Spendry 	struct vop_symlink_args /* {
115865963Spendry 		struct vnode *a_dvp;
115965963Spendry 		struct vnode **a_vpp;
116065963Spendry 		struct componentname *a_cnp;
116165963Spendry 		struct vattr *a_vap;
116265963Spendry 		char *a_target;
116365963Spendry 	} */ *ap;
116465963Spendry {
116565963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
116665963Spendry 	struct vnode *dvp = un->un_uppervp;
1167*69447Smckusick 	struct componentname *cnp = ap->a_cnp;
1168*69447Smckusick 	struct proc *p = cnp->cn_proc;
116965963Spendry 
117067076Spendry 	if (dvp != NULLVP) {
117165963Spendry 		int error;
117265963Spendry 		struct vnode *vp;
117365963Spendry 		struct mount *mp = ap->a_dvp->v_mount;
117465963Spendry 
1175*69447Smckusick 		FIXUP(un, p);
117665963Spendry 		VREF(dvp);
117766051Spendry 		un->un_flags |= UN_KLOCK;
117865963Spendry 		vput(ap->a_dvp);
1179*69447Smckusick 		error = VOP_SYMLINK(dvp, &vp, cnp, ap->a_vap, ap->a_target);
118065997Spendry 		*ap->a_vpp = NULLVP;
118165963Spendry 		return (error);
118265963Spendry 	}
118365963Spendry 
118465963Spendry 	vput(ap->a_dvp);
118565963Spendry 	return (EROFS);
118665963Spendry }
118765963Spendry 
118865935Spendry /*
118965935Spendry  * union_readdir works in concert with getdirentries and
119065935Spendry  * readdir(3) to provide a list of entries in the unioned
119165935Spendry  * directories.  getdirentries is responsible for walking
119265935Spendry  * down the union stack.  readdir(3) is responsible for
119365935Spendry  * eliminating duplicate names from the returned data stream.
119465935Spendry  */
119565935Spendry int
119665935Spendry union_readdir(ap)
119765935Spendry 	struct vop_readdir_args /* {
119865935Spendry 		struct vnodeop_desc *a_desc;
119965935Spendry 		struct vnode *a_vp;
120065935Spendry 		struct uio *a_uio;
120165935Spendry 		struct ucred *a_cred;
120267369Smckusick 		int *a_eofflag;
120367369Smckusick 		u_long *a_cookies;
120467369Smckusick 		int a_ncookies;
120565935Spendry 	} */ *ap;
120665935Spendry {
1207*69447Smckusick 	struct union_node *un = VTOUNION(ap->a_vp);
1208*69447Smckusick 	struct vnode *uvp = un->un_uppervp;
1209*69447Smckusick 	struct proc *p = ap->a_uio->uio_procp;
121065935Spendry 
121167369Smckusick 	if (uvp == NULLVP)
121267369Smckusick 		return (0);
121365935Spendry 
1214*69447Smckusick 	FIXUP(un, p);
121567369Smckusick 	ap->a_vp = uvp;
121667369Smckusick 	return (VOCALL(uvp->v_op, VOFFSET(vop_readdir), ap));
121765935Spendry }
121865935Spendry 
121965935Spendry int
122065963Spendry union_readlink(ap)
122165963Spendry 	struct vop_readlink_args /* {
122265963Spendry 		struct vnode *a_vp;
122365963Spendry 		struct uio *a_uio;
122465963Spendry 		struct ucred *a_cred;
122565963Spendry 	} */ *ap;
122665963Spendry {
122765963Spendry 	int error;
1228*69447Smckusick 	struct uio *uio = ap->a_uio;
1229*69447Smckusick 	struct proc *p = uio->uio_procp;
123065963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
123166051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
123265963Spendry 
123366051Spendry 	if (dolock)
1234*69447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
123566152Spendry 	else
1236*69447Smckusick 		FIXUP(VTOUNION(ap->a_vp), p);
1237*69447Smckusick 	error = VOP_READLINK(vp, uio, ap->a_cred);
123866051Spendry 	if (dolock)
1239*69447Smckusick 		VOP_UNLOCK(vp, 0, p);
124065963Spendry 
124165963Spendry 	return (error);
124265963Spendry }
124365963Spendry 
124465963Spendry int
124565963Spendry union_abortop(ap)
124665963Spendry 	struct vop_abortop_args /* {
124765963Spendry 		struct vnode *a_dvp;
124865963Spendry 		struct componentname *a_cnp;
124965963Spendry 	} */ *ap;
125065963Spendry {
125165963Spendry 	int error;
1252*69447Smckusick 	struct componentname *cnp = ap->a_cnp;
1253*69447Smckusick 	struct proc *p = cnp->cn_proc;
125465965Spendry 	struct vnode *vp = OTHERVP(ap->a_dvp);
125565963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
125665963Spendry 	int islocked = un->un_flags & UN_LOCKED;
125766051Spendry 	int dolock = (vp == LOWERVP(ap->a_dvp));
125865963Spendry 
125966152Spendry 	if (islocked) {
126066152Spendry 		if (dolock)
1261*69447Smckusick 			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
126266152Spendry 		else
1263*69447Smckusick 			FIXUP(VTOUNION(ap->a_dvp), p);
126466152Spendry 	}
1265*69447Smckusick 	error = VOP_ABORTOP(vp, cnp);
126666051Spendry 	if (islocked && dolock)
1267*69447Smckusick 		VOP_UNLOCK(vp, 0, p);
126865963Spendry 
126965963Spendry 	return (error);
127065963Spendry }
127165963Spendry 
127265963Spendry int
127365935Spendry union_inactive(ap)
127465935Spendry 	struct vop_inactive_args /* {
127565935Spendry 		struct vnode *a_vp;
1276*69447Smckusick 		struct proc *a_p;
127765935Spendry 	} */ *ap;
127865935Spendry {
127967073Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
128068078Spendry 	struct vnode **vpp;
128165935Spendry 
128265935Spendry 	/*
128365935Spendry 	 * Do nothing (and _don't_ bypass).
128465935Spendry 	 * Wait to vrele lowervp until reclaim,
128565935Spendry 	 * so that until then our union_node is in the
128665935Spendry 	 * cache and reusable.
128765935Spendry 	 *
128865935Spendry 	 * NEEDSWORK: Someday, consider inactive'ing
128965935Spendry 	 * the lowervp and then trying to reactivate it
129065935Spendry 	 * with capabilities (v_id)
129165935Spendry 	 * like they do in the name lookup cache code.
129265935Spendry 	 * That's too much work for now.
129365935Spendry 	 */
129465989Spendry 
129566027Spendry #ifdef UNION_DIAGNOSTIC
129665989Spendry 	if (un->un_flags & UN_LOCKED)
129765989Spendry 		panic("union: inactivating locked node");
129867073Spendry 	if (un->un_flags & UN_ULOCK)
129967073Spendry 		panic("union: inactivating w/locked upper node");
130065989Spendry #endif
130165989Spendry 
130268078Spendry 	if (un->un_dircache != 0) {
130368078Spendry 		for (vpp = un->un_dircache; *vpp != NULLVP; vpp++)
130468078Spendry 			vrele(*vpp);
130568078Spendry 		free(un->un_dircache, M_TEMP);
130668078Spendry 		un->un_dircache = 0;
130768078Spendry 	}
130868078Spendry 
130967073Spendry 	if ((un->un_flags & UN_CACHED) == 0)
131069389Spendry 		vgone(ap->a_vp);
131167073Spendry 
131265935Spendry 	return (0);
131365935Spendry }
131465935Spendry 
131565935Spendry int
131665935Spendry union_reclaim(ap)
131765935Spendry 	struct vop_reclaim_args /* {
131865935Spendry 		struct vnode *a_vp;
131965935Spendry 	} */ *ap;
132065935Spendry {
132165935Spendry 
132266053Spendry 	union_freevp(ap->a_vp);
132366053Spendry 
132465935Spendry 	return (0);
132565935Spendry }
132665935Spendry 
132765963Spendry int
132865963Spendry union_lock(ap)
132965963Spendry 	struct vop_lock_args *ap;
133065963Spendry {
133166149Spendry 	struct vnode *vp = ap->a_vp;
1332*69447Smckusick 	struct proc *p = ap->a_p;
1333*69447Smckusick 	int flags = ap->a_flags;
133466149Spendry 	struct union_node *un;
133565935Spendry 
133666149Spendry start:
1337*69447Smckusick 	if ((flags & LK_INTERLOCK) == 0)
1338*69447Smckusick 		simple_lock(&vp->v_interlock);
1339*69447Smckusick 	if (vp->v_flag & VXLOCK) {
134066149Spendry 		vp->v_flag |= VXWANT;
1341*69447Smckusick 		simple_unlock(&vp->v_interlock);
1342*69447Smckusick 		tsleep((caddr_t)vp, PINOD, "unionlk1", 0);
1343*69447Smckusick 		return (ENOENT);
134466149Spendry 	}
1345*69447Smckusick 	simple_unlock(&vp->v_interlock);
1346*69447Smckusick 	flags &= ~LK_INTERLOCK;
134766149Spendry 
134866149Spendry 	un = VTOUNION(vp);
134966149Spendry 
135067076Spendry 	if (un->un_uppervp != NULLVP) {
135167230Spendry 		if (((un->un_flags & UN_ULOCK) == 0) &&
135267230Spendry 		    (vp->v_usecount != 0)) {
1353*69447Smckusick 			if (vn_lock(un->un_uppervp, flags, p))
1354*69447Smckusick 				goto start;
135566149Spendry 			un->un_flags |= UN_ULOCK;
135666051Spendry 		}
135766051Spendry #ifdef DIAGNOSTIC
135869389Spendry 		if (un->un_flags & UN_KLOCK) {
135968590Spendry 			vprint("union: dangling klock", vp);
136068590Spendry 			panic("union: dangling upper lock (%lx)", vp);
136169389Spendry 		}
136266051Spendry #endif
136366051Spendry 	}
136466051Spendry 
136566149Spendry 	if (un->un_flags & UN_LOCKED) {
136665963Spendry #ifdef DIAGNOSTIC
136765989Spendry 		if (curproc && un->un_pid == curproc->p_pid &&
136865989Spendry 			    un->un_pid > -1 && curproc->p_pid > -1)
136965989Spendry 			panic("union: locking against myself");
137065963Spendry #endif
137165963Spendry 		un->un_flags |= UN_WANT;
1372*69447Smckusick 		tsleep((caddr_t)&un->un_flags, PINOD, "unionlk2", 0);
137366149Spendry 		goto start;
137465963Spendry 	}
137565989Spendry 
137665963Spendry #ifdef DIAGNOSTIC
137765989Spendry 	if (curproc)
137865989Spendry 		un->un_pid = curproc->p_pid;
137965989Spendry 	else
138065989Spendry 		un->un_pid = -1;
138165963Spendry #endif
138266028Spendry 
138366149Spendry 	un->un_flags |= UN_LOCKED;
138466028Spendry 	return (0);
138565963Spendry }
138665963Spendry 
138768590Spendry /*
138868590Spendry  * When operations want to vput() a union node yet retain a lock on
138968590Spendry  * the upper vnode (say, to do some further operations like link(),
139068590Spendry  * mkdir(), ...), they set UN_KLOCK on the union node, then call
139168590Spendry  * vput() which calls VOP_UNLOCK() and comes here.  union_unlock()
139268590Spendry  * unlocks the union node (leaving the upper vnode alone), clears the
139368590Spendry  * KLOCK flag, and then returns to vput().  The caller then does whatever
139468590Spendry  * is left to do with the upper vnode, and ensures that it gets unlocked.
139568590Spendry  *
139668590Spendry  * If UN_KLOCK isn't set, then the upper vnode is unlocked here.
139768590Spendry  */
139865935Spendry int
139965963Spendry union_unlock(ap)
140065963Spendry 	struct vop_lock_args *ap;
140165963Spendry {
140265963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
1403*69447Smckusick 	struct proc *p = ap->a_p;
140465963Spendry 
140565963Spendry #ifdef DIAGNOSTIC
140665965Spendry 	if ((un->un_flags & UN_LOCKED) == 0)
140765965Spendry 		panic("union: unlock unlocked node");
140865989Spendry 	if (curproc && un->un_pid != curproc->p_pid &&
140965989Spendry 			curproc->p_pid > -1 && un->un_pid > -1)
141065963Spendry 		panic("union: unlocking other process's union node");
141165963Spendry #endif
141265963Spendry 
141365963Spendry 	un->un_flags &= ~UN_LOCKED;
141466051Spendry 
141566051Spendry 	if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK)
1416*69447Smckusick 		VOP_UNLOCK(un->un_uppervp, 0, p);
141766051Spendry 
141866051Spendry 	un->un_flags &= ~(UN_ULOCK|UN_KLOCK);
141966051Spendry 
142065963Spendry 	if (un->un_flags & UN_WANT) {
142165963Spendry 		un->un_flags &= ~UN_WANT;
142265963Spendry 		wakeup((caddr_t) &un->un_flags);
142365963Spendry 	}
142465963Spendry 
142565963Spendry #ifdef DIAGNOSTIC
142665963Spendry 	un->un_pid = 0;
142765963Spendry #endif
142866028Spendry 
142966028Spendry 	return (0);
143065963Spendry }
143165963Spendry 
143265963Spendry int
143365963Spendry union_bmap(ap)
143465963Spendry 	struct vop_bmap_args /* {
143565963Spendry 		struct vnode *a_vp;
143665963Spendry 		daddr_t  a_bn;
143765963Spendry 		struct vnode **a_vpp;
143865963Spendry 		daddr_t *a_bnp;
143965963Spendry 		int *a_runp;
144065963Spendry 	} */ *ap;
144165963Spendry {
144265963Spendry 	int error;
1443*69447Smckusick 	struct proc *p = curproc;		/* XXX */
144465963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
144566051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
144665963Spendry 
144766051Spendry 	if (dolock)
1448*69447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
144966152Spendry 	else
1450*69447Smckusick 		FIXUP(VTOUNION(ap->a_vp), p);
145165963Spendry 	error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp);
145266051Spendry 	if (dolock)
1453*69447Smckusick 		VOP_UNLOCK(vp, 0, p);
145465963Spendry 
145565963Spendry 	return (error);
145665963Spendry }
145765963Spendry 
145865963Spendry int
145965935Spendry union_print(ap)
146065935Spendry 	struct vop_print_args /* {
146165935Spendry 		struct vnode *a_vp;
146265935Spendry 	} */ *ap;
146365935Spendry {
146465935Spendry 	struct vnode *vp = ap->a_vp;
146565935Spendry 
146665935Spendry 	printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n",
146765935Spendry 			vp, UPPERVP(vp), LOWERVP(vp));
146868590Spendry 	if (UPPERVP(vp) != NULLVP)
146968590Spendry 		vprint("union: upper", UPPERVP(vp));
147068590Spendry 	if (LOWERVP(vp) != NULLVP)
147168590Spendry 		vprint("union: lower", LOWERVP(vp));
147268590Spendry 
147365935Spendry 	return (0);
147465935Spendry }
147565935Spendry 
147665963Spendry int
147765963Spendry union_islocked(ap)
147865963Spendry 	struct vop_islocked_args /* {
147965963Spendry 		struct vnode *a_vp;
148065963Spendry 	} */ *ap;
148165963Spendry {
148265935Spendry 
148365963Spendry 	return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0);
148465963Spendry }
148565963Spendry 
148665935Spendry int
148765963Spendry union_pathconf(ap)
148865963Spendry 	struct vop_pathconf_args /* {
148965963Spendry 		struct vnode *a_vp;
149065963Spendry 		int a_name;
149165963Spendry 		int *a_retval;
149265935Spendry 	} */ *ap;
149365935Spendry {
149465935Spendry 	int error;
1495*69447Smckusick 	struct proc *p = curproc;		/* XXX */
149665963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
149766051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
149865935Spendry 
149966051Spendry 	if (dolock)
1500*69447Smckusick 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
150166152Spendry 	else
1502*69447Smckusick 		FIXUP(VTOUNION(ap->a_vp), p);
150365963Spendry 	error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
150466051Spendry 	if (dolock)
1505*69447Smckusick 		VOP_UNLOCK(vp, 0, p);
150665935Spendry 
150765963Spendry 	return (error);
150865963Spendry }
150965935Spendry 
151065963Spendry int
151165963Spendry union_advlock(ap)
151265963Spendry 	struct vop_advlock_args /* {
151365963Spendry 		struct vnode *a_vp;
151465963Spendry 		caddr_t  a_id;
151565963Spendry 		int  a_op;
151665963Spendry 		struct flock *a_fl;
151765963Spendry 		int  a_flags;
151865963Spendry 	} */ *ap;
151965963Spendry {
152065935Spendry 
152165963Spendry 	return (VOP_ADVLOCK(OTHERVP(ap->a_vp), ap->a_id, ap->a_op,
152265963Spendry 				ap->a_fl, ap->a_flags));
152365935Spendry }
152465935Spendry 
152565935Spendry 
152665935Spendry /*
152765963Spendry  * XXX - vop_strategy must be hand coded because it has no
152865935Spendry  * vnode in its arguments.
152965935Spendry  * This goes away with a merged VM/buffer cache.
153065935Spendry  */
153165935Spendry int
153265963Spendry union_strategy(ap)
153365963Spendry 	struct vop_strategy_args /* {
153465935Spendry 		struct buf *a_bp;
153565935Spendry 	} */ *ap;
153665935Spendry {
153765935Spendry 	struct buf *bp = ap->a_bp;
153865935Spendry 	int error;
153965935Spendry 	struct vnode *savedvp;
154065935Spendry 
154165935Spendry 	savedvp = bp->b_vp;
154265963Spendry 	bp->b_vp = OTHERVP(bp->b_vp);
154365935Spendry 
154465935Spendry #ifdef DIAGNOSTIC
154565997Spendry 	if (bp->b_vp == NULLVP)
154665963Spendry 		panic("union_strategy: nil vp");
154765963Spendry 	if (((bp->b_flags & B_READ) == 0) &&
154865963Spendry 	    (bp->b_vp == LOWERVP(savedvp)))
154965963Spendry 		panic("union_strategy: writing to lowervp");
155065935Spendry #endif
155165935Spendry 
155265963Spendry 	error = VOP_STRATEGY(bp);
155365935Spendry 	bp->b_vp = savedvp;
155465935Spendry 
155565935Spendry 	return (error);
155665935Spendry }
155765935Spendry 
155865935Spendry /*
155965935Spendry  * Global vfs data structures
156065935Spendry  */
156165935Spendry int (**union_vnodeop_p)();
156265965Spendry struct vnodeopv_entry_desc union_vnodeop_entries[] = {
156365963Spendry 	{ &vop_default_desc, vn_default_error },
156465963Spendry 	{ &vop_lookup_desc, union_lookup },		/* lookup */
156565963Spendry 	{ &vop_create_desc, union_create },		/* create */
156667575Spendry 	{ &vop_whiteout_desc, union_whiteout },		/* whiteout */
156765963Spendry 	{ &vop_mknod_desc, union_mknod },		/* mknod */
156865963Spendry 	{ &vop_open_desc, union_open },			/* open */
156965963Spendry 	{ &vop_close_desc, union_close },		/* close */
157065963Spendry 	{ &vop_access_desc, union_access },		/* access */
157165963Spendry 	{ &vop_getattr_desc, union_getattr },		/* getattr */
157265963Spendry 	{ &vop_setattr_desc, union_setattr },		/* setattr */
157365963Spendry 	{ &vop_read_desc, union_read },			/* read */
157465963Spendry 	{ &vop_write_desc, union_write },		/* write */
157567751Spendry 	{ &vop_lease_desc, union_lease },		/* lease */
157665963Spendry 	{ &vop_ioctl_desc, union_ioctl },		/* ioctl */
157765963Spendry 	{ &vop_select_desc, union_select },		/* select */
157869389Spendry 	{ &vop_revoke_desc, union_revoke },		/* revoke */
157965963Spendry 	{ &vop_mmap_desc, union_mmap },			/* mmap */
158065963Spendry 	{ &vop_fsync_desc, union_fsync },		/* fsync */
158165963Spendry 	{ &vop_seek_desc, union_seek },			/* seek */
158265963Spendry 	{ &vop_remove_desc, union_remove },		/* remove */
158365963Spendry 	{ &vop_link_desc, union_link },			/* link */
158465963Spendry 	{ &vop_rename_desc, union_rename },		/* rename */
158565963Spendry 	{ &vop_mkdir_desc, union_mkdir },		/* mkdir */
158665963Spendry 	{ &vop_rmdir_desc, union_rmdir },		/* rmdir */
158765963Spendry 	{ &vop_symlink_desc, union_symlink },		/* symlink */
158865963Spendry 	{ &vop_readdir_desc, union_readdir },		/* readdir */
158965963Spendry 	{ &vop_readlink_desc, union_readlink },		/* readlink */
159065963Spendry 	{ &vop_abortop_desc, union_abortop },		/* abortop */
159165963Spendry 	{ &vop_inactive_desc, union_inactive },		/* inactive */
159265963Spendry 	{ &vop_reclaim_desc, union_reclaim },		/* reclaim */
159365963Spendry 	{ &vop_lock_desc, union_lock },			/* lock */
159465963Spendry 	{ &vop_unlock_desc, union_unlock },		/* unlock */
159565963Spendry 	{ &vop_bmap_desc, union_bmap },			/* bmap */
159665963Spendry 	{ &vop_strategy_desc, union_strategy },		/* strategy */
159765963Spendry 	{ &vop_print_desc, union_print },		/* print */
159865963Spendry 	{ &vop_islocked_desc, union_islocked },		/* islocked */
159965963Spendry 	{ &vop_pathconf_desc, union_pathconf },		/* pathconf */
160065963Spendry 	{ &vop_advlock_desc, union_advlock },		/* advlock */
160165963Spendry #ifdef notdef
160265963Spendry 	{ &vop_blkatoff_desc, union_blkatoff },		/* blkatoff */
160365963Spendry 	{ &vop_valloc_desc, union_valloc },		/* valloc */
160465963Spendry 	{ &vop_vfree_desc, union_vfree },		/* vfree */
160565963Spendry 	{ &vop_truncate_desc, union_truncate },		/* truncate */
160665963Spendry 	{ &vop_update_desc, union_update },		/* update */
160765963Spendry 	{ &vop_bwrite_desc, union_bwrite },		/* bwrite */
160865963Spendry #endif
160965935Spendry 	{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
161065935Spendry };
161165935Spendry struct vnodeopv_desc union_vnodeop_opv_desc =
161265935Spendry 	{ &union_vnodeop_p, union_vnodeop_entries };
1613