1d167cf6fSWarner Losh /*- 251369649SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 351369649SPedro F. Giffuni * 4996c772fSJohn Dyson * Copyright (c) 1994, 1995 The Regents of the University of California. 5996c772fSJohn Dyson * Copyright (c) 1994, 1995 Jan-Simon Pendry. 6cb5736b7SDaichi GOTO * Copyright (c) 2005, 2006, 2012 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc. 7cb5736b7SDaichi GOTO * Copyright (c) 2006, 2012 Daichi Goto <daichi@freebsd.org> 8df8bae1dSRodney W. Grimes * All rights reserved. 9df8bae1dSRodney W. Grimes * 10df8bae1dSRodney W. Grimes * This code is derived from software donated to Berkeley by 11df8bae1dSRodney W. Grimes * Jan-Simon Pendry. 12df8bae1dSRodney W. Grimes * 13df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 14df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 15df8bae1dSRodney W. Grimes * are met: 16df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 17df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 18df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 19df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 20df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 21fbbd9655SWarner Losh * 3. 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 */ 37df8bae1dSRodney W. Grimes 38df8bae1dSRodney W. Grimes #include <sys/param.h> 39df8bae1dSRodney W. Grimes #include <sys/systm.h> 40d00947d8SCraig Rodrigues #include <sys/kdb.h> 4157b4252eSKonstantin Belousov #include <sys/fcntl.h> 42c9b1d604SGarrett Wollman #include <sys/kernel.h> 43805d90f7SJohn Baldwin #include <sys/lock.h> 44d00947d8SCraig Rodrigues #include <sys/malloc.h> 45df8bae1dSRodney W. Grimes #include <sys/mount.h> 46df8bae1dSRodney W. Grimes #include <sys/namei.h> 47d00947d8SCraig Rodrigues #include <sys/proc.h> 48d00947d8SCraig Rodrigues #include <sys/vnode.h> 49d00947d8SCraig Rodrigues #include <sys/stat.h> 50d00947d8SCraig Rodrigues 5199d300a1SRuslan Ermilov #include <fs/unionfs/union.h> 52df8bae1dSRodney W. Grimes 53d00947d8SCraig Rodrigues static MALLOC_DEFINE(M_UNIONFSMNT, "UNIONFS mount", "UNIONFS mount structure"); 54a1c995b6SPoul-Henning Kamp 55d00947d8SCraig Rodrigues static vfs_fhtovp_t unionfs_fhtovp; 56d00947d8SCraig Rodrigues static vfs_checkexp_t unionfs_checkexp; 57d00947d8SCraig Rodrigues static vfs_mount_t unionfs_domount; 58d00947d8SCraig Rodrigues static vfs_quotactl_t unionfs_quotactl; 59d00947d8SCraig Rodrigues static vfs_root_t unionfs_root; 60d00947d8SCraig Rodrigues static vfs_sync_t unionfs_sync; 61d00947d8SCraig Rodrigues static vfs_statfs_t unionfs_statfs; 62d00947d8SCraig Rodrigues static vfs_unmount_t unionfs_unmount; 63d00947d8SCraig Rodrigues static vfs_vget_t unionfs_vget; 64d00947d8SCraig Rodrigues static vfs_extattrctl_t unionfs_extattrctl; 65d00947d8SCraig Rodrigues 66d00947d8SCraig Rodrigues static struct vfsops unionfs_vfsops; 67b5e8ce9fSBruce Evans 68df8bae1dSRodney W. Grimes /* 69d00947d8SCraig Rodrigues * Mount unionfs layer. 70df8bae1dSRodney W. Grimes */ 7180b301c3SPoul-Henning Kamp static int 72dfd233edSAttilio Rao unionfs_domount(struct mount *mp) 73df8bae1dSRodney W. Grimes { 74d00947d8SCraig Rodrigues struct vnode *lowerrootvp; 75d00947d8SCraig Rodrigues struct vnode *upperrootvp; 76d00947d8SCraig Rodrigues struct unionfs_mount *ump; 77d00947d8SCraig Rodrigues char *target; 78d00947d8SCraig Rodrigues char *tmp; 79d00947d8SCraig Rodrigues char *ep; 80312d49efSJason A. Harmening struct nameidata nd, *ndp; 81312d49efSJason A. Harmening struct vattr va; 82312d49efSJason A. Harmening unionfs_copymode copymode; 83312d49efSJason A. Harmening unionfs_whitemode whitemode; 84d00947d8SCraig Rodrigues int below; 85312d49efSJason A. Harmening int error; 86312d49efSJason A. Harmening int len; 87d00947d8SCraig Rodrigues uid_t uid; 88d00947d8SCraig Rodrigues gid_t gid; 89d00947d8SCraig Rodrigues u_short udir; 90d00947d8SCraig Rodrigues u_short ufile; 91df8bae1dSRodney W. Grimes 92312d49efSJason A. Harmening UNIONFSDEBUG("unionfs_mount(mp = %p)\n", mp); 93df8bae1dSRodney W. Grimes 94d00947d8SCraig Rodrigues error = 0; 95d00947d8SCraig Rodrigues below = 0; 96d00947d8SCraig Rodrigues uid = 0; 97d00947d8SCraig Rodrigues gid = 0; 98d00947d8SCraig Rodrigues udir = 0; 99d00947d8SCraig Rodrigues ufile = 0; 100524f3f28SDaichi GOTO copymode = UNIONFS_TRANSPARENT; /* default */ 10120885defSDaichi GOTO whitemode = UNIONFS_WHITE_ALWAYS; 102d00947d8SCraig Rodrigues ndp = &nd; 10381bca6ddSKATO Takenori 1041e370dbbSCraig Rodrigues if (mp->mnt_flag & MNT_ROOTFS) { 1051e370dbbSCraig Rodrigues vfs_mount_error(mp, "Cannot union mount root filesystem"); 10664042a76SPoul-Henning Kamp return (EOPNOTSUPP); 1071e370dbbSCraig Rodrigues } 108d00947d8SCraig Rodrigues 10981bca6ddSKATO Takenori /* 110d00947d8SCraig Rodrigues * Update is a no operation. 111df8bae1dSRodney W. Grimes */ 1121e370dbbSCraig Rodrigues if (mp->mnt_flag & MNT_UPDATE) { 1131e370dbbSCraig Rodrigues vfs_mount_error(mp, "unionfs does not support mount update"); 114a9f5c04aSMaxime Henrion return (EOPNOTSUPP); 1151e370dbbSCraig Rodrigues } 116df8bae1dSRodney W. Grimes 117df8bae1dSRodney W. Grimes /* 118d00947d8SCraig Rodrigues * Get argument 119df8bae1dSRodney W. Grimes */ 120d00947d8SCraig Rodrigues error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len); 1213a773ad0SPoul-Henning Kamp if (error) 122d00947d8SCraig Rodrigues error = vfs_getopt(mp->mnt_optnew, "from", (void **)&target, 123d00947d8SCraig Rodrigues &len); 124d00947d8SCraig Rodrigues if (error || target[len - 1] != '\0') { 125d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid target"); 126d00947d8SCraig Rodrigues return (EINVAL); 127d00947d8SCraig Rodrigues } 128d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "below", NULL, NULL) == 0) 129d00947d8SCraig Rodrigues below = 1; 130d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "udir", (void **)&tmp, NULL) == 0) { 131d00947d8SCraig Rodrigues if (tmp != NULL) 132d00947d8SCraig Rodrigues udir = (mode_t)strtol(tmp, &ep, 8); 133d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 134d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid udir"); 135d00947d8SCraig Rodrigues return (EINVAL); 136d00947d8SCraig Rodrigues } 13716385727SDaichi GOTO udir &= S_IRWXU | S_IRWXG | S_IRWXO; 138d00947d8SCraig Rodrigues } 139d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "ufile", (void **)&tmp, NULL) == 0) { 140d00947d8SCraig Rodrigues if (tmp != NULL) 141d00947d8SCraig Rodrigues ufile = (mode_t)strtol(tmp, &ep, 8); 142d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 143d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid ufile"); 144d00947d8SCraig Rodrigues return (EINVAL); 145d00947d8SCraig Rodrigues } 14616385727SDaichi GOTO ufile &= S_IRWXU | S_IRWXG | S_IRWXO; 147d00947d8SCraig Rodrigues } 148d00947d8SCraig Rodrigues /* check umask, uid and gid */ 149d00947d8SCraig Rodrigues if (udir == 0 && ufile != 0) 150d00947d8SCraig Rodrigues udir = ufile; 151d00947d8SCraig Rodrigues if (ufile == 0 && udir != 0) 152d00947d8SCraig Rodrigues ufile = udir; 153d00947d8SCraig Rodrigues 154cb05b60aSAttilio Rao vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY); 1550359a12eSAttilio Rao error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred); 156d00947d8SCraig Rodrigues if (!error) { 157d00947d8SCraig Rodrigues if (udir == 0) 158d00947d8SCraig Rodrigues udir = va.va_mode; 159d00947d8SCraig Rodrigues if (ufile == 0) 160d00947d8SCraig Rodrigues ufile = va.va_mode; 161d00947d8SCraig Rodrigues uid = va.va_uid; 162d00947d8SCraig Rodrigues gid = va.va_gid; 163d00947d8SCraig Rodrigues } 164b249ce48SMateusz Guzik VOP_UNLOCK(mp->mnt_vnodecovered); 165d00947d8SCraig Rodrigues if (error) 166d00947d8SCraig Rodrigues return (error); 167d00947d8SCraig Rodrigues 168d00947d8SCraig Rodrigues if (mp->mnt_cred->cr_ruid == 0) { /* root only */ 169d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "uid", (void **)&tmp, 170d00947d8SCraig Rodrigues NULL) == 0) { 171d00947d8SCraig Rodrigues if (tmp != NULL) 172d00947d8SCraig Rodrigues uid = (uid_t)strtol(tmp, &ep, 10); 173d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 174d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid uid"); 175d00947d8SCraig Rodrigues return (EINVAL); 176d00947d8SCraig Rodrigues } 177d00947d8SCraig Rodrigues } 178d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "gid", (void **)&tmp, 179d00947d8SCraig Rodrigues NULL) == 0) { 180d00947d8SCraig Rodrigues if (tmp != NULL) 181d00947d8SCraig Rodrigues gid = (gid_t)strtol(tmp, &ep, 10); 182d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 183d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid gid"); 184d00947d8SCraig Rodrigues return (EINVAL); 185d00947d8SCraig Rodrigues } 186d00947d8SCraig Rodrigues } 187d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "copymode", (void **)&tmp, 188d00947d8SCraig Rodrigues NULL) == 0) { 189d00947d8SCraig Rodrigues if (tmp == NULL) { 190d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid copymode"); 191d00947d8SCraig Rodrigues return (EINVAL); 192d00947d8SCraig Rodrigues } else if (strcasecmp(tmp, "traditional") == 0) 193d00947d8SCraig Rodrigues copymode = UNIONFS_TRADITIONAL; 194d00947d8SCraig Rodrigues else if (strcasecmp(tmp, "transparent") == 0) 195d00947d8SCraig Rodrigues copymode = UNIONFS_TRANSPARENT; 196d00947d8SCraig Rodrigues else if (strcasecmp(tmp, "masquerade") == 0) 197d00947d8SCraig Rodrigues copymode = UNIONFS_MASQUERADE; 198d00947d8SCraig Rodrigues else { 199d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid copymode"); 200d00947d8SCraig Rodrigues return (EINVAL); 201d00947d8SCraig Rodrigues } 202d00947d8SCraig Rodrigues } 20320885defSDaichi GOTO if (vfs_getopt(mp->mnt_optnew, "whiteout", (void **)&tmp, 20420885defSDaichi GOTO NULL) == 0) { 20520885defSDaichi GOTO if (tmp == NULL) { 20620885defSDaichi GOTO vfs_mount_error(mp, "Invalid whiteout mode"); 20720885defSDaichi GOTO return (EINVAL); 20820885defSDaichi GOTO } else if (strcasecmp(tmp, "always") == 0) 20920885defSDaichi GOTO whitemode = UNIONFS_WHITE_ALWAYS; 21020885defSDaichi GOTO else if (strcasecmp(tmp, "whenneeded") == 0) 21120885defSDaichi GOTO whitemode = UNIONFS_WHITE_WHENNEEDED; 21220885defSDaichi GOTO else { 21320885defSDaichi GOTO vfs_mount_error(mp, "Invalid whiteout mode"); 21420885defSDaichi GOTO return (EINVAL); 21520885defSDaichi GOTO } 21620885defSDaichi GOTO } 217d00947d8SCraig Rodrigues } 218d00947d8SCraig Rodrigues /* If copymode is UNIONFS_TRADITIONAL, uid/gid is mounted user. */ 219d00947d8SCraig Rodrigues if (copymode == UNIONFS_TRADITIONAL) { 220d00947d8SCraig Rodrigues uid = mp->mnt_cred->cr_ruid; 221d00947d8SCraig Rodrigues gid = mp->mnt_cred->cr_rgid; 222d00947d8SCraig Rodrigues } 223d00947d8SCraig Rodrigues 224d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: uid=%d, gid=%d\n", uid, gid); 225d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: udir=0%03o, ufile=0%03o\n", udir, ufile); 226d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: copymode=%d\n", copymode); 227d00947d8SCraig Rodrigues 228d00947d8SCraig Rodrigues /* 229d00947d8SCraig Rodrigues * Find upper node 230d00947d8SCraig Rodrigues */ 2317e1d3eefSMateusz Guzik NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, target); 232d00947d8SCraig Rodrigues if ((error = namei(ndp))) 233d00947d8SCraig Rodrigues return (error); 234d00947d8SCraig Rodrigues 235bb92cd7bSMateusz Guzik NDFREE_PNBUF(ndp); 236d00947d8SCraig Rodrigues 237d00947d8SCraig Rodrigues /* get root vnodes */ 238d00947d8SCraig Rodrigues lowerrootvp = mp->mnt_vnodecovered; 239df8bae1dSRodney W. Grimes upperrootvp = ndp->ni_vp; 240a01ca46bSJason A. Harmening KASSERT(lowerrootvp != NULL, ("%s: NULL lower root vp", __func__)); 241a01ca46bSJason A. Harmening KASSERT(upperrootvp != NULL, ("%s: NULL upper root vp", __func__)); 242df8bae1dSRodney W. Grimes 243d00947d8SCraig Rodrigues /* create unionfs_mount */ 244312d49efSJason A. Harmening ump = malloc(sizeof(struct unionfs_mount), M_UNIONFSMNT, 245312d49efSJason A. Harmening M_WAITOK | M_ZERO); 2462a31267eSMatthew Dillon 247d00947d8SCraig Rodrigues /* 248d00947d8SCraig Rodrigues * Save reference 249d00947d8SCraig Rodrigues */ 250d00947d8SCraig Rodrigues if (below) { 251b249ce48SMateusz Guzik VOP_UNLOCK(upperrootvp); 252cb05b60aSAttilio Rao vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY); 253d00947d8SCraig Rodrigues ump->um_lowervp = upperrootvp; 254d00947d8SCraig Rodrigues ump->um_uppervp = lowerrootvp; 255d00947d8SCraig Rodrigues } else { 256d00947d8SCraig Rodrigues ump->um_lowervp = lowerrootvp; 257d00947d8SCraig Rodrigues ump->um_uppervp = upperrootvp; 258df8bae1dSRodney W. Grimes } 259d00947d8SCraig Rodrigues ump->um_rootvp = NULLVP; 260d00947d8SCraig Rodrigues ump->um_uid = uid; 261d00947d8SCraig Rodrigues ump->um_gid = gid; 262d00947d8SCraig Rodrigues ump->um_udir = udir; 263d00947d8SCraig Rodrigues ump->um_ufile = ufile; 264d00947d8SCraig Rodrigues ump->um_copymode = copymode; 26520885defSDaichi GOTO ump->um_whitemode = whitemode; 266d00947d8SCraig Rodrigues 26777465d93SAlfred Perlstein mp->mnt_data = ump; 268df8bae1dSRodney W. Grimes 269996c772fSJohn Dyson /* 270d00947d8SCraig Rodrigues * Copy upper layer's RDONLY flag. 271d00947d8SCraig Rodrigues */ 272d00947d8SCraig Rodrigues mp->mnt_flag |= ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY; 273d00947d8SCraig Rodrigues 274d00947d8SCraig Rodrigues /* 275d00947d8SCraig Rodrigues * Unlock the node 276df8bae1dSRodney W. Grimes */ 277b249ce48SMateusz Guzik VOP_UNLOCK(ump->um_uppervp); 278df8bae1dSRodney W. Grimes 279d00947d8SCraig Rodrigues /* 280d00947d8SCraig Rodrigues * Get the unionfs root vnode. 281d00947d8SCraig Rodrigues */ 282d00947d8SCraig Rodrigues error = unionfs_nodeget(mp, ump->um_uppervp, ump->um_lowervp, 2836d8420d4SJason A. Harmening NULLVP, &(ump->um_rootvp), NULL); 28459409cb9SJason A. Harmening if (error != 0) { 2850745d837SJason A. Harmening vrele(upperrootvp); 286d00947d8SCraig Rodrigues free(ump, M_UNIONFSMNT); 287d00947d8SCraig Rodrigues mp->mnt_data = NULL; 288df8bae1dSRodney W. Grimes return (error); 289df8bae1dSRodney W. Grimes } 290a01ca46bSJason A. Harmening KASSERT(ump->um_rootvp != NULL, ("rootvp cannot be NULL")); 291a01ca46bSJason A. Harmening KASSERT((ump->um_rootvp->v_vflag & VV_ROOT) != 0, 292a01ca46bSJason A. Harmening ("%s: rootvp without VV_ROOT", __func__)); 293df8bae1dSRodney W. Grimes 2940745d837SJason A. Harmening /* 2950745d837SJason A. Harmening * Do not release the namei() reference on upperrootvp until after 2960745d837SJason A. Harmening * we attempt to register the upper mounts. A concurrent unmount 2970745d837SJason A. Harmening * of the upper or lower FS may have caused unionfs_nodeget() to 2980745d837SJason A. Harmening * create a unionfs node with a NULL upper or lower vp and with 2990745d837SJason A. Harmening * no reference held on upperrootvp or lowerrootvp. 3000745d837SJason A. Harmening * vfs_register_upper() should subsequently fail, which is what 3010745d837SJason A. Harmening * we want, but we must ensure neither underlying vnode can be 3020745d837SJason A. Harmening * reused until that happens. We assume the caller holds a reference 3030745d837SJason A. Harmening * to lowerrootvp as it is the mount's covered vnode. 3040745d837SJason A. Harmening */ 305*cc3ec9f7SJason A. Harmening ump->um_lowermp = vfs_register_upper_from_vp(ump->um_lowervp, mp, 306c746ed72SJason A. Harmening &ump->um_lower_link); 307*cc3ec9f7SJason A. Harmening ump->um_uppermp = vfs_register_upper_from_vp(ump->um_uppervp, mp, 308c746ed72SJason A. Harmening &ump->um_upper_link); 30959409cb9SJason A. Harmening 3100745d837SJason A. Harmening vrele(upperrootvp); 3110745d837SJason A. Harmening 312*cc3ec9f7SJason A. Harmening if (ump->um_lowermp == NULL || ump->um_uppermp == NULL) { 313*cc3ec9f7SJason A. Harmening if (ump->um_lowermp != NULL) 314*cc3ec9f7SJason A. Harmening vfs_unregister_upper(ump->um_lowermp, &ump->um_lower_link); 315*cc3ec9f7SJason A. Harmening if (ump->um_uppermp != NULL) 316*cc3ec9f7SJason A. Harmening vfs_unregister_upper(ump->um_uppermp, &ump->um_upper_link); 317356e6980SJason A. Harmening vflush(mp, 1, FORCECLOSE, curthread); 31859409cb9SJason A. Harmening free(ump, M_UNIONFSMNT); 31959409cb9SJason A. Harmening mp->mnt_data = NULL; 32059409cb9SJason A. Harmening return (ENOENT); 32159409cb9SJason A. Harmening } 32259409cb9SJason A. Harmening 323080ef8a4SJason A. Harmening /* 324080ef8a4SJason A. Harmening * Specify that the covered vnode lock should remain held while 325080ef8a4SJason A. Harmening * lookup() performs the cross-mount walk. This prevents a lock-order 326080ef8a4SJason A. Harmening * reversal between the covered vnode lock (which is also locked by 327080ef8a4SJason A. Harmening * unionfs_lock()) and the mountpoint's busy count. Without this, 328080ef8a4SJason A. Harmening * unmount will lock the covered vnode lock (directly through the 329080ef8a4SJason A. Harmening * covered vnode) and wait for the busy count to drain, while a 330080ef8a4SJason A. Harmening * concurrent lookup will increment the busy count and then lock 331080ef8a4SJason A. Harmening * the covered vnode lock (indirectly through unionfs_lock()). 332080ef8a4SJason A. Harmening * 333080ef8a4SJason A. Harmening * Note that we can't yet use this facility for the 'below' case 334080ef8a4SJason A. Harmening * in which the upper vnode is the covered vnode, because that would 335080ef8a4SJason A. Harmening * introduce a different LOR in which the cross-mount lookup would 336080ef8a4SJason A. Harmening * effectively hold the upper vnode lock before acquiring the lower 337080ef8a4SJason A. Harmening * vnode lock, while an unrelated lock operation would still acquire 338080ef8a4SJason A. Harmening * the lower vnode lock before the upper vnode lock, which is the 339080ef8a4SJason A. Harmening * order unionfs currently requires. 340080ef8a4SJason A. Harmening */ 341080ef8a4SJason A. Harmening if (!below) { 3425cec725cSJason A. Harmening vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY | LK_CANRECURSE); 343080ef8a4SJason A. Harmening mp->mnt_vnodecovered->v_vflag |= VV_CROSSLOCK; 344080ef8a4SJason A. Harmening VOP_UNLOCK(mp->mnt_vnodecovered); 345080ef8a4SJason A. Harmening } 346080ef8a4SJason A. Harmening 347a8a07fd6SMateusz Guzik MNT_ILOCK(mp); 348*cc3ec9f7SJason A. Harmening if ((ump->um_lowermp->mnt_flag & MNT_LOCAL) != 0 && 349*cc3ec9f7SJason A. Harmening (ump->um_uppermp->mnt_flag & MNT_LOCAL) != 0) 350d00947d8SCraig Rodrigues mp->mnt_flag |= MNT_LOCAL; 351d3cc5354SMateusz Guzik mp->mnt_kern_flag |= MNTK_NOMSYNC | MNTK_UNIONFS; 352a8a07fd6SMateusz Guzik MNT_IUNLOCK(mp); 353d00947d8SCraig Rodrigues 354d00947d8SCraig Rodrigues /* 355d00947d8SCraig Rodrigues * Get new fsid 356d00947d8SCraig Rodrigues */ 357d00947d8SCraig Rodrigues vfs_getnewfsid(mp); 358d00947d8SCraig Rodrigues 359852c303bSConrad Meyer snprintf(mp->mnt_stat.f_mntfromname, MNAMELEN, "<%s>:%s", 360852c303bSConrad Meyer below ? "below" : "above", target); 361d00947d8SCraig Rodrigues 362d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: from %s, on %s\n", 363d00947d8SCraig Rodrigues mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 364d00947d8SCraig Rodrigues 365d00947d8SCraig Rodrigues return (0); 366d00947d8SCraig Rodrigues } 367d00947d8SCraig Rodrigues 368d00947d8SCraig Rodrigues /* 369d00947d8SCraig Rodrigues * Free reference to unionfs layer 370df8bae1dSRodney W. Grimes */ 37180b301c3SPoul-Henning Kamp static int 372dfd233edSAttilio Rao unionfs_unmount(struct mount *mp, int mntflags) 373df8bae1dSRodney W. Grimes { 374d00947d8SCraig Rodrigues struct unionfs_mount *ump; 375df8bae1dSRodney W. Grimes int error; 376d00947d8SCraig Rodrigues int num; 377996c772fSJohn Dyson int freeing; 378d00947d8SCraig Rodrigues int flags; 379df8bae1dSRodney W. Grimes 380312d49efSJason A. Harmening UNIONFSDEBUG("unionfs_unmount: mp = %p\n", mp); 381d00947d8SCraig Rodrigues 382d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 383d00947d8SCraig Rodrigues flags = 0; 384df8bae1dSRodney W. Grimes 385996c772fSJohn Dyson if (mntflags & MNT_FORCE) 386996c772fSJohn Dyson flags |= FORCECLOSE; 387996c772fSJohn Dyson 388d00947d8SCraig Rodrigues /* vflush (no need to call vrele) */ 389dfd233edSAttilio Rao for (freeing = 0; (error = vflush(mp, 1, flags, curthread)) != 0;) { 390d00947d8SCraig Rodrigues num = mp->mnt_nvnodelistsize; 391d00947d8SCraig Rodrigues if (num == freeing) 392996c772fSJohn Dyson break; 393d00947d8SCraig Rodrigues freeing = num; 394df8bae1dSRodney W. Grimes } 395df8bae1dSRodney W. Grimes 3960864ef1eSIan Dowse if (error) 3970864ef1eSIan Dowse return (error); 398df8bae1dSRodney W. Grimes 3995cec725cSJason A. Harmening vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY | LK_CANRECURSE); 400080ef8a4SJason A. Harmening mp->mnt_vnodecovered->v_vflag &= ~VV_CROSSLOCK; 401080ef8a4SJason A. Harmening VOP_UNLOCK(mp->mnt_vnodecovered); 402*cc3ec9f7SJason A. Harmening vfs_unregister_upper(ump->um_lowermp, &ump->um_lower_link); 403*cc3ec9f7SJason A. Harmening vfs_unregister_upper(ump->um_uppermp, &ump->um_upper_link); 404d00947d8SCraig Rodrigues free(ump, M_UNIONFSMNT); 40511753bd0SKevin Lo mp->mnt_data = NULL; 406d00947d8SCraig Rodrigues 407df8bae1dSRodney W. Grimes return (0); 408df8bae1dSRodney W. Grimes } 409df8bae1dSRodney W. Grimes 41080b301c3SPoul-Henning Kamp static int 411dfd233edSAttilio Rao unionfs_root(struct mount *mp, int flags, struct vnode **vpp) 412df8bae1dSRodney W. Grimes { 413d00947d8SCraig Rodrigues struct unionfs_mount *ump; 414d00947d8SCraig Rodrigues struct vnode *vp; 415df8bae1dSRodney W. Grimes 416d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 417d00947d8SCraig Rodrigues vp = ump->um_rootvp; 4182a31267eSMatthew Dillon 419d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_root: rootvp=%p locked=%x\n", 42081c794f9SAttilio Rao vp, VOP_ISLOCKED(vp)); 421df8bae1dSRodney W. Grimes 422d00947d8SCraig Rodrigues vref(vp); 423d00947d8SCraig Rodrigues if (flags & LK_TYPE_MASK) 424cb05b60aSAttilio Rao vn_lock(vp, flags); 425df8bae1dSRodney W. Grimes 426d00947d8SCraig Rodrigues *vpp = vp; 427d00947d8SCraig Rodrigues 428d00947d8SCraig Rodrigues return (0); 429df8bae1dSRodney W. Grimes } 430df8bae1dSRodney W. Grimes 43180b301c3SPoul-Henning Kamp static int 432a4b07a27SJason A. Harmening unionfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg, 433a4b07a27SJason A. Harmening bool *mp_busy) 434df8bae1dSRodney W. Grimes { 435a4b07a27SJason A. Harmening struct mount *uppermp; 436d00947d8SCraig Rodrigues struct unionfs_mount *ump; 437a4b07a27SJason A. Harmening int error; 438a4b07a27SJason A. Harmening bool unbusy; 439df8bae1dSRodney W. Grimes 440d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 441*cc3ec9f7SJason A. Harmening /* 442*cc3ec9f7SJason A. Harmening * Issue a volatile load of um_uppermp here, as the mount may be 443*cc3ec9f7SJason A. Harmening * torn down after we call vfs_unbusy(). 444*cc3ec9f7SJason A. Harmening */ 445*cc3ec9f7SJason A. Harmening uppermp = atomic_load_ptr(&ump->um_uppermp); 446a4b07a27SJason A. Harmening KASSERT(*mp_busy == true, ("upper mount not busy")); 447a4b07a27SJason A. Harmening /* 448a4b07a27SJason A. Harmening * See comment in sys_quotactl() for an explanation of why the 449a4b07a27SJason A. Harmening * lower mount needs to be busied by the caller of VFS_QUOTACTL() 450a4b07a27SJason A. Harmening * but may be unbusied by the implementation. We must unbusy 451a4b07a27SJason A. Harmening * the upper mount for the same reason; otherwise a namei lookup 452a4b07a27SJason A. Harmening * issued by the VFS_QUOTACTL() implementation could traverse the 453a4b07a27SJason A. Harmening * upper mount and deadlock. 454a4b07a27SJason A. Harmening */ 455a4b07a27SJason A. Harmening vfs_unbusy(mp); 456a4b07a27SJason A. Harmening *mp_busy = false; 457a4b07a27SJason A. Harmening unbusy = true; 458a4b07a27SJason A. Harmening error = vfs_busy(uppermp, 0); 459d00947d8SCraig Rodrigues /* 460d00947d8SCraig Rodrigues * Writing is always performed to upper vnode. 461d00947d8SCraig Rodrigues */ 462a4b07a27SJason A. Harmening if (error == 0) 463a4b07a27SJason A. Harmening error = VFS_QUOTACTL(uppermp, cmd, uid, arg, &unbusy); 464a4b07a27SJason A. Harmening if (unbusy) 465a4b07a27SJason A. Harmening vfs_unbusy(uppermp); 466a4b07a27SJason A. Harmening 467a4b07a27SJason A. Harmening return (error); 468d00947d8SCraig Rodrigues } 469d00947d8SCraig Rodrigues 470d00947d8SCraig Rodrigues static int 471dfd233edSAttilio Rao unionfs_statfs(struct mount *mp, struct statfs *sbp) 472d00947d8SCraig Rodrigues { 473d00947d8SCraig Rodrigues struct unionfs_mount *ump; 4742f304845SKonstantin Belousov struct statfs *mstat; 475d00947d8SCraig Rodrigues uint64_t lbsize; 476312d49efSJason A. Harmening int error; 477d00947d8SCraig Rodrigues 478d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 479d00947d8SCraig Rodrigues 480d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_statfs(mp = %p, lvp = %p, uvp = %p)\n", 481312d49efSJason A. Harmening mp, ump->um_lowervp, ump->um_uppervp); 482df8bae1dSRodney W. Grimes 4832f304845SKonstantin Belousov mstat = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK | M_ZERO); 484df8bae1dSRodney W. Grimes 485*cc3ec9f7SJason A. Harmening error = VFS_STATFS(ump->um_lowermp, mstat); 4862f304845SKonstantin Belousov if (error) { 4872f304845SKonstantin Belousov free(mstat, M_STATFS); 488df8bae1dSRodney W. Grimes return (error); 4892f304845SKonstantin Belousov } 490d00947d8SCraig Rodrigues 491d00947d8SCraig Rodrigues /* now copy across the "interesting" information and fake the rest */ 4922f304845SKonstantin Belousov sbp->f_blocks = mstat->f_blocks; 4932f304845SKonstantin Belousov sbp->f_files = mstat->f_files; 494d00947d8SCraig Rodrigues 4952f304845SKonstantin Belousov lbsize = mstat->f_bsize; 496d00947d8SCraig Rodrigues 497*cc3ec9f7SJason A. Harmening error = VFS_STATFS(ump->um_uppermp, mstat); 4982f304845SKonstantin Belousov if (error) { 4992f304845SKonstantin Belousov free(mstat, M_STATFS); 500d00947d8SCraig Rodrigues return (error); 5012f304845SKonstantin Belousov } 5022f304845SKonstantin Belousov 5031c4ccf09SDon Lewis /* 504d00947d8SCraig Rodrigues * The FS type etc is copy from upper vfs. 505d00947d8SCraig Rodrigues * (write able vfs have priority) 5061c4ccf09SDon Lewis */ 5072f304845SKonstantin Belousov sbp->f_type = mstat->f_type; 5082f304845SKonstantin Belousov sbp->f_flags = mstat->f_flags; 5092f304845SKonstantin Belousov sbp->f_bsize = mstat->f_bsize; 5102f304845SKonstantin Belousov sbp->f_iosize = mstat->f_iosize; 511df8bae1dSRodney W. Grimes 5122f304845SKonstantin Belousov if (mstat->f_bsize != lbsize) 5132f304845SKonstantin Belousov sbp->f_blocks = ((off_t)sbp->f_blocks * lbsize) / 5142f304845SKonstantin Belousov mstat->f_bsize; 515996c772fSJohn Dyson 5162f304845SKonstantin Belousov sbp->f_blocks += mstat->f_blocks; 5172f304845SKonstantin Belousov sbp->f_bfree = mstat->f_bfree; 5182f304845SKonstantin Belousov sbp->f_bavail = mstat->f_bavail; 5192f304845SKonstantin Belousov sbp->f_files += mstat->f_files; 5202f304845SKonstantin Belousov sbp->f_ffree = mstat->f_ffree; 5212f304845SKonstantin Belousov 5222f304845SKonstantin Belousov free(mstat, M_STATFS); 523df8bae1dSRodney W. Grimes return (0); 524df8bae1dSRodney W. Grimes } 525df8bae1dSRodney W. Grimes 526d00947d8SCraig Rodrigues static int 527dfd233edSAttilio Rao unionfs_sync(struct mount *mp, int waitfor) 528d00947d8SCraig Rodrigues { 529d00947d8SCraig Rodrigues /* nothing to do */ 530d00947d8SCraig Rodrigues return (0); 531d00947d8SCraig Rodrigues } 532d00947d8SCraig Rodrigues 533d00947d8SCraig Rodrigues static int 534d00947d8SCraig Rodrigues unionfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) 535d00947d8SCraig Rodrigues { 536d00947d8SCraig Rodrigues return (EOPNOTSUPP); 537d00947d8SCraig Rodrigues } 538d00947d8SCraig Rodrigues 539d00947d8SCraig Rodrigues static int 540694a586aSRick Macklem unionfs_fhtovp(struct mount *mp, struct fid *fidp, int flags, 541694a586aSRick Macklem struct vnode **vpp) 542d00947d8SCraig Rodrigues { 543d00947d8SCraig Rodrigues return (EOPNOTSUPP); 544d00947d8SCraig Rodrigues } 545d00947d8SCraig Rodrigues 546d00947d8SCraig Rodrigues static int 5471f7104d7SRick Macklem unionfs_checkexp(struct mount *mp, struct sockaddr *nam, uint64_t *extflagsp, 5481f7104d7SRick Macklem struct ucred **credanonp, int *numsecflavors, int *secflavors) 549d00947d8SCraig Rodrigues { 550d00947d8SCraig Rodrigues return (EOPNOTSUPP); 551d00947d8SCraig Rodrigues } 552d00947d8SCraig Rodrigues 553d00947d8SCraig Rodrigues static int 554d00947d8SCraig Rodrigues unionfs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, 555dfd233edSAttilio Rao int namespace, const char *attrname) 556d00947d8SCraig Rodrigues { 557d00947d8SCraig Rodrigues struct unionfs_mount *ump; 558d00947d8SCraig Rodrigues struct unionfs_node *unp; 559d00947d8SCraig Rodrigues 560d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 561d00947d8SCraig Rodrigues unp = VTOUNIONFS(filename_vp); 562d00947d8SCraig Rodrigues 563d00947d8SCraig Rodrigues if (unp->un_uppervp != NULLVP) { 564*cc3ec9f7SJason A. Harmening return (VFS_EXTATTRCTL(ump->um_uppermp, cmd, 565dfd233edSAttilio Rao unp->un_uppervp, namespace, attrname)); 566d00947d8SCraig Rodrigues } else { 567*cc3ec9f7SJason A. Harmening return (VFS_EXTATTRCTL(ump->um_lowermp, cmd, 568dfd233edSAttilio Rao unp->un_lowervp, namespace, attrname)); 569d00947d8SCraig Rodrigues } 570d00947d8SCraig Rodrigues } 571d00947d8SCraig Rodrigues 572d00947d8SCraig Rodrigues static struct vfsops unionfs_vfsops = { 573d00947d8SCraig Rodrigues .vfs_checkexp = unionfs_checkexp, 574d00947d8SCraig Rodrigues .vfs_extattrctl = unionfs_extattrctl, 575d00947d8SCraig Rodrigues .vfs_fhtovp = unionfs_fhtovp, 576d00947d8SCraig Rodrigues .vfs_init = unionfs_init, 577d00947d8SCraig Rodrigues .vfs_mount = unionfs_domount, 578d00947d8SCraig Rodrigues .vfs_quotactl = unionfs_quotactl, 579d00947d8SCraig Rodrigues .vfs_root = unionfs_root, 580d00947d8SCraig Rodrigues .vfs_statfs = unionfs_statfs, 581d00947d8SCraig Rodrigues .vfs_sync = unionfs_sync, 582d00947d8SCraig Rodrigues .vfs_uninit = unionfs_uninit, 583d00947d8SCraig Rodrigues .vfs_unmount = unionfs_unmount, 584d00947d8SCraig Rodrigues .vfs_vget = unionfs_vget, 585df8bae1dSRodney W. Grimes }; 586c901836cSGarrett Wollman 587d00947d8SCraig Rodrigues VFS_SET(unionfs_vfsops, unionfs, VFCF_LOOPBACK); 588