null_vfsops.c revision 240285
11830SN/A/*-
22224Sctornqvi * Copyright (c) 1992, 1993, 1995
31830SN/A *	The Regents of the University of California.  All rights reserved.
41830SN/A *
51830SN/A * This code is derived from software donated to Berkeley by
61830SN/A * Jan-Simon Pendry.
71830SN/A *
81830SN/A * Redistribution and use in source and binary forms, with or without
91830SN/A * modification, are permitted provided that the following conditions
101830SN/A * are met:
111830SN/A * 1. Redistributions of source code must retain the above copyright
121830SN/A *    notice, this list of conditions and the following disclaimer.
131830SN/A * 2. Redistributions in binary form must reproduce the above copyright
141830SN/A *    notice, this list of conditions and the following disclaimer in the
151830SN/A *    documentation and/or other materials provided with the distribution.
161830SN/A * 4. Neither the name of the University nor the names of its contributors
171830SN/A *    may be used to endorse or promote products derived from this software
181830SN/A *    without specific prior written permission.
191830SN/A *
201830SN/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211830SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221830SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231830SN/A * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241830SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251830SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261830SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271830SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281830SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291830SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301830SN/A * SUCH DAMAGE.
311830SN/A *
321830SN/A *	@(#)null_vfsops.c	8.2 (Berkeley) 1/21/94
331830SN/A *
341830SN/A * @(#)lofs_vfsops.c	1.2 (Berkeley) 6/18/92
351830SN/A * $FreeBSD: head/sys/fs/nullfs/null_vfsops.c 240285 2012-09-09 19:20:23Z kib $
361830SN/A */
371830SN/A
381830SN/A/*
391830SN/A * Null Layer
401830SN/A * (See null_vnops.c for a description of what this does.)
411830SN/A */
421830SN/A
431830SN/A#include <sys/param.h>
441830SN/A#include <sys/systm.h>
451830SN/A#include <sys/fcntl.h>
461830SN/A#include <sys/kernel.h>
471830SN/A#include <sys/lock.h>
481830SN/A#include <sys/malloc.h>
491830SN/A#include <sys/mount.h>
501830SN/A#include <sys/namei.h>
511830SN/A#include <sys/proc.h>
521830SN/A#include <sys/vnode.h>
531830SN/A#include <sys/jail.h>
541830SN/A
551830SN/A#include <fs/nullfs/null.h>
561830SN/A
571830SN/Astatic MALLOC_DEFINE(M_NULLFSMNT, "nullfs_mount", "NULLFS mount structure");
581830SN/A
591830SN/Astatic vfs_fhtovp_t	nullfs_fhtovp;
601830SN/Astatic vfs_mount_t	nullfs_mount;
611830SN/Astatic vfs_quotactl_t	nullfs_quotactl;
621830SN/Astatic vfs_root_t	nullfs_root;
631830SN/Astatic vfs_sync_t	nullfs_sync;
641830SN/Astatic vfs_statfs_t	nullfs_statfs;
651830SN/Astatic vfs_unmount_t	nullfs_unmount;
661830SN/Astatic vfs_vget_t	nullfs_vget;
671830SN/Astatic vfs_extattrctl_t	nullfs_extattrctl;
681830SN/Astatic vfs_reclaim_lowervp_t nullfs_reclaim_lowervp;
691830SN/A
701830SN/A/*
711830SN/A * Mount null layer
721830SN/A */
731830SN/Astatic int
741830SN/Anullfs_mount(struct mount *mp)
751830SN/A{
761830SN/A	int error = 0;
771830SN/A	struct vnode *lowerrootvp, *vp;
781830SN/A	struct vnode *nullm_rootvp;
791830SN/A	struct null_mount *xmp;
801830SN/A	struct thread *td = curthread;
811830SN/A	char *target;
821830SN/A	int isvnunlocked = 0, len;
831830SN/A	struct nameidata nd, *ndp = &nd;
841830SN/A
851830SN/A	NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp);
861830SN/A
871830SN/A	if (!prison_allow(td->td_ucred, PR_ALLOW_MOUNT_NULLFS))
881830SN/A		return (EPERM);
891830SN/A
901830SN/A	if (mp->mnt_flag & MNT_ROOTFS)
911830SN/A		return (EOPNOTSUPP);
921830SN/A	/*
931830SN/A	 * Update is a no-op
941830SN/A	 */
951830SN/A	if (mp->mnt_flag & MNT_UPDATE) {
961830SN/A		/*
971830SN/A		 * Only support update mounts for NFS export.
981830SN/A		 */
991830SN/A		if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0))
1001830SN/A			return (0);
1011830SN/A		else
1021830SN/A			return (EOPNOTSUPP);
1031830SN/A	}
1041830SN/A
1051830SN/A	/*
1061830SN/A	 * Get argument
1071830SN/A	 */
1081830SN/A	error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len);
1091830SN/A	if (error || target[len - 1] != '\0')
1101830SN/A		return (EINVAL);
1111830SN/A
1121830SN/A	/*
1131830SN/A	 * Unlock lower node to avoid possible deadlock.
1141830SN/A	 */
1151830SN/A	if ((mp->mnt_vnodecovered->v_op == &null_vnodeops) &&
1161830SN/A	    VOP_ISLOCKED(mp->mnt_vnodecovered) == LK_EXCLUSIVE) {
1171830SN/A		VOP_UNLOCK(mp->mnt_vnodecovered, 0);
1181830SN/A		isvnunlocked = 1;
1191830SN/A	}
1201830SN/A	/*
1211830SN/A	 * Find lower node
1221830SN/A	 */
1231830SN/A	NDINIT(ndp, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, target, curthread);
1241830SN/A	error = namei(ndp);
1251830SN/A
1261830SN/A	/*
1271830SN/A	 * Re-lock vnode.
1281830SN/A	 * XXXKIB This is deadlock-prone as well.
1291830SN/A	 */
1301830SN/A	if (isvnunlocked)
1311830SN/A		vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY);
1321830SN/A
1331830SN/A	if (error)
1341830SN/A		return (error);
1351830SN/A	NDFREE(ndp, NDF_ONLY_PNBUF);
1361830SN/A
1371830SN/A	/*
1381830SN/A	 * Sanity check on lower vnode
1391830SN/A	 */
1401830SN/A	lowerrootvp = ndp->ni_vp;
1411830SN/A
1421830SN/A	/*
1431830SN/A	 * Check multi null mount to avoid `lock against myself' panic.
1441830SN/A	 */
1451830SN/A	if (lowerrootvp == VTONULL(mp->mnt_vnodecovered)->null_lowervp) {
1461830SN/A		NULLFSDEBUG("nullfs_mount: multi null mount?\n");
1471830SN/A		vput(lowerrootvp);
1481830SN/A		return (EDEADLK);
1491830SN/A	}
1501830SN/A
1511830SN/A	xmp = (struct null_mount *) malloc(sizeof(struct null_mount),
1521830SN/A	    M_NULLFSMNT, M_WAITOK);
1531830SN/A
1541830SN/A	/*
1551830SN/A	 * Save reference to underlying FS
1561830SN/A	 */
1571830SN/A	xmp->nullm_vfs = lowerrootvp->v_mount;
1581830SN/A
1591830SN/A	/*
1601830SN/A	 * Save reference.  Each mount also holds
1611830SN/A	 * a reference on the root vnode.
1621830SN/A	 */
1631830SN/A	error = null_nodeget(mp, lowerrootvp, &vp);
1641830SN/A	/*
1651830SN/A	 * Make sure the node alias worked
1661830SN/A	 */
1671830SN/A	if (error) {
1681830SN/A		free(xmp, M_NULLFSMNT);
1691830SN/A		return (error);
1701830SN/A	}
1711830SN/A
1721830SN/A	/*
1731830SN/A	 * Keep a held reference to the root vnode.
1741830SN/A	 * It is vrele'd in nullfs_unmount.
1751830SN/A	 */
1761830SN/A	nullm_rootvp = vp;
1771830SN/A	nullm_rootvp->v_vflag |= VV_ROOT;
1781830SN/A	xmp->nullm_rootvp = nullm_rootvp;
1791830SN/A
1801830SN/A	/*
1811830SN/A	 * Unlock the node (either the lower or the alias)
1821830SN/A	 */
1831830SN/A	VOP_UNLOCK(vp, 0);
1841830SN/A
1851830SN/A	if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) {
1861830SN/A		MNT_ILOCK(mp);
1871830SN/A		mp->mnt_flag |= MNT_LOCAL;
1881830SN/A		MNT_IUNLOCK(mp);
1891830SN/A	}
1901830SN/A	MNT_ILOCK(mp);
1911830SN/A	mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag &
1921830SN/A	    (MNTK_MPSAFE | MNTK_SHARED_WRITES | MNTK_LOOKUP_SHARED |
1931830SN/A	    MNTK_EXTENDED_SHARED);
1941830SN/A	mp->mnt_kern_flag |= MNTK_LOOKUP_EXCL_DOTDOT;
1951830SN/A	MNT_IUNLOCK(mp);
1961830SN/A	mp->mnt_data = xmp;
1971830SN/A	vfs_getnewfsid(mp);
1981830SN/A	MNT_ILOCK(xmp->nullm_vfs);
1991830SN/A	TAILQ_INSERT_TAIL(&xmp->nullm_vfs->mnt_uppers, mp, mnt_upper_link);
2001830SN/A	MNT_IUNLOCK(xmp->nullm_vfs);
2011830SN/A
2021830SN/A	vfs_mountedfrom(mp, target);
2031830SN/A
2041830SN/A	NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n",
2051830SN/A		mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
2061830SN/A	return (0);
2071830SN/A}
2081830SN/A
2091830SN/A/*
2101830SN/A * Free reference to null layer
2111830SN/A */
2121830SN/Astatic int
2131830SN/Anullfs_unmount(mp, mntflags)
2141830SN/A	struct mount *mp;
2151830SN/A	int mntflags;
2161830SN/A{
2171830SN/A	struct null_mount *mntdata;
2181830SN/A	struct mount *ump;
2191830SN/A	int error, flags;
2201830SN/A
2211830SN/A	NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp);
2221830SN/A
2231830SN/A	if (mntflags & MNT_FORCE)
2241830SN/A		flags = FORCECLOSE;
2251830SN/A	else
2261830SN/A		flags = 0;
2271830SN/A
2281830SN/A	/* There is 1 extra root vnode reference (nullm_rootvp). */
2291830SN/A	error = vflush(mp, 1, flags, curthread);
2301830SN/A	if (error)
2311830SN/A		return (error);
2321830SN/A
2331830SN/A	/*
2341830SN/A	 * Finally, throw away the null_mount structure
2351830SN/A	 */
2361830SN/A	mntdata = mp->mnt_data;
2371830SN/A	ump = mntdata->nullm_vfs;
2381830SN/A	MNT_ILOCK(ump);
2391830SN/A	while ((ump->mnt_kern_flag & MNTK_VGONE_UPPER) != 0) {
2401830SN/A		ump->mnt_kern_flag |= MNTK_VGONE_WAITER;
2411830SN/A		msleep(&ump->mnt_uppers, &ump->mnt_mtx, 0, "vgnupw", 0);
2421830SN/A	}
2431830SN/A	TAILQ_REMOVE(&ump->mnt_uppers, mp, mnt_upper_link);
2441830SN/A	MNT_IUNLOCK(ump);
2451830SN/A	mp->mnt_data = NULL;
2461830SN/A	free(mntdata, M_NULLFSMNT);
2471830SN/A	return (0);
2481830SN/A}
2491830SN/A
2501830SN/Astatic int
2511830SN/Anullfs_root(mp, flags, vpp)
2521830SN/A	struct mount *mp;
2531830SN/A	int flags;
2541830SN/A	struct vnode **vpp;
2551830SN/A{
2561830SN/A	struct vnode *vp;
2571830SN/A
2581830SN/A	NULLFSDEBUG("nullfs_root(mp = %p, vp = %p->%p)\n", (void *)mp,
2591830SN/A	    (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp,
2601830SN/A	    (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp));
2611830SN/A
2621830SN/A	/*
2631830SN/A	 * Return locked reference to root.
2641830SN/A	 */
2651830SN/A	vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
2661830SN/A	VREF(vp);
2671830SN/A
2681830SN/A	ASSERT_VOP_UNLOCKED(vp, "root vnode is locked");
2691830SN/A	vn_lock(vp, flags | LK_RETRY);
2701830SN/A	*vpp = vp;
2711830SN/A	return 0;
2721830SN/A}
2731830SN/A
2741830SN/Astatic int
2751830SN/Anullfs_quotactl(mp, cmd, uid, arg)
2761830SN/A	struct mount *mp;
2771830SN/A	int cmd;
2781830SN/A	uid_t uid;
2791830SN/A	void *arg;
2801830SN/A{
2811830SN/A	return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg);
2821830SN/A}
2831830SN/A
2841830SN/Astatic int
2851830SN/Anullfs_statfs(mp, sbp)
2861830SN/A	struct mount *mp;
2871830SN/A	struct statfs *sbp;
2881830SN/A{
2891830SN/A	int error;
2901830SN/A	struct statfs mstat;
2911830SN/A
2921830SN/A	NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp,
2931830SN/A	    (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp,
2941830SN/A	    (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp));
2951830SN/A
2961830SN/A	bzero(&mstat, sizeof(mstat));
2971830SN/A
2981830SN/A	error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat);
2991830SN/A	if (error)
3001830SN/A		return (error);
3011830SN/A
3021830SN/A	/* now copy across the "interesting" information and fake the rest */
3031830SN/A	sbp->f_type = mstat.f_type;
3041830SN/A	sbp->f_flags = mstat.f_flags;
3051830SN/A	sbp->f_bsize = mstat.f_bsize;
3061830SN/A	sbp->f_iosize = mstat.f_iosize;
3071830SN/A	sbp->f_blocks = mstat.f_blocks;
3081830SN/A	sbp->f_bfree = mstat.f_bfree;
3091830SN/A	sbp->f_bavail = mstat.f_bavail;
3101830SN/A	sbp->f_files = mstat.f_files;
3111830SN/A	sbp->f_ffree = mstat.f_ffree;
3121830SN/A	return (0);
3131830SN/A}
3141830SN/A
3151830SN/Astatic int
3161830SN/Anullfs_sync(mp, waitfor)
3171830SN/A	struct mount *mp;
3181830SN/A	int waitfor;
3191830SN/A{
3201830SN/A	/*
3211830SN/A	 * XXX - Assumes no data cached at null layer.
3221830SN/A	 */
3231830SN/A	return (0);
3241830SN/A}
3251830SN/A
3261830SN/Astatic int
3271830SN/Anullfs_vget(mp, ino, flags, vpp)
3281830SN/A	struct mount *mp;
3291830SN/A	ino_t ino;
3301830SN/A	int flags;
3311830SN/A	struct vnode **vpp;
3321830SN/A{
3331830SN/A	int error;
3341830SN/A
3351830SN/A	KASSERT((flags & LK_TYPE_MASK) != 0,
3361830SN/A	    ("nullfs_vget: no lock requested"));
3371830SN/A
3381830SN/A	error = VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, flags, vpp);
3391830SN/A	if (error != 0)
3401830SN/A		return (error);
3411830SN/A	return (null_nodeget(mp, *vpp, vpp));
3421830SN/A}
3431830SN/A
3441830SN/Astatic int
3451830SN/Anullfs_fhtovp(mp, fidp, flags, vpp)
3461830SN/A	struct mount *mp;
3471830SN/A	struct fid *fidp;
3481830SN/A	int flags;
3491830SN/A	struct vnode **vpp;
3501830SN/A{
3511830SN/A	int error;
3521830SN/A
3531830SN/A	error = VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, flags,
3541830SN/A	    vpp);
3551830SN/A	if (error != 0)
3561830SN/A		return (error);
3571830SN/A	return (null_nodeget(mp, *vpp, vpp));
3581830SN/A}
3591830SN/A
3601830SN/Astatic int
3611830SN/Anullfs_extattrctl(mp, cmd, filename_vp, namespace, attrname)
3621830SN/A	struct mount *mp;
3631830SN/A	int cmd;
3641830SN/A	struct vnode *filename_vp;
3651830SN/A	int namespace;
3661830SN/A	const char *attrname;
3671830SN/A{
3681830SN/A
3691830SN/A	return (VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd,
3701830SN/A	    filename_vp, namespace, attrname));
3711830SN/A}
3721830SN/A
3731830SN/Astatic void
3741830SN/Anullfs_reclaim_lowervp(struct mount *mp, struct vnode *lowervp)
3751830SN/A{
3761830SN/A	struct vnode *vp;
3771830SN/A
3781830SN/A	vp = null_hashget(mp, lowervp);
3791830SN/A	if (vp == NULL)
3801830SN/A		return;
3811830SN/A	vgone(vp);
3821830SN/A	vn_lock(lowervp, LK_EXCLUSIVE | LK_RETRY);
3831830SN/A}
3841830SN/A
3851830SN/Astatic struct vfsops null_vfsops = {
3861830SN/A	.vfs_extattrctl =	nullfs_extattrctl,
3871830SN/A	.vfs_fhtovp =		nullfs_fhtovp,
3881830SN/A	.vfs_init =		nullfs_init,
3891830SN/A	.vfs_mount =		nullfs_mount,
3901830SN/A	.vfs_quotactl =		nullfs_quotactl,
3911830SN/A	.vfs_root =		nullfs_root,
3921830SN/A	.vfs_statfs =		nullfs_statfs,
3931830SN/A	.vfs_sync =		nullfs_sync,
3941830SN/A	.vfs_uninit =		nullfs_uninit,
3951830SN/A	.vfs_unmount =		nullfs_unmount,
3961830SN/A	.vfs_vget =		nullfs_vget,
3971830SN/A	.vfs_reclaim_lowervp =	nullfs_reclaim_lowervp,
3981830SN/A};
3991830SN/A
4001830SN/AVFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL);
4011830SN/A