xref: /freebsd-13-stable/sys/fs/nullfs/null_vfsops.c (revision 82c8a5f4f18c72b3be34f182b0509a133e4f3fb4)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1992, 1993, 1995
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software donated to Berkeley by
8  * Jan-Simon Pendry.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *	@(#)null_vfsops.c	8.2 (Berkeley) 1/21/94
35  *
36  * @(#)lofs_vfsops.c	1.2 (Berkeley) 6/18/92
37  */
38 
39 /*
40  * Null Layer
41  * (See null_vnops.c for a description of what this does.)
42  */
43 
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/fcntl.h>
47 #include <sys/kernel.h>
48 #include <sys/lock.h>
49 #include <sys/malloc.h>
50 #include <sys/mount.h>
51 #include <sys/namei.h>
52 #include <sys/proc.h>
53 #include <sys/sysctl.h>
54 #include <sys/vnode.h>
55 #include <sys/jail.h>
56 
57 #include <fs/nullfs/null.h>
58 
59 static MALLOC_DEFINE(M_NULLFSMNT, "nullfs_mount", "NULLFS mount structure");
60 
61 static vfs_fhtovp_t	nullfs_fhtovp;
62 static vfs_mount_t	nullfs_mount;
63 static vfs_quotactl_t	nullfs_quotactl;
64 static vfs_root_t	nullfs_root;
65 static vfs_sync_t	nullfs_sync;
66 static vfs_statfs_t	nullfs_statfs;
67 static vfs_unmount_t	nullfs_unmount;
68 static vfs_vget_t	nullfs_vget;
69 static vfs_extattrctl_t	nullfs_extattrctl;
70 
71 SYSCTL_NODE(_vfs, OID_AUTO, nullfs, CTLFLAG_RW, 0, "nullfs");
72 
73 static bool null_cache_vnodes = true;
74 SYSCTL_BOOL(_vfs_nullfs, OID_AUTO, cache_vnodes, CTLFLAG_RWTUN,
75     &null_cache_vnodes, 0,
76     "cache free nullfs vnodes");
77 
78 /*
79  * Mount null layer
80  */
81 static int
nullfs_mount(struct mount * mp)82 nullfs_mount(struct mount *mp)
83 {
84 	struct vnode *lowerrootvp;
85 	struct vnode *nullm_rootvp;
86 	struct null_mount *xmp;
87 	struct null_node *nn;
88 	struct nameidata nd, *ndp;
89 	char *target;
90 	int error, len;
91 	bool isvnunlocked;
92 
93 	NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp);
94 
95 	if (mp->mnt_flag & MNT_ROOTFS)
96 		return (EOPNOTSUPP);
97 
98 	/*
99 	 * Update is a no-op
100 	 */
101 	if (mp->mnt_flag & MNT_UPDATE) {
102 		/*
103 		 * Only support update mounts for NFS export.
104 		 */
105 		if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0))
106 			return (0);
107 		else
108 			return (EOPNOTSUPP);
109 	}
110 
111 	/*
112 	 * Get argument
113 	 */
114 	error = vfs_getopt(mp->mnt_optnew, "from", (void **)&target, &len);
115 	if (error != 0)
116 		error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len);
117 	if (error || target[len - 1] != '\0')
118 		return (EINVAL);
119 
120 	/*
121 	 * Unlock lower node to avoid possible deadlock.
122 	 */
123 	if (mp->mnt_vnodecovered->v_op == &null_vnodeops &&
124 	    VOP_ISLOCKED(mp->mnt_vnodecovered) == LK_EXCLUSIVE) {
125 		VOP_UNLOCK(mp->mnt_vnodecovered);
126 		isvnunlocked = true;
127 	} else {
128 		isvnunlocked = false;
129 	}
130 
131 	/*
132 	 * Find lower node
133 	 */
134 	ndp = &nd;
135 	NDINIT(ndp, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, target, curthread);
136 	error = namei(ndp);
137 
138 	/*
139 	 * Re-lock vnode.
140 	 * XXXKIB This is deadlock-prone as well.
141 	 */
142 	if (isvnunlocked)
143 		vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY);
144 
145 	if (error)
146 		return (error);
147 	NDFREE(ndp, NDF_ONLY_PNBUF);
148 
149 	/*
150 	 * Sanity check on lower vnode
151 	 */
152 	lowerrootvp = ndp->ni_vp;
153 
154 	/*
155 	 * Check multi null mount to avoid `lock against myself' panic.
156 	 */
157 	if (mp->mnt_vnodecovered->v_op == &null_vnodeops) {
158 		nn = VTONULL(mp->mnt_vnodecovered);
159 		if (nn == NULL || lowerrootvp == nn->null_lowervp) {
160 			NULLFSDEBUG("nullfs_mount: multi null mount?\n");
161 			vput(lowerrootvp);
162 			return (EDEADLK);
163 		}
164 	}
165 
166 	/*
167 	 * Lower vnode must be the same type as the covered vnode - we
168 	 * don't allow mounting directories to files or vice versa.
169 	 */
170 	if ((lowerrootvp->v_type != VDIR && lowerrootvp->v_type != VREG) ||
171 	    lowerrootvp->v_type != mp->mnt_vnodecovered->v_type) {
172 		NULLFSDEBUG("nullfs_mount: target must be same type as fspath");
173 		vput(lowerrootvp);
174 		return (EINVAL);
175 	}
176 
177 	xmp = malloc(sizeof(struct null_mount), M_NULLFSMNT,
178 	    M_WAITOK | M_ZERO);
179 
180 	/*
181 	 * Save pointer to underlying FS and the reference to the
182 	 * lower root vnode.
183 	 */
184 	xmp->nullm_vfs = lowerrootvp->v_mount;
185 	vref(lowerrootvp);
186 	xmp->nullm_lowerrootvp = lowerrootvp;
187 	mp->mnt_data = xmp;
188 
189 	/*
190 	 * Make sure the node alias worked.
191 	 */
192 	error = null_nodeget(mp, lowerrootvp, &nullm_rootvp);
193 	if (error != 0) {
194 		vrele(lowerrootvp);
195 		free(xmp, M_NULLFSMNT);
196 		return (error);
197 	}
198 
199 	if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) {
200 		MNT_ILOCK(mp);
201 		mp->mnt_flag |= MNT_LOCAL;
202 		MNT_IUNLOCK(mp);
203 	}
204 
205 	if (vfs_getopt(mp->mnt_optnew, "cache", NULL, NULL) == 0) {
206 		xmp->nullm_flags |= NULLM_CACHE;
207 	} else if (vfs_getopt(mp->mnt_optnew, "nocache", NULL, NULL) == 0) {
208 		;
209 	} else if (null_cache_vnodes &&
210 	    (xmp->nullm_vfs->mnt_kern_flag & MNTK_NULL_NOCACHE) == 0) {
211 		xmp->nullm_flags |= NULLM_CACHE;
212 	}
213 
214 	MNT_ILOCK(mp);
215 	if ((xmp->nullm_flags & NULLM_CACHE) != 0) {
216 		mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag &
217 		    (MNTK_SHARED_WRITES | MNTK_LOOKUP_SHARED |
218 		    MNTK_EXTENDED_SHARED);
219 	}
220 	mp->mnt_kern_flag |= MNTK_LOOKUP_EXCL_DOTDOT | MNTK_NOMSYNC;
221 	mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag &
222 	    (MNTK_USES_BCACHE | MNTK_NO_IOPF | MNTK_UNMAPPED_BUFS);
223 	MNT_IUNLOCK(mp);
224 	vfs_getnewfsid(mp);
225 	if ((xmp->nullm_flags & NULLM_CACHE) != 0) {
226 		MNT_ILOCK(xmp->nullm_vfs);
227 		TAILQ_INSERT_TAIL(&xmp->nullm_vfs->mnt_uppers, mp,
228 		    mnt_upper_link);
229 		MNT_IUNLOCK(xmp->nullm_vfs);
230 	}
231 
232 	vfs_mountedfrom(mp, target);
233 	vput(nullm_rootvp);
234 
235 	NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n",
236 		mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
237 	return (0);
238 }
239 
240 /*
241  * Free reference to null layer
242  */
243 static int
nullfs_unmount(struct mount * mp,int mntflags)244 nullfs_unmount(struct mount *mp, int mntflags)
245 {
246 	struct null_mount *mntdata;
247 	struct mount *ump;
248 	int error, flags;
249 
250 	NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp);
251 
252 	if (mntflags & MNT_FORCE)
253 		flags = FORCECLOSE;
254 	else
255 		flags = 0;
256 
257 	for (;;) {
258 		/* There is 1 extra root vnode reference (nullm_rootvp). */
259 		error = vflush(mp, 0, flags, curthread);
260 		if (error)
261 			return (error);
262 		MNT_ILOCK(mp);
263 		if (mp->mnt_nvnodelistsize == 0) {
264 			MNT_IUNLOCK(mp);
265 			break;
266 		}
267 		MNT_IUNLOCK(mp);
268 		if ((mntflags & MNT_FORCE) == 0)
269 			return (EBUSY);
270 	}
271 
272 	/*
273 	 * Finally, throw away the null_mount structure
274 	 */
275 	mntdata = mp->mnt_data;
276 	ump = mntdata->nullm_vfs;
277 	if ((mntdata->nullm_flags & NULLM_CACHE) != 0) {
278 		MNT_ILOCK(ump);
279 		while ((ump->mnt_kern_flag & MNTK_VGONE_UPPER) != 0) {
280 			ump->mnt_kern_flag |= MNTK_VGONE_WAITER;
281 			msleep(&ump->mnt_uppers, &ump->mnt_mtx, 0, "vgnupw", 0);
282 		}
283 		TAILQ_REMOVE(&ump->mnt_uppers, mp, mnt_upper_link);
284 		MNT_IUNLOCK(ump);
285 	}
286 	vrele(mntdata->nullm_lowerrootvp);
287 	mp->mnt_data = NULL;
288 	free(mntdata, M_NULLFSMNT);
289 	return (0);
290 }
291 
292 static int
nullfs_root(struct mount * mp,int flags,struct vnode ** vpp)293 nullfs_root(struct mount *mp, int flags, struct vnode **vpp)
294 {
295 	struct vnode *vp;
296 	struct null_mount *mntdata;
297 	int error;
298 
299 	mntdata = MOUNTTONULLMOUNT(mp);
300 	NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", mp,
301 	    mntdata->nullm_lowerrootvp);
302 
303 	error = vget(mntdata->nullm_lowerrootvp, flags);
304 	if (error == 0) {
305 		error = null_nodeget(mp, mntdata->nullm_lowerrootvp, &vp);
306 		if (error == 0) {
307 			*vpp = vp;
308 		}
309 	}
310 	return (error);
311 }
312 
313 static int
nullfs_quotactl(struct mount * mp,int cmd,uid_t uid,void * arg)314 nullfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg)
315 {
316 	return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg);
317 }
318 
319 static int
nullfs_statfs(struct mount * mp,struct statfs * sbp)320 nullfs_statfs(struct mount *mp, struct statfs *sbp)
321 {
322 	int error;
323 	struct statfs *mstat;
324 
325 	NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp,
326 	    (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp,
327 	    (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp));
328 
329 	mstat = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK | M_ZERO);
330 
331 	error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, mstat);
332 	if (error) {
333 		free(mstat, M_STATFS);
334 		return (error);
335 	}
336 
337 	/* now copy across the "interesting" information and fake the rest */
338 	sbp->f_type = mstat->f_type;
339 	sbp->f_flags &= MNT_RDONLY | MNT_NOEXEC | MNT_NOSUID | MNT_UNION |
340 	    MNT_NOSYMFOLLOW | MNT_AUTOMOUNTED | MNT_EXPORTED | MNT_IGNORE;
341 	mstat->f_flags &= ~(MNT_ROOTFS | MNT_AUTOMOUNTED | MNT_EXPORTED);
342 	sbp->f_flags |= mstat->f_flags;
343 	sbp->f_bsize = mstat->f_bsize;
344 	sbp->f_iosize = mstat->f_iosize;
345 	sbp->f_blocks = mstat->f_blocks;
346 	sbp->f_bfree = mstat->f_bfree;
347 	sbp->f_bavail = mstat->f_bavail;
348 	sbp->f_files = mstat->f_files;
349 	sbp->f_ffree = mstat->f_ffree;
350 
351 	free(mstat, M_STATFS);
352 	return (0);
353 }
354 
355 static int
nullfs_sync(struct mount * mp,int waitfor)356 nullfs_sync(struct mount *mp, int waitfor)
357 {
358 	/*
359 	 * XXX - Assumes no data cached at null layer.
360 	 */
361 	return (0);
362 }
363 
364 static int
nullfs_vget(struct mount * mp,ino_t ino,int flags,struct vnode ** vpp)365 nullfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
366 {
367 	int error;
368 
369 	KASSERT((flags & LK_TYPE_MASK) != 0,
370 	    ("nullfs_vget: no lock requested"));
371 
372 	error = VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, flags, vpp);
373 	if (error != 0)
374 		return (error);
375 	return (null_nodeget(mp, *vpp, vpp));
376 }
377 
378 static int
nullfs_fhtovp(struct mount * mp,struct fid * fidp,int flags,struct vnode ** vpp)379 nullfs_fhtovp(struct mount *mp, struct fid *fidp, int flags, struct vnode **vpp)
380 {
381 	int error;
382 
383 	error = VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, flags,
384 	    vpp);
385 	if (error != 0)
386 		return (error);
387 	return (null_nodeget(mp, *vpp, vpp));
388 }
389 
390 static int
nullfs_extattrctl(struct mount * mp,int cmd,struct vnode * filename_vp,int namespace,const char * attrname)391 nullfs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
392     int namespace, const char *attrname)
393 {
394 
395 	return (VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd,
396 	    filename_vp, namespace, attrname));
397 }
398 
399 static void
nullfs_reclaim_lowervp(struct mount * mp,struct vnode * lowervp)400 nullfs_reclaim_lowervp(struct mount *mp, struct vnode *lowervp)
401 {
402 	struct vnode *vp;
403 
404 	vp = null_hashget(mp, lowervp);
405 	if (vp == NULL)
406 		return;
407 	VTONULL(vp)->null_flags |= NULLV_NOUNLOCK;
408 	vgone(vp);
409 	vput(vp);
410 }
411 
412 static void
nullfs_unlink_lowervp(struct mount * mp,struct vnode * lowervp)413 nullfs_unlink_lowervp(struct mount *mp, struct vnode *lowervp)
414 {
415 	struct vnode *vp;
416 	struct null_node *xp;
417 
418 	vp = null_hashget(mp, lowervp);
419 	if (vp == NULL)
420 		return;
421 	xp = VTONULL(vp);
422 	xp->null_flags |= NULLV_DROP | NULLV_NOUNLOCK;
423 	vhold(vp);
424 	vunref(vp);
425 
426 	if (vp->v_usecount == 0) {
427 		/*
428 		 * If vunref() dropped the last use reference on the
429 		 * nullfs vnode, it must be reclaimed, and its lock
430 		 * was split from the lower vnode lock.  Need to do
431 		 * extra unlock before allowing the final vdrop() to
432 		 * free the vnode.
433 		 */
434 		KASSERT(VN_IS_DOOMED(vp),
435 		    ("not reclaimed nullfs vnode %p", vp));
436 		VOP_UNLOCK(vp);
437 	} else {
438 		/*
439 		 * Otherwise, the nullfs vnode still shares the lock
440 		 * with the lower vnode, and must not be unlocked.
441 		 * Also clear the NULLV_NOUNLOCK, the flag is not
442 		 * relevant for future reclamations.
443 		 */
444 		ASSERT_VOP_ELOCKED(vp, "unlink_lowervp");
445 		KASSERT(!VN_IS_DOOMED(vp),
446 		    ("reclaimed nullfs vnode %p", vp));
447 		xp->null_flags &= ~NULLV_NOUNLOCK;
448 	}
449 	vdrop(vp);
450 }
451 
452 static struct vfsops null_vfsops = {
453 	.vfs_extattrctl =	nullfs_extattrctl,
454 	.vfs_fhtovp =		nullfs_fhtovp,
455 	.vfs_init =		nullfs_init,
456 	.vfs_mount =		nullfs_mount,
457 	.vfs_quotactl =		nullfs_quotactl,
458 	.vfs_root =		nullfs_root,
459 	.vfs_statfs =		nullfs_statfs,
460 	.vfs_sync =		nullfs_sync,
461 	.vfs_uninit =		nullfs_uninit,
462 	.vfs_unmount =		nullfs_unmount,
463 	.vfs_vget =		nullfs_vget,
464 	.vfs_reclaim_lowervp =	nullfs_reclaim_lowervp,
465 	.vfs_unlink_lowervp =	nullfs_unlink_lowervp,
466 };
467 
468 VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL | VFCF_FILEMOUNT);
469