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 * 37996c772fSJohn Dyson * @(#)union_vfsops.c 8.20 (Berkeley) 5/20/95 38c3aac50fSPeter Wemm * $FreeBSD$ 39df8bae1dSRodney W. Grimes */ 40df8bae1dSRodney W. Grimes 41df8bae1dSRodney W. Grimes #include <sys/param.h> 42df8bae1dSRodney W. Grimes #include <sys/systm.h> 43d00947d8SCraig Rodrigues #include <sys/kdb.h> 4457b4252eSKonstantin Belousov #include <sys/fcntl.h> 45c9b1d604SGarrett Wollman #include <sys/kernel.h> 46805d90f7SJohn Baldwin #include <sys/lock.h> 47d00947d8SCraig Rodrigues #include <sys/malloc.h> 48df8bae1dSRodney W. Grimes #include <sys/mount.h> 49df8bae1dSRodney W. Grimes #include <sys/namei.h> 50d00947d8SCraig Rodrigues #include <sys/proc.h> 51d00947d8SCraig Rodrigues #include <sys/vnode.h> 52d00947d8SCraig Rodrigues #include <sys/stat.h> 53d00947d8SCraig Rodrigues 5499d300a1SRuslan Ermilov #include <fs/unionfs/union.h> 55df8bae1dSRodney W. Grimes 56d00947d8SCraig Rodrigues static MALLOC_DEFINE(M_UNIONFSMNT, "UNIONFS mount", "UNIONFS mount structure"); 57a1c995b6SPoul-Henning Kamp 58d00947d8SCraig Rodrigues static vfs_fhtovp_t unionfs_fhtovp; 59d00947d8SCraig Rodrigues static vfs_checkexp_t unionfs_checkexp; 60d00947d8SCraig Rodrigues static vfs_mount_t unionfs_domount; 61d00947d8SCraig Rodrigues static vfs_quotactl_t unionfs_quotactl; 62d00947d8SCraig Rodrigues static vfs_root_t unionfs_root; 63d00947d8SCraig Rodrigues static vfs_sync_t unionfs_sync; 64d00947d8SCraig Rodrigues static vfs_statfs_t unionfs_statfs; 65d00947d8SCraig Rodrigues static vfs_unmount_t unionfs_unmount; 66d00947d8SCraig Rodrigues static vfs_vget_t unionfs_vget; 67d00947d8SCraig Rodrigues static vfs_extattrctl_t unionfs_extattrctl; 68d00947d8SCraig Rodrigues 69d00947d8SCraig Rodrigues static struct vfsops unionfs_vfsops; 70b5e8ce9fSBruce Evans 71df8bae1dSRodney W. Grimes /* 72d00947d8SCraig Rodrigues * Mount unionfs layer. 73df8bae1dSRodney W. Grimes */ 7480b301c3SPoul-Henning Kamp static int 75dfd233edSAttilio Rao unionfs_domount(struct mount *mp) 76df8bae1dSRodney W. Grimes { 77d00947d8SCraig Rodrigues int error; 7859409cb9SJason A. Harmening struct mount *lowermp, *uppermp; 79d00947d8SCraig Rodrigues struct vnode *lowerrootvp; 80d00947d8SCraig Rodrigues struct vnode *upperrootvp; 81d00947d8SCraig Rodrigues struct unionfs_mount *ump; 82dfd233edSAttilio Rao struct thread *td; 83d00947d8SCraig Rodrigues char *target; 84d00947d8SCraig Rodrigues char *tmp; 85d00947d8SCraig Rodrigues char *ep; 86df8bae1dSRodney W. Grimes int len; 87d00947d8SCraig Rodrigues int below; 88d00947d8SCraig Rodrigues uid_t uid; 89d00947d8SCraig Rodrigues gid_t gid; 90d00947d8SCraig Rodrigues u_short udir; 91d00947d8SCraig Rodrigues u_short ufile; 92d00947d8SCraig Rodrigues unionfs_copymode copymode; 9320885defSDaichi GOTO unionfs_whitemode whitemode; 94d00947d8SCraig Rodrigues struct nameidata nd, *ndp; 95d00947d8SCraig Rodrigues struct vattr va; 96df8bae1dSRodney W. Grimes 97d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount(mp = %p)\n", (void *)mp); 98df8bae1dSRodney W. Grimes 99d00947d8SCraig Rodrigues error = 0; 100d00947d8SCraig Rodrigues below = 0; 101d00947d8SCraig Rodrigues uid = 0; 102d00947d8SCraig Rodrigues gid = 0; 103d00947d8SCraig Rodrigues udir = 0; 104d00947d8SCraig Rodrigues ufile = 0; 105524f3f28SDaichi GOTO copymode = UNIONFS_TRANSPARENT; /* default */ 10620885defSDaichi GOTO whitemode = UNIONFS_WHITE_ALWAYS; 107d00947d8SCraig Rodrigues ndp = &nd; 108dfd233edSAttilio Rao td = curthread; 10981bca6ddSKATO Takenori 1101e370dbbSCraig Rodrigues if (mp->mnt_flag & MNT_ROOTFS) { 1111e370dbbSCraig Rodrigues vfs_mount_error(mp, "Cannot union mount root filesystem"); 11264042a76SPoul-Henning Kamp return (EOPNOTSUPP); 1131e370dbbSCraig Rodrigues } 114d00947d8SCraig Rodrigues 11581bca6ddSKATO Takenori /* 116d00947d8SCraig Rodrigues * Update is a no operation. 117df8bae1dSRodney W. Grimes */ 1181e370dbbSCraig Rodrigues if (mp->mnt_flag & MNT_UPDATE) { 1191e370dbbSCraig Rodrigues vfs_mount_error(mp, "unionfs does not support mount update"); 120a9f5c04aSMaxime Henrion return (EOPNOTSUPP); 1211e370dbbSCraig Rodrigues } 122df8bae1dSRodney W. Grimes 123df8bae1dSRodney W. Grimes /* 124d00947d8SCraig Rodrigues * Get argument 125df8bae1dSRodney W. Grimes */ 126d00947d8SCraig Rodrigues error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len); 1273a773ad0SPoul-Henning Kamp if (error) 128d00947d8SCraig Rodrigues error = vfs_getopt(mp->mnt_optnew, "from", (void **)&target, 129d00947d8SCraig Rodrigues &len); 130d00947d8SCraig Rodrigues if (error || target[len - 1] != '\0') { 131d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid target"); 132d00947d8SCraig Rodrigues return (EINVAL); 133d00947d8SCraig Rodrigues } 134d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "below", NULL, NULL) == 0) 135d00947d8SCraig Rodrigues below = 1; 136d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "udir", (void **)&tmp, NULL) == 0) { 137d00947d8SCraig Rodrigues if (tmp != NULL) 138d00947d8SCraig Rodrigues udir = (mode_t)strtol(tmp, &ep, 8); 139d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 140d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid udir"); 141d00947d8SCraig Rodrigues return (EINVAL); 142d00947d8SCraig Rodrigues } 14316385727SDaichi GOTO udir &= S_IRWXU | S_IRWXG | S_IRWXO; 144d00947d8SCraig Rodrigues } 145d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "ufile", (void **)&tmp, NULL) == 0) { 146d00947d8SCraig Rodrigues if (tmp != NULL) 147d00947d8SCraig Rodrigues ufile = (mode_t)strtol(tmp, &ep, 8); 148d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 149d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid ufile"); 150d00947d8SCraig Rodrigues return (EINVAL); 151d00947d8SCraig Rodrigues } 15216385727SDaichi GOTO ufile &= S_IRWXU | S_IRWXG | S_IRWXO; 153d00947d8SCraig Rodrigues } 154d00947d8SCraig Rodrigues /* check umask, uid and gid */ 155d00947d8SCraig Rodrigues if (udir == 0 && ufile != 0) 156d00947d8SCraig Rodrigues udir = ufile; 157d00947d8SCraig Rodrigues if (ufile == 0 && udir != 0) 158d00947d8SCraig Rodrigues ufile = udir; 159d00947d8SCraig Rodrigues 160cb05b60aSAttilio Rao vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY); 1610359a12eSAttilio Rao error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred); 162d00947d8SCraig Rodrigues if (!error) { 163d00947d8SCraig Rodrigues if (udir == 0) 164d00947d8SCraig Rodrigues udir = va.va_mode; 165d00947d8SCraig Rodrigues if (ufile == 0) 166d00947d8SCraig Rodrigues ufile = va.va_mode; 167d00947d8SCraig Rodrigues uid = va.va_uid; 168d00947d8SCraig Rodrigues gid = va.va_gid; 169d00947d8SCraig Rodrigues } 170b249ce48SMateusz Guzik VOP_UNLOCK(mp->mnt_vnodecovered); 171d00947d8SCraig Rodrigues if (error) 172d00947d8SCraig Rodrigues return (error); 173d00947d8SCraig Rodrigues 174d00947d8SCraig Rodrigues if (mp->mnt_cred->cr_ruid == 0) { /* root only */ 175d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "uid", (void **)&tmp, 176d00947d8SCraig Rodrigues NULL) == 0) { 177d00947d8SCraig Rodrigues if (tmp != NULL) 178d00947d8SCraig Rodrigues uid = (uid_t)strtol(tmp, &ep, 10); 179d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 180d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid uid"); 181d00947d8SCraig Rodrigues return (EINVAL); 182d00947d8SCraig Rodrigues } 183d00947d8SCraig Rodrigues } 184d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "gid", (void **)&tmp, 185d00947d8SCraig Rodrigues NULL) == 0) { 186d00947d8SCraig Rodrigues if (tmp != NULL) 187d00947d8SCraig Rodrigues gid = (gid_t)strtol(tmp, &ep, 10); 188d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 189d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid gid"); 190d00947d8SCraig Rodrigues return (EINVAL); 191d00947d8SCraig Rodrigues } 192d00947d8SCraig Rodrigues } 193d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "copymode", (void **)&tmp, 194d00947d8SCraig Rodrigues NULL) == 0) { 195d00947d8SCraig Rodrigues if (tmp == NULL) { 196d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid copymode"); 197d00947d8SCraig Rodrigues return (EINVAL); 198d00947d8SCraig Rodrigues } else if (strcasecmp(tmp, "traditional") == 0) 199d00947d8SCraig Rodrigues copymode = UNIONFS_TRADITIONAL; 200d00947d8SCraig Rodrigues else if (strcasecmp(tmp, "transparent") == 0) 201d00947d8SCraig Rodrigues copymode = UNIONFS_TRANSPARENT; 202d00947d8SCraig Rodrigues else if (strcasecmp(tmp, "masquerade") == 0) 203d00947d8SCraig Rodrigues copymode = UNIONFS_MASQUERADE; 204d00947d8SCraig Rodrigues else { 205d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid copymode"); 206d00947d8SCraig Rodrigues return (EINVAL); 207d00947d8SCraig Rodrigues } 208d00947d8SCraig Rodrigues } 20920885defSDaichi GOTO if (vfs_getopt(mp->mnt_optnew, "whiteout", (void **)&tmp, 21020885defSDaichi GOTO NULL) == 0) { 21120885defSDaichi GOTO if (tmp == NULL) { 21220885defSDaichi GOTO vfs_mount_error(mp, "Invalid whiteout mode"); 21320885defSDaichi GOTO return (EINVAL); 21420885defSDaichi GOTO } else if (strcasecmp(tmp, "always") == 0) 21520885defSDaichi GOTO whitemode = UNIONFS_WHITE_ALWAYS; 21620885defSDaichi GOTO else if (strcasecmp(tmp, "whenneeded") == 0) 21720885defSDaichi GOTO whitemode = UNIONFS_WHITE_WHENNEEDED; 21820885defSDaichi GOTO else { 21920885defSDaichi GOTO vfs_mount_error(mp, "Invalid whiteout mode"); 22020885defSDaichi GOTO return (EINVAL); 22120885defSDaichi GOTO } 22220885defSDaichi GOTO } 223d00947d8SCraig Rodrigues } 224d00947d8SCraig Rodrigues /* If copymode is UNIONFS_TRADITIONAL, uid/gid is mounted user. */ 225d00947d8SCraig Rodrigues if (copymode == UNIONFS_TRADITIONAL) { 226d00947d8SCraig Rodrigues uid = mp->mnt_cred->cr_ruid; 227d00947d8SCraig Rodrigues gid = mp->mnt_cred->cr_rgid; 228d00947d8SCraig Rodrigues } 229d00947d8SCraig Rodrigues 230d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: uid=%d, gid=%d\n", uid, gid); 231d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: udir=0%03o, ufile=0%03o\n", udir, ufile); 232d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: copymode=%d\n", copymode); 233d00947d8SCraig Rodrigues 234d00947d8SCraig Rodrigues /* 235d00947d8SCraig Rodrigues * Find upper node 236d00947d8SCraig Rodrigues */ 2377265164fSJohn Baldwin NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, target, td); 238d00947d8SCraig Rodrigues if ((error = namei(ndp))) 239d00947d8SCraig Rodrigues return (error); 240d00947d8SCraig Rodrigues 241762e6b85SEivind Eklund NDFREE(ndp, NDF_ONLY_PNBUF); 242d00947d8SCraig Rodrigues 243d00947d8SCraig Rodrigues /* get root vnodes */ 244d00947d8SCraig Rodrigues lowerrootvp = mp->mnt_vnodecovered; 245df8bae1dSRodney W. Grimes upperrootvp = ndp->ni_vp; 246df8bae1dSRodney W. Grimes 247d00947d8SCraig Rodrigues /* create unionfs_mount */ 248d00947d8SCraig Rodrigues ump = (struct unionfs_mount *)malloc(sizeof(struct unionfs_mount), 249a163d034SWarner Losh M_UNIONFSMNT, M_WAITOK | M_ZERO); 2502a31267eSMatthew Dillon 251d00947d8SCraig Rodrigues /* 252d00947d8SCraig Rodrigues * Save reference 253d00947d8SCraig Rodrigues */ 254d00947d8SCraig Rodrigues if (below) { 255b249ce48SMateusz Guzik VOP_UNLOCK(upperrootvp); 256cb05b60aSAttilio Rao vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY); 257d00947d8SCraig Rodrigues ump->um_lowervp = upperrootvp; 258d00947d8SCraig Rodrigues ump->um_uppervp = lowerrootvp; 259d00947d8SCraig Rodrigues } else { 260d00947d8SCraig Rodrigues ump->um_lowervp = lowerrootvp; 261d00947d8SCraig Rodrigues ump->um_uppervp = upperrootvp; 262df8bae1dSRodney W. Grimes } 263d00947d8SCraig Rodrigues ump->um_rootvp = NULLVP; 264d00947d8SCraig Rodrigues ump->um_uid = uid; 265d00947d8SCraig Rodrigues ump->um_gid = gid; 266d00947d8SCraig Rodrigues ump->um_udir = udir; 267d00947d8SCraig Rodrigues ump->um_ufile = ufile; 268d00947d8SCraig Rodrigues ump->um_copymode = copymode; 26920885defSDaichi GOTO ump->um_whitemode = whitemode; 270d00947d8SCraig Rodrigues 27177465d93SAlfred Perlstein mp->mnt_data = ump; 272df8bae1dSRodney W. Grimes 273996c772fSJohn Dyson /* 274d00947d8SCraig Rodrigues * Copy upper layer's RDONLY flag. 275d00947d8SCraig Rodrigues */ 276d00947d8SCraig Rodrigues mp->mnt_flag |= ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY; 277d00947d8SCraig Rodrigues 278d00947d8SCraig Rodrigues /* 279d00947d8SCraig Rodrigues * Unlock the node 280df8bae1dSRodney W. Grimes */ 281b249ce48SMateusz Guzik VOP_UNLOCK(ump->um_uppervp); 282df8bae1dSRodney W. Grimes 283d00947d8SCraig Rodrigues /* 284d00947d8SCraig Rodrigues * Get the unionfs root vnode. 285d00947d8SCraig Rodrigues */ 286d00947d8SCraig Rodrigues error = unionfs_nodeget(mp, ump->um_uppervp, ump->um_lowervp, 287d00947d8SCraig Rodrigues NULLVP, &(ump->um_rootvp), NULL, td); 288d00947d8SCraig Rodrigues vrele(upperrootvp); 28959409cb9SJason A. Harmening if (error != 0) { 290d00947d8SCraig Rodrigues free(ump, M_UNIONFSMNT); 291d00947d8SCraig Rodrigues mp->mnt_data = NULL; 292df8bae1dSRodney W. Grimes return (error); 293df8bae1dSRodney W. Grimes } 294df8bae1dSRodney W. Grimes 295*c746ed72SJason A. Harmening lowermp = vfs_register_upper_from_vp(ump->um_lowervp, mp, 296*c746ed72SJason A. Harmening &ump->um_lower_link); 297*c746ed72SJason A. Harmening uppermp = vfs_register_upper_from_vp(ump->um_uppervp, mp, 298*c746ed72SJason A. Harmening &ump->um_upper_link); 29959409cb9SJason A. Harmening 30059409cb9SJason A. Harmening if (lowermp == NULL || uppermp == NULL) { 30159409cb9SJason A. Harmening if (lowermp != NULL) 302*c746ed72SJason A. Harmening vfs_unregister_upper(lowermp, &ump->um_lower_link); 30359409cb9SJason A. Harmening if (uppermp != NULL) 304*c746ed72SJason A. Harmening vfs_unregister_upper(uppermp, &ump->um_upper_link); 30559409cb9SJason A. Harmening free(ump, M_UNIONFSMNT); 30659409cb9SJason A. Harmening mp->mnt_data = NULL; 30759409cb9SJason A. Harmening return (ENOENT); 30859409cb9SJason A. Harmening } 30959409cb9SJason A. Harmening 310a8a07fd6SMateusz Guzik MNT_ILOCK(mp); 31159409cb9SJason A. Harmening if ((lowermp->mnt_flag & MNT_LOCAL) != 0 && 31259409cb9SJason A. Harmening (uppermp->mnt_flag & MNT_LOCAL) != 0) 313d00947d8SCraig Rodrigues mp->mnt_flag |= MNT_LOCAL; 314d3cc5354SMateusz Guzik mp->mnt_kern_flag |= MNTK_NOMSYNC | MNTK_UNIONFS; 315a8a07fd6SMateusz Guzik MNT_IUNLOCK(mp); 316d00947d8SCraig Rodrigues 317d00947d8SCraig Rodrigues /* 318d00947d8SCraig Rodrigues * Get new fsid 319d00947d8SCraig Rodrigues */ 320d00947d8SCraig Rodrigues vfs_getnewfsid(mp); 321d00947d8SCraig Rodrigues 322852c303bSConrad Meyer snprintf(mp->mnt_stat.f_mntfromname, MNAMELEN, "<%s>:%s", 323852c303bSConrad Meyer below ? "below" : "above", target); 324d00947d8SCraig Rodrigues 325d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: from %s, on %s\n", 326d00947d8SCraig Rodrigues mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 327d00947d8SCraig Rodrigues 328d00947d8SCraig Rodrigues return (0); 329d00947d8SCraig Rodrigues } 330d00947d8SCraig Rodrigues 331d00947d8SCraig Rodrigues /* 332d00947d8SCraig Rodrigues * Free reference to unionfs layer 333df8bae1dSRodney W. Grimes */ 33480b301c3SPoul-Henning Kamp static int 335dfd233edSAttilio Rao unionfs_unmount(struct mount *mp, int mntflags) 336df8bae1dSRodney W. Grimes { 337d00947d8SCraig Rodrigues struct unionfs_mount *ump; 338df8bae1dSRodney W. Grimes int error; 339d00947d8SCraig Rodrigues int num; 340996c772fSJohn Dyson int freeing; 341d00947d8SCraig Rodrigues int flags; 342df8bae1dSRodney W. Grimes 343d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_unmount: mp = %p\n", (void *)mp); 344d00947d8SCraig Rodrigues 345d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 346d00947d8SCraig Rodrigues flags = 0; 347df8bae1dSRodney W. Grimes 348996c772fSJohn Dyson if (mntflags & MNT_FORCE) 349996c772fSJohn Dyson flags |= FORCECLOSE; 350996c772fSJohn Dyson 351d00947d8SCraig Rodrigues /* vflush (no need to call vrele) */ 352dfd233edSAttilio Rao for (freeing = 0; (error = vflush(mp, 1, flags, curthread)) != 0;) { 353d00947d8SCraig Rodrigues num = mp->mnt_nvnodelistsize; 354d00947d8SCraig Rodrigues if (num == freeing) 355996c772fSJohn Dyson break; 356d00947d8SCraig Rodrigues freeing = num; 357df8bae1dSRodney W. Grimes } 358df8bae1dSRodney W. Grimes 3590864ef1eSIan Dowse if (error) 3600864ef1eSIan Dowse return (error); 361df8bae1dSRodney W. Grimes 362*c746ed72SJason A. Harmening vfs_unregister_upper(ump->um_lowervp->v_mount, &ump->um_lower_link); 363*c746ed72SJason A. Harmening vfs_unregister_upper(ump->um_uppervp->v_mount, &ump->um_upper_link); 364d00947d8SCraig Rodrigues free(ump, M_UNIONFSMNT); 36511753bd0SKevin Lo mp->mnt_data = NULL; 366d00947d8SCraig Rodrigues 367df8bae1dSRodney W. Grimes return (0); 368df8bae1dSRodney W. Grimes } 369df8bae1dSRodney W. Grimes 37080b301c3SPoul-Henning Kamp static int 371dfd233edSAttilio Rao unionfs_root(struct mount *mp, int flags, struct vnode **vpp) 372df8bae1dSRodney W. Grimes { 373d00947d8SCraig Rodrigues struct unionfs_mount *ump; 374d00947d8SCraig Rodrigues struct vnode *vp; 375df8bae1dSRodney W. Grimes 376d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 377d00947d8SCraig Rodrigues vp = ump->um_rootvp; 3782a31267eSMatthew Dillon 379d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_root: rootvp=%p locked=%x\n", 38081c794f9SAttilio Rao vp, VOP_ISLOCKED(vp)); 381df8bae1dSRodney W. Grimes 382d00947d8SCraig Rodrigues vref(vp); 383d00947d8SCraig Rodrigues if (flags & LK_TYPE_MASK) 384cb05b60aSAttilio Rao vn_lock(vp, flags); 385df8bae1dSRodney W. Grimes 386d00947d8SCraig Rodrigues *vpp = vp; 387d00947d8SCraig Rodrigues 388d00947d8SCraig Rodrigues return (0); 389df8bae1dSRodney W. Grimes } 390df8bae1dSRodney W. Grimes 39180b301c3SPoul-Henning Kamp static int 392a4b07a27SJason A. Harmening unionfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg, 393a4b07a27SJason A. Harmening bool *mp_busy) 394df8bae1dSRodney W. Grimes { 395a4b07a27SJason A. Harmening struct mount *uppermp; 396d00947d8SCraig Rodrigues struct unionfs_mount *ump; 397a4b07a27SJason A. Harmening int error; 398a4b07a27SJason A. Harmening bool unbusy; 399df8bae1dSRodney W. Grimes 400d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 401a4b07a27SJason A. Harmening uppermp = atomic_load_ptr(&ump->um_uppervp->v_mount); 402a4b07a27SJason A. Harmening KASSERT(*mp_busy == true, ("upper mount not busy")); 403a4b07a27SJason A. Harmening /* 404a4b07a27SJason A. Harmening * See comment in sys_quotactl() for an explanation of why the 405a4b07a27SJason A. Harmening * lower mount needs to be busied by the caller of VFS_QUOTACTL() 406a4b07a27SJason A. Harmening * but may be unbusied by the implementation. We must unbusy 407a4b07a27SJason A. Harmening * the upper mount for the same reason; otherwise a namei lookup 408a4b07a27SJason A. Harmening * issued by the VFS_QUOTACTL() implementation could traverse the 409a4b07a27SJason A. Harmening * upper mount and deadlock. 410a4b07a27SJason A. Harmening */ 411a4b07a27SJason A. Harmening vfs_unbusy(mp); 412a4b07a27SJason A. Harmening *mp_busy = false; 413a4b07a27SJason A. Harmening unbusy = true; 414a4b07a27SJason A. Harmening error = vfs_busy(uppermp, 0); 415d00947d8SCraig Rodrigues /* 416d00947d8SCraig Rodrigues * Writing is always performed to upper vnode. 417d00947d8SCraig Rodrigues */ 418a4b07a27SJason A. Harmening if (error == 0) 419a4b07a27SJason A. Harmening error = VFS_QUOTACTL(uppermp, cmd, uid, arg, &unbusy); 420a4b07a27SJason A. Harmening if (unbusy) 421a4b07a27SJason A. Harmening vfs_unbusy(uppermp); 422a4b07a27SJason A. Harmening 423a4b07a27SJason A. Harmening return (error); 424d00947d8SCraig Rodrigues } 425d00947d8SCraig Rodrigues 426d00947d8SCraig Rodrigues static int 427dfd233edSAttilio Rao unionfs_statfs(struct mount *mp, struct statfs *sbp) 428d00947d8SCraig Rodrigues { 429d00947d8SCraig Rodrigues struct unionfs_mount *ump; 430d00947d8SCraig Rodrigues int error; 4312f304845SKonstantin Belousov struct statfs *mstat; 432d00947d8SCraig Rodrigues uint64_t lbsize; 433d00947d8SCraig Rodrigues 434d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 435d00947d8SCraig Rodrigues 436d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_statfs(mp = %p, lvp = %p, uvp = %p)\n", 437d00947d8SCraig Rodrigues (void *)mp, (void *)ump->um_lowervp, (void *)ump->um_uppervp); 438df8bae1dSRodney W. Grimes 4392f304845SKonstantin Belousov mstat = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK | M_ZERO); 440df8bae1dSRodney W. Grimes 4412f304845SKonstantin Belousov error = VFS_STATFS(ump->um_lowervp->v_mount, mstat); 4422f304845SKonstantin Belousov if (error) { 4432f304845SKonstantin Belousov free(mstat, M_STATFS); 444df8bae1dSRodney W. Grimes return (error); 4452f304845SKonstantin Belousov } 446d00947d8SCraig Rodrigues 447d00947d8SCraig Rodrigues /* now copy across the "interesting" information and fake the rest */ 4482f304845SKonstantin Belousov sbp->f_blocks = mstat->f_blocks; 4492f304845SKonstantin Belousov sbp->f_files = mstat->f_files; 450d00947d8SCraig Rodrigues 4512f304845SKonstantin Belousov lbsize = mstat->f_bsize; 452d00947d8SCraig Rodrigues 4532f304845SKonstantin Belousov error = VFS_STATFS(ump->um_uppervp->v_mount, mstat); 4542f304845SKonstantin Belousov if (error) { 4552f304845SKonstantin Belousov free(mstat, M_STATFS); 456d00947d8SCraig Rodrigues return (error); 4572f304845SKonstantin Belousov } 4582f304845SKonstantin Belousov 4591c4ccf09SDon Lewis /* 460d00947d8SCraig Rodrigues * The FS type etc is copy from upper vfs. 461d00947d8SCraig Rodrigues * (write able vfs have priority) 4621c4ccf09SDon Lewis */ 4632f304845SKonstantin Belousov sbp->f_type = mstat->f_type; 4642f304845SKonstantin Belousov sbp->f_flags = mstat->f_flags; 4652f304845SKonstantin Belousov sbp->f_bsize = mstat->f_bsize; 4662f304845SKonstantin Belousov sbp->f_iosize = mstat->f_iosize; 467df8bae1dSRodney W. Grimes 4682f304845SKonstantin Belousov if (mstat->f_bsize != lbsize) 4692f304845SKonstantin Belousov sbp->f_blocks = ((off_t)sbp->f_blocks * lbsize) / 4702f304845SKonstantin Belousov mstat->f_bsize; 471996c772fSJohn Dyson 4722f304845SKonstantin Belousov sbp->f_blocks += mstat->f_blocks; 4732f304845SKonstantin Belousov sbp->f_bfree = mstat->f_bfree; 4742f304845SKonstantin Belousov sbp->f_bavail = mstat->f_bavail; 4752f304845SKonstantin Belousov sbp->f_files += mstat->f_files; 4762f304845SKonstantin Belousov sbp->f_ffree = mstat->f_ffree; 4772f304845SKonstantin Belousov 4782f304845SKonstantin Belousov free(mstat, M_STATFS); 479df8bae1dSRodney W. Grimes return (0); 480df8bae1dSRodney W. Grimes } 481df8bae1dSRodney W. Grimes 482d00947d8SCraig Rodrigues static int 483dfd233edSAttilio Rao unionfs_sync(struct mount *mp, int waitfor) 484d00947d8SCraig Rodrigues { 485d00947d8SCraig Rodrigues /* nothing to do */ 486d00947d8SCraig Rodrigues return (0); 487d00947d8SCraig Rodrigues } 488d00947d8SCraig Rodrigues 489d00947d8SCraig Rodrigues static int 490d00947d8SCraig Rodrigues unionfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) 491d00947d8SCraig Rodrigues { 492d00947d8SCraig Rodrigues return (EOPNOTSUPP); 493d00947d8SCraig Rodrigues } 494d00947d8SCraig Rodrigues 495d00947d8SCraig Rodrigues static int 496694a586aSRick Macklem unionfs_fhtovp(struct mount *mp, struct fid *fidp, int flags, 497694a586aSRick Macklem struct vnode **vpp) 498d00947d8SCraig Rodrigues { 499d00947d8SCraig Rodrigues return (EOPNOTSUPP); 500d00947d8SCraig Rodrigues } 501d00947d8SCraig Rodrigues 502d00947d8SCraig Rodrigues static int 5031f7104d7SRick Macklem unionfs_checkexp(struct mount *mp, struct sockaddr *nam, uint64_t *extflagsp, 5041f7104d7SRick Macklem struct ucred **credanonp, int *numsecflavors, int *secflavors) 505d00947d8SCraig Rodrigues { 506d00947d8SCraig Rodrigues return (EOPNOTSUPP); 507d00947d8SCraig Rodrigues } 508d00947d8SCraig Rodrigues 509d00947d8SCraig Rodrigues static int 510d00947d8SCraig Rodrigues unionfs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, 511dfd233edSAttilio Rao int namespace, const char *attrname) 512d00947d8SCraig Rodrigues { 513d00947d8SCraig Rodrigues struct unionfs_mount *ump; 514d00947d8SCraig Rodrigues struct unionfs_node *unp; 515d00947d8SCraig Rodrigues 516d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 517d00947d8SCraig Rodrigues unp = VTOUNIONFS(filename_vp); 518d00947d8SCraig Rodrigues 519d00947d8SCraig Rodrigues if (unp->un_uppervp != NULLVP) { 520d00947d8SCraig Rodrigues return (VFS_EXTATTRCTL(ump->um_uppervp->v_mount, cmd, 521dfd233edSAttilio Rao unp->un_uppervp, namespace, attrname)); 522d00947d8SCraig Rodrigues } else { 523d00947d8SCraig Rodrigues return (VFS_EXTATTRCTL(ump->um_lowervp->v_mount, cmd, 524dfd233edSAttilio Rao unp->un_lowervp, namespace, attrname)); 525d00947d8SCraig Rodrigues } 526d00947d8SCraig Rodrigues } 527d00947d8SCraig Rodrigues 528d00947d8SCraig Rodrigues static struct vfsops unionfs_vfsops = { 529d00947d8SCraig Rodrigues .vfs_checkexp = unionfs_checkexp, 530d00947d8SCraig Rodrigues .vfs_extattrctl = unionfs_extattrctl, 531d00947d8SCraig Rodrigues .vfs_fhtovp = unionfs_fhtovp, 532d00947d8SCraig Rodrigues .vfs_init = unionfs_init, 533d00947d8SCraig Rodrigues .vfs_mount = unionfs_domount, 534d00947d8SCraig Rodrigues .vfs_quotactl = unionfs_quotactl, 535d00947d8SCraig Rodrigues .vfs_root = unionfs_root, 536d00947d8SCraig Rodrigues .vfs_statfs = unionfs_statfs, 537d00947d8SCraig Rodrigues .vfs_sync = unionfs_sync, 538d00947d8SCraig Rodrigues .vfs_uninit = unionfs_uninit, 539d00947d8SCraig Rodrigues .vfs_unmount = unionfs_unmount, 540d00947d8SCraig Rodrigues .vfs_vget = unionfs_vget, 541df8bae1dSRodney W. Grimes }; 542c901836cSGarrett Wollman 543d00947d8SCraig Rodrigues VFS_SET(unionfs_vfsops, unionfs, VFCF_LOOPBACK); 544