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