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 38a1c995b6SPoul-Henning Kamp * $Id: union_vfsops.c,v 1.20 1997/09/27 13:39:29 kato Exp $ 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> 48df8bae1dSRodney W. Grimes #include <sys/proc.h> 49df8bae1dSRodney W. Grimes #include <sys/vnode.h> 50df8bae1dSRodney W. Grimes #include <sys/mount.h> 51df8bae1dSRodney W. Grimes #include <sys/namei.h> 52df8bae1dSRodney W. Grimes #include <sys/malloc.h> 53df8bae1dSRodney W. Grimes #include <sys/filedesc.h> 54df8bae1dSRodney W. Grimes #include <miscfs/union/union.h> 55df8bae1dSRodney W. Grimes 56a1c995b6SPoul-Henning Kamp static MALLOC_DEFINE(M_UNIONFSMNT, "UNION mount", "UNION mount structure"); 57a1c995b6SPoul-Henning Kamp 58996c772fSJohn Dyson extern int union_init __P((struct vfsconf *)); 599b5e8b3aSBruce Evans 609b5e8b3aSBruce Evans extern int union_fhtovp __P((struct mount *mp, struct fid *fidp, 619b5e8b3aSBruce Evans struct mbuf *nam, struct vnode **vpp, 629b5e8b3aSBruce Evans int *exflagsp, struct ucred **credanonp)); 639b5e8b3aSBruce Evans extern int union_mount __P((struct mount *mp, char *path, caddr_t data, 649b5e8b3aSBruce Evans struct nameidata *ndp, struct proc *p)); 659b5e8b3aSBruce Evans extern int union_quotactl __P((struct mount *mp, int cmd, uid_t uid, 669b5e8b3aSBruce Evans caddr_t arg, struct proc *p)); 679b5e8b3aSBruce Evans extern int union_root __P((struct mount *mp, struct vnode **vpp)); 689b5e8b3aSBruce Evans extern int union_start __P((struct mount *mp, int flags, struct proc *p)); 699b5e8b3aSBruce Evans extern int union_statfs __P((struct mount *mp, struct statfs *sbp, 709b5e8b3aSBruce Evans struct proc *p)); 719b5e8b3aSBruce Evans extern int union_sync __P((struct mount *mp, int waitfor, 729b5e8b3aSBruce Evans struct ucred *cred, struct proc *p)); 739b5e8b3aSBruce Evans extern int union_unmount __P((struct mount *mp, int mntflags, 749b5e8b3aSBruce Evans struct proc *p)); 759b5e8b3aSBruce Evans extern int union_vget __P((struct mount *mp, ino_t ino, 769b5e8b3aSBruce Evans struct vnode **vpp)); 779b5e8b3aSBruce Evans extern int union_vptofh __P((struct vnode *vp, struct fid *fhp)); 78b5e8ce9fSBruce Evans 79df8bae1dSRodney W. Grimes /* 80df8bae1dSRodney W. Grimes * Mount union filesystem 81df8bae1dSRodney W. Grimes */ 82df8bae1dSRodney W. Grimes int 83df8bae1dSRodney W. Grimes union_mount(mp, path, data, ndp, p) 84df8bae1dSRodney W. Grimes struct mount *mp; 85df8bae1dSRodney W. Grimes char *path; 86df8bae1dSRodney W. Grimes caddr_t data; 87df8bae1dSRodney W. Grimes struct nameidata *ndp; 88df8bae1dSRodney W. Grimes struct proc *p; 89df8bae1dSRodney W. Grimes { 90df8bae1dSRodney W. Grimes int error = 0; 91df8bae1dSRodney W. Grimes struct union_args args; 92df8bae1dSRodney W. Grimes struct vnode *lowerrootvp = NULLVP; 93df8bae1dSRodney W. Grimes struct vnode *upperrootvp = NULLVP; 94996c772fSJohn Dyson struct union_mount *um = 0; 95df8bae1dSRodney W. Grimes struct ucred *cred = 0; 96df8bae1dSRodney W. Grimes struct ucred *scred; 97df8bae1dSRodney W. Grimes struct vattr va; 9826f9a767SRodney W. Grimes char *cp = 0; 99df8bae1dSRodney W. Grimes int len; 100df8bae1dSRodney W. Grimes u_int size; 1016db918e3SKATO Takenori int islowerunlocked = 0; 102df8bae1dSRodney W. Grimes 103df8bae1dSRodney W. Grimes #ifdef UNION_DIAGNOSTIC 104df8bae1dSRodney W. Grimes printf("union_mount(mp = %x)\n", mp); 105df8bae1dSRodney W. Grimes #endif 106df8bae1dSRodney W. Grimes 107df8bae1dSRodney W. Grimes /* 10881bca6ddSKATO Takenori * Disable clustered write, otherwise system becomes unstable. 10981bca6ddSKATO Takenori */ 11081bca6ddSKATO Takenori mp->mnt_flag |= MNT_NOCLUSTERW; 11181bca6ddSKATO Takenori 11281bca6ddSKATO Takenori /* 113df8bae1dSRodney W. Grimes * Update is a no-op 114df8bae1dSRodney W. Grimes */ 115df8bae1dSRodney W. Grimes if (mp->mnt_flag & MNT_UPDATE) { 116df8bae1dSRodney W. Grimes /* 117df8bae1dSRodney W. Grimes * Need to provide. 118df8bae1dSRodney W. Grimes * 1. a way to convert between rdonly and rdwr mounts. 119df8bae1dSRodney W. Grimes * 2. support for nfs exports. 120df8bae1dSRodney W. Grimes */ 121df8bae1dSRodney W. Grimes error = EOPNOTSUPP; 122df8bae1dSRodney W. Grimes goto bad; 123df8bae1dSRodney W. Grimes } 124df8bae1dSRodney W. Grimes 125df8bae1dSRodney W. Grimes /* 126df8bae1dSRodney W. Grimes * Get argument 127df8bae1dSRodney W. Grimes */ 1283a773ad0SPoul-Henning Kamp error = copyin(data, (caddr_t)&args, sizeof(struct union_args)); 1293a773ad0SPoul-Henning Kamp if (error) 130df8bae1dSRodney W. Grimes goto bad; 131df8bae1dSRodney W. Grimes 132df8bae1dSRodney W. Grimes lowerrootvp = mp->mnt_vnodecovered; 133df8bae1dSRodney W. Grimes VREF(lowerrootvp); 134df8bae1dSRodney W. Grimes 135df8bae1dSRodney W. Grimes /* 1366db918e3SKATO Takenori * Unlock lower node to avoid deadlock. 1376db918e3SKATO Takenori * (XXX) VOP_ISLOCKED is needed? 1386db918e3SKATO Takenori */ 1396db918e3SKATO Takenori if ((lowerrootvp->v_op == union_vnodeop_p) && VOP_ISLOCKED(lowerrootvp)) { 1406db918e3SKATO Takenori VOP_UNLOCK(lowerrootvp, 0, p); 1416db918e3SKATO Takenori islowerunlocked = 1; 1426db918e3SKATO Takenori } 1436db918e3SKATO Takenori 1446db918e3SKATO Takenori /* 145996c772fSJohn Dyson * Find upper node. 146df8bae1dSRodney W. Grimes */ 147df8bae1dSRodney W. Grimes NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT, 148df8bae1dSRodney W. Grimes UIO_USERSPACE, args.target, p); 149df8bae1dSRodney W. Grimes 1503a773ad0SPoul-Henning Kamp error = namei(ndp); 1516db918e3SKATO Takenori /* 1526db918e3SKATO Takenori * Re-lock vnode. 1536db918e3SKATO Takenori * (XXX) VOP_ISLOCKED is needed? 1546db918e3SKATO Takenori */ 1556db918e3SKATO Takenori if (islowerunlocked && !VOP_ISLOCKED(lowerrootvp)) 1566db918e3SKATO Takenori vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY, p); 1573a773ad0SPoul-Henning Kamp if (error) 158df8bae1dSRodney W. Grimes goto bad; 159df8bae1dSRodney W. Grimes 160df8bae1dSRodney W. Grimes upperrootvp = ndp->ni_vp; 161df8bae1dSRodney W. Grimes vrele(ndp->ni_dvp); 162df8bae1dSRodney W. Grimes ndp->ni_dvp = NULL; 163df8bae1dSRodney W. Grimes 1646db918e3SKATO Takenori /* 1656db918e3SKATO Takenori * Check multi union mount to avoid `lock myself again' panic. 1666db918e3SKATO Takenori */ 1676db918e3SKATO Takenori if (upperrootvp == VTOUNION(lowerrootvp)->un_uppervp) { 168747e9157SKATO Takenori #ifdef DIAGNOSTIC 169747e9157SKATO Takenori printf("union_mount: multi union mount?\n"); 170747e9157SKATO Takenori #endif 1716db918e3SKATO Takenori error = EDEADLK; 1726db918e3SKATO Takenori goto bad; 1736db918e3SKATO Takenori } 1746db918e3SKATO Takenori 175df8bae1dSRodney W. Grimes if (upperrootvp->v_type != VDIR) { 176df8bae1dSRodney W. Grimes error = EINVAL; 177df8bae1dSRodney W. Grimes goto bad; 178df8bae1dSRodney W. Grimes } 179df8bae1dSRodney W. Grimes 180df8bae1dSRodney W. Grimes um = (struct union_mount *) malloc(sizeof(struct union_mount), 181a1c995b6SPoul-Henning Kamp M_UNIONFSMNT, M_WAITOK); /* XXX */ 182df8bae1dSRodney W. Grimes 183df8bae1dSRodney W. Grimes /* 184df8bae1dSRodney W. Grimes * Keep a held reference to the target vnodes. 185df8bae1dSRodney W. Grimes * They are vrele'd in union_unmount. 186df8bae1dSRodney W. Grimes * 187df8bae1dSRodney W. Grimes * Depending on the _BELOW flag, the filesystems are 188df8bae1dSRodney W. Grimes * viewed in a different order. In effect, this is the 189df8bae1dSRodney W. Grimes * same as providing a mount under option to the mount syscall. 190df8bae1dSRodney W. Grimes */ 191df8bae1dSRodney W. Grimes 192df8bae1dSRodney W. Grimes um->um_op = args.mntflags & UNMNT_OPMASK; 193df8bae1dSRodney W. Grimes switch (um->um_op) { 194df8bae1dSRodney W. Grimes case UNMNT_ABOVE: 195df8bae1dSRodney W. Grimes um->um_lowervp = lowerrootvp; 196df8bae1dSRodney W. Grimes um->um_uppervp = upperrootvp; 197df8bae1dSRodney W. Grimes break; 198df8bae1dSRodney W. Grimes 199df8bae1dSRodney W. Grimes case UNMNT_BELOW: 200df8bae1dSRodney W. Grimes um->um_lowervp = upperrootvp; 201df8bae1dSRodney W. Grimes um->um_uppervp = lowerrootvp; 202df8bae1dSRodney W. Grimes break; 203df8bae1dSRodney W. Grimes 204df8bae1dSRodney W. Grimes case UNMNT_REPLACE: 205df8bae1dSRodney W. Grimes vrele(lowerrootvp); 206df8bae1dSRodney W. Grimes lowerrootvp = NULLVP; 207df8bae1dSRodney W. Grimes um->um_uppervp = upperrootvp; 208df8bae1dSRodney W. Grimes um->um_lowervp = lowerrootvp; 209df8bae1dSRodney W. Grimes break; 210df8bae1dSRodney W. Grimes 211df8bae1dSRodney W. Grimes default: 212df8bae1dSRodney W. Grimes error = EINVAL; 213df8bae1dSRodney W. Grimes goto bad; 214df8bae1dSRodney W. Grimes } 215df8bae1dSRodney W. Grimes 216996c772fSJohn Dyson /* 217996c772fSJohn Dyson * Unless the mount is readonly, ensure that the top layer 218996c772fSJohn Dyson * supports whiteout operations 219996c772fSJohn Dyson */ 220996c772fSJohn Dyson if ((mp->mnt_flag & MNT_RDONLY) == 0) { 221996c772fSJohn Dyson error = VOP_WHITEOUT(um->um_uppervp, (struct componentname *) 0, LOOKUP); 222996c772fSJohn Dyson if (error) 223996c772fSJohn Dyson goto bad; 224996c772fSJohn Dyson } 225996c772fSJohn Dyson 226996c772fSJohn Dyson um->um_cred = p->p_ucred; 227996c772fSJohn Dyson crhold(um->um_cred); 228df8bae1dSRodney W. Grimes um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask; 229df8bae1dSRodney W. Grimes 230df8bae1dSRodney W. Grimes /* 231df8bae1dSRodney W. Grimes * Depending on what you think the MNT_LOCAL flag might mean, 232df8bae1dSRodney W. Grimes * you may want the && to be || on the conditional below. 233df8bae1dSRodney W. Grimes * At the moment it has been defined that the filesystem is 234df8bae1dSRodney W. Grimes * only local if it is all local, ie the MNT_LOCAL flag implies 235df8bae1dSRodney W. Grimes * that the entire namespace is local. If you think the MNT_LOCAL 236df8bae1dSRodney W. Grimes * flag implies that some of the files might be stored locally 237df8bae1dSRodney W. Grimes * then you will want to change the conditional. 238df8bae1dSRodney W. Grimes */ 239df8bae1dSRodney W. Grimes if (um->um_op == UNMNT_ABOVE) { 240df8bae1dSRodney W. Grimes if (((um->um_lowervp == NULLVP) || 241df8bae1dSRodney W. Grimes (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) && 242df8bae1dSRodney W. Grimes (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) 243df8bae1dSRodney W. Grimes mp->mnt_flag |= MNT_LOCAL; 244df8bae1dSRodney W. Grimes } 245df8bae1dSRodney W. Grimes 246df8bae1dSRodney W. Grimes /* 247df8bae1dSRodney W. Grimes * Copy in the upper layer's RDONLY flag. This is for the benefit 248df8bae1dSRodney W. Grimes * of lookup() which explicitly checks the flag, rather than asking 249df8bae1dSRodney W. Grimes * the filesystem for it's own opinion. This means, that an update 250df8bae1dSRodney W. Grimes * mount of the underlying filesystem to go from rdonly to rdwr 251df8bae1dSRodney W. Grimes * will leave the unioned view as read-only. 252df8bae1dSRodney W. Grimes */ 253df8bae1dSRodney W. Grimes mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY); 254df8bae1dSRodney W. Grimes 255df8bae1dSRodney W. Grimes mp->mnt_data = (qaddr_t) um; 256996c772fSJohn Dyson vfs_getnewfsid(mp); 257df8bae1dSRodney W. Grimes 258df8bae1dSRodney W. Grimes (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); 259df8bae1dSRodney W. Grimes bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); 260df8bae1dSRodney W. Grimes 261df8bae1dSRodney W. Grimes switch (um->um_op) { 262df8bae1dSRodney W. Grimes case UNMNT_ABOVE: 263996c772fSJohn Dyson cp = "<above>:"; 264df8bae1dSRodney W. Grimes break; 265df8bae1dSRodney W. Grimes case UNMNT_BELOW: 266996c772fSJohn Dyson cp = "<below>:"; 267df8bae1dSRodney W. Grimes break; 268df8bae1dSRodney W. Grimes case UNMNT_REPLACE: 269df8bae1dSRodney W. Grimes cp = ""; 270df8bae1dSRodney W. Grimes break; 271df8bae1dSRodney W. Grimes } 272df8bae1dSRodney W. Grimes len = strlen(cp); 273df8bae1dSRodney W. Grimes bcopy(cp, mp->mnt_stat.f_mntfromname, len); 274df8bae1dSRodney W. Grimes 275df8bae1dSRodney W. Grimes cp = mp->mnt_stat.f_mntfromname + len; 276df8bae1dSRodney W. Grimes len = MNAMELEN - len; 277df8bae1dSRodney W. Grimes 278df8bae1dSRodney W. Grimes (void) copyinstr(args.target, cp, len - 1, &size); 279df8bae1dSRodney W. Grimes bzero(cp + size, len - size); 280df8bae1dSRodney W. Grimes 281c4a7b7e1SDavid Greenman (void)union_statfs(mp, &mp->mnt_stat, p); 282c4a7b7e1SDavid Greenman 283df8bae1dSRodney W. Grimes #ifdef UNION_DIAGNOSTIC 284df8bae1dSRodney W. Grimes printf("union_mount: from %s, on %s\n", 285df8bae1dSRodney W. Grimes mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 286df8bae1dSRodney W. Grimes #endif 287df8bae1dSRodney W. Grimes return (0); 288df8bae1dSRodney W. Grimes 289df8bae1dSRodney W. Grimes bad: 290996c772fSJohn Dyson if (um) 291a1c995b6SPoul-Henning Kamp free(um, M_UNIONFSMNT); 292df8bae1dSRodney W. Grimes if (cred) 293df8bae1dSRodney W. Grimes crfree(cred); 294df8bae1dSRodney W. Grimes if (upperrootvp) 295df8bae1dSRodney W. Grimes vrele(upperrootvp); 296df8bae1dSRodney W. Grimes if (lowerrootvp) 297df8bae1dSRodney W. Grimes vrele(lowerrootvp); 298df8bae1dSRodney W. Grimes return (error); 299df8bae1dSRodney W. Grimes } 300df8bae1dSRodney W. Grimes 301df8bae1dSRodney W. Grimes /* 302df8bae1dSRodney W. Grimes * VFS start. Nothing needed here - the start routine 303df8bae1dSRodney W. Grimes * on the underlying filesystem(s) will have been called 304df8bae1dSRodney W. Grimes * when that filesystem was mounted. 305df8bae1dSRodney W. Grimes */ 306df8bae1dSRodney W. Grimes int 307df8bae1dSRodney W. Grimes union_start(mp, flags, p) 308df8bae1dSRodney W. Grimes struct mount *mp; 309df8bae1dSRodney W. Grimes int flags; 310df8bae1dSRodney W. Grimes struct proc *p; 311df8bae1dSRodney W. Grimes { 312df8bae1dSRodney W. Grimes 313df8bae1dSRodney W. Grimes return (0); 314df8bae1dSRodney W. Grimes } 315df8bae1dSRodney W. Grimes 316df8bae1dSRodney W. Grimes /* 317df8bae1dSRodney W. Grimes * Free reference to union layer 318df8bae1dSRodney W. Grimes */ 319df8bae1dSRodney W. Grimes int 320df8bae1dSRodney W. Grimes union_unmount(mp, mntflags, p) 321df8bae1dSRodney W. Grimes struct mount *mp; 322df8bae1dSRodney W. Grimes int mntflags; 323df8bae1dSRodney W. Grimes struct proc *p; 324df8bae1dSRodney W. Grimes { 325df8bae1dSRodney W. Grimes struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 326df8bae1dSRodney W. Grimes struct vnode *um_rootvp; 327df8bae1dSRodney W. Grimes int error; 328996c772fSJohn Dyson int freeing; 329df8bae1dSRodney W. Grimes int flags = 0; 330df8bae1dSRodney W. Grimes 331df8bae1dSRodney W. Grimes #ifdef UNION_DIAGNOSTIC 332df8bae1dSRodney W. Grimes printf("union_unmount(mp = %x)\n", mp); 333df8bae1dSRodney W. Grimes #endif 334df8bae1dSRodney W. Grimes 335996c772fSJohn Dyson if (mntflags & MNT_FORCE) 336996c772fSJohn Dyson flags |= FORCECLOSE; 337996c772fSJohn Dyson 338996c772fSJohn Dyson if (error = union_root(mp, &um_rootvp)) 339df8bae1dSRodney W. Grimes return (error); 340df8bae1dSRodney W. Grimes 341996c772fSJohn Dyson /* 342996c772fSJohn Dyson * Keep flushing vnodes from the mount list. 343996c772fSJohn Dyson * This is needed because of the un_pvp held 344996c772fSJohn Dyson * reference to the parent vnode. 345996c772fSJohn Dyson * If more vnodes have been freed on a given pass, 346996c772fSJohn Dyson * the try again. The loop will iterate at most 347996c772fSJohn Dyson * (d) times, where (d) is the maximum tree depth 348996c772fSJohn Dyson * in the filesystem. 349996c772fSJohn Dyson */ 350996c772fSJohn Dyson for (freeing = 0; vflush(mp, um_rootvp, flags) != 0;) { 351996c772fSJohn Dyson struct vnode *vp; 352996c772fSJohn Dyson int n; 353996c772fSJohn Dyson 354996c772fSJohn Dyson /* count #vnodes held on mount list */ 355996c772fSJohn Dyson for (n = 0, vp = mp->mnt_vnodelist.lh_first; 356996c772fSJohn Dyson vp != NULLVP; 357996c772fSJohn Dyson vp = vp->v_mntvnodes.le_next) 358996c772fSJohn Dyson n++; 359996c772fSJohn Dyson 360996c772fSJohn Dyson /* if this is unchanged then stop */ 361996c772fSJohn Dyson if (n == freeing) 362996c772fSJohn Dyson break; 363996c772fSJohn Dyson 364996c772fSJohn Dyson /* otherwise try once more time */ 365996c772fSJohn Dyson freeing = n; 366df8bae1dSRodney W. Grimes } 367df8bae1dSRodney W. Grimes 368996c772fSJohn Dyson /* At this point the root vnode should have a single reference */ 369df8bae1dSRodney W. Grimes if (um_rootvp->v_usecount > 1) { 370df8bae1dSRodney W. Grimes vput(um_rootvp); 371df8bae1dSRodney W. Grimes return (EBUSY); 372df8bae1dSRodney W. Grimes } 373df8bae1dSRodney W. Grimes 374df8bae1dSRodney W. Grimes #ifdef UNION_DIAGNOSTIC 375996c772fSJohn Dyson vprint("union root", um_rootvp); 376df8bae1dSRodney W. Grimes #endif 377df8bae1dSRodney W. Grimes /* 378df8bae1dSRodney W. Grimes * Discard references to upper and lower target vnodes. 379df8bae1dSRodney W. Grimes */ 380df8bae1dSRodney W. Grimes if (um->um_lowervp) 381df8bae1dSRodney W. Grimes vrele(um->um_lowervp); 382df8bae1dSRodney W. Grimes vrele(um->um_uppervp); 383df8bae1dSRodney W. Grimes crfree(um->um_cred); 384df8bae1dSRodney W. Grimes /* 385df8bae1dSRodney W. Grimes * Release reference on underlying root vnode 386df8bae1dSRodney W. Grimes */ 387df8bae1dSRodney W. Grimes vput(um_rootvp); 388df8bae1dSRodney W. Grimes /* 389df8bae1dSRodney W. Grimes * And blow it away for future re-use 390df8bae1dSRodney W. Grimes */ 391df8bae1dSRodney W. Grimes vgone(um_rootvp); 392df8bae1dSRodney W. Grimes /* 393df8bae1dSRodney W. Grimes * Finally, throw away the union_mount structure 394df8bae1dSRodney W. Grimes */ 395a1c995b6SPoul-Henning Kamp free(mp->mnt_data, M_UNIONFSMNT); /* XXX */ 396df8bae1dSRodney W. Grimes mp->mnt_data = 0; 397df8bae1dSRodney W. Grimes return (0); 398df8bae1dSRodney W. Grimes } 399df8bae1dSRodney W. Grimes 400df8bae1dSRodney W. Grimes int 401df8bae1dSRodney W. Grimes union_root(mp, vpp) 402df8bae1dSRodney W. Grimes struct mount *mp; 403df8bae1dSRodney W. Grimes struct vnode **vpp; 404df8bae1dSRodney W. Grimes { 405996c772fSJohn Dyson struct proc *p = curproc; /* XXX */ 406df8bae1dSRodney W. Grimes struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 407df8bae1dSRodney W. Grimes int error; 408df8bae1dSRodney W. Grimes int loselock; 409df8bae1dSRodney W. Grimes 410df8bae1dSRodney W. Grimes /* 411df8bae1dSRodney W. Grimes * Return locked reference to root. 412df8bae1dSRodney W. Grimes */ 413df8bae1dSRodney W. Grimes VREF(um->um_uppervp); 414df8bae1dSRodney W. Grimes if ((um->um_op == UNMNT_BELOW) && 415df8bae1dSRodney W. Grimes VOP_ISLOCKED(um->um_uppervp)) { 416df8bae1dSRodney W. Grimes loselock = 1; 417df8bae1dSRodney W. Grimes } else { 418747e9157SKATO Takenori if (VOP_ISLOCKED(um->um_uppervp)) { 419747e9157SKATO Takenori /* 420747e9157SKATO Takenori * XXX 421747e9157SKATO Takenori * Should we check type of node? 422747e9157SKATO Takenori */ 423747e9157SKATO Takenori #ifdef DIAGNOSTIC 424747e9157SKATO Takenori printf("union_root: multi union mount?"); 425747e9157SKATO Takenori #endif 426747e9157SKATO Takenori vrele(um->um_uppervp); 427747e9157SKATO Takenori return EDEADLK; 428747e9157SKATO Takenori } else 429996c772fSJohn Dyson vn_lock(um->um_uppervp, LK_EXCLUSIVE | LK_RETRY, p); 430df8bae1dSRodney W. Grimes loselock = 0; 431df8bae1dSRodney W. Grimes } 432df8bae1dSRodney W. Grimes if (um->um_lowervp) 433df8bae1dSRodney W. Grimes VREF(um->um_lowervp); 434df8bae1dSRodney W. Grimes error = union_allocvp(vpp, mp, 435df8bae1dSRodney W. Grimes (struct vnode *) 0, 436df8bae1dSRodney W. Grimes (struct vnode *) 0, 437df8bae1dSRodney W. Grimes (struct componentname *) 0, 438df8bae1dSRodney W. Grimes um->um_uppervp, 439996c772fSJohn Dyson um->um_lowervp, 440996c772fSJohn Dyson 1); 441df8bae1dSRodney W. Grimes 442df8bae1dSRodney W. Grimes if (error) { 443996c772fSJohn Dyson if (loselock) 444df8bae1dSRodney W. Grimes vrele(um->um_uppervp); 445996c772fSJohn Dyson else 446996c772fSJohn Dyson vput(um->um_uppervp); 447df8bae1dSRodney W. Grimes if (um->um_lowervp) 448df8bae1dSRodney W. Grimes vrele(um->um_lowervp); 449df8bae1dSRodney W. Grimes } else { 450df8bae1dSRodney W. Grimes if (loselock) 451df8bae1dSRodney W. Grimes VTOUNION(*vpp)->un_flags &= ~UN_ULOCK; 452df8bae1dSRodney W. Grimes } 453df8bae1dSRodney W. Grimes 454df8bae1dSRodney W. Grimes return (error); 455df8bae1dSRodney W. Grimes } 456df8bae1dSRodney W. Grimes 457df8bae1dSRodney W. Grimes int 458df8bae1dSRodney W. Grimes union_statfs(mp, sbp, p) 459df8bae1dSRodney W. Grimes struct mount *mp; 460df8bae1dSRodney W. Grimes struct statfs *sbp; 461df8bae1dSRodney W. Grimes struct proc *p; 462df8bae1dSRodney W. Grimes { 463df8bae1dSRodney W. Grimes int error; 464df8bae1dSRodney W. Grimes struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 465df8bae1dSRodney W. Grimes struct statfs mstat; 466df8bae1dSRodney W. Grimes int lbsize; 467df8bae1dSRodney W. Grimes 468df8bae1dSRodney W. Grimes #ifdef UNION_DIAGNOSTIC 469df8bae1dSRodney W. Grimes printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp, 470df8bae1dSRodney W. Grimes um->um_lowervp, 471df8bae1dSRodney W. Grimes um->um_uppervp); 472df8bae1dSRodney W. Grimes #endif 473df8bae1dSRodney W. Grimes 474df8bae1dSRodney W. Grimes bzero(&mstat, sizeof(mstat)); 475df8bae1dSRodney W. Grimes 476df8bae1dSRodney W. Grimes if (um->um_lowervp) { 477df8bae1dSRodney W. Grimes error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p); 478df8bae1dSRodney W. Grimes if (error) 479df8bae1dSRodney W. Grimes return (error); 480df8bae1dSRodney W. Grimes } 481df8bae1dSRodney W. Grimes 482df8bae1dSRodney W. Grimes /* now copy across the "interesting" information and fake the rest */ 483df8bae1dSRodney W. Grimes #if 0 484df8bae1dSRodney W. Grimes sbp->f_type = mstat.f_type; 485df8bae1dSRodney W. Grimes sbp->f_flags = mstat.f_flags; 486df8bae1dSRodney W. Grimes sbp->f_bsize = mstat.f_bsize; 487df8bae1dSRodney W. Grimes sbp->f_iosize = mstat.f_iosize; 488df8bae1dSRodney W. Grimes #endif 489df8bae1dSRodney W. Grimes lbsize = mstat.f_bsize; 490df8bae1dSRodney W. Grimes sbp->f_blocks = mstat.f_blocks; 491df8bae1dSRodney W. Grimes sbp->f_bfree = mstat.f_bfree; 492df8bae1dSRodney W. Grimes sbp->f_bavail = mstat.f_bavail; 493df8bae1dSRodney W. Grimes sbp->f_files = mstat.f_files; 494df8bae1dSRodney W. Grimes sbp->f_ffree = mstat.f_ffree; 495df8bae1dSRodney W. Grimes 496df8bae1dSRodney W. Grimes error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p); 497df8bae1dSRodney W. Grimes if (error) 498df8bae1dSRodney W. Grimes return (error); 499df8bae1dSRodney W. Grimes 500df8bae1dSRodney W. Grimes sbp->f_flags = mstat.f_flags; 501df8bae1dSRodney W. Grimes sbp->f_bsize = mstat.f_bsize; 502df8bae1dSRodney W. Grimes sbp->f_iosize = mstat.f_iosize; 503df8bae1dSRodney W. Grimes 504df8bae1dSRodney W. Grimes /* 505df8bae1dSRodney W. Grimes * if the lower and upper blocksizes differ, then frig the 506df8bae1dSRodney W. Grimes * block counts so that the sizes reported by df make some 507df8bae1dSRodney W. Grimes * kind of sense. none of this makes sense though. 508df8bae1dSRodney W. Grimes */ 509df8bae1dSRodney W. Grimes 510996c772fSJohn Dyson if (mstat.f_bsize != lbsize) 511c9bf0111SKATO Takenori sbp->f_blocks = ((off_t) sbp->f_blocks * lbsize) / mstat.f_bsize; 512996c772fSJohn Dyson 513996c772fSJohn Dyson /* 514996c772fSJohn Dyson * The "total" fields count total resources in all layers, 515996c772fSJohn Dyson * the "free" fields count only those resources which are 516996c772fSJohn Dyson * free in the upper layer (since only the upper layer 517996c772fSJohn Dyson * is writeable). 518996c772fSJohn Dyson */ 519df8bae1dSRodney W. Grimes sbp->f_blocks += mstat.f_blocks; 520996c772fSJohn Dyson sbp->f_bfree = mstat.f_bfree; 521996c772fSJohn Dyson sbp->f_bavail = mstat.f_bavail; 522df8bae1dSRodney W. Grimes sbp->f_files += mstat.f_files; 523996c772fSJohn Dyson sbp->f_ffree = mstat.f_ffree; 524df8bae1dSRodney W. Grimes 525df8bae1dSRodney W. Grimes if (sbp != &mp->mnt_stat) { 526996c772fSJohn Dyson sbp->f_type = mp->mnt_vfc->vfc_typenum; 527df8bae1dSRodney W. Grimes bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); 528df8bae1dSRodney W. Grimes bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 529df8bae1dSRodney W. Grimes bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 530df8bae1dSRodney W. Grimes } 531df8bae1dSRodney W. Grimes return (0); 532df8bae1dSRodney W. Grimes } 533df8bae1dSRodney W. Grimes 534df8bae1dSRodney W. Grimes /* 535df8bae1dSRodney W. Grimes * XXX - Assumes no data cached at union layer. 536df8bae1dSRodney W. Grimes */ 537996c772fSJohn Dyson #define union_sync ((int (*) __P((struct mount *, int, struct ucred *, \ 538996c772fSJohn Dyson struct proc *)))nullop) 539df8bae1dSRodney W. Grimes 540996c772fSJohn Dyson #define union_fhtovp ((int (*) __P((struct mount *, struct fid *, \ 54157bf258eSGarrett Wollman struct sockaddr *, struct vnode **, int *, struct ucred **)))eopnotsupp) 542996c772fSJohn Dyson #define union_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \ 543996c772fSJohn Dyson struct proc *)))eopnotsupp) 544996c772fSJohn Dyson #define union_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \ 545996c772fSJohn Dyson size_t, struct proc *)))eopnotsupp) 546996c772fSJohn Dyson #define union_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \ 547996c772fSJohn Dyson eopnotsupp) 548996c772fSJohn Dyson #define union_vptofh ((int (*) __P((struct vnode *, struct fid *)))eopnotsupp) 549df8bae1dSRodney W. Grimes 550df8bae1dSRodney W. Grimes struct vfsops union_vfsops = { 551df8bae1dSRodney W. Grimes union_mount, 552df8bae1dSRodney W. Grimes union_start, 553df8bae1dSRodney W. Grimes union_unmount, 554df8bae1dSRodney W. Grimes union_root, 555df8bae1dSRodney W. Grimes union_quotactl, 556df8bae1dSRodney W. Grimes union_statfs, 557df8bae1dSRodney W. Grimes union_sync, 558df8bae1dSRodney W. Grimes union_vget, 559df8bae1dSRodney W. Grimes union_fhtovp, 560df8bae1dSRodney W. Grimes union_vptofh, 561df8bae1dSRodney W. Grimes union_init, 562df8bae1dSRodney W. Grimes }; 563c901836cSGarrett Wollman 564bbf3a566SGarrett Wollman VFS_SET(union_vfsops, union, MOUNT_UNION, VFCF_LOOPBACK); 565