union_vfsops.c revision 3496
196263Sobrien/*
296263Sobrien * Copyright (c) 1994 The Regents of the University of California.
396263Sobrien * Copyright (c) 1994 Jan-Simon Pendry.
496263Sobrien * All rights reserved.
596263Sobrien *
696263Sobrien * This code is derived from software donated to Berkeley by
796263Sobrien * Jan-Simon Pendry.
896263Sobrien *
996263Sobrien * Redistribution and use in source and binary forms, with or without
1096263Sobrien * modification, are permitted provided that the following conditions
1196263Sobrien * are met:
1296263Sobrien * 1. Redistributions of source code must retain the above copyright
1396263Sobrien *    notice, this list of conditions and the following disclaimer.
1496263Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1596263Sobrien *    notice, this list of conditions and the following disclaimer in the
1696263Sobrien *    documentation and/or other materials provided with the distribution.
1796263Sobrien * 3. All advertising materials mentioning features or use of this software
1896263Sobrien *    must display the following acknowledgement:
1996263Sobrien *	This product includes software developed by the University of
2096263Sobrien *	California, Berkeley and its contributors.
2196263Sobrien * 4. Neither the name of the University nor the names of its contributors
2296263Sobrien *    may be used to endorse or promote products derived from this software
2396263Sobrien *    without specific prior written permission.
2496263Sobrien *
2596263Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2696263Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2796263Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2896263Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2996263Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3096263Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3196263Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3296263Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3396263Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3496263Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3596263Sobrien * SUCH DAMAGE.
3696263Sobrien *
3796263Sobrien *	@(#)union_vfsops.c	8.7 (Berkeley) 3/5/94
3896263Sobrien * $Id: union_vfsops.c,v 1.5 1994/09/22 19:38:20 wollman Exp $
3996263Sobrien */
4096263Sobrien
4196263Sobrien/*
4296263Sobrien * Union Layer
4396263Sobrien */
4496263Sobrien
4596263Sobrien#include <sys/param.h>
4696263Sobrien#include <sys/systm.h>
4796263Sobrien#include <sys/kernel.h>
4896263Sobrien#include <sys/time.h>
4996263Sobrien#include <sys/types.h>
5096263Sobrien#include <sys/proc.h>
5196263Sobrien#include <sys/vnode.h>
5296263Sobrien#include <sys/mount.h>
5396263Sobrien#include <sys/namei.h>
5496263Sobrien#include <sys/malloc.h>
5596263Sobrien#include <sys/filedesc.h>
5696263Sobrien#include <sys/queue.h>
5796263Sobrien#include <miscfs/union/union.h>
5896263Sobrien
5996263Sobrien/*
6096263Sobrien * Mount union filesystem
6196263Sobrien */
6296263Sobrienint
6396263Sobrienunion_mount(mp, path, data, ndp, p)
6496263Sobrien	struct mount *mp;
6596263Sobrien	char *path;
6696263Sobrien	caddr_t data;
6796263Sobrien	struct nameidata *ndp;
6896263Sobrien	struct proc *p;
6996263Sobrien{
7096263Sobrien	int error = 0;
7196263Sobrien	struct union_args args;
7296263Sobrien	struct vnode *lowerrootvp = NULLVP;
7396263Sobrien	struct vnode *upperrootvp = NULLVP;
7496263Sobrien	struct union_mount *um;
7596263Sobrien	struct ucred *cred = 0;
7696263Sobrien	struct ucred *scred;
7796263Sobrien	struct vattr va;
7896263Sobrien	char *cp = 0;
7996263Sobrien	int len;
8096263Sobrien	u_int size;
8196263Sobrien
8296263Sobrien#ifdef UNION_DIAGNOSTIC
8396263Sobrien	printf("union_mount(mp = %x)\n", mp);
8496263Sobrien#endif
8596263Sobrien
8696263Sobrien	/*
8796263Sobrien	 * Update is a no-op
8896263Sobrien	 */
8996263Sobrien	if (mp->mnt_flag & MNT_UPDATE) {
9096263Sobrien		/*
9196263Sobrien		 * Need to provide.
9296263Sobrien		 * 1. a way to convert between rdonly and rdwr mounts.
9396263Sobrien		 * 2. support for nfs exports.
9496263Sobrien		 */
9596263Sobrien		error = EOPNOTSUPP;
9696263Sobrien		goto bad;
9796263Sobrien	}
9896263Sobrien
9996263Sobrien	/*
10096263Sobrien	 * Take a copy of the process's credentials.  This isn't
10196263Sobrien	 * quite right since the euid will always be zero and we
10296263Sobrien	 * want to get the "real" users credentials.  So fix up
10396263Sobrien	 * the uid field after taking the copy.
10496263Sobrien	 */
10596263Sobrien	cred = crdup(p->p_ucred);
10696263Sobrien	cred->cr_uid = p->p_cred->p_ruid;
10796263Sobrien
10896263Sobrien	/*
10996263Sobrien	 * Ensure the *real* user has write permission on the
11096263Sobrien	 * mounted-on directory.  This allows the mount_union
11196263Sobrien	 * command to be made setuid root so allowing anyone
11296263Sobrien	 * to do union mounts onto any directory on which they
11396263Sobrien	 * have write permission and which they also own.
11496263Sobrien	 */
11596263Sobrien	error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p);
11696263Sobrien	if (error)
11796263Sobrien		goto bad;
11896263Sobrien	if ((va.va_uid != cred->cr_uid) &&
11996263Sobrien	    (cred->cr_uid != 0)) {
12096263Sobrien		error = EACCES;
12196263Sobrien		goto bad;
12296263Sobrien	}
12396263Sobrien	error = VOP_ACCESS(mp->mnt_vnodecovered, VWRITE, cred, p);
12496263Sobrien	if (error)
12596263Sobrien		goto bad;
12696263Sobrien
12796263Sobrien	/*
12896263Sobrien	 * Get argument
12996263Sobrien	 */
13096263Sobrien	error = copyin(data, (caddr_t)&args, sizeof(struct union_args));
13196263Sobrien	if (error)
13296263Sobrien		goto bad;
13396263Sobrien
13496263Sobrien	lowerrootvp = mp->mnt_vnodecovered;
13596263Sobrien	VREF(lowerrootvp);
13696263Sobrien
13796263Sobrien	/*
13896263Sobrien	 * Find upper node.  Use the real process credentials,
13996263Sobrien	 * not the effective ones since this will have come
14096263Sobrien	 * through a setuid process (mount_union).  All this
14196263Sobrien	 * messing around with permissions is entirely bogus
14296263Sobrien	 * and should be removed by allowing any user straight
14396263Sobrien	 * past the mount system call.
14496263Sobrien	 */
14596263Sobrien	scred = p->p_ucred;
14696263Sobrien	p->p_ucred = cred;
14796263Sobrien	NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
14896263Sobrien	       UIO_USERSPACE, args.target, p);
14996263Sobrien	p->p_ucred = scred;
15096263Sobrien
15196263Sobrien	error = namei(ndp);
15296263Sobrien	if (error)
15396263Sobrien		goto bad;
15496263Sobrien
15596263Sobrien	upperrootvp = ndp->ni_vp;
15696263Sobrien	vrele(ndp->ni_dvp);
15796263Sobrien	ndp->ni_dvp = NULL;
15896263Sobrien
15996263Sobrien	if (upperrootvp->v_type != VDIR) {
16096263Sobrien		error = EINVAL;
16196263Sobrien		goto bad;
16296263Sobrien	}
16396263Sobrien
16496263Sobrien	um = (struct union_mount *) malloc(sizeof(struct union_mount),
16596263Sobrien				M_UFSMNT, M_WAITOK);	/* XXX */
16696263Sobrien
16796263Sobrien	/*
16896263Sobrien	 * Keep a held reference to the target vnodes.
16996263Sobrien	 * They are vrele'd in union_unmount.
17096263Sobrien	 *
17196263Sobrien	 * Depending on the _BELOW flag, the filesystems are
17296263Sobrien	 * viewed in a different order.  In effect, this is the
17396263Sobrien	 * same as providing a mount under option to the mount syscall.
17496263Sobrien	 */
17596263Sobrien
17696263Sobrien	um->um_op = args.mntflags & UNMNT_OPMASK;
17796263Sobrien	switch (um->um_op) {
17896263Sobrien	case UNMNT_ABOVE:
17996263Sobrien		um->um_lowervp = lowerrootvp;
18096263Sobrien		um->um_uppervp = upperrootvp;
18196263Sobrien		break;
18296263Sobrien
18396263Sobrien	case UNMNT_BELOW:
18496263Sobrien		um->um_lowervp = upperrootvp;
18596263Sobrien		um->um_uppervp = lowerrootvp;
18696263Sobrien		break;
18796263Sobrien
18896263Sobrien	case UNMNT_REPLACE:
18996263Sobrien		vrele(lowerrootvp);
19096263Sobrien		lowerrootvp = NULLVP;
19196263Sobrien		um->um_uppervp = upperrootvp;
19296263Sobrien		um->um_lowervp = lowerrootvp;
19396263Sobrien		break;
19496263Sobrien
19596263Sobrien	default:
19696263Sobrien		error = EINVAL;
19796263Sobrien		goto bad;
19896263Sobrien	}
19996263Sobrien
20096263Sobrien	um->um_cred = cred;
20196263Sobrien	um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
20296263Sobrien
20396263Sobrien	/*
20496263Sobrien	 * Depending on what you think the MNT_LOCAL flag might mean,
20596263Sobrien	 * you may want the && to be || on the conditional below.
20696263Sobrien	 * At the moment it has been defined that the filesystem is
20796263Sobrien	 * only local if it is all local, ie the MNT_LOCAL flag implies
20896263Sobrien	 * that the entire namespace is local.  If you think the MNT_LOCAL
20996263Sobrien	 * flag implies that some of the files might be stored locally
21096263Sobrien	 * then you will want to change the conditional.
21196263Sobrien	 */
21296263Sobrien	if (um->um_op == UNMNT_ABOVE) {
21396263Sobrien		if (((um->um_lowervp == NULLVP) ||
21496263Sobrien		     (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
21596263Sobrien		    (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
21696263Sobrien			mp->mnt_flag |= MNT_LOCAL;
21796263Sobrien	}
21896263Sobrien
21996263Sobrien	/*
22096263Sobrien	 * Copy in the upper layer's RDONLY flag.  This is for the benefit
22196263Sobrien	 * of lookup() which explicitly checks the flag, rather than asking
22296263Sobrien	 * the filesystem for it's own opinion.  This means, that an update
22396263Sobrien	 * mount of the underlying filesystem to go from rdonly to rdwr
22496263Sobrien	 * will leave the unioned view as read-only.
22596263Sobrien	 */
22696263Sobrien	mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
22796263Sobrien
22896263Sobrien	/*
22996263Sobrien	 * This is a user mount.  Privilege check for unmount
23096263Sobrien	 * will be done in union_unmount.
23196263Sobrien	 */
23296263Sobrien	mp->mnt_flag |= MNT_USER;
23396263Sobrien
23496263Sobrien	mp->mnt_data = (qaddr_t) um;
23596263Sobrien	getnewfsid(mp, MOUNT_UNION);
23696263Sobrien
23796263Sobrien	(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
23896263Sobrien	bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
23996263Sobrien
24096263Sobrien	switch (um->um_op) {
24196263Sobrien	case UNMNT_ABOVE:
24296263Sobrien		cp = "<above>";
24396263Sobrien		break;
24496263Sobrien	case UNMNT_BELOW:
24596263Sobrien		cp = "<below>";
24696263Sobrien		break;
24796263Sobrien	case UNMNT_REPLACE:
24896263Sobrien		cp = "";
24996263Sobrien		break;
25096263Sobrien	}
25196263Sobrien	len = strlen(cp);
25296263Sobrien	bcopy(cp, mp->mnt_stat.f_mntfromname, len);
25396263Sobrien
25496263Sobrien	cp = mp->mnt_stat.f_mntfromname + len;
25596263Sobrien	len = MNAMELEN - len;
25696263Sobrien
25796263Sobrien	(void) copyinstr(args.target, cp, len - 1, &size);
25896263Sobrien	bzero(cp + size, len - size);
25996263Sobrien
26096263Sobrien#ifdef UNION_DIAGNOSTIC
26196263Sobrien	printf("union_mount: from %s, on %s\n",
26296263Sobrien		mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
26396263Sobrien#endif
26496263Sobrien	return (0);
26596263Sobrien
26696263Sobrienbad:
26796263Sobrien	if (cred)
26896263Sobrien		crfree(cred);
26996263Sobrien	if (upperrootvp)
27096263Sobrien		vrele(upperrootvp);
27196263Sobrien	if (lowerrootvp)
27296263Sobrien		vrele(lowerrootvp);
27396263Sobrien	return (error);
27496263Sobrien}
27596263Sobrien
27696263Sobrien/*
27796263Sobrien * VFS start.  Nothing needed here - the start routine
27896263Sobrien * on the underlying filesystem(s) will have been called
27996263Sobrien * when that filesystem was mounted.
28096263Sobrien */
28196263Sobrienint
28296263Sobrienunion_start(mp, flags, p)
28396263Sobrien	struct mount *mp;
28496263Sobrien	int flags;
28596263Sobrien	struct proc *p;
28696263Sobrien{
28796263Sobrien
28896263Sobrien	return (0);
28996263Sobrien}
29096263Sobrien
29196263Sobrien/*
29296263Sobrien * Free reference to union layer
29396263Sobrien */
29496263Sobrienint
29596263Sobrienunion_unmount(mp, mntflags, p)
29696263Sobrien	struct mount *mp;
29796263Sobrien	int mntflags;
29896263Sobrien	struct proc *p;
29996263Sobrien{
30096263Sobrien	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
30196263Sobrien	struct vnode *um_rootvp;
30296263Sobrien	int error;
30396263Sobrien	int flags = 0;
30496263Sobrien	extern int doforce;
30596263Sobrien
30696263Sobrien#ifdef UNION_DIAGNOSTIC
30796263Sobrien	printf("union_unmount(mp = %x)\n", mp);
30896263Sobrien#endif
30996263Sobrien
31096263Sobrien	/* only the mounter, or superuser can unmount */
31196263Sobrien	if ((p->p_cred->p_ruid != um->um_cred->cr_uid) &&
31296263Sobrien	    (error = suser(p->p_ucred, &p->p_acflag)))
31396263Sobrien		return (error);
31496263Sobrien
31596263Sobrien	if (mntflags & MNT_FORCE) {
31696263Sobrien		/* union can never be rootfs so don't check for it */
31796263Sobrien		if (!doforce)
31896263Sobrien			return (EINVAL);
31996263Sobrien		flags |= FORCECLOSE;
32096263Sobrien	}
32196263Sobrien
32296263Sobrien	error = union_root(mp, &um_rootvp);
32396263Sobrien	if (error)
32496263Sobrien		return (error);
32596263Sobrien	if (um_rootvp->v_usecount > 1) {
32696263Sobrien		vput(um_rootvp);
32796263Sobrien		return (EBUSY);
32896263Sobrien	}
32996263Sobrien	error = vflush(mp, um_rootvp, flags);
33096263Sobrien	if (error) {
33196263Sobrien		vput(um_rootvp);
33296263Sobrien		return (error);
33396263Sobrien	}
33496263Sobrien
33596263Sobrien#ifdef UNION_DIAGNOSTIC
33696263Sobrien	vprint("alias root of lower", um_rootvp);
33796263Sobrien#endif
33896263Sobrien	/*
33996263Sobrien	 * Discard references to upper and lower target vnodes.
34096263Sobrien	 */
34196263Sobrien	if (um->um_lowervp)
34296263Sobrien		vrele(um->um_lowervp);
34396263Sobrien	vrele(um->um_uppervp);
34496263Sobrien	crfree(um->um_cred);
34596263Sobrien	/*
34696263Sobrien	 * Release reference on underlying root vnode
34796263Sobrien	 */
34896263Sobrien	vput(um_rootvp);
34996263Sobrien	/*
35096263Sobrien	 * And blow it away for future re-use
35196263Sobrien	 */
35296263Sobrien	vgone(um_rootvp);
35396263Sobrien	/*
35496263Sobrien	 * Finally, throw away the union_mount structure
35596263Sobrien	 */
35696263Sobrien	free(mp->mnt_data, M_UFSMNT);	/* XXX */
35796263Sobrien	mp->mnt_data = 0;
35896263Sobrien	return (0);
35996263Sobrien}
36096263Sobrien
36196263Sobrienint
36296263Sobrienunion_root(mp, vpp)
36396263Sobrien	struct mount *mp;
36496263Sobrien	struct vnode **vpp;
36596263Sobrien{
36696263Sobrien	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
36796263Sobrien	int error;
36896263Sobrien	int loselock;
36996263Sobrien
37096263Sobrien#ifdef UNION_DIAGNOSTIC
37196263Sobrien	printf("union_root(mp = %x, lvp = %x, uvp = %x)\n", mp,
37296263Sobrien			um->um_lowervp,
37396263Sobrien			um->um_uppervp);
37496263Sobrien#endif
37596263Sobrien
37696263Sobrien	/*
37796263Sobrien	 * Return locked reference to root.
37896263Sobrien	 */
37996263Sobrien	VREF(um->um_uppervp);
38096263Sobrien	if ((um->um_op == UNMNT_BELOW) &&
38196263Sobrien	     VOP_ISLOCKED(um->um_uppervp)) {
38296263Sobrien		loselock = 1;
38396263Sobrien	} else {
38496263Sobrien		VOP_LOCK(um->um_uppervp);
38596263Sobrien		loselock = 0;
38696263Sobrien	}
38796263Sobrien	if (um->um_lowervp)
38896263Sobrien		VREF(um->um_lowervp);
38996263Sobrien	error = union_allocvp(vpp, mp,
39096263Sobrien			      (struct vnode *) 0,
39196263Sobrien			      (struct vnode *) 0,
39296263Sobrien			      (struct componentname *) 0,
39396263Sobrien			      um->um_uppervp,
39496263Sobrien			      um->um_lowervp);
39596263Sobrien
39696263Sobrien	if (error) {
39796263Sobrien		if (!loselock)
39896263Sobrien			VOP_UNLOCK(um->um_uppervp);
39996263Sobrien		vrele(um->um_uppervp);
40096263Sobrien		if (um->um_lowervp)
40196263Sobrien			vrele(um->um_lowervp);
40296263Sobrien	} else {
40396263Sobrien		(*vpp)->v_flag |= VROOT;
40496263Sobrien		if (loselock)
40596263Sobrien			VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
40696263Sobrien	}
40796263Sobrien
40896263Sobrien	return (error);
40996263Sobrien}
41096263Sobrien
41196263Sobrienint
41296263Sobrienunion_quotactl(mp, cmd, uid, arg, p)
41396263Sobrien	struct mount *mp;
41496263Sobrien	int cmd;
41596263Sobrien	uid_t uid;
41696263Sobrien	caddr_t arg;
41796263Sobrien	struct proc *p;
41896263Sobrien{
41996263Sobrien
42096263Sobrien	return (EOPNOTSUPP);
42196263Sobrien}
42296263Sobrien
42396263Sobrienint
42496263Sobrienunion_statfs(mp, sbp, p)
42596263Sobrien	struct mount *mp;
42696263Sobrien	struct statfs *sbp;
42796263Sobrien	struct proc *p;
42896263Sobrien{
42996263Sobrien	int error;
43096263Sobrien	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
43196263Sobrien	struct statfs mstat;
43296263Sobrien	int lbsize;
43396263Sobrien
43496263Sobrien#ifdef UNION_DIAGNOSTIC
43596263Sobrien	printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
43696263Sobrien			um->um_lowervp,
43796263Sobrien	       		um->um_uppervp);
43896263Sobrien#endif
43996263Sobrien
44096263Sobrien	bzero(&mstat, sizeof(mstat));
44196263Sobrien
44296263Sobrien	if (um->um_lowervp) {
44396263Sobrien		error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
44496263Sobrien		if (error)
44596263Sobrien			return (error);
44696263Sobrien	}
44796263Sobrien
44896263Sobrien	/* now copy across the "interesting" information and fake the rest */
44996263Sobrien#if 0
45096263Sobrien	sbp->f_type = mstat.f_type;
45196263Sobrien	sbp->f_flags = mstat.f_flags;
45296263Sobrien	sbp->f_bsize = mstat.f_bsize;
45396263Sobrien	sbp->f_iosize = mstat.f_iosize;
45496263Sobrien#endif
45596263Sobrien	lbsize = mstat.f_bsize;
45696263Sobrien	sbp->f_blocks = mstat.f_blocks;
45796263Sobrien	sbp->f_bfree = mstat.f_bfree;
45896263Sobrien	sbp->f_bavail = mstat.f_bavail;
45996263Sobrien	sbp->f_files = mstat.f_files;
46096263Sobrien	sbp->f_ffree = mstat.f_ffree;
46196263Sobrien
46296263Sobrien	error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
46396263Sobrien	if (error)
46496263Sobrien		return (error);
46596263Sobrien
46696263Sobrien	sbp->f_type = MOUNT_UNION;
46796263Sobrien	sbp->f_flags = mstat.f_flags;
46896263Sobrien	sbp->f_bsize = mstat.f_bsize;
46996263Sobrien	sbp->f_iosize = mstat.f_iosize;
47096263Sobrien
47196263Sobrien	/*
47296263Sobrien	 * if the lower and upper blocksizes differ, then frig the
47396263Sobrien	 * block counts so that the sizes reported by df make some
47496263Sobrien	 * kind of sense.  none of this makes sense though.
47596263Sobrien	 */
47696263Sobrien
47796263Sobrien	if (mstat.f_bsize != lbsize) {
47896263Sobrien		sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
47996263Sobrien		sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize;
48096263Sobrien		sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize;
48196263Sobrien	}
48296263Sobrien	sbp->f_blocks += mstat.f_blocks;
48396263Sobrien	sbp->f_bfree += mstat.f_bfree;
48496263Sobrien	sbp->f_bavail += mstat.f_bavail;
48596263Sobrien	sbp->f_files += mstat.f_files;
48696263Sobrien	sbp->f_ffree += mstat.f_ffree;
48796263Sobrien
48896263Sobrien	if (sbp != &mp->mnt_stat) {
48996263Sobrien		bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
49096263Sobrien		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
49196263Sobrien		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
49296263Sobrien	}
49396263Sobrien	return (0);
49496263Sobrien}
49596263Sobrien
49696263Sobrienint
49796263Sobrienunion_sync(mp, waitfor, cred, p)
49896263Sobrien	struct mount *mp;
49996263Sobrien	int waitfor;
50096263Sobrien	struct ucred *cred;
50196263Sobrien	struct proc *p;
50296263Sobrien{
50396263Sobrien
50496263Sobrien	/*
50596263Sobrien	 * XXX - Assumes no data cached at union layer.
50696263Sobrien	 */
50796263Sobrien	return (0);
50896263Sobrien}
50996263Sobrien
51096263Sobrienint
51196263Sobrienunion_vget(mp, ino, vpp)
51296263Sobrien	struct mount *mp;
51396263Sobrien	ino_t ino;
51496263Sobrien	struct vnode **vpp;
51596263Sobrien{
51696263Sobrien
51796263Sobrien	return (EOPNOTSUPP);
51896263Sobrien}
51996263Sobrien
52096263Sobrienint
52196263Sobrienunion_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
52296263Sobrien	struct mount *mp;
52396263Sobrien	struct fid *fidp;
52496263Sobrien	struct mbuf *nam;
52596263Sobrien	struct vnode **vpp;
52696263Sobrien	int *exflagsp;
52796263Sobrien	struct ucred **credanonp;
52896263Sobrien{
52996263Sobrien
53096263Sobrien	return (EOPNOTSUPP);
53196263Sobrien}
53296263Sobrien
53396263Sobrienint
53496263Sobrienunion_vptofh(vp, fhp)
53596263Sobrien	struct vnode *vp;
53696263Sobrien	struct fid *fhp;
53796263Sobrien{
53896263Sobrien
53996263Sobrien	return (EOPNOTSUPP);
54096263Sobrien}
54196263Sobrien
54296263Sobrienint union_init __P((void));
54396263Sobrien
54496263Sobrienstruct vfsops union_vfsops = {
54596263Sobrien	union_mount,
54696263Sobrien	union_start,
54796263Sobrien	union_unmount,
54896263Sobrien	union_root,
54996263Sobrien	union_quotactl,
55096263Sobrien	union_statfs,
55196263Sobrien	union_sync,
55296263Sobrien	union_vget,
55396263Sobrien	union_fhtovp,
55496263Sobrien	union_vptofh,
55596263Sobrien	union_init,
55696263Sobrien};
55796263Sobrien
55896263SobrienVFS_SET(union_vfsops, union, MOUNT_UNION, 0);
55996263Sobrien