]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/nullfs/null_vfsops.c
MFV 364468:
[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;
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);
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 pointer to underlying FS and the reference to the
162          * lower root vnode.
163          */
164         xmp->nullm_vfs = lowerrootvp->v_mount;
165         vref(lowerrootvp);
166         xmp->nullm_lowerrootvp = lowerrootvp;
167         mp->mnt_data = xmp;
168
169         /*
170          * Make sure the node alias worked.
171          */
172         error = null_nodeget(mp, lowerrootvp, &nullm_rootvp);
173         if (error != 0) {
174                 vrele(lowerrootvp);
175                 free(xmp, M_NULLFSMNT);
176                 return (error);
177         }
178
179         if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) {
180                 MNT_ILOCK(mp);
181                 mp->mnt_flag |= MNT_LOCAL;
182                 MNT_IUNLOCK(mp);
183         }
184
185         xmp->nullm_flags |= NULLM_CACHE;
186         if (vfs_getopt(mp->mnt_optnew, "nocache", NULL, NULL) == 0 ||
187             (xmp->nullm_vfs->mnt_kern_flag & MNTK_NULL_NOCACHE) != 0)
188                 xmp->nullm_flags &= ~NULLM_CACHE;
189
190         MNT_ILOCK(mp);
191         if ((xmp->nullm_flags & NULLM_CACHE) != 0) {
192                 mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag &
193                     (MNTK_SHARED_WRITES | MNTK_LOOKUP_SHARED |
194                     MNTK_EXTENDED_SHARED);
195         }
196         mp->mnt_kern_flag |= MNTK_LOOKUP_EXCL_DOTDOT | MNTK_NOMSYNC;
197         mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag &
198             (MNTK_USES_BCACHE | MNTK_NO_IOPF | MNTK_UNMAPPED_BUFS);
199         MNT_IUNLOCK(mp);
200         vfs_getnewfsid(mp);
201         if ((xmp->nullm_flags & NULLM_CACHE) != 0) {
202                 MNT_ILOCK(xmp->nullm_vfs);
203                 TAILQ_INSERT_TAIL(&xmp->nullm_vfs->mnt_uppers, mp,
204                     mnt_upper_link);
205                 MNT_IUNLOCK(xmp->nullm_vfs);
206         }
207
208         vfs_mountedfrom(mp, target);
209         vput(nullm_rootvp);
210
211         NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n",
212                 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
213         return (0);
214 }
215
216 /*
217  * Free reference to null layer
218  */
219 static int
220 nullfs_unmount(mp, mntflags)
221         struct mount *mp;
222         int mntflags;
223 {
224         struct null_mount *mntdata;
225         struct mount *ump;
226         int error, flags;
227
228         NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp);
229
230         if (mntflags & MNT_FORCE)
231                 flags = FORCECLOSE;
232         else
233                 flags = 0;
234
235         for (;;) {
236                 /* There is 1 extra root vnode reference (nullm_rootvp). */
237                 error = vflush(mp, 0, flags, curthread);
238                 if (error)
239                         return (error);
240                 MNT_ILOCK(mp);
241                 if (mp->mnt_nvnodelistsize == 0) {
242                         MNT_IUNLOCK(mp);
243                         break;
244                 }
245                 MNT_IUNLOCK(mp);
246                 if ((mntflags & MNT_FORCE) == 0)
247                         return (EBUSY);
248         }
249
250         /*
251          * Finally, throw away the null_mount structure
252          */
253         mntdata = mp->mnt_data;
254         ump = mntdata->nullm_vfs;
255         if ((mntdata->nullm_flags & NULLM_CACHE) != 0) {
256                 MNT_ILOCK(ump);
257                 while ((ump->mnt_kern_flag & MNTK_VGONE_UPPER) != 0) {
258                         ump->mnt_kern_flag |= MNTK_VGONE_WAITER;
259                         msleep(&ump->mnt_uppers, &ump->mnt_mtx, 0, "vgnupw", 0);
260                 }
261                 TAILQ_REMOVE(&ump->mnt_uppers, mp, mnt_upper_link);
262                 MNT_IUNLOCK(ump);
263         }
264         vrele(mntdata->nullm_lowerrootvp);
265         mp->mnt_data = NULL;
266         free(mntdata, M_NULLFSMNT);
267         return (0);
268 }
269
270 static int
271 nullfs_root(mp, flags, vpp)
272         struct mount *mp;
273         int flags;
274         struct vnode **vpp;
275 {
276         struct vnode *vp;
277         struct null_mount *mntdata;
278         int error;
279
280         mntdata = MOUNTTONULLMOUNT(mp);
281         NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", mp,
282             mntdata->nullm_lowerrootvp);
283
284         error = vget(mntdata->nullm_lowerrootvp, flags);
285         if (error == 0) {
286                 error = null_nodeget(mp, mntdata->nullm_lowerrootvp, &vp);
287                 if (error == 0) {
288                         *vpp = vp;
289                 }
290         }
291         return (error);
292 }
293
294 static int
295 nullfs_quotactl(mp, cmd, uid, arg)
296         struct mount *mp;
297         int cmd;
298         uid_t uid;
299         void *arg;
300 {
301         return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg);
302 }
303
304 static int
305 nullfs_statfs(mp, sbp)
306         struct mount *mp;
307         struct statfs *sbp;
308 {
309         int error;
310         struct statfs *mstat;
311
312         NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp,
313             (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp,
314             (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp));
315
316         mstat = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK | M_ZERO);
317
318         error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, mstat);
319         if (error) {
320                 free(mstat, M_STATFS);
321                 return (error);
322         }
323
324         /* now copy across the "interesting" information and fake the rest */
325         sbp->f_type = mstat->f_type;
326         sbp->f_flags = (sbp->f_flags & (MNT_RDONLY | MNT_NOEXEC | MNT_NOSUID |
327             MNT_UNION | MNT_NOSYMFOLLOW | MNT_AUTOMOUNTED)) |
328             (mstat->f_flags & ~(MNT_ROOTFS | MNT_AUTOMOUNTED));
329         sbp->f_bsize = mstat->f_bsize;
330         sbp->f_iosize = mstat->f_iosize;
331         sbp->f_blocks = mstat->f_blocks;
332         sbp->f_bfree = mstat->f_bfree;
333         sbp->f_bavail = mstat->f_bavail;
334         sbp->f_files = mstat->f_files;
335         sbp->f_ffree = mstat->f_ffree;
336
337         free(mstat, M_STATFS);
338         return (0);
339 }
340
341 static int
342 nullfs_sync(mp, waitfor)
343         struct mount *mp;
344         int waitfor;
345 {
346         /*
347          * XXX - Assumes no data cached at null layer.
348          */
349         return (0);
350 }
351
352 static int
353 nullfs_vget(mp, ino, flags, vpp)
354         struct mount *mp;
355         ino_t ino;
356         int flags;
357         struct vnode **vpp;
358 {
359         int error;
360
361         KASSERT((flags & LK_TYPE_MASK) != 0,
362             ("nullfs_vget: no lock requested"));
363
364         error = VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, flags, vpp);
365         if (error != 0)
366                 return (error);
367         return (null_nodeget(mp, *vpp, vpp));
368 }
369
370 static int
371 nullfs_fhtovp(mp, fidp, flags, vpp)
372         struct mount *mp;
373         struct fid *fidp;
374         int flags;
375         struct vnode **vpp;
376 {
377         int error;
378
379         error = VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, flags,
380             vpp);
381         if (error != 0)
382                 return (error);
383         return (null_nodeget(mp, *vpp, vpp));
384 }
385
386 static int                        
387 nullfs_extattrctl(mp, cmd, filename_vp, namespace, attrname)
388         struct mount *mp;
389         int cmd;
390         struct vnode *filename_vp;
391         int namespace;
392         const char *attrname;
393 {
394
395         return (VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd,
396             filename_vp, namespace, attrname));
397 }
398
399 static void
400 nullfs_reclaim_lowervp(struct mount *mp, struct vnode *lowervp)
401 {
402         struct vnode *vp;
403
404         vp = null_hashget(mp, lowervp);
405         if (vp == NULL)
406                 return;
407         VTONULL(vp)->null_flags |= NULLV_NOUNLOCK;
408         vgone(vp);
409         vput(vp);
410 }
411
412 static void
413 nullfs_unlink_lowervp(struct mount *mp, struct vnode *lowervp)
414 {
415         struct vnode *vp;
416         struct null_node *xp;
417
418         vp = null_hashget(mp, lowervp);
419         if (vp == NULL)
420                 return;
421         xp = VTONULL(vp);
422         xp->null_flags |= NULLV_DROP | NULLV_NOUNLOCK;
423         vhold(vp);
424         vunref(vp);
425
426         if (vp->v_usecount == 0) {
427                 /*
428                  * If vunref() dropped the last use reference on the
429                  * nullfs vnode, it must be reclaimed, and its lock
430                  * was split from the lower vnode lock.  Need to do
431                  * extra unlock before allowing the final vdrop() to
432                  * free the vnode.
433                  */
434                 KASSERT(VN_IS_DOOMED(vp),
435                     ("not reclaimed nullfs vnode %p", vp));
436                 VOP_UNLOCK(vp);
437         } else {
438                 /*
439                  * Otherwise, the nullfs vnode still shares the lock
440                  * with the lower vnode, and must not be unlocked.
441                  * Also clear the NULLV_NOUNLOCK, the flag is not
442                  * relevant for future reclamations.
443                  */
444                 ASSERT_VOP_ELOCKED(vp, "unlink_lowervp");
445                 KASSERT(!VN_IS_DOOMED(vp),
446                     ("reclaimed nullfs vnode %p", vp));
447                 xp->null_flags &= ~NULLV_NOUNLOCK;
448         }
449         vdrop(vp);
450 }
451
452 static struct vfsops null_vfsops = {
453         .vfs_extattrctl =       nullfs_extattrctl,
454         .vfs_fhtovp =           nullfs_fhtovp,
455         .vfs_init =             nullfs_init,
456         .vfs_mount =            nullfs_mount,
457         .vfs_quotactl =         nullfs_quotactl,
458         .vfs_root =             nullfs_root,
459         .vfs_statfs =           nullfs_statfs,
460         .vfs_sync =             nullfs_sync,
461         .vfs_uninit =           nullfs_uninit,
462         .vfs_unmount =          nullfs_unmount,
463         .vfs_vget =             nullfs_vget,
464         .vfs_reclaim_lowervp =  nullfs_reclaim_lowervp,
465         .vfs_unlink_lowervp =   nullfs_unlink_lowervp,
466 };
467
468 VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL);