1d167cf6fSWarner Losh /*- 2996c772fSJohn Dyson * Copyright (c) 1994, 1995 The Regents of the University of California. 3996c772fSJohn Dyson * Copyright (c) 1994, 1995 Jan-Simon Pendry. 4d00947d8SCraig Rodrigues * Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc. 5d00947d8SCraig Rodrigues * Copyright (c) 2006 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. 19df8bae1dSRodney W. Grimes * 4. 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> 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 * Exchange from userland file mode to vmode. 70d00947d8SCraig Rodrigues */ 71d00947d8SCraig Rodrigues static u_short 72d00947d8SCraig Rodrigues mode2vmode(mode_t mode) 73d00947d8SCraig Rodrigues { 74d00947d8SCraig Rodrigues u_short ret; 75d00947d8SCraig Rodrigues 76d00947d8SCraig Rodrigues ret = 0; 77d00947d8SCraig Rodrigues 78d00947d8SCraig Rodrigues /* other */ 79d00947d8SCraig Rodrigues if (mode & S_IXOTH) 80d00947d8SCraig Rodrigues ret |= VEXEC >> 6; 81d00947d8SCraig Rodrigues if (mode & S_IWOTH) 82d00947d8SCraig Rodrigues ret |= VWRITE >> 6; 83d00947d8SCraig Rodrigues if (mode & S_IROTH) 84d00947d8SCraig Rodrigues ret |= VREAD >> 6; 85d00947d8SCraig Rodrigues 86d00947d8SCraig Rodrigues /* group */ 87d00947d8SCraig Rodrigues if (mode & S_IXGRP) 88d00947d8SCraig Rodrigues ret |= VEXEC >> 3; 89d00947d8SCraig Rodrigues if (mode & S_IWGRP) 90d00947d8SCraig Rodrigues ret |= VWRITE >> 3; 91d00947d8SCraig Rodrigues if (mode & S_IRGRP) 92d00947d8SCraig Rodrigues ret |= VREAD >> 3; 93d00947d8SCraig Rodrigues 94d00947d8SCraig Rodrigues /* owner */ 95d00947d8SCraig Rodrigues if (mode & S_IXUSR) 96d00947d8SCraig Rodrigues ret |= VEXEC; 97d00947d8SCraig Rodrigues if (mode & S_IWUSR) 98d00947d8SCraig Rodrigues ret |= VWRITE; 99d00947d8SCraig Rodrigues if (mode & S_IRUSR) 100d00947d8SCraig Rodrigues ret |= VREAD; 101d00947d8SCraig Rodrigues 102d00947d8SCraig Rodrigues return (ret); 103d00947d8SCraig Rodrigues } 104d00947d8SCraig Rodrigues 105d00947d8SCraig Rodrigues /* 106d00947d8SCraig Rodrigues * Mount unionfs layer. 107df8bae1dSRodney W. Grimes */ 10880b301c3SPoul-Henning Kamp static int 109d00947d8SCraig Rodrigues unionfs_domount(struct mount *mp, struct thread *td) 110df8bae1dSRodney W. Grimes { 111d00947d8SCraig Rodrigues int error; 112d00947d8SCraig Rodrigues struct vnode *lowerrootvp; 113d00947d8SCraig Rodrigues struct vnode *upperrootvp; 114d00947d8SCraig Rodrigues struct unionfs_mount *ump; 115d00947d8SCraig Rodrigues char *target; 116d00947d8SCraig Rodrigues char *tmp; 117d00947d8SCraig Rodrigues char *ep; 118df8bae1dSRodney W. Grimes int len; 119d00947d8SCraig Rodrigues size_t done; 120d00947d8SCraig Rodrigues int below; 121d00947d8SCraig Rodrigues uid_t uid; 122d00947d8SCraig Rodrigues gid_t gid; 123d00947d8SCraig Rodrigues u_short udir; 124d00947d8SCraig Rodrigues u_short ufile; 125d00947d8SCraig Rodrigues unionfs_copymode copymode; 12620885defSDaichi GOTO unionfs_whitemode whitemode; 127d1841903STim J. Robbins struct componentname fakecn; 128d00947d8SCraig Rodrigues struct nameidata nd, *ndp; 129d00947d8SCraig Rodrigues struct vattr va; 130df8bae1dSRodney W. Grimes 131d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount(mp = %p)\n", (void *)mp); 132df8bae1dSRodney W. Grimes 133d00947d8SCraig Rodrigues error = 0; 134d00947d8SCraig Rodrigues below = 0; 135d00947d8SCraig Rodrigues uid = 0; 136d00947d8SCraig Rodrigues gid = 0; 137d00947d8SCraig Rodrigues udir = 0; 138d00947d8SCraig Rodrigues ufile = 0; 139524f3f28SDaichi GOTO copymode = UNIONFS_TRANSPARENT; /* default */ 14020885defSDaichi GOTO whitemode = UNIONFS_WHITE_ALWAYS; 141d00947d8SCraig Rodrigues ndp = &nd; 14281bca6ddSKATO Takenori 1431e370dbbSCraig Rodrigues if (mp->mnt_flag & MNT_ROOTFS) { 1441e370dbbSCraig Rodrigues vfs_mount_error(mp, "Cannot union mount root filesystem"); 14564042a76SPoul-Henning Kamp return (EOPNOTSUPP); 1461e370dbbSCraig Rodrigues } 147d00947d8SCraig Rodrigues 14881bca6ddSKATO Takenori /* 149d00947d8SCraig Rodrigues * Update is a no operation. 150df8bae1dSRodney W. Grimes */ 1511e370dbbSCraig Rodrigues if (mp->mnt_flag & MNT_UPDATE) { 1521e370dbbSCraig Rodrigues vfs_mount_error(mp, "unionfs does not support mount update"); 153a9f5c04aSMaxime Henrion return (EOPNOTSUPP); 1541e370dbbSCraig Rodrigues } 155df8bae1dSRodney W. Grimes 156df8bae1dSRodney W. Grimes /* 157d00947d8SCraig Rodrigues * Get argument 158df8bae1dSRodney W. Grimes */ 159d00947d8SCraig Rodrigues error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len); 1603a773ad0SPoul-Henning Kamp if (error) 161d00947d8SCraig Rodrigues error = vfs_getopt(mp->mnt_optnew, "from", (void **)&target, 162d00947d8SCraig Rodrigues &len); 163d00947d8SCraig Rodrigues if (error || target[len - 1] != '\0') { 164d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid target"); 165d00947d8SCraig Rodrigues return (EINVAL); 166d00947d8SCraig Rodrigues } 167d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "below", NULL, NULL) == 0) 168d00947d8SCraig Rodrigues below = 1; 169d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "udir", (void **)&tmp, NULL) == 0) { 170d00947d8SCraig Rodrigues if (tmp != NULL) 171d00947d8SCraig Rodrigues udir = (mode_t)strtol(tmp, &ep, 8); 172d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 173d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid udir"); 174d00947d8SCraig Rodrigues return (EINVAL); 175d00947d8SCraig Rodrigues } 176d00947d8SCraig Rodrigues udir = mode2vmode(udir); 177d00947d8SCraig Rodrigues } 178d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "ufile", (void **)&tmp, NULL) == 0) { 179d00947d8SCraig Rodrigues if (tmp != NULL) 180d00947d8SCraig Rodrigues ufile = (mode_t)strtol(tmp, &ep, 8); 181d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 182d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid ufile"); 183d00947d8SCraig Rodrigues return (EINVAL); 184d00947d8SCraig Rodrigues } 185d00947d8SCraig Rodrigues ufile = mode2vmode(ufile); 186d00947d8SCraig Rodrigues } 187d00947d8SCraig Rodrigues /* check umask, uid and gid */ 188d00947d8SCraig Rodrigues if (udir == 0 && ufile != 0) 189d00947d8SCraig Rodrigues udir = ufile; 190d00947d8SCraig Rodrigues if (ufile == 0 && udir != 0) 191d00947d8SCraig Rodrigues ufile = udir; 192d00947d8SCraig Rodrigues 193d00947d8SCraig Rodrigues vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY, td); 194d00947d8SCraig Rodrigues error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred, td); 195d00947d8SCraig Rodrigues if (!error) { 196d00947d8SCraig Rodrigues if (udir == 0) 197d00947d8SCraig Rodrigues udir = va.va_mode; 198d00947d8SCraig Rodrigues if (ufile == 0) 199d00947d8SCraig Rodrigues ufile = va.va_mode; 200d00947d8SCraig Rodrigues uid = va.va_uid; 201d00947d8SCraig Rodrigues gid = va.va_gid; 202d00947d8SCraig Rodrigues } 203d00947d8SCraig Rodrigues VOP_UNLOCK(mp->mnt_vnodecovered, 0, td); 204d00947d8SCraig Rodrigues if (error) 205d00947d8SCraig Rodrigues return (error); 206d00947d8SCraig Rodrigues 207d00947d8SCraig Rodrigues if (mp->mnt_cred->cr_ruid == 0) { /* root only */ 208d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "uid", (void **)&tmp, 209d00947d8SCraig Rodrigues NULL) == 0) { 210d00947d8SCraig Rodrigues if (tmp != NULL) 211d00947d8SCraig Rodrigues uid = (uid_t)strtol(tmp, &ep, 10); 212d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 213d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid uid"); 214d00947d8SCraig Rodrigues return (EINVAL); 215d00947d8SCraig Rodrigues } 216d00947d8SCraig Rodrigues } 217d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "gid", (void **)&tmp, 218d00947d8SCraig Rodrigues NULL) == 0) { 219d00947d8SCraig Rodrigues if (tmp != NULL) 220d00947d8SCraig Rodrigues gid = (gid_t)strtol(tmp, &ep, 10); 221d00947d8SCraig Rodrigues if (tmp == NULL || *ep) { 222d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid gid"); 223d00947d8SCraig Rodrigues return (EINVAL); 224d00947d8SCraig Rodrigues } 225d00947d8SCraig Rodrigues } 226d00947d8SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "copymode", (void **)&tmp, 227d00947d8SCraig Rodrigues NULL) == 0) { 228d00947d8SCraig Rodrigues if (tmp == NULL) { 229d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid copymode"); 230d00947d8SCraig Rodrigues return (EINVAL); 231d00947d8SCraig Rodrigues } else if (strcasecmp(tmp, "traditional") == 0) 232d00947d8SCraig Rodrigues copymode = UNIONFS_TRADITIONAL; 233d00947d8SCraig Rodrigues else if (strcasecmp(tmp, "transparent") == 0) 234d00947d8SCraig Rodrigues copymode = UNIONFS_TRANSPARENT; 235d00947d8SCraig Rodrigues else if (strcasecmp(tmp, "masquerade") == 0) 236d00947d8SCraig Rodrigues copymode = UNIONFS_MASQUERADE; 237d00947d8SCraig Rodrigues else { 238d00947d8SCraig Rodrigues vfs_mount_error(mp, "Invalid copymode"); 239d00947d8SCraig Rodrigues return (EINVAL); 240d00947d8SCraig Rodrigues } 241d00947d8SCraig Rodrigues } 24220885defSDaichi GOTO if (vfs_getopt(mp->mnt_optnew, "whiteout", (void **)&tmp, 24320885defSDaichi GOTO NULL) == 0) { 24420885defSDaichi GOTO if (tmp == NULL) { 24520885defSDaichi GOTO vfs_mount_error(mp, "Invalid whiteout mode"); 24620885defSDaichi GOTO return (EINVAL); 24720885defSDaichi GOTO } else if (strcasecmp(tmp, "always") == 0) 24820885defSDaichi GOTO whitemode = UNIONFS_WHITE_ALWAYS; 24920885defSDaichi GOTO else if (strcasecmp(tmp, "whenneeded") == 0) 25020885defSDaichi GOTO whitemode = UNIONFS_WHITE_WHENNEEDED; 25120885defSDaichi GOTO else { 25220885defSDaichi GOTO vfs_mount_error(mp, "Invalid whiteout mode"); 25320885defSDaichi GOTO return (EINVAL); 25420885defSDaichi GOTO } 25520885defSDaichi GOTO } 256d00947d8SCraig Rodrigues } 257d00947d8SCraig Rodrigues /* If copymode is UNIONFS_TRADITIONAL, uid/gid is mounted user. */ 258d00947d8SCraig Rodrigues if (copymode == UNIONFS_TRADITIONAL) { 259d00947d8SCraig Rodrigues uid = mp->mnt_cred->cr_ruid; 260d00947d8SCraig Rodrigues gid = mp->mnt_cred->cr_rgid; 261d00947d8SCraig Rodrigues } 262d00947d8SCraig Rodrigues 263d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: uid=%d, gid=%d\n", uid, gid); 264d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: udir=0%03o, ufile=0%03o\n", udir, ufile); 265d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: copymode=%d\n", copymode); 266d00947d8SCraig Rodrigues 267d00947d8SCraig Rodrigues /* 268d00947d8SCraig Rodrigues * Find upper node 269d00947d8SCraig Rodrigues */ 270d00947d8SCraig Rodrigues NDINIT(ndp, LOOKUP, FOLLOW | WANTPARENT | LOCKLEAF, UIO_SYSSPACE, target, td); 271d00947d8SCraig Rodrigues if ((error = namei(ndp))) 272d00947d8SCraig Rodrigues return (error); 273d00947d8SCraig Rodrigues 274762e6b85SEivind Eklund NDFREE(ndp, NDF_ONLY_PNBUF); 275d00947d8SCraig Rodrigues 276d00947d8SCraig Rodrigues /* get root vnodes */ 277d00947d8SCraig Rodrigues lowerrootvp = mp->mnt_vnodecovered; 278df8bae1dSRodney W. Grimes upperrootvp = ndp->ni_vp; 279df8bae1dSRodney W. Grimes 280d00947d8SCraig Rodrigues vrele(ndp->ni_dvp); 281d00947d8SCraig Rodrigues ndp->ni_dvp = NULLVP; 2822a31267eSMatthew Dillon 283d00947d8SCraig Rodrigues /* create unionfs_mount */ 284d00947d8SCraig Rodrigues ump = (struct unionfs_mount *)malloc(sizeof(struct unionfs_mount), 285a163d034SWarner Losh M_UNIONFSMNT, M_WAITOK | M_ZERO); 2862a31267eSMatthew Dillon 287d00947d8SCraig Rodrigues /* 288d00947d8SCraig Rodrigues * Save reference 289d00947d8SCraig Rodrigues */ 290d00947d8SCraig Rodrigues if (below) { 291568556d7SJeff Roberson VOP_UNLOCK(upperrootvp, 0, td); 292d00947d8SCraig Rodrigues vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY, td); 293d00947d8SCraig Rodrigues ump->um_lowervp = upperrootvp; 294d00947d8SCraig Rodrigues ump->um_uppervp = lowerrootvp; 295d00947d8SCraig Rodrigues } else { 296d00947d8SCraig Rodrigues ump->um_lowervp = lowerrootvp; 297d00947d8SCraig Rodrigues ump->um_uppervp = upperrootvp; 298df8bae1dSRodney W. Grimes } 299d00947d8SCraig Rodrigues ump->um_rootvp = NULLVP; 300d00947d8SCraig Rodrigues ump->um_uid = uid; 301d00947d8SCraig Rodrigues ump->um_gid = gid; 302d00947d8SCraig Rodrigues ump->um_udir = udir; 303d00947d8SCraig Rodrigues ump->um_ufile = ufile; 304d00947d8SCraig Rodrigues ump->um_copymode = copymode; 30520885defSDaichi GOTO ump->um_whitemode = whitemode; 306d00947d8SCraig Rodrigues 30757821163SDaichi GOTO MNT_ILOCK(mp); 30857821163SDaichi GOTO if ((lowerrootvp->v_mount->mnt_kern_flag & MNTK_MPSAFE) && 30957821163SDaichi GOTO (upperrootvp->v_mount->mnt_kern_flag & MNTK_MPSAFE)) 31057821163SDaichi GOTO mp->mnt_kern_flag |= MNTK_MPSAFE; 31157821163SDaichi GOTO MNT_IUNLOCK(mp); 31277465d93SAlfred Perlstein mp->mnt_data = ump; 313df8bae1dSRodney W. Grimes 314996c772fSJohn Dyson /* 315d00947d8SCraig Rodrigues * Copy upper layer's RDONLY flag. 316d00947d8SCraig Rodrigues */ 317d00947d8SCraig Rodrigues mp->mnt_flag |= ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY; 318d00947d8SCraig Rodrigues 319d00947d8SCraig Rodrigues /* 320d00947d8SCraig Rodrigues * Check whiteout 321996c772fSJohn Dyson */ 322996c772fSJohn Dyson if ((mp->mnt_flag & MNT_RDONLY) == 0) { 323d00947d8SCraig Rodrigues memset(&fakecn, 0, sizeof(fakecn)); 324d1841903STim J. Robbins fakecn.cn_nameiop = LOOKUP; 325d1841903STim J. Robbins fakecn.cn_thread = td; 326d00947d8SCraig Rodrigues error = VOP_WHITEOUT(ump->um_uppervp, &fakecn, LOOKUP); 327d00947d8SCraig Rodrigues if (error) { 328d00947d8SCraig Rodrigues if (below) { 329d00947d8SCraig Rodrigues VOP_UNLOCK(ump->um_uppervp, 0, td); 330d00947d8SCraig Rodrigues vrele(upperrootvp); 331d00947d8SCraig Rodrigues } else 332d00947d8SCraig Rodrigues vput(ump->um_uppervp); 333d00947d8SCraig Rodrigues free(ump, M_UNIONFSMNT); 334d00947d8SCraig Rodrigues mp->mnt_data = NULL; 335d00947d8SCraig Rodrigues return (error); 3365da56ddbSTor Egge } 337df8bae1dSRodney W. Grimes } 338df8bae1dSRodney W. Grimes 339df8bae1dSRodney W. Grimes /* 340d00947d8SCraig Rodrigues * Unlock the node 341df8bae1dSRodney W. Grimes */ 342d00947d8SCraig Rodrigues VOP_UNLOCK(ump->um_uppervp, 0, td); 343df8bae1dSRodney W. Grimes 344d00947d8SCraig Rodrigues /* 345d00947d8SCraig Rodrigues * Get the unionfs root vnode. 346d00947d8SCraig Rodrigues */ 347d00947d8SCraig Rodrigues error = unionfs_nodeget(mp, ump->um_uppervp, ump->um_lowervp, 348d00947d8SCraig Rodrigues NULLVP, &(ump->um_rootvp), NULL, td); 349d00947d8SCraig Rodrigues vrele(upperrootvp); 3507d72c5e6SDaichi GOTO if (error) { 351d00947d8SCraig Rodrigues free(ump, M_UNIONFSMNT); 352d00947d8SCraig Rodrigues mp->mnt_data = NULL; 353df8bae1dSRodney W. Grimes return (error); 354df8bae1dSRodney W. Grimes } 355df8bae1dSRodney W. Grimes 356df8bae1dSRodney W. Grimes /* 357d00947d8SCraig Rodrigues * Check mnt_flag 358d00947d8SCraig Rodrigues */ 359d00947d8SCraig Rodrigues if ((ump->um_lowervp->v_mount->mnt_flag & MNT_LOCAL) && 360d00947d8SCraig Rodrigues (ump->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) 361d00947d8SCraig Rodrigues mp->mnt_flag |= MNT_LOCAL; 362d00947d8SCraig Rodrigues 363d00947d8SCraig Rodrigues /* 364d00947d8SCraig Rodrigues * Get new fsid 365d00947d8SCraig Rodrigues */ 366d00947d8SCraig Rodrigues vfs_getnewfsid(mp); 367d00947d8SCraig Rodrigues 368d00947d8SCraig Rodrigues len = MNAMELEN - 1; 369d00947d8SCraig Rodrigues tmp = mp->mnt_stat.f_mntfromname; 370d00947d8SCraig Rodrigues copystr((below ? "<below>:" : "<above>:"), tmp, len, &done); 371d00947d8SCraig Rodrigues len -= done - 1; 372d00947d8SCraig Rodrigues tmp += done - 1; 373d00947d8SCraig Rodrigues copystr(target, tmp, len, NULL); 374d00947d8SCraig Rodrigues 375d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_mount: from %s, on %s\n", 376d00947d8SCraig Rodrigues mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 377d00947d8SCraig Rodrigues 378d00947d8SCraig Rodrigues return (0); 379d00947d8SCraig Rodrigues } 380d00947d8SCraig Rodrigues 381d00947d8SCraig Rodrigues /* 382d00947d8SCraig Rodrigues * Free reference to unionfs layer 383df8bae1dSRodney W. Grimes */ 38480b301c3SPoul-Henning Kamp static int 385d00947d8SCraig Rodrigues unionfs_unmount(struct mount *mp, int mntflags, struct thread *td) 386df8bae1dSRodney W. Grimes { 387d00947d8SCraig Rodrigues struct unionfs_mount *ump; 388df8bae1dSRodney W. Grimes int error; 389d00947d8SCraig Rodrigues int num; 390996c772fSJohn Dyson int freeing; 391d00947d8SCraig Rodrigues int flags; 392df8bae1dSRodney W. Grimes 393d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_unmount: mp = %p\n", (void *)mp); 394d00947d8SCraig Rodrigues 395d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 396d00947d8SCraig Rodrigues flags = 0; 397df8bae1dSRodney W. Grimes 398996c772fSJohn Dyson if (mntflags & MNT_FORCE) 399996c772fSJohn Dyson flags |= FORCECLOSE; 400996c772fSJohn Dyson 401d00947d8SCraig Rodrigues /* vflush (no need to call vrele) */ 402d00947d8SCraig Rodrigues for (freeing = 0; (error = vflush(mp, 1, flags, td)) != 0;) { 403d00947d8SCraig Rodrigues num = mp->mnt_nvnodelistsize; 404d00947d8SCraig Rodrigues if (num == freeing) 405996c772fSJohn Dyson break; 406d00947d8SCraig Rodrigues freeing = num; 407df8bae1dSRodney W. Grimes } 408df8bae1dSRodney W. Grimes 4090864ef1eSIan Dowse if (error) 4100864ef1eSIan Dowse return (error); 411df8bae1dSRodney W. Grimes 412d00947d8SCraig Rodrigues free(ump, M_UNIONFSMNT); 413df8bae1dSRodney W. Grimes mp->mnt_data = 0; 414d00947d8SCraig Rodrigues 415df8bae1dSRodney W. Grimes return (0); 416df8bae1dSRodney W. Grimes } 417df8bae1dSRodney W. Grimes 41880b301c3SPoul-Henning Kamp static int 419d00947d8SCraig Rodrigues unionfs_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td) 420df8bae1dSRodney W. Grimes { 421d00947d8SCraig Rodrigues struct unionfs_mount *ump; 422d00947d8SCraig Rodrigues struct vnode *vp; 423df8bae1dSRodney W. Grimes 424d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 425d00947d8SCraig Rodrigues vp = ump->um_rootvp; 4262a31267eSMatthew Dillon 427d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_root: rootvp=%p locked=%x\n", 428d00947d8SCraig Rodrigues vp, VOP_ISLOCKED(vp, td)); 429df8bae1dSRodney W. Grimes 430d00947d8SCraig Rodrigues vref(vp); 431d00947d8SCraig Rodrigues if (flags & LK_TYPE_MASK) 432d00947d8SCraig Rodrigues vn_lock(vp, flags, td); 433df8bae1dSRodney W. Grimes 434d00947d8SCraig Rodrigues *vpp = vp; 435d00947d8SCraig Rodrigues 436d00947d8SCraig Rodrigues return (0); 437df8bae1dSRodney W. Grimes } 438df8bae1dSRodney W. Grimes 43980b301c3SPoul-Henning Kamp static int 440d00947d8SCraig Rodrigues unionfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg, 441d00947d8SCraig Rodrigues struct thread *td) 442df8bae1dSRodney W. Grimes { 443d00947d8SCraig Rodrigues struct unionfs_mount *ump; 444df8bae1dSRodney W. Grimes 445d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 446d00947d8SCraig Rodrigues 447d00947d8SCraig Rodrigues /* 448d00947d8SCraig Rodrigues * Writing is always performed to upper vnode. 449d00947d8SCraig Rodrigues */ 450d00947d8SCraig Rodrigues return (VFS_QUOTACTL(ump->um_uppervp->v_mount, cmd, uid, arg, td)); 451d00947d8SCraig Rodrigues } 452d00947d8SCraig Rodrigues 453d00947d8SCraig Rodrigues static int 454d00947d8SCraig Rodrigues unionfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td) 455d00947d8SCraig Rodrigues { 456d00947d8SCraig Rodrigues struct unionfs_mount *ump; 457d00947d8SCraig Rodrigues int error; 458d00947d8SCraig Rodrigues struct statfs mstat; 459d00947d8SCraig Rodrigues uint64_t lbsize; 460d00947d8SCraig Rodrigues 461d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 462d00947d8SCraig Rodrigues 463d00947d8SCraig Rodrigues UNIONFSDEBUG("unionfs_statfs(mp = %p, lvp = %p, uvp = %p)\n", 464d00947d8SCraig Rodrigues (void *)mp, (void *)ump->um_lowervp, (void *)ump->um_uppervp); 465df8bae1dSRodney W. Grimes 466df8bae1dSRodney W. Grimes bzero(&mstat, sizeof(mstat)); 467df8bae1dSRodney W. Grimes 468d00947d8SCraig Rodrigues error = VFS_STATFS(ump->um_lowervp->v_mount, &mstat, td); 469df8bae1dSRodney W. Grimes if (error) 470df8bae1dSRodney W. Grimes return (error); 471d00947d8SCraig Rodrigues 472d00947d8SCraig Rodrigues /* now copy across the "interesting" information and fake the rest */ 473d00947d8SCraig Rodrigues sbp->f_blocks = mstat.f_blocks; 474d00947d8SCraig Rodrigues sbp->f_files = mstat.f_files; 475d00947d8SCraig Rodrigues 476d00947d8SCraig Rodrigues lbsize = mstat.f_bsize; 477d00947d8SCraig Rodrigues 478d00947d8SCraig Rodrigues error = VFS_STATFS(ump->um_uppervp->v_mount, &mstat, td); 479d00947d8SCraig Rodrigues if (error) 480d00947d8SCraig Rodrigues return (error); 481df8bae1dSRodney W. Grimes 4821c4ccf09SDon Lewis /* 483d00947d8SCraig Rodrigues * The FS type etc is copy from upper vfs. 484d00947d8SCraig Rodrigues * (write able vfs have priority) 4851c4ccf09SDon Lewis */ 486df8bae1dSRodney W. Grimes sbp->f_type = mstat.f_type; 487df8bae1dSRodney W. Grimes sbp->f_flags = mstat.f_flags; 488df8bae1dSRodney W. Grimes sbp->f_bsize = mstat.f_bsize; 489df8bae1dSRodney W. Grimes sbp->f_iosize = mstat.f_iosize; 490df8bae1dSRodney W. Grimes 491996c772fSJohn Dyson if (mstat.f_bsize != lbsize) 492c9bf0111SKATO Takenori sbp->f_blocks = ((off_t)sbp->f_blocks * lbsize) / mstat.f_bsize; 493996c772fSJohn Dyson 494df8bae1dSRodney W. Grimes sbp->f_blocks += mstat.f_blocks; 495996c772fSJohn Dyson sbp->f_bfree = mstat.f_bfree; 496996c772fSJohn Dyson sbp->f_bavail = mstat.f_bavail; 497df8bae1dSRodney W. Grimes sbp->f_files += mstat.f_files; 498996c772fSJohn Dyson sbp->f_ffree = mstat.f_ffree; 499df8bae1dSRodney W. Grimes return (0); 500df8bae1dSRodney W. Grimes } 501df8bae1dSRodney W. Grimes 502d00947d8SCraig Rodrigues static int 503d00947d8SCraig Rodrigues unionfs_sync(struct mount *mp, int waitfor, struct thread *td) 504d00947d8SCraig Rodrigues { 505d00947d8SCraig Rodrigues /* nothing to do */ 506d00947d8SCraig Rodrigues return (0); 507d00947d8SCraig Rodrigues } 508d00947d8SCraig Rodrigues 509d00947d8SCraig Rodrigues static int 510d00947d8SCraig Rodrigues unionfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) 511d00947d8SCraig Rodrigues { 512d00947d8SCraig Rodrigues return (EOPNOTSUPP); 513d00947d8SCraig Rodrigues } 514d00947d8SCraig Rodrigues 515d00947d8SCraig Rodrigues static int 516d00947d8SCraig Rodrigues unionfs_fhtovp(struct mount *mp, struct fid *fidp, struct vnode **vpp) 517d00947d8SCraig Rodrigues { 518d00947d8SCraig Rodrigues return (EOPNOTSUPP); 519d00947d8SCraig Rodrigues } 520d00947d8SCraig Rodrigues 521d00947d8SCraig Rodrigues static int 522d00947d8SCraig Rodrigues unionfs_checkexp(struct mount *mp, struct sockaddr *nam, int *extflagsp, 523d00947d8SCraig Rodrigues struct ucred **credanonp) 524d00947d8SCraig Rodrigues { 525d00947d8SCraig Rodrigues return (EOPNOTSUPP); 526d00947d8SCraig Rodrigues } 527d00947d8SCraig Rodrigues 528d00947d8SCraig Rodrigues static int 529d00947d8SCraig Rodrigues unionfs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, 530d00947d8SCraig Rodrigues int namespace, const char *attrname, struct thread *td) 531d00947d8SCraig Rodrigues { 532d00947d8SCraig Rodrigues struct unionfs_mount *ump; 533d00947d8SCraig Rodrigues struct unionfs_node *unp; 534d00947d8SCraig Rodrigues 535d00947d8SCraig Rodrigues ump = MOUNTTOUNIONFSMOUNT(mp); 536d00947d8SCraig Rodrigues unp = VTOUNIONFS(filename_vp); 537d00947d8SCraig Rodrigues 538d00947d8SCraig Rodrigues if (unp->un_uppervp != NULLVP) { 539d00947d8SCraig Rodrigues return (VFS_EXTATTRCTL(ump->um_uppervp->v_mount, cmd, 540d00947d8SCraig Rodrigues unp->un_uppervp, namespace, attrname, td)); 541d00947d8SCraig Rodrigues } else { 542d00947d8SCraig Rodrigues return (VFS_EXTATTRCTL(ump->um_lowervp->v_mount, cmd, 543d00947d8SCraig Rodrigues unp->un_lowervp, namespace, attrname, td)); 544d00947d8SCraig Rodrigues } 545d00947d8SCraig Rodrigues } 546d00947d8SCraig Rodrigues 547d00947d8SCraig Rodrigues static struct vfsops unionfs_vfsops = { 548d00947d8SCraig Rodrigues .vfs_checkexp = unionfs_checkexp, 549d00947d8SCraig Rodrigues .vfs_extattrctl = unionfs_extattrctl, 550d00947d8SCraig Rodrigues .vfs_fhtovp = unionfs_fhtovp, 551d00947d8SCraig Rodrigues .vfs_init = unionfs_init, 552d00947d8SCraig Rodrigues .vfs_mount = unionfs_domount, 553d00947d8SCraig Rodrigues .vfs_quotactl = unionfs_quotactl, 554d00947d8SCraig Rodrigues .vfs_root = unionfs_root, 555d00947d8SCraig Rodrigues .vfs_statfs = unionfs_statfs, 556d00947d8SCraig Rodrigues .vfs_sync = unionfs_sync, 557d00947d8SCraig Rodrigues .vfs_uninit = unionfs_uninit, 558d00947d8SCraig Rodrigues .vfs_unmount = unionfs_unmount, 559d00947d8SCraig Rodrigues .vfs_vget = unionfs_vget, 560df8bae1dSRodney W. Grimes }; 561c901836cSGarrett Wollman 562d00947d8SCraig Rodrigues VFS_SET(unionfs_vfsops, unionfs, VFCF_LOOPBACK); 563