1d167cf6fSWarner Losh /*- 251369649SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 351369649SPedro F. Giffuni * 4996c772fSJohn Dyson * Copyright (c) 1992, 1993, 1995 5df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 6df8bae1dSRodney W. Grimes * 7df8bae1dSRodney W. Grimes * This code is derived from software donated to Berkeley by 8df8bae1dSRodney W. Grimes * Jan-Simon Pendry. 9df8bae1dSRodney W. Grimes * 10df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 11df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 12df8bae1dSRodney W. Grimes * are met: 13df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 14df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 15df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 16df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 17df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 18fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 19df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 20df8bae1dSRodney W. Grimes * without specific prior written permission. 21df8bae1dSRodney W. Grimes * 22df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32df8bae1dSRodney W. Grimes * SUCH DAMAGE. 33df8bae1dSRodney W. Grimes * 34df8bae1dSRodney W. Grimes * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94 35df8bae1dSRodney W. Grimes * 36df8bae1dSRodney W. Grimes * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92 37c3aac50fSPeter Wemm * $FreeBSD$ 38df8bae1dSRodney W. Grimes */ 39df8bae1dSRodney W. Grimes 40df8bae1dSRodney W. Grimes /* 41df8bae1dSRodney W. Grimes * Null Layer 42df8bae1dSRodney W. Grimes * (See null_vnops.c for a description of what this does.) 43df8bae1dSRodney W. Grimes */ 44df8bae1dSRodney W. Grimes 45df8bae1dSRodney W. Grimes #include <sys/param.h> 46df8bae1dSRodney W. Grimes #include <sys/systm.h> 4757b4252eSKonstantin Belousov #include <sys/fcntl.h> 48fdc0430eSMike Pritchard #include <sys/kernel.h> 49fb919e4dSMark Murray #include <sys/lock.h> 50a1c995b6SPoul-Henning Kamp #include <sys/malloc.h> 51df8bae1dSRodney W. Grimes #include <sys/mount.h> 52df8bae1dSRodney W. Grimes #include <sys/namei.h> 53fb919e4dSMark Murray #include <sys/proc.h> 54fb919e4dSMark Murray #include <sys/vnode.h> 55bf3db8aaSMartin Matuska #include <sys/jail.h> 56fb919e4dSMark Murray 5799d300a1SRuslan Ermilov #include <fs/nullfs/null.h> 58df8bae1dSRodney W. Grimes 595bb84bc8SRobert Watson static MALLOC_DEFINE(M_NULLFSMNT, "nullfs_mount", "NULLFS mount structure"); 60a1c995b6SPoul-Henning Kamp 617652131bSPoul-Henning Kamp static vfs_fhtovp_t nullfs_fhtovp; 625e8c582aSPoul-Henning Kamp static vfs_mount_t nullfs_mount; 637652131bSPoul-Henning Kamp static vfs_quotactl_t nullfs_quotactl; 647652131bSPoul-Henning Kamp static vfs_root_t nullfs_root; 657652131bSPoul-Henning Kamp static vfs_sync_t nullfs_sync; 667652131bSPoul-Henning Kamp static vfs_statfs_t nullfs_statfs; 677652131bSPoul-Henning Kamp static vfs_unmount_t nullfs_unmount; 687652131bSPoul-Henning Kamp static vfs_vget_t nullfs_vget; 697652131bSPoul-Henning Kamp static vfs_extattrctl_t nullfs_extattrctl; 709b5e8b3aSBruce Evans 71df8bae1dSRodney W. Grimes /* 72df8bae1dSRodney W. Grimes * Mount null layer 73df8bae1dSRodney W. Grimes */ 74d4b7a369SPoul-Henning Kamp static int 75dfd233edSAttilio Rao nullfs_mount(struct mount *mp) 76df8bae1dSRodney W. Grimes { 775fc9e11cSKonstantin Belousov struct vnode *lowerrootvp; 78df8bae1dSRodney W. Grimes struct vnode *nullm_rootvp; 79df8bae1dSRodney W. Grimes struct null_mount *xmp; 80930cc2dbSKonstantin Belousov struct null_node *nn; 81930cc2dbSKonstantin Belousov struct nameidata nd, *ndp; 829fcc512cSMaxime Henrion char *target; 83930cc2dbSKonstantin Belousov int error, len; 84930cc2dbSKonstantin Belousov bool isvnunlocked; 85df8bae1dSRodney W. Grimes 868da80660SBoris Popov NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp); 87df8bae1dSRodney W. Grimes 8864042a76SPoul-Henning Kamp if (mp->mnt_flag & MNT_ROOTFS) 8964042a76SPoul-Henning Kamp return (EOPNOTSUPP); 909cf4c952SKonstantin Belousov 91df8bae1dSRodney W. Grimes /* 92df8bae1dSRodney W. Grimes * Update is a no-op 93df8bae1dSRodney W. Grimes */ 94df8bae1dSRodney W. Grimes if (mp->mnt_flag & MNT_UPDATE) { 95ebbf93fdSCraig Rodrigues /* 96ebbf93fdSCraig Rodrigues * Only support update mounts for NFS export. 97ebbf93fdSCraig Rodrigues */ 98ebbf93fdSCraig Rodrigues if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) 99ebbf93fdSCraig Rodrigues return (0); 100ebbf93fdSCraig Rodrigues else 101df8bae1dSRodney W. Grimes return (EOPNOTSUPP); 102df8bae1dSRodney W. Grimes } 103df8bae1dSRodney W. Grimes 104df8bae1dSRodney W. Grimes /* 105df8bae1dSRodney W. Grimes * Get argument 106df8bae1dSRodney W. Grimes */ 107e3c51151SEdward Tomasz Napierala error = vfs_getopt(mp->mnt_optnew, "from", (void **)&target, &len); 108e3c51151SEdward Tomasz Napierala if (error != 0) 1099fcc512cSMaxime Henrion error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len); 1109fcc512cSMaxime Henrion if (error || target[len - 1] != '\0') 1119fcc512cSMaxime Henrion return (EINVAL); 112df8bae1dSRodney W. Grimes 113df8bae1dSRodney W. Grimes /* 1149ce73797SPeter Holm * Unlock lower node to avoid possible deadlock. 115f85e8fc5SKATO Takenori */ 116930cc2dbSKonstantin Belousov if (mp->mnt_vnodecovered->v_op == &null_vnodeops && 1179ce73797SPeter Holm VOP_ISLOCKED(mp->mnt_vnodecovered) == LK_EXCLUSIVE) { 118b249ce48SMateusz Guzik VOP_UNLOCK(mp->mnt_vnodecovered); 119930cc2dbSKonstantin Belousov isvnunlocked = true; 120930cc2dbSKonstantin Belousov } else { 121930cc2dbSKonstantin Belousov isvnunlocked = false; 122f85e8fc5SKATO Takenori } 123930cc2dbSKonstantin Belousov 124f85e8fc5SKATO Takenori /* 125df8bae1dSRodney W. Grimes * Find lower node 126df8bae1dSRodney W. Grimes */ 127930cc2dbSKonstantin Belousov ndp = &nd; 1287e1d3eefSMateusz Guzik NDINIT(ndp, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, target); 1293a773ad0SPoul-Henning Kamp error = namei(ndp); 130d9e9650aSKonstantin Belousov 131f85e8fc5SKATO Takenori /* 132f85e8fc5SKATO Takenori * Re-lock vnode. 133d9e9650aSKonstantin Belousov * XXXKIB This is deadlock-prone as well. 134f85e8fc5SKATO Takenori */ 135ffa43617SKonstantin Belousov if (isvnunlocked) 136cb05b60aSAttilio Rao vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY); 137f85e8fc5SKATO Takenori 1383a773ad0SPoul-Henning Kamp if (error) 139df8bae1dSRodney W. Grimes return (error); 140bb92cd7bSMateusz Guzik NDFREE_PNBUF(ndp); 141df8bae1dSRodney W. Grimes 142df8bae1dSRodney W. Grimes /* 143df8bae1dSRodney W. Grimes * Sanity check on lower vnode 144df8bae1dSRodney W. Grimes */ 145df8bae1dSRodney W. Grimes lowerrootvp = ndp->ni_vp; 146df8bae1dSRodney W. Grimes 147f85e8fc5SKATO Takenori /* 148f85e8fc5SKATO Takenori * Check multi null mount to avoid `lock against myself' panic. 149f85e8fc5SKATO Takenori */ 15025728e84SKonstantin Belousov if (mp->mnt_vnodecovered->v_op == &null_vnodeops) { 15125728e84SKonstantin Belousov nn = VTONULL(mp->mnt_vnodecovered); 15225728e84SKonstantin Belousov if (nn == NULL || lowerrootvp == nn->null_lowervp) { 1538da80660SBoris Popov NULLFSDEBUG("nullfs_mount: multi null mount?\n"); 1546716c905SBoris Popov vput(lowerrootvp); 155747e9157SKATO Takenori return (EDEADLK); 156f85e8fc5SKATO Takenori } 15725728e84SKonstantin Belousov } 158f85e8fc5SKATO Takenori 159*521fbb72SDoug Rabson /* 160*521fbb72SDoug Rabson * Lower vnode must be the same type as the covered vnode - we 161*521fbb72SDoug Rabson * don't allow mounting directories to files or vice versa. 162*521fbb72SDoug Rabson */ 163*521fbb72SDoug Rabson if ((lowerrootvp->v_type != VDIR && lowerrootvp->v_type != VREG) || 164*521fbb72SDoug Rabson lowerrootvp->v_type != mp->mnt_vnodecovered->v_type) { 165*521fbb72SDoug Rabson NULLFSDEBUG("nullfs_mount: target must be same type as fspath"); 166*521fbb72SDoug Rabson vput(lowerrootvp); 167*521fbb72SDoug Rabson return (EINVAL); 168*521fbb72SDoug Rabson } 169*521fbb72SDoug Rabson 170df8bae1dSRodney W. Grimes xmp = (struct null_mount *) malloc(sizeof(struct null_mount), 1719cf4c952SKonstantin Belousov M_NULLFSMNT, M_WAITOK | M_ZERO); 172df8bae1dSRodney W. Grimes 173df8bae1dSRodney W. Grimes /* 1745fc9e11cSKonstantin Belousov * Save pointer to underlying FS and the reference to the 1755fc9e11cSKonstantin Belousov * lower root vnode. 176df8bae1dSRodney W. Grimes */ 177c746ed72SJason A. Harmening xmp->nullm_vfs = vfs_register_upper_from_vp(lowerrootvp, mp, 178c746ed72SJason A. Harmening &xmp->upper_node); 17959409cb9SJason A. Harmening if (xmp->nullm_vfs == NULL) { 18059409cb9SJason A. Harmening vput(lowerrootvp); 18159409cb9SJason A. Harmening free(xmp, M_NULLFSMNT); 18259409cb9SJason A. Harmening return (ENOENT); 18359409cb9SJason A. Harmening } 1845fc9e11cSKonstantin Belousov vref(lowerrootvp); 1855fc9e11cSKonstantin Belousov xmp->nullm_lowerrootvp = lowerrootvp; 1865fc9e11cSKonstantin Belousov mp->mnt_data = xmp; 187df8bae1dSRodney W. Grimes 188df8bae1dSRodney W. Grimes /* 1895fc9e11cSKonstantin Belousov * Make sure the node alias worked. 190df8bae1dSRodney W. Grimes */ 1915fc9e11cSKonstantin Belousov error = null_nodeget(mp, lowerrootvp, &nullm_rootvp); 1925fc9e11cSKonstantin Belousov if (error != 0) { 193c746ed72SJason A. Harmening vfs_unregister_upper(xmp->nullm_vfs, &xmp->upper_node); 1945fc9e11cSKonstantin Belousov vrele(lowerrootvp); 195dd0f9532SKonstantin Belousov free(xmp, M_NULLFSMNT); 196df8bae1dSRodney W. Grimes return (error); 197df8bae1dSRodney W. Grimes } 198df8bae1dSRodney W. Grimes 1995da56ddbSTor Egge if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) { 2005da56ddbSTor Egge MNT_ILOCK(mp); 201df8bae1dSRodney W. Grimes mp->mnt_flag |= MNT_LOCAL; 2025da56ddbSTor Egge MNT_IUNLOCK(mp); 2035da56ddbSTor Egge } 2049cf4c952SKonstantin Belousov 2059cf4c952SKonstantin Belousov xmp->nullm_flags |= NULLM_CACHE; 206abc15156SKonstantin Belousov if (vfs_getopt(mp->mnt_optnew, "nocache", NULL, NULL) == 0 || 207abc15156SKonstantin Belousov (xmp->nullm_vfs->mnt_kern_flag & MNTK_NULL_NOCACHE) != 0) 2089cf4c952SKonstantin Belousov xmp->nullm_flags &= ~NULLM_CACHE; 2099cf4c952SKonstantin Belousov 210c746ed72SJason A. Harmening if ((xmp->nullm_flags & NULLM_CACHE) != 0) { 211c746ed72SJason A. Harmening vfs_register_for_notification(xmp->nullm_vfs, mp, 212c746ed72SJason A. Harmening &xmp->notify_node); 213c746ed72SJason A. Harmening } 214c746ed72SJason A. Harmening 2150ef861e6SJason A. Harmening if (lowerrootvp == mp->mnt_vnodecovered) { 2160ef861e6SJason A. Harmening vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY | LK_CANRECURSE); 2170ef861e6SJason A. Harmening lowerrootvp->v_vflag |= VV_CROSSLOCK; 2180ef861e6SJason A. Harmening VOP_UNLOCK(lowerrootvp); 2190ef861e6SJason A. Harmening } 2200ef861e6SJason A. Harmening 2215da56ddbSTor Egge MNT_ILOCK(mp); 2229cf4c952SKonstantin Belousov if ((xmp->nullm_flags & NULLM_CACHE) != 0) { 22337a1046eSKonstantin Belousov mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag & 2249cf4c952SKonstantin Belousov (MNTK_SHARED_WRITES | MNTK_LOOKUP_SHARED | 2259cf4c952SKonstantin Belousov MNTK_EXTENDED_SHARED); 2269cf4c952SKonstantin Belousov } 227aeabf8d4SMateusz Guzik mp->mnt_kern_flag |= MNTK_NOMSYNC | MNTK_UNLOCKED_INSMNTQUE; 2284fce16e4SMateusz Guzik mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag & 229f36aa2b7SKonstantin Belousov (MNTK_USES_BCACHE | MNTK_NO_IOPF | MNTK_UNMAPPED_BUFS); 2305da56ddbSTor Egge MNT_IUNLOCK(mp); 231996c772fSJohn Dyson vfs_getnewfsid(mp); 2327ab8c8c0SPoul-Henning Kamp vfs_mountedfrom(mp, target); 2335fc9e11cSKonstantin Belousov vput(nullm_rootvp); 2347ab8c8c0SPoul-Henning Kamp 2358da80660SBoris Popov NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", 236df8bae1dSRodney W. Grimes mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 237df8bae1dSRodney W. Grimes return (0); 238df8bae1dSRodney W. Grimes } 239df8bae1dSRodney W. Grimes 240df8bae1dSRodney W. Grimes /* 241df8bae1dSRodney W. Grimes * Free reference to null layer 242df8bae1dSRodney W. Grimes */ 243d4b7a369SPoul-Henning Kamp static int 244dfd233edSAttilio Rao nullfs_unmount(mp, mntflags) 245df8bae1dSRodney W. Grimes struct mount *mp; 246df8bae1dSRodney W. Grimes int mntflags; 247df8bae1dSRodney W. Grimes { 248d9e9650aSKonstantin Belousov struct null_mount *mntdata; 2495fc9e11cSKonstantin Belousov int error, flags; 250df8bae1dSRodney W. Grimes 2518da80660SBoris Popov NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp); 252df8bae1dSRodney W. Grimes 253996c772fSJohn Dyson if (mntflags & MNT_FORCE) 254d9e9650aSKonstantin Belousov flags = FORCECLOSE; 255d9e9650aSKonstantin Belousov else 256d9e9650aSKonstantin Belousov flags = 0; 257df8bae1dSRodney W. Grimes 2585fc9e11cSKonstantin Belousov for (;;) { 2590864ef1eSIan Dowse /* There is 1 extra root vnode reference (nullm_rootvp). */ 2605fc9e11cSKonstantin Belousov error = vflush(mp, 0, flags, curthread); 2613a773ad0SPoul-Henning Kamp if (error) 262df8bae1dSRodney W. Grimes return (error); 2637ae3486eSKonstantin Belousov MNT_ILOCK(mp); 2647ae3486eSKonstantin Belousov if (mp->mnt_nvnodelistsize == 0) { 2657ae3486eSKonstantin Belousov MNT_IUNLOCK(mp); 2667ae3486eSKonstantin Belousov break; 2677ae3486eSKonstantin Belousov } 2687ae3486eSKonstantin Belousov MNT_IUNLOCK(mp); 2697ae3486eSKonstantin Belousov if ((mntflags & MNT_FORCE) == 0) 2707ae3486eSKonstantin Belousov return (EBUSY); 2717ae3486eSKonstantin Belousov } 272df8bae1dSRodney W. Grimes 273df8bae1dSRodney W. Grimes /* 274df8bae1dSRodney W. Grimes * Finally, throw away the null_mount structure 275df8bae1dSRodney W. Grimes */ 2768da80660SBoris Popov mntdata = mp->mnt_data; 2779cf4c952SKonstantin Belousov if ((mntdata->nullm_flags & NULLM_CACHE) != 0) { 278c746ed72SJason A. Harmening vfs_unregister_for_notification(mntdata->nullm_vfs, 279c746ed72SJason A. Harmening &mntdata->notify_node); 280d9e9650aSKonstantin Belousov } 2810ef861e6SJason A. Harmening if (mntdata->nullm_lowerrootvp == mp->mnt_vnodecovered) { 2820ef861e6SJason A. Harmening vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY | LK_CANRECURSE); 2830ef861e6SJason A. Harmening mp->mnt_vnodecovered->v_vflag &= ~VV_CROSSLOCK; 2840ef861e6SJason A. Harmening VOP_UNLOCK(mp->mnt_vnodecovered); 2850ef861e6SJason A. Harmening } 286c746ed72SJason A. Harmening vfs_unregister_upper(mntdata->nullm_vfs, &mntdata->upper_node); 2875fc9e11cSKonstantin Belousov vrele(mntdata->nullm_lowerrootvp); 28811753bd0SKevin Lo mp->mnt_data = NULL; 2894451405fSBoris Popov free(mntdata, M_NULLFSMNT); 290d9e9650aSKonstantin Belousov return (0); 291df8bae1dSRodney W. Grimes } 292df8bae1dSRodney W. Grimes 293d4b7a369SPoul-Henning Kamp static int 294dfd233edSAttilio Rao nullfs_root(mp, flags, vpp) 295df8bae1dSRodney W. Grimes struct mount *mp; 296d9b2d9f7SJeff Roberson int flags; 297df8bae1dSRodney W. Grimes struct vnode **vpp; 298df8bae1dSRodney W. Grimes { 299df8bae1dSRodney W. Grimes struct vnode *vp; 3005fc9e11cSKonstantin Belousov struct null_mount *mntdata; 3015fc9e11cSKonstantin Belousov int error; 302df8bae1dSRodney W. Grimes 3035fc9e11cSKonstantin Belousov mntdata = MOUNTTONULLMOUNT(mp); 3045fc9e11cSKonstantin Belousov NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", mp, 3055fc9e11cSKonstantin Belousov mntdata->nullm_lowerrootvp); 306df8bae1dSRodney W. Grimes 307a92a971bSMateusz Guzik error = vget(mntdata->nullm_lowerrootvp, flags); 3085fc9e11cSKonstantin Belousov if (error == 0) { 3095fc9e11cSKonstantin Belousov error = null_nodeget(mp, mntdata->nullm_lowerrootvp, &vp); 3105fc9e11cSKonstantin Belousov if (error == 0) { 311df8bae1dSRodney W. Grimes *vpp = vp; 3125fc9e11cSKonstantin Belousov } 3135fc9e11cSKonstantin Belousov } 3145fc9e11cSKonstantin Belousov return (error); 315df8bae1dSRodney W. Grimes } 316df8bae1dSRodney W. Grimes 317d4b7a369SPoul-Henning Kamp static int 318a4b07a27SJason A. Harmening nullfs_quotactl(mp, cmd, uid, arg, mp_busy) 319df8bae1dSRodney W. Grimes struct mount *mp; 320df8bae1dSRodney W. Grimes int cmd; 321df8bae1dSRodney W. Grimes uid_t uid; 3220430a5e2SDag-Erling Smørgrav void *arg; 323a4b07a27SJason A. Harmening bool *mp_busy; 324df8bae1dSRodney W. Grimes { 325a4b07a27SJason A. Harmening struct mount *lowermp; 326a4b07a27SJason A. Harmening struct null_mount *mntdata; 327a4b07a27SJason A. Harmening int error; 328a4b07a27SJason A. Harmening bool unbusy; 329a4b07a27SJason A. Harmening 330a4b07a27SJason A. Harmening mntdata = MOUNTTONULLMOUNT(mp); 331a4b07a27SJason A. Harmening lowermp = atomic_load_ptr(&mntdata->nullm_vfs); 332a4b07a27SJason A. Harmening KASSERT(*mp_busy == true, ("upper mount not busy")); 333a4b07a27SJason A. Harmening /* 334a4b07a27SJason A. Harmening * See comment in sys_quotactl() for an explanation of why the 335a4b07a27SJason A. Harmening * lower mount needs to be busied by the caller of VFS_QUOTACTL() 336a4b07a27SJason A. Harmening * but may be unbusied by the implementation. We must unbusy 337a4b07a27SJason A. Harmening * the upper mount for the same reason; otherwise a namei lookup 338a4b07a27SJason A. Harmening * issued by the VFS_QUOTACTL() implementation could traverse the 339a4b07a27SJason A. Harmening * upper mount and deadlock. 340a4b07a27SJason A. Harmening */ 341a4b07a27SJason A. Harmening vfs_unbusy(mp); 342a4b07a27SJason A. Harmening *mp_busy = false; 343a4b07a27SJason A. Harmening unbusy = true; 344a4b07a27SJason A. Harmening error = vfs_busy(lowermp, 0); 345a4b07a27SJason A. Harmening if (error == 0) 346a4b07a27SJason A. Harmening error = VFS_QUOTACTL(lowermp, cmd, uid, arg, &unbusy); 347a4b07a27SJason A. Harmening if (unbusy) 348a4b07a27SJason A. Harmening vfs_unbusy(lowermp); 349a4b07a27SJason A. Harmening 350a4b07a27SJason A. Harmening return (error); 351df8bae1dSRodney W. Grimes } 352df8bae1dSRodney W. Grimes 353d4b7a369SPoul-Henning Kamp static int 354dfd233edSAttilio Rao nullfs_statfs(mp, sbp) 355df8bae1dSRodney W. Grimes struct mount *mp; 356df8bae1dSRodney W. Grimes struct statfs *sbp; 357df8bae1dSRodney W. Grimes { 358df8bae1dSRodney W. Grimes int error; 3592f304845SKonstantin Belousov struct statfs *mstat; 360df8bae1dSRodney W. Grimes 3618da80660SBoris Popov NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp, 36289785a16SBruce Evans (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp, 36389785a16SBruce Evans (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)); 364df8bae1dSRodney W. Grimes 3652f304845SKonstantin Belousov mstat = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK | M_ZERO); 366df8bae1dSRodney W. Grimes 3672f304845SKonstantin Belousov error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, mstat); 3682f304845SKonstantin Belousov if (error) { 3692f304845SKonstantin Belousov free(mstat, M_STATFS); 370df8bae1dSRodney W. Grimes return (error); 3712f304845SKonstantin Belousov } 372df8bae1dSRodney W. Grimes 373df8bae1dSRodney W. Grimes /* now copy across the "interesting" information and fake the rest */ 3742f304845SKonstantin Belousov sbp->f_type = mstat->f_type; 3756d6a91c5SJilles Tjoelker sbp->f_flags = (sbp->f_flags & (MNT_RDONLY | MNT_NOEXEC | MNT_NOSUID | 376ed5cdcb6SEdward Tomasz Napierala MNT_UNION | MNT_NOSYMFOLLOW | MNT_AUTOMOUNTED)) | 377ed5cdcb6SEdward Tomasz Napierala (mstat->f_flags & ~(MNT_ROOTFS | MNT_AUTOMOUNTED)); 3782f304845SKonstantin Belousov sbp->f_bsize = mstat->f_bsize; 3792f304845SKonstantin Belousov sbp->f_iosize = mstat->f_iosize; 3802f304845SKonstantin Belousov sbp->f_blocks = mstat->f_blocks; 3812f304845SKonstantin Belousov sbp->f_bfree = mstat->f_bfree; 3822f304845SKonstantin Belousov sbp->f_bavail = mstat->f_bavail; 3832f304845SKonstantin Belousov sbp->f_files = mstat->f_files; 3842f304845SKonstantin Belousov sbp->f_ffree = mstat->f_ffree; 3852f304845SKonstantin Belousov 3862f304845SKonstantin Belousov free(mstat, M_STATFS); 387df8bae1dSRodney W. Grimes return (0); 388df8bae1dSRodney W. Grimes } 389df8bae1dSRodney W. Grimes 390d4b7a369SPoul-Henning Kamp static int 391dfd233edSAttilio Rao nullfs_sync(mp, waitfor) 392df8bae1dSRodney W. Grimes struct mount *mp; 393df8bae1dSRodney W. Grimes int waitfor; 394df8bae1dSRodney W. Grimes { 395df8bae1dSRodney W. Grimes /* 396df8bae1dSRodney W. Grimes * XXX - Assumes no data cached at null layer. 397df8bae1dSRodney W. Grimes */ 398df8bae1dSRodney W. Grimes return (0); 399df8bae1dSRodney W. Grimes } 400df8bae1dSRodney W. Grimes 401d4b7a369SPoul-Henning Kamp static int 402a0595d02SKirk McKusick nullfs_vget(mp, ino, flags, vpp) 403df8bae1dSRodney W. Grimes struct mount *mp; 404df8bae1dSRodney W. Grimes ino_t ino; 405a0595d02SKirk McKusick int flags; 406df8bae1dSRodney W. Grimes struct vnode **vpp; 407df8bae1dSRodney W. Grimes { 4084451405fSBoris Popov int error; 409e4e1d9f3SKonstantin Belousov 410e4e1d9f3SKonstantin Belousov KASSERT((flags & LK_TYPE_MASK) != 0, 411e4e1d9f3SKonstantin Belousov ("nullfs_vget: no lock requested")); 412e4e1d9f3SKonstantin Belousov 413a0595d02SKirk McKusick error = VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, flags, vpp); 414d9e9650aSKonstantin Belousov if (error != 0) 4154451405fSBoris Popov return (error); 4161cfdefbbSSemen Ustimenko return (null_nodeget(mp, *vpp, vpp)); 417df8bae1dSRodney W. Grimes } 418df8bae1dSRodney W. Grimes 419d4b7a369SPoul-Henning Kamp static int 420694a586aSRick Macklem nullfs_fhtovp(mp, fidp, flags, vpp) 421df8bae1dSRodney W. Grimes struct mount *mp; 422df8bae1dSRodney W. Grimes struct fid *fidp; 423694a586aSRick Macklem int flags; 424df8bae1dSRodney W. Grimes struct vnode **vpp; 425c24fda81SAlfred Perlstein { 4264451405fSBoris Popov int error; 427c24fda81SAlfred Perlstein 428d9e9650aSKonstantin Belousov error = VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, flags, 429d9e9650aSKonstantin Belousov vpp); 430d9e9650aSKonstantin Belousov if (error != 0) 431d9e9650aSKonstantin Belousov return (error); 4321cfdefbbSSemen Ustimenko return (null_nodeget(mp, *vpp, vpp)); 433c24fda81SAlfred Perlstein } 434c24fda81SAlfred Perlstein 435c24fda81SAlfred Perlstein static int 436dfd233edSAttilio Rao nullfs_extattrctl(mp, cmd, filename_vp, namespace, attrname) 43791f37dcbSRobert Watson struct mount *mp; 43891f37dcbSRobert Watson int cmd; 43970f36851SRobert Watson struct vnode *filename_vp; 44070f36851SRobert Watson int namespace; 4418f073875SRobert Watson const char *attrname; 44291f37dcbSRobert Watson { 443d9e9650aSKonstantin Belousov 444d9e9650aSKonstantin Belousov return (VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, 445d9e9650aSKonstantin Belousov filename_vp, namespace, attrname)); 44691f37dcbSRobert Watson } 44791f37dcbSRobert Watson 448d9e9650aSKonstantin Belousov static void 449d9e9650aSKonstantin Belousov nullfs_reclaim_lowervp(struct mount *mp, struct vnode *lowervp) 450d9e9650aSKonstantin Belousov { 451d9e9650aSKonstantin Belousov struct vnode *vp; 452d9e9650aSKonstantin Belousov 453d9e9650aSKonstantin Belousov vp = null_hashget(mp, lowervp); 454d9e9650aSKonstantin Belousov if (vp == NULL) 455d9e9650aSKonstantin Belousov return; 4560fc6daa7SKonstantin Belousov VTONULL(vp)->null_flags |= NULLV_NOUNLOCK; 457d9e9650aSKonstantin Belousov vgone(vp); 4580fc6daa7SKonstantin Belousov vput(vp); 4590fc6daa7SKonstantin Belousov } 4600fc6daa7SKonstantin Belousov 4610fc6daa7SKonstantin Belousov static void 4620fc6daa7SKonstantin Belousov nullfs_unlink_lowervp(struct mount *mp, struct vnode *lowervp) 4630fc6daa7SKonstantin Belousov { 4640fc6daa7SKonstantin Belousov struct vnode *vp; 4650fc6daa7SKonstantin Belousov struct null_node *xp; 4660fc6daa7SKonstantin Belousov 4670fc6daa7SKonstantin Belousov vp = null_hashget(mp, lowervp); 4680fc6daa7SKonstantin Belousov if (vp == NULL) 4690fc6daa7SKonstantin Belousov return; 4700fc6daa7SKonstantin Belousov xp = VTONULL(vp); 4710fc6daa7SKonstantin Belousov xp->null_flags |= NULLV_DROP | NULLV_NOUNLOCK; 4720fc6daa7SKonstantin Belousov vhold(vp); 4730fc6daa7SKonstantin Belousov vunref(vp); 4740fc6daa7SKonstantin Belousov 4750fc6daa7SKonstantin Belousov if (vp->v_usecount == 0) { 47674c7ff1aSKonstantin Belousov /* 47774c7ff1aSKonstantin Belousov * If vunref() dropped the last use reference on the 47874c7ff1aSKonstantin Belousov * nullfs vnode, it must be reclaimed, and its lock 47974c7ff1aSKonstantin Belousov * was split from the lower vnode lock. Need to do 48074c7ff1aSKonstantin Belousov * extra unlock before allowing the final vdrop() to 48174c7ff1aSKonstantin Belousov * free the vnode. 48274c7ff1aSKonstantin Belousov */ 483abd80ddbSMateusz Guzik KASSERT(VN_IS_DOOMED(vp), 48474c7ff1aSKonstantin Belousov ("not reclaimed nullfs vnode %p", vp)); 485b249ce48SMateusz Guzik VOP_UNLOCK(vp); 48674c7ff1aSKonstantin Belousov } else { 48774c7ff1aSKonstantin Belousov /* 48874c7ff1aSKonstantin Belousov * Otherwise, the nullfs vnode still shares the lock 48974c7ff1aSKonstantin Belousov * with the lower vnode, and must not be unlocked. 49074c7ff1aSKonstantin Belousov * Also clear the NULLV_NOUNLOCK, the flag is not 49174c7ff1aSKonstantin Belousov * relevant for future reclamations. 49274c7ff1aSKonstantin Belousov */ 49374c7ff1aSKonstantin Belousov ASSERT_VOP_ELOCKED(vp, "unlink_lowervp"); 494abd80ddbSMateusz Guzik KASSERT(!VN_IS_DOOMED(vp), 49574c7ff1aSKonstantin Belousov ("reclaimed nullfs vnode %p", vp)); 49674c7ff1aSKonstantin Belousov xp->null_flags &= ~NULLV_NOUNLOCK; 4970fc6daa7SKonstantin Belousov } 4980fc6daa7SKonstantin Belousov vdrop(vp); 499d9e9650aSKonstantin Belousov } 50091f37dcbSRobert Watson 501d4b7a369SPoul-Henning Kamp static struct vfsops null_vfsops = { 5027652131bSPoul-Henning Kamp .vfs_extattrctl = nullfs_extattrctl, 5037652131bSPoul-Henning Kamp .vfs_fhtovp = nullfs_fhtovp, 5047652131bSPoul-Henning Kamp .vfs_init = nullfs_init, 5055e8c582aSPoul-Henning Kamp .vfs_mount = nullfs_mount, 5067652131bSPoul-Henning Kamp .vfs_quotactl = nullfs_quotactl, 5077652131bSPoul-Henning Kamp .vfs_root = nullfs_root, 5087652131bSPoul-Henning Kamp .vfs_statfs = nullfs_statfs, 5097652131bSPoul-Henning Kamp .vfs_sync = nullfs_sync, 5107652131bSPoul-Henning Kamp .vfs_uninit = nullfs_uninit, 5117652131bSPoul-Henning Kamp .vfs_unmount = nullfs_unmount, 5127652131bSPoul-Henning Kamp .vfs_vget = nullfs_vget, 513d9e9650aSKonstantin Belousov .vfs_reclaim_lowervp = nullfs_reclaim_lowervp, 5140fc6daa7SKonstantin Belousov .vfs_unlink_lowervp = nullfs_unlink_lowervp, 515df8bae1dSRodney W. Grimes }; 516c901836cSGarrett Wollman 517*521fbb72SDoug Rabson VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL | VFCF_FILEMOUNT); 518