1139776Simp/*- 222521Sdyson * Copyright (c) 1992, 1993, 1995 31541Srgrimes * The Regents of the University of California. All rights reserved. 41541Srgrimes * 51541Srgrimes * This code is derived from software donated to Berkeley by 61541Srgrimes * Jan-Simon Pendry. 71541Srgrimes * 81541Srgrimes * Redistribution and use in source and binary forms, with or without 91541Srgrimes * modification, are permitted provided that the following conditions 101541Srgrimes * are met: 111541Srgrimes * 1. Redistributions of source code must retain the above copyright 121541Srgrimes * notice, this list of conditions and the following disclaimer. 131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141541Srgrimes * notice, this list of conditions and the following disclaimer in the 151541Srgrimes * documentation and/or other materials provided with the distribution. 161541Srgrimes * 4. Neither the name of the University nor the names of its contributors 171541Srgrimes * may be used to endorse or promote products derived from this software 181541Srgrimes * without specific prior written permission. 191541Srgrimes * 201541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301541Srgrimes * SUCH DAMAGE. 311541Srgrimes * 321541Srgrimes * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94 331541Srgrimes * 341541Srgrimes * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92 3550477Speter * $FreeBSD: stable/11/sys/fs/nullfs/null_vfsops.c 357508 2020-02-04 17:17:40Z kib $ 361541Srgrimes */ 371541Srgrimes 381541Srgrimes/* 391541Srgrimes * Null Layer 401541Srgrimes * (See null_vnops.c for a description of what this does.) 411541Srgrimes */ 421541Srgrimes 431541Srgrimes#include <sys/param.h> 441541Srgrimes#include <sys/systm.h> 45177785Skib#include <sys/fcntl.h> 4622605Smpp#include <sys/kernel.h> 4776166Smarkm#include <sys/lock.h> 4830354Sphk#include <sys/malloc.h> 491541Srgrimes#include <sys/mount.h> 501541Srgrimes#include <sys/namei.h> 5176166Smarkm#include <sys/proc.h> 5276166Smarkm#include <sys/vnode.h> 53232059Smm#include <sys/jail.h> 5476166Smarkm 5577031Sru#include <fs/nullfs/null.h> 561541Srgrimes 57151897Srwatsonstatic MALLOC_DEFINE(M_NULLFSMNT, "nullfs_mount", "NULLFS mount structure"); 5830354Sphk 59116271Sphkstatic vfs_fhtovp_t nullfs_fhtovp; 60132902Sphkstatic vfs_mount_t nullfs_mount; 61116271Sphkstatic vfs_quotactl_t nullfs_quotactl; 62116271Sphkstatic vfs_root_t nullfs_root; 63116271Sphkstatic vfs_sync_t nullfs_sync; 64116271Sphkstatic vfs_statfs_t nullfs_statfs; 65116271Sphkstatic vfs_unmount_t nullfs_unmount; 66116271Sphkstatic vfs_vget_t nullfs_vget; 67116271Sphkstatic vfs_extattrctl_t nullfs_extattrctl; 6812595Sbde 691541Srgrimes/* 701541Srgrimes * Mount null layer 711541Srgrimes */ 7212769Sphkstatic int 73191990Sattilionullfs_mount(struct mount *mp) 741541Srgrimes{ 75357508Skib struct vnode *lowerrootvp; 761541Srgrimes struct vnode *nullm_rootvp; 771541Srgrimes struct null_mount *xmp; 78232059Smm struct thread *td = curthread; 79344155Skib struct null_node *nn; 80344155Skib struct nameidata nd, *ndp; 8197186Smux char *target; 82344155Skib int error, len; 83344155Skib bool isvnunlocked; 841541Srgrimes 8565467Sbp NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp); 861541Srgrimes 87232059Smm if (!prison_allow(td->td_ucred, PR_ALLOW_MOUNT_NULLFS)) 88232059Smm return (EPERM); 89137479Sphk if (mp->mnt_flag & MNT_ROOTFS) 90137479Sphk return (EOPNOTSUPP); 91245004Skib 921541Srgrimes /* 931541Srgrimes * Update is a no-op 941541Srgrimes */ 951541Srgrimes if (mp->mnt_flag & MNT_UPDATE) { 96159019Srodrigc /* 97159019Srodrigc * Only support update mounts for NFS export. 98159019Srodrigc */ 99159019Srodrigc if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) 100159019Srodrigc return (0); 101159019Srodrigc else 102159019Srodrigc return (EOPNOTSUPP); 1031541Srgrimes } 1041541Srgrimes 1051541Srgrimes /* 1061541Srgrimes * Get argument 1071541Srgrimes */ 10897186Smux error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len); 10997186Smux if (error || target[len - 1] != '\0') 11097186Smux return (EINVAL); 1111541Srgrimes 1121541Srgrimes /* 113226681Spho * Unlock lower node to avoid possible deadlock. 11424988Skato */ 115344155Skib if (mp->mnt_vnodecovered->v_op == &null_vnodeops && 116226681Spho VOP_ISLOCKED(mp->mnt_vnodecovered) == LK_EXCLUSIVE) { 117175294Sattilio VOP_UNLOCK(mp->mnt_vnodecovered, 0); 118344155Skib isvnunlocked = true; 119344155Skib } else { 120344155Skib isvnunlocked = false; 12124988Skato } 122344155Skib 12324988Skato /* 1241541Srgrimes * Find lower node 1251541Srgrimes */ 126344155Skib ndp = &nd; 127191990Sattilio NDINIT(ndp, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, target, curthread); 1283496Sphk error = namei(ndp); 129240285Skib 13024988Skato /* 13124988Skato * Re-lock vnode. 132240285Skib * XXXKIB This is deadlock-prone as well. 13324988Skato */ 134226686Skib if (isvnunlocked) 135175202Sattilio vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY); 13624988Skato 1373496Sphk if (error) 1381541Srgrimes return (error); 13954655Seivind NDFREE(ndp, NDF_ONLY_PNBUF); 1401541Srgrimes 1411541Srgrimes /* 1421541Srgrimes * Sanity check on lower vnode 1431541Srgrimes */ 1441541Srgrimes lowerrootvp = ndp->ni_vp; 1451541Srgrimes 14624988Skato /* 14724988Skato * Check multi null mount to avoid `lock against myself' panic. 14824988Skato */ 149344155Skib if (mp->mnt_vnodecovered->v_op == &null_vnodeops) { 150344155Skib nn = VTONULL(mp->mnt_vnodecovered); 151344155Skib if (nn == NULL || lowerrootvp == nn->null_lowervp) { 152344155Skib NULLFSDEBUG("nullfs_mount: multi null mount?\n"); 153344155Skib vput(lowerrootvp); 154344155Skib return (EDEADLK); 155344155Skib } 15624988Skato } 15724988Skato 1581541Srgrimes xmp = (struct null_mount *) malloc(sizeof(struct null_mount), 159245004Skib M_NULLFSMNT, M_WAITOK | M_ZERO); 1601541Srgrimes 1611541Srgrimes /* 162357508Skib * Save pointer to underlying FS and the reference to the 163357508Skib * lower root vnode. 1641541Srgrimes */ 1651541Srgrimes xmp->nullm_vfs = lowerrootvp->v_mount; 166357508Skib vref(lowerrootvp); 167357508Skib xmp->nullm_lowerrootvp = lowerrootvp; 168357508Skib mp->mnt_data = xmp; 1691541Srgrimes 1701541Srgrimes /* 171357508Skib * Make sure the node alias worked. 1721541Srgrimes */ 173357508Skib error = null_nodeget(mp, lowerrootvp, &nullm_rootvp); 174357508Skib if (error != 0) { 175357508Skib vrele(lowerrootvp); 176229431Skib free(xmp, M_NULLFSMNT); 1771541Srgrimes return (error); 1781541Srgrimes } 1791541Srgrimes 180162647Stegge if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) { 181162647Stegge MNT_ILOCK(mp); 1821541Srgrimes mp->mnt_flag |= MNT_LOCAL; 183162647Stegge MNT_IUNLOCK(mp); 184162647Stegge } 185245004Skib 186245004Skib xmp->nullm_flags |= NULLM_CACHE; 187309520Skib if (vfs_getopt(mp->mnt_optnew, "nocache", NULL, NULL) == 0 || 188309520Skib (xmp->nullm_vfs->mnt_kern_flag & MNTK_NULL_NOCACHE) != 0) 189245004Skib xmp->nullm_flags &= ~NULLM_CACHE; 190245004Skib 191162647Stegge MNT_ILOCK(mp); 192245004Skib if ((xmp->nullm_flags & NULLM_CACHE) != 0) { 193245004Skib mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag & 194245004Skib (MNTK_SHARED_WRITES | MNTK_LOOKUP_SHARED | 195245004Skib MNTK_EXTENDED_SHARED); 196245004Skib } 197240285Skib mp->mnt_kern_flag |= MNTK_LOOKUP_EXCL_DOTDOT; 198273336Smjg mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag & 199296388Skib (MNTK_USES_BCACHE | MNTK_NO_IOPF | MNTK_UNMAPPED_BUFS); 200162647Stegge MNT_IUNLOCK(mp); 20122521Sdyson vfs_getnewfsid(mp); 202245004Skib if ((xmp->nullm_flags & NULLM_CACHE) != 0) { 203245004Skib MNT_ILOCK(xmp->nullm_vfs); 204245004Skib TAILQ_INSERT_TAIL(&xmp->nullm_vfs->mnt_uppers, mp, 205245004Skib mnt_upper_link); 206245004Skib MNT_IUNLOCK(xmp->nullm_vfs); 207245004Skib } 2081541Srgrimes 209138483Sphk vfs_mountedfrom(mp, target); 210357508Skib vput(nullm_rootvp); 211138483Sphk 21265467Sbp NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", 2131541Srgrimes mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 2141541Srgrimes return (0); 2151541Srgrimes} 2161541Srgrimes 2171541Srgrimes/* 2181541Srgrimes * Free reference to null layer 2191541Srgrimes */ 22012769Sphkstatic int 221191990Sattilionullfs_unmount(mp, mntflags) 2221541Srgrimes struct mount *mp; 2231541Srgrimes int mntflags; 2241541Srgrimes{ 225240285Skib struct null_mount *mntdata; 226240285Skib struct mount *ump; 227357508Skib int error, flags; 2281541Srgrimes 22965467Sbp NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp); 2301541Srgrimes 23122521Sdyson if (mntflags & MNT_FORCE) 232240285Skib flags = FORCECLOSE; 233240285Skib else 234240285Skib flags = 0; 2351541Srgrimes 236357508Skib for (;;) { 237345642Skib /* There is 1 extra root vnode reference (nullm_rootvp). */ 238357508Skib error = vflush(mp, 0, flags, curthread); 239345642Skib if (error) 240345642Skib return (error); 241345642Skib MNT_ILOCK(mp); 242345642Skib if (mp->mnt_nvnodelistsize == 0) { 243345642Skib MNT_IUNLOCK(mp); 244345642Skib break; 245345642Skib } 246345642Skib MNT_IUNLOCK(mp); 247345642Skib if ((mntflags & MNT_FORCE) == 0) 248345642Skib return (EBUSY); 249345642Skib } 2501541Srgrimes 2511541Srgrimes /* 2521541Srgrimes * Finally, throw away the null_mount structure 2531541Srgrimes */ 25465467Sbp mntdata = mp->mnt_data; 255240285Skib ump = mntdata->nullm_vfs; 256245004Skib if ((mntdata->nullm_flags & NULLM_CACHE) != 0) { 257245004Skib MNT_ILOCK(ump); 258245004Skib while ((ump->mnt_kern_flag & MNTK_VGONE_UPPER) != 0) { 259245004Skib ump->mnt_kern_flag |= MNTK_VGONE_WAITER; 260245004Skib msleep(&ump->mnt_uppers, &ump->mnt_mtx, 0, "vgnupw", 0); 261245004Skib } 262245004Skib TAILQ_REMOVE(&ump->mnt_uppers, mp, mnt_upper_link); 263245004Skib MNT_IUNLOCK(ump); 264240285Skib } 265357508Skib vrele(mntdata->nullm_lowerrootvp); 266232918Skevlo mp->mnt_data = NULL; 26766356Sbp free(mntdata, M_NULLFSMNT); 268240285Skib return (0); 2691541Srgrimes} 2701541Srgrimes 27112769Sphkstatic int 272191990Sattilionullfs_root(mp, flags, vpp) 2731541Srgrimes struct mount *mp; 274144058Sjeff int flags; 2751541Srgrimes struct vnode **vpp; 2761541Srgrimes{ 2771541Srgrimes struct vnode *vp; 278357508Skib struct null_mount *mntdata; 279357508Skib int error; 2801541Srgrimes 281357508Skib mntdata = MOUNTTONULLMOUNT(mp); 282357508Skib NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", mp, 283357508Skib mntdata->nullm_lowerrootvp); 2841541Srgrimes 285357508Skib error = vget(mntdata->nullm_lowerrootvp, (flags & ~LK_TYPE_MASK) | 286357508Skib LK_EXCLUSIVE, curthread); 287357508Skib if (error == 0) { 288357508Skib error = null_nodeget(mp, mntdata->nullm_lowerrootvp, &vp); 289357508Skib if (error == 0) { 290357508Skib if ((flags & LK_TYPE_MASK) == LK_SHARED) 291357508Skib vn_lock(vp, LK_DOWNGRADE | LK_RETRY); 292357508Skib *vpp = vp; 293357508Skib } 294357508Skib } 295357508Skib return (error); 2961541Srgrimes} 2971541Srgrimes 29812769Sphkstatic int 299191990Sattilionullfs_quotactl(mp, cmd, uid, arg) 3001541Srgrimes struct mount *mp; 3011541Srgrimes int cmd; 3021541Srgrimes uid_t uid; 303153400Sdes void *arg; 3041541Srgrimes{ 305191990Sattilio return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg); 3061541Srgrimes} 3071541Srgrimes 30812769Sphkstatic int 309191990Sattilionullfs_statfs(mp, sbp) 3101541Srgrimes struct mount *mp; 3111541Srgrimes struct statfs *sbp; 3121541Srgrimes{ 3131541Srgrimes int error; 314311957Skib struct statfs *mstat; 3151541Srgrimes 31665467Sbp NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp, 31737977Sbde (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp, 31837977Sbde (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)); 3191541Srgrimes 320311957Skib mstat = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK | M_ZERO); 3211541Srgrimes 322311957Skib error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, mstat); 323311957Skib if (error) { 324311957Skib free(mstat, M_STATFS); 3251541Srgrimes return (error); 326311957Skib } 3271541Srgrimes 3281541Srgrimes /* now copy across the "interesting" information and fake the rest */ 329311957Skib sbp->f_type = mstat->f_type; 330247619Sjilles sbp->f_flags = (sbp->f_flags & (MNT_RDONLY | MNT_NOEXEC | MNT_NOSUID | 331332613Strasz MNT_UNION | MNT_NOSYMFOLLOW | MNT_AUTOMOUNTED)) | 332332613Strasz (mstat->f_flags & ~(MNT_ROOTFS | MNT_AUTOMOUNTED)); 333311957Skib sbp->f_bsize = mstat->f_bsize; 334311957Skib sbp->f_iosize = mstat->f_iosize; 335311957Skib sbp->f_blocks = mstat->f_blocks; 336311957Skib sbp->f_bfree = mstat->f_bfree; 337311957Skib sbp->f_bavail = mstat->f_bavail; 338311957Skib sbp->f_files = mstat->f_files; 339311957Skib sbp->f_ffree = mstat->f_ffree; 340311957Skib 341311957Skib free(mstat, M_STATFS); 3421541Srgrimes return (0); 3431541Srgrimes} 3441541Srgrimes 34512769Sphkstatic int 346191990Sattilionullfs_sync(mp, waitfor) 3471541Srgrimes struct mount *mp; 3481541Srgrimes int waitfor; 3491541Srgrimes{ 3501541Srgrimes /* 3511541Srgrimes * XXX - Assumes no data cached at null layer. 3521541Srgrimes */ 3531541Srgrimes return (0); 3541541Srgrimes} 3551541Srgrimes 35612769Sphkstatic int 35792462Smckusicknullfs_vget(mp, ino, flags, vpp) 3581541Srgrimes struct mount *mp; 3591541Srgrimes ino_t ino; 36092462Smckusick int flags; 3611541Srgrimes struct vnode **vpp; 3621541Srgrimes{ 36366356Sbp int error; 364232301Skib 365232301Skib KASSERT((flags & LK_TYPE_MASK) != 0, 366232301Skib ("nullfs_vget: no lock requested")); 367232301Skib 36892462Smckusick error = VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, flags, vpp); 369240285Skib if (error != 0) 37066356Sbp return (error); 37198183Ssemenu return (null_nodeget(mp, *vpp, vpp)); 3721541Srgrimes} 3731541Srgrimes 37412769Sphkstatic int 375222167Srmacklemnullfs_fhtovp(mp, fidp, flags, vpp) 3761541Srgrimes struct mount *mp; 3771541Srgrimes struct fid *fidp; 378222167Srmacklem int flags; 3791541Srgrimes struct vnode **vpp; 3801541Srgrimes{ 38166356Sbp int error; 382240285Skib 383240285Skib error = VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, flags, 384222167Srmacklem vpp); 385240285Skib if (error != 0) 38666356Sbp return (error); 38798183Ssemenu return (null_nodeget(mp, *vpp, vpp)); 3881541Srgrimes} 3891541Srgrimes 39054803Srwatsonstatic int 391191990Sattilionullfs_extattrctl(mp, cmd, filename_vp, namespace, attrname) 39254803Srwatson struct mount *mp; 39354803Srwatson int cmd; 39474273Srwatson struct vnode *filename_vp; 39574273Srwatson int namespace; 39656272Srwatson const char *attrname; 39754803Srwatson{ 398240285Skib 399240285Skib return (VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, 400240285Skib filename_vp, namespace, attrname)); 40154803Srwatson} 40254803Srwatson 403240285Skibstatic void 404240285Skibnullfs_reclaim_lowervp(struct mount *mp, struct vnode *lowervp) 405240285Skib{ 406240285Skib struct vnode *vp; 40754803Srwatson 408240285Skib vp = null_hashget(mp, lowervp); 409240285Skib if (vp == NULL) 410240285Skib return; 411250505Skib VTONULL(vp)->null_flags |= NULLV_NOUNLOCK; 412240285Skib vgone(vp); 413250505Skib vput(vp); 414240285Skib} 415240285Skib 416250505Skibstatic void 417250505Skibnullfs_unlink_lowervp(struct mount *mp, struct vnode *lowervp) 418250505Skib{ 419250505Skib struct vnode *vp; 420250505Skib struct null_node *xp; 421250505Skib 422250505Skib vp = null_hashget(mp, lowervp); 423250505Skib if (vp == NULL) 424250505Skib return; 425250505Skib xp = VTONULL(vp); 426250505Skib xp->null_flags |= NULLV_DROP | NULLV_NOUNLOCK; 427250505Skib vhold(vp); 428250505Skib vunref(vp); 429250505Skib 430250505Skib if (vp->v_usecount == 0) { 431250852Skib /* 432250852Skib * If vunref() dropped the last use reference on the 433250852Skib * nullfs vnode, it must be reclaimed, and its lock 434250852Skib * was split from the lower vnode lock. Need to do 435250852Skib * extra unlock before allowing the final vdrop() to 436250852Skib * free the vnode. 437250852Skib */ 438250505Skib KASSERT((vp->v_iflag & VI_DOOMED) != 0, 439250852Skib ("not reclaimed nullfs vnode %p", vp)); 440250505Skib VOP_UNLOCK(vp, 0); 441250852Skib } else { 442250852Skib /* 443250852Skib * Otherwise, the nullfs vnode still shares the lock 444250852Skib * with the lower vnode, and must not be unlocked. 445250852Skib * Also clear the NULLV_NOUNLOCK, the flag is not 446250852Skib * relevant for future reclamations. 447250852Skib */ 448250852Skib ASSERT_VOP_ELOCKED(vp, "unlink_lowervp"); 449250852Skib KASSERT((vp->v_iflag & VI_DOOMED) == 0, 450250852Skib ("reclaimed nullfs vnode %p", vp)); 451250852Skib xp->null_flags &= ~NULLV_NOUNLOCK; 452250505Skib } 453250505Skib vdrop(vp); 454250505Skib} 455250505Skib 45612769Sphkstatic struct vfsops null_vfsops = { 457116271Sphk .vfs_extattrctl = nullfs_extattrctl, 458116271Sphk .vfs_fhtovp = nullfs_fhtovp, 459116271Sphk .vfs_init = nullfs_init, 460132902Sphk .vfs_mount = nullfs_mount, 461116271Sphk .vfs_quotactl = nullfs_quotactl, 462116271Sphk .vfs_root = nullfs_root, 463116271Sphk .vfs_statfs = nullfs_statfs, 464116271Sphk .vfs_sync = nullfs_sync, 465116271Sphk .vfs_uninit = nullfs_uninit, 466116271Sphk .vfs_unmount = nullfs_unmount, 467116271Sphk .vfs_vget = nullfs_vget, 468240285Skib .vfs_reclaim_lowervp = nullfs_reclaim_lowervp, 469250505Skib .vfs_unlink_lowervp = nullfs_unlink_lowervp, 4701541Srgrimes}; 4712946Swollman 472231269SmmVFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL); 473