xref: /freebsd/sys/fs/nullfs/null_vfsops.c (revision 5e53a4f90f82c4345f277dd87cc9292f26e04a29)
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  * $FreeBSD$
38  */
39 
40 /*
41  * Null Layer
42  * (See null_vnops.c for a description of what this does.)
43  */
44 
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/fcntl.h>
48 #include <sys/kernel.h>
49 #include <sys/lock.h>
50 #include <sys/malloc.h>
51 #include <sys/mount.h>
52 #include <sys/namei.h>
53 #include <sys/proc.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 /*
72  * Mount null layer
73  */
74 static int
75 nullfs_mount(struct mount *mp)
76 {
77 	int error = 0;
78 	struct vnode *lowerrootvp, *vp;
79 	struct vnode *nullm_rootvp;
80 	struct null_mount *xmp;
81 	struct thread *td = curthread;
82 	char *target;
83 	int isvnunlocked = 0, len;
84 	struct nameidata nd, *ndp = &nd;
85 
86 	NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp);
87 
88 	if (!prison_allow(td->td_ucred, PR_ALLOW_MOUNT_NULLFS))
89 		return (EPERM);
90 	if (mp->mnt_flag & MNT_ROOTFS)
91 		return (EOPNOTSUPP);
92 
93 	/*
94 	 * Update is a no-op
95 	 */
96 	if (mp->mnt_flag & MNT_UPDATE) {
97 		/*
98 		 * Only support update mounts for NFS export.
99 		 */
100 		if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0))
101 			return (0);
102 		else
103 			return (EOPNOTSUPP);
104 	}
105 
106 	/*
107 	 * Get argument
108 	 */
109 	error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len);
110 	if (error || target[len - 1] != '\0')
111 		return (EINVAL);
112 
113 	/*
114 	 * Unlock lower node to avoid possible deadlock.
115 	 */
116 	if ((mp->mnt_vnodecovered->v_op == &null_vnodeops) &&
117 	    VOP_ISLOCKED(mp->mnt_vnodecovered) == LK_EXCLUSIVE) {
118 		VOP_UNLOCK(mp->mnt_vnodecovered, 0);
119 		isvnunlocked = 1;
120 	}
121 	/*
122 	 * Find lower node
123 	 */
124 	NDINIT(ndp, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, target, curthread);
125 	error = namei(ndp);
126 
127 	/*
128 	 * Re-lock vnode.
129 	 * XXXKIB This is deadlock-prone as well.
130 	 */
131 	if (isvnunlocked)
132 		vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY);
133 
134 	if (error)
135 		return (error);
136 	NDFREE(ndp, NDF_ONLY_PNBUF);
137 
138 	/*
139 	 * Sanity check on lower vnode
140 	 */
141 	lowerrootvp = ndp->ni_vp;
142 
143 	/*
144 	 * Check multi null mount to avoid `lock against myself' panic.
145 	 */
146 	if (lowerrootvp == VTONULL(mp->mnt_vnodecovered)->null_lowervp) {
147 		NULLFSDEBUG("nullfs_mount: multi null mount?\n");
148 		vput(lowerrootvp);
149 		return (EDEADLK);
150 	}
151 
152 	xmp = (struct null_mount *) malloc(sizeof(struct null_mount),
153 	    M_NULLFSMNT, M_WAITOK | M_ZERO);
154 
155 	/*
156 	 * Save reference to underlying FS
157 	 */
158 	xmp->nullm_vfs = lowerrootvp->v_mount;
159 
160 	/*
161 	 * Save reference.  Each mount also holds
162 	 * a reference on the root vnode.
163 	 */
164 	error = null_nodeget(mp, lowerrootvp, &vp);
165 	/*
166 	 * Make sure the node alias worked
167 	 */
168 	if (error) {
169 		free(xmp, M_NULLFSMNT);
170 		return (error);
171 	}
172 
173 	/*
174 	 * Keep a held reference to the root vnode.
175 	 * It is vrele'd in nullfs_unmount.
176 	 */
177 	nullm_rootvp = vp;
178 	nullm_rootvp->v_vflag |= VV_ROOT;
179 	xmp->nullm_rootvp = nullm_rootvp;
180 
181 	/*
182 	 * Unlock the node (either the lower or the alias)
183 	 */
184 	VOP_UNLOCK(vp, 0);
185 
186 	if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) {
187 		MNT_ILOCK(mp);
188 		mp->mnt_flag |= MNT_LOCAL;
189 		MNT_IUNLOCK(mp);
190 	}
191 
192 	xmp->nullm_flags |= NULLM_CACHE;
193 	if (vfs_getopt(mp->mnt_optnew, "nocache", NULL, NULL) == 0 ||
194 	    (xmp->nullm_vfs->mnt_kern_flag & MNTK_NULL_NOCACHE) != 0)
195 		xmp->nullm_flags &= ~NULLM_CACHE;
196 
197 	MNT_ILOCK(mp);
198 	if ((xmp->nullm_flags & NULLM_CACHE) != 0) {
199 		mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag &
200 		    (MNTK_SHARED_WRITES | MNTK_LOOKUP_SHARED |
201 		    MNTK_EXTENDED_SHARED);
202 	}
203 	mp->mnt_kern_flag |= MNTK_LOOKUP_EXCL_DOTDOT;
204 	mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag &
205 	    (MNTK_USES_BCACHE | MNTK_NO_IOPF | MNTK_UNMAPPED_BUFS);
206 	MNT_IUNLOCK(mp);
207 	mp->mnt_data = xmp;
208 	vfs_getnewfsid(mp);
209 	if ((xmp->nullm_flags & NULLM_CACHE) != 0) {
210 		MNT_ILOCK(xmp->nullm_vfs);
211 		TAILQ_INSERT_TAIL(&xmp->nullm_vfs->mnt_uppers, mp,
212 		    mnt_upper_link);
213 		MNT_IUNLOCK(xmp->nullm_vfs);
214 	}
215 
216 	vfs_mountedfrom(mp, target);
217 
218 	NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n",
219 		mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
220 	return (0);
221 }
222 
223 /*
224  * Free reference to null layer
225  */
226 static int
227 nullfs_unmount(mp, mntflags)
228 	struct mount *mp;
229 	int mntflags;
230 {
231 	struct null_mount *mntdata;
232 	struct mount *ump;
233 	int error, flags;
234 
235 	NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp);
236 
237 	if (mntflags & MNT_FORCE)
238 		flags = FORCECLOSE;
239 	else
240 		flags = 0;
241 
242 	/* There is 1 extra root vnode reference (nullm_rootvp). */
243 	error = vflush(mp, 1, flags, curthread);
244 	if (error)
245 		return (error);
246 
247 	/*
248 	 * Finally, throw away the null_mount structure
249 	 */
250 	mntdata = mp->mnt_data;
251 	ump = mntdata->nullm_vfs;
252 	if ((mntdata->nullm_flags & NULLM_CACHE) != 0) {
253 		MNT_ILOCK(ump);
254 		while ((ump->mnt_kern_flag & MNTK_VGONE_UPPER) != 0) {
255 			ump->mnt_kern_flag |= MNTK_VGONE_WAITER;
256 			msleep(&ump->mnt_uppers, &ump->mnt_mtx, 0, "vgnupw", 0);
257 		}
258 		TAILQ_REMOVE(&ump->mnt_uppers, mp, mnt_upper_link);
259 		MNT_IUNLOCK(ump);
260 	}
261 	mp->mnt_data = NULL;
262 	free(mntdata, M_NULLFSMNT);
263 	return (0);
264 }
265 
266 static int
267 nullfs_root(mp, flags, vpp)
268 	struct mount *mp;
269 	int flags;
270 	struct vnode **vpp;
271 {
272 	struct vnode *vp;
273 
274 	NULLFSDEBUG("nullfs_root(mp = %p, vp = %p->%p)\n", (void *)mp,
275 	    (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp,
276 	    (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp));
277 
278 	/*
279 	 * Return locked reference to root.
280 	 */
281 	vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
282 	VREF(vp);
283 
284 	ASSERT_VOP_UNLOCKED(vp, "root vnode is locked");
285 	vn_lock(vp, flags | LK_RETRY);
286 	*vpp = vp;
287 	return 0;
288 }
289 
290 static int
291 nullfs_quotactl(mp, cmd, uid, arg)
292 	struct mount *mp;
293 	int cmd;
294 	uid_t uid;
295 	void *arg;
296 {
297 	return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg);
298 }
299 
300 static int
301 nullfs_statfs(mp, sbp)
302 	struct mount *mp;
303 	struct statfs *sbp;
304 {
305 	int error;
306 	struct statfs *mstat;
307 
308 	NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp,
309 	    (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp,
310 	    (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp));
311 
312 	mstat = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK | M_ZERO);
313 
314 	error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, mstat);
315 	if (error) {
316 		free(mstat, M_STATFS);
317 		return (error);
318 	}
319 
320 	/* now copy across the "interesting" information and fake the rest */
321 	sbp->f_type = mstat->f_type;
322 	sbp->f_flags = (sbp->f_flags & (MNT_RDONLY | MNT_NOEXEC | MNT_NOSUID |
323 	    MNT_UNION | MNT_NOSYMFOLLOW)) | (mstat->f_flags & ~MNT_ROOTFS);
324 	sbp->f_bsize = mstat->f_bsize;
325 	sbp->f_iosize = mstat->f_iosize;
326 	sbp->f_blocks = mstat->f_blocks;
327 	sbp->f_bfree = mstat->f_bfree;
328 	sbp->f_bavail = mstat->f_bavail;
329 	sbp->f_files = mstat->f_files;
330 	sbp->f_ffree = mstat->f_ffree;
331 
332 	free(mstat, M_STATFS);
333 	return (0);
334 }
335 
336 static int
337 nullfs_sync(mp, waitfor)
338 	struct mount *mp;
339 	int waitfor;
340 {
341 	/*
342 	 * XXX - Assumes no data cached at null layer.
343 	 */
344 	return (0);
345 }
346 
347 static int
348 nullfs_vget(mp, ino, flags, vpp)
349 	struct mount *mp;
350 	ino_t ino;
351 	int flags;
352 	struct vnode **vpp;
353 {
354 	int error;
355 
356 	KASSERT((flags & LK_TYPE_MASK) != 0,
357 	    ("nullfs_vget: no lock requested"));
358 
359 	error = VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, flags, vpp);
360 	if (error != 0)
361 		return (error);
362 	return (null_nodeget(mp, *vpp, vpp));
363 }
364 
365 static int
366 nullfs_fhtovp(mp, fidp, flags, vpp)
367 	struct mount *mp;
368 	struct fid *fidp;
369 	int flags;
370 	struct vnode **vpp;
371 {
372 	int error;
373 
374 	error = VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, flags,
375 	    vpp);
376 	if (error != 0)
377 		return (error);
378 	return (null_nodeget(mp, *vpp, vpp));
379 }
380 
381 static int
382 nullfs_extattrctl(mp, cmd, filename_vp, namespace, attrname)
383 	struct mount *mp;
384 	int cmd;
385 	struct vnode *filename_vp;
386 	int namespace;
387 	const char *attrname;
388 {
389 
390 	return (VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd,
391 	    filename_vp, namespace, attrname));
392 }
393 
394 static void
395 nullfs_reclaim_lowervp(struct mount *mp, struct vnode *lowervp)
396 {
397 	struct vnode *vp;
398 
399 	vp = null_hashget(mp, lowervp);
400 	if (vp == NULL)
401 		return;
402 	VTONULL(vp)->null_flags |= NULLV_NOUNLOCK;
403 	vgone(vp);
404 	vput(vp);
405 }
406 
407 static void
408 nullfs_unlink_lowervp(struct mount *mp, struct vnode *lowervp)
409 {
410 	struct vnode *vp;
411 	struct null_node *xp;
412 
413 	vp = null_hashget(mp, lowervp);
414 	if (vp == NULL)
415 		return;
416 	xp = VTONULL(vp);
417 	xp->null_flags |= NULLV_DROP | NULLV_NOUNLOCK;
418 	vhold(vp);
419 	vunref(vp);
420 
421 	if (vp->v_usecount == 0) {
422 		/*
423 		 * If vunref() dropped the last use reference on the
424 		 * nullfs vnode, it must be reclaimed, and its lock
425 		 * was split from the lower vnode lock.  Need to do
426 		 * extra unlock before allowing the final vdrop() to
427 		 * free the vnode.
428 		 */
429 		KASSERT((vp->v_iflag & VI_DOOMED) != 0,
430 		    ("not reclaimed nullfs vnode %p", vp));
431 		VOP_UNLOCK(vp, 0);
432 	} else {
433 		/*
434 		 * Otherwise, the nullfs vnode still shares the lock
435 		 * with the lower vnode, and must not be unlocked.
436 		 * Also clear the NULLV_NOUNLOCK, the flag is not
437 		 * relevant for future reclamations.
438 		 */
439 		ASSERT_VOP_ELOCKED(vp, "unlink_lowervp");
440 		KASSERT((vp->v_iflag & VI_DOOMED) == 0,
441 		    ("reclaimed nullfs vnode %p", vp));
442 		xp->null_flags &= ~NULLV_NOUNLOCK;
443 	}
444 	vdrop(vp);
445 }
446 
447 static struct vfsops null_vfsops = {
448 	.vfs_extattrctl =	nullfs_extattrctl,
449 	.vfs_fhtovp =		nullfs_fhtovp,
450 	.vfs_init =		nullfs_init,
451 	.vfs_mount =		nullfs_mount,
452 	.vfs_quotactl =		nullfs_quotactl,
453 	.vfs_root =		nullfs_root,
454 	.vfs_statfs =		nullfs_statfs,
455 	.vfs_sync =		nullfs_sync,
456 	.vfs_uninit =		nullfs_uninit,
457 	.vfs_unmount =		nullfs_unmount,
458 	.vfs_vget =		nullfs_vget,
459 	.vfs_reclaim_lowervp =	nullfs_reclaim_lowervp,
460 	.vfs_unlink_lowervp =	nullfs_unlink_lowervp,
461 };
462 
463 VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL);
464