xref: /csrg-svn/sys/miscfs/union/union_vnops.c (revision 67369)
165935Spendry /*
265935Spendry  * Copyright (c) 1992, 1993, 1994 The Regents of the University of California.
365935Spendry  * Copyright (c) 1992, 1993, 1994 Jan-Simon Pendry.
465935Spendry  * 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*67369Smckusick  *	@(#)union_vnops.c	8.15 (Berkeley) 06/04/94
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>
1965935Spendry #include <sys/types.h>
2065935Spendry #include <sys/vnode.h>
2165935Spendry #include <sys/mount.h>
2265935Spendry #include <sys/namei.h>
2365935Spendry #include <sys/malloc.h>
2465935Spendry #include <sys/buf.h>
2566053Spendry #include <sys/queue.h>
2666055Spendry #include <miscfs/union/union.h>
2765935Spendry 
2866152Spendry #define FIXUP(un) { \
2966152Spendry 	if (((un)->un_flags & UN_ULOCK) == 0) { \
3066152Spendry 		union_fixup(un); \
3166152Spendry 	} \
3266152Spendry }
3366152Spendry 
3466152Spendry static void
3566152Spendry union_fixup(un)
3666152Spendry 	struct union_node *un;
3766152Spendry {
3866152Spendry 
3966152Spendry 	VOP_LOCK(un->un_uppervp);
4066152Spendry 	un->un_flags |= UN_ULOCK;
4166152Spendry }
4266152Spendry 
4365935Spendry static int
4467064Spendry union_lookup1(udvp, dvpp, vpp, cnp)
4565989Spendry 	struct vnode *udvp;
4667064Spendry 	struct vnode **dvpp;
4765935Spendry 	struct vnode **vpp;
4865935Spendry 	struct componentname *cnp;
4965935Spendry {
5065935Spendry 	int error;
5165935Spendry 	struct vnode *tdvp;
5267064Spendry 	struct vnode *dvp;
5365935Spendry 	struct mount *mp;
5465935Spendry 
5567064Spendry 	dvp = *dvpp;
5667064Spendry 
5765994Spendry 	/*
5865994Spendry 	 * If stepping up the directory tree, check for going
5965994Spendry 	 * back across the mount point, in which case do what
6065994Spendry 	 * lookup would do by stepping back down the mount
6165994Spendry 	 * hierarchy.
6265994Spendry 	 */
6365935Spendry 	if (cnp->cn_flags & ISDOTDOT) {
6467064Spendry 		while ((dvp != udvp) && (dvp->v_flag & VROOT)) {
6566034Spendry 			/*
6666034Spendry 			 * Don't do the NOCROSSMOUNT check
6766034Spendry 			 * at this level.  By definition,
6866034Spendry 			 * union fs deals with namespaces, not
6966034Spendry 			 * filesystems.
7066034Spendry 			 */
7165935Spendry 			tdvp = dvp;
7267064Spendry 			*dvpp = dvp = dvp->v_mount->mnt_vnodecovered;
7365935Spendry 			vput(tdvp);
7465935Spendry 			VREF(dvp);
7565935Spendry 			VOP_LOCK(dvp);
7665935Spendry 		}
7765935Spendry 	}
7866027Spendry 
7965935Spendry         error = VOP_LOOKUP(dvp, &tdvp, cnp);
8065935Spendry 	if (error)
8165935Spendry 		return (error);
8265935Spendry 
8365994Spendry 	/*
8466027Spendry 	 * The parent directory will have been unlocked, unless lookup
8566027Spendry 	 * found the last component.  In which case, re-lock the node
8666027Spendry 	 * here to allow it to be unlocked again (phew) in union_lookup.
8765994Spendry 	 */
8866027Spendry 	if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN))
8965994Spendry 		VOP_LOCK(dvp);
9065994Spendry 
9165935Spendry 	dvp = tdvp;
9265994Spendry 
9365994Spendry 	/*
9465994Spendry 	 * Lastly check if the current node is a mount point in
9566034Spendry 	 * which case walk up the mount hierarchy making sure not to
9665994Spendry 	 * bump into the root of the mount tree (ie. dvp != udvp).
9765994Spendry 	 */
9865989Spendry 	while (dvp != udvp && (dvp->v_type == VDIR) &&
9966034Spendry 	       (mp = dvp->v_mountedhere)) {
10065935Spendry 
10165935Spendry 		if (mp->mnt_flag & MNT_MLOCK) {
10265935Spendry 			mp->mnt_flag |= MNT_MWAIT;
10365935Spendry 			sleep((caddr_t) mp, PVFS);
10465935Spendry 			continue;
10565935Spendry 		}
10665935Spendry 
10765935Spendry 		if (error = VFS_ROOT(mp, &tdvp)) {
10865935Spendry 			vput(dvp);
10965935Spendry 			return (error);
11065935Spendry 		}
11165935Spendry 
11265965Spendry 		vput(dvp);
11365935Spendry 		dvp = tdvp;
11465935Spendry 	}
11565935Spendry 
11665935Spendry 	*vpp = dvp;
11765935Spendry 	return (0);
11865935Spendry }
11965935Spendry 
12065935Spendry int
12165935Spendry union_lookup(ap)
12265935Spendry 	struct vop_lookup_args /* {
12365935Spendry 		struct vnodeop_desc *a_desc;
12465935Spendry 		struct vnode *a_dvp;
12565935Spendry 		struct vnode **a_vpp;
12665935Spendry 		struct componentname *a_cnp;
12765935Spendry 	} */ *ap;
12865935Spendry {
12965965Spendry 	int error;
13065935Spendry 	int uerror, lerror;
13165935Spendry 	struct vnode *uppervp, *lowervp;
13265935Spendry 	struct vnode *upperdvp, *lowerdvp;
13365935Spendry 	struct vnode *dvp = ap->a_dvp;
13465989Spendry 	struct union_node *dun = VTOUNION(dvp);
13565935Spendry 	struct componentname *cnp = ap->a_cnp;
13665935Spendry 	int lockparent = cnp->cn_flags & LOCKPARENT;
13765994Spendry 	int rdonly = cnp->cn_flags & RDONLY;
13865997Spendry 	struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
13966152Spendry 	struct ucred *saved_cred;
14065935Spendry 
14165965Spendry 	cnp->cn_flags |= LOCKPARENT;
14265965Spendry 
14365935Spendry 	upperdvp = dun->un_uppervp;
14465935Spendry 	lowerdvp = dun->un_lowervp;
14565997Spendry 	uppervp = NULLVP;
14665997Spendry 	lowervp = NULLVP;
14765935Spendry 
14865935Spendry 	/*
14965935Spendry 	 * do the lookup in the upper level.
15065935Spendry 	 * if that level comsumes additional pathnames,
15165935Spendry 	 * then assume that something special is going
15265935Spendry 	 * on and just return that vnode.
15365935Spendry 	 */
15467076Spendry 	if (upperdvp != NULLVP) {
15566152Spendry 		FIXUP(dun);
15667064Spendry 		uerror = union_lookup1(um->um_uppervp, &upperdvp,
15765997Spendry 					&uppervp, cnp);
15866051Spendry 		/*if (uppervp == upperdvp)
15966051Spendry 			dun->un_flags |= UN_KLOCK;*/
16065965Spendry 
16165935Spendry 		if (cnp->cn_consume != 0) {
16265935Spendry 			*ap->a_vpp = uppervp;
16365965Spendry 			if (!lockparent)
16465965Spendry 				cnp->cn_flags &= ~LOCKPARENT;
16565935Spendry 			return (uerror);
16665935Spendry 		}
16765935Spendry 	} else {
16865935Spendry 		uerror = ENOENT;
16965935Spendry 	}
17065935Spendry 
17165935Spendry 	/*
17265935Spendry 	 * in a similar way to the upper layer, do the lookup
17365935Spendry 	 * in the lower layer.   this time, if there is some
17465935Spendry 	 * component magic going on, then vput whatever we got
17565935Spendry 	 * back from the upper layer and return the lower vnode
17665935Spendry 	 * instead.
17765935Spendry 	 */
17867076Spendry 	if (lowerdvp != NULLVP) {
17966051Spendry 		int nameiop;
18066051Spendry 
18165965Spendry 		VOP_LOCK(lowerdvp);
18266051Spendry 
18366051Spendry 		/*
18466051Spendry 		 * Only do a LOOKUP on the bottom node, since
18566051Spendry 		 * we won't be making changes to it anyway.
18666051Spendry 		 */
18766051Spendry 		nameiop = cnp->cn_nameiop;
18866051Spendry 		cnp->cn_nameiop = LOOKUP;
18966152Spendry 		if (um->um_op == UNMNT_BELOW) {
19066152Spendry 			saved_cred = cnp->cn_cred;
19166152Spendry 			cnp->cn_cred = um->um_cred;
19266152Spendry 		}
19367064Spendry 		lerror = union_lookup1(um->um_lowervp, &lowerdvp,
19466051Spendry 				&lowervp, cnp);
19566152Spendry 		if (um->um_op == UNMNT_BELOW)
19666152Spendry 			cnp->cn_cred = saved_cred;
19766051Spendry 		cnp->cn_nameiop = nameiop;
19866051Spendry 
19965989Spendry 		if (lowervp != lowerdvp)
20065989Spendry 			VOP_UNLOCK(lowerdvp);
20165965Spendry 
20265935Spendry 		if (cnp->cn_consume != 0) {
20367076Spendry 			if (uppervp != NULLVP) {
20466051Spendry 				if (uppervp == upperdvp)
20566051Spendry 					vrele(uppervp);
20666051Spendry 				else
20766051Spendry 					vput(uppervp);
20865997Spendry 				uppervp = NULLVP;
20965935Spendry 			}
21065935Spendry 			*ap->a_vpp = lowervp;
21165965Spendry 			if (!lockparent)
21265965Spendry 				cnp->cn_flags &= ~LOCKPARENT;
21365935Spendry 			return (lerror);
21465935Spendry 		}
21565935Spendry 	} else {
21665935Spendry 		lerror = ENOENT;
21765935Spendry 	}
21865935Spendry 
21965965Spendry 	if (!lockparent)
22065965Spendry 		cnp->cn_flags &= ~LOCKPARENT;
22165965Spendry 
22265935Spendry 	/*
22365935Spendry 	 * at this point, we have uerror and lerror indicating
22465935Spendry 	 * possible errors with the lookups in the upper and lower
22565935Spendry 	 * layers.  additionally, uppervp and lowervp are (locked)
22665935Spendry 	 * references to existing vnodes in the upper and lower layers.
22765935Spendry 	 *
22865935Spendry 	 * there are now three cases to consider.
22965935Spendry 	 * 1. if both layers returned an error, then return whatever
23065935Spendry 	 *    error the upper layer generated.
23165935Spendry 	 *
23265935Spendry 	 * 2. if the top layer failed and the bottom layer succeeded
23365935Spendry 	 *    then two subcases occur.
23465935Spendry 	 *    a.  the bottom vnode is not a directory, in which
23565935Spendry 	 *	  case just return a new union vnode referencing
23665935Spendry 	 *	  an empty top layer and the existing bottom layer.
23765935Spendry 	 *    b.  the bottom vnode is a directory, in which case
23865935Spendry 	 *	  create a new directory in the top-level and
23965935Spendry 	 *	  continue as in case 3.
24065935Spendry 	 *
24165935Spendry 	 * 3. if the top layer succeeded then return a new union
24265935Spendry 	 *    vnode referencing whatever the new top layer and
24365935Spendry 	 *    whatever the bottom layer returned.
24465935Spendry 	 */
24565935Spendry 
24666027Spendry 	*ap->a_vpp = NULLVP;
24766027Spendry 
24865935Spendry 	/* case 1. */
24965935Spendry 	if ((uerror != 0) && (lerror != 0)) {
25065935Spendry 		return (uerror);
25165935Spendry 	}
25265935Spendry 
25365935Spendry 	/* case 2. */
25465935Spendry 	if (uerror != 0 /* && (lerror == 0) */ ) {
25565935Spendry 		if (lowervp->v_type == VDIR) { /* case 2b. */
25666051Spendry 			dun->un_flags &= ~UN_ULOCK;
25766051Spendry 			VOP_UNLOCK(upperdvp);
25865997Spendry 			uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
25966051Spendry 			VOP_LOCK(upperdvp);
26066051Spendry 			dun->un_flags |= UN_ULOCK;
26166051Spendry 
26265935Spendry 			if (uerror) {
26367076Spendry 				if (lowervp != NULLVP) {
26465935Spendry 					vput(lowervp);
26565997Spendry 					lowervp = NULLVP;
26665935Spendry 				}
26765935Spendry 				return (uerror);
26865935Spendry 			}
26965935Spendry 		}
27065935Spendry 	}
27165935Spendry 
27267076Spendry 	if (lowervp != NULLVP)
27365965Spendry 		VOP_UNLOCK(lowervp);
27465965Spendry 
27565997Spendry 	error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
27665997Spendry 			      uppervp, lowervp);
27765997Spendry 
27865965Spendry 	if (error) {
27967076Spendry 		if (uppervp != NULLVP)
28066051Spendry 			vput(uppervp);
28167076Spendry 		if (lowervp != NULLVP)
28265965Spendry 			vrele(lowervp);
28365965Spendry 	} else {
28465994Spendry 		if (*ap->a_vpp != dvp)
28565994Spendry 			if (!lockparent || !(cnp->cn_flags & ISLASTCN))
28665994Spendry 				VOP_UNLOCK(dvp);
28765965Spendry 	}
28865965Spendry 
28965965Spendry 	return (error);
29065935Spendry }
29165935Spendry 
29265963Spendry int
29365963Spendry union_create(ap)
29465963Spendry 	struct vop_create_args /* {
29565963Spendry 		struct vnode *a_dvp;
29665963Spendry 		struct vnode **a_vpp;
29765963Spendry 		struct componentname *a_cnp;
29865963Spendry 		struct vattr *a_vap;
29965963Spendry 	} */ *ap;
30065963Spendry {
30165963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
30265963Spendry 	struct vnode *dvp = un->un_uppervp;
30365963Spendry 
30467076Spendry 	if (dvp != NULLVP) {
30565963Spendry 		int error;
30665963Spendry 		struct vnode *vp;
30765963Spendry 
30866152Spendry 		FIXUP(un);
30966152Spendry 
31065963Spendry 		VREF(dvp);
31166051Spendry 		un->un_flags |= UN_KLOCK;
31265963Spendry 		vput(ap->a_dvp);
31365963Spendry 		error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap);
31465963Spendry 		if (error)
31565963Spendry 			return (error);
31665963Spendry 
31765963Spendry 		error = union_allocvp(
31865963Spendry 				ap->a_vpp,
31965989Spendry 				ap->a_dvp->v_mount,
32065989Spendry 				ap->a_dvp,
32165965Spendry 				NULLVP,
32265963Spendry 				ap->a_cnp,
32365963Spendry 				vp,
32465963Spendry 				NULLVP);
32565965Spendry 		if (error)
32666051Spendry 			vput(vp);
32765963Spendry 		return (error);
32865963Spendry 	}
32965963Spendry 
33065963Spendry 	vput(ap->a_dvp);
33165963Spendry 	return (EROFS);
33265963Spendry }
33365963Spendry 
33465963Spendry int
33565963Spendry union_mknod(ap)
33665963Spendry 	struct vop_mknod_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;
34565963Spendry 
34667076Spendry 	if (dvp != NULLVP) {
34765963Spendry 		int error;
34865963Spendry 		struct vnode *vp;
34965963Spendry 
35066152Spendry 		FIXUP(un);
35166152Spendry 
35265963Spendry 		VREF(dvp);
35366051Spendry 		un->un_flags |= UN_KLOCK;
35465963Spendry 		vput(ap->a_dvp);
35565963Spendry 		error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap);
35665963Spendry 		if (error)
35765963Spendry 			return (error);
35865963Spendry 
35967076Spendry 		if (vp != NULLVP) {
36065965Spendry 			error = union_allocvp(
36165965Spendry 					ap->a_vpp,
36265989Spendry 					ap->a_dvp->v_mount,
36365989Spendry 					ap->a_dvp,
36465965Spendry 					NULLVP,
36565965Spendry 					ap->a_cnp,
36665965Spendry 					vp,
36765965Spendry 					NULLVP);
36865965Spendry 			if (error)
36966051Spendry 				vput(vp);
37065965Spendry 		}
37165963Spendry 		return (error);
37265963Spendry 	}
37365963Spendry 
37465963Spendry 	vput(ap->a_dvp);
37565963Spendry 	return (EROFS);
37665963Spendry }
37765963Spendry 
37865935Spendry int
37965935Spendry union_open(ap)
38065935Spendry 	struct vop_open_args /* {
38165935Spendry 		struct vnodeop_desc *a_desc;
38265935Spendry 		struct vnode *a_vp;
38365935Spendry 		int a_mode;
38465935Spendry 		struct ucred *a_cred;
38565935Spendry 		struct proc *a_p;
38665935Spendry 	} */ *ap;
38765935Spendry {
38865935Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
38965965Spendry 	struct vnode *tvp;
39065935Spendry 	int mode = ap->a_mode;
39165935Spendry 	struct ucred *cred = ap->a_cred;
39265935Spendry 	struct proc *p = ap->a_p;
39365965Spendry 	int error;
39465935Spendry 
39565935Spendry 	/*
39665935Spendry 	 * If there is an existing upper vp then simply open that.
39765935Spendry 	 */
39865965Spendry 	tvp = un->un_uppervp;
39965965Spendry 	if (tvp == NULLVP) {
40065935Spendry 		/*
40165965Spendry 		 * If the lower vnode is being opened for writing, then
40265965Spendry 		 * copy the file contents to the upper vnode and open that,
40365965Spendry 		 * otherwise can simply open the lower vnode.
40465935Spendry 		 */
40565965Spendry 		tvp = un->un_lowervp;
40665965Spendry 		if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
40767169Spendry 			error = union_copyup(un, (mode&O_TRUNC) == 0, cred, p);
40865965Spendry 			if (error == 0)
40965965Spendry 				error = VOP_OPEN(un->un_uppervp, mode, cred, p);
41065965Spendry 			return (error);
41165935Spendry 		}
41266051Spendry 
41366051Spendry 		/*
41466051Spendry 		 * Just open the lower vnode
41566051Spendry 		 */
41666027Spendry 		un->un_openl++;
41766051Spendry 		VOP_LOCK(tvp);
41866051Spendry 		error = VOP_OPEN(tvp, mode, cred, p);
41966051Spendry 		VOP_UNLOCK(tvp);
42066051Spendry 
42166051Spendry 		return (error);
42265935Spendry 	}
42365935Spendry 
42466152Spendry 	FIXUP(un);
42566152Spendry 
42665965Spendry 	error = VOP_OPEN(tvp, mode, cred, p);
42765965Spendry 
42865965Spendry 	return (error);
42965935Spendry }
43065935Spendry 
43165963Spendry int
43265963Spendry union_close(ap)
43365963Spendry 	struct vop_close_args /* {
43465963Spendry 		struct vnode *a_vp;
43565963Spendry 		int  a_fflag;
43665963Spendry 		struct ucred *a_cred;
43765963Spendry 		struct proc *a_p;
43865963Spendry 	} */ *ap;
43965963Spendry {
44065997Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
44165997Spendry 	struct vnode *vp;
44265963Spendry 
44367076Spendry 	if (un->un_uppervp != NULLVP) {
44465997Spendry 		vp = un->un_uppervp;
44565997Spendry 	} else {
44666027Spendry #ifdef UNION_DIAGNOSTIC
44766027Spendry 		if (un->un_openl <= 0)
44866027Spendry 			panic("union: un_openl cnt");
44965997Spendry #endif
45066027Spendry 		--un->un_openl;
45165997Spendry 		vp = un->un_lowervp;
45265997Spendry 	}
45366027Spendry 
45465997Spendry 	return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p));
45565963Spendry }
45665963Spendry 
45765935Spendry /*
45865963Spendry  * Check access permission on the union vnode.
45965963Spendry  * The access check being enforced is to check
46065963Spendry  * against both the underlying vnode, and any
46165963Spendry  * copied vnode.  This ensures that no additional
46265963Spendry  * file permissions are given away simply because
46365963Spendry  * the user caused an implicit file copy.
46465963Spendry  */
46565963Spendry int
46665963Spendry union_access(ap)
46765963Spendry 	struct vop_access_args /* {
46865963Spendry 		struct vnodeop_desc *a_desc;
46965963Spendry 		struct vnode *a_vp;
47065963Spendry 		int a_mode;
47165963Spendry 		struct ucred *a_cred;
47265963Spendry 		struct proc *a_p;
47365963Spendry 	} */ *ap;
47465963Spendry {
47565963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
47666149Spendry 	int error = EACCES;
47765963Spendry 	struct vnode *vp;
47865963Spendry 
47967076Spendry 	if ((vp = un->un_uppervp) != NULLVP) {
48066152Spendry 		FIXUP(un);
48166152Spendry 		return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p));
48266152Spendry 	}
48366152Spendry 
48467076Spendry 	if ((vp = un->un_lowervp) != NULLVP) {
48565965Spendry 		VOP_LOCK(vp);
48665963Spendry 		error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
48766152Spendry 		if (error == 0) {
48866152Spendry 			struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
48966152Spendry 
49066152Spendry 			if (um->um_op == UNMNT_BELOW)
49166152Spendry 				error = VOP_ACCESS(vp, ap->a_mode,
49266152Spendry 						um->um_cred, ap->a_p);
49366152Spendry 		}
49465965Spendry 		VOP_UNLOCK(vp);
49565963Spendry 		if (error)
49665963Spendry 			return (error);
49765963Spendry 	}
49865963Spendry 
49965965Spendry 	return (error);
50065963Spendry }
50165963Spendry 
50265963Spendry /*
50367109Spendry  * We handle getattr only to change the fsid and
50467109Spendry  * track object sizes
50565935Spendry  */
50665935Spendry int
50765935Spendry union_getattr(ap)
50865935Spendry 	struct vop_getattr_args /* {
50965935Spendry 		struct vnode *a_vp;
51065935Spendry 		struct vattr *a_vap;
51165935Spendry 		struct ucred *a_cred;
51265935Spendry 		struct proc *a_p;
51365935Spendry 	} */ *ap;
51465935Spendry {
51565935Spendry 	int error;
51666062Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
51766062Spendry 	struct vnode *vp = un->un_uppervp;
51866062Spendry 	struct vattr *vap;
51966062Spendry 	struct vattr va;
52065935Spendry 
52166062Spendry 
52266062Spendry 	/*
52366062Spendry 	 * Some programs walk the filesystem hierarchy by counting
52466062Spendry 	 * links to directories to avoid stat'ing all the time.
52566062Spendry 	 * This means the link count on directories needs to be "correct".
52666062Spendry 	 * The only way to do that is to call getattr on both layers
52766062Spendry 	 * and fix up the link count.  The link count will not necessarily
52866062Spendry 	 * be accurate but will be large enough to defeat the tree walkers.
52966062Spendry 	 */
53066062Spendry 
53166062Spendry 	vap = ap->a_vap;
53266062Spendry 
53366062Spendry 	vp = un->un_uppervp;
53466062Spendry 	if (vp != NULLVP) {
53567073Spendry 		/*
53667073Spendry 		 * It's not clear whether VOP_GETATTR is to be
53767073Spendry 		 * called with the vnode locked or not.  stat() calls
53867073Spendry 		 * it with (vp) locked, and fstat calls it with
53967073Spendry 		 * (vp) unlocked.
54067073Spendry 		 * In the mean time, compensate here by checking
54167073Spendry 		 * the union_node's lock flag.
54267073Spendry 		 */
54367073Spendry 		if (un->un_flags & UN_LOCKED)
54467073Spendry 			FIXUP(un);
54567073Spendry 
54666062Spendry 		error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
54766062Spendry 		if (error)
54866062Spendry 			return (error);
54967109Spendry 		union_newsize(ap->a_vp, vap->va_size, VNOVAL);
55066062Spendry 	}
55166062Spendry 
55266062Spendry 	if (vp == NULLVP) {
55366062Spendry 		vp = un->un_lowervp;
55466062Spendry 	} else if (vp->v_type == VDIR) {
55566062Spendry 		vp = un->un_lowervp;
55666062Spendry 		vap = &va;
55766062Spendry 	} else {
55866062Spendry 		vp = NULLVP;
55966062Spendry 	}
56066062Spendry 
56166062Spendry 	if (vp != NULLVP) {
56266051Spendry 		VOP_LOCK(vp);
56366062Spendry 		error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
56466051Spendry 		VOP_UNLOCK(vp);
56566062Spendry 		if (error)
56666062Spendry 			return (error);
56767109Spendry 		union_newsize(ap->a_vp, VNOVAL, vap->va_size);
56866062Spendry 	}
56965965Spendry 
57066062Spendry 	if ((vap != ap->a_vap) && (vap->va_type == VDIR))
57166062Spendry 		ap->a_vap->va_nlink += vap->va_nlink;
57266062Spendry 
57366062Spendry 	vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
57465935Spendry 	return (0);
57565935Spendry }
57665935Spendry 
57765963Spendry int
57865965Spendry union_setattr(ap)
57965963Spendry 	struct vop_setattr_args /* {
58065963Spendry 		struct vnode *a_vp;
58165963Spendry 		struct vattr *a_vap;
58265963Spendry 		struct ucred *a_cred;
58365963Spendry 		struct proc *a_p;
58465963Spendry 	} */ *ap;
58565963Spendry {
58665963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
58765963Spendry 	int error;
58865963Spendry 
58966057Spendry 	/*
59066057Spendry 	 * Handle case of truncating lower object to zero size,
59166057Spendry 	 * by creating a zero length upper object.  This is to
59266057Spendry 	 * handle the case of open with O_TRUNC and O_CREAT.
59366057Spendry 	 */
59466057Spendry 	if ((un->un_uppervp == NULLVP) &&
59566057Spendry 	    /* assert(un->un_lowervp != NULLVP) */
59666057Spendry 	    (un->un_lowervp->v_type == VREG) &&
59766057Spendry 	    (ap->a_vap->va_size == 0)) {
59866057Spendry 		struct vnode *vp;
59966057Spendry 
60066057Spendry 		error = union_vn_create(&vp, un, ap->a_p);
60166057Spendry 		if (error)
60266057Spendry 			return (error);
60366057Spendry 
60466057Spendry 		/* at this point, uppervp is locked */
60566057Spendry 		union_newupper(un, vp);
60666057Spendry 
60766057Spendry 		VOP_UNLOCK(vp);
60866057Spendry 		union_vn_close(un->un_uppervp, FWRITE, ap->a_cred, ap->a_p);
60966057Spendry 		VOP_LOCK(vp);
61066057Spendry 		un->un_flags |= UN_ULOCK;
61166057Spendry 	}
61266057Spendry 
61366057Spendry 	/*
61466057Spendry 	 * Try to set attributes in upper layer,
61566057Spendry 	 * otherwise return read-only filesystem error.
61666057Spendry 	 */
61766057Spendry 	if (un->un_uppervp != NULLVP) {
61866152Spendry 		FIXUP(un);
61965963Spendry 		error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
62065963Spendry 					ap->a_cred, ap->a_p);
62167109Spendry 		if ((error == 0) && (ap->a_vap->va_size != VNOVAL))
62267109Spendry 			union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL);
62365963Spendry 	} else {
62465963Spendry 		error = EROFS;
62565963Spendry 	}
62665963Spendry 
62765963Spendry 	return (error);
62865963Spendry }
62965963Spendry 
63065963Spendry int
63165963Spendry union_read(ap)
63265963Spendry 	struct vop_read_args /* {
63365963Spendry 		struct vnode *a_vp;
63465963Spendry 		struct uio *a_uio;
63565963Spendry 		int  a_ioflag;
63665963Spendry 		struct ucred *a_cred;
63765963Spendry 	} */ *ap;
63865963Spendry {
63965963Spendry 	int error;
64065963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
64166051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
64265963Spendry 
64366051Spendry 	if (dolock)
64466051Spendry 		VOP_LOCK(vp);
64566152Spendry 	else
64666152Spendry 		FIXUP(VTOUNION(ap->a_vp));
64765963Spendry 	error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
64866051Spendry 	if (dolock)
64966051Spendry 		VOP_UNLOCK(vp);
65065963Spendry 
65167109Spendry 	/*
65267109Spendry 	 * XXX
65367109Spendry 	 * perhaps the size of the underlying object has changed under
65467109Spendry 	 * our feet.  take advantage of the offset information present
65567109Spendry 	 * in the uio structure.
65667109Spendry 	 */
65767109Spendry 	if (error == 0) {
65867109Spendry 		struct union_node *un = VTOUNION(ap->a_vp);
65967109Spendry 		off_t cur = ap->a_uio->uio_offset;
66067109Spendry 
66167109Spendry 		if (vp == un->un_uppervp) {
66267109Spendry 			if (cur > un->un_uppersz)
66367109Spendry 				union_newsize(ap->a_vp, cur, VNOVAL);
66467109Spendry 		} else {
66567109Spendry 			if (cur > un->un_lowersz)
66667109Spendry 				union_newsize(ap->a_vp, VNOVAL, cur);
66767109Spendry 		}
66867109Spendry 	}
66967109Spendry 
67065963Spendry 	return (error);
67165963Spendry }
67265963Spendry 
67365963Spendry int
67465963Spendry union_write(ap)
67565963Spendry 	struct vop_read_args /* {
67665963Spendry 		struct vnode *a_vp;
67765963Spendry 		struct uio *a_uio;
67865963Spendry 		int  a_ioflag;
67965963Spendry 		struct ucred *a_cred;
68065963Spendry 	} */ *ap;
68165963Spendry {
68265963Spendry 	int error;
68365963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
68466051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
68565963Spendry 
68666051Spendry 	if (dolock)
68766051Spendry 		VOP_LOCK(vp);
68866152Spendry 	else
68966152Spendry 		FIXUP(VTOUNION(ap->a_vp));
69065963Spendry 	error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
69166051Spendry 	if (dolock)
69266051Spendry 		VOP_UNLOCK(vp);
69365963Spendry 
69467109Spendry 	/*
69567109Spendry 	 * the size of the underlying object may be changed by the
69667109Spendry 	 * write.
69767109Spendry 	 */
69867109Spendry 	if (error == 0) {
69967109Spendry 		struct union_node *un = VTOUNION(ap->a_vp);
70067109Spendry 		off_t cur = ap->a_uio->uio_offset;
70167109Spendry 
70267109Spendry 		if (vp == un->un_uppervp) {
70367109Spendry 			if (cur > un->un_uppersz)
70467109Spendry 				union_newsize(ap->a_vp, cur, VNOVAL);
70567109Spendry 		} else {
70667109Spendry 			if (cur > un->un_lowersz)
70767109Spendry 				union_newsize(ap->a_vp, VNOVAL, cur);
70867109Spendry 		}
70967109Spendry 	}
71067109Spendry 
71165963Spendry 	return (error);
71265963Spendry }
71365963Spendry 
71465963Spendry int
71565963Spendry union_ioctl(ap)
71665963Spendry 	struct vop_ioctl_args /* {
71765963Spendry 		struct vnode *a_vp;
71865963Spendry 		int  a_command;
71965963Spendry 		caddr_t  a_data;
72065963Spendry 		int  a_fflag;
72165963Spendry 		struct ucred *a_cred;
72265963Spendry 		struct proc *a_p;
72365963Spendry 	} */ *ap;
72465963Spendry {
72565963Spendry 
72665963Spendry 	return (VOP_IOCTL(OTHERVP(ap->a_vp), ap->a_command, ap->a_data,
72765963Spendry 				ap->a_fflag, ap->a_cred, ap->a_p));
72865963Spendry }
72965963Spendry 
73065963Spendry int
73165963Spendry union_select(ap)
73265963Spendry 	struct vop_select_args /* {
73365963Spendry 		struct vnode *a_vp;
73465963Spendry 		int  a_which;
73565963Spendry 		int  a_fflags;
73665963Spendry 		struct ucred *a_cred;
73765963Spendry 		struct proc *a_p;
73865963Spendry 	} */ *ap;
73965963Spendry {
74065963Spendry 
74165963Spendry 	return (VOP_SELECT(OTHERVP(ap->a_vp), ap->a_which, ap->a_fflags,
74265963Spendry 				ap->a_cred, ap->a_p));
74365963Spendry }
74465963Spendry 
74565963Spendry int
74665963Spendry union_mmap(ap)
74765963Spendry 	struct vop_mmap_args /* {
74865963Spendry 		struct vnode *a_vp;
74965963Spendry 		int  a_fflags;
75065963Spendry 		struct ucred *a_cred;
75165963Spendry 		struct proc *a_p;
75265963Spendry 	} */ *ap;
75365963Spendry {
75465963Spendry 
75565963Spendry 	return (VOP_MMAP(OTHERVP(ap->a_vp), ap->a_fflags,
75665963Spendry 				ap->a_cred, ap->a_p));
75765963Spendry }
75865963Spendry 
75965963Spendry int
76065963Spendry union_fsync(ap)
76165963Spendry 	struct vop_fsync_args /* {
76265963Spendry 		struct vnode *a_vp;
76365963Spendry 		struct ucred *a_cred;
76465963Spendry 		int  a_waitfor;
76565963Spendry 		struct proc *a_p;
76665963Spendry 	} */ *ap;
76765963Spendry {
76865963Spendry 	int error = 0;
76965963Spendry 	struct vnode *targetvp = OTHERVP(ap->a_vp);
77065963Spendry 
77167076Spendry 	if (targetvp != NULLVP) {
77266051Spendry 		int dolock = (targetvp == LOWERVP(ap->a_vp));
77366051Spendry 
77466051Spendry 		if (dolock)
77566051Spendry 			VOP_LOCK(targetvp);
77666152Spendry 		else
77766152Spendry 			FIXUP(VTOUNION(ap->a_vp));
77865963Spendry 		error = VOP_FSYNC(targetvp, ap->a_cred,
77965963Spendry 					ap->a_waitfor, ap->a_p);
78066051Spendry 		if (dolock)
78166051Spendry 			VOP_UNLOCK(targetvp);
78265963Spendry 	}
78365963Spendry 
78465963Spendry 	return (error);
78565963Spendry }
78665963Spendry 
78765963Spendry int
78865963Spendry union_seek(ap)
78965963Spendry 	struct vop_seek_args /* {
79065963Spendry 		struct vnode *a_vp;
79165963Spendry 		off_t  a_oldoff;
79265963Spendry 		off_t  a_newoff;
79365963Spendry 		struct ucred *a_cred;
79465963Spendry 	} */ *ap;
79565963Spendry {
79665963Spendry 
79765963Spendry 	return (VOP_SEEK(OTHERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred));
79865963Spendry }
79965963Spendry 
80065963Spendry int
80165963Spendry union_remove(ap)
80265963Spendry 	struct vop_remove_args /* {
80365963Spendry 		struct vnode *a_dvp;
80465963Spendry 		struct vnode *a_vp;
80565963Spendry 		struct componentname *a_cnp;
80665963Spendry 	} */ *ap;
80765963Spendry {
80865963Spendry 	int error;
80965963Spendry 	struct union_node *dun = VTOUNION(ap->a_dvp);
81065963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
81165963Spendry 
81267076Spendry 	if (dun->un_uppervp != NULLVP && un->un_uppervp != NULLVP) {
81365963Spendry 		struct vnode *dvp = dun->un_uppervp;
81465963Spendry 		struct vnode *vp = un->un_uppervp;
81565963Spendry 
81666152Spendry 		FIXUP(dun);
81765963Spendry 		VREF(dvp);
81866051Spendry 		dun->un_flags |= UN_KLOCK;
81965963Spendry 		vput(ap->a_dvp);
82066152Spendry 		FIXUP(un);
82165963Spendry 		VREF(vp);
82266051Spendry 		un->un_flags |= UN_KLOCK;
82365963Spendry 		vput(ap->a_vp);
82465963Spendry 
82565963Spendry 		error = VOP_REMOVE(dvp, vp, ap->a_cnp);
82666027Spendry 		if (!error)
82766027Spendry 			union_removed_upper(un);
82866027Spendry 
82966027Spendry 		/*
83066027Spendry 		 * XXX: should create a whiteout here
83166027Spendry 		 */
83265963Spendry 	} else {
83365963Spendry 		/*
83465963Spendry 		 * XXX: should create a whiteout here
83565963Spendry 		 */
83665963Spendry 		vput(ap->a_dvp);
83765963Spendry 		vput(ap->a_vp);
83865963Spendry 		error = EROFS;
83965963Spendry 	}
84065963Spendry 
84165963Spendry 	return (error);
84265963Spendry }
84365963Spendry 
84465963Spendry int
84565963Spendry union_link(ap)
84665963Spendry 	struct vop_link_args /* {
84765963Spendry 		struct vnode *a_vp;
84865963Spendry 		struct vnode *a_tdvp;
84965963Spendry 		struct componentname *a_cnp;
85065963Spendry 	} */ *ap;
85165963Spendry {
85267169Spendry 	int error = 0;
85367169Spendry 	struct union_node *un;
85467169Spendry 	struct vnode *vp;
85567169Spendry 	struct vnode *tdvp;
85665963Spendry 
85767169Spendry 	un = VTOUNION(ap->a_vp);
85865963Spendry 
85967169Spendry 	if (ap->a_vp->v_op != ap->a_tdvp->v_op) {
86067169Spendry 		tdvp = ap->a_tdvp;
86167169Spendry 	} else {
86267169Spendry 		struct union_node *tdun = VTOUNION(ap->a_tdvp);
86367169Spendry 		if (tdun->un_uppervp == NULLVP) {
86467169Spendry 			VOP_LOCK(ap->a_tdvp);
86567169Spendry 			if (un->un_uppervp == tdun->un_dirvp) {
86667169Spendry 				un->un_flags &= ~UN_ULOCK;
86767169Spendry 				VOP_UNLOCK(un->un_uppervp);
86867169Spendry 			}
86967169Spendry 			error = union_copyup(tdun, 1, ap->a_cnp->cn_cred,
87067169Spendry 						ap->a_cnp->cn_proc);
87167169Spendry 			if (un->un_uppervp == tdun->un_dirvp) {
87267169Spendry 				VOP_LOCK(un->un_uppervp);
87367169Spendry 				un->un_flags |= UN_ULOCK;
87467169Spendry 			}
87567169Spendry 			VOP_UNLOCK(ap->a_tdvp);
87667169Spendry 		}
87767169Spendry 		tdvp = tdun->un_uppervp;
87867169Spendry 	}
87965963Spendry 
88067169Spendry 	vp = un->un_uppervp;
88167169Spendry 	if (vp == NULLVP)
88267169Spendry 		error = EROFS;
88367169Spendry 
88467169Spendry 	if (error) {
88565963Spendry 		vput(ap->a_vp);
88667169Spendry 		return (error);
88765963Spendry 	}
88865963Spendry 
88967169Spendry 	FIXUP(un);
89067169Spendry 	VREF(vp);
89167169Spendry 	un->un_flags |= UN_KLOCK;
89267169Spendry 	vput(ap->a_vp);
89367169Spendry 
89467169Spendry 	return (VOP_LINK(vp, tdvp, ap->a_cnp));
89565963Spendry }
89665963Spendry 
89765963Spendry int
89865963Spendry union_rename(ap)
89965963Spendry 	struct vop_rename_args  /* {
90065963Spendry 		struct vnode *a_fdvp;
90165963Spendry 		struct vnode *a_fvp;
90265963Spendry 		struct componentname *a_fcnp;
90365963Spendry 		struct vnode *a_tdvp;
90465963Spendry 		struct vnode *a_tvp;
90565963Spendry 		struct componentname *a_tcnp;
90665963Spendry 	} */ *ap;
90765963Spendry {
90865963Spendry 	int error;
90965963Spendry 
91065963Spendry 	struct vnode *fdvp = ap->a_fdvp;
91165963Spendry 	struct vnode *fvp = ap->a_fvp;
91265963Spendry 	struct vnode *tdvp = ap->a_tdvp;
91365963Spendry 	struct vnode *tvp = ap->a_tvp;
91465963Spendry 
91565963Spendry 	if (fdvp->v_op == union_vnodeop_p) {	/* always true */
91665963Spendry 		struct union_node *un = VTOUNION(fdvp);
91765997Spendry 		if (un->un_uppervp == NULLVP) {
91865963Spendry 			error = EROFS;
91965963Spendry 			goto bad;
92065963Spendry 		}
92165963Spendry 
92265963Spendry 		fdvp = un->un_uppervp;
92365963Spendry 		VREF(fdvp);
92465963Spendry 		vrele(ap->a_fdvp);
92565963Spendry 	}
92665963Spendry 
92765963Spendry 	if (fvp->v_op == union_vnodeop_p) {	/* always true */
92865963Spendry 		struct union_node *un = VTOUNION(fvp);
92965997Spendry 		if (un->un_uppervp == NULLVP) {
93065963Spendry 			error = EROFS;
93165963Spendry 			goto bad;
93265963Spendry 		}
93365963Spendry 
93465963Spendry 		fvp = un->un_uppervp;
93565963Spendry 		VREF(fvp);
93665963Spendry 		vrele(ap->a_fvp);
93765963Spendry 	}
93865963Spendry 
93965963Spendry 	if (tdvp->v_op == union_vnodeop_p) {
94065963Spendry 		struct union_node *un = VTOUNION(tdvp);
94165997Spendry 		if (un->un_uppervp == NULLVP) {
94267076Spendry 			/*
94367076Spendry 			 * this should never happen in normal
94467076Spendry 			 * operation but might if there was
94567076Spendry 			 * a problem creating the top-level shadow
94667076Spendry 			 * directory.
94767076Spendry 			 */
94865963Spendry 			error = EROFS;
94965963Spendry 			goto bad;
95065963Spendry 		}
95165963Spendry 
95265963Spendry 		tdvp = un->un_uppervp;
95365963Spendry 		VREF(tdvp);
95466051Spendry 		un->un_flags |= UN_KLOCK;
95565997Spendry 		vput(ap->a_tdvp);
95665963Spendry 	}
95765963Spendry 
95867076Spendry 	if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) {
95965963Spendry 		struct union_node *un = VTOUNION(tvp);
96065963Spendry 
96165963Spendry 		tvp = un->un_uppervp;
96267076Spendry 		if (tvp != NULLVP) {
96367076Spendry 			VREF(tvp);
96467076Spendry 			un->un_flags |= UN_KLOCK;
96567076Spendry 		}
96665963Spendry 		vput(ap->a_tvp);
96765963Spendry 	}
96865963Spendry 
96965963Spendry 	return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));
97065963Spendry 
97165963Spendry bad:
97265963Spendry 	vrele(fdvp);
97365963Spendry 	vrele(fvp);
97465963Spendry 	vput(tdvp);
97567076Spendry 	if (tvp != NULLVP)
97665963Spendry 		vput(tvp);
97765963Spendry 
97865963Spendry 	return (error);
97965963Spendry }
98065963Spendry 
98165963Spendry int
98265963Spendry union_mkdir(ap)
98365963Spendry 	struct vop_mkdir_args /* {
98465963Spendry 		struct vnode *a_dvp;
98565963Spendry 		struct vnode **a_vpp;
98665963Spendry 		struct componentname *a_cnp;
98765963Spendry 		struct vattr *a_vap;
98865963Spendry 	} */ *ap;
98965963Spendry {
99065963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
99165963Spendry 	struct vnode *dvp = un->un_uppervp;
99265963Spendry 
99367076Spendry 	if (dvp != NULLVP) {
99465963Spendry 		int error;
99565963Spendry 		struct vnode *vp;
99665963Spendry 
99766152Spendry 		FIXUP(un);
99865963Spendry 		VREF(dvp);
99966051Spendry 		un->un_flags |= UN_KLOCK;
100065963Spendry 		vput(ap->a_dvp);
100165963Spendry 		error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap);
100265963Spendry 		if (error)
100365963Spendry 			return (error);
100465963Spendry 
100565963Spendry 		error = union_allocvp(
100665963Spendry 				ap->a_vpp,
100765989Spendry 				ap->a_dvp->v_mount,
100865989Spendry 				ap->a_dvp,
100965965Spendry 				NULLVP,
101065963Spendry 				ap->a_cnp,
101165963Spendry 				vp,
101265963Spendry 				NULLVP);
101365965Spendry 		if (error)
101466051Spendry 			vput(vp);
101565963Spendry 		return (error);
101665963Spendry 	}
101765963Spendry 
101865963Spendry 	vput(ap->a_dvp);
101965963Spendry 	return (EROFS);
102065963Spendry }
102165963Spendry 
102265963Spendry int
102365963Spendry union_rmdir(ap)
102465963Spendry 	struct vop_rmdir_args /* {
102565963Spendry 		struct vnode *a_dvp;
102665963Spendry 		struct vnode *a_vp;
102765963Spendry 		struct componentname *a_cnp;
102865963Spendry 	} */ *ap;
102965963Spendry {
103065963Spendry 	int error;
103165963Spendry 	struct union_node *dun = VTOUNION(ap->a_dvp);
103265963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
103365963Spendry 
103467076Spendry 	if (dun->un_uppervp != NULLVP && un->un_uppervp != NULLVP) {
103565963Spendry 		struct vnode *dvp = dun->un_uppervp;
103665963Spendry 		struct vnode *vp = un->un_uppervp;
103765963Spendry 
103866152Spendry 		FIXUP(dun);
103965963Spendry 		VREF(dvp);
104066051Spendry 		dun->un_flags |= UN_KLOCK;
104165963Spendry 		vput(ap->a_dvp);
104266152Spendry 		FIXUP(un);
104365963Spendry 		VREF(vp);
104466051Spendry 		un->un_flags |= UN_KLOCK;
104565963Spendry 		vput(ap->a_vp);
104665963Spendry 
104766051Spendry 		error = VOP_RMDIR(dvp, vp, ap->a_cnp);
104866027Spendry 		if (!error)
104966027Spendry 			union_removed_upper(un);
105066027Spendry 
105166027Spendry 		/*
105266027Spendry 		 * XXX: should create a whiteout here
105366027Spendry 		 */
105465963Spendry 	} else {
105565963Spendry 		/*
105665963Spendry 		 * XXX: should create a whiteout here
105765963Spendry 		 */
105865963Spendry 		vput(ap->a_dvp);
105965963Spendry 		vput(ap->a_vp);
106065963Spendry 		error = EROFS;
106165963Spendry 	}
106265963Spendry 
106365963Spendry 	return (error);
106465963Spendry }
106565963Spendry 
106665963Spendry int
106765963Spendry union_symlink(ap)
106865963Spendry 	struct vop_symlink_args /* {
106965963Spendry 		struct vnode *a_dvp;
107065963Spendry 		struct vnode **a_vpp;
107165963Spendry 		struct componentname *a_cnp;
107265963Spendry 		struct vattr *a_vap;
107365963Spendry 		char *a_target;
107465963Spendry 	} */ *ap;
107565963Spendry {
107665963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
107765963Spendry 	struct vnode *dvp = un->un_uppervp;
107865963Spendry 
107967076Spendry 	if (dvp != NULLVP) {
108065963Spendry 		int error;
108165963Spendry 		struct vnode *vp;
108265963Spendry 		struct mount *mp = ap->a_dvp->v_mount;
108365963Spendry 
108466152Spendry 		FIXUP(un);
108565963Spendry 		VREF(dvp);
108666051Spendry 		un->un_flags |= UN_KLOCK;
108765963Spendry 		vput(ap->a_dvp);
108865963Spendry 		error = VOP_SYMLINK(dvp, &vp, ap->a_cnp,
108965963Spendry 					ap->a_vap, ap->a_target);
109065997Spendry 		*ap->a_vpp = NULLVP;
109165963Spendry 		return (error);
109265963Spendry 	}
109365963Spendry 
109465963Spendry 	vput(ap->a_dvp);
109565963Spendry 	return (EROFS);
109665963Spendry }
109765963Spendry 
109865935Spendry /*
109965935Spendry  * union_readdir works in concert with getdirentries and
110065935Spendry  * readdir(3) to provide a list of entries in the unioned
110165935Spendry  * directories.  getdirentries is responsible for walking
110265935Spendry  * down the union stack.  readdir(3) is responsible for
110365935Spendry  * eliminating duplicate names from the returned data stream.
110465935Spendry  */
110565935Spendry int
110665935Spendry union_readdir(ap)
110765935Spendry 	struct vop_readdir_args /* {
110865935Spendry 		struct vnodeop_desc *a_desc;
110965935Spendry 		struct vnode *a_vp;
111065935Spendry 		struct uio *a_uio;
111165935Spendry 		struct ucred *a_cred;
1112*67369Smckusick 		int *a_eofflag;
1113*67369Smckusick 		u_long *a_cookies;
1114*67369Smckusick 		int a_ncookies;
111565935Spendry 	} */ *ap;
111665935Spendry {
1117*67369Smckusick 	register struct union_node *un = VTOUNION(ap->a_vp);
1118*67369Smckusick 	register struct vnode *uvp = un->un_uppervp;
111965935Spendry 
1120*67369Smckusick 	if (uvp == NULLVP)
1121*67369Smckusick 		return (0);
112265935Spendry 
1123*67369Smckusick 	FIXUP(un);
1124*67369Smckusick 	ap->a_vp = uvp;
1125*67369Smckusick 	return (VOCALL(uvp->v_op, VOFFSET(vop_readdir), ap));
112665935Spendry }
112765935Spendry 
112865935Spendry int
112965963Spendry union_readlink(ap)
113065963Spendry 	struct vop_readlink_args /* {
113165963Spendry 		struct vnode *a_vp;
113265963Spendry 		struct uio *a_uio;
113365963Spendry 		struct ucred *a_cred;
113465963Spendry 	} */ *ap;
113565963Spendry {
113665963Spendry 	int error;
113765963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
113866051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
113965963Spendry 
114066051Spendry 	if (dolock)
114166051Spendry 		VOP_LOCK(vp);
114266152Spendry 	else
114366152Spendry 		FIXUP(VTOUNION(ap->a_vp));
114465963Spendry 	error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
114566051Spendry 	if (dolock)
114666051Spendry 		VOP_UNLOCK(vp);
114765963Spendry 
114865963Spendry 	return (error);
114965963Spendry }
115065963Spendry 
115165963Spendry int
115265963Spendry union_abortop(ap)
115365963Spendry 	struct vop_abortop_args /* {
115465963Spendry 		struct vnode *a_dvp;
115565963Spendry 		struct componentname *a_cnp;
115665963Spendry 	} */ *ap;
115765963Spendry {
115865963Spendry 	int error;
115965965Spendry 	struct vnode *vp = OTHERVP(ap->a_dvp);
116065963Spendry 	struct union_node *un = VTOUNION(ap->a_dvp);
116165963Spendry 	int islocked = un->un_flags & UN_LOCKED;
116266051Spendry 	int dolock = (vp == LOWERVP(ap->a_dvp));
116365963Spendry 
116466152Spendry 	if (islocked) {
116566152Spendry 		if (dolock)
116666152Spendry 			VOP_LOCK(vp);
116766152Spendry 		else
116866152Spendry 			FIXUP(VTOUNION(ap->a_dvp));
116966152Spendry 	}
117065963Spendry 	error = VOP_ABORTOP(vp, ap->a_cnp);
117166051Spendry 	if (islocked && dolock)
117265963Spendry 		VOP_UNLOCK(vp);
117365963Spendry 
117465963Spendry 	return (error);
117565963Spendry }
117665963Spendry 
117765963Spendry int
117865935Spendry union_inactive(ap)
117965935Spendry 	struct vop_inactive_args /* {
118065935Spendry 		struct vnode *a_vp;
118165935Spendry 	} */ *ap;
118265935Spendry {
118367073Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
118465935Spendry 
118565935Spendry 	/*
118665935Spendry 	 * Do nothing (and _don't_ bypass).
118765935Spendry 	 * Wait to vrele lowervp until reclaim,
118865935Spendry 	 * so that until then our union_node is in the
118965935Spendry 	 * cache and reusable.
119065935Spendry 	 *
119165935Spendry 	 * NEEDSWORK: Someday, consider inactive'ing
119265935Spendry 	 * the lowervp and then trying to reactivate it
119365935Spendry 	 * with capabilities (v_id)
119465935Spendry 	 * like they do in the name lookup cache code.
119565935Spendry 	 * That's too much work for now.
119665935Spendry 	 */
119765989Spendry 
119866027Spendry #ifdef UNION_DIAGNOSTIC
119965989Spendry 	if (un->un_flags & UN_LOCKED)
120065989Spendry 		panic("union: inactivating locked node");
120167073Spendry 	if (un->un_flags & UN_ULOCK)
120267073Spendry 		panic("union: inactivating w/locked upper node");
120365989Spendry #endif
120465989Spendry 
120567073Spendry 	if ((un->un_flags & UN_CACHED) == 0)
120667073Spendry 		vgone(ap->a_vp);
120767073Spendry 
120865935Spendry 	return (0);
120965935Spendry }
121065935Spendry 
121165935Spendry int
121265935Spendry union_reclaim(ap)
121365935Spendry 	struct vop_reclaim_args /* {
121465935Spendry 		struct vnode *a_vp;
121565935Spendry 	} */ *ap;
121665935Spendry {
121765935Spendry 
121866053Spendry 	union_freevp(ap->a_vp);
121966053Spendry 
122065935Spendry 	return (0);
122165935Spendry }
122265935Spendry 
122365963Spendry int
122465963Spendry union_lock(ap)
122565963Spendry 	struct vop_lock_args *ap;
122665963Spendry {
122766149Spendry 	struct vnode *vp = ap->a_vp;
122866149Spendry 	struct union_node *un;
122965935Spendry 
123066149Spendry start:
123166149Spendry 	while (vp->v_flag & VXLOCK) {
123266149Spendry 		vp->v_flag |= VXWANT;
123366149Spendry 		sleep((caddr_t)vp, PINOD);
123466149Spendry 	}
123566149Spendry 
123666149Spendry 	un = VTOUNION(vp);
123766149Spendry 
123867076Spendry 	if (un->un_uppervp != NULLVP) {
123967230Spendry 		if (((un->un_flags & UN_ULOCK) == 0) &&
124067230Spendry 		    (vp->v_usecount != 0)) {
124166149Spendry 			un->un_flags |= UN_ULOCK;
124266051Spendry 			VOP_LOCK(un->un_uppervp);
124366051Spendry 		}
124466051Spendry #ifdef DIAGNOSTIC
124566051Spendry 		if (un->un_flags & UN_KLOCK)
124666051Spendry 			panic("union: dangling upper lock");
124766051Spendry #endif
124866051Spendry 	}
124966051Spendry 
125066149Spendry 	if (un->un_flags & UN_LOCKED) {
125165963Spendry #ifdef DIAGNOSTIC
125265989Spendry 		if (curproc && un->un_pid == curproc->p_pid &&
125365989Spendry 			    un->un_pid > -1 && curproc->p_pid > -1)
125465989Spendry 			panic("union: locking against myself");
125565963Spendry #endif
125665963Spendry 		un->un_flags |= UN_WANT;
125765963Spendry 		sleep((caddr_t) &un->un_flags, PINOD);
125866149Spendry 		goto start;
125965963Spendry 	}
126065989Spendry 
126165963Spendry #ifdef DIAGNOSTIC
126265989Spendry 	if (curproc)
126365989Spendry 		un->un_pid = curproc->p_pid;
126465989Spendry 	else
126565989Spendry 		un->un_pid = -1;
126665963Spendry #endif
126766028Spendry 
126866149Spendry 	un->un_flags |= UN_LOCKED;
126966028Spendry 	return (0);
127065963Spendry }
127165963Spendry 
127265935Spendry int
127365963Spendry union_unlock(ap)
127465963Spendry 	struct vop_lock_args *ap;
127565963Spendry {
127665963Spendry 	struct union_node *un = VTOUNION(ap->a_vp);
127765963Spendry 
127865963Spendry #ifdef DIAGNOSTIC
127965965Spendry 	if ((un->un_flags & UN_LOCKED) == 0)
128065965Spendry 		panic("union: unlock unlocked node");
128165989Spendry 	if (curproc && un->un_pid != curproc->p_pid &&
128265989Spendry 			curproc->p_pid > -1 && un->un_pid > -1)
128365963Spendry 		panic("union: unlocking other process's union node");
128465963Spendry #endif
128565963Spendry 
128665963Spendry 	un->un_flags &= ~UN_LOCKED;
128766051Spendry 
128866051Spendry 	if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK)
128966051Spendry 		VOP_UNLOCK(un->un_uppervp);
129066051Spendry 
129166051Spendry 	un->un_flags &= ~(UN_ULOCK|UN_KLOCK);
129266051Spendry 
129365963Spendry 	if (un->un_flags & UN_WANT) {
129465963Spendry 		un->un_flags &= ~UN_WANT;
129565963Spendry 		wakeup((caddr_t) &un->un_flags);
129665963Spendry 	}
129765963Spendry 
129865963Spendry #ifdef DIAGNOSTIC
129965963Spendry 	un->un_pid = 0;
130065963Spendry #endif
130166028Spendry 
130266028Spendry 	return (0);
130365963Spendry }
130465963Spendry 
130565963Spendry int
130665963Spendry union_bmap(ap)
130765963Spendry 	struct vop_bmap_args /* {
130865963Spendry 		struct vnode *a_vp;
130965963Spendry 		daddr_t  a_bn;
131065963Spendry 		struct vnode **a_vpp;
131165963Spendry 		daddr_t *a_bnp;
131265963Spendry 		int *a_runp;
131365963Spendry 	} */ *ap;
131465963Spendry {
131565963Spendry 	int error;
131665963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
131766051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
131865963Spendry 
131966051Spendry 	if (dolock)
132066051Spendry 		VOP_LOCK(vp);
132166152Spendry 	else
132266152Spendry 		FIXUP(VTOUNION(ap->a_vp));
132365963Spendry 	error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp);
132466051Spendry 	if (dolock)
132566051Spendry 		VOP_UNLOCK(vp);
132665963Spendry 
132765963Spendry 	return (error);
132865963Spendry }
132965963Spendry 
133065963Spendry int
133165935Spendry union_print(ap)
133265935Spendry 	struct vop_print_args /* {
133365935Spendry 		struct vnode *a_vp;
133465935Spendry 	} */ *ap;
133565935Spendry {
133665935Spendry 	struct vnode *vp = ap->a_vp;
133765935Spendry 
133865935Spendry 	printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n",
133965935Spendry 			vp, UPPERVP(vp), LOWERVP(vp));
134065935Spendry 	return (0);
134165935Spendry }
134265935Spendry 
134365963Spendry int
134465963Spendry union_islocked(ap)
134565963Spendry 	struct vop_islocked_args /* {
134665963Spendry 		struct vnode *a_vp;
134765963Spendry 	} */ *ap;
134865963Spendry {
134965935Spendry 
135065963Spendry 	return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0);
135165963Spendry }
135265963Spendry 
135365935Spendry int
135465963Spendry union_pathconf(ap)
135565963Spendry 	struct vop_pathconf_args /* {
135665963Spendry 		struct vnode *a_vp;
135765963Spendry 		int a_name;
135865963Spendry 		int *a_retval;
135965935Spendry 	} */ *ap;
136065935Spendry {
136165935Spendry 	int error;
136265963Spendry 	struct vnode *vp = OTHERVP(ap->a_vp);
136366051Spendry 	int dolock = (vp == LOWERVP(ap->a_vp));
136465935Spendry 
136566051Spendry 	if (dolock)
136666051Spendry 		VOP_LOCK(vp);
136766152Spendry 	else
136866152Spendry 		FIXUP(VTOUNION(ap->a_vp));
136965963Spendry 	error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
137066051Spendry 	if (dolock)
137166051Spendry 		VOP_UNLOCK(vp);
137265935Spendry 
137365963Spendry 	return (error);
137465963Spendry }
137565935Spendry 
137665963Spendry int
137765963Spendry union_advlock(ap)
137865963Spendry 	struct vop_advlock_args /* {
137965963Spendry 		struct vnode *a_vp;
138065963Spendry 		caddr_t  a_id;
138165963Spendry 		int  a_op;
138265963Spendry 		struct flock *a_fl;
138365963Spendry 		int  a_flags;
138465963Spendry 	} */ *ap;
138565963Spendry {
138665935Spendry 
138765963Spendry 	return (VOP_ADVLOCK(OTHERVP(ap->a_vp), ap->a_id, ap->a_op,
138865963Spendry 				ap->a_fl, ap->a_flags));
138965935Spendry }
139065935Spendry 
139165935Spendry 
139265935Spendry /*
139365963Spendry  * XXX - vop_strategy must be hand coded because it has no
139465935Spendry  * vnode in its arguments.
139565935Spendry  * This goes away with a merged VM/buffer cache.
139665935Spendry  */
139765935Spendry int
139865963Spendry union_strategy(ap)
139965963Spendry 	struct vop_strategy_args /* {
140065935Spendry 		struct buf *a_bp;
140165935Spendry 	} */ *ap;
140265935Spendry {
140365935Spendry 	struct buf *bp = ap->a_bp;
140465935Spendry 	int error;
140565935Spendry 	struct vnode *savedvp;
140665935Spendry 
140765935Spendry 	savedvp = bp->b_vp;
140865963Spendry 	bp->b_vp = OTHERVP(bp->b_vp);
140965935Spendry 
141065935Spendry #ifdef DIAGNOSTIC
141165997Spendry 	if (bp->b_vp == NULLVP)
141265963Spendry 		panic("union_strategy: nil vp");
141365963Spendry 	if (((bp->b_flags & B_READ) == 0) &&
141465963Spendry 	    (bp->b_vp == LOWERVP(savedvp)))
141565963Spendry 		panic("union_strategy: writing to lowervp");
141665935Spendry #endif
141765935Spendry 
141865963Spendry 	error = VOP_STRATEGY(bp);
141965935Spendry 	bp->b_vp = savedvp;
142065935Spendry 
142165935Spendry 	return (error);
142265935Spendry }
142365935Spendry 
142465935Spendry /*
142565935Spendry  * Global vfs data structures
142665935Spendry  */
142765935Spendry int (**union_vnodeop_p)();
142865965Spendry struct vnodeopv_entry_desc union_vnodeop_entries[] = {
142965963Spendry 	{ &vop_default_desc, vn_default_error },
143065963Spendry 	{ &vop_lookup_desc, union_lookup },		/* lookup */
143165963Spendry 	{ &vop_create_desc, union_create },		/* create */
143265963Spendry 	{ &vop_mknod_desc, union_mknod },		/* mknod */
143365963Spendry 	{ &vop_open_desc, union_open },			/* open */
143465963Spendry 	{ &vop_close_desc, union_close },		/* close */
143565963Spendry 	{ &vop_access_desc, union_access },		/* access */
143665963Spendry 	{ &vop_getattr_desc, union_getattr },		/* getattr */
143765963Spendry 	{ &vop_setattr_desc, union_setattr },		/* setattr */
143865963Spendry 	{ &vop_read_desc, union_read },			/* read */
143965963Spendry 	{ &vop_write_desc, union_write },		/* write */
144065963Spendry 	{ &vop_ioctl_desc, union_ioctl },		/* ioctl */
144165963Spendry 	{ &vop_select_desc, union_select },		/* select */
144265963Spendry 	{ &vop_mmap_desc, union_mmap },			/* mmap */
144365963Spendry 	{ &vop_fsync_desc, union_fsync },		/* fsync */
144465963Spendry 	{ &vop_seek_desc, union_seek },			/* seek */
144565963Spendry 	{ &vop_remove_desc, union_remove },		/* remove */
144665963Spendry 	{ &vop_link_desc, union_link },			/* link */
144765963Spendry 	{ &vop_rename_desc, union_rename },		/* rename */
144865963Spendry 	{ &vop_mkdir_desc, union_mkdir },		/* mkdir */
144965963Spendry 	{ &vop_rmdir_desc, union_rmdir },		/* rmdir */
145065963Spendry 	{ &vop_symlink_desc, union_symlink },		/* symlink */
145165963Spendry 	{ &vop_readdir_desc, union_readdir },		/* readdir */
145265963Spendry 	{ &vop_readlink_desc, union_readlink },		/* readlink */
145365963Spendry 	{ &vop_abortop_desc, union_abortop },		/* abortop */
145465963Spendry 	{ &vop_inactive_desc, union_inactive },		/* inactive */
145565963Spendry 	{ &vop_reclaim_desc, union_reclaim },		/* reclaim */
145665963Spendry 	{ &vop_lock_desc, union_lock },			/* lock */
145765963Spendry 	{ &vop_unlock_desc, union_unlock },		/* unlock */
145865963Spendry 	{ &vop_bmap_desc, union_bmap },			/* bmap */
145965963Spendry 	{ &vop_strategy_desc, union_strategy },		/* strategy */
146065963Spendry 	{ &vop_print_desc, union_print },		/* print */
146165963Spendry 	{ &vop_islocked_desc, union_islocked },		/* islocked */
146265963Spendry 	{ &vop_pathconf_desc, union_pathconf },		/* pathconf */
146365963Spendry 	{ &vop_advlock_desc, union_advlock },		/* advlock */
146465963Spendry #ifdef notdef
146565963Spendry 	{ &vop_blkatoff_desc, union_blkatoff },		/* blkatoff */
146665963Spendry 	{ &vop_valloc_desc, union_valloc },		/* valloc */
146765963Spendry 	{ &vop_vfree_desc, union_vfree },		/* vfree */
146865963Spendry 	{ &vop_truncate_desc, union_truncate },		/* truncate */
146965963Spendry 	{ &vop_update_desc, union_update },		/* update */
147065963Spendry 	{ &vop_bwrite_desc, union_bwrite },		/* bwrite */
147165963Spendry #endif
147265935Spendry 	{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
147365935Spendry };
147465935Spendry struct vnodeopv_desc union_vnodeop_opv_desc =
147565935Spendry 	{ &union_vnodeop_p, union_vnodeop_entries };
1476