]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/miscfs/nullfs/null_vfsops.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / bsd / miscfs / nullfs / null_vfsops.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2019 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*-
25 * Portions Copyright (c) 1992, 1993, 1995
26 * The Regents of the University of California. All rights reserved.
27 *
28 * This code is derived from software donated to Berkeley by
29 * Jan-Simon Pendry.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53 * SUCH DAMAGE.
54 *
55 * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94
56 *
57 * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92
58 * $FreeBSD$
59 */
60
61#include <sys/param.h>
62#include <sys/systm.h>
63#include <sys/fcntl.h>
64#include <sys/kernel.h>
65#include <sys/lock.h>
66#include <sys/malloc.h>
67#include <sys/mount.h>
68#include <sys/namei.h>
69#include <sys/proc.h>
70#include <sys/vnode.h>
71#include <sys/vnode_internal.h>
72#include <security/mac_internal.h>
73#include <sys/kauth.h>
74
75#include <sys/param.h>
76
77#include <IOKit/IOBSD.h>
78
79#include "nullfs.h"
80
81#define NULLFS_ENTITLEMENT "com.apple.private.nullfs_allow"
82
83#define SIZEOF_MEMBER(type, member) (sizeof(((type *)0)->member))
84#define MAX_MNT_FROM_LENGTH (SIZEOF_MEMBER(struct vfsstatfs, f_mntfromname))
85
86static int
87nullfs_vfs_getlowerattr(mount_t mp, struct vfs_attr * vfap, vfs_context_t ctx)
88{
89 memset(vfap, 0, sizeof(*vfap));
90 VFSATTR_INIT(vfap);
91 VFSATTR_WANTED(vfap, f_bsize);
92 VFSATTR_WANTED(vfap, f_iosize);
93 VFSATTR_WANTED(vfap, f_blocks);
94 VFSATTR_WANTED(vfap, f_bfree);
95 VFSATTR_WANTED(vfap, f_bavail);
96 VFSATTR_WANTED(vfap, f_bused);
97 VFSATTR_WANTED(vfap, f_files);
98 VFSATTR_WANTED(vfap, f_ffree);
99 VFSATTR_WANTED(vfap, f_capabilities);
100
101 return vfs_getattr(mp, vfap, ctx);
102}
103
104/*
105 * Mount null layer
106 */
107static int
108nullfs_mount(struct mount * mp, __unused vnode_t devvp, user_addr_t user_data, vfs_context_t ctx)
109{
110 int error = 0;
111 struct vnode *lowerrootvp = NULL, *vp = NULL;
112 struct vfsstatfs * sp = NULL;
113 struct null_mount * xmp = NULL;
114 struct null_mount_conf conf = {0};
115 char path[MAXPATHLEN];
116
117 size_t count;
118 struct vfs_attr vfa;
119 /* set defaults (arbitrary since this file system is readonly) */
120 uint32_t bsize = BLKDEV_IOSIZE;
121 size_t iosize = BLKDEV_IOSIZE;
122 uint64_t blocks = 4711 * 4711;
123 uint64_t bfree = 0;
124 uint64_t bavail = 0;
125 uint64_t bused = 4711;
126 uint64_t files = 4711;
127 uint64_t ffree = 0;
128
129 kauth_cred_t cred = vfs_context_ucred(ctx);
130
131 NULLFSDEBUG("nullfs_mount(mp = %p) %llx\n", (void *)mp, vfs_flags(mp));
132
133 if (vfs_flags(mp) & MNT_ROOTFS) {
134 return EOPNOTSUPP;
135 }
136
137 /*
138 * Update is a no-op
139 */
140 if (vfs_isupdate(mp)) {
141 return ENOTSUP;
142 }
143
144 /* check entitlement */
145 if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT)) {
146 return EPERM;
147 }
148
149 /*
150 * Get configuration
151 */
152 error = copyin(user_data, &conf, sizeof(conf));
153 if (error) {
154 NULLFSDEBUG("nullfs: error copying configuration form user %d\n", error);
155 goto error;
156 }
157
158 /*
159 * Get argument
160 */
161 error = copyinstr(user_data + sizeof(conf), path, MAXPATHLEN - 1, &count);
162 if (error) {
163 NULLFSDEBUG("nullfs: error copying data form user %d\n", error);
164 goto error;
165 }
166
167 /* This could happen if the system is configured for 32 bit inodes instead of
168 * 64 bit */
169 if (count > MAX_MNT_FROM_LENGTH) {
170 error = EINVAL;
171 NULLFSDEBUG("nullfs: path to translocate too large for this system %ld vs %ld\n", count, MAX_MNT_FROM_LENGTH);
172 goto error;
173 }
174
175 error = vnode_lookup(path, 0, &lowerrootvp, ctx);
176 if (error) {
177 NULLFSDEBUG("lookup %s -> %d\n", path, error);
178 goto error;
179 }
180
181 /* lowervrootvp has an iocount after vnode_lookup, drop that for a usecount.
182 * Keep this to signal what we want to keep around the thing we are mirroring.
183 * Drop it in unmount.*/
184 error = vnode_ref(lowerrootvp);
185 vnode_put(lowerrootvp);
186 if (error) {
187 // If vnode_ref failed, then null it out so it can't be used anymore in cleanup.
188 lowerrootvp = NULL;
189 goto error;
190 }
191
192 NULLFSDEBUG("mount %s\n", path);
193
194 MALLOC(xmp, struct null_mount *, sizeof(*xmp), M_TEMP, M_WAITOK | M_ZERO);
195 if (xmp == NULL) {
196 error = ENOMEM;
197 goto error;
198 }
199
200 /*
201 * Grab the uid/gid of the caller, which may be used for unveil later
202 */
203 xmp->uid = kauth_cred_getuid(cred);
204 xmp->gid = kauth_cred_getgid(cred);
205
206 /*
207 * Save reference to underlying FS
208 */
209 xmp->nullm_lowerrootvp = lowerrootvp;
210 xmp->nullm_lowerrootvid = vnode_vid(lowerrootvp);
211
212 error = null_getnewvnode(mp, NULL, NULL, &vp, NULL, 1);
213 if (error) {
214 goto error;
215 }
216
217 /* vp has an iocount on it from vnode_create. drop that for a usecount. This
218 * is our root vnode so we drop the ref in unmount
219 *
220 * Assuming for now that because we created this vnode and we aren't finished mounting we can get a ref*/
221 vnode_ref(vp);
222 vnode_put(vp);
223
224 nullfs_init_lck(&xmp->nullm_lock);
225
226 xmp->nullm_rootvp = vp;
227
228 /* read the flags the user set, but then ignore some of them, we will only
229 * allow them if they are set on the lower file system */
230 uint64_t flags = vfs_flags(mp) & (~(MNT_IGNORE_OWNERSHIP | MNT_LOCAL));
231 uint64_t lowerflags = vfs_flags(vnode_mount(lowerrootvp)) & (MNT_LOCAL | MNT_QUARANTINE | MNT_IGNORE_OWNERSHIP | MNT_NOEXEC);
232
233 if (lowerflags) {
234 flags |= lowerflags;
235 }
236
237 /* force these flags */
238 flags |= (MNT_DONTBROWSE | MNT_MULTILABEL | MNT_NOSUID | MNT_RDONLY);
239 vfs_setflags(mp, flags);
240
241 vfs_setfsprivate(mp, xmp);
242 vfs_getnewfsid(mp);
243 vfs_setlocklocal(mp);
244
245 /* fill in the stat block */
246 sp = vfs_statfs(mp);
247 strlcpy(sp->f_mntfromname, path, MAX_MNT_FROM_LENGTH);
248
249 sp->f_flags = flags;
250
251 xmp->nullm_flags = NULLM_CASEINSENSITIVE; /* default to case insensitive */
252
253 // Set the flags that are requested
254 xmp->nullm_flags |= conf.flags & NULLM_UNVEIL;
255
256 error = nullfs_vfs_getlowerattr(vnode_mount(lowerrootvp), &vfa, ctx);
257 if (error == 0) {
258 if (VFSATTR_IS_SUPPORTED(&vfa, f_bsize)) {
259 bsize = vfa.f_bsize;
260 }
261 if (VFSATTR_IS_SUPPORTED(&vfa, f_iosize)) {
262 iosize = vfa.f_iosize;
263 }
264 if (VFSATTR_IS_SUPPORTED(&vfa, f_blocks)) {
265 blocks = vfa.f_blocks;
266 }
267 if (VFSATTR_IS_SUPPORTED(&vfa, f_bfree)) {
268 bfree = vfa.f_bfree;
269 }
270 if (VFSATTR_IS_SUPPORTED(&vfa, f_bavail)) {
271 bavail = vfa.f_bavail;
272 }
273 if (VFSATTR_IS_SUPPORTED(&vfa, f_bused)) {
274 bused = vfa.f_bused;
275 }
276 if (VFSATTR_IS_SUPPORTED(&vfa, f_files)) {
277 files = vfa.f_files;
278 }
279 if (VFSATTR_IS_SUPPORTED(&vfa, f_ffree)) {
280 ffree = vfa.f_ffree;
281 }
282 if (VFSATTR_IS_SUPPORTED(&vfa, f_capabilities)) {
283 if ((vfa.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] & (VOL_CAP_FMT_CASE_SENSITIVE)) &&
284 (vfa.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & (VOL_CAP_FMT_CASE_SENSITIVE))) {
285 xmp->nullm_flags &= ~NULLM_CASEINSENSITIVE;
286 }
287 }
288 } else {
289 goto error;
290 }
291
292 sp->f_bsize = bsize;
293 sp->f_iosize = iosize;
294 sp->f_blocks = blocks;
295 sp->f_bfree = bfree;
296 sp->f_bavail = bavail;
297 sp->f_bused = bused;
298 sp->f_files = files;
299 sp->f_ffree = ffree;
300
301 /* Associate the mac label information from the mirrored filesystem with the
302 * mirror */
303 MAC_PERFORM(mount_label_associate, cred, vnode_mount(lowerrootvp), vfs_mntlabel(mp));
304
305 NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", sp->f_mntfromname, sp->f_mntonname);
306 return 0;
307
308error:
309 if (xmp) {
310 FREE(xmp, M_TEMP);
311 }
312 if (lowerrootvp) {
313 vnode_getwithref(lowerrootvp);
314 vnode_rele(lowerrootvp);
315 vnode_put(lowerrootvp);
316 }
317 if (vp) {
318 /* we made the root vnode but the mount is failed, so clean it up */
319 vnode_getwithref(vp);
320 vnode_rele(vp);
321 /* give vp back */
322 vnode_recycle(vp);
323 vnode_put(vp);
324 }
325 return error;
326}
327
328/*
329 * Free reference to null layer
330 */
331static int
332nullfs_unmount(struct mount * mp, int mntflags, __unused vfs_context_t ctx)
333{
334 struct null_mount * mntdata;
335 struct vnode * vp;
336 int error, flags;
337
338 NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp);
339
340 /* check entitlement or superuser*/
341 if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT) &&
342 vfs_context_suser(ctx) != 0) {
343 return EPERM;
344 }
345
346 if (mntflags & MNT_FORCE) {
347 flags = FORCECLOSE;
348 } else {
349 flags = 0;
350 }
351
352 mntdata = MOUNTTONULLMOUNT(mp);
353 vp = mntdata->nullm_rootvp;
354
355 // release our reference on the root before flushing.
356 // it will get pulled out of the mount structure by reclaim
357 vnode_getalways(vp);
358
359 error = vflush(mp, vp, flags);
360 if (error) {
361 vnode_put(vp);
362 return error;
363 }
364
365 if (vnode_isinuse(vp, 1) && flags == 0) {
366 vnode_put(vp);
367 return EBUSY;
368 }
369
370 vnode_rele(vp); // Drop reference taken by nullfs_mount
371 vnode_put(vp); // Drop ref taken above
372
373 //Force close to get rid of the last vnode
374 (void)vflush(mp, NULL, FORCECLOSE);
375
376 /* no more vnodes, so tear down the mountpoint */
377
378 lck_mtx_lock(&mntdata->nullm_lock);
379
380 vfs_setfsprivate(mp, NULL);
381
382 vnode_getalways(mntdata->nullm_lowerrootvp);
383 vnode_rele(mntdata->nullm_lowerrootvp);
384 vnode_put(mntdata->nullm_lowerrootvp);
385
386 lck_mtx_unlock(&mntdata->nullm_lock);
387
388 nullfs_destroy_lck(&mntdata->nullm_lock);
389
390 FREE(mntdata, M_TEMP);
391
392 uint64_t vflags = vfs_flags(mp);
393 vfs_setflags(mp, vflags & ~MNT_LOCAL);
394
395 return 0;
396}
397
398static int
399nullfs_root(struct mount * mp, struct vnode ** vpp, __unused vfs_context_t ctx)
400{
401 struct vnode * vp;
402 int error;
403
404 NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", (void *)mp, (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp);
405
406 /*
407 * Return locked reference to root.
408 */
409 vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
410
411 error = vnode_get(vp);
412 if (error) {
413 return error;
414 }
415
416 *vpp = vp;
417 return 0;
418}
419
420static int
421nullfs_vfs_getattr(struct mount * mp, struct vfs_attr * vfap, vfs_context_t ctx)
422{
423 struct vnode * coveredvp = NULL;
424 struct vfs_attr vfa;
425 struct null_mount * null_mp = MOUNTTONULLMOUNT(mp);
426 vol_capabilities_attr_t capabilities;
427 struct vfsstatfs * sp = vfs_statfs(mp);
428 vfs_context_t ectx = nullfs_get_patched_context(null_mp, ctx);
429
430 struct timespec tzero = {.tv_sec = 0, .tv_nsec = 0};
431
432 NULLFSDEBUG("%s\n", __FUNCTION__);
433
434 /* Set default capabilities in case the lower file system is gone */
435 memset(&capabilities, 0, sizeof(capabilities));
436 capabilities.capabilities[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_FAST_STATFS | VOL_CAP_FMT_HIDDEN_FILES;
437 capabilities.valid[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_FAST_STATFS | VOL_CAP_FMT_HIDDEN_FILES;
438
439 if (nullfs_vfs_getlowerattr(vnode_mount(null_mp->nullm_lowerrootvp), &vfa, ectx) == 0) {
440 if (VFSATTR_IS_SUPPORTED(&vfa, f_capabilities)) {
441 memcpy(&capabilities, &vfa.f_capabilities, sizeof(capabilities));
442 /* don't support vget */
443 capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_PATH_FROM_ID);
444
445 capabilities.capabilities[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_HIDDEN_FILES; /* Always support UF_HIDDEN */
446
447 capabilities.valid[VOL_CAPABILITIES_FORMAT] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_PATH_FROM_ID);
448
449 capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_HIDDEN_FILES; /* Always support UF_HIDDEN */
450
451 /* dont' support interfaces that only make sense on a writable file system
452 * or one with specific vnops implemented */
453 capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = 0;
454
455 capabilities.valid[VOL_CAPABILITIES_INTERFACES] &=
456 ~(VOL_CAP_INT_SEARCHFS | VOL_CAP_INT_ATTRLIST | VOL_CAP_INT_READDIRATTR | VOL_CAP_INT_EXCHANGEDATA |
457 VOL_CAP_INT_COPYFILE | VOL_CAP_INT_ALLOCATE | VOL_CAP_INT_VOL_RENAME | VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK);
458 }
459 }
460
461 if (VFSATTR_IS_ACTIVE(vfap, f_create_time)) {
462 VFSATTR_RETURN(vfap, f_create_time, tzero);
463 }
464
465 if (VFSATTR_IS_ACTIVE(vfap, f_modify_time)) {
466 VFSATTR_RETURN(vfap, f_modify_time, tzero);
467 }
468
469 if (VFSATTR_IS_ACTIVE(vfap, f_access_time)) {
470 VFSATTR_RETURN(vfap, f_access_time, tzero);
471 }
472
473 if (VFSATTR_IS_ACTIVE(vfap, f_bsize)) {
474 VFSATTR_RETURN(vfap, f_bsize, sp->f_bsize);
475 }
476
477 if (VFSATTR_IS_ACTIVE(vfap, f_iosize)) {
478 VFSATTR_RETURN(vfap, f_iosize, sp->f_iosize);
479 }
480
481 if (VFSATTR_IS_ACTIVE(vfap, f_owner)) {
482 VFSATTR_RETURN(vfap, f_owner, 0);
483 }
484
485 if (VFSATTR_IS_ACTIVE(vfap, f_blocks)) {
486 VFSATTR_RETURN(vfap, f_blocks, sp->f_blocks);
487 }
488
489 if (VFSATTR_IS_ACTIVE(vfap, f_bfree)) {
490 VFSATTR_RETURN(vfap, f_bfree, sp->f_bfree);
491 }
492
493 if (VFSATTR_IS_ACTIVE(vfap, f_bavail)) {
494 VFSATTR_RETURN(vfap, f_bavail, sp->f_bavail);
495 }
496
497 if (VFSATTR_IS_ACTIVE(vfap, f_bused)) {
498 VFSATTR_RETURN(vfap, f_bused, sp->f_bused);
499 }
500
501 if (VFSATTR_IS_ACTIVE(vfap, f_files)) {
502 VFSATTR_RETURN(vfap, f_files, sp->f_files);
503 }
504
505 if (VFSATTR_IS_ACTIVE(vfap, f_ffree)) {
506 VFSATTR_RETURN(vfap, f_ffree, sp->f_ffree);
507 }
508
509 if (VFSATTR_IS_ACTIVE(vfap, f_fssubtype)) {
510 VFSATTR_RETURN(vfap, f_fssubtype, 0);
511 }
512
513 if (VFSATTR_IS_ACTIVE(vfap, f_capabilities)) {
514 memcpy(&vfap->f_capabilities, &capabilities, sizeof(vol_capabilities_attr_t));
515
516 VFSATTR_SET_SUPPORTED(vfap, f_capabilities);
517 }
518
519 if (VFSATTR_IS_ACTIVE(vfap, f_attributes)) {
520 vol_attributes_attr_t * volattr = &vfap->f_attributes;
521
522 volattr->validattr.commonattr = 0;
523 volattr->validattr.volattr = ATTR_VOL_NAME | ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES;
524 volattr->validattr.dirattr = 0;
525 volattr->validattr.fileattr = 0;
526 volattr->validattr.forkattr = 0;
527
528 volattr->nativeattr.commonattr = 0;
529 volattr->nativeattr.volattr = ATTR_VOL_NAME | ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES;
530 volattr->nativeattr.dirattr = 0;
531 volattr->nativeattr.fileattr = 0;
532 volattr->nativeattr.forkattr = 0;
533
534 VFSATTR_SET_SUPPORTED(vfap, f_attributes);
535 }
536
537 if (VFSATTR_IS_ACTIVE(vfap, f_vol_name)) {
538 /* The name of the volume is the same as the directory we mounted on */
539 coveredvp = vfs_vnodecovered(mp);
540 if (coveredvp) {
541 const char * name = vnode_getname_printable(coveredvp);
542 strlcpy(vfap->f_vol_name, name, MAXPATHLEN);
543 vnode_putname_printable(name);
544
545 VFSATTR_SET_SUPPORTED(vfap, f_vol_name);
546 vnode_put(coveredvp);
547 }
548 }
549
550 nullfs_cleanup_patched_context(null_mp, ectx);
551
552 return 0;
553}
554
555static int
556nullfs_sync(__unused struct mount * mp, __unused int waitfor, __unused vfs_context_t ctx)
557{
558 /*
559 * XXX - Assumes no data cached at null layer.
560 */
561 return 0;
562}
563
564
565
566static int
567nullfs_vfs_start(__unused struct mount * mp, __unused int flags, __unused vfs_context_t ctx)
568{
569 NULLFSDEBUG("%s\n", __FUNCTION__);
570 return 0;
571}
572
573extern const struct vnodeopv_desc nullfs_vnodeop_opv_desc;
574
575const struct vnodeopv_desc * nullfs_vnodeopv_descs[] = {
576 &nullfs_vnodeop_opv_desc,
577};
578
579struct vfsops nullfs_vfsops = {
580 .vfs_mount = nullfs_mount,
581 .vfs_unmount = nullfs_unmount,
582 .vfs_start = nullfs_vfs_start,
583 .vfs_root = nullfs_root,
584 .vfs_getattr = nullfs_vfs_getattr,
585 .vfs_sync = nullfs_sync,
586 .vfs_init = nullfs_init,
587 .vfs_sysctl = NULL,
588 .vfs_setattr = NULL,
589};