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