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