1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2016 Joyent, Inc.
25  * Copyright 2016 Toomas Soome <tsoome@me.com>
26  * Copyright (c) 2016 by Delphix. All rights reserved.
27  * Copyright 2017 RackTop Systems.
28  * Copyright 2018 Nexenta Systems, Inc.
29  */
30 
31 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
32 /*	  All Rights Reserved	*/
33 
34 /*
35  * University Copyright- Copyright (c) 1982, 1986, 1988
36  * The Regents of the University of California
37  * All Rights Reserved
38  *
39  * University Acknowledgment- Portions of this document are derived from
40  * software developed by the University of California, Berkeley, and its
41  * contributors.
42  */
43 
44 /*
45  * This file contains those functions from fs/vfs.c that can be
46  * used with relatively little change.  Functions that differ
47  * significantly from that are in other files.
48  */
49 
50 #include <sys/types.h>
51 #include <sys/t_lock.h>
52 #include <sys/param.h>
53 #include <sys/errno.h>
54 #include <sys/user.h>
55 #include <sys/fstyp.h>
56 #include <sys/kmem.h>
57 #include <sys/systm.h>
58 #include <sys/proc.h>
59 #include <sys/mount.h>
60 #include <sys/vfs.h>
61 #include <sys/vfs_opreg.h>
62 #include <sys/fem.h>
63 #include <sys/mntent.h>
64 #include <sys/stat.h>
65 #include <sys/statvfs.h>
66 #include <sys/statfs.h>
67 #include <sys/cred.h>
68 #include <sys/vnode.h>
69 #include <sys/rwstlock.h>
70 #include <sys/dnlc.h>
71 #include <sys/file.h>
72 #include <sys/time.h>
73 #include <sys/atomic.h>
74 #include <sys/cmn_err.h>
75 #include <sys/buf.h>
76 #include <sys/debug.h>
77 #include <sys/vnode.h>
78 #include <sys/ddi.h>
79 #include <sys/pathname.h>
80 #include <sys/poll.h>
81 #include <sys/sunddi.h>
82 #include <sys/sysmacros.h>
83 #include <sys/zone.h>
84 #include <sys/policy.h>
85 #include <sys/attr.h>
86 #include <fs/fs_subr.h>
87 
88 #include <libfksmbfs.h>
89 
90 static void vfs_clearmntopt_nolock(mntopts_t *, const char *, int);
91 static void vfs_setmntopt_nolock(mntopts_t *, const char *,
92     const char *, int, int);
93 static int  vfs_optionisset_nolock(const mntopts_t *, const char *, char **);
94 // static void vfs_freemnttab(struct vfs *);
95 static void vfs_freeopt(mntopt_t *);
96 static void vfs_swapopttbl_nolock(mntopts_t *, mntopts_t *);
97 static void vfs_swapopttbl(mntopts_t *, mntopts_t *);
98 static void vfs_copyopttbl_extend(const mntopts_t *, mntopts_t *, int);
99 // static void vfs_createopttbl_extend(mntopts_t *, const char *,
100 //    const mntopts_t *);
101 // static char **vfs_copycancelopt_extend(char **const, int);
102 static void vfs_freecancelopt(char **);
103 
104 /*
105  * VFS global data.
106  */
107 vnode_t *rootdir;		/* pointer to root inode vnode. */
108 struct vfs *rootvfs = NULL;	/* pointer to root vfs; head of VFS list. */
109 static krwlock_t vfslist;
110 struct vfs	*zone_vfslist;	/* list of FS's mounted in zone */
111 
112 /* from os/vfs_conf.c */
113 const int nfstype = 5;
114 struct vfssw vfssw[10] = {
115 	{ "BADVFS" },				/* 0:invalid */
116 	{ "" },					/* reserved for loadable fs */
117 	{ "" },
118 	{ "" },
119 	{ "" },
120 };
121 
122 /*
123  * Table for generic options recognized in the VFS layer and acted
124  * on at this level before parsing file system specific options.
125  * The nosuid option is stronger than any of the devices and setuid
126  * options, so those are canceled when nosuid is seen.
127  *
128  * All options which are added here need to be added to the
129  * list of standard options in usr/src/cmd/fs.d/fslib.c as well.
130  */
131 /*
132  * VFS Mount options table
133  */
134 static char *ro_cancel[] = { MNTOPT_RW, NULL };
135 static char *rw_cancel[] = { MNTOPT_RO, NULL };
136 static char *suid_cancel[] = { MNTOPT_NOSUID, NULL };
137 static char *nosuid_cancel[] = { MNTOPT_SUID, MNTOPT_DEVICES, MNTOPT_NODEVICES,
138     MNTOPT_NOSETUID, MNTOPT_SETUID, NULL };
139 static char *devices_cancel[] = { MNTOPT_NODEVICES, NULL };
140 static char *nodevices_cancel[] = { MNTOPT_DEVICES, NULL };
141 static char *setuid_cancel[] = { MNTOPT_NOSETUID, NULL };
142 static char *nosetuid_cancel[] = { MNTOPT_SETUID, NULL };
143 static char *nbmand_cancel[] = { MNTOPT_NONBMAND, NULL };
144 static char *nonbmand_cancel[] = { MNTOPT_NBMAND, NULL };
145 static char *exec_cancel[] = { MNTOPT_NOEXEC, NULL };
146 static char *noexec_cancel[] = { MNTOPT_EXEC, NULL };
147 
148 static const mntopt_t mntopts[] = {
149 /*
150  *	option name		cancel options		default arg	flags
151  */
152 	{ MNTOPT_REMOUNT,	NULL,			NULL,
153 		MO_NODISPLAY, (void *)0 },
154 	{ MNTOPT_RO,		ro_cancel,		NULL,		0,
155 		(void *)0 },
156 	{ MNTOPT_RW,		rw_cancel,		NULL,		0,
157 		(void *)0 },
158 	{ MNTOPT_SUID,		suid_cancel,		NULL,		0,
159 		(void *)0 },
160 	{ MNTOPT_NOSUID,	nosuid_cancel,		NULL,		0,
161 		(void *)0 },
162 	{ MNTOPT_DEVICES,	devices_cancel,		NULL,		0,
163 		(void *)0 },
164 	{ MNTOPT_NODEVICES,	nodevices_cancel,	NULL,		0,
165 		(void *)0 },
166 	{ MNTOPT_SETUID,	setuid_cancel,		NULL,		0,
167 		(void *)0 },
168 	{ MNTOPT_NOSETUID,	nosetuid_cancel,	NULL,		0,
169 		(void *)0 },
170 	{ MNTOPT_NBMAND,	nbmand_cancel,		NULL,		0,
171 		(void *)0 },
172 	{ MNTOPT_NONBMAND,	nonbmand_cancel,	NULL,		0,
173 		(void *)0 },
174 	{ MNTOPT_EXEC,		exec_cancel,		NULL,		0,
175 		(void *)0 },
176 	{ MNTOPT_NOEXEC,	noexec_cancel,		NULL,		0,
177 		(void *)0 },
178 };
179 
180 const mntopts_t vfs_mntopts = {
181 	sizeof (mntopts) / sizeof (mntopt_t),
182 	(mntopt_t *)&mntopts[0]
183 };
184 
185 /*
186  * File system operation dispatch functions.
187  */
188 
189 int
190 fsop_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
191 {
192 	return (*(vfsp)->vfs_op->vfs_mount)(vfsp, mvp, uap, cr);
193 }
194 
195 int
196 fsop_unmount(vfs_t *vfsp, int flag, cred_t *cr)
197 {
198 	return (*(vfsp)->vfs_op->vfs_unmount)(vfsp, flag, cr);
199 }
200 
201 int
202 fsop_root(vfs_t *vfsp, vnode_t **vpp)
203 {
204 	return ((*(vfsp)->vfs_op->vfs_root)(vfsp, vpp));
205 }
206 
207 int
208 fsop_statfs(vfs_t *vfsp, statvfs64_t *sp)
209 {
210 	return (*(vfsp)->vfs_op->vfs_statvfs)(vfsp, sp);
211 }
212 
213 int
214 fsop_sync(vfs_t *vfsp, short flag, cred_t *cr)
215 {
216 	return (*(vfsp)->vfs_op->vfs_sync)(vfsp, flag, cr);
217 }
218 
219 int
220 fsop_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
221 {
222 	return (*(vfsp)->vfs_op->vfs_vget)(vfsp, vpp, fidp);
223 }
224 
225 int
226 fsop_mountroot(vfs_t *vfsp, enum whymountroot reason)
227 {
228 	return (*(vfsp)->vfs_op->vfs_mountroot)(vfsp, reason);
229 }
230 
231 void
232 fsop_freefs(vfs_t *vfsp)
233 {
234 	(*(vfsp)->vfs_op->vfs_freevfs)(vfsp);
235 }
236 
237 int
238 fsop_vnstate(vfs_t *vfsp, vnode_t *vp, vntrans_t nstate)
239 {
240 	return ((*(vfsp)->vfs_op->vfs_vnstate)(vfsp, vp, nstate));
241 }
242 
243 int
244 fsop_sync_by_kind(int fstype, short flag, cred_t *cr)
245 {
246 	ASSERT((fstype >= 0) && (fstype < nfstype));
247 
248 	if (ALLOCATED_VFSSW(&vfssw[fstype]) && VFS_INSTALLED(&vfssw[fstype]))
249 		return (*vfssw[fstype].vsw_vfsops.vfs_sync) (NULL, flag, cr);
250 	else
251 		return (ENOTSUP);
252 }
253 
254 /*
255  * File system initialization.  vfs_setfsops() must be called from a file
256  * system's init routine.
257  */
258 
259 static int
260 fs_copyfsops(const fs_operation_def_t *template, vfsops_t *actual,
261     int *unused_ops)
262 {
263 	static const fs_operation_trans_def_t vfs_ops_table[] = {
264 		VFSNAME_MOUNT, offsetof(vfsops_t, vfs_mount),
265 			fs_nosys, fs_nosys,
266 
267 		VFSNAME_UNMOUNT, offsetof(vfsops_t, vfs_unmount),
268 			fs_nosys, fs_nosys,
269 
270 		VFSNAME_ROOT, offsetof(vfsops_t, vfs_root),
271 			fs_nosys, fs_nosys,
272 
273 		VFSNAME_STATVFS, offsetof(vfsops_t, vfs_statvfs),
274 			fs_nosys, fs_nosys,
275 
276 		VFSNAME_SYNC, offsetof(vfsops_t, vfs_sync),
277 			(fs_generic_func_p) fs_sync,
278 			(fs_generic_func_p) fs_sync,	/* No errors allowed */
279 
280 		VFSNAME_VGET, offsetof(vfsops_t, vfs_vget),
281 			fs_nosys, fs_nosys,
282 
283 		VFSNAME_MOUNTROOT, offsetof(vfsops_t, vfs_mountroot),
284 			fs_nosys, fs_nosys,
285 
286 		VFSNAME_FREEVFS, offsetof(vfsops_t, vfs_freevfs),
287 			(fs_generic_func_p)fs_freevfs,
288 			(fs_generic_func_p)fs_freevfs,	/* Shouldn't fail */
289 
290 		VFSNAME_VNSTATE, offsetof(vfsops_t, vfs_vnstate),
291 			(fs_generic_func_p)fs_nosys,
292 			(fs_generic_func_p)fs_nosys,
293 
294 		NULL, 0, NULL, NULL
295 	};
296 
297 	return (fs_build_vector(actual, unused_ops, vfs_ops_table, template));
298 }
299 
300 /* zfs_boot_init() */
301 
302 int
303 vfs_setfsops(int fstype, const fs_operation_def_t *template, vfsops_t **actual)
304 {
305 	int error;
306 	int unused_ops;
307 
308 	/*
309 	 * Verify that fstype refers to a valid fs.  Note that
310 	 * 0 is valid since it's used to set "stray" ops.
311 	 */
312 	if ((fstype < 0) || (fstype >= nfstype))
313 		return (EINVAL);
314 
315 	if (!ALLOCATED_VFSSW(&vfssw[fstype]))
316 		return (EINVAL);
317 
318 	/* Set up the operations vector. */
319 
320 	error = fs_copyfsops(template, &vfssw[fstype].vsw_vfsops, &unused_ops);
321 
322 	if (error != 0)
323 		return (error);
324 
325 	vfssw[fstype].vsw_flag |= VSW_INSTALLED;
326 
327 	if (actual != NULL)
328 		*actual = &vfssw[fstype].vsw_vfsops;
329 
330 #if DEBUG
331 	if (unused_ops != 0)
332 		cmn_err(CE_WARN, "vfs_setfsops: %s: %d operations supplied "
333 		    "but not used", vfssw[fstype].vsw_name, unused_ops);
334 #endif
335 
336 	return (0);
337 }
338 
339 int
340 vfs_makefsops(const fs_operation_def_t *template, vfsops_t **actual)
341 {
342 	int error;
343 	int unused_ops;
344 
345 	*actual = (vfsops_t *)kmem_alloc(sizeof (vfsops_t), KM_SLEEP);
346 
347 	error = fs_copyfsops(template, *actual, &unused_ops);
348 	if (error != 0) {
349 		kmem_free(*actual, sizeof (vfsops_t));
350 		*actual = NULL;
351 		return (error);
352 	}
353 
354 	return (0);
355 }
356 
357 /*
358  * Free a vfsops structure created as a result of vfs_makefsops().
359  * NOTE: For a vfsops structure initialized by vfs_setfsops(), use
360  * vfs_freevfsops_by_type().
361  */
362 void
363 vfs_freevfsops(vfsops_t *vfsops)
364 {
365 	kmem_free(vfsops, sizeof (vfsops_t));
366 }
367 
368 /*
369  * Since the vfsops structure is part of the vfssw table and wasn't
370  * really allocated, we're not really freeing anything.  We keep
371  * the name for consistency with vfs_freevfsops().  We do, however,
372  * need to take care of a little bookkeeping.
373  * NOTE: For a vfsops structure created by vfs_setfsops(), use
374  * vfs_freevfsops_by_type().
375  */
376 int
377 vfs_freevfsops_by_type(int fstype)
378 {
379 
380 	/* Verify that fstype refers to a loaded fs (and not fsid 0). */
381 	if ((fstype <= 0) || (fstype >= nfstype))
382 		return (EINVAL);
383 
384 	WLOCK_VFSSW();
385 	if ((vfssw[fstype].vsw_flag & VSW_INSTALLED) == 0) {
386 		WUNLOCK_VFSSW();
387 		return (EINVAL);
388 	}
389 
390 	vfssw[fstype].vsw_flag &= ~VSW_INSTALLED;
391 	WUNLOCK_VFSSW();
392 
393 	return (0);
394 }
395 
396 /* Support routines used to reference vfs_op */
397 
398 /* Set the operations vector for a vfs */
399 void
400 vfs_setops(vfs_t *vfsp, vfsops_t *vfsops)
401 {
402 
403 	ASSERT(vfsp != NULL);
404 	ASSERT(vfsops != NULL);
405 
406 	vfsp->vfs_op = vfsops;
407 }
408 
409 /* Retrieve the operations vector for a vfs */
410 vfsops_t *
411 vfs_getops(vfs_t *vfsp)
412 {
413 
414 	ASSERT(vfsp != NULL);
415 
416 	return (vfsp->vfs_op);
417 }
418 
419 /*
420  * Returns non-zero (1) if the vfsops matches that of the vfs.
421  * Returns zero (0) if not.
422  */
423 int
424 vfs_matchops(vfs_t *vfsp, vfsops_t *vfsops)
425 {
426 	return (vfs_getops(vfsp) == vfsops);
427 }
428 
429 /*
430  * Returns non-zero (1) if the file system has installed a non-default,
431  * non-error vfs_sync routine.  Returns zero (0) otherwise.
432  */
433 int
434 vfs_can_sync(vfs_t *vfsp)
435 {
436 	/* vfs_sync() routine is not the default/error function */
437 	return (vfs_getops(vfsp)->vfs_sync != fs_sync);
438 }
439 
440 /*
441  * Initialize a vfs structure.
442  */
443 void
444 vfs_init(vfs_t *vfsp, vfsops_t *op, void *data)
445 {
446 	/* Always do full init, like vfs_alloc() */
447 	bzero(vfsp, sizeof (vfs_t));
448 	vfsp->vfs_count = 0;
449 	vfsp->vfs_next = vfsp;
450 	vfsp->vfs_prev = vfsp;
451 	vfsp->vfs_zone_next = vfsp;
452 	vfsp->vfs_zone_prev = vfsp;
453 	vfsp->vfs_lofi_id = 0;
454 	sema_init(&vfsp->vfs_reflock, 1, NULL, SEMA_DEFAULT, NULL);
455 	vfsimpl_setup(vfsp);
456 	vfsp->vfs_data = (data);
457 	vfs_setops((vfsp), (op));
458 }
459 
460 /*
461  * Allocate and initialize the vfs implementation private data
462  * structure, vfs_impl_t.
463  */
464 void
465 vfsimpl_setup(vfs_t *vfsp)
466 {
467 	int i;
468 
469 	if (vfsp->vfs_implp != NULL) {
470 		return;
471 	}
472 
473 	vfsp->vfs_implp = kmem_alloc(sizeof (vfs_impl_t), KM_SLEEP);
474 	/* Note that these are #define'd in vfs.h */
475 	vfsp->vfs_vskap = NULL;
476 	vfsp->vfs_fstypevsp = NULL;
477 
478 	/* Set size of counted array, then zero the array */
479 	vfsp->vfs_featureset[0] = VFS_FEATURE_MAXSZ - 1;
480 	for (i = 1; i <  VFS_FEATURE_MAXSZ; i++) {
481 		vfsp->vfs_featureset[i] = 0;
482 	}
483 }
484 
485 /*
486  * Release the vfs_impl_t structure, if it exists. Some unbundled
487  * filesystems may not use the newer version of vfs and thus
488  * would not contain this implementation private data structure.
489  */
490 void
491 vfsimpl_teardown(vfs_t *vfsp)
492 {
493 	vfs_impl_t	*vip = vfsp->vfs_implp;
494 
495 	if (vip == NULL)
496 		return;
497 
498 	kmem_free(vfsp->vfs_implp, sizeof (vfs_impl_t));
499 	vfsp->vfs_implp = NULL;
500 }
501 
502 /*
503  * VFS system calls: mount, umount, syssync, statfs, fstatfs, statvfs,
504  * fstatvfs, and sysfs moved to common/syscall.
505  */
506 
507 // vfs_sync, sync
508 
509 /*
510  * External routines.
511  */
512 
513 krwlock_t vfssw_lock;	/* lock accesses to vfssw */
514 
515 /*
516  * Lock for accessing the vfs linked list.  Initialized in vfs_mountroot(),
517  * but otherwise should be accessed only via vfs_list_lock() and
518  * vfs_list_unlock().  Also used to protect the timestamp for mods to the list.
519  */
520 static krwlock_t vfslist;
521 
522 // vfs_mountdevices(void)
523 // vfs_mountdev1(void)
524 // vfs_mountfs()
525 // vfs_mountroot()
526 // lofi_add, lofi_remove
527 
528 
529 /*
530  * Mount the FS for the test jig.  Based on domount()
531  */
532 int
533 fake_domount(char *fsname, struct mounta *uap, struct vfs **vfspp)
534 {
535 	vnode_t		*vp;
536 	struct cred	*credp;
537 	struct vfssw	*vswp;
538 	vfsops_t	*vfsops;
539 	struct vfs	*vfsp = NULL;
540 	mntopts_t	mnt_mntopts;
541 	int		error = 0;
542 	int		copyout_error = 0;
543 	char		*opts = uap->optptr;
544 	char		*inargs = opts;
545 	int		optlen = uap->optlen;
546 
547 	credp = CRED();
548 
549 	/*
550 	 * Test jig specific: mount on rootdir
551 	 */
552 	if (rootvfs != NULL)
553 		return (EBUSY);
554 	vp = rootdir;
555 
556 	/*
557 	 * The v_flag value for the mount point vp is permanently set
558 	 * to VVFSLOCK so that no one bypasses the vn_vfs*locks routine
559 	 * for mount point locking.
560 	 */
561 	mutex_enter(&vp->v_lock);
562 	vp->v_flag |= VVFSLOCK;
563 	mutex_exit(&vp->v_lock);
564 
565 	mnt_mntopts.mo_count = 0;
566 
567 	/*
568 	 * Find the ops vector to use to invoke the file system-specific mount
569 	 * method.  If the fsname argument is non-NULL, use it directly.
570 	 */
571 	if ((vswp = vfs_getvfssw(fsname)) == NULL) {
572 		return (EINVAL);
573 	}
574 	if (!VFS_INSTALLED(vswp))
575 		return (EINVAL);
576 
577 	// secpolicy_fs_allowed_mount(fsname)
578 
579 	vfsops = &vswp->vsw_vfsops;
580 
581 	vfs_copyopttbl(&vswp->vsw_optproto, &mnt_mntopts);
582 
583 	/*
584 	 * Fetch mount options and parse them for generic vfs options
585 	 */
586 	if (uap->flags & MS_OPTIONSTR) {
587 		/*
588 		 * Limit the buffer size
589 		 */
590 		if (optlen < 0 || optlen > MAX_MNTOPT_STR) {
591 			error = EINVAL;
592 			goto errout;
593 		}
594 		if ((uap->flags & MS_SYSSPACE) == 0) {
595 			inargs = kmem_alloc(MAX_MNTOPT_STR, KM_SLEEP);
596 			inargs[0] = '\0';
597 			if (optlen) {
598 				error = copyinstr(opts, inargs, (size_t)optlen,
599 				    NULL);
600 				if (error) {
601 					goto errout;
602 				}
603 			}
604 		}
605 		vfs_parsemntopts(&mnt_mntopts, inargs, 0);
606 	}
607 	/*
608 	 * Flag bits override the options string.
609 	 */
610 	if (uap->flags & MS_REMOUNT)
611 		vfs_setmntopt_nolock(&mnt_mntopts, MNTOPT_REMOUNT, NULL, 0, 0);
612 	if (uap->flags & MS_RDONLY)
613 		vfs_setmntopt_nolock(&mnt_mntopts, MNTOPT_RO, NULL, 0, 0);
614 	if (uap->flags & MS_NOSUID)
615 		vfs_setmntopt_nolock(&mnt_mntopts, MNTOPT_NOSUID, NULL, 0, 0);
616 
617 	/*
618 	 * Check if this is a remount; must be set in the option string and
619 	 * the file system must support a remount option.
620 	 */
621 	if (vfs_optionisset_nolock(&mnt_mntopts,
622 	    MNTOPT_REMOUNT, NULL)) {
623 		/* disallow here */
624 		error = ENOTSUP;
625 		goto errout;
626 	}
627 
628 	/*
629 	 * uap->flags and vfs_optionisset() should agree.
630 	 */
631 	if (vfs_optionisset_nolock(&mnt_mntopts, MNTOPT_RO, NULL)) {
632 		uap->flags |= MS_RDONLY;
633 	}
634 	if (vfs_optionisset_nolock(&mnt_mntopts, MNTOPT_NOSUID, NULL)) {
635 		uap->flags |= MS_NOSUID;
636 	}
637 	// nbmand ...
638 
639 	/*
640 	 * If we are splicing the fs into the namespace,
641 	 * perform mount point checks...
642 	 * (always splice=0 here)
643 	 */
644 
645 	if ((uap->flags & (MS_DATA | MS_OPTIONSTR)) == 0) {
646 		uap->dataptr = NULL;
647 		uap->datalen = 0;
648 	}
649 
650 	/*
651 	 * If this is a remount, ... (never here)
652 	 */
653 	vfsp = vfs_alloc(KM_SLEEP);
654 	VFS_INIT(vfsp, vfsops, NULL);
655 
656 	VFS_HOLD(vfsp);
657 
658 	// lofi_add(fsname, vfsp, &mnt_mntopts, uap)
659 
660 	/*
661 	 * PRIV_SYS_MOUNT doesn't mean you can become root.
662 	 */
663 	uap->flags |= MS_NOSUID;
664 	vfs_setmntopt_nolock(&mnt_mntopts, MNTOPT_NOSUID, NULL, 0, 0);
665 
666 	/*
667 	 * The vfs_reflock...
668 	 */
669 
670 	/*
671 	 * Lock the vfs...
672 	 */
673 	if ((error = vfs_lock(vfsp)) != 0) {
674 		vfs_free(vfsp);
675 		vfsp = NULL;
676 		goto errout;
677 	}
678 
679 	/*
680 	 * Add device to mount in progress table...
681 	 */
682 	/*
683 	 * Invalidate cached entry for the mount point.
684 	 */
685 
686 	/*
687 	 * If have an option string but the filesystem doesn't supply a
688 	 * prototype options table, create a table...
689 	 */
690 
691 	/*
692 	 * Serialize with zone state transitions...
693 	 */
694 
695 	// mount_in_progress(zone);
696 
697 	/*
698 	 * Instantiate (or reinstantiate) the file system...
699 	 */
700 	vfs_swapopttbl(&mnt_mntopts, &vfsp->vfs_mntopts);
701 
702 	vfs_setresource(vfsp, uap->spec, 0);
703 	vfs_setmntpoint(vfsp, uap->dir, 0);
704 
705 	/*
706 	 * going to mount on this vnode, so notify.
707 	 */
708 	// vnevent_mountedover(vp, NULL);
709 	error = VFS_MOUNT(vfsp, vp, uap, credp);
710 
711 	if (uap->flags & MS_RDONLY)
712 		vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
713 	if (uap->flags & MS_NOSUID)
714 		vfs_setmntopt(vfsp, MNTOPT_NOSUID, NULL, 0);
715 	if (uap->flags & MS_GLOBAL)
716 		vfs_setmntopt(vfsp, MNTOPT_GLOBAL, NULL, 0);
717 
718 	if (error) {
719 		// lofi_remove(vfsp);
720 
721 		// (remount == 0)
722 		vfs_unlock(vfsp);
723 		// vfs_freemnttab(vfsp);
724 		vfs_free(vfsp);
725 		vfsp = NULL;
726 	} else {
727 		/*
728 		 * Set the mount time to now
729 		 */
730 		// vfsp->vfs_mtime = ddi_get_time();
731 		// if (remount) ...
732 		// else if (splice) vfs_add(vp, vfsp, flags)
733 		// else VFS_HOLD(vfsp);
734 
735 		/*
736 		 * Test jig specific:
737 		 * Do sort of like vfs_add for vp=rootdir
738 		 * Already have hold on vp.
739 		 */
740 		vfsp->vfs_vnodecovered = vp;
741 		vfsp->vfs_flag |= (VFS_NOSETUID|VFS_NODEVICES);
742 		VFS_HOLD(vfsp);
743 		rootvfs = vfsp;
744 
745 		/*
746 		 * Set flags for global options encountered
747 		 */
748 		if (vfs_optionisset(vfsp, MNTOPT_RO, NULL))
749 			vfsp->vfs_flag |= VFS_RDONLY;
750 		else
751 			vfsp->vfs_flag &= ~VFS_RDONLY;
752 		if (vfs_optionisset(vfsp, MNTOPT_NOSUID, NULL)) {
753 			vfsp->vfs_flag |= (VFS_NOSETUID|VFS_NODEVICES);
754 		} else {
755 			if (vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL))
756 				vfsp->vfs_flag |= VFS_NODEVICES;
757 			else
758 				vfsp->vfs_flag &= ~VFS_NODEVICES;
759 			if (vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL))
760 				vfsp->vfs_flag |= VFS_NOSETUID;
761 			else
762 				vfsp->vfs_flag &= ~VFS_NOSETUID;
763 		}
764 		if (vfs_optionisset(vfsp, MNTOPT_NBMAND, NULL))
765 			vfsp->vfs_flag |= VFS_NBMAND;
766 		else
767 			vfsp->vfs_flag &= ~VFS_NBMAND;
768 
769 		if (vfs_optionisset(vfsp, MNTOPT_XATTR, NULL))
770 			vfsp->vfs_flag |= VFS_XATTR;
771 		else
772 			vfsp->vfs_flag &= ~VFS_XATTR;
773 
774 		if (vfs_optionisset(vfsp, MNTOPT_NOEXEC, NULL))
775 			vfsp->vfs_flag |= VFS_NOEXEC;
776 		else
777 			vfsp->vfs_flag &= ~VFS_NOEXEC;
778 
779 		/*
780 		 * Now construct the output option string of options
781 		 * we recognized.
782 		 */
783 		if (uap->flags & MS_OPTIONSTR) {
784 			vfs_list_read_lock();
785 			copyout_error = vfs_buildoptionstr(
786 			    &vfsp->vfs_mntopts, inargs, optlen);
787 			vfs_list_unlock();
788 			if (copyout_error == 0 &&
789 			    (uap->flags & MS_SYSSPACE) == 0) {
790 				copyout_error = copyout(inargs, opts, optlen);
791 			}
792 		}
793 
794 		/*
795 		 * If this isn't a remount, set up the vopstats...
796 		 */
797 		if (vswp->vsw_flag & VSW_XID)
798 			vfsp->vfs_flag |= VFS_XID;
799 
800 		vfs_unlock(vfsp);
801 
802 		/*
803 		 * Test jig specicific:
804 		 * Replace rootdir with the mounted root.
805 		 */
806 		error = VFS_ROOT(vfsp, &rootdir);
807 		if (error != 0) {
808 			panic("fake_domount, get root %d\n", error);
809 		}
810 	}
811 	// mount_completed(zone);
812 	// zone_rele(zone);
813 
814 	// if (splice)
815 	//	vn_vfsunlock(vp);
816 
817 	if ((error == 0) && (copyout_error == 0)) {
818 		/* get_vskstat_anchor() */
819 		/* Return vfsp to caller. */
820 		*vfspp = vfsp;
821 	}
822 errout:
823 	vfs_freeopttbl(&mnt_mntopts);
824 	/* resource, mountpt not allocated */
825 	/* no addmip, delmip */
826 	ASSERT(vswp != NULL);
827 	vfs_unrefvfssw(vswp);
828 	if (inargs != opts)
829 		kmem_free(inargs, MAX_MNTOPT_STR);
830 	if (copyout_error) {
831 		if (vfsp != NULL) {
832 			// lofi_remove(vfsp);
833 			VFS_RELE(vfsp);
834 		}
835 		error = copyout_error;
836 	}
837 	return (error);
838 }
839 
840 
841 static void
842 vfs_setpath(
843     struct vfs *vfsp,		/* vfs being updated */
844     refstr_t **refp,		/* Ref-count string to contain the new path */
845     const char *newpath,	/* Path to add to refp (above) */
846     uint32_t flag)		/* flag */
847 {
848 	// size_t len;
849 	refstr_t *ref;
850 	// char *sp;
851 	int have_list_lock = 0;
852 
853 	ASSERT(!VFS_ON_LIST(vfsp) || vfs_lock_held(vfsp));
854 
855 	/*
856 	 * New path must be less than MAXPATHLEN because mntfs
857 	 * will only display up to MAXPATHLEN bytes. This is currently
858 	 * safe, because domount() uses pn_get(), and other callers
859 	 * similarly cap the size to fewer than MAXPATHLEN bytes.
860 	 */
861 
862 	ASSERT(strlen(newpath) < MAXPATHLEN);
863 
864 	/* mntfs requires consistency while vfs list lock is held */
865 
866 	if (VFS_ON_LIST(vfsp)) {
867 		have_list_lock = 1;
868 		vfs_list_lock();
869 	}
870 
871 	if (*refp != NULL)
872 		refstr_rele(*refp);
873 
874 	/*
875 	 * If we are in a non-global zone... (do something else)
876 	 */
877 	ref = refstr_alloc(newpath);
878 	*refp = ref;
879 
880 	if (have_list_lock) {
881 		vfs_mnttab_modtimeupd();
882 		vfs_list_unlock();
883 	}
884 }
885 
886 /*
887  * Record a mounted resource name in a vfs structure.
888  * If vfsp is already mounted, caller must hold the vfs lock.
889  */
890 void
891 vfs_setresource(struct vfs *vfsp, const char *resource, uint32_t flag)
892 {
893 	if (resource == NULL || resource[0] == '\0')
894 		resource = VFS_NORESOURCE;
895 	vfs_setpath(vfsp, &vfsp->vfs_resource, resource, flag);
896 }
897 
898 /*
899  * Record a mount point name in a vfs structure.
900  * If vfsp is already mounted, caller must hold the vfs lock.
901  */
902 void
903 vfs_setmntpoint(struct vfs *vfsp, const char *mntpt, uint32_t flag)
904 {
905 	if (mntpt == NULL || mntpt[0] == '\0')
906 		mntpt = VFS_NOMNTPT;
907 	vfs_setpath(vfsp, &vfsp->vfs_mntpt, mntpt, flag);
908 }
909 
910 /* Returns the vfs_resource. Caller must call refstr_rele() when finished. */
911 
912 refstr_t *
913 vfs_getresource(const struct vfs *vfsp)
914 {
915 	refstr_t *resource;
916 
917 	vfs_list_read_lock();
918 	resource = vfsp->vfs_resource;
919 	refstr_hold(resource);
920 	vfs_list_unlock();
921 
922 	return (resource);
923 }
924 
925 /* Returns the vfs_mntpt. Caller must call refstr_rele() when finished. */
926 
927 refstr_t *
928 vfs_getmntpoint(const struct vfs *vfsp)
929 {
930 	refstr_t *mntpt;
931 
932 	vfs_list_read_lock();
933 	mntpt = vfsp->vfs_mntpt;
934 	refstr_hold(mntpt);
935 	vfs_list_unlock();
936 
937 	return (mntpt);
938 }
939 
940 // vfs_createopttbl_extend
941 // vfs_createopttbl
942 
943 /*
944  * Swap two mount options tables
945  */
946 static void
947 vfs_swapopttbl_nolock(mntopts_t *optbl1, mntopts_t *optbl2)
948 {
949 	uint_t tmpcnt;
950 	mntopt_t *tmplist;
951 
952 	tmpcnt = optbl2->mo_count;
953 	tmplist = optbl2->mo_list;
954 	optbl2->mo_count = optbl1->mo_count;
955 	optbl2->mo_list = optbl1->mo_list;
956 	optbl1->mo_count = tmpcnt;
957 	optbl1->mo_list = tmplist;
958 }
959 
960 static void
961 vfs_swapopttbl(mntopts_t *optbl1, mntopts_t *optbl2)
962 {
963 	vfs_list_lock();
964 	vfs_swapopttbl_nolock(optbl1, optbl2);
965 	vfs_mnttab_modtimeupd();
966 	vfs_list_unlock();
967 }
968 
969 static char **
970 vfs_copycancelopt_extend(char **const moc, int extend)
971 {
972 	int i = 0;
973 	int j;
974 	char **result;
975 
976 	if (moc != NULL) {
977 		for (; moc[i] != NULL; i++)
978 			/* count number of options to cancel */;
979 	}
980 
981 	if (i + extend == 0)
982 		return (NULL);
983 
984 	result = kmem_alloc((i + extend + 1) * sizeof (char *), KM_SLEEP);
985 
986 	for (j = 0; j < i; j++) {
987 		result[j] = kmem_alloc(strlen(moc[j]) + 1, KM_SLEEP);
988 		(void) strcpy(result[j], moc[j]);
989 	}
990 	for (; j <= i + extend; j++)
991 		result[j] = NULL;
992 
993 	return (result);
994 }
995 
996 static void
997 vfs_copyopt(const mntopt_t *s, mntopt_t *d)
998 {
999 	char *sp, *dp;
1000 
1001 	d->mo_flags = s->mo_flags;
1002 	d->mo_data = s->mo_data;
1003 	sp = s->mo_name;
1004 	if (sp != NULL) {
1005 		dp = kmem_alloc(strlen(sp) + 1, KM_SLEEP);
1006 		(void) strcpy(dp, sp);
1007 		d->mo_name = dp;
1008 	} else {
1009 		d->mo_name = NULL; /* should never happen */
1010 	}
1011 
1012 	d->mo_cancel = vfs_copycancelopt_extend(s->mo_cancel, 0);
1013 
1014 	sp = s->mo_arg;
1015 	if (sp != NULL) {
1016 		dp = kmem_alloc(strlen(sp) + 1, KM_SLEEP);
1017 		(void) strcpy(dp, sp);
1018 		d->mo_arg = dp;
1019 	} else {
1020 		d->mo_arg = NULL;
1021 	}
1022 }
1023 
1024 // vfs_copyopttbl_extend
1025 // vfs_copyopttbl
1026 
1027 /*
1028  * Copy a mount options table, possibly allocating some spare
1029  * slots at the end.  It is permissible to copy_extend the NULL table.
1030  */
1031 static void
1032 vfs_copyopttbl_extend(const mntopts_t *smo, mntopts_t *dmo, int extra)
1033 {
1034 	uint_t i, count;
1035 	mntopt_t *motbl;
1036 
1037 	/*
1038 	 * Clear out any existing stuff in the options table being initialized
1039 	 */
1040 	vfs_freeopttbl(dmo);
1041 	count = (smo == NULL) ? 0 : smo->mo_count;
1042 	if ((count + extra) == 0)	/* nothing to do */
1043 		return;
1044 	dmo->mo_count = count + extra;
1045 	motbl = kmem_zalloc((count + extra) * sizeof (mntopt_t), KM_SLEEP);
1046 	dmo->mo_list = motbl;
1047 	for (i = 0; i < count; i++) {
1048 		vfs_copyopt(&smo->mo_list[i], &motbl[i]);
1049 	}
1050 	for (i = count; i < count + extra; i++) {
1051 		motbl[i].mo_flags = MO_EMPTY;
1052 	}
1053 }
1054 
1055 /*
1056  * Copy a mount options table.
1057  *
1058  * This function is *not* for general use by filesystems.
1059  *
1060  * Note: caller is responsible for locking the vfs list, if needed,
1061  *       to protect smo and dmo.
1062  */
1063 void
1064 vfs_copyopttbl(const mntopts_t *smo, mntopts_t *dmo)
1065 {
1066 	vfs_copyopttbl_extend(smo, dmo, 0);
1067 }
1068 
1069 static char **
1070 vfs_mergecancelopts(const mntopt_t *mop1, const mntopt_t *mop2)
1071 {
1072 	int c1 = 0;
1073 	int c2 = 0;
1074 	char **result;
1075 	char **sp1, **sp2, **dp;
1076 
1077 	/*
1078 	 * First we count both lists of cancel options.
1079 	 * If either is NULL or has no elements, we return a copy of
1080 	 * the other.
1081 	 */
1082 	if (mop1->mo_cancel != NULL) {
1083 		for (; mop1->mo_cancel[c1] != NULL; c1++)
1084 			/* count cancel options in mop1 */;
1085 	}
1086 
1087 	if (c1 == 0)
1088 		return (vfs_copycancelopt_extend(mop2->mo_cancel, 0));
1089 
1090 	if (mop2->mo_cancel != NULL) {
1091 		for (; mop2->mo_cancel[c2] != NULL; c2++)
1092 			/* count cancel options in mop2 */;
1093 	}
1094 
1095 	result = vfs_copycancelopt_extend(mop1->mo_cancel, c2);
1096 
1097 	if (c2 == 0)
1098 		return (result);
1099 
1100 	/*
1101 	 * When we get here, we've got two sets of cancel options;
1102 	 * we need to merge the two sets.  We know that the result
1103 	 * array has "c1+c2+1" entries and in the end we might shrink
1104 	 * it.
1105 	 * Result now has a copy of the c1 entries from mop1; we'll
1106 	 * now lookup all the entries of mop2 in mop1 and copy it if
1107 	 * it is unique.
1108 	 * This operation is O(n^2) but it's only called once per
1109 	 * filesystem per duplicate option.  This is a situation
1110 	 * which doesn't arise with the filesystems in ON and
1111 	 * n is generally 1.
1112 	 */
1113 
1114 	dp = &result[c1];
1115 	for (sp2 = mop2->mo_cancel; *sp2 != NULL; sp2++) {
1116 		for (sp1 = mop1->mo_cancel; *sp1 != NULL; sp1++) {
1117 			if (strcmp(*sp1, *sp2) == 0)
1118 				break;
1119 		}
1120 		if (*sp1 == NULL) {
1121 			/*
1122 			 * Option *sp2 not found in mop1, so copy it.
1123 			 * The calls to vfs_copycancelopt_extend()
1124 			 * guarantee that there's enough room.
1125 			 */
1126 			*dp = kmem_alloc(strlen(*sp2) + 1, KM_SLEEP);
1127 			(void) strcpy(*dp++, *sp2);
1128 		}
1129 	}
1130 	if (dp != &result[c1+c2]) {
1131 		size_t bytes = (dp - result + 1) * sizeof (char *);
1132 		char **nres = kmem_alloc(bytes, KM_SLEEP);
1133 
1134 		bcopy(result, nres, bytes);
1135 		kmem_free(result, (c1 + c2 + 1) * sizeof (char *));
1136 		result = nres;
1137 	}
1138 	return (result);
1139 }
1140 
1141 /*
1142  * Merge two mount option tables (outer and inner) into one.  This is very
1143  * similar to "merging" global variables and automatic variables in C.
1144  *
1145  * This isn't (and doesn't have to be) fast.
1146  *
1147  * This function is *not* for general use by filesystems.
1148  *
1149  * Note: caller is responsible for locking the vfs list, if needed,
1150  *       to protect omo, imo & dmo.
1151  */
1152 void
1153 vfs_mergeopttbl(const mntopts_t *omo, const mntopts_t *imo, mntopts_t *dmo)
1154 {
1155 	uint_t i, count;
1156 	mntopt_t *mop, *motbl;
1157 	uint_t freeidx;
1158 
1159 	/*
1160 	 * First determine how much space we need to allocate.
1161 	 */
1162 	count = omo->mo_count;
1163 	for (i = 0; i < imo->mo_count; i++) {
1164 		if (imo->mo_list[i].mo_flags & MO_EMPTY)
1165 			continue;
1166 		if (vfs_hasopt(omo, imo->mo_list[i].mo_name) == NULL)
1167 			count++;
1168 	}
1169 	ASSERT(count >= omo->mo_count &&
1170 	    count <= omo->mo_count + imo->mo_count);
1171 	motbl = kmem_alloc(count * sizeof (mntopt_t), KM_SLEEP);
1172 	for (i = 0; i < omo->mo_count; i++)
1173 		vfs_copyopt(&omo->mo_list[i], &motbl[i]);
1174 	freeidx = omo->mo_count;
1175 	for (i = 0; i < imo->mo_count; i++) {
1176 		if (imo->mo_list[i].mo_flags & MO_EMPTY)
1177 			continue;
1178 		if ((mop = vfs_hasopt(omo, imo->mo_list[i].mo_name)) != NULL) {
1179 			char **newcanp;
1180 			uint_t index = mop - omo->mo_list;
1181 
1182 			newcanp = vfs_mergecancelopts(mop, &motbl[index]);
1183 
1184 			vfs_freeopt(&motbl[index]);
1185 			vfs_copyopt(&imo->mo_list[i], &motbl[index]);
1186 
1187 			vfs_freecancelopt(motbl[index].mo_cancel);
1188 			motbl[index].mo_cancel = newcanp;
1189 		} else {
1190 			/*
1191 			 * If it's a new option, just copy it over to the first
1192 			 * free location.
1193 			 */
1194 			vfs_copyopt(&imo->mo_list[i], &motbl[freeidx++]);
1195 		}
1196 	}
1197 	dmo->mo_count = count;
1198 	dmo->mo_list = motbl;
1199 }
1200 
1201 /*
1202  * Functions to set and clear mount options in a mount options table.
1203  */
1204 
1205 /*
1206  * Clear a mount option, if it exists.
1207  *
1208  * The update_mnttab arg indicates whether mops is part of a vfs that is on
1209  * the vfs list.
1210  */
1211 static void
1212 vfs_clearmntopt_nolock(mntopts_t *mops, const char *opt, int update_mnttab)
1213 {
1214 	struct mntopt *mop;
1215 	uint_t i, count;
1216 
1217 	ASSERT(!update_mnttab || RW_WRITE_HELD(&vfslist));
1218 
1219 	count = mops->mo_count;
1220 	for (i = 0; i < count; i++) {
1221 		mop = &mops->mo_list[i];
1222 
1223 		if (mop->mo_flags & MO_EMPTY)
1224 			continue;
1225 		if (strcmp(opt, mop->mo_name))
1226 			continue;
1227 		mop->mo_flags &= ~MO_SET;
1228 		if (mop->mo_arg != NULL) {
1229 			kmem_free(mop->mo_arg, strlen(mop->mo_arg) + 1);
1230 		}
1231 		mop->mo_arg = NULL;
1232 		if (update_mnttab)
1233 			vfs_mnttab_modtimeupd();
1234 		break;
1235 	}
1236 }
1237 
1238 void
1239 vfs_clearmntopt(struct vfs *vfsp, const char *opt)
1240 {
1241 	int gotlock = 0;
1242 
1243 	if (VFS_ON_LIST(vfsp)) {
1244 		gotlock = 1;
1245 		vfs_list_lock();
1246 	}
1247 	vfs_clearmntopt_nolock(&vfsp->vfs_mntopts, opt, gotlock);
1248 	if (gotlock)
1249 		vfs_list_unlock();
1250 }
1251 
1252 
1253 /*
1254  * Set a mount option on...
1255  */
1256 static void
1257 vfs_setmntopt_nolock(mntopts_t *mops, const char *opt,
1258     const char *arg, int flags, int update_mnttab)
1259 {
1260 	mntopt_t *mop;
1261 	uint_t i, count;
1262 	char *sp;
1263 
1264 	ASSERT(!update_mnttab || RW_WRITE_HELD(&vfslist));
1265 
1266 	if (flags & VFS_CREATEOPT) {
1267 		if (vfs_hasopt(mops, opt) != NULL) {
1268 			flags &= ~VFS_CREATEOPT;
1269 		}
1270 	}
1271 	count = mops->mo_count;
1272 	for (i = 0; i < count; i++) {
1273 		mop = &mops->mo_list[i];
1274 
1275 		if (mop->mo_flags & MO_EMPTY) {
1276 			if ((flags & VFS_CREATEOPT) == 0)
1277 				continue;
1278 			sp = kmem_alloc(strlen(opt) + 1, KM_SLEEP);
1279 			(void) strcpy(sp, opt);
1280 			mop->mo_name = sp;
1281 			if (arg != NULL)
1282 				mop->mo_flags = MO_HASVALUE;
1283 			else
1284 				mop->mo_flags = 0;
1285 		} else if (strcmp(opt, mop->mo_name)) {
1286 			continue;
1287 		}
1288 		if ((mop->mo_flags & MO_IGNORE) && (flags & VFS_NOFORCEOPT))
1289 			break;
1290 		if (arg != NULL && (mop->mo_flags & MO_HASVALUE) != 0) {
1291 			sp = kmem_alloc(strlen(arg) + 1, KM_SLEEP);
1292 			(void) strcpy(sp, arg);
1293 		} else {
1294 			sp = NULL;
1295 		}
1296 		if (mop->mo_arg != NULL)
1297 			kmem_free(mop->mo_arg, strlen(mop->mo_arg) + 1);
1298 		mop->mo_arg = sp;
1299 		if (flags & VFS_DISPLAY)
1300 			mop->mo_flags &= ~MO_NODISPLAY;
1301 		if (flags & VFS_NODISPLAY)
1302 			mop->mo_flags |= MO_NODISPLAY;
1303 		mop->mo_flags |= MO_SET;
1304 		if (mop->mo_cancel != NULL) {
1305 			char **cp;
1306 
1307 			for (cp = mop->mo_cancel; *cp != NULL; cp++)
1308 				vfs_clearmntopt_nolock(mops, *cp, 0);
1309 		}
1310 		if (update_mnttab)
1311 			vfs_mnttab_modtimeupd();
1312 		break;
1313 	}
1314 }
1315 
1316 void
1317 vfs_setmntopt(struct vfs *vfsp, const char *opt, const char *arg, int flags)
1318 {
1319 	int gotlock = 0;
1320 
1321 	if (VFS_ON_LIST(vfsp)) {
1322 		gotlock = 1;
1323 		vfs_list_lock();
1324 	}
1325 	vfs_setmntopt_nolock(&vfsp->vfs_mntopts, opt, arg, flags, gotlock);
1326 	if (gotlock)
1327 		vfs_list_unlock();
1328 }
1329 
1330 // vfs_addtag
1331 // vfs_settag
1332 // vfs_clrtag
1333 
1334 /*
1335  * Function to parse an option string and fill in a mount options table.
1336  * Unknown options are silently ignored.  The input option string is modified
1337  * by replacing separators with nulls.  If the create flag is set, options
1338  * not found in the table are just added on the fly.  The table must have
1339  * an option slot marked MO_EMPTY to add an option on the fly.
1340  *
1341  * This function is *not* for general use by filesystems.
1342  *
1343  * Note: caller is responsible for locking the vfs list, if needed,
1344  *       to protect mops..
1345  */
1346 void
1347 vfs_parsemntopts(mntopts_t *mops, char *osp, int create)
1348 {
1349 	char *s = osp, *p, *nextop, *valp, *cp, *ep = NULL;
1350 	int setflg = VFS_NOFORCEOPT;
1351 
1352 	if (osp == NULL)
1353 		return;
1354 	while (*s != '\0') {
1355 		p = strchr(s, ',');	/* find next option */
1356 		if (p == NULL) {
1357 			cp = NULL;
1358 			p = s + strlen(s);
1359 		} else {
1360 			cp = p;		/* save location of comma */
1361 			*p++ = '\0';	/* mark end and point to next option */
1362 		}
1363 		nextop = p;
1364 		p = strchr(s, '=');	/* look for value */
1365 		if (p == NULL) {
1366 			valp = NULL;	/* no value supplied */
1367 			ep = NULL;
1368 		} else {
1369 			ep = p;		/* save location of equals */
1370 			*p++ = '\0';	/* end option and point to value */
1371 			valp = p;
1372 		}
1373 		/*
1374 		 * set option into options table
1375 		 */
1376 		if (create)
1377 			setflg |= VFS_CREATEOPT;
1378 		vfs_setmntopt_nolock(mops, s, valp, setflg, 0);
1379 		if (cp != NULL)
1380 			*cp = ',';	/* restore the comma */
1381 		if (valp != NULL)
1382 			*ep = '=';	/* restore the equals */
1383 		s = nextop;
1384 	}
1385 }
1386 
1387 /*
1388  * Function to inquire if an option exists in a mount options table.
1389  * Returns a pointer to the option if it exists, else NULL.
1390  */
1391 struct mntopt *
1392 vfs_hasopt(const mntopts_t *mops, const char *opt)
1393 {
1394 	struct mntopt *mop;
1395 	uint_t i, count;
1396 
1397 	count = mops->mo_count;
1398 	for (i = 0; i < count; i++) {
1399 		mop = &mops->mo_list[i];
1400 
1401 		if (mop->mo_flags & MO_EMPTY)
1402 			continue;
1403 		if (strcmp(opt, mop->mo_name) == 0)
1404 			return (mop);
1405 	}
1406 	return (NULL);
1407 }
1408 
1409 /*
1410  * Function to inquire if an option is set in a mount options table.
1411  * Returns non-zero if set and fills in the arg pointer with a pointer to
1412  * the argument string or NULL if there is no argument string.
1413  */
1414 static int
1415 vfs_optionisset_nolock(const mntopts_t *mops, const char *opt, char **argp)
1416 {
1417 	struct mntopt *mop;
1418 	uint_t i, count;
1419 
1420 	count = mops->mo_count;
1421 	for (i = 0; i < count; i++) {
1422 		mop = &mops->mo_list[i];
1423 
1424 		if (mop->mo_flags & MO_EMPTY)
1425 			continue;
1426 		if (strcmp(opt, mop->mo_name))
1427 			continue;
1428 		if ((mop->mo_flags & MO_SET) == 0)
1429 			return (0);
1430 		if (argp != NULL && (mop->mo_flags & MO_HASVALUE) != 0)
1431 			*argp = mop->mo_arg;
1432 		return (1);
1433 	}
1434 	return (0);
1435 }
1436 
1437 
1438 int
1439 vfs_optionisset(const struct vfs *vfsp, const char *opt, char **argp)
1440 {
1441 	int ret;
1442 
1443 	vfs_list_read_lock();
1444 	ret = vfs_optionisset_nolock(&vfsp->vfs_mntopts, opt, argp);
1445 	vfs_list_unlock();
1446 	return (ret);
1447 }
1448 
1449 
1450 /*
1451  * Construct a comma separated string of the options set in the given
1452  * mount table, return the string in the given buffer.  Return non-zero if
1453  * the buffer would overflow.
1454  *
1455  * This function is *not* for general use by filesystems.
1456  *
1457  * Note: caller is responsible for locking the vfs list, if needed,
1458  *       to protect mp.
1459  */
1460 int
1461 vfs_buildoptionstr(const mntopts_t *mp, char *buf, int len)
1462 {
1463 	char *cp;
1464 	uint_t i;
1465 
1466 	buf[0] = '\0';
1467 	cp = buf;
1468 	for (i = 0; i < mp->mo_count; i++) {
1469 		struct mntopt *mop;
1470 
1471 		mop = &mp->mo_list[i];
1472 		if (mop->mo_flags & MO_SET) {
1473 			int optlen, comma = 0;
1474 
1475 			if (buf[0] != '\0')
1476 				comma = 1;
1477 			optlen = strlen(mop->mo_name);
1478 			if (strlen(buf) + comma + optlen + 1 > len)
1479 				goto err;
1480 			if (comma)
1481 				*cp++ = ',';
1482 			(void) strcpy(cp, mop->mo_name);
1483 			cp += optlen;
1484 			/*
1485 			 * Append option value if there is one
1486 			 */
1487 			if (mop->mo_arg != NULL) {
1488 				int arglen;
1489 
1490 				arglen = strlen(mop->mo_arg);
1491 				if (strlen(buf) + arglen + 2 > len)
1492 					goto err;
1493 				*cp++ = '=';
1494 				(void) strcpy(cp, mop->mo_arg);
1495 				cp += arglen;
1496 			}
1497 		}
1498 	}
1499 	return (0);
1500 err:
1501 	return (EOVERFLOW);
1502 }
1503 
1504 static void
1505 vfs_freecancelopt(char **moc)
1506 {
1507 	if (moc != NULL) {
1508 		int ccnt = 0;
1509 		char **cp;
1510 
1511 		for (cp = moc; *cp != NULL; cp++) {
1512 			kmem_free(*cp, strlen(*cp) + 1);
1513 			ccnt++;
1514 		}
1515 		kmem_free(moc, (ccnt + 1) * sizeof (char *));
1516 	}
1517 }
1518 
1519 static void
1520 vfs_freeopt(mntopt_t *mop)
1521 {
1522 	if (mop->mo_name != NULL)
1523 		kmem_free(mop->mo_name, strlen(mop->mo_name) + 1);
1524 
1525 	vfs_freecancelopt(mop->mo_cancel);
1526 
1527 	if (mop->mo_arg != NULL)
1528 		kmem_free(mop->mo_arg, strlen(mop->mo_arg) + 1);
1529 }
1530 
1531 /*
1532  * Free a mount options table
1533  *
1534  * This function is *not* for general use by filesystems.
1535  *
1536  * Note: caller is responsible for locking the vfs list, if needed,
1537  *       to protect mp.
1538  */
1539 void
1540 vfs_freeopttbl(mntopts_t *mp)
1541 {
1542 	uint_t i, count;
1543 
1544 	count = mp->mo_count;
1545 	for (i = 0; i < count; i++) {
1546 		vfs_freeopt(&mp->mo_list[i]);
1547 	}
1548 	if (count) {
1549 		kmem_free(mp->mo_list, sizeof (mntopt_t) * count);
1550 		mp->mo_count = 0;
1551 		mp->mo_list = NULL;
1552 	}
1553 }
1554 
1555 // vfs_mntdummyread
1556 // vfs_mntdummywrite
1557 // vfs_mntdummygetattr
1558 // vfs_mnttabvp_setup
1559 // vfs_mnttab_rwop
1560 // vfs_mnttab_writeop
1561 // vfs_mnttab_readop
1562 // vfs_freemnttab
1563 // vfs_mnttab_modtime
1564 // vfs_mnttab_poll
1565 // vfs_mono_time
1566 
1567 /*
1568  * Update the mnttab modification time...
1569  */
1570 void
1571 vfs_mnttab_modtimeupd()
1572 {
1573 }
1574 
1575 /*
1576  * Unlike the real dounmount, we don't have
1577  * vn_vfswlock_held(coveredvp)
1578  */
1579 int
1580 fake_dounmount(struct vfs *vfsp, int flag)
1581 {
1582 	cred_t *cr = CRED();
1583 	vnode_t *coveredvp;
1584 	int error;
1585 
1586 	/*
1587 	 * Get covered vnode. This will be NULL if the vfs is not linked
1588 	 * into the file system name space (i.e., domount() with MNT_NOSPICE).
1589 	 */
1590 	coveredvp = vfsp->vfs_vnodecovered;
1591 
1592 	/* For forcible umount, skip VFS_SYNC() since it may hang */
1593 	if ((flag & MS_FORCE) == 0)
1594 		(void) VFS_SYNC(vfsp, 0, cr);
1595 
1596 	/*
1597 	 * Test-jig specific:
1598 	 * Need to release rootdir before unmount or VFS_UNMOUNT
1599 	 * may fail due to that node being active.
1600 	 */
1601 	if (rootdir != NULL) {
1602 		ASSERT(rootdir != coveredvp);
1603 		VN_RELE(rootdir);
1604 		rootdir = NULL;
1605 	}
1606 
1607 	/*
1608 	 * Lock the vfs to maintain fs status quo during unmount.  This
1609 	 * has to be done after the sync because ufs_update tries to acquire
1610 	 * the vfs_reflock.
1611 	 */
1612 	vfs_lock_wait(vfsp);
1613 
1614 	if ((error = VFS_UNMOUNT(vfsp, flag, cr)) != 0) {
1615 		int err2;
1616 		vfs_unlock(vfsp);
1617 		/* Get rootdir back */
1618 		err2 = VFS_ROOT(vfsp, &rootdir);
1619 		if (err2 != 0) {
1620 			panic("fake_dounmount, get root %d\n", err2);
1621 		}
1622 	} else {
1623 		/*
1624 		 * Real dounmount does vfs_remove.
1625 		 *
1626 		 * Test-jig specific:
1627 		 * Restore the covered rootdir,
1628 		 * release the rootvfs hold and clear.
1629 		 */
1630 		if (coveredvp != NULL) {
1631 			// vfs_list_remove(vfsp);
1632 			vfsp->vfs_vnodecovered = NULL;
1633 			rootdir = coveredvp;
1634 		}
1635 		if (rootvfs == vfsp) {
1636 			VFS_RELE(vfsp);
1637 			rootvfs = NULL;
1638 		}
1639 
1640 		/*
1641 		 * Release the (final) reference to vfs
1642 		 */
1643 		vfs_unlock(vfsp);
1644 		VFS_RELE(vfsp);
1645 	}
1646 	return (error);
1647 }
1648 
1649 // vfs_unmountall(void)
1650 // vfs_addmip
1651 // vfs_delmip
1652 // vfs_add
1653 // vfs_remove
1654 
1655 static krwlock_t vpvfsentry_ve_lock;
1656 
1657 /*
1658  * Lock a filesystem to prevent access to it while mounting,
1659  * unmounting and syncing.  Return EBUSY immediately if lock
1660  * can't be acquired.
1661  */
1662 int
1663 vfs_lock(vfs_t *vfsp)
1664 {
1665 
1666 	if (rw_tryenter(&vpvfsentry_ve_lock, RW_WRITER))
1667 		return (0);
1668 
1669 	return (EBUSY);
1670 }
1671 
1672 int
1673 vfs_rlock(vfs_t *vfsp)
1674 {
1675 
1676 	if (rw_tryenter(&vpvfsentry_ve_lock, RW_READER))
1677 		return (0);
1678 
1679 	return (EBUSY);
1680 }
1681 
1682 void
1683 vfs_lock_wait(vfs_t *vfsp)
1684 {
1685 
1686 	rw_enter(&vpvfsentry_ve_lock, RW_WRITER);
1687 }
1688 
1689 void
1690 vfs_rlock_wait(vfs_t *vfsp)
1691 {
1692 	rw_enter(&vpvfsentry_ve_lock, RW_READER);
1693 }
1694 
1695 /*
1696  * Unlock a locked filesystem.
1697  */
1698 void
1699 vfs_unlock(vfs_t *vfsp)
1700 {
1701 
1702 	rw_exit(&vpvfsentry_ve_lock);
1703 }
1704 
1705 /*
1706  * Utility routine that allows a filesystem to construct its
1707  * fsid in "the usual way" - by munging some underlying dev_t and
1708  * the filesystem type number into the 64-bit fsid. ...
1709  */
1710 void
1711 vfs_make_fsid(fsid_t *fsi, dev_t dev, int val)
1712 {
1713 	if (!cmpldev((dev32_t *)&fsi->val[0], dev))
1714 		panic("device number too big for fsid!");
1715 	fsi->val[1] = val;
1716 }
1717 
1718 int
1719 vfs_lock_held(vfs_t *vfsp)
1720 {
1721 	int held;
1722 
1723 	held = rw_write_held(&vpvfsentry_ve_lock);
1724 
1725 	return (held);
1726 }
1727 
1728 // vfs_lock_owner
1729 
1730 /*
1731  * vfs list locking.
1732  */
1733 
1734 void
1735 vfs_list_lock()
1736 {
1737 	rw_enter(&vfslist, RW_WRITER);
1738 }
1739 
1740 void
1741 vfs_list_read_lock()
1742 {
1743 	rw_enter(&vfslist, RW_READER);
1744 }
1745 
1746 void
1747 vfs_list_unlock()
1748 {
1749 	rw_exit(&vfslist);
1750 }
1751 
1752 /*
1753  * Low level worker routines for adding entries to and removing entries from
1754  * the vfs list.
1755  */
1756 
1757 // vfs_hash_add
1758 // vfs_hash_remove
1759 // vfs_list_add
1760 // vfs_list_remove
1761 // getvfs
1762 // vfs_devmounting
1763 
1764 /*
1765  * Search the vfs list for a specified device.  Returns 1, if entry is found
1766  * or 0 if no suitable entry is found.
1767  */
1768 
1769 int
1770 vfs_devismounted(dev_t dev)
1771 {
1772 	return (0);
1773 }
1774 
1775 // vfs_dev2vfsp
1776 // vfs_mntpoint2vfsp
1777 
1778 /*
1779  * Search the vfs list for a specified vfsops.
1780  * if vfs entry is found then return 1, else 0.
1781  */
1782 int
1783 vfs_opsinuse(vfsops_t *ops)
1784 {
1785 	return (0);
1786 }
1787 
1788 /*
1789  * Allocate an entry in vfssw for a file system type
1790  */
1791 struct vfssw *
1792 allocate_vfssw(const char *type)
1793 {
1794 	struct vfssw *vswp;
1795 
1796 	if (type[0] == '\0' || strlen(type) + 1 > _ST_FSTYPSZ) {
1797 		/*
1798 		 * The vfssw table uses the empty string to identify an
1799 		 * available entry; we cannot add any type which has
1800 		 * a leading NUL. The string length is limited to
1801 		 * the size of the st_fstype array in struct stat.
1802 		 */
1803 		return (NULL);
1804 	}
1805 
1806 	ASSERT(VFSSW_WRITE_LOCKED());
1807 	for (vswp = &vfssw[1]; vswp < &vfssw[nfstype]; vswp++)
1808 		if (!ALLOCATED_VFSSW(vswp)) {
1809 			vswp->vsw_name = kmem_alloc(strlen(type) + 1, KM_SLEEP);
1810 			(void) strcpy(vswp->vsw_name, type);
1811 			ASSERT(vswp->vsw_count == 0);
1812 			vswp->vsw_count = 1;
1813 			mutex_init(&vswp->vsw_lock, NULL, MUTEX_DEFAULT, NULL);
1814 			return (vswp);
1815 		}
1816 	return (NULL);
1817 }
1818 
1819 // vfs_to_modname
1820 // vfs_getvfssw
1821 
1822 /*
1823  * Find a vfssw entry given a file system type name.
1824  */
1825 struct vfssw *
1826 vfs_getvfssw(const char *type)
1827 {
1828 	struct vfssw *vswp;
1829 
1830 	if (type == NULL || *type == '\0')
1831 		return (NULL);
1832 
1833 	for (vswp = &vfssw[1]; vswp < &vfssw[nfstype]; vswp++) {
1834 		if (strcmp(type, vswp->vsw_name) == 0) {
1835 			return (vswp);
1836 		}
1837 	}
1838 
1839 	return (NULL);
1840 
1841 }
1842 
1843 /*
1844  * Find a vfssw entry given a file system type name.
1845  */
1846 struct vfssw *
1847 vfs_getvfsswbyname(const char *type)
1848 {
1849 	struct vfssw *vswp;
1850 
1851 	ASSERT(VFSSW_LOCKED());
1852 	if (type == NULL || *type == '\0')
1853 		return (NULL);
1854 
1855 	for (vswp = &vfssw[1]; vswp < &vfssw[nfstype]; vswp++) {
1856 		if (strcmp(type, vswp->vsw_name) == 0) {
1857 			vfs_refvfssw(vswp);
1858 			return (vswp);
1859 		}
1860 	}
1861 
1862 	return (NULL);
1863 }
1864 
1865 // vfs_getvfsswbyvfsops
1866 
1867 /*
1868  * Reference a vfssw entry.
1869  */
1870 void
1871 vfs_refvfssw(struct vfssw *vswp)
1872 {
1873 
1874 	mutex_enter(&vswp->vsw_lock);
1875 	vswp->vsw_count++;
1876 	mutex_exit(&vswp->vsw_lock);
1877 }
1878 
1879 /*
1880  * Unreference a vfssw entry.
1881  */
1882 void
1883 vfs_unrefvfssw(struct vfssw *vswp)
1884 {
1885 
1886 	mutex_enter(&vswp->vsw_lock);
1887 	vswp->vsw_count--;
1888 	mutex_exit(&vswp->vsw_lock);
1889 }
1890 
1891 // vfs_syncall
1892 
1893 /*
1894  * Map VFS flags to statvfs flags.  These shouldn't really be separate
1895  * flags at all.
1896  */
1897 uint_t
1898 vf_to_stf(uint_t vf)
1899 {
1900 	uint_t stf = 0;
1901 
1902 	if (vf & VFS_RDONLY)
1903 		stf |= ST_RDONLY;
1904 	if (vf & VFS_NOSETUID)
1905 		stf |= ST_NOSUID;
1906 	if (vf & VFS_NOTRUNC)
1907 		stf |= ST_NOTRUNC;
1908 
1909 	return (stf);
1910 }
1911 
1912 // vfsstray_sync
1913 // vfsstray
1914 // vfs_EIO
1915 // vfs_EIO_sync
1916 // EIO_vfs
1917 // EIO_vfsops
1918 
1919 #pragma init(vfsinit)
1920 
1921 /*
1922  * Called from startup() to initialize all loaded vfs's
1923  */
1924 void
1925 vfsinit(void)
1926 {
1927 	vn_create_cache();
1928 
1929 	/* Temporary, until we mount root */
1930 	rootdir = vn_alloc(KM_SLEEP);
1931 	rootdir->v_type = VDIR;
1932 }
1933 
1934 vfs_t *
1935 vfs_alloc(int kmflag)
1936 {
1937 	vfs_t *vfsp;
1938 
1939 	vfsp = kmem_alloc(sizeof (struct vfs), kmflag);
1940 
1941 	/*
1942 	 * Do the simplest initialization here.
1943 	 * Everything else gets done in vfs_init()
1944 	 */
1945 	bzero(vfsp, sizeof (vfs_t));
1946 	return (vfsp);
1947 }
1948 
1949 void
1950 vfs_free(vfs_t *vfsp)
1951 {
1952 	/*
1953 	 * One would be tempted to assert that "vfsp->vfs_count == 0".
1954 	 * Don't.  See fs/vfs.c
1955 	 */
1956 
1957 	/* If FEM was in use, make sure everything gets cleaned up */
1958 
1959 	if (vfsp->vfs_implp)
1960 		vfsimpl_teardown(vfsp);
1961 	sema_destroy(&vfsp->vfs_reflock);
1962 	kmem_free(vfsp, sizeof (struct vfs));
1963 }
1964 
1965 /*
1966  * Increments the vfs reference count by one atomically.
1967  */
1968 void
1969 vfs_hold(vfs_t *vfsp)
1970 {
1971 	atomic_inc_32(&vfsp->vfs_count);
1972 	ASSERT(vfsp->vfs_count != 0);
1973 }
1974 
1975 /*
1976  * Decrements the vfs reference count by one atomically. When
1977  * vfs reference count becomes zero, it calls the file system
1978  * specific vfs_freevfs() to free up the resources.
1979  */
1980 void
1981 vfs_rele(vfs_t *vfsp)
1982 {
1983 	ASSERT(vfsp->vfs_count != 0);
1984 	if (atomic_dec_32_nv(&vfsp->vfs_count) == 0) {
1985 		VFS_FREEVFS(vfsp);
1986 		// lofi_remove(vfsp);
1987 		// zone_rele_ref...
1988 		// vfs_freemnttab(vfsp);
1989 		vfs_free(vfsp);
1990 	}
1991 }
1992 
1993 /*
1994  * Generic operations vector support.
1995  */
1996 
1997 int
1998 fs_build_vector(void *vector, int *unused_ops,
1999     const fs_operation_trans_def_t *translation,
2000     const fs_operation_def_t *operations)
2001 {
2002 	int i, num_trans, num_ops, used;
2003 
2004 	/*
2005 	 * Count the number of translations and the number of supplied
2006 	 * operations.
2007 	 */
2008 
2009 	{
2010 		const fs_operation_trans_def_t *p;
2011 
2012 		for (num_trans = 0, p = translation;
2013 		    p->name != NULL;
2014 		    num_trans++, p++)
2015 			;
2016 	}
2017 
2018 	{
2019 		const fs_operation_def_t *p;
2020 
2021 		for (num_ops = 0, p = operations;
2022 		    p->name != NULL;
2023 		    num_ops++, p++)
2024 			;
2025 	}
2026 
2027 	/* Walk through each operation known to our caller.  There will be */
2028 	/* one entry in the supplied "translation table" for each. */
2029 
2030 	used = 0;
2031 
2032 	for (i = 0; i < num_trans; i++) {
2033 		int j, found;
2034 		char *curname;
2035 		fs_generic_func_p result;
2036 		fs_generic_func_p *location;
2037 
2038 		curname = translation[i].name;
2039 
2040 		/* Look for a matching operation in the list supplied by the */
2041 		/* file system. */
2042 
2043 		found = 0;
2044 
2045 		for (j = 0; j < num_ops; j++) {
2046 			if (strcmp(operations[j].name, curname) == 0) {
2047 				used++;
2048 				found = 1;
2049 				break;
2050 			}
2051 		}
2052 
2053 		/*
2054 		 * If the file system is using a "placeholder" for default
2055 		 * or error functions, grab the appropriate function out of
2056 		 * the translation table.  If the file system didn't supply
2057 		 * this operation at all, use the default function.
2058 		 */
2059 
2060 		if (found) {
2061 			result = operations[j].func.fs_generic;
2062 			if (result == fs_default) {
2063 				result = translation[i].defaultFunc;
2064 			} else if (result == fs_error) {
2065 				result = translation[i].errorFunc;
2066 			} else if (result == NULL) {
2067 				/* Null values are PROHIBITED */
2068 				return (EINVAL);
2069 			}
2070 		} else {
2071 			result = translation[i].defaultFunc;
2072 		}
2073 
2074 		/* Now store the function into the operations vector. */
2075 
2076 		/* LINTED E_BAD_PTR_CAST_ALIGN */
2077 		location = (fs_generic_func_p *)
2078 		    (((char *)vector) + translation[i].offset);
2079 
2080 		*location = result;
2081 	}
2082 
2083 	*unused_ops = num_ops - used;
2084 
2085 	return (0);
2086 }
2087 
2088 /* Placeholder functions, should never be called. */
2089 
2090 int
2091 fs_error(void)
2092 {
2093 	cmn_err(CE_PANIC, "fs_error called");
2094 	return (0);
2095 }
2096 
2097 int
2098 fs_default(void)
2099 {
2100 	cmn_err(CE_PANIC, "fs_default called");
2101 	return (0);
2102 }
2103 
2104 // rootconf
2105 // getfsname
2106 // getrootfs
2107 
2108 /*
2109  * VFS feature routines
2110  */
2111 
2112 #define	VFTINDEX(feature)	(((feature) >> 32) & 0xFFFFFFFF)
2113 #define	VFTBITS(feature)	((feature) & 0xFFFFFFFFLL)
2114 
2115 /* Register a feature in the vfs */
2116 void
2117 vfs_set_feature(vfs_t *vfsp, vfs_feature_t feature)
2118 {
2119 	/* Note that vfs_featureset[] is found in *vfsp->vfs_implp */
2120 	if (vfsp->vfs_implp == NULL)
2121 		return;
2122 
2123 	vfsp->vfs_featureset[VFTINDEX(feature)] |= VFTBITS(feature);
2124 }
2125 
2126 void
2127 vfs_clear_feature(vfs_t *vfsp, vfs_feature_t feature)
2128 {
2129 	/* Note that vfs_featureset[] is found in *vfsp->vfs_implp */
2130 	if (vfsp->vfs_implp == NULL)
2131 		return;
2132 	vfsp->vfs_featureset[VFTINDEX(feature)] &= VFTBITS(~feature);
2133 }
2134 
2135 /*
2136  * Query a vfs for a feature.
2137  * Returns 1 if feature is present, 0 if not
2138  */
2139 int
2140 vfs_has_feature(vfs_t *vfsp, vfs_feature_t feature)
2141 {
2142 	int	ret = 0;
2143 
2144 	/* Note that vfs_featureset[] is found in *vfsp->vfs_implp */
2145 	if (vfsp->vfs_implp == NULL)
2146 		return (ret);
2147 
2148 	if (vfsp->vfs_featureset[VFTINDEX(feature)] & VFTBITS(feature))
2149 		ret = 1;
2150 
2151 	return (ret);
2152 }
2153 
2154 // vfs_propagate_features
2155 // vfs_get_lofi
2156