1d167cf6fSWarner Losh /*- 2996c772fSJohn Dyson * Copyright (c) 1994, 1995 The Regents of the University of California. 3996c772fSJohn Dyson * Copyright (c) 1994, 1995 Jan-Simon Pendry. 4cb5736b7SDaichi GOTO * Copyright (c) 2005, 2006, 2012 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc. 5cb5736b7SDaichi GOTO * Copyright (c) 2006, 2012 Daichi Goto <daichi@freebsd.org> 6df8bae1dSRodney W. Grimes * All rights reserved. 7df8bae1dSRodney W. Grimes * 8df8bae1dSRodney W. Grimes * This code is derived from software donated to Berkeley by 9df8bae1dSRodney W. Grimes * Jan-Simon Pendry. 10df8bae1dSRodney W. Grimes * 11df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 12df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 13df8bae1dSRodney W. Grimes * are met: 14df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 15df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 16df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 17df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 18df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 19*fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 20df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 21df8bae1dSRodney W. Grimes * without specific prior written permission. 22df8bae1dSRodney W. Grimes * 23df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33df8bae1dSRodney W. Grimes * SUCH DAMAGE. 34df8bae1dSRodney W. Grimes * 35996c772fSJohn Dyson * @(#)union_vfsops.c 8.20 (Berkeley) 5/20/95 36c3aac50fSPeter Wemm * $FreeBSD$ 37df8bae1dSRodney W. Grimes */ 38df8bae1dSRodney W. Grimes 39df8bae1dSRodney W. Grimes #include <sys/param.h> 40df8bae1dSRodney W. Grimes #include <sys/systm.h> 41d00947d8SCraig Rodrigues #include <sys/kdb.h> 4257b4252eSKonstantin Belousov #include <sys/fcntl.h> 43c9b1d604SGarrett Wollman #include <sys/kernel.h> 44805d90f7SJohn Baldwin #include <sys/lock.h> 45d00947d8SCraig Rodrigues #include <sys/malloc.h> 46df8bae1dSRodney W. Grimes #include <sys/mount.h> 47df8bae1dSRodney W. Grimes #include <sys/namei.h> 48d00947d8SCraig Rodrigues #include <sys/proc.h> 49d00947d8SCraig Rodrigues #include <sys/vnode.h> 50d00947d8SCraig Rodrigues #include <sys/stat.h> 51d00947d8SCraig Rodrigues 5299d300a1SRuslan Ermilov #include <fs/unionfs/union.h> 53df8bae1dSRodney W. Grimes 54d00947d8SCraig Rodrigues static MALLOC_DEFINE(M_UNIONFSMNT, "UNIONFS mount", "UNIONFS mount structure"); 55a1c995b6SPoul-Henning Kamp 56d00947d8SCraig Rodrigues static vfs_fhtovp_t unionfs_fhtovp; 57d00947d8SCraig Rodrigues static vfs_checkexp_t unionfs_checkexp; 58d00947d8SCraig Rodrigues static vfs_mount_t unionfs_domount; 59d00947d8SCraig Rodrigues static vfs_quotactl_t unionfs_quotactl; 60d00947d8SCraig Rodrigues static vfs_root_t unionfs_root; 61d00947d8SCraig Rodrigues static vfs_sync_t unionfs_sync; 62d00947d8SCraig Rodrigues static vfs_statfs_t unionfs_statfs; 63d00947d8SCraig Rodrigues static vfs_unmount_t unionfs_unmount; 64d00947d8SCraig Rodrigues static vfs_vget_t unionfs_vget; 65d00947d8SCraig Rodrigues static vfs_extattrctl_t unionfs_extattrctl; 66d00947d8SCraig Rodrigues 67d00947d8SCraig Rodrigues static struct vfsops unionfs_vfsops; 68b5e8ce9fSBruce Evans 69df8bae1dSRodney W. Grimes /* 70d00947d8SCraig Rodrigues * Mount unionfs layer. 71df8bae1dSRodney W. Grimes */ 7280b301c3SPoul-Henning Kamp static int 73dfd233edSAttilio Rao unionfs_domount(struct mount *mp) 74df8bae1dSRodney W. Grimes { 75d00947d8SCraig Rodrigues int error; 76d00947d8SCraig Rodrigues struct vnode *lowerrootvp; 77d00947d8SCraig Rodrigues struct vnode *upperrootvp; 78d00947d8SCraig Rodrigues struct unionfs_mount *ump; 79dfd233edSAttilio Rao struct thread *td; 80d00947d8SCraig Rodrigues char *target; 81d00947d8SCraig Rodrigues char *tmp; 82d00947d8SCraig Rodrigues char *ep; 83df8bae1dSRodney W. Grimes int len; 84d00947d8SCraig Rodrigues size_t done; 85d00947d8SCraig Rodrigues int below; 86d00947d8SCraig Rodrigues uid_t uid; 87d00947d8SCraig Rodrigues gid_t gid; 88d00947d8SCraig Rodrigues u_short udir; 89d00947d8SCraig Rodrigues u_short ufile; 90d00947d8SCraig Rodrigues unionfs_copymode copymode; 9120885defSDaichi GOTO unionfs_whitemode whitemode; 92d00947d8SCraig Rodrigues struct nameidata nd, *ndp; 93d00947d8SCraig Rodrigues struct vattr va; 94df8bae1dSRodney W. Grimes 95d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount(mp = %p)\n", (void *)mp); 96df8bae1dSRodney W. Grimes 97d00947d8SCraig Rodrigues error = 0; 98d00947d8SCraig Rodrigues below = 0; 99d00947d8SCraig Rodrigues uid = 0; 100d00947d8SCraig Rodrigues gid = 0; 101d00947d8SCraig Rodrigues udir = 0; 102d00947d8SCraig Rodrigues ufile = 0; 103524f3f28SDaichi GOTO copymode = UNIONFS_TRANSPARENT; /* default */ 10420885defSDaichi GOTO whitemode = UNIONFS_WHITE_ALWAYS; 105d00947d8SCraig Rodrigues ndp = &nd; 106dfd233edSAttilio Rao td = curthread; 10781bca6ddSKATO Takenori 1081e370dbbSCraig Rodrigues if (mp->mnt_flag & MNT_ROOTFS) { 1091e370dbbSCraig Rodrigues vfs_mount_error(mp, "Cannot union mount root filesystem"); 11064042a76SPoul-Henning Kamp return (EOPNOTSUPP); 1111e370dbbSCraig Rodrigues } 112d00947d8SCraig Rodrigues 11381bca6ddSKATO Takenori /* 114d00947d8SCraig Rodrigues * Update is a no operation. 115df8bae1dSRodney W. Grimes */ 1161e370dbbSCraig Rodrigues if (mp->mnt_flag & MNT_UPDATE) { 1171e370dbbSCraig Rodrigues vfs_mount_error(mp, "unionfs does not support mount update"); 118a9f5c04aSMaxime Henrion return (EOPNOTSUPP); 1191e370dbbSCraig Rodrigues } 120df8bae1dSRodney W. Grimes 121df8bae1dSRodney W. Grimes /* 122d00947d8SCraig Rodrigues * Get argument 123df8bae1dSRodney W. Grimes */ 124d00947d8SCraig Rodrigues error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len); 1253a773ad0SPoul-Henning Kamp if (error) 126d00947d8SCraig Rodrigues error = vfs_getopt(mp->mnt_optnew, "from", (void **)&target, 127d00947d8SCraig Rodrigues &len); 128d00947d8SCraig Rodrigues if (error || target[len - 1] != '\0') { 129d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid target"); 130d00947d8SCraig Rodrigues return (EINVAL); 131d00947d8SCraig Rodrigues } 132d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "below", NULL, NULL) == 0) 133d00947d8SCraig Rodrigues below = 1; 134d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "udir", (void **)&tmp, NULL) == 0) { 135d00947d8SCraig Rodrigues if (tmp != NULL) 136d00947d8SCraig Rodrigues udir = (mode_t)strtol(tmp, &ep, 8); 137d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 138d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid udir"); 139d00947d8SCraig Rodrigues return (EINVAL); 140d00947d8SCraig Rodrigues } 14116385727SDaichi GOTO udir &= S_IRWXU | S_IRWXG | S_IRWXO; 142d00947d8SCraig Rodrigues } 143d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "ufile", (void **)&tmp, NULL) == 0) { 144d00947d8SCraig Rodrigues if (tmp != NULL) 145d00947d8SCraig Rodrigues ufile = (mode_t)strtol(tmp, &ep, 8); 146d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 147d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid ufile"); 148d00947d8SCraig Rodrigues return (EINVAL); 149d00947d8SCraig Rodrigues } 15016385727SDaichi GOTO ufile &= S_IRWXU | S_IRWXG | S_IRWXO; 151d00947d8SCraig Rodrigues } 152d00947d8SCraig Rodrigues /* check umask, uid and gid */ 153d00947d8SCraig Rodrigues if (udir == 0 && ufile != 0) 154d00947d8SCraig Rodrigues udir = ufile; 155d00947d8SCraig Rodrigues if (ufile == 0 && udir != 0) 156d00947d8SCraig Rodrigues ufile = udir; 157d00947d8SCraig Rodrigues 158cb05b60aSAttilio Rao vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY); 1590359a12eSAttilio Rao error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred); 160d00947d8SCraig Rodrigues if (!error) { 161d00947d8SCraig Rodrigues if (udir == 0) 162d00947d8SCraig Rodrigues udir = va.va_mode; 163d00947d8SCraig Rodrigues if (ufile == 0) 164d00947d8SCraig Rodrigues ufile = va.va_mode; 165d00947d8SCraig Rodrigues uid = va.va_uid; 166d00947d8SCraig Rodrigues gid = va.va_gid; 167d00947d8SCraig Rodrigues } 168cb5736b7SDaichi GOTO VOP_UNLOCK(mp->mnt_vnodecovered, LK_RELEASE); 169d00947d8SCraig Rodrigues if (error) 170d00947d8SCraig Rodrigues return (error); 171d00947d8SCraig Rodrigues 172d00947d8SCraig Rodrigues if (mp->mnt_cred->cr_ruid == 0) { /* root only */ 173d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "uid", (void **)&tmp, 174d00947d8SCraig Rodrigues NULL) == 0) { 175d00947d8SCraig Rodrigues if (tmp != NULL) 176d00947d8SCraig Rodrigues uid = (uid_t)strtol(tmp, &ep, 10); 177d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 178d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid uid"); 179d00947d8SCraig Rodrigues return (EINVAL); 180d00947d8SCraig Rodrigues } 181d00947d8SCraig Rodrigues } 182d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "gid", (void **)&tmp, 183d00947d8SCraig Rodrigues NULL) == 0) { 184d00947d8SCraig Rodrigues if (tmp != NULL) 185d00947d8SCraig Rodrigues gid = (gid_t)strtol(tmp, &ep, 10); 186d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 187d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid gid"); 188d00947d8SCraig Rodrigues return (EINVAL); 189d00947d8SCraig Rodrigues } 190d00947d8SCraig Rodrigues } 191d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "copymode", (void **)&tmp, 192d00947d8SCraig Rodrigues NULL) == 0) { 193d00947d8SCraig Rodrigues if (tmp == NULL) { 194d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid copymode"); 195d00947d8SCraig Rodrigues return (EINVAL); 196d00947d8SCraig Rodrigues } else if (strcasecmp(tmp, "traditional") == 0) 197d00947d8SCraig Rodrigues copymode = UNIONFS_TRADITIONAL; 198d00947d8SCraig Rodrigues else if (strcasecmp(tmp, "transparent") == 0) 199d00947d8SCraig Rodrigues copymode = UNIONFS_TRANSPARENT; 200d00947d8SCraig Rodrigues else if (strcasecmp(tmp, "masquerade") == 0) 201d00947d8SCraig Rodrigues copymode = UNIONFS_MASQUERADE; 202d00947d8SCraig Rodrigues else { 203d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid copymode"); 204d00947d8SCraig Rodrigues return (EINVAL); 205d00947d8SCraig Rodrigues } 206d00947d8SCraig Rodrigues } 20720885defSDaichi GOTO if (vfs_getopt(mp->mnt_optnew, "whiteout", (void **)&tmp, 20820885defSDaichi GOTO NULL) == 0) { 20920885defSDaichi GOTO if (tmp == NULL) { 21020885defSDaichi GOTO vfs_mount_error(mp, "Invalid whiteout mode"); 21120885defSDaichi GOTO return (EINVAL); 21220885defSDaichi GOTO } else if (strcasecmp(tmp, "always") == 0) 21320885defSDaichi GOTO whitemode = UNIONFS_WHITE_ALWAYS; 21420885defSDaichi GOTO else if (strcasecmp(tmp, "whenneeded") == 0) 21520885defSDaichi GOTO whitemode = UNIONFS_WHITE_WHENNEEDED; 21620885defSDaichi GOTO else { 21720885defSDaichi GOTO vfs_mount_error(mp, "Invalid whiteout mode"); 21820885defSDaichi GOTO return (EINVAL); 21920885defSDaichi GOTO } 22020885defSDaichi GOTO } 221d00947d8SCraig Rodrigues } 222d00947d8SCraig Rodrigues /* If copymode is UNIONFS_TRADITIONAL, uid/gid is mounted user. */ 223d00947d8SCraig Rodrigues if (copymode == UNIONFS_TRADITIONAL) { 224d00947d8SCraig Rodrigues uid = mp->mnt_cred->cr_ruid; 225d00947d8SCraig Rodrigues gid = mp->mnt_cred->cr_rgid; 226d00947d8SCraig Rodrigues } 227d00947d8SCraig Rodrigues 228d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: uid=%d, gid=%d\n", uid, gid); 229d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: udir=0%03o, ufile=0%03o\n", udir, ufile); 230d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: copymode=%d\n", copymode); 231d00947d8SCraig Rodrigues 232d00947d8SCraig Rodrigues /* 233d00947d8SCraig Rodrigues * Find upper node 234d00947d8SCraig Rodrigues */ 2357265164fSJohn Baldwin NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, target, td); 236d00947d8SCraig Rodrigues if ((error = namei(ndp))) 237d00947d8SCraig Rodrigues return (error); 238d00947d8SCraig Rodrigues 239762e6b85SEivind Eklund NDFREE(ndp, NDF_ONLY_PNBUF); 240d00947d8SCraig Rodrigues 241d00947d8SCraig Rodrigues /* get root vnodes */ 242d00947d8SCraig Rodrigues lowerrootvp = mp->mnt_vnodecovered; 243df8bae1dSRodney W. Grimes upperrootvp = ndp->ni_vp; 244df8bae1dSRodney W. Grimes 245d00947d8SCraig Rodrigues /* create unionfs_mount */ 246d00947d8SCraig Rodrigues ump = (struct unionfs_mount *)malloc(sizeof(struct unionfs_mount), 247a163d034SWarner Losh M_UNIONFSMNT, M_WAITOK | M_ZERO); 2482a31267eSMatthew Dillon 249d00947d8SCraig Rodrigues /* 250d00947d8SCraig Rodrigues * Save reference 251d00947d8SCraig Rodrigues */ 252d00947d8SCraig Rodrigues if (below) { 253cb5736b7SDaichi GOTO VOP_UNLOCK(upperrootvp, LK_RELEASE); 254cb05b60aSAttilio Rao vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY); 255d00947d8SCraig Rodrigues ump->um_lowervp = upperrootvp; 256d00947d8SCraig Rodrigues ump->um_uppervp = lowerrootvp; 257d00947d8SCraig Rodrigues } else { 258d00947d8SCraig Rodrigues ump->um_lowervp = lowerrootvp; 259d00947d8SCraig Rodrigues ump->um_uppervp = upperrootvp; 260df8bae1dSRodney W. Grimes } 261d00947d8SCraig Rodrigues ump->um_rootvp = NULLVP; 262d00947d8SCraig Rodrigues ump->um_uid = uid; 263d00947d8SCraig Rodrigues ump->um_gid = gid; 264d00947d8SCraig Rodrigues ump->um_udir = udir; 265d00947d8SCraig Rodrigues ump->um_ufile = ufile; 266d00947d8SCraig Rodrigues ump->um_copymode = copymode; 26720885defSDaichi GOTO ump->um_whitemode = whitemode; 268d00947d8SCraig Rodrigues 26977465d93SAlfred Perlstein mp->mnt_data = ump; 270df8bae1dSRodney W. Grimes 271996c772fSJohn Dyson /* 272d00947d8SCraig Rodrigues * Copy upper layer's RDONLY flag. 273d00947d8SCraig Rodrigues */ 274d00947d8SCraig Rodrigues mp->mnt_flag |= ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY; 275d00947d8SCraig Rodrigues 276d00947d8SCraig Rodrigues /* 277d00947d8SCraig Rodrigues * Unlock the node 278df8bae1dSRodney W. Grimes */ 279cb5736b7SDaichi GOTO VOP_UNLOCK(ump->um_uppervp, LK_RELEASE); 280df8bae1dSRodney W. Grimes 281d00947d8SCraig Rodrigues /* 282d00947d8SCraig Rodrigues * Get the unionfs root vnode. 283d00947d8SCraig Rodrigues */ 284d00947d8SCraig Rodrigues error = unionfs_nodeget(mp, ump->um_uppervp, ump->um_lowervp, 285d00947d8SCraig Rodrigues NULLVP, &(ump->um_rootvp), NULL, td); 286d00947d8SCraig Rodrigues vrele(upperrootvp); 2877d72c5e6SDaichi GOTO if (error) { 288d00947d8SCraig Rodrigues free(ump, M_UNIONFSMNT); 289d00947d8SCraig Rodrigues mp->mnt_data = NULL; 290df8bae1dSRodney W. Grimes return (error); 291df8bae1dSRodney W. Grimes } 292df8bae1dSRodney W. Grimes 293a8a07fd6SMateusz Guzik MNT_ILOCK(mp); 294d00947d8SCraig Rodrigues if ((ump->um_lowervp->v_mount->mnt_flag & MNT_LOCAL) && 295d00947d8SCraig Rodrigues (ump->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) 296d00947d8SCraig Rodrigues mp->mnt_flag |= MNT_LOCAL; 297a8a07fd6SMateusz Guzik MNT_IUNLOCK(mp); 298d00947d8SCraig Rodrigues 299d00947d8SCraig Rodrigues /* 300d00947d8SCraig Rodrigues * Get new fsid 301d00947d8SCraig Rodrigues */ 302d00947d8SCraig Rodrigues vfs_getnewfsid(mp); 303d00947d8SCraig Rodrigues 304d00947d8SCraig Rodrigues len = MNAMELEN - 1; 305d00947d8SCraig Rodrigues tmp = mp->mnt_stat.f_mntfromname; 306d00947d8SCraig Rodrigues copystr((below ? "<below>:" : "<above>:"), tmp, len, &done); 307d00947d8SCraig Rodrigues len -= done - 1; 308d00947d8SCraig Rodrigues tmp += done - 1; 309d00947d8SCraig Rodrigues copystr(target, tmp, len, NULL); 310d00947d8SCraig Rodrigues 311d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: from %s, on %s\n", 312d00947d8SCraig Rodrigues mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 313d00947d8SCraig Rodrigues 314d00947d8SCraig Rodrigues return (0); 315d00947d8SCraig Rodrigues } 316d00947d8SCraig Rodrigues 317d00947d8SCraig Rodrigues /* 318d00947d8SCraig Rodrigues * Free reference to unionfs layer 319df8bae1dSRodney W. Grimes */ 32080b301c3SPoul-Henning Kamp static int 321dfd233edSAttilio Rao unionfs_unmount(struct mount *mp, int mntflags) 322df8bae1dSRodney W. Grimes { 323d00947d8SCraig Rodrigues struct unionfs_mount *ump; 324df8bae1dSRodney W. Grimes int error; 325d00947d8SCraig Rodrigues int num; 326996c772fSJohn Dyson int freeing; 327d00947d8SCraig Rodrigues int flags; 328df8bae1dSRodney W. Grimes 329d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_unmount: mp = %p\n", (void *)mp); 330d00947d8SCraig Rodrigues 331d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 332d00947d8SCraig Rodrigues flags = 0; 333df8bae1dSRodney W. Grimes 334996c772fSJohn Dyson if (mntflags & MNT_FORCE) 335996c772fSJohn Dyson flags |= FORCECLOSE; 336996c772fSJohn Dyson 337d00947d8SCraig Rodrigues /* vflush (no need to call vrele) */ 338dfd233edSAttilio Rao for (freeing = 0; (error = vflush(mp, 1, flags, curthread)) != 0;) { 339d00947d8SCraig Rodrigues num = mp->mnt_nvnodelistsize; 340d00947d8SCraig Rodrigues if (num == freeing) 341996c772fSJohn Dyson break; 342d00947d8SCraig Rodrigues freeing = num; 343df8bae1dSRodney W. Grimes } 344df8bae1dSRodney W. Grimes 3450864ef1eSIan Dowse if (error) 3460864ef1eSIan Dowse return (error); 347df8bae1dSRodney W. Grimes 348d00947d8SCraig Rodrigues free(ump, M_UNIONFSMNT); 34911753bd0SKevin Lo mp->mnt_data = NULL; 350d00947d8SCraig Rodrigues 351df8bae1dSRodney W. Grimes return (0); 352df8bae1dSRodney W. Grimes } 353df8bae1dSRodney W. Grimes 35480b301c3SPoul-Henning Kamp static int 355dfd233edSAttilio Rao unionfs_root(struct mount *mp, int flags, struct vnode **vpp) 356df8bae1dSRodney W. Grimes { 357d00947d8SCraig Rodrigues struct unionfs_mount *ump; 358d00947d8SCraig Rodrigues struct vnode *vp; 359df8bae1dSRodney W. Grimes 360d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 361d00947d8SCraig Rodrigues vp = ump->um_rootvp; 3622a31267eSMatthew Dillon 363d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_root: rootvp=%p locked=%x\n", 36481c794f9SAttilio Rao vp, VOP_ISLOCKED(vp)); 365df8bae1dSRodney W. Grimes 366d00947d8SCraig Rodrigues vref(vp); 367d00947d8SCraig Rodrigues if (flags & LK_TYPE_MASK) 368cb05b60aSAttilio Rao vn_lock(vp, flags); 369df8bae1dSRodney W. Grimes 370d00947d8SCraig Rodrigues *vpp = vp; 371d00947d8SCraig Rodrigues 372d00947d8SCraig Rodrigues return (0); 373df8bae1dSRodney W. Grimes } 374df8bae1dSRodney W. Grimes 37580b301c3SPoul-Henning Kamp static int 376dfd233edSAttilio Rao unionfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg) 377df8bae1dSRodney W. Grimes { 378d00947d8SCraig Rodrigues struct unionfs_mount *ump; 379df8bae1dSRodney W. Grimes 380d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 381d00947d8SCraig Rodrigues 382d00947d8SCraig Rodrigues /* 383d00947d8SCraig Rodrigues * Writing is always performed to upper vnode. 384d00947d8SCraig Rodrigues */ 385dfd233edSAttilio Rao return (VFS_QUOTACTL(ump->um_uppervp->v_mount, cmd, uid, arg)); 386d00947d8SCraig Rodrigues } 387d00947d8SCraig Rodrigues 388d00947d8SCraig Rodrigues static int 389dfd233edSAttilio Rao unionfs_statfs(struct mount *mp, struct statfs *sbp) 390d00947d8SCraig Rodrigues { 391d00947d8SCraig Rodrigues struct unionfs_mount *ump; 392d00947d8SCraig Rodrigues int error; 3932f304845SKonstantin Belousov struct statfs *mstat; 394d00947d8SCraig Rodrigues uint64_t lbsize; 395d00947d8SCraig Rodrigues 396d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 397d00947d8SCraig Rodrigues 398d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_statfs(mp = %p, lvp = %p, uvp = %p)\n", 399d00947d8SCraig Rodrigues (void *)mp, (void *)ump->um_lowervp, (void *)ump->um_uppervp); 400df8bae1dSRodney W. Grimes 4012f304845SKonstantin Belousov mstat = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK | M_ZERO); 402df8bae1dSRodney W. Grimes 4032f304845SKonstantin Belousov error = VFS_STATFS(ump->um_lowervp->v_mount, mstat); 4042f304845SKonstantin Belousov if (error) { 4052f304845SKonstantin Belousov free(mstat, M_STATFS); 406df8bae1dSRodney W. Grimes return (error); 4072f304845SKonstantin Belousov } 408d00947d8SCraig Rodrigues 409d00947d8SCraig Rodrigues /* now copy across the "interesting" information and fake the rest */ 4102f304845SKonstantin Belousov sbp->f_blocks = mstat->f_blocks; 4112f304845SKonstantin Belousov sbp->f_files = mstat->f_files; 412d00947d8SCraig Rodrigues 4132f304845SKonstantin Belousov lbsize = mstat->f_bsize; 414d00947d8SCraig Rodrigues 4152f304845SKonstantin Belousov error = VFS_STATFS(ump->um_uppervp->v_mount, mstat); 4162f304845SKonstantin Belousov if (error) { 4172f304845SKonstantin Belousov free(mstat, M_STATFS); 418d00947d8SCraig Rodrigues return (error); 4192f304845SKonstantin Belousov } 4202f304845SKonstantin Belousov 421df8bae1dSRodney W. Grimes 4221c4ccf09SDon Lewis /* 423d00947d8SCraig Rodrigues * The FS type etc is copy from upper vfs. 424d00947d8SCraig Rodrigues * (write able vfs have priority) 4251c4ccf09SDon Lewis */ 4262f304845SKonstantin Belousov sbp->f_type = mstat->f_type; 4272f304845SKonstantin Belousov sbp->f_flags = mstat->f_flags; 4282f304845SKonstantin Belousov sbp->f_bsize = mstat->f_bsize; 4292f304845SKonstantin Belousov sbp->f_iosize = mstat->f_iosize; 430df8bae1dSRodney W. Grimes 4312f304845SKonstantin Belousov if (mstat->f_bsize != lbsize) 4322f304845SKonstantin Belousov sbp->f_blocks = ((off_t)sbp->f_blocks * lbsize) / 4332f304845SKonstantin Belousov mstat->f_bsize; 434996c772fSJohn Dyson 4352f304845SKonstantin Belousov sbp->f_blocks += mstat->f_blocks; 4362f304845SKonstantin Belousov sbp->f_bfree = mstat->f_bfree; 4372f304845SKonstantin Belousov sbp->f_bavail = mstat->f_bavail; 4382f304845SKonstantin Belousov sbp->f_files += mstat->f_files; 4392f304845SKonstantin Belousov sbp->f_ffree = mstat->f_ffree; 4402f304845SKonstantin Belousov 4412f304845SKonstantin Belousov free(mstat, M_STATFS); 442df8bae1dSRodney W. Grimes return (0); 443df8bae1dSRodney W. Grimes } 444df8bae1dSRodney W. Grimes 445d00947d8SCraig Rodrigues static int 446dfd233edSAttilio Rao unionfs_sync(struct mount *mp, int waitfor) 447d00947d8SCraig Rodrigues { 448d00947d8SCraig Rodrigues /* nothing to do */ 449d00947d8SCraig Rodrigues return (0); 450d00947d8SCraig Rodrigues } 451d00947d8SCraig Rodrigues 452d00947d8SCraig Rodrigues static int 453d00947d8SCraig Rodrigues unionfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) 454d00947d8SCraig Rodrigues { 455d00947d8SCraig Rodrigues return (EOPNOTSUPP); 456d00947d8SCraig Rodrigues } 457d00947d8SCraig Rodrigues 458d00947d8SCraig Rodrigues static int 459694a586aSRick Macklem unionfs_fhtovp(struct mount *mp, struct fid *fidp, int flags, 460694a586aSRick Macklem struct vnode **vpp) 461d00947d8SCraig Rodrigues { 462d00947d8SCraig Rodrigues return (EOPNOTSUPP); 463d00947d8SCraig Rodrigues } 464d00947d8SCraig Rodrigues 465d00947d8SCraig Rodrigues static int 466d00947d8SCraig Rodrigues unionfs_checkexp(struct mount *mp, struct sockaddr *nam, int *extflagsp, 467a9148abdSDoug Rabson struct ucred **credanonp, int *numsecflavors, int **secflavors) 468d00947d8SCraig Rodrigues { 469d00947d8SCraig Rodrigues return (EOPNOTSUPP); 470d00947d8SCraig Rodrigues } 471d00947d8SCraig Rodrigues 472d00947d8SCraig Rodrigues static int 473d00947d8SCraig Rodrigues unionfs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, 474dfd233edSAttilio Rao int namespace, const char *attrname) 475d00947d8SCraig Rodrigues { 476d00947d8SCraig Rodrigues struct unionfs_mount *ump; 477d00947d8SCraig Rodrigues struct unionfs_node *unp; 478d00947d8SCraig Rodrigues 479d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 480d00947d8SCraig Rodrigues unp = VTOUNIONFS(filename_vp); 481d00947d8SCraig Rodrigues 482d00947d8SCraig Rodrigues if (unp->un_uppervp != NULLVP) { 483d00947d8SCraig Rodrigues return (VFS_EXTATTRCTL(ump->um_uppervp->v_mount, cmd, 484dfd233edSAttilio Rao unp->un_uppervp, namespace, attrname)); 485d00947d8SCraig Rodrigues } else { 486d00947d8SCraig Rodrigues return (VFS_EXTATTRCTL(ump->um_lowervp->v_mount, cmd, 487dfd233edSAttilio Rao unp->un_lowervp, namespace, attrname)); 488d00947d8SCraig Rodrigues } 489d00947d8SCraig Rodrigues } 490d00947d8SCraig Rodrigues 491d00947d8SCraig Rodrigues static struct vfsops unionfs_vfsops = { 492d00947d8SCraig Rodrigues .vfs_checkexp = unionfs_checkexp, 493d00947d8SCraig Rodrigues .vfs_extattrctl = unionfs_extattrctl, 494d00947d8SCraig Rodrigues .vfs_fhtovp = unionfs_fhtovp, 495d00947d8SCraig Rodrigues .vfs_init = unionfs_init, 496d00947d8SCraig Rodrigues .vfs_mount = unionfs_domount, 497d00947d8SCraig Rodrigues .vfs_quotactl = unionfs_quotactl, 498d00947d8SCraig Rodrigues .vfs_root = unionfs_root, 499d00947d8SCraig Rodrigues .vfs_statfs = unionfs_statfs, 500d00947d8SCraig Rodrigues .vfs_sync = unionfs_sync, 501d00947d8SCraig Rodrigues .vfs_uninit = unionfs_uninit, 502d00947d8SCraig Rodrigues .vfs_unmount = unionfs_unmount, 503d00947d8SCraig Rodrigues .vfs_vget = unionfs_vget, 504df8bae1dSRodney W. Grimes }; 505c901836cSGarrett Wollman 506d00947d8SCraig Rodrigues VFS_SET(unionfs_vfsops, unionfs, VFCF_LOOPBACK); 507