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