xref: /xnu-11215.81.4/bsd/miscfs/nullfs/null_vfsops.c (revision d4514f0bc1d3f944c22d92e68b646ac3fb40d452)
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 
86 static int
nullfs_vfs_getlowerattr(mount_t mp,struct vfs_attr * vfap,vfs_context_t ctx)87 nullfs_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  */
107 static int
nullfs_mount(struct mount * mp,__unused vnode_t devvp,user_addr_t user_data,vfs_context_t ctx)108 nullfs_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 (!IOCurrentTaskHasEntitlement(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 	xmp = kalloc_type(struct null_mount, Z_WAITOK | Z_ZERO | Z_NOFAIL);
195 
196 	/*
197 	 * Grab the uid/gid of the caller, which may be used for unveil later
198 	 */
199 	xmp->uid = kauth_cred_getuid(cred);
200 	xmp->gid = kauth_cred_getgid(cred);
201 
202 	/*
203 	 * Save reference to underlying FS
204 	 */
205 	xmp->nullm_lowerrootvp  = lowerrootvp;
206 	xmp->nullm_lowerrootvid = vnode_vid(lowerrootvp);
207 
208 	error = null_getnewvnode(mp, NULL, NULL, &vp, NULL, 1);
209 	if (error) {
210 		goto error;
211 	}
212 
213 	/* vp has an iocount on it from vnode_create. drop that for a usecount. This
214 	 * is our root vnode so we drop the ref in unmount
215 	 *
216 	 * Assuming for now that because we created this vnode and we aren't finished mounting we can get a ref*/
217 	vnode_ref(vp);
218 	vnode_put(vp);
219 
220 	nullfs_init_lck(&xmp->nullm_lock);
221 
222 	xmp->nullm_rootvp = vp;
223 
224 	/* read the flags the user set, but then ignore some of them, we will only
225 	 *  allow them if they are set on the lower file system */
226 	uint64_t flags      = vfs_flags(mp) & (~(MNT_IGNORE_OWNERSHIP | MNT_LOCAL));
227 	uint64_t lowerflags = vfs_flags(vnode_mount(lowerrootvp)) & (MNT_LOCAL | MNT_QUARANTINE | MNT_IGNORE_OWNERSHIP | MNT_NOEXEC);
228 
229 	if (lowerflags) {
230 		flags |= lowerflags;
231 	}
232 
233 	/* force these flags */
234 	flags |= (MNT_DONTBROWSE | MNT_MULTILABEL | MNT_NOSUID | MNT_RDONLY);
235 	vfs_setflags(mp, flags);
236 
237 	vfs_setfsprivate(mp, xmp);
238 	vfs_getnewfsid(mp);
239 	vfs_setlocklocal(mp);
240 
241 	/* fill in the stat block */
242 	sp = vfs_statfs(mp);
243 	strlcpy(sp->f_mntfromname, path, MAX_MNT_FROM_LENGTH);
244 
245 	sp->f_flags = flags;
246 
247 	xmp->nullm_flags = NULLM_CASEINSENSITIVE; /* default to case insensitive */
248 
249 	// Set the flags that are requested
250 	xmp->nullm_flags |= conf.flags & NULLM_UNVEIL;
251 
252 	error = nullfs_vfs_getlowerattr(vnode_mount(lowerrootvp), &vfa, ctx);
253 	if (error == 0) {
254 		if (VFSATTR_IS_SUPPORTED(&vfa, f_bsize)) {
255 			bsize = vfa.f_bsize;
256 		}
257 		if (VFSATTR_IS_SUPPORTED(&vfa, f_iosize)) {
258 			iosize = vfa.f_iosize;
259 		}
260 		if (VFSATTR_IS_SUPPORTED(&vfa, f_blocks)) {
261 			blocks = vfa.f_blocks;
262 		}
263 		if (VFSATTR_IS_SUPPORTED(&vfa, f_bfree)) {
264 			bfree = vfa.f_bfree;
265 		}
266 		if (VFSATTR_IS_SUPPORTED(&vfa, f_bavail)) {
267 			bavail = vfa.f_bavail;
268 		}
269 		if (VFSATTR_IS_SUPPORTED(&vfa, f_bused)) {
270 			bused = vfa.f_bused;
271 		}
272 		if (VFSATTR_IS_SUPPORTED(&vfa, f_files)) {
273 			files = vfa.f_files;
274 		}
275 		if (VFSATTR_IS_SUPPORTED(&vfa, f_ffree)) {
276 			ffree = vfa.f_ffree;
277 		}
278 		if (VFSATTR_IS_SUPPORTED(&vfa, f_capabilities)) {
279 			if ((vfa.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] & (VOL_CAP_FMT_CASE_SENSITIVE)) &&
280 			    (vfa.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & (VOL_CAP_FMT_CASE_SENSITIVE))) {
281 				xmp->nullm_flags &= ~NULLM_CASEINSENSITIVE;
282 			}
283 		}
284 	} else {
285 		goto error;
286 	}
287 
288 	sp->f_bsize  = bsize;
289 	sp->f_iosize = iosize;
290 	sp->f_blocks = blocks;
291 	sp->f_bfree  = bfree;
292 	sp->f_bavail = bavail;
293 	sp->f_bused  = bused;
294 	sp->f_files  = files;
295 	sp->f_ffree  = ffree;
296 
297 	/* Associate the mac label information from the mirrored filesystem with the
298 	 * mirror */
299 	MAC_PERFORM(mount_label_associate, cred, vnode_mount(lowerrootvp), vfs_mntlabel(mp));
300 
301 	NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", sp->f_mntfromname, sp->f_mntonname);
302 	return 0;
303 
304 error:
305 	if (xmp) {
306 		kfree_type(struct null_mount, xmp);
307 	}
308 	if (lowerrootvp) {
309 		vnode_getwithref(lowerrootvp);
310 		vnode_rele(lowerrootvp);
311 		vnode_put(lowerrootvp);
312 	}
313 	if (vp) {
314 		/* we made the root vnode but the mount is failed, so clean it up */
315 		vnode_getwithref(vp);
316 		vnode_rele(vp);
317 		/* give vp back */
318 		vnode_recycle(vp);
319 		vnode_put(vp);
320 	}
321 	return error;
322 }
323 
324 /*
325  * Free reference to null layer
326  */
327 static int
nullfs_unmount(struct mount * mp,int mntflags,__unused vfs_context_t ctx)328 nullfs_unmount(struct mount * mp, int mntflags, __unused vfs_context_t ctx)
329 {
330 	struct null_mount * mntdata;
331 	struct vnode * vp;
332 	int error, flags;
333 
334 	NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp);
335 
336 	/* check entitlement or superuser*/
337 	if (!IOCurrentTaskHasEntitlement(NULLFS_ENTITLEMENT) &&
338 	    vfs_context_suser(ctx) != 0) {
339 		return EPERM;
340 	}
341 
342 	if (mntflags & MNT_FORCE) {
343 		flags = FORCECLOSE;
344 	} else {
345 		flags = 0;
346 	}
347 
348 	mntdata = MOUNTTONULLMOUNT(mp);
349 	vp      = mntdata->nullm_rootvp;
350 
351 	// release our reference on the root before flushing.
352 	// it will get pulled out of the mount structure by reclaim
353 	vnode_getalways(vp);
354 
355 	error = vflush(mp, vp, flags);
356 	if (error) {
357 		vnode_put(vp);
358 		return error;
359 	}
360 
361 	if (vnode_isinuse(vp, 1) && flags == 0) {
362 		vnode_put(vp);
363 		return EBUSY;
364 	}
365 
366 	vnode_rele(vp); // Drop reference taken by nullfs_mount
367 	vnode_put(vp); // Drop ref taken above
368 
369 	//Force close to get rid of the last vnode
370 	(void)vflush(mp, NULL, FORCECLOSE);
371 
372 	/* no more vnodes, so tear down the mountpoint */
373 
374 	lck_mtx_lock(&mntdata->nullm_lock);
375 
376 	vfs_setfsprivate(mp, NULL);
377 
378 	vnode_getalways(mntdata->nullm_lowerrootvp);
379 	vnode_rele(mntdata->nullm_lowerrootvp);
380 	vnode_put(mntdata->nullm_lowerrootvp);
381 
382 	lck_mtx_unlock(&mntdata->nullm_lock);
383 
384 	nullfs_destroy_lck(&mntdata->nullm_lock);
385 
386 	kfree_type(struct null_mount, mntdata);
387 
388 	uint64_t vflags = vfs_flags(mp);
389 	vfs_setflags(mp, vflags & ~MNT_LOCAL);
390 
391 	return 0;
392 }
393 
394 static int
nullfs_root(struct mount * mp,struct vnode ** vpp,__unused vfs_context_t ctx)395 nullfs_root(struct mount * mp, struct vnode ** vpp, __unused vfs_context_t ctx)
396 {
397 	struct vnode * vp;
398 	int error;
399 
400 	NULLFSDEBUG("nullfs_root(mp = %p, vp = %p)\n", (void *)mp, (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp);
401 
402 	/*
403 	 * Return locked reference to root.
404 	 */
405 	vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
406 
407 	error = vnode_get(vp);
408 	if (error) {
409 		return error;
410 	}
411 
412 	*vpp = vp;
413 	return 0;
414 }
415 
416 static int
nullfs_vfs_getattr(struct mount * mp,struct vfs_attr * vfap,vfs_context_t ctx)417 nullfs_vfs_getattr(struct mount * mp, struct vfs_attr * vfap, vfs_context_t ctx)
418 {
419 	struct vnode * coveredvp = NULL;
420 	struct vfs_attr vfa;
421 	struct null_mount * null_mp = MOUNTTONULLMOUNT(mp);
422 	vol_capabilities_attr_t capabilities;
423 	struct vfsstatfs * sp = vfs_statfs(mp);
424 	vfs_context_t ectx = nullfs_get_patched_context(null_mp, ctx);
425 
426 	struct timespec tzero = {.tv_sec = 0, .tv_nsec = 0};
427 
428 	NULLFSDEBUG("%s\n", __FUNCTION__);
429 
430 	/* Set default capabilities in case the lower file system is gone */
431 	memset(&capabilities, 0, sizeof(capabilities));
432 	capabilities.capabilities[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_FAST_STATFS | VOL_CAP_FMT_HIDDEN_FILES;
433 	capabilities.valid[VOL_CAPABILITIES_FORMAT]        = VOL_CAP_FMT_FAST_STATFS | VOL_CAP_FMT_HIDDEN_FILES;
434 
435 	if (nullfs_vfs_getlowerattr(vnode_mount(null_mp->nullm_lowerrootvp), &vfa, ectx) == 0) {
436 		if (VFSATTR_IS_SUPPORTED(&vfa, f_capabilities)) {
437 			memcpy(&capabilities, &vfa.f_capabilities, sizeof(capabilities));
438 			/* don't support vget */
439 			capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_PATH_FROM_ID);
440 
441 			capabilities.capabilities[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_HIDDEN_FILES; /* Always support UF_HIDDEN */
442 
443 			capabilities.valid[VOL_CAPABILITIES_FORMAT] &= ~(VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_PATH_FROM_ID);
444 
445 			capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_HIDDEN_FILES; /* Always support UF_HIDDEN */
446 
447 			/* dont' support interfaces that only make sense on a writable file system
448 			 * or one with specific vnops implemented */
449 			capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = 0;
450 
451 			capabilities.valid[VOL_CAPABILITIES_INTERFACES] &=
452 			    ~(VOL_CAP_INT_SEARCHFS | VOL_CAP_INT_ATTRLIST | VOL_CAP_INT_READDIRATTR | VOL_CAP_INT_EXCHANGEDATA |
453 			    VOL_CAP_INT_COPYFILE | VOL_CAP_INT_ALLOCATE | VOL_CAP_INT_VOL_RENAME | VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK);
454 		}
455 	}
456 
457 	if (VFSATTR_IS_ACTIVE(vfap, f_create_time)) {
458 		VFSATTR_RETURN(vfap, f_create_time, tzero);
459 	}
460 
461 	if (VFSATTR_IS_ACTIVE(vfap, f_modify_time)) {
462 		VFSATTR_RETURN(vfap, f_modify_time, tzero);
463 	}
464 
465 	if (VFSATTR_IS_ACTIVE(vfap, f_access_time)) {
466 		VFSATTR_RETURN(vfap, f_access_time, tzero);
467 	}
468 
469 	if (VFSATTR_IS_ACTIVE(vfap, f_bsize)) {
470 		VFSATTR_RETURN(vfap, f_bsize, sp->f_bsize);
471 	}
472 
473 	if (VFSATTR_IS_ACTIVE(vfap, f_iosize)) {
474 		VFSATTR_RETURN(vfap, f_iosize, sp->f_iosize);
475 	}
476 
477 	if (VFSATTR_IS_ACTIVE(vfap, f_owner)) {
478 		VFSATTR_RETURN(vfap, f_owner, 0);
479 	}
480 
481 	if (VFSATTR_IS_ACTIVE(vfap, f_blocks)) {
482 		VFSATTR_RETURN(vfap, f_blocks, sp->f_blocks);
483 	}
484 
485 	if (VFSATTR_IS_ACTIVE(vfap, f_bfree)) {
486 		VFSATTR_RETURN(vfap, f_bfree, sp->f_bfree);
487 	}
488 
489 	if (VFSATTR_IS_ACTIVE(vfap, f_bavail)) {
490 		VFSATTR_RETURN(vfap, f_bavail, sp->f_bavail);
491 	}
492 
493 	if (VFSATTR_IS_ACTIVE(vfap, f_bused)) {
494 		VFSATTR_RETURN(vfap, f_bused, sp->f_bused);
495 	}
496 
497 	if (VFSATTR_IS_ACTIVE(vfap, f_files)) {
498 		VFSATTR_RETURN(vfap, f_files, sp->f_files);
499 	}
500 
501 	if (VFSATTR_IS_ACTIVE(vfap, f_ffree)) {
502 		VFSATTR_RETURN(vfap, f_ffree, sp->f_ffree);
503 	}
504 
505 	if (VFSATTR_IS_ACTIVE(vfap, f_fssubtype)) {
506 		VFSATTR_RETURN(vfap, f_fssubtype, 0);
507 	}
508 
509 	if (VFSATTR_IS_ACTIVE(vfap, f_capabilities)) {
510 		memcpy(&vfap->f_capabilities, &capabilities, sizeof(vol_capabilities_attr_t));
511 
512 		VFSATTR_SET_SUPPORTED(vfap, f_capabilities);
513 	}
514 
515 	if (VFSATTR_IS_ACTIVE(vfap, f_attributes)) {
516 		vol_attributes_attr_t * volattr = &vfap->f_attributes;
517 
518 		volattr->validattr.commonattr = 0;
519 		volattr->validattr.volattr    = ATTR_VOL_NAME | ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES;
520 		volattr->validattr.dirattr    = 0;
521 		volattr->validattr.fileattr   = 0;
522 		volattr->validattr.forkattr   = 0;
523 
524 		volattr->nativeattr.commonattr = 0;
525 		volattr->nativeattr.volattr    = ATTR_VOL_NAME | ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES;
526 		volattr->nativeattr.dirattr    = 0;
527 		volattr->nativeattr.fileattr   = 0;
528 		volattr->nativeattr.forkattr   = 0;
529 
530 		VFSATTR_SET_SUPPORTED(vfap, f_attributes);
531 	}
532 
533 	if (VFSATTR_IS_ACTIVE(vfap, f_vol_name)) {
534 		/* The name of the volume is the same as the directory we mounted on */
535 		coveredvp = vfs_vnodecovered(mp);
536 		if (coveredvp) {
537 			const char * name = vnode_getname_printable(coveredvp);
538 			strlcpy(vfap->f_vol_name, name, MAXPATHLEN);
539 			vnode_putname_printable(name);
540 
541 			VFSATTR_SET_SUPPORTED(vfap, f_vol_name);
542 			vnode_put(coveredvp);
543 		}
544 	}
545 
546 	nullfs_cleanup_patched_context(null_mp, ectx);
547 
548 	return 0;
549 }
550 
551 static int
nullfs_sync(__unused struct mount * mp,__unused int waitfor,__unused vfs_context_t ctx)552 nullfs_sync(__unused struct mount * mp, __unused int waitfor, __unused vfs_context_t ctx)
553 {
554 	/*
555 	 * XXX - Assumes no data cached at null layer.
556 	 */
557 	return 0;
558 }
559 
560 
561 
562 static int
nullfs_vfs_start(__unused struct mount * mp,__unused int flags,__unused vfs_context_t ctx)563 nullfs_vfs_start(__unused struct mount * mp, __unused int flags, __unused vfs_context_t ctx)
564 {
565 	NULLFSDEBUG("%s\n", __FUNCTION__);
566 	return 0;
567 }
568 
569 extern const struct vnodeopv_desc nullfs_vnodeop_opv_desc;
570 
571 const struct vnodeopv_desc * nullfs_vnodeopv_descs[] = {
572 	&nullfs_vnodeop_opv_desc,
573 };
574 
575 struct vfsops nullfs_vfsops = {
576 	.vfs_mount              = nullfs_mount,
577 	.vfs_unmount            = nullfs_unmount,
578 	.vfs_start              = nullfs_vfs_start,
579 	.vfs_root               = nullfs_root,
580 	.vfs_getattr            = nullfs_vfs_getattr,
581 	.vfs_sync               = nullfs_sync,
582 	.vfs_init               = nullfs_init,
583 	.vfs_sysctl             = NULL,
584 	.vfs_setattr            = NULL,
585 };
586