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 * 4. Neither the name of the University nor the names of its contributors 18df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 19df8bae1dSRodney W. Grimes * without specific prior written permission. 20df8bae1dSRodney W. Grimes * 21df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31df8bae1dSRodney W. Grimes * SUCH DAMAGE. 32df8bae1dSRodney W. Grimes * 33996c772fSJohn Dyson * @(#)union_vfsops.c 8.20 (Berkeley) 5/20/95 34c3aac50fSPeter Wemm * $FreeBSD$ 35df8bae1dSRodney W. Grimes */ 36df8bae1dSRodney W. Grimes 37df8bae1dSRodney W. Grimes /* 38df8bae1dSRodney W. Grimes * Union Layer 39df8bae1dSRodney W. Grimes */ 40df8bae1dSRodney W. Grimes 41df8bae1dSRodney W. Grimes #include <sys/param.h> 42df8bae1dSRodney W. Grimes #include <sys/systm.h> 43c9b1d604SGarrett Wollman #include <sys/kernel.h> 44805d90f7SJohn Baldwin #include <sys/lock.h> 45805d90f7SJohn Baldwin #include <sys/mutex.h> 46df8bae1dSRodney W. Grimes #include <sys/proc.h> 47df8bae1dSRodney W. Grimes #include <sys/vnode.h> 48df8bae1dSRodney W. Grimes #include <sys/mount.h> 49df8bae1dSRodney W. Grimes #include <sys/namei.h> 50df8bae1dSRodney W. Grimes #include <sys/malloc.h> 51df8bae1dSRodney W. Grimes #include <sys/filedesc.h> 5299d300a1SRuslan Ermilov #include <fs/unionfs/union.h> 53df8bae1dSRodney W. Grimes 54a1c995b6SPoul-Henning Kamp static MALLOC_DEFINE(M_UNIONFSMNT, "UNION mount", "UNION mount structure"); 55a1c995b6SPoul-Henning Kamp 567652131bSPoul-Henning Kamp extern vfs_init_t union_init; 577652131bSPoul-Henning Kamp static vfs_root_t union_root; 585e8c582aSPoul-Henning Kamp static vfs_mount_t union_mount; 597652131bSPoul-Henning Kamp static vfs_statfs_t union_statfs; 607652131bSPoul-Henning Kamp static vfs_unmount_t union_unmount; 61b5e8ce9fSBruce Evans 62df8bae1dSRodney W. Grimes /* 631c4ccf09SDon Lewis * Mount union filesystem. 64df8bae1dSRodney W. Grimes */ 6580b301c3SPoul-Henning Kamp static int 665e8c582aSPoul-Henning Kamp union_mount(mp, td) 67df8bae1dSRodney W. Grimes struct mount *mp; 68b40ce416SJulian Elischer struct thread *td; 69df8bae1dSRodney W. Grimes { 70df8bae1dSRodney W. Grimes int error = 0; 71a9f5c04aSMaxime Henrion struct vfsoptlist *opts; 72df8bae1dSRodney W. Grimes struct vnode *lowerrootvp = NULLVP; 73df8bae1dSRodney W. Grimes struct vnode *upperrootvp = NULLVP; 74996c772fSJohn Dyson struct union_mount *um = 0; 75919f5630STakanori Watanabe struct vattr va; 76df8bae1dSRodney W. Grimes struct ucred *cred = 0; 77a9f5c04aSMaxime Henrion char *cp = 0, *target; 784ce86ffdSMaxime Henrion int op; 79df8bae1dSRodney W. Grimes int len; 80a8f15ed7SMaxime Henrion size_t size; 81d1841903STim J. Robbins struct componentname fakecn; 825e8c582aSPoul-Henning Kamp struct nameidata nd, *ndp = &nd; 83df8bae1dSRodney W. Grimes 842a31267eSMatthew Dillon UDEBUG(("union_mount(mp = %p)\n", (void *)mp)); 85df8bae1dSRodney W. Grimes 86a9f5c04aSMaxime Henrion opts = mp->mnt_optnew; 87df8bae1dSRodney W. Grimes /* 8881bca6ddSKATO Takenori * Disable clustered write, otherwise system becomes unstable. 8981bca6ddSKATO Takenori */ 9081bca6ddSKATO Takenori mp->mnt_flag |= MNT_NOCLUSTERW; 9181bca6ddSKATO Takenori 9264042a76SPoul-Henning Kamp if (mp->mnt_flag & MNT_ROOTFS) 9364042a76SPoul-Henning Kamp return (EOPNOTSUPP); 9481bca6ddSKATO Takenori /* 95df8bae1dSRodney W. Grimes * Update is a no-op 96df8bae1dSRodney W. Grimes */ 97a9f5c04aSMaxime Henrion if (mp->mnt_flag & MNT_UPDATE) 98df8bae1dSRodney W. Grimes /* 991c4ccf09SDon Lewis * Need to provide: 100df8bae1dSRodney W. Grimes * 1. a way to convert between rdonly and rdwr mounts. 101df8bae1dSRodney W. Grimes * 2. support for nfs exports. 102df8bae1dSRodney W. Grimes */ 103a9f5c04aSMaxime Henrion return (EOPNOTSUPP); 104df8bae1dSRodney W. Grimes 105df8bae1dSRodney W. Grimes /* 106a9f5c04aSMaxime Henrion * Get arguments. 107df8bae1dSRodney W. Grimes */ 108a9f5c04aSMaxime Henrion error = vfs_getopt(opts, "target", (void **)&target, &len); 109a9f5c04aSMaxime Henrion if (error || target[len - 1] != '\0') 110a9f5c04aSMaxime Henrion return (EINVAL); 111a9f5c04aSMaxime Henrion 1124ce86ffdSMaxime Henrion op = 0; 1134ce86ffdSMaxime Henrion if (vfs_getopt(opts, "below", NULL, NULL) == 0) 1144ce86ffdSMaxime Henrion op = UNMNT_BELOW; 1154ce86ffdSMaxime Henrion if (vfs_getopt(opts, "replace", NULL, NULL) == 0) { 1164ce86ffdSMaxime Henrion /* These options are mutually exclusive. */ 1174ce86ffdSMaxime Henrion if (op) 118a9f5c04aSMaxime Henrion return (EINVAL); 1194ce86ffdSMaxime Henrion op = UNMNT_REPLACE; 1204ce86ffdSMaxime Henrion } 1214ce86ffdSMaxime Henrion /* 1224ce86ffdSMaxime Henrion * UNMNT_ABOVE is the default. 1234ce86ffdSMaxime Henrion */ 1244ce86ffdSMaxime Henrion if (op == 0) 1254ce86ffdSMaxime Henrion op = UNMNT_ABOVE; 126df8bae1dSRodney W. Grimes 1272a31267eSMatthew Dillon /* 1282a31267eSMatthew Dillon * Obtain lower vnode. Vnode is stored in mp->mnt_vnodecovered. 1292a31267eSMatthew Dillon * We need to reference it but not lock it. 1302a31267eSMatthew Dillon */ 1312a31267eSMatthew Dillon 132df8bae1dSRodney W. Grimes lowerrootvp = mp->mnt_vnodecovered; 133df8bae1dSRodney W. Grimes VREF(lowerrootvp); 134df8bae1dSRodney W. Grimes 1352a31267eSMatthew Dillon #if 0 136df8bae1dSRodney W. Grimes /* 1376db918e3SKATO Takenori * Unlock lower node to avoid deadlock. 1386db918e3SKATO Takenori */ 139afc2a558SKATO Takenori if (lowerrootvp->v_op == union_vnodeop_p) 140b40ce416SJulian Elischer VOP_UNLOCK(lowerrootvp, 0, td); 1412a31267eSMatthew Dillon #endif 1426db918e3SKATO Takenori 1436db918e3SKATO Takenori /* 1442a31267eSMatthew Dillon * Obtain upper vnode by calling namei() on the path. The 1452a31267eSMatthew Dillon * upperrootvp will be turned referenced but not locked. 146df8bae1dSRodney W. Grimes */ 147a9f5c04aSMaxime Henrion NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT, UIO_SYSSPACE, target, td); 148df8bae1dSRodney W. Grimes 1493a773ad0SPoul-Henning Kamp error = namei(ndp); 1502a31267eSMatthew Dillon 1512a31267eSMatthew Dillon #if 0 152afc2a558SKATO Takenori if (lowerrootvp->v_op == union_vnodeop_p) 153b40ce416SJulian Elischer vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY, td); 1542a31267eSMatthew Dillon #endif 1553a773ad0SPoul-Henning Kamp if (error) 156df8bae1dSRodney W. Grimes goto bad; 157df8bae1dSRodney W. Grimes 158762e6b85SEivind Eklund NDFREE(ndp, NDF_ONLY_PNBUF); 159df8bae1dSRodney W. Grimes upperrootvp = ndp->ni_vp; 160df8bae1dSRodney W. Grimes vrele(ndp->ni_dvp); 161df8bae1dSRodney W. Grimes ndp->ni_dvp = NULL; 162df8bae1dSRodney W. Grimes 1636bdfe06aSEivind Eklund UDEBUG(("mount_root UPPERVP %p locked = %d\n", upperrootvp, 1646bdfe06aSEivind Eklund VOP_ISLOCKED(upperrootvp, NULL))); 1652a31267eSMatthew Dillon 1666db918e3SKATO Takenori /* 1676db918e3SKATO Takenori * Check multi union mount to avoid `lock myself again' panic. 1682a31267eSMatthew Dillon * Also require that it be a directory. 1696db918e3SKATO Takenori */ 1706db918e3SKATO Takenori if (upperrootvp == VTOUNION(lowerrootvp)->un_uppervp) { 171747e9157SKATO Takenori #ifdef DIAGNOSTIC 172747e9157SKATO Takenori printf("union_mount: multi union mount?\n"); 173747e9157SKATO Takenori #endif 1746db918e3SKATO Takenori error = EDEADLK; 1756db918e3SKATO Takenori goto bad; 1766db918e3SKATO Takenori } 1776db918e3SKATO Takenori 178df8bae1dSRodney W. Grimes if (upperrootvp->v_type != VDIR) { 179df8bae1dSRodney W. Grimes error = EINVAL; 180df8bae1dSRodney W. Grimes goto bad; 181df8bae1dSRodney W. Grimes } 182df8bae1dSRodney W. Grimes 183df8bae1dSRodney W. Grimes /* 1842a31267eSMatthew Dillon * Allocate our union_mount structure and populate the fields. 1852a31267eSMatthew Dillon * The vnode references are stored in the union_mount as held, 1862a31267eSMatthew Dillon * unlocked references. Depending on the _BELOW flag, the 1872a31267eSMatthew Dillon * filesystems are viewed in a different order. In effect this 1882a31267eSMatthew Dillon * is the same as providing a mount-under option to the mount 1892a31267eSMatthew Dillon * syscall. 190df8bae1dSRodney W. Grimes */ 191df8bae1dSRodney W. Grimes 1922a31267eSMatthew Dillon um = (struct union_mount *) malloc(sizeof(struct union_mount), 193a163d034SWarner Losh M_UNIONFSMNT, M_WAITOK | M_ZERO); 1942a31267eSMatthew Dillon 1954ce86ffdSMaxime Henrion um->um_op = op; 1962a31267eSMatthew Dillon 197919f5630STakanori Watanabe error = VOP_GETATTR(upperrootvp, &va, td->td_ucred, td); 198919f5630STakanori Watanabe if (error) 199919f5630STakanori Watanabe goto bad; 200919f5630STakanori Watanabe 201919f5630STakanori Watanabe um->um_upperdev = va.va_fsid; 202919f5630STakanori Watanabe 203df8bae1dSRodney W. Grimes switch (um->um_op) { 204df8bae1dSRodney W. Grimes case UNMNT_ABOVE: 205df8bae1dSRodney W. Grimes um->um_lowervp = lowerrootvp; 206df8bae1dSRodney W. Grimes um->um_uppervp = upperrootvp; 2072a31267eSMatthew Dillon upperrootvp = NULL; 2082a31267eSMatthew Dillon lowerrootvp = NULL; 209df8bae1dSRodney W. Grimes break; 210df8bae1dSRodney W. Grimes 211df8bae1dSRodney W. Grimes case UNMNT_BELOW: 212df8bae1dSRodney W. Grimes um->um_lowervp = upperrootvp; 213df8bae1dSRodney W. Grimes um->um_uppervp = lowerrootvp; 2142a31267eSMatthew Dillon upperrootvp = NULL; 2152a31267eSMatthew Dillon lowerrootvp = NULL; 216df8bae1dSRodney W. Grimes break; 217df8bae1dSRodney W. Grimes 218df8bae1dSRodney W. Grimes case UNMNT_REPLACE: 219df8bae1dSRodney W. Grimes vrele(lowerrootvp); 2202a31267eSMatthew Dillon lowerrootvp = NULL; 221df8bae1dSRodney W. Grimes um->um_uppervp = upperrootvp; 222df8bae1dSRodney W. Grimes um->um_lowervp = lowerrootvp; 2232a31267eSMatthew Dillon upperrootvp = NULL; 224df8bae1dSRodney W. Grimes break; 225df8bae1dSRodney W. Grimes 226df8bae1dSRodney W. Grimes default: 227df8bae1dSRodney W. Grimes error = EINVAL; 228df8bae1dSRodney W. Grimes goto bad; 229df8bae1dSRodney W. Grimes } 230df8bae1dSRodney W. Grimes 231996c772fSJohn Dyson /* 232996c772fSJohn Dyson * Unless the mount is readonly, ensure that the top layer 2331c4ccf09SDon Lewis * supports whiteout operations. 234996c772fSJohn Dyson */ 235996c772fSJohn Dyson if ((mp->mnt_flag & MNT_RDONLY) == 0) { 236d1841903STim J. Robbins /* 237d1841903STim J. Robbins * XXX Fake up a struct componentname with only cn_nameiop 238d1841903STim J. Robbins * and cn_thread valid; union_whiteout() needs to use the 239d1841903STim J. Robbins * thread pointer to lock the vnode. 240d1841903STim J. Robbins */ 241d1841903STim J. Robbins bzero(&fakecn, sizeof(fakecn)); 242d1841903STim J. Robbins fakecn.cn_nameiop = LOOKUP; 243d1841903STim J. Robbins fakecn.cn_thread = td; 244d1841903STim J. Robbins error = VOP_WHITEOUT(um->um_uppervp, &fakecn, LOOKUP); 245996c772fSJohn Dyson if (error) 246996c772fSJohn Dyson goto bad; 247996c772fSJohn Dyson } 248996c772fSJohn Dyson 249a854ed98SJohn Baldwin um->um_cred = crhold(td->td_ucred); 250426da3bcSAlfred Perlstein FILEDESC_LOCK(td->td_proc->p_fd); 251b40ce416SJulian Elischer um->um_cmode = UN_DIRMODE &~ td->td_proc->p_fd->fd_cmask; 252426da3bcSAlfred Perlstein FILEDESC_UNLOCK(td->td_proc->p_fd); 253df8bae1dSRodney W. Grimes 254df8bae1dSRodney W. Grimes /* 255df8bae1dSRodney W. Grimes * Depending on what you think the MNT_LOCAL flag might mean, 256df8bae1dSRodney W. Grimes * you may want the && to be || on the conditional below. 257df8bae1dSRodney W. Grimes * At the moment it has been defined that the filesystem is 258df8bae1dSRodney W. Grimes * only local if it is all local, ie the MNT_LOCAL flag implies 259df8bae1dSRodney W. Grimes * that the entire namespace is local. If you think the MNT_LOCAL 260df8bae1dSRodney W. Grimes * flag implies that some of the files might be stored locally 261df8bae1dSRodney W. Grimes * then you will want to change the conditional. 262df8bae1dSRodney W. Grimes */ 263df8bae1dSRodney W. Grimes if (um->um_op == UNMNT_ABOVE) { 264df8bae1dSRodney W. Grimes if (((um->um_lowervp == NULLVP) || 265df8bae1dSRodney W. Grimes (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) && 266df8bae1dSRodney W. Grimes (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) 267df8bae1dSRodney W. Grimes mp->mnt_flag |= MNT_LOCAL; 268df8bae1dSRodney W. Grimes } 269df8bae1dSRodney W. Grimes 270df8bae1dSRodney W. Grimes /* 271df8bae1dSRodney W. Grimes * Copy in the upper layer's RDONLY flag. This is for the benefit 272df8bae1dSRodney W. Grimes * of lookup() which explicitly checks the flag, rather than asking 273dc733423SDag-Erling Smørgrav * the filesystem for its own opinion. This means, that an update 274df8bae1dSRodney W. Grimes * mount of the underlying filesystem to go from rdonly to rdwr 275df8bae1dSRodney W. Grimes * will leave the unioned view as read-only. 276df8bae1dSRodney W. Grimes */ 277df8bae1dSRodney W. Grimes mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY); 278df8bae1dSRodney W. Grimes 279df8bae1dSRodney W. Grimes mp->mnt_data = (qaddr_t) um; 280996c772fSJohn Dyson vfs_getnewfsid(mp); 281df8bae1dSRodney W. Grimes 282df8bae1dSRodney W. Grimes switch (um->um_op) { 283df8bae1dSRodney W. Grimes case UNMNT_ABOVE: 284996c772fSJohn Dyson cp = "<above>:"; 285df8bae1dSRodney W. Grimes break; 286df8bae1dSRodney W. Grimes case UNMNT_BELOW: 287996c772fSJohn Dyson cp = "<below>:"; 288df8bae1dSRodney W. Grimes break; 289df8bae1dSRodney W. Grimes case UNMNT_REPLACE: 290df8bae1dSRodney W. Grimes cp = ""; 291df8bae1dSRodney W. Grimes break; 292df8bae1dSRodney W. Grimes } 293df8bae1dSRodney W. Grimes len = strlen(cp); 294df8bae1dSRodney W. Grimes bcopy(cp, mp->mnt_stat.f_mntfromname, len); 295df8bae1dSRodney W. Grimes 296df8bae1dSRodney W. Grimes cp = mp->mnt_stat.f_mntfromname + len; 297df8bae1dSRodney W. Grimes len = MNAMELEN - len; 298df8bae1dSRodney W. Grimes 299a9f5c04aSMaxime Henrion (void) copystr(target, cp, len - 1, &size); 300df8bae1dSRodney W. Grimes bzero(cp + size, len - size); 301df8bae1dSRodney W. Grimes 302b40ce416SJulian Elischer (void)union_statfs(mp, &mp->mnt_stat, td); 303c4a7b7e1SDavid Greenman 3042a31267eSMatthew Dillon UDEBUG(("union_mount: from %s, on %s\n", 3052a31267eSMatthew Dillon mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname)); 306df8bae1dSRodney W. Grimes return (0); 307df8bae1dSRodney W. Grimes 308df8bae1dSRodney W. Grimes bad: 3092a31267eSMatthew Dillon if (um) { 3102a31267eSMatthew Dillon if (um->um_uppervp) 3112a31267eSMatthew Dillon vrele(um->um_uppervp); 3122a31267eSMatthew Dillon if (um->um_lowervp) 3132a31267eSMatthew Dillon vrele(um->um_lowervp); 3142a31267eSMatthew Dillon /* XXX other fields */ 315a1c995b6SPoul-Henning Kamp free(um, M_UNIONFSMNT); 3162a31267eSMatthew Dillon } 317df8bae1dSRodney W. Grimes if (cred) 318df8bae1dSRodney W. Grimes crfree(cred); 319df8bae1dSRodney W. Grimes if (upperrootvp) 320df8bae1dSRodney W. Grimes vrele(upperrootvp); 321df8bae1dSRodney W. Grimes if (lowerrootvp) 322df8bae1dSRodney W. Grimes vrele(lowerrootvp); 323df8bae1dSRodney W. Grimes return (error); 324df8bae1dSRodney W. Grimes } 325df8bae1dSRodney W. Grimes 326df8bae1dSRodney W. Grimes /* 3271c4ccf09SDon Lewis * Free reference to union layer. 328df8bae1dSRodney W. Grimes */ 32980b301c3SPoul-Henning Kamp static int 330b40ce416SJulian Elischer union_unmount(mp, mntflags, td) 331df8bae1dSRodney W. Grimes struct mount *mp; 332df8bae1dSRodney W. Grimes int mntflags; 333b40ce416SJulian Elischer struct thread *td; 334df8bae1dSRodney W. Grimes { 335df8bae1dSRodney W. Grimes struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 336df8bae1dSRodney W. Grimes int error; 337996c772fSJohn Dyson int freeing; 338df8bae1dSRodney W. Grimes int flags = 0; 339df8bae1dSRodney W. Grimes 3402a31267eSMatthew Dillon UDEBUG(("union_unmount(mp = %p)\n", (void *)mp)); 341df8bae1dSRodney W. Grimes 342996c772fSJohn Dyson if (mntflags & MNT_FORCE) 343996c772fSJohn Dyson flags |= FORCECLOSE; 344996c772fSJohn Dyson 345996c772fSJohn Dyson /* 346996c772fSJohn Dyson * Keep flushing vnodes from the mount list. 347996c772fSJohn Dyson * This is needed because of the un_pvp held 348996c772fSJohn Dyson * reference to the parent vnode. 349996c772fSJohn Dyson * If more vnodes have been freed on a given pass, 350996c772fSJohn Dyson * the try again. The loop will iterate at most 351996c772fSJohn Dyson * (d) times, where (d) is the maximum tree depth 352996c772fSJohn Dyson * in the filesystem. 353996c772fSJohn Dyson */ 354f257b7a5SAlfred Perlstein for (freeing = 0; (error = vflush(mp, 0, flags, td)) != 0;) { 355996c772fSJohn Dyson int n; 356996c772fSJohn Dyson 357996c772fSJohn Dyson /* count #vnodes held on mount list */ 358b792e030SAlexander Kabaev n = mp->mnt_nvnodelistsize; 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 3681c4ccf09SDon Lewis /* 3691c4ccf09SDon Lewis * If the most recent vflush failed, the filesystem is still busy. 3701c4ccf09SDon Lewis */ 3710864ef1eSIan Dowse if (error) 3720864ef1eSIan Dowse return (error); 373df8bae1dSRodney W. Grimes 374df8bae1dSRodney W. Grimes /* 375df8bae1dSRodney W. Grimes * Discard references to upper and lower target vnodes. 376df8bae1dSRodney W. Grimes */ 377df8bae1dSRodney W. Grimes if (um->um_lowervp) 378df8bae1dSRodney W. Grimes vrele(um->um_lowervp); 379df8bae1dSRodney W. Grimes vrele(um->um_uppervp); 380df8bae1dSRodney W. Grimes crfree(um->um_cred); 381df8bae1dSRodney W. Grimes /* 3821c4ccf09SDon Lewis * Finally, throw away the union_mount structure. 383df8bae1dSRodney W. Grimes */ 384a1c995b6SPoul-Henning Kamp free(mp->mnt_data, M_UNIONFSMNT); /* XXX */ 385df8bae1dSRodney W. Grimes mp->mnt_data = 0; 386df8bae1dSRodney W. Grimes return (0); 387df8bae1dSRodney W. Grimes } 388df8bae1dSRodney W. Grimes 38980b301c3SPoul-Henning Kamp static int 390f257b7a5SAlfred Perlstein union_root(mp, vpp, td) 391df8bae1dSRodney W. Grimes struct mount *mp; 392df8bae1dSRodney W. Grimes struct vnode **vpp; 393f257b7a5SAlfred Perlstein struct thread *td; 394df8bae1dSRodney W. Grimes { 395df8bae1dSRodney W. Grimes struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 396df8bae1dSRodney W. Grimes int error; 397df8bae1dSRodney W. Grimes 398df8bae1dSRodney W. Grimes /* 3992a31267eSMatthew Dillon * Supply an unlocked reference to um_uppervp and to um_lowervp. It 4002a31267eSMatthew Dillon * is possible for um_uppervp to be locked without the associated 4012a31267eSMatthew Dillon * root union_node being locked. We let union_allocvp() deal with 4022a31267eSMatthew Dillon * it. 403df8bae1dSRodney W. Grimes */ 4046bdfe06aSEivind Eklund UDEBUG(("union_root UPPERVP %p locked = %d\n", um->um_uppervp, 4056bdfe06aSEivind Eklund VOP_ISLOCKED(um->um_uppervp, NULL))); 4062a31267eSMatthew Dillon 407df8bae1dSRodney W. Grimes VREF(um->um_uppervp); 408df8bae1dSRodney W. Grimes if (um->um_lowervp) 409df8bae1dSRodney W. Grimes VREF(um->um_lowervp); 410df8bae1dSRodney W. Grimes 4112a31267eSMatthew Dillon error = union_allocvp(vpp, mp, NULLVP, NULLVP, NULL, 4122a31267eSMatthew Dillon um->um_uppervp, um->um_lowervp, 1); 4132a31267eSMatthew Dillon UDEBUG(("error %d\n", error)); 4146bdfe06aSEivind Eklund UDEBUG(("union_root2 UPPERVP %p locked = %d\n", um->um_uppervp, 4156bdfe06aSEivind Eklund VOP_ISLOCKED(um->um_uppervp, NULL))); 416df8bae1dSRodney W. Grimes 417df8bae1dSRodney W. Grimes return (error); 418df8bae1dSRodney W. Grimes } 419df8bae1dSRodney W. Grimes 42080b301c3SPoul-Henning Kamp static int 421b40ce416SJulian Elischer union_statfs(mp, sbp, td) 422df8bae1dSRodney W. Grimes struct mount *mp; 423df8bae1dSRodney W. Grimes struct statfs *sbp; 424b40ce416SJulian Elischer struct thread *td; 425df8bae1dSRodney W. Grimes { 426df8bae1dSRodney W. Grimes int error; 427df8bae1dSRodney W. Grimes struct union_mount *um = MOUNTTOUNIONMOUNT(mp); 428df8bae1dSRodney W. Grimes struct statfs mstat; 429df8bae1dSRodney W. Grimes int lbsize; 430df8bae1dSRodney W. Grimes 4312a31267eSMatthew Dillon UDEBUG(("union_statfs(mp = %p, lvp = %p, uvp = %p)\n", 4322a31267eSMatthew Dillon (void *)mp, (void *)um->um_lowervp, (void *)um->um_uppervp)); 433df8bae1dSRodney W. Grimes 434df8bae1dSRodney W. Grimes bzero(&mstat, sizeof(mstat)); 435df8bae1dSRodney W. Grimes 436df8bae1dSRodney W. Grimes if (um->um_lowervp) { 437b40ce416SJulian Elischer error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, td); 438df8bae1dSRodney W. Grimes if (error) 439df8bae1dSRodney W. Grimes return (error); 440df8bae1dSRodney W. Grimes } 441df8bae1dSRodney W. Grimes 4421c4ccf09SDon Lewis /* 4431c4ccf09SDon Lewis * Now copy across the "interesting" information and fake the rest. 4441c4ccf09SDon Lewis */ 445df8bae1dSRodney W. Grimes #if 0 446df8bae1dSRodney W. Grimes sbp->f_type = mstat.f_type; 447df8bae1dSRodney W. Grimes sbp->f_flags = mstat.f_flags; 448df8bae1dSRodney W. Grimes sbp->f_bsize = mstat.f_bsize; 449df8bae1dSRodney W. Grimes sbp->f_iosize = mstat.f_iosize; 450df8bae1dSRodney W. Grimes #endif 451df8bae1dSRodney W. Grimes lbsize = mstat.f_bsize; 452df8bae1dSRodney W. Grimes sbp->f_blocks = mstat.f_blocks; 453df8bae1dSRodney W. Grimes sbp->f_bfree = mstat.f_bfree; 454df8bae1dSRodney W. Grimes sbp->f_bavail = mstat.f_bavail; 455df8bae1dSRodney W. Grimes sbp->f_files = mstat.f_files; 456df8bae1dSRodney W. Grimes sbp->f_ffree = mstat.f_ffree; 457df8bae1dSRodney W. Grimes 458b40ce416SJulian Elischer error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, td); 459df8bae1dSRodney W. Grimes if (error) 460df8bae1dSRodney W. Grimes return (error); 461df8bae1dSRodney W. Grimes 462df8bae1dSRodney W. Grimes sbp->f_flags = mstat.f_flags; 463df8bae1dSRodney W. Grimes sbp->f_bsize = mstat.f_bsize; 464df8bae1dSRodney W. Grimes sbp->f_iosize = mstat.f_iosize; 465df8bae1dSRodney W. Grimes 466df8bae1dSRodney W. Grimes /* 4671c4ccf09SDon Lewis * If the lower and upper blocksizes differ, then frig the 468df8bae1dSRodney W. Grimes * block counts so that the sizes reported by df make some 4691c4ccf09SDon Lewis * kind of sense. None of this makes sense though. 470df8bae1dSRodney W. Grimes */ 471df8bae1dSRodney W. Grimes 472996c772fSJohn Dyson if (mstat.f_bsize != lbsize) 473c9bf0111SKATO Takenori sbp->f_blocks = ((off_t) sbp->f_blocks * lbsize) / mstat.f_bsize; 474996c772fSJohn Dyson 475996c772fSJohn Dyson /* 476996c772fSJohn Dyson * The "total" fields count total resources in all layers, 477996c772fSJohn Dyson * the "free" fields count only those resources which are 478996c772fSJohn Dyson * free in the upper layer (since only the upper layer 479996c772fSJohn Dyson * is writeable). 480996c772fSJohn Dyson */ 481df8bae1dSRodney W. Grimes sbp->f_blocks += mstat.f_blocks; 482996c772fSJohn Dyson sbp->f_bfree = mstat.f_bfree; 483996c772fSJohn Dyson sbp->f_bavail = mstat.f_bavail; 484df8bae1dSRodney W. Grimes sbp->f_files += mstat.f_files; 485996c772fSJohn Dyson sbp->f_ffree = mstat.f_ffree; 486df8bae1dSRodney W. Grimes 487df8bae1dSRodney W. Grimes if (sbp != &mp->mnt_stat) { 488996c772fSJohn Dyson sbp->f_type = mp->mnt_vfc->vfc_typenum; 489df8bae1dSRodney W. Grimes bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); 490df8bae1dSRodney W. Grimes bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 491df8bae1dSRodney W. Grimes bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 492df8bae1dSRodney W. Grimes } 493df8bae1dSRodney W. Grimes return (0); 494df8bae1dSRodney W. Grimes } 495df8bae1dSRodney W. Grimes 49680b301c3SPoul-Henning Kamp static struct vfsops union_vfsops = { 4977652131bSPoul-Henning Kamp .vfs_init = union_init, 4985e8c582aSPoul-Henning Kamp .vfs_mount = union_mount, 4997652131bSPoul-Henning Kamp .vfs_root = union_root, 5007652131bSPoul-Henning Kamp .vfs_statfs = union_statfs, 5017652131bSPoul-Henning Kamp .vfs_unmount = union_unmount, 502df8bae1dSRodney W. Grimes }; 503c901836cSGarrett Wollman 504c7b23e0fSRuslan Ermilov VFS_SET(union_vfsops, unionfs, VFCF_LOOPBACK); 505