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