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$ 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{ 751541Srgrimes int error = 0; 761541Srgrimes struct vnode *lowerrootvp, *vp; 771541Srgrimes struct vnode *nullm_rootvp; 781541Srgrimes struct null_mount *xmp; 79232059Smm struct thread *td = curthread; 8097186Smux char *target; 8197186Smux int isvnunlocked = 0, len; 82132902Sphk struct nameidata nd, *ndp = &nd; 831541Srgrimes 8465467Sbp NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp); 851541Srgrimes 86232059Smm if (!prison_allow(td->td_ucred, PR_ALLOW_MOUNT_NULLFS)) 87232059Smm return (EPERM); 88137479Sphk if (mp->mnt_flag & MNT_ROOTFS) 89137479Sphk return (EOPNOTSUPP); 90245004Skib 911541Srgrimes /* 921541Srgrimes * Update is a no-op 931541Srgrimes */ 941541Srgrimes if (mp->mnt_flag & MNT_UPDATE) { 95159019Srodrigc /* 96159019Srodrigc * Only support update mounts for NFS export. 97159019Srodrigc */ 98159019Srodrigc if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) 99159019Srodrigc return (0); 100159019Srodrigc else 101159019Srodrigc return (EOPNOTSUPP); 1021541Srgrimes } 1031541Srgrimes 1041541Srgrimes /* 1051541Srgrimes * Get argument 1061541Srgrimes */ 10797186Smux error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len); 10897186Smux if (error || target[len - 1] != '\0') 10997186Smux return (EINVAL); 1101541Srgrimes 1111541Srgrimes /* 112226681Spho * Unlock lower node to avoid possible deadlock. 11324988Skato */ 114138290Sphk if ((mp->mnt_vnodecovered->v_op == &null_vnodeops) && 115226681Spho VOP_ISLOCKED(mp->mnt_vnodecovered) == LK_EXCLUSIVE) { 116175294Sattilio VOP_UNLOCK(mp->mnt_vnodecovered, 0); 11724988Skato isvnunlocked = 1; 11824988Skato } 11924988Skato /* 1201541Srgrimes * Find lower node 1211541Srgrimes */ 122191990Sattilio NDINIT(ndp, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, target, curthread); 1233496Sphk error = namei(ndp); 124240285Skib 12524988Skato /* 12624988Skato * Re-lock vnode. 127240285Skib * XXXKIB This is deadlock-prone as well. 12824988Skato */ 129226686Skib if (isvnunlocked) 130175202Sattilio vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY); 13124988Skato 1323496Sphk if (error) 1331541Srgrimes return (error); 13454655Seivind NDFREE(ndp, NDF_ONLY_PNBUF); 1351541Srgrimes 1361541Srgrimes /* 1371541Srgrimes * Sanity check on lower vnode 1381541Srgrimes */ 1391541Srgrimes lowerrootvp = ndp->ni_vp; 1401541Srgrimes 14124988Skato /* 14224988Skato * Check multi null mount to avoid `lock against myself' panic. 14324988Skato */ 14424988Skato if (lowerrootvp == VTONULL(mp->mnt_vnodecovered)->null_lowervp) { 14565467Sbp NULLFSDEBUG("nullfs_mount: multi null mount?\n"); 14667441Sbp vput(lowerrootvp); 14725016Skato return (EDEADLK); 14824988Skato } 14924988Skato 1501541Srgrimes xmp = (struct null_mount *) malloc(sizeof(struct null_mount), 151245004Skib M_NULLFSMNT, M_WAITOK | M_ZERO); 1521541Srgrimes 1531541Srgrimes /* 1541541Srgrimes * Save reference to underlying FS 1551541Srgrimes */ 1561541Srgrimes xmp->nullm_vfs = lowerrootvp->v_mount; 1571541Srgrimes 1581541Srgrimes /* 1591541Srgrimes * Save reference. Each mount also holds 1601541Srgrimes * a reference on the root vnode. 1611541Srgrimes */ 16298183Ssemenu error = null_nodeget(mp, lowerrootvp, &vp); 1631541Srgrimes /* 1641541Srgrimes * Make sure the node alias worked 1651541Srgrimes */ 1661541Srgrimes if (error) { 167229431Skib free(xmp, M_NULLFSMNT); 1681541Srgrimes return (error); 1691541Srgrimes } 1701541Srgrimes 1711541Srgrimes /* 1721541Srgrimes * Keep a held reference to the root vnode. 1731541Srgrimes * It is vrele'd in nullfs_unmount. 1741541Srgrimes */ 1751541Srgrimes nullm_rootvp = vp; 176101308Sjeff nullm_rootvp->v_vflag |= VV_ROOT; 1771541Srgrimes xmp->nullm_rootvp = nullm_rootvp; 178103934Sjeff 179103934Sjeff /* 180103934Sjeff * Unlock the node (either the lower or the alias) 181103934Sjeff */ 182175294Sattilio VOP_UNLOCK(vp, 0); 183103934Sjeff 184162647Stegge if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) { 185162647Stegge MNT_ILOCK(mp); 1861541Srgrimes mp->mnt_flag |= MNT_LOCAL; 187162647Stegge MNT_IUNLOCK(mp); 188162647Stegge } 189245004Skib 190245004Skib xmp->nullm_flags |= NULLM_CACHE; 191245004Skib if (vfs_getopt(mp->mnt_optnew, "nocache", NULL, NULL) == 0) 192245004Skib xmp->nullm_flags &= ~NULLM_CACHE; 193245004Skib 194162647Stegge MNT_ILOCK(mp); 195245004Skib if ((xmp->nullm_flags & NULLM_CACHE) != 0) { 196245004Skib mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag & 197245004Skib (MNTK_SHARED_WRITES | MNTK_LOOKUP_SHARED | 198245004Skib MNTK_EXTENDED_SHARED); 199245004Skib } 200240285Skib mp->mnt_kern_flag |= MNTK_LOOKUP_EXCL_DOTDOT; 201162647Stegge MNT_IUNLOCK(mp); 202232918Skevlo mp->mnt_data = xmp; 20322521Sdyson vfs_getnewfsid(mp); 204245004Skib if ((xmp->nullm_flags & NULLM_CACHE) != 0) { 205245004Skib MNT_ILOCK(xmp->nullm_vfs); 206245004Skib TAILQ_INSERT_TAIL(&xmp->nullm_vfs->mnt_uppers, mp, 207245004Skib mnt_upper_link); 208245004Skib MNT_IUNLOCK(xmp->nullm_vfs); 209245004Skib } 2101541Srgrimes 211138483Sphk vfs_mountedfrom(mp, target); 212138483Sphk 21365467Sbp NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", 2141541Srgrimes mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); 2151541Srgrimes return (0); 2161541Srgrimes} 2171541Srgrimes 2181541Srgrimes/* 2191541Srgrimes * Free reference to null layer 2201541Srgrimes */ 22112769Sphkstatic int 222191990Sattilionullfs_unmount(mp, mntflags) 2231541Srgrimes struct mount *mp; 2241541Srgrimes int mntflags; 2251541Srgrimes{ 226240285Skib struct null_mount *mntdata; 227240285Skib struct mount *ump; 228240285Skib int error, flags; 2291541Srgrimes 23065467Sbp NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp); 2311541Srgrimes 23222521Sdyson if (mntflags & MNT_FORCE) 233240285Skib flags = FORCECLOSE; 234240285Skib else 235240285Skib flags = 0; 2361541Srgrimes 23776688Siedowse /* There is 1 extra root vnode reference (nullm_rootvp). */ 238191990Sattilio error = vflush(mp, 1, flags, curthread); 23966356Sbp if (error) 24066356Sbp return (error); 2411541Srgrimes 2421541Srgrimes /* 2431541Srgrimes * Finally, throw away the null_mount structure 2441541Srgrimes */ 24565467Sbp mntdata = mp->mnt_data; 246240285Skib ump = mntdata->nullm_vfs; 247245004Skib if ((mntdata->nullm_flags & NULLM_CACHE) != 0) { 248245004Skib MNT_ILOCK(ump); 249245004Skib while ((ump->mnt_kern_flag & MNTK_VGONE_UPPER) != 0) { 250245004Skib ump->mnt_kern_flag |= MNTK_VGONE_WAITER; 251245004Skib msleep(&ump->mnt_uppers, &ump->mnt_mtx, 0, "vgnupw", 0); 252245004Skib } 253245004Skib TAILQ_REMOVE(&ump->mnt_uppers, mp, mnt_upper_link); 254245004Skib MNT_IUNLOCK(ump); 255240285Skib } 256232918Skevlo mp->mnt_data = NULL; 25766356Sbp free(mntdata, M_NULLFSMNT); 258240285Skib return (0); 2591541Srgrimes} 2601541Srgrimes 26112769Sphkstatic int 262191990Sattilionullfs_root(mp, flags, vpp) 2631541Srgrimes struct mount *mp; 264144058Sjeff int flags; 2651541Srgrimes struct vnode **vpp; 2661541Srgrimes{ 2671541Srgrimes struct vnode *vp; 2681541Srgrimes 26965467Sbp NULLFSDEBUG("nullfs_root(mp = %p, vp = %p->%p)\n", (void *)mp, 27037977Sbde (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp, 27137977Sbde (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)); 2721541Srgrimes 2731541Srgrimes /* 2741541Srgrimes * Return locked reference to root. 2751541Srgrimes */ 2761541Srgrimes vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; 2771541Srgrimes VREF(vp); 27866356Sbp 279226688Skib ASSERT_VOP_UNLOCKED(vp, "root vnode is locked"); 280175202Sattilio vn_lock(vp, flags | LK_RETRY); 2811541Srgrimes *vpp = vp; 2821541Srgrimes return 0; 2831541Srgrimes} 2841541Srgrimes 28512769Sphkstatic int 286191990Sattilionullfs_quotactl(mp, cmd, uid, arg) 2871541Srgrimes struct mount *mp; 2881541Srgrimes int cmd; 2891541Srgrimes uid_t uid; 290153400Sdes void *arg; 2911541Srgrimes{ 292191990Sattilio return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg); 2931541Srgrimes} 2941541Srgrimes 29512769Sphkstatic int 296191990Sattilionullfs_statfs(mp, sbp) 2971541Srgrimes struct mount *mp; 2981541Srgrimes struct statfs *sbp; 2991541Srgrimes{ 3001541Srgrimes int error; 3011541Srgrimes struct statfs mstat; 3021541Srgrimes 30365467Sbp NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp, 30437977Sbde (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp, 30537977Sbde (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)); 3061541Srgrimes 3071541Srgrimes bzero(&mstat, sizeof(mstat)); 3081541Srgrimes 309191990Sattilio error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat); 3101541Srgrimes if (error) 3111541Srgrimes return (error); 3121541Srgrimes 3131541Srgrimes /* now copy across the "interesting" information and fake the rest */ 3141541Srgrimes sbp->f_type = mstat.f_type; 315247619Sjilles sbp->f_flags = (sbp->f_flags & (MNT_RDONLY | MNT_NOEXEC | MNT_NOSUID | 316247619Sjilles MNT_UNION | MNT_NOSYMFOLLOW)) | (mstat.f_flags & ~MNT_ROOTFS); 3171541Srgrimes sbp->f_bsize = mstat.f_bsize; 3181541Srgrimes sbp->f_iosize = mstat.f_iosize; 3191541Srgrimes sbp->f_blocks = mstat.f_blocks; 3201541Srgrimes sbp->f_bfree = mstat.f_bfree; 3211541Srgrimes sbp->f_bavail = mstat.f_bavail; 3221541Srgrimes sbp->f_files = mstat.f_files; 3231541Srgrimes sbp->f_ffree = mstat.f_ffree; 3241541Srgrimes return (0); 3251541Srgrimes} 3261541Srgrimes 32712769Sphkstatic int 328191990Sattilionullfs_sync(mp, waitfor) 3291541Srgrimes struct mount *mp; 3301541Srgrimes int waitfor; 3311541Srgrimes{ 3321541Srgrimes /* 3331541Srgrimes * XXX - Assumes no data cached at null layer. 3341541Srgrimes */ 3351541Srgrimes return (0); 3361541Srgrimes} 3371541Srgrimes 33812769Sphkstatic int 33992462Smckusicknullfs_vget(mp, ino, flags, vpp) 3401541Srgrimes struct mount *mp; 3411541Srgrimes ino_t ino; 34292462Smckusick int flags; 3431541Srgrimes struct vnode **vpp; 3441541Srgrimes{ 34566356Sbp int error; 346232301Skib 347232301Skib KASSERT((flags & LK_TYPE_MASK) != 0, 348232301Skib ("nullfs_vget: no lock requested")); 349232301Skib 35092462Smckusick error = VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, flags, vpp); 351240285Skib if (error != 0) 35266356Sbp return (error); 35398183Ssemenu return (null_nodeget(mp, *vpp, vpp)); 3541541Srgrimes} 3551541Srgrimes 35612769Sphkstatic int 357222167Srmacklemnullfs_fhtovp(mp, fidp, flags, vpp) 3581541Srgrimes struct mount *mp; 3591541Srgrimes struct fid *fidp; 360222167Srmacklem int flags; 3611541Srgrimes struct vnode **vpp; 3621541Srgrimes{ 36366356Sbp int error; 364240285Skib 365240285Skib error = VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, flags, 366222167Srmacklem vpp); 367240285Skib if (error != 0) 36866356Sbp return (error); 36998183Ssemenu return (null_nodeget(mp, *vpp, vpp)); 3701541Srgrimes} 3711541Srgrimes 37254803Srwatsonstatic int 373191990Sattilionullfs_extattrctl(mp, cmd, filename_vp, namespace, attrname) 37454803Srwatson struct mount *mp; 37554803Srwatson int cmd; 37674273Srwatson struct vnode *filename_vp; 37774273Srwatson int namespace; 37856272Srwatson const char *attrname; 37954803Srwatson{ 380240285Skib 381240285Skib return (VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, 382240285Skib filename_vp, namespace, attrname)); 38354803Srwatson} 38454803Srwatson 385240285Skibstatic void 386240285Skibnullfs_reclaim_lowervp(struct mount *mp, struct vnode *lowervp) 387240285Skib{ 388240285Skib struct vnode *vp; 38954803Srwatson 390240285Skib vp = null_hashget(mp, lowervp); 391240285Skib if (vp == NULL) 392240285Skib return; 393250505Skib VTONULL(vp)->null_flags |= NULLV_NOUNLOCK; 394240285Skib vgone(vp); 395250505Skib vput(vp); 396240285Skib} 397240285Skib 398250505Skibstatic void 399250505Skibnullfs_unlink_lowervp(struct mount *mp, struct vnode *lowervp) 400250505Skib{ 401250505Skib struct vnode *vp; 402250505Skib struct null_node *xp; 403250505Skib 404250505Skib vp = null_hashget(mp, lowervp); 405250505Skib if (vp == NULL) 406250505Skib return; 407250505Skib xp = VTONULL(vp); 408250505Skib xp->null_flags |= NULLV_DROP | NULLV_NOUNLOCK; 409250505Skib vhold(vp); 410250505Skib vunref(vp); 411250505Skib 412250505Skib if (vp->v_usecount == 0) { 413250852Skib /* 414250852Skib * If vunref() dropped the last use reference on the 415250852Skib * nullfs vnode, it must be reclaimed, and its lock 416250852Skib * was split from the lower vnode lock. Need to do 417250852Skib * extra unlock before allowing the final vdrop() to 418250852Skib * free the vnode. 419250852Skib */ 420250505Skib KASSERT((vp->v_iflag & VI_DOOMED) != 0, 421250852Skib ("not reclaimed nullfs vnode %p", vp)); 422250505Skib VOP_UNLOCK(vp, 0); 423250852Skib } else { 424250852Skib /* 425250852Skib * Otherwise, the nullfs vnode still shares the lock 426250852Skib * with the lower vnode, and must not be unlocked. 427250852Skib * Also clear the NULLV_NOUNLOCK, the flag is not 428250852Skib * relevant for future reclamations. 429250852Skib */ 430250852Skib ASSERT_VOP_ELOCKED(vp, "unlink_lowervp"); 431250852Skib KASSERT((vp->v_iflag & VI_DOOMED) == 0, 432250852Skib ("reclaimed nullfs vnode %p", vp)); 433250852Skib xp->null_flags &= ~NULLV_NOUNLOCK; 434250505Skib } 435250505Skib vdrop(vp); 436250505Skib} 437250505Skib 43812769Sphkstatic struct vfsops null_vfsops = { 439116271Sphk .vfs_extattrctl = nullfs_extattrctl, 440116271Sphk .vfs_fhtovp = nullfs_fhtovp, 441116271Sphk .vfs_init = nullfs_init, 442132902Sphk .vfs_mount = nullfs_mount, 443116271Sphk .vfs_quotactl = nullfs_quotactl, 444116271Sphk .vfs_root = nullfs_root, 445116271Sphk .vfs_statfs = nullfs_statfs, 446116271Sphk .vfs_sync = nullfs_sync, 447116271Sphk .vfs_uninit = nullfs_uninit, 448116271Sphk .vfs_unmount = nullfs_unmount, 449116271Sphk .vfs_vget = nullfs_vget, 450240285Skib .vfs_reclaim_lowervp = nullfs_reclaim_lowervp, 451250505Skib .vfs_unlink_lowervp = nullfs_unlink_lowervp, 4521541Srgrimes}; 4532946Swollman 454231269SmmVFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL); 455