19666aa02Spendry /*
25acdc075Spendry  * Copyright (c) 1994, 1995 The Regents of the University of California.
35acdc075Spendry  * Copyright (c) 1994, 1995 Jan-Simon Pendry.
49666aa02Spendry  * All rights reserved.
59666aa02Spendry  *
69666aa02Spendry  * This code is derived from software donated to Berkeley by
79666aa02Spendry  * Jan-Simon Pendry.
89666aa02Spendry  *
99666aa02Spendry  * %sccs.include.redist.c%
109666aa02Spendry  *
11*5fdf4401Spendry  *	@(#)union_vfsops.c	8.20 (Berkeley) 05/20/95
129666aa02Spendry  */
139666aa02Spendry 
149666aa02Spendry /*
159979a570Spendry  * Union Layer
169666aa02Spendry  */
179666aa02Spendry 
189666aa02Spendry #include <sys/param.h>
199666aa02Spendry #include <sys/systm.h>
209666aa02Spendry #include <sys/time.h>
219666aa02Spendry #include <sys/types.h>
229666aa02Spendry #include <sys/proc.h>
239666aa02Spendry #include <sys/vnode.h>
249666aa02Spendry #include <sys/mount.h>
259666aa02Spendry #include <sys/namei.h>
269666aa02Spendry #include <sys/malloc.h>
27ab1360c4Spendry #include <sys/filedesc.h>
2813cf9c7cSpendry #include <sys/queue.h>
29f6d85c23Spendry #include <miscfs/union/union.h>
309666aa02Spendry 
319666aa02Spendry /*
329666aa02Spendry  * Mount union filesystem
339666aa02Spendry  */
349666aa02Spendry int
union_mount(mp,path,data,ndp,p)359666aa02Spendry union_mount(mp, path, data, ndp, p)
369666aa02Spendry 	struct mount *mp;
379666aa02Spendry 	char *path;
389666aa02Spendry 	caddr_t data;
399666aa02Spendry 	struct nameidata *ndp;
409666aa02Spendry 	struct proc *p;
419666aa02Spendry {
429666aa02Spendry 	int error = 0;
439666aa02Spendry 	struct union_args args;
446d0f5438Spendry 	struct vnode *lowerrootvp = NULLVP;
456d0f5438Spendry 	struct vnode *upperrootvp = NULLVP;
464ba124f7Spendry 	struct union_mount *um = 0;
476d0f5438Spendry 	struct ucred *cred = 0;
4852a6a3dcSpendry 	struct ucred *scred;
4980428da2Spendry 	struct vattr va;
506d0f5438Spendry 	char *cp;
516d0f5438Spendry 	int len;
529666aa02Spendry 	u_int size;
539666aa02Spendry 
549666aa02Spendry #ifdef UNION_DIAGNOSTIC
559666aa02Spendry 	printf("union_mount(mp = %x)\n", mp);
569666aa02Spendry #endif
579666aa02Spendry 
589666aa02Spendry 	/*
599666aa02Spendry 	 * Update is a no-op
609666aa02Spendry 	 */
619979a570Spendry 	if (mp->mnt_flag & MNT_UPDATE) {
629979a570Spendry 		/*
639979a570Spendry 		 * Need to provide.
649979a570Spendry 		 * 1. a way to convert between rdonly and rdwr mounts.
659979a570Spendry 		 * 2. support for nfs exports.
669979a570Spendry 		 */
676d0f5438Spendry 		error = EOPNOTSUPP;
686d0f5438Spendry 		goto bad;
699979a570Spendry 	}
709666aa02Spendry 
719666aa02Spendry 	/*
729666aa02Spendry 	 * Get argument
739666aa02Spendry 	 */
749666aa02Spendry 	if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
756d0f5438Spendry 		goto bad;
769666aa02Spendry 
779666aa02Spendry 	lowerrootvp = mp->mnt_vnodecovered;
789666aa02Spendry 	VREF(lowerrootvp);
799666aa02Spendry 
809666aa02Spendry 	/*
814ba124f7Spendry 	 * Find upper node.
829666aa02Spendry 	 */
839666aa02Spendry 	NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
849666aa02Spendry 	       UIO_USERSPACE, args.target, p);
8552a6a3dcSpendry 
866d0f5438Spendry 	if (error = namei(ndp))
876d0f5438Spendry 		goto bad;
886d0f5438Spendry 
899666aa02Spendry 	upperrootvp = ndp->ni_vp;
909666aa02Spendry 	vrele(ndp->ni_dvp);
919666aa02Spendry 	ndp->ni_dvp = NULL;
929666aa02Spendry 
939666aa02Spendry 	if (upperrootvp->v_type != VDIR) {
946d0f5438Spendry 		error = EINVAL;
956d0f5438Spendry 		goto bad;
969666aa02Spendry 	}
979666aa02Spendry 
989666aa02Spendry 	um = (struct union_mount *) malloc(sizeof(struct union_mount),
999666aa02Spendry 				M_UFSMNT, M_WAITOK);	/* XXX */
1009666aa02Spendry 
1019666aa02Spendry 	/*
1029666aa02Spendry 	 * Keep a held reference to the target vnodes.
1039666aa02Spendry 	 * They are vrele'd in union_unmount.
1046d0f5438Spendry 	 *
1056d0f5438Spendry 	 * Depending on the _BELOW flag, the filesystems are
1066d0f5438Spendry 	 * viewed in a different order.  In effect, this is the
1076d0f5438Spendry 	 * same as providing a mount under option to the mount syscall.
1089666aa02Spendry 	 */
1096d0f5438Spendry 
11080428da2Spendry 	um->um_op = args.mntflags & UNMNT_OPMASK;
11180428da2Spendry 	switch (um->um_op) {
1126d0f5438Spendry 	case UNMNT_ABOVE:
1139666aa02Spendry 		um->um_lowervp = lowerrootvp;
1149666aa02Spendry 		um->um_uppervp = upperrootvp;
1156d0f5438Spendry 		break;
1166d0f5438Spendry 
1176d0f5438Spendry 	case UNMNT_BELOW:
1186d0f5438Spendry 		um->um_lowervp = upperrootvp;
1196d0f5438Spendry 		um->um_uppervp = lowerrootvp;
1206d0f5438Spendry 		break;
1216d0f5438Spendry 
1226d0f5438Spendry 	case UNMNT_REPLACE:
1236d0f5438Spendry 		vrele(lowerrootvp);
1246d0f5438Spendry 		lowerrootvp = NULLVP;
1256d0f5438Spendry 		um->um_uppervp = upperrootvp;
1266d0f5438Spendry 		um->um_lowervp = lowerrootvp;
1276d0f5438Spendry 		break;
1286d0f5438Spendry 
1296d0f5438Spendry 	default:
1306d0f5438Spendry 		error = EINVAL;
1316d0f5438Spendry 		goto bad;
1326d0f5438Spendry 	}
1336d0f5438Spendry 
1344ba124f7Spendry 	/*
1354ba124f7Spendry 	 * Unless the mount is readonly, ensure that the top layer
1364ba124f7Spendry 	 * supports whiteout operations
1374ba124f7Spendry 	 */
1384ba124f7Spendry 	if ((mp->mnt_flag & MNT_RDONLY) == 0) {
1394ba124f7Spendry 		error = VOP_WHITEOUT(um->um_uppervp, (struct componentname *) 0, LOOKUP);
1404ba124f7Spendry 		if (error)
1414ba124f7Spendry 			goto bad;
1424ba124f7Spendry 	}
1434ba124f7Spendry 
1444ba124f7Spendry 	um->um_cred = p->p_ucred;
1454ba124f7Spendry 	crhold(um->um_cred);
146ab1360c4Spendry 	um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
1479666aa02Spendry 
148ab1360c4Spendry 	/*
149ab1360c4Spendry 	 * Depending on what you think the MNT_LOCAL flag might mean,
150ab1360c4Spendry 	 * you may want the && to be || on the conditional below.
151ab1360c4Spendry 	 * At the moment it has been defined that the filesystem is
152ab1360c4Spendry 	 * only local if it is all local, ie the MNT_LOCAL flag implies
153ab1360c4Spendry 	 * that the entire namespace is local.  If you think the MNT_LOCAL
154ab1360c4Spendry 	 * flag implies that some of the files might be stored locally
155ab1360c4Spendry 	 * then you will want to change the conditional.
156ab1360c4Spendry 	 */
15780428da2Spendry 	if (um->um_op == UNMNT_ABOVE) {
1586d0f5438Spendry 		if (((um->um_lowervp == NULLVP) ||
1596d0f5438Spendry 		     (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
1606d0f5438Spendry 		    (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
1619666aa02Spendry 			mp->mnt_flag |= MNT_LOCAL;
16280428da2Spendry 	}
163ab1360c4Spendry 
1649979a570Spendry 	/*
1659979a570Spendry 	 * Copy in the upper layer's RDONLY flag.  This is for the benefit
1669979a570Spendry 	 * of lookup() which explicitly checks the flag, rather than asking
1679979a570Spendry 	 * the filesystem for it's own opinion.  This means, that an update
1689979a570Spendry 	 * mount of the underlying filesystem to go from rdonly to rdwr
1699979a570Spendry 	 * will leave the unioned view as read-only.
1709979a570Spendry 	 */
1716d0f5438Spendry 	mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
172d4710d8bSpendry 
1739666aa02Spendry 	mp->mnt_data = (qaddr_t) um;
174b799a20eSmckusick 	vfs_getnewfsid(mp);
1759666aa02Spendry 
1769666aa02Spendry 	(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
1779666aa02Spendry 	bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
1786d0f5438Spendry 
17980428da2Spendry 	switch (um->um_op) {
1806d0f5438Spendry 	case UNMNT_ABOVE:
1812bbbbcfeSpendry 		cp = "<above>:";
1826d0f5438Spendry 		break;
1836d0f5438Spendry 	case UNMNT_BELOW:
1842bbbbcfeSpendry 		cp = "<below>:";
1856d0f5438Spendry 		break;
1866d0f5438Spendry 	case UNMNT_REPLACE:
18751b0e03dSpendry 		cp = "";
1886d0f5438Spendry 		break;
1896d0f5438Spendry 	}
1906d0f5438Spendry 	len = strlen(cp);
1916d0f5438Spendry 	bcopy(cp, mp->mnt_stat.f_mntfromname, len);
1926d0f5438Spendry 
1936d0f5438Spendry 	cp = mp->mnt_stat.f_mntfromname + len;
1946d0f5438Spendry 	len = MNAMELEN - len;
1956d0f5438Spendry 
1966d0f5438Spendry 	(void) copyinstr(args.target, cp, len - 1, &size);
1976d0f5438Spendry 	bzero(cp + size, len - size);
1986d0f5438Spendry 
1999666aa02Spendry #ifdef UNION_DIAGNOSTIC
2006d0f5438Spendry 	printf("union_mount: from %s, on %s\n",
2019666aa02Spendry 		mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
2029666aa02Spendry #endif
2039666aa02Spendry 	return (0);
2046d0f5438Spendry 
2056d0f5438Spendry bad:
2064ba124f7Spendry 	if (um)
2074ba124f7Spendry 		free(um, M_UFSMNT);
2086d0f5438Spendry 	if (cred)
2096d0f5438Spendry 		crfree(cred);
2106d0f5438Spendry 	if (upperrootvp)
2116d0f5438Spendry 		vrele(upperrootvp);
2126d0f5438Spendry 	if (lowerrootvp)
2136d0f5438Spendry 		vrele(lowerrootvp);
2146d0f5438Spendry 	return (error);
2159666aa02Spendry }
2169666aa02Spendry 
2179666aa02Spendry /*
2189666aa02Spendry  * VFS start.  Nothing needed here - the start routine
2199666aa02Spendry  * on the underlying filesystem(s) will have been called
2209666aa02Spendry  * when that filesystem was mounted.
2219666aa02Spendry  */
2229666aa02Spendry int
union_start(mp,flags,p)2239666aa02Spendry union_start(mp, flags, p)
2249666aa02Spendry 	struct mount *mp;
2259666aa02Spendry 	int flags;
2269666aa02Spendry 	struct proc *p;
2279666aa02Spendry {
2289666aa02Spendry 
2299666aa02Spendry 	return (0);
2309666aa02Spendry }
2319666aa02Spendry 
2329666aa02Spendry /*
2339666aa02Spendry  * Free reference to union layer
2349666aa02Spendry  */
2359666aa02Spendry int
union_unmount(mp,mntflags,p)2369666aa02Spendry union_unmount(mp, mntflags, p)
2379666aa02Spendry 	struct mount *mp;
2389666aa02Spendry 	int mntflags;
2399666aa02Spendry 	struct proc *p;
2409666aa02Spendry {
2419666aa02Spendry 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
2429666aa02Spendry 	struct vnode *um_rootvp;
2439666aa02Spendry 	int error;
244785a71e5Spendry 	int freeing;
2459666aa02Spendry 	int flags = 0;
2469666aa02Spendry 
2479666aa02Spendry #ifdef UNION_DIAGNOSTIC
2489666aa02Spendry 	printf("union_unmount(mp = %x)\n", mp);
2499666aa02Spendry #endif
2509666aa02Spendry 
251fba4b590Smckusick 	if (mntflags & MNT_FORCE)
2529666aa02Spendry 		flags |= FORCECLOSE;
2539666aa02Spendry 
2549666aa02Spendry 	if (error = union_root(mp, &um_rootvp))
2559666aa02Spendry 		return (error);
256785a71e5Spendry 
257785a71e5Spendry 	/*
258785a71e5Spendry 	 * Keep flushing vnodes from the mount list.
259785a71e5Spendry 	 * This is needed because of the un_pvp held
260785a71e5Spendry 	 * reference to the parent vnode.
261785a71e5Spendry 	 * If more vnodes have been freed on a given pass,
262785a71e5Spendry 	 * the try again.  The loop will iterate at most
263785a71e5Spendry 	 * (d) times, where (d) is the maximum tree depth
264785a71e5Spendry 	 * in the filesystem.
265785a71e5Spendry 	 */
266785a71e5Spendry 	for (freeing = 0; vflush(mp, um_rootvp, flags) != 0;) {
267785a71e5Spendry 		struct vnode *vp;
268785a71e5Spendry 		int n;
269785a71e5Spendry 
270785a71e5Spendry 		/* count #vnodes held on mount list */
271785a71e5Spendry 		for (n = 0, vp = mp->mnt_vnodelist.lh_first;
272785a71e5Spendry 				vp != NULLVP;
273785a71e5Spendry 				vp = vp->v_mntvnodes.le_next)
274785a71e5Spendry 			n++;
275785a71e5Spendry 
276785a71e5Spendry 		/* if this is unchanged then stop */
277785a71e5Spendry 		if (n == freeing)
278785a71e5Spendry 			break;
279785a71e5Spendry 
280785a71e5Spendry 		/* otherwise try once more time */
281785a71e5Spendry 		freeing = n;
282785a71e5Spendry 	}
283785a71e5Spendry 
284785a71e5Spendry 	/* At this point the root vnode should have a single reference */
2854fa8e650Spendry 	if (um_rootvp->v_usecount > 1) {
2864fa8e650Spendry 		vput(um_rootvp);
2879666aa02Spendry 		return (EBUSY);
2884fa8e650Spendry 	}
2899666aa02Spendry 
2909666aa02Spendry #ifdef UNION_DIAGNOSTIC
291785a71e5Spendry 	vprint("union root", um_rootvp);
2929666aa02Spendry #endif
2939666aa02Spendry 	/*
2949666aa02Spendry 	 * Discard references to upper and lower target vnodes.
2959666aa02Spendry 	 */
2966d0f5438Spendry 	if (um->um_lowervp)
2979666aa02Spendry 		vrele(um->um_lowervp);
2989666aa02Spendry 	vrele(um->um_uppervp);
2999666aa02Spendry 	crfree(um->um_cred);
3009666aa02Spendry 	/*
3019666aa02Spendry 	 * Release reference on underlying root vnode
3029666aa02Spendry 	 */
3034fa8e650Spendry 	vput(um_rootvp);
3049666aa02Spendry 	/*
3059666aa02Spendry 	 * And blow it away for future re-use
3069666aa02Spendry 	 */
3075acdc075Spendry 	vgone(um_rootvp);
3089666aa02Spendry 	/*
3099666aa02Spendry 	 * Finally, throw away the union_mount structure
3109666aa02Spendry 	 */
3119666aa02Spendry 	free(mp->mnt_data, M_UFSMNT);	/* XXX */
3129666aa02Spendry 	mp->mnt_data = 0;
313ab1360c4Spendry 	return (0);
3149666aa02Spendry }
3159666aa02Spendry 
3169666aa02Spendry int
union_root(mp,vpp)3179666aa02Spendry union_root(mp, vpp)
3189666aa02Spendry 	struct mount *mp;
3199666aa02Spendry 	struct vnode **vpp;
3209666aa02Spendry {
32128a12a76Smckusick 	struct proc *p = curproc;	/* XXX */
3229666aa02Spendry 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
3239666aa02Spendry 	int error;
32480428da2Spendry 	int loselock;
3259666aa02Spendry 
3269666aa02Spendry 	/*
3279666aa02Spendry 	 * Return locked reference to root.
3289666aa02Spendry 	 */
3294fa8e650Spendry 	VREF(um->um_uppervp);
33080428da2Spendry 	if ((um->um_op == UNMNT_BELOW) &&
33180428da2Spendry 	     VOP_ISLOCKED(um->um_uppervp)) {
33280428da2Spendry 		loselock = 1;
33380428da2Spendry 	} else {
33428a12a76Smckusick 		vn_lock(um->um_uppervp, LK_EXCLUSIVE | LK_RETRY, p);
33580428da2Spendry 		loselock = 0;
33680428da2Spendry 	}
3376d0f5438Spendry 	if (um->um_lowervp)
3384fa8e650Spendry 		VREF(um->um_lowervp);
3391113bb97Spendry 	error = union_allocvp(vpp, mp,
3401113bb97Spendry 			      (struct vnode *) 0,
3411113bb97Spendry 			      (struct vnode *) 0,
3429666aa02Spendry 			      (struct componentname *) 0,
3439666aa02Spendry 			      um->um_uppervp,
344b6a23b57Spendry 			      um->um_lowervp,
345b6a23b57Spendry 			      1);
3464fa8e650Spendry 
3474fa8e650Spendry 	if (error) {
348*5fdf4401Spendry 		if (loselock)
3494fa8e650Spendry 			vrele(um->um_uppervp);
350*5fdf4401Spendry 		else
351*5fdf4401Spendry 			vput(um->um_uppervp);
3526d0f5438Spendry 		if (um->um_lowervp)
3534fa8e650Spendry 			vrele(um->um_lowervp);
3544fa8e650Spendry 	} else {
35580428da2Spendry 		if (loselock)
35680428da2Spendry 			VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
3574fa8e650Spendry 	}
3589666aa02Spendry 
3599666aa02Spendry 	return (error);
3609666aa02Spendry }
3619666aa02Spendry 
3629666aa02Spendry int
union_statfs(mp,sbp,p)3639666aa02Spendry union_statfs(mp, sbp, p)
3649666aa02Spendry 	struct mount *mp;
3659666aa02Spendry 	struct statfs *sbp;
3669666aa02Spendry 	struct proc *p;
3679666aa02Spendry {
3689666aa02Spendry 	int error;
3699666aa02Spendry 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
3709666aa02Spendry 	struct statfs mstat;
3719666aa02Spendry 	int lbsize;
3729666aa02Spendry 
3739666aa02Spendry #ifdef UNION_DIAGNOSTIC
3749666aa02Spendry 	printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
3759666aa02Spendry 			um->um_lowervp,
3769666aa02Spendry 	       		um->um_uppervp);
3779666aa02Spendry #endif
3789666aa02Spendry 
3799666aa02Spendry 	bzero(&mstat, sizeof(mstat));
3809666aa02Spendry 
3816d0f5438Spendry 	if (um->um_lowervp) {
3829666aa02Spendry 		error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
3839666aa02Spendry 		if (error)
3849666aa02Spendry 			return (error);
3856d0f5438Spendry 	}
3869666aa02Spendry 
3879666aa02Spendry 	/* now copy across the "interesting" information and fake the rest */
3889666aa02Spendry #if 0
3899666aa02Spendry 	sbp->f_type = mstat.f_type;
3909666aa02Spendry 	sbp->f_flags = mstat.f_flags;
3919666aa02Spendry 	sbp->f_bsize = mstat.f_bsize;
3929666aa02Spendry 	sbp->f_iosize = mstat.f_iosize;
3939666aa02Spendry #endif
3949666aa02Spendry 	lbsize = mstat.f_bsize;
3959666aa02Spendry 	sbp->f_blocks = mstat.f_blocks;
3969666aa02Spendry 	sbp->f_bfree = mstat.f_bfree;
3979666aa02Spendry 	sbp->f_bavail = mstat.f_bavail;
3989666aa02Spendry 	sbp->f_files = mstat.f_files;
3999666aa02Spendry 	sbp->f_ffree = mstat.f_ffree;
4009666aa02Spendry 
4019666aa02Spendry 	error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
4029666aa02Spendry 	if (error)
4039666aa02Spendry 		return (error);
4049666aa02Spendry 
4059666aa02Spendry 	sbp->f_flags = mstat.f_flags;
4069666aa02Spendry 	sbp->f_bsize = mstat.f_bsize;
4079666aa02Spendry 	sbp->f_iosize = mstat.f_iosize;
4089666aa02Spendry 
4099666aa02Spendry 	/*
4109666aa02Spendry 	 * if the lower and upper blocksizes differ, then frig the
4119666aa02Spendry 	 * block counts so that the sizes reported by df make some
4129666aa02Spendry 	 * kind of sense.  none of this makes sense though.
4139666aa02Spendry 	 */
4149666aa02Spendry 
41547dea905Spendry 	if (mstat.f_bsize != lbsize)
4169666aa02Spendry 		sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
41747dea905Spendry 
41847dea905Spendry 	/*
41947dea905Spendry 	 * The "total" fields count total resources in all layers,
42047dea905Spendry 	 * the "free" fields count only those resources which are
42147dea905Spendry 	 * free in the upper layer (since only the upper layer
42247dea905Spendry 	 * is writeable).
42347dea905Spendry 	 */
4249666aa02Spendry 	sbp->f_blocks += mstat.f_blocks;
42547dea905Spendry 	sbp->f_bfree = mstat.f_bfree;
42647dea905Spendry 	sbp->f_bavail = mstat.f_bavail;
4279666aa02Spendry 	sbp->f_files += mstat.f_files;
42847dea905Spendry 	sbp->f_ffree = mstat.f_ffree;
4299666aa02Spendry 
4309666aa02Spendry 	if (sbp != &mp->mnt_stat) {
431b799a20eSmckusick 		sbp->f_type = mp->mnt_vfc->vfc_typenum;
4329666aa02Spendry 		bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
4339666aa02Spendry 		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
4349666aa02Spendry 		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
4359666aa02Spendry 	}
4369666aa02Spendry 	return (0);
4379666aa02Spendry }
4389666aa02Spendry 
4399666aa02Spendry /*
4409666aa02Spendry  * XXX - Assumes no data cached at union layer.
4419666aa02Spendry  */
442b799a20eSmckusick #define union_sync ((int (*) __P((struct mount *, int, struct ucred *, \
443b799a20eSmckusick 	    struct proc *)))nullop)
4449666aa02Spendry 
445b799a20eSmckusick #define union_fhtovp ((int (*) __P((struct mount *, struct fid *, \
446b799a20eSmckusick 	    struct mbuf *, struct vnode **, int *, struct ucred **)))eopnotsupp)
447b799a20eSmckusick int union_init __P((struct vfsconf *));
448b799a20eSmckusick #define union_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \
449b799a20eSmckusick 	    struct proc *)))eopnotsupp)
450b799a20eSmckusick #define union_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \
451b799a20eSmckusick 	    size_t, struct proc *)))eopnotsupp)
452b799a20eSmckusick #define union_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \
453b799a20eSmckusick 	    eopnotsupp)
454b799a20eSmckusick #define union_vptofh ((int (*) __P((struct vnode *, struct fid *)))eopnotsupp)
4559666aa02Spendry 
4569666aa02Spendry struct vfsops union_vfsops = {
4579666aa02Spendry 	union_mount,
4589666aa02Spendry 	union_start,
4599666aa02Spendry 	union_unmount,
4609666aa02Spendry 	union_root,
4619666aa02Spendry 	union_quotactl,
4629666aa02Spendry 	union_statfs,
4639666aa02Spendry 	union_sync,
4649666aa02Spendry 	union_vget,
4659666aa02Spendry 	union_fhtovp,
4669666aa02Spendry 	union_vptofh,
4679666aa02Spendry 	union_init,
468b799a20eSmckusick 	union_sysctl,
4699666aa02Spendry };
470