1df8bae1dSRodney W. Grimes /* 2996c772fSJohn Dyson * Copyright (c) 1994, 1995 The Regents of the University of California. 3996c772fSJohn Dyson * Copyright (c) 1994, 1995 Jan-Simon Pendry. 4df8bae1dSRodney W. Grimes * All rights reserved. 5df8bae1dSRodney W. Grimes * 6df8bae1dSRodney W. Grimes * This code is derived from software donated to Berkeley by 7df8bae1dSRodney W. Grimes * Jan-Simon Pendry. 8df8bae1dSRodney W. Grimes * 9df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 10df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 11df8bae1dSRodney W. Grimes * are met: 12df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 13df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 14df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 15df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 16df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 17df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 18df8bae1dSRodney W. Grimes * must display the following acknowledgement: 19df8bae1dSRodney W. Grimes * This product includes software developed by the University of 20df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 21df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 22df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 23df8bae1dSRodney W. Grimes * without specific prior written permission. 24df8bae1dSRodney W. Grimes * 25df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35df8bae1dSRodney W. Grimes * SUCH DAMAGE. 36df8bae1dSRodney W. Grimes * 37996c772fSJohn Dyson * @(#)union_vfsops.c 8.20 (Berkeley) 5/20/95 38c3aac50fSPeter Wemm * $FreeBSD$ 39df8bae1dSRodney W. Grimes */ 40df8bae1dSRodney W. Grimes 41df8bae1dSRodney W. Grimes /* 42df8bae1dSRodney W. Grimes * Union Layer 43df8bae1dSRodney W. Grimes */ 44df8bae1dSRodney W. Grimes 45df8bae1dSRodney W. Grimes #include <sys/param.h> 46df8bae1dSRodney W. Grimes #include <sys/systm.h> 47c9b1d604SGarrett Wollman #include <sys/kernel.h> 48805d90f7SJohn Baldwin #include <sys/lock.h> 49805d90f7SJohn Baldwin #include <sys/mutex.h> 50df8bae1dSRodney W. Grimes #include <sys/proc.h> 51df8bae1dSRodney W. Grimes #include <sys/vnode.h> 52df8bae1dSRodney W. Grimes #include <sys/mount.h> 53df8bae1dSRodney W. Grimes #include <sys/namei.h> 54df8bae1dSRodney W. Grimes #include <sys/malloc.h> 55df8bae1dSRodney W. Grimes #include <sys/filedesc.h> 5699d300a1SRuslan Ermilov #include <fs/unionfs/union.h> 57df8bae1dSRodney W. Grimes 58a1c995b6SPoul-Henning Kamp static MALLOC_DEFINE(M_UNIONFSMNT, "UNION mount", "UNION mount structure"); 59a1c995b6SPoul-Henning Kamp 6011caded3SAlfred Perlstein extern int union_init(struct vfsconf *); 61a9f5c04aSMaxime Henrion static int union_mount(struct mount *mp, struct nameidata *ndp, 62a9f5c04aSMaxime Henrion struct thread *td); 6311caded3SAlfred Perlstein static int union_root(struct mount *mp, struct vnode **vpp); 6411caded3SAlfred Perlstein static int union_statfs(struct mount *mp, struct statfs *sbp, 6511caded3SAlfred Perlstein struct thread *td); 6611caded3SAlfred Perlstein static int union_unmount(struct mount *mp, int mntflags, 6711caded3SAlfred Perlstein struct thread *td); 68b5e8ce9fSBruce Evans 69df8bae1dSRodney W. Grimes /* 70df8bae1dSRodney W. Grimes * Mount union filesystem 71df8bae1dSRodney W. Grimes */ 7280b301c3SPoul-Henning Kamp static int 73a9f5c04aSMaxime Henrion union_mount(mp, ndp, td) 74df8bae1dSRodney W. Grimes struct mount *mp; 75df8bae1dSRodney W. Grimes struct nameidata *ndp; 76b40ce416SJulian Elischer struct thread *td; 77df8bae1dSRodney W. Grimes { 78df8bae1dSRodney W. Grimes int error = 0; 79a9f5c04aSMaxime Henrion struct vfsoptlist *opts; 80df8bae1dSRodney W. Grimes struct vnode *lowerrootvp = NULLVP; 81df8bae1dSRodney W. Grimes struct vnode *upperrootvp = NULLVP; 82996c772fSJohn Dyson struct union_mount *um = 0; 83df8bae1dSRodney W. Grimes struct ucred *cred = 0; 84a9f5c04aSMaxime Henrion char *cp = 0, *target; 85a9f5c04aSMaxime Henrion int *flags; 86df8bae1dSRodney W. Grimes int len; 87df8bae1dSRodney W. Grimes u_int size; 88df8bae1dSRodney W. Grimes 892a31267eSMatthew Dillon UDEBUG(("union_mount(mp = %p)\n", (void *)mp)); 90df8bae1dSRodney W. Grimes 91a9f5c04aSMaxime Henrion opts = mp->mnt_optnew; 92df8bae1dSRodney W. Grimes /* 9381bca6ddSKATO Takenori * Disable clustered write, otherwise system becomes unstable. 9481bca6ddSKATO Takenori */ 9581bca6ddSKATO Takenori mp->mnt_flag |= MNT_NOCLUSTERW; 9681bca6ddSKATO Takenori 9781bca6ddSKATO Takenori /* 98df8bae1dSRodney W. Grimes * Update is a no-op 99df8bae1dSRodney W. Grimes */ 100a9f5c04aSMaxime Henrion if (mp->mnt_flag & MNT_UPDATE) 101df8bae1dSRodney W. Grimes /* 102df8bae1dSRodney W. Grimes * Need to provide. 103df8bae1dSRodney W. Grimes * 1. a way to convert between rdonly and rdwr mounts. 104df8bae1dSRodney W. Grimes * 2. support for nfs exports. 105df8bae1dSRodney W. Grimes */ 106a9f5c04aSMaxime Henrion return (EOPNOTSUPP); 107df8bae1dSRodney W. Grimes 108df8bae1dSRodney W. Grimes /* 109a9f5c04aSMaxime Henrion * Get arguments. 110df8bae1dSRodney W. Grimes */ 111a9f5c04aSMaxime Henrion error = vfs_getopt(opts, "target", (void **)&target, &len); 112a9f5c04aSMaxime Henrion if (error || target[len - 1] != '\0') 113a9f5c04aSMaxime Henrion return (EINVAL); 114a9f5c04aSMaxime Henrion 115a9f5c04aSMaxime Henrion error = vfs_getopt(opts, "unionflags", (void **)&flags, &len); 116a9f5c04aSMaxime Henrion if (error || len != sizeof(int)) 117a9f5c04aSMaxime Henrion return (EINVAL); 118df8bae1dSRodney W. Grimes 1192a31267eSMatthew Dillon /* 1202a31267eSMatthew Dillon * Obtain lower vnode. Vnode is stored in mp->mnt_vnodecovered. 1212a31267eSMatthew Dillon * We need to reference it but not lock it. 1222a31267eSMatthew Dillon */ 1232a31267eSMatthew Dillon 124df8bae1dSRodney W. Grimes lowerrootvp = mp->mnt_vnodecovered; 125df8bae1dSRodney W. Grimes VREF(lowerrootvp); 126df8bae1dSRodney W. Grimes 1272a31267eSMatthew Dillon #if 0 128df8bae1dSRodney W. Grimes /* 1296db918e3SKATO Takenori * Unlock lower node to avoid deadlock. 1306db918e3SKATO Takenori */ 131afc2a558SKATO Takenori if (lowerrootvp->v_op == union_vnodeop_p) 132b40ce416SJulian Elischer VOP_UNLOCK(lowerrootvp, 0, td); 1332a31267eSMatthew Dillon #endif 1346db918e3SKATO Takenori 1356db918e3SKATO Takenori /* 1362a31267eSMatthew Dillon * Obtain upper vnode by calling namei() on the path. The 1372a31267eSMatthew Dillon * upperrootvp will be turned referenced but not locked. 138df8bae1dSRodney W. Grimes */ 139a9f5c04aSMaxime Henrion NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT, UIO_SYSSPACE, target, td); 140df8bae1dSRodney W. Grimes 1413a773ad0SPoul-Henning Kamp error = namei(ndp); 1422a31267eSMatthew Dillon 1432a31267eSMatthew Dillon #if 0 144afc2a558SKATO Takenori if (lowerrootvp->v_op == union_vnodeop_p) 145b40ce416SJulian Elischer vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY, td); 1462a31267eSMatthew Dillon #endif 1473a773ad0SPoul-Henning Kamp if (error) 148df8bae1dSRodney W. Grimes goto bad; 149df8bae1dSRodney W. Grimes 150762e6b85SEivind Eklund NDFREE(ndp, NDF_ONLY_PNBUF); 151df8bae1dSRodney W. Grimes upperrootvp = ndp->ni_vp; 152df8bae1dSRodney W. Grimes vrele(ndp->ni_dvp); 153df8bae1dSRodney W. Grimes ndp->ni_dvp = NULL; 154df8bae1dSRodney W. Grimes 1556bdfe06aSEivind Eklund UDEBUG(("mount_root UPPERVP %p locked = %d\n", upperrootvp, 1566bdfe06aSEivind Eklund VOP_ISLOCKED(upperrootvp, NULL))); 1572a31267eSMatthew Dillon 1586db918e3SKATO Takenori /* 1596db918e3SKATO Takenori * Check multi union mount to avoid `lock myself again' panic. 1602a31267eSMatthew Dillon * Also require that it be a directory. 1616db918e3SKATO Takenori */ 1626db918e3SKATO Takenori if (upperrootvp == VTOUNION(lowerrootvp)->un_uppervp) { 163747e9157SKATO Takenori #ifdef DIAGNOSTIC 164747e9157SKATO Takenori printf("union_mount: multi union mount?\n"); 165747e9157SKATO Takenori #endif 1666db918e3SKATO Takenori error = EDEADLK; 1676db918e3SKATO Takenori goto bad; 1686db918e3SKATO Takenori } 1696db918e3SKATO Takenori 170df8bae1dSRodney W. Grimes if (upperrootvp->v_type != VDIR) { 171df8bae1dSRodney W. Grimes error = EINVAL; 172df8bae1dSRodney W. Grimes goto bad; 173df8bae1dSRodney W. Grimes } 174df8bae1dSRodney W. Grimes 175df8bae1dSRodney W. Grimes /* 1762a31267eSMatthew Dillon * Allocate our union_mount structure and populate the fields. 1772a31267eSMatthew Dillon * The vnode references are stored in the union_mount as held, 1782a31267eSMatthew Dillon * unlocked references. Depending on the _BELOW flag, the 1792a31267eSMatthew Dillon * filesystems are viewed in a different order. In effect this 1802a31267eSMatthew Dillon * is the same as providing a mount-under option to the mount 1812a31267eSMatthew Dillon * syscall. 182df8bae1dSRodney W. Grimes */ 183df8bae1dSRodney W. Grimes 1842a31267eSMatthew Dillon um = (struct union_mount *) malloc(sizeof(struct union_mount), 1857cc0979fSDavid Malone M_UNIONFSMNT, M_WAITOK | M_ZERO); 1862a31267eSMatthew Dillon 187a9f5c04aSMaxime Henrion um->um_op = *flags & UNMNT_OPMASK; 1882a31267eSMatthew Dillon 189df8bae1dSRodney W. Grimes switch (um->um_op) { 190df8bae1dSRodney W. Grimes case UNMNT_ABOVE: 191df8bae1dSRodney W. Grimes um->um_lowervp = lowerrootvp; 192df8bae1dSRodney W. Grimes um->um_uppervp = upperrootvp; 1932a31267eSMatthew Dillon upperrootvp = NULL; 1942a31267eSMatthew Dillon lowerrootvp = NULL; 195df8bae1dSRodney W. Grimes break; 196df8bae1dSRodney W. Grimes 197df8bae1dSRodney W. Grimes case UNMNT_BELOW: 198df8bae1dSRodney W. Grimes um->um_lowervp = upperrootvp; 199df8bae1dSRodney W. Grimes um->um_uppervp = lowerrootvp; 2002a31267eSMatthew Dillon upperrootvp = NULL; 2012a31267eSMatthew Dillon lowerrootvp = NULL; 202df8bae1dSRodney W. Grimes break; 203df8bae1dSRodney W. Grimes 204df8bae1dSRodney W. Grimes case UNMNT_REPLACE: 205df8bae1dSRodney W. Grimes vrele(lowerrootvp); 2062a31267eSMatthew Dillon lowerrootvp = NULL; 207df8bae1dSRodney W. Grimes um->um_uppervp = upperrootvp; 208df8bae1dSRodney W. Grimes um->um_lowervp = lowerrootvp; 2092a31267eSMatthew Dillon upperrootvp = NULL; 210df8bae1dSRodney W. Grimes break; 211df8bae1dSRodney W. Grimes 212df8bae1dSRodney W. Grimes default: 213df8bae1dSRodney W. Grimes error = EINVAL; 214df8bae1dSRodney W. Grimes goto bad; 215df8bae1dSRodney W. Grimes } 216df8bae1dSRodney W. Grimes 217996c772fSJohn Dyson /* 218996c772fSJohn Dyson * Unless the mount is readonly, ensure that the top layer 219996c772fSJohn Dyson * supports whiteout operations 220996c772fSJohn Dyson */ 221996c772fSJohn Dyson if ((mp->mnt_flag & MNT_RDONLY) == 0) { 2222a31267eSMatthew Dillon error = VOP_WHITEOUT(um->um_uppervp, NULL, LOOKUP); 223996c772fSJohn Dyson if (error) 224996c772fSJohn Dyson goto bad; 225996c772fSJohn Dyson } 226996c772fSJohn Dyson 227a854ed98SJohn Baldwin um->um_cred = crhold(td->td_ucred); 228426da3bcSAlfred Perlstein FILEDESC_LOCK(td->td_proc->p_fd); 229b40ce416SJulian Elischer um->um_cmode = UN_DIRMODE &~ td->td_proc->p_fd->fd_cmask; 230426da3bcSAlfred Perlstein FILEDESC_UNLOCK(td->td_proc->p_fd); 231df8bae1dSRodney W. Grimes 232df8bae1dSRodney W. Grimes /* 233df8bae1dSRodney W. Grimes * Depending on what you think the MNT_LOCAL flag might mean, 234df8bae1dSRodney W. Grimes * you may want the && to be || on the conditional below. 235df8bae1dSRodney W. Grimes * At the moment it has been defined that the filesystem is 236df8bae1dSRodney W. Grimes * only local if it is all local, ie the MNT_LOCAL flag implies 237df8bae1dSRodney W. Grimes * that the entire namespace is local. If you think the MNT_LOCAL 238df8bae1dSRodney W. Grimes * flag implies that some of the files might be stored locally 239df8bae1dSRodney W. Grimes * then you will want to change the conditional. 240df8bae1dSRodney W. Grimes */ 241df8bae1dSRodney W. Grimes if (um->um_op == UNMNT_ABOVE) { 242df8bae1dSRodney W. Grimes if (((um->um_lowervp == NULLVP) || 243df8bae1dSRodney W. Grimes (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) && 244df8bae1dSRodney W. Grimes (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) 245df8bae1dSRodney W. Grimes mp->mnt_flag |= MNT_LOCAL; 246df8bae1dSRodney W. Grimes } 247df8bae1dSRodney W. Grimes 248df8bae1dSRodney W. Grimes /* 249df8bae1dSRodney W. Grimes * Copy in the upper layer's RDONLY flag. This is for the benefit 250df8bae1dSRodney W. Grimes * of lookup() which explicitly checks the flag, rather than asking 251dc733423SDag-Erling Smørgrav * the filesystem for its own opinion. This means, that an update 252df8bae1dSRodney W. Grimes * mount of the underlying filesystem to go from rdonly to rdwr 253df8bae1dSRodney W. Grimes * will leave the unioned view as read-only. 254df8bae1dSRodney W. Grimes */ 255df8bae1dSRodney W. Grimes mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY); 256df8bae1dSRodney W. Grimes 257df8bae1dSRodney W. Grimes mp->mnt_data = (qaddr_t) um; 258996c772fSJohn Dyson vfs_getnewfsid(mp); 259df8bae1dSRodney W. Grimes 260df8bae1dSRodney W. Grimes switch (um->um_op) { 261df8bae1dSRodney W. Grimes case UNMNT_ABOVE: 262996c772fSJohn Dyson cp = "<above>:"; 263df8bae1dSRodney W. Grimes break; 264df8bae1dSRodney W. Grimes case UNMNT_BELOW: 265996c772fSJohn Dyson cp = "<below>:"; 266df8bae1dSRodney W. Grimes break; 267df8bae1dSRodney W. Grimes case UNMNT_REPLACE: 268df8bae1dSRodney W. Grimes cp = ""; 269df8bae1dSRodney W. Grimes break; 270df8bae1dSRodney W. Grimes } 271df8bae1dSRodney W. Grimes len = strlen(cp); 272df8bae1dSRodney W. Grimes bcopy(cp, mp->mnt_stat.f_mntfromname, len); 273df8bae1dSRodney W. Grimes 274df8bae1dSRodney W. Grimes cp = mp->mnt_stat.f_mntfromname + len; 275df8bae1dSRodney W. Grimes len = MNAMELEN - len; 276df8bae1dSRodney W. Grimes 277a9f5c04aSMaxime Henrion (void) copystr(target, cp, len - 1, &size); 278df8bae1dSRodney W. Grimes bzero(cp + size, len - size); 279df8bae1dSRodney W. Grimes 280b40ce416SJulian Elischer (void)union_statfs(mp, &mp->mnt_stat, td); 281c4a7b7e1SDavid Greenman 2822a31267eSMatthew Dillon UDEBUG(("union_mount: from %s, on %s\n", 2832a31267eSMatthew Dillon mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname)); 284df8bae1dSRodney W. Grimes return (0); 285df8bae1dSRodney W. Grimes 286df8bae1dSRodney W. Grimes bad: 2872a31267eSMatthew Dillon if (um) { 2882a31267eSMatthew Dillon if (um->um_uppervp) 2892a31267eSMatthew Dillon vrele(um->um_uppervp); 2902a31267eSMatthew Dillon if (um->um_lowervp) 2912a31267eSMatthew Dillon vrele(um->um_lowervp); 2922a31267eSMatthew Dillon /* XXX other fields */ 293a1c995b6SPoul-Henning Kamp free(um, M_UNIONFSMNT); 2942a31267eSMatthew Dillon } 295df8bae1dSRodney W. Grimes if (cred) 296df8bae1dSRodney W. Grimes crfree(cred); 297df8bae1dSRodney W. Grimes if (upperrootvp) 298df8bae1dSRodney W. Grimes vrele(upperrootvp); 299df8bae1dSRodney W. Grimes if (lowerrootvp) 300df8bae1dSRodney W. Grimes vrele(lowerrootvp); 301df8bae1dSRodney W. Grimes return (error); 302df8bae1dSRodney W. Grimes } 303df8bae1dSRodney W. Grimes 304df8bae1dSRodney W. Grimes /* 305df8bae1dSRodney W. Grimes * Free reference to union layer 306df8bae1dSRodney W. Grimes */ 30780b301c3SPoul-Henning Kamp static int 308b40ce416SJulian Elischer union_unmount(mp, mntflags, td) 309df8bae1dSRodney W. Grimes struct mount *mp; 310df8bae1dSRodney W. Grimes int mntflags; 311b40ce416SJulian Elischer struct thread *td; 312df8bae1dSRodney W. Grimes { 313df8bae1dSRodney W. Grimes struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 314df8bae1dSRodney W. Grimes int error; 315996c772fSJohn Dyson int freeing; 316df8bae1dSRodney W. Grimes int flags = 0; 317df8bae1dSRodney W. Grimes 3182a31267eSMatthew Dillon UDEBUG(("union_unmount(mp = %p)\n", (void *)mp)); 319df8bae1dSRodney W. Grimes 320996c772fSJohn Dyson if (mntflags & MNT_FORCE) 321996c772fSJohn Dyson flags |= FORCECLOSE; 322996c772fSJohn Dyson 323996c772fSJohn Dyson /* 324996c772fSJohn Dyson * Keep flushing vnodes from the mount list. 325996c772fSJohn Dyson * This is needed because of the un_pvp held 326996c772fSJohn Dyson * reference to the parent vnode. 327996c772fSJohn Dyson * If more vnodes have been freed on a given pass, 328996c772fSJohn Dyson * the try again. The loop will iterate at most 329996c772fSJohn Dyson * (d) times, where (d) is the maximum tree depth 330996c772fSJohn Dyson * in the filesystem. 331996c772fSJohn Dyson */ 3320864ef1eSIan Dowse for (freeing = 0; (error = vflush(mp, 0, flags)) != 0;) { 333996c772fSJohn Dyson struct vnode *vp; 334996c772fSJohn Dyson int n; 335996c772fSJohn Dyson 336996c772fSJohn Dyson /* count #vnodes held on mount list */ 337805d90f7SJohn Baldwin mtx_lock(&mntvnode_mtx); 3380864ef1eSIan Dowse n = 0; 339c72ccd01SMatthew Dillon TAILQ_FOREACH(vp, &mp->mnt_nvnodelist, v_nmntvnodes) 340996c772fSJohn Dyson n++; 341805d90f7SJohn Baldwin mtx_unlock(&mntvnode_mtx); 342996c772fSJohn Dyson 343996c772fSJohn Dyson /* if this is unchanged then stop */ 344996c772fSJohn Dyson if (n == freeing) 345996c772fSJohn Dyson break; 346996c772fSJohn Dyson 347996c772fSJohn Dyson /* otherwise try once more time */ 348996c772fSJohn Dyson freeing = n; 349df8bae1dSRodney W. Grimes } 350df8bae1dSRodney W. Grimes 3510864ef1eSIan Dowse /* If the most recent vflush failed, the filesystem is still busy. */ 3520864ef1eSIan Dowse if (error) 3530864ef1eSIan Dowse return (error); 354df8bae1dSRodney W. Grimes 355df8bae1dSRodney W. Grimes /* 356df8bae1dSRodney W. Grimes * Discard references to upper and lower target vnodes. 357df8bae1dSRodney W. Grimes */ 358df8bae1dSRodney W. Grimes if (um->um_lowervp) 359df8bae1dSRodney W. Grimes vrele(um->um_lowervp); 360df8bae1dSRodney W. Grimes vrele(um->um_uppervp); 361df8bae1dSRodney W. Grimes crfree(um->um_cred); 362df8bae1dSRodney W. Grimes /* 363df8bae1dSRodney W. Grimes * Finally, throw away the union_mount structure 364df8bae1dSRodney W. Grimes */ 365a1c995b6SPoul-Henning Kamp free(mp->mnt_data, M_UNIONFSMNT); /* XXX */ 366df8bae1dSRodney W. Grimes mp->mnt_data = 0; 367df8bae1dSRodney W. Grimes return (0); 368df8bae1dSRodney W. Grimes } 369df8bae1dSRodney W. Grimes 37080b301c3SPoul-Henning Kamp static int 371df8bae1dSRodney W. Grimes union_root(mp, vpp) 372df8bae1dSRodney W. Grimes struct mount *mp; 373df8bae1dSRodney W. Grimes struct vnode **vpp; 374df8bae1dSRodney W. Grimes { 375df8bae1dSRodney W. Grimes struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 376df8bae1dSRodney W. Grimes int error; 377df8bae1dSRodney W. Grimes 378df8bae1dSRodney W. Grimes /* 3792a31267eSMatthew Dillon * Supply an unlocked reference to um_uppervp and to um_lowervp. It 3802a31267eSMatthew Dillon * is possible for um_uppervp to be locked without the associated 3812a31267eSMatthew Dillon * root union_node being locked. We let union_allocvp() deal with 3822a31267eSMatthew Dillon * it. 383df8bae1dSRodney W. Grimes */ 3846bdfe06aSEivind Eklund UDEBUG(("union_root UPPERVP %p locked = %d\n", um->um_uppervp, 3856bdfe06aSEivind Eklund VOP_ISLOCKED(um->um_uppervp, NULL))); 3862a31267eSMatthew Dillon 387df8bae1dSRodney W. Grimes VREF(um->um_uppervp); 388df8bae1dSRodney W. Grimes if (um->um_lowervp) 389df8bae1dSRodney W. Grimes VREF(um->um_lowervp); 390df8bae1dSRodney W. Grimes 3912a31267eSMatthew Dillon error = union_allocvp(vpp, mp, NULLVP, NULLVP, NULL, 3922a31267eSMatthew Dillon um->um_uppervp, um->um_lowervp, 1); 3932a31267eSMatthew Dillon UDEBUG(("error %d\n", error)); 3946bdfe06aSEivind Eklund UDEBUG(("union_root2 UPPERVP %p locked = %d\n", um->um_uppervp, 3956bdfe06aSEivind Eklund VOP_ISLOCKED(um->um_uppervp, NULL))); 396df8bae1dSRodney W. Grimes 397df8bae1dSRodney W. Grimes return (error); 398df8bae1dSRodney W. Grimes } 399df8bae1dSRodney W. Grimes 40080b301c3SPoul-Henning Kamp static int 401b40ce416SJulian Elischer union_statfs(mp, sbp, td) 402df8bae1dSRodney W. Grimes struct mount *mp; 403df8bae1dSRodney W. Grimes struct statfs *sbp; 404b40ce416SJulian Elischer struct thread *td; 405df8bae1dSRodney W. Grimes { 406df8bae1dSRodney W. Grimes int error; 407df8bae1dSRodney W. Grimes struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 408df8bae1dSRodney W. Grimes struct statfs mstat; 409df8bae1dSRodney W. Grimes int lbsize; 410df8bae1dSRodney W. Grimes 4112a31267eSMatthew Dillon UDEBUG(("union_statfs(mp = %p, lvp = %p, uvp = %p)\n", 4122a31267eSMatthew Dillon (void *)mp, (void *)um->um_lowervp, (void *)um->um_uppervp)); 413df8bae1dSRodney W. Grimes 414df8bae1dSRodney W. Grimes bzero(&mstat, sizeof(mstat)); 415df8bae1dSRodney W. Grimes 416df8bae1dSRodney W. Grimes if (um->um_lowervp) { 417b40ce416SJulian Elischer error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, td); 418df8bae1dSRodney W. Grimes if (error) 419df8bae1dSRodney W. Grimes return (error); 420df8bae1dSRodney W. Grimes } 421df8bae1dSRodney W. Grimes 422df8bae1dSRodney W. Grimes /* now copy across the "interesting" information and fake the rest */ 423df8bae1dSRodney W. Grimes #if 0 424df8bae1dSRodney W. Grimes sbp->f_type = mstat.f_type; 425df8bae1dSRodney W. Grimes sbp->f_flags = mstat.f_flags; 426df8bae1dSRodney W. Grimes sbp->f_bsize = mstat.f_bsize; 427df8bae1dSRodney W. Grimes sbp->f_iosize = mstat.f_iosize; 428df8bae1dSRodney W. Grimes #endif 429df8bae1dSRodney W. Grimes lbsize = mstat.f_bsize; 430df8bae1dSRodney W. Grimes sbp->f_blocks = mstat.f_blocks; 431df8bae1dSRodney W. Grimes sbp->f_bfree = mstat.f_bfree; 432df8bae1dSRodney W. Grimes sbp->f_bavail = mstat.f_bavail; 433df8bae1dSRodney W. Grimes sbp->f_files = mstat.f_files; 434df8bae1dSRodney W. Grimes sbp->f_ffree = mstat.f_ffree; 435df8bae1dSRodney W. Grimes 436b40ce416SJulian Elischer error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, td); 437df8bae1dSRodney W. Grimes if (error) 438df8bae1dSRodney W. Grimes return (error); 439df8bae1dSRodney W. Grimes 440df8bae1dSRodney W. Grimes sbp->f_flags = mstat.f_flags; 441df8bae1dSRodney W. Grimes sbp->f_bsize = mstat.f_bsize; 442df8bae1dSRodney W. Grimes sbp->f_iosize = mstat.f_iosize; 443df8bae1dSRodney W. Grimes 444df8bae1dSRodney W. Grimes /* 445df8bae1dSRodney W. Grimes * if the lower and upper blocksizes differ, then frig the 446df8bae1dSRodney W. Grimes * block counts so that the sizes reported by df make some 447df8bae1dSRodney W. Grimes * kind of sense. none of this makes sense though. 448df8bae1dSRodney W. Grimes */ 449df8bae1dSRodney W. Grimes 450996c772fSJohn Dyson if (mstat.f_bsize != lbsize) 451c9bf0111SKATO Takenori sbp->f_blocks = ((off_t) sbp->f_blocks * lbsize) / mstat.f_bsize; 452996c772fSJohn Dyson 453996c772fSJohn Dyson /* 454996c772fSJohn Dyson * The "total" fields count total resources in all layers, 455996c772fSJohn Dyson * the "free" fields count only those resources which are 456996c772fSJohn Dyson * free in the upper layer (since only the upper layer 457996c772fSJohn Dyson * is writeable). 458996c772fSJohn Dyson */ 459df8bae1dSRodney W. Grimes sbp->f_blocks += mstat.f_blocks; 460996c772fSJohn Dyson sbp->f_bfree = mstat.f_bfree; 461996c772fSJohn Dyson sbp->f_bavail = mstat.f_bavail; 462df8bae1dSRodney W. Grimes sbp->f_files += mstat.f_files; 463996c772fSJohn Dyson sbp->f_ffree = mstat.f_ffree; 464df8bae1dSRodney W. Grimes 465df8bae1dSRodney W. Grimes if (sbp != &mp->mnt_stat) { 466996c772fSJohn Dyson sbp->f_type = mp->mnt_vfc->vfc_typenum; 467df8bae1dSRodney W. Grimes bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); 468df8bae1dSRodney W. Grimes bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 469df8bae1dSRodney W. Grimes bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 470df8bae1dSRodney W. Grimes } 471df8bae1dSRodney W. Grimes return (0); 472df8bae1dSRodney W. Grimes } 473df8bae1dSRodney W. Grimes 47480b301c3SPoul-Henning Kamp static struct vfsops union_vfsops = { 475a9f5c04aSMaxime Henrion NULL, 476c24fda81SAlfred Perlstein vfs_stdstart, /* underlying start already done */ 477df8bae1dSRodney W. Grimes union_unmount, 478df8bae1dSRodney W. Grimes union_root, 4795a5fccc8SAlfred Perlstein vfs_stdquotactl, 480df8bae1dSRodney W. Grimes union_statfs, 4815a5fccc8SAlfred Perlstein vfs_stdsync, /* XXX assumes no cached data on union level */ 4825a5fccc8SAlfred Perlstein vfs_stdvget, 4835a5fccc8SAlfred Perlstein vfs_stdfhtovp, 484c24fda81SAlfred Perlstein vfs_stdcheckexp, 4855a5fccc8SAlfred Perlstein vfs_stdvptofh, 486df8bae1dSRodney W. Grimes union_init, 48791f37dcbSRobert Watson vfs_stduninit, 48891f37dcbSRobert Watson vfs_stdextattrctl, 489a9f5c04aSMaxime Henrion union_mount, 490df8bae1dSRodney W. Grimes }; 491c901836cSGarrett Wollman 492c7b23e0fSRuslan Ermilov VFS_SET(union_vfsops, unionfs, VFCF_LOOPBACK); 493