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