xref: /freebsd/sys/fs/nullfs/null_vfsops.c (revision c6ec7d31830ab1c80edae95ad5e4b9dba10c47ac)
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 
90 	if (mp->mnt_flag & MNT_ROOTFS)
91 		return (EOPNOTSUPP);
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);
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 	MNT_ILOCK(mp);
191 	mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag &
192 	    (MNTK_SHARED_WRITES | MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED);
193 	mp->mnt_kern_flag |= MNTK_LOOKUP_EXCL_DOTDOT;
194 	MNT_IUNLOCK(mp);
195 	mp->mnt_data = xmp;
196 	vfs_getnewfsid(mp);
197 	MNT_ILOCK(xmp->nullm_vfs);
198 	TAILQ_INSERT_TAIL(&xmp->nullm_vfs->mnt_uppers, mp, mnt_upper_link);
199 	MNT_IUNLOCK(xmp->nullm_vfs);
200 
201 	vfs_mountedfrom(mp, target);
202 
203 	NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n",
204 		mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
205 	return (0);
206 }
207 
208 /*
209  * Free reference to null layer
210  */
211 static int
212 nullfs_unmount(mp, mntflags)
213 	struct mount *mp;
214 	int mntflags;
215 {
216 	struct null_mount *mntdata;
217 	struct mount *ump;
218 	int error, flags;
219 
220 	NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp);
221 
222 	if (mntflags & MNT_FORCE)
223 		flags = FORCECLOSE;
224 	else
225 		flags = 0;
226 
227 	/* There is 1 extra root vnode reference (nullm_rootvp). */
228 	error = vflush(mp, 1, flags, curthread);
229 	if (error)
230 		return (error);
231 
232 	/*
233 	 * Finally, throw away the null_mount structure
234 	 */
235 	mntdata = mp->mnt_data;
236 	ump = mntdata->nullm_vfs;
237 	MNT_ILOCK(ump);
238 	while ((ump->mnt_kern_flag & MNTK_VGONE_UPPER) != 0) {
239 		ump->mnt_kern_flag |= MNTK_VGONE_WAITER;
240 		msleep(&ump->mnt_uppers, &ump->mnt_mtx, 0, "vgnupw", 0);
241 	}
242 	TAILQ_REMOVE(&ump->mnt_uppers, mp, mnt_upper_link);
243 	MNT_IUNLOCK(ump);
244 	mp->mnt_data = NULL;
245 	free(mntdata, M_NULLFSMNT);
246 	return (0);
247 }
248 
249 static int
250 nullfs_root(mp, flags, vpp)
251 	struct mount *mp;
252 	int flags;
253 	struct vnode **vpp;
254 {
255 	struct vnode *vp;
256 
257 	NULLFSDEBUG("nullfs_root(mp = %p, vp = %p->%p)\n", (void *)mp,
258 	    (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp,
259 	    (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp));
260 
261 	/*
262 	 * Return locked reference to root.
263 	 */
264 	vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
265 	VREF(vp);
266 
267 	ASSERT_VOP_UNLOCKED(vp, "root vnode is locked");
268 	vn_lock(vp, flags | LK_RETRY);
269 	*vpp = vp;
270 	return 0;
271 }
272 
273 static int
274 nullfs_quotactl(mp, cmd, uid, arg)
275 	struct mount *mp;
276 	int cmd;
277 	uid_t uid;
278 	void *arg;
279 {
280 	return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg);
281 }
282 
283 static int
284 nullfs_statfs(mp, sbp)
285 	struct mount *mp;
286 	struct statfs *sbp;
287 {
288 	int error;
289 	struct statfs mstat;
290 
291 	NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp,
292 	    (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp,
293 	    (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp));
294 
295 	bzero(&mstat, sizeof(mstat));
296 
297 	error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat);
298 	if (error)
299 		return (error);
300 
301 	/* now copy across the "interesting" information and fake the rest */
302 	sbp->f_type = mstat.f_type;
303 	sbp->f_flags = mstat.f_flags;
304 	sbp->f_bsize = mstat.f_bsize;
305 	sbp->f_iosize = mstat.f_iosize;
306 	sbp->f_blocks = mstat.f_blocks;
307 	sbp->f_bfree = mstat.f_bfree;
308 	sbp->f_bavail = mstat.f_bavail;
309 	sbp->f_files = mstat.f_files;
310 	sbp->f_ffree = mstat.f_ffree;
311 	return (0);
312 }
313 
314 static int
315 nullfs_sync(mp, waitfor)
316 	struct mount *mp;
317 	int waitfor;
318 {
319 	/*
320 	 * XXX - Assumes no data cached at null layer.
321 	 */
322 	return (0);
323 }
324 
325 static int
326 nullfs_vget(mp, ino, flags, vpp)
327 	struct mount *mp;
328 	ino_t ino;
329 	int flags;
330 	struct vnode **vpp;
331 {
332 	int error;
333 
334 	KASSERT((flags & LK_TYPE_MASK) != 0,
335 	    ("nullfs_vget: no lock requested"));
336 
337 	error = VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, flags, vpp);
338 	if (error != 0)
339 		return (error);
340 	return (null_nodeget(mp, *vpp, vpp));
341 }
342 
343 static int
344 nullfs_fhtovp(mp, fidp, flags, vpp)
345 	struct mount *mp;
346 	struct fid *fidp;
347 	int flags;
348 	struct vnode **vpp;
349 {
350 	int error;
351 
352 	error = VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, flags,
353 	    vpp);
354 	if (error != 0)
355 		return (error);
356 	return (null_nodeget(mp, *vpp, vpp));
357 }
358 
359 static int
360 nullfs_extattrctl(mp, cmd, filename_vp, namespace, attrname)
361 	struct mount *mp;
362 	int cmd;
363 	struct vnode *filename_vp;
364 	int namespace;
365 	const char *attrname;
366 {
367 
368 	return (VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd,
369 	    filename_vp, namespace, attrname));
370 }
371 
372 static void
373 nullfs_reclaim_lowervp(struct mount *mp, struct vnode *lowervp)
374 {
375 	struct vnode *vp;
376 
377 	vp = null_hashget(mp, lowervp);
378 	if (vp == NULL)
379 		return;
380 	vgone(vp);
381 	vn_lock(lowervp, LK_EXCLUSIVE | LK_RETRY);
382 }
383 
384 static struct vfsops null_vfsops = {
385 	.vfs_extattrctl =	nullfs_extattrctl,
386 	.vfs_fhtovp =		nullfs_fhtovp,
387 	.vfs_init =		nullfs_init,
388 	.vfs_mount =		nullfs_mount,
389 	.vfs_quotactl =		nullfs_quotactl,
390 	.vfs_root =		nullfs_root,
391 	.vfs_statfs =		nullfs_statfs,
392 	.vfs_sync =		nullfs_sync,
393 	.vfs_uninit =		nullfs_uninit,
394 	.vfs_unmount =		nullfs_unmount,
395 	.vfs_vget =		nullfs_vget,
396 	.vfs_reclaim_lowervp =	nullfs_reclaim_lowervp,
397 };
398 
399 VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL);
400