--- /dev/null	Mon Aug  5 17:17:03 2019
+++ new/usr/src/lib/smbclnt/libfksmbfs/common/fake_vfs.c	Mon Aug  5 17:17:03 2019
@@ -0,0 +1,2155 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
+ * Copyright 2016 Toomas Soome <tsoome@me.com>
+ * Copyright (c) 2016 by Delphix. All rights reserved.
+ * Copyright 2017 RackTop Systems.
+ * Copyright 2018 Nexenta Systems, Inc.
+ */
+
+/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
+/*	  All Rights Reserved  	*/
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+/*
+ * This file contains those functions from fs/vfs.c that can be
+ * used with relatively little change.  Functions that differ
+ * significantly from that are in other files.
+ */
+
+#include <sys/types.h>
+#include <sys/t_lock.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/user.h>
+#include <sys/fstyp.h>
+#include <sys/kmem.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/fem.h>
+#include <sys/mntent.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/statfs.h>
+#include <sys/cred.h>
+#include <sys/vnode.h>
+#include <sys/rwstlock.h>
+#include <sys/dnlc.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/atomic.h>
+#include <sys/cmn_err.h>
+#include <sys/buf.h>
+#include <sys/debug.h>
+#include <sys/vnode.h>
+#include <sys/ddi.h>
+#include <sys/pathname.h>
+#include <sys/poll.h>
+#include <sys/sunddi.h>
+#include <sys/sysmacros.h>
+#include <sys/zone.h>
+#include <sys/policy.h>
+#include <sys/attr.h>
+#include <fs/fs_subr.h>
+
+#include <libfksmbfs.h>
+
+static void vfs_clearmntopt_nolock(mntopts_t *, const char *, int);
+static void vfs_setmntopt_nolock(mntopts_t *, const char *,
+    const char *, int, int);
+static int  vfs_optionisset_nolock(const mntopts_t *, const char *, char **);
+// static void vfs_freemnttab(struct vfs *);
+static void vfs_freeopt(mntopt_t *);
+static void vfs_swapopttbl_nolock(mntopts_t *, mntopts_t *);
+static void vfs_swapopttbl(mntopts_t *, mntopts_t *);
+static void vfs_copyopttbl_extend(const mntopts_t *, mntopts_t *, int);
+// static void vfs_createopttbl_extend(mntopts_t *, const char *,
+//    const mntopts_t *);
+// static char **vfs_copycancelopt_extend(char **const, int);
+static void vfs_freecancelopt(char **);
+
+/*
+ * VFS global data.
+ */
+vnode_t *rootdir;		/* pointer to root inode vnode. */
+struct vfs *rootvfs = NULL;	/* pointer to root vfs; head of VFS list. */
+static krwlock_t vfslist;
+struct vfs	*zone_vfslist;	/* list of FS's mounted in zone */
+
+/* from os/vfs_conf.c */
+const int nfstype = 5;
+struct vfssw vfssw[10] = {
+	{ "BADVFS" },				/* 0:invalid */
+	{ "" },					/* reserved for loadable fs */
+	{ "" },
+	{ "" },
+	{ "" },
+};
+
+/*
+ * Table for generic options recognized in the VFS layer and acted
+ * on at this level before parsing file system specific options.
+ * The nosuid option is stronger than any of the devices and setuid
+ * options, so those are canceled when nosuid is seen.
+ *
+ * All options which are added here need to be added to the
+ * list of standard options in usr/src/cmd/fs.d/fslib.c as well.
+ */
+/*
+ * VFS Mount options table
+ */
+static char *ro_cancel[] = { MNTOPT_RW, NULL };
+static char *rw_cancel[] = { MNTOPT_RO, NULL };
+static char *suid_cancel[] = { MNTOPT_NOSUID, NULL };
+static char *nosuid_cancel[] = { MNTOPT_SUID, MNTOPT_DEVICES, MNTOPT_NODEVICES,
+    MNTOPT_NOSETUID, MNTOPT_SETUID, NULL };
+static char *devices_cancel[] = { MNTOPT_NODEVICES, NULL };
+static char *nodevices_cancel[] = { MNTOPT_DEVICES, NULL };
+static char *setuid_cancel[] = { MNTOPT_NOSETUID, NULL };
+static char *nosetuid_cancel[] = { MNTOPT_SETUID, NULL };
+static char *nbmand_cancel[] = { MNTOPT_NONBMAND, NULL };
+static char *nonbmand_cancel[] = { MNTOPT_NBMAND, NULL };
+static char *exec_cancel[] = { MNTOPT_NOEXEC, NULL };
+static char *noexec_cancel[] = { MNTOPT_EXEC, NULL };
+
+static const mntopt_t mntopts[] = {
+/*
+ *	option name		cancel options		default arg	flags
+ */
+	{ MNTOPT_REMOUNT,	NULL,			NULL,
+		MO_NODISPLAY, (void *)0 },
+	{ MNTOPT_RO,		ro_cancel,		NULL,		0,
+		(void *)0 },
+	{ MNTOPT_RW,		rw_cancel,		NULL,		0,
+		(void *)0 },
+	{ MNTOPT_SUID,		suid_cancel,		NULL,		0,
+		(void *)0 },
+	{ MNTOPT_NOSUID,	nosuid_cancel,		NULL,		0,
+		(void *)0 },
+	{ MNTOPT_DEVICES,	devices_cancel,		NULL,		0,
+		(void *)0 },
+	{ MNTOPT_NODEVICES,	nodevices_cancel,	NULL,		0,
+		(void *)0 },
+	{ MNTOPT_SETUID,	setuid_cancel,		NULL,		0,
+		(void *)0 },
+	{ MNTOPT_NOSETUID,	nosetuid_cancel,	NULL,		0,
+		(void *)0 },
+	{ MNTOPT_NBMAND,	nbmand_cancel,		NULL,		0,
+		(void *)0 },
+	{ MNTOPT_NONBMAND,	nonbmand_cancel,	NULL,		0,
+		(void *)0 },
+	{ MNTOPT_EXEC,		exec_cancel,		NULL,		0,
+		(void *)0 },
+	{ MNTOPT_NOEXEC,	noexec_cancel,		NULL,		0,
+		(void *)0 },
+};
+
+const mntopts_t vfs_mntopts = {
+	sizeof (mntopts) / sizeof (mntopt_t),
+	(mntopt_t *)&mntopts[0]
+};
+
+/*
+ * File system operation dispatch functions.
+ */
+
+int
+fsop_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+	return (*(vfsp)->vfs_op->vfs_mount)(vfsp, mvp, uap, cr);
+}
+
+int
+fsop_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+	return (*(vfsp)->vfs_op->vfs_unmount)(vfsp, flag, cr);
+}
+
+int
+fsop_root(vfs_t *vfsp, vnode_t **vpp)
+{
+	return ((*(vfsp)->vfs_op->vfs_root)(vfsp, vpp));
+}
+
+int
+fsop_statfs(vfs_t *vfsp, statvfs64_t *sp)
+{
+	return (*(vfsp)->vfs_op->vfs_statvfs)(vfsp, sp);
+}
+
+int
+fsop_sync(vfs_t *vfsp, short flag, cred_t *cr)
+{
+	return (*(vfsp)->vfs_op->vfs_sync)(vfsp, flag, cr);
+}
+
+int
+fsop_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
+{
+	return (*(vfsp)->vfs_op->vfs_vget)(vfsp, vpp, fidp);
+}
+
+int
+fsop_mountroot(vfs_t *vfsp, enum whymountroot reason)
+{
+	return (*(vfsp)->vfs_op->vfs_mountroot)(vfsp, reason);
+}
+
+void
+fsop_freefs(vfs_t *vfsp)
+{
+	(*(vfsp)->vfs_op->vfs_freevfs)(vfsp);
+}
+
+int
+fsop_vnstate(vfs_t *vfsp, vnode_t *vp, vntrans_t nstate)
+{
+	return ((*(vfsp)->vfs_op->vfs_vnstate)(vfsp, vp, nstate));
+}
+
+int
+fsop_sync_by_kind(int fstype, short flag, cred_t *cr)
+{
+	ASSERT((fstype >= 0) && (fstype < nfstype));
+
+	if (ALLOCATED_VFSSW(&vfssw[fstype]) && VFS_INSTALLED(&vfssw[fstype]))
+		return (*vfssw[fstype].vsw_vfsops.vfs_sync) (NULL, flag, cr);
+	else
+		return (ENOTSUP);
+}
+
+/*
+ * File system initialization.  vfs_setfsops() must be called from a file
+ * system's init routine.
+ */
+
+static int
+fs_copyfsops(const fs_operation_def_t *template, vfsops_t *actual,
+    int *unused_ops)
+{
+	static const fs_operation_trans_def_t vfs_ops_table[] = {
+		VFSNAME_MOUNT, offsetof(vfsops_t, vfs_mount),
+			fs_nosys, fs_nosys,
+
+		VFSNAME_UNMOUNT, offsetof(vfsops_t, vfs_unmount),
+			fs_nosys, fs_nosys,
+
+		VFSNAME_ROOT, offsetof(vfsops_t, vfs_root),
+			fs_nosys, fs_nosys,
+
+		VFSNAME_STATVFS, offsetof(vfsops_t, vfs_statvfs),
+			fs_nosys, fs_nosys,
+
+		VFSNAME_SYNC, offsetof(vfsops_t, vfs_sync),
+			(fs_generic_func_p) fs_sync,
+			(fs_generic_func_p) fs_sync,	/* No errors allowed */
+
+		VFSNAME_VGET, offsetof(vfsops_t, vfs_vget),
+			fs_nosys, fs_nosys,
+
+		VFSNAME_MOUNTROOT, offsetof(vfsops_t, vfs_mountroot),
+			fs_nosys, fs_nosys,
+
+		VFSNAME_FREEVFS, offsetof(vfsops_t, vfs_freevfs),
+			(fs_generic_func_p)fs_freevfs,
+			(fs_generic_func_p)fs_freevfs,	/* Shouldn't fail */
+
+		VFSNAME_VNSTATE, offsetof(vfsops_t, vfs_vnstate),
+			(fs_generic_func_p)fs_nosys,
+			(fs_generic_func_p)fs_nosys,
+
+		NULL, 0, NULL, NULL
+	};
+
+	return (fs_build_vector(actual, unused_ops, vfs_ops_table, template));
+}
+
+/* zfs_boot_init() */
+
+int
+vfs_setfsops(int fstype, const fs_operation_def_t *template, vfsops_t **actual)
+{
+	int error;
+	int unused_ops;
+
+	/*
+	 * Verify that fstype refers to a valid fs.  Note that
+	 * 0 is valid since it's used to set "stray" ops.
+	 */
+	if ((fstype < 0) || (fstype >= nfstype))
+		return (EINVAL);
+
+	if (!ALLOCATED_VFSSW(&vfssw[fstype]))
+		return (EINVAL);
+
+	/* Set up the operations vector. */
+
+	error = fs_copyfsops(template, &vfssw[fstype].vsw_vfsops, &unused_ops);
+
+	if (error != 0)
+		return (error);
+
+	vfssw[fstype].vsw_flag |= VSW_INSTALLED;
+
+	if (actual != NULL)
+		*actual = &vfssw[fstype].vsw_vfsops;
+
+#if DEBUG
+	if (unused_ops != 0)
+		cmn_err(CE_WARN, "vfs_setfsops: %s: %d operations supplied "
+		    "but not used", vfssw[fstype].vsw_name, unused_ops);
+#endif
+
+	return (0);
+}
+
+int
+vfs_makefsops(const fs_operation_def_t *template, vfsops_t **actual)
+{
+	int error;
+	int unused_ops;
+
+	*actual = (vfsops_t *)kmem_alloc(sizeof (vfsops_t), KM_SLEEP);
+
+	error = fs_copyfsops(template, *actual, &unused_ops);
+	if (error != 0) {
+		kmem_free(*actual, sizeof (vfsops_t));
+		*actual = NULL;
+		return (error);
+	}
+
+	return (0);
+}
+
+/*
+ * Free a vfsops structure created as a result of vfs_makefsops().
+ * NOTE: For a vfsops structure initialized by vfs_setfsops(), use
+ * vfs_freevfsops_by_type().
+ */
+void
+vfs_freevfsops(vfsops_t *vfsops)
+{
+	kmem_free(vfsops, sizeof (vfsops_t));
+}
+
+/*
+ * Since the vfsops structure is part of the vfssw table and wasn't
+ * really allocated, we're not really freeing anything.  We keep
+ * the name for consistency with vfs_freevfsops().  We do, however,
+ * need to take care of a little bookkeeping.
+ * NOTE: For a vfsops structure created by vfs_setfsops(), use
+ * vfs_freevfsops_by_type().
+ */
+int
+vfs_freevfsops_by_type(int fstype)
+{
+
+	/* Verify that fstype refers to a loaded fs (and not fsid 0). */
+	if ((fstype <= 0) || (fstype >= nfstype))
+		return (EINVAL);
+
+	WLOCK_VFSSW();
+	if ((vfssw[fstype].vsw_flag & VSW_INSTALLED) == 0) {
+		WUNLOCK_VFSSW();
+		return (EINVAL);
+	}
+
+	vfssw[fstype].vsw_flag &= ~VSW_INSTALLED;
+	WUNLOCK_VFSSW();
+
+	return (0);
+}
+
+/* Support routines used to reference vfs_op */
+
+/* Set the operations vector for a vfs */
+void
+vfs_setops(vfs_t *vfsp, vfsops_t *vfsops)
+{
+
+	ASSERT(vfsp != NULL);
+	ASSERT(vfsops != NULL);
+
+	vfsp->vfs_op = vfsops;
+}
+
+/* Retrieve the operations vector for a vfs */
+vfsops_t *
+vfs_getops(vfs_t *vfsp)
+{
+
+	ASSERT(vfsp != NULL);
+
+	return (vfsp->vfs_op);
+}
+
+/*
+ * Returns non-zero (1) if the vfsops matches that of the vfs.
+ * Returns zero (0) if not.
+ */
+int
+vfs_matchops(vfs_t *vfsp, vfsops_t *vfsops)
+{
+	return (vfs_getops(vfsp) == vfsops);
+}
+
+/*
+ * Returns non-zero (1) if the file system has installed a non-default,
+ * non-error vfs_sync routine.  Returns zero (0) otherwise.
+ */
+int
+vfs_can_sync(vfs_t *vfsp)
+{
+	/* vfs_sync() routine is not the default/error function */
+	return (vfs_getops(vfsp)->vfs_sync != fs_sync);
+}
+
+/*
+ * Initialize a vfs structure.
+ */
+void
+vfs_init(vfs_t *vfsp, vfsops_t *op, void *data)
+{
+	/* Always do full init, like vfs_alloc() */
+	bzero(vfsp, sizeof (vfs_t));
+	vfsp->vfs_count = 0;
+	vfsp->vfs_next = vfsp;
+	vfsp->vfs_prev = vfsp;
+	vfsp->vfs_zone_next = vfsp;
+	vfsp->vfs_zone_prev = vfsp;
+	vfsp->vfs_lofi_id = 0;
+	sema_init(&vfsp->vfs_reflock, 1, NULL, SEMA_DEFAULT, NULL);
+	vfsimpl_setup(vfsp);
+	vfsp->vfs_data = (data);
+	vfs_setops((vfsp), (op));
+}
+
+/*
+ * Allocate and initialize the vfs implementation private data
+ * structure, vfs_impl_t.
+ */
+void
+vfsimpl_setup(vfs_t *vfsp)
+{
+	int i;
+
+	if (vfsp->vfs_implp != NULL) {
+		return;
+	}
+
+	vfsp->vfs_implp = kmem_alloc(sizeof (vfs_impl_t), KM_SLEEP);
+	/* Note that these are #define'd in vfs.h */
+	vfsp->vfs_vskap = NULL;
+	vfsp->vfs_fstypevsp = NULL;
+
+	/* Set size of counted array, then zero the array */
+	vfsp->vfs_featureset[0] = VFS_FEATURE_MAXSZ - 1;
+	for (i = 1; i <  VFS_FEATURE_MAXSZ; i++) {
+		vfsp->vfs_featureset[i] = 0;
+	}
+}
+
+/*
+ * Release the vfs_impl_t structure, if it exists. Some unbundled
+ * filesystems may not use the newer version of vfs and thus
+ * would not contain this implementation private data structure.
+ */
+void
+vfsimpl_teardown(vfs_t *vfsp)
+{
+	vfs_impl_t	*vip = vfsp->vfs_implp;
+
+	if (vip == NULL)
+		return;
+
+	kmem_free(vfsp->vfs_implp, sizeof (vfs_impl_t));
+	vfsp->vfs_implp = NULL;
+}
+
+/*
+ * VFS system calls: mount, umount, syssync, statfs, fstatfs, statvfs,
+ * fstatvfs, and sysfs moved to common/syscall.
+ */
+
+// vfs_sync, sync
+
+/*
+ * External routines.
+ */
+
+krwlock_t vfssw_lock;	/* lock accesses to vfssw */
+
+/*
+ * Lock for accessing the vfs linked list.  Initialized in vfs_mountroot(),
+ * but otherwise should be accessed only via vfs_list_lock() and
+ * vfs_list_unlock().  Also used to protect the timestamp for mods to the list.
+ */
+static krwlock_t vfslist;
+
+// vfs_mountdevices(void)
+// vfs_mountdev1(void)
+// vfs_mountfs()
+// vfs_mountroot()
+// lofi_add, lofi_remove
+
+
+/*
+ * Mount the FS for the test jig.  Based on domount()
+ */
+int
+fake_domount(char *fsname, struct mounta *uap, struct vfs **vfspp)
+{
+	vnode_t		*vp;
+	struct cred	*credp;
+	struct vfssw	*vswp;
+	vfsops_t	*vfsops;
+	struct vfs	*vfsp = NULL;
+	mntopts_t	mnt_mntopts;
+	int		error = 0;
+	int		copyout_error = 0;
+	char		*opts = uap->optptr;
+	char		*inargs = opts;
+	int		optlen = uap->optlen;
+
+	credp = CRED();
+
+	/*
+	 * Test jig specific: mount on rootdir
+	 */
+	if (rootvfs != NULL)
+		return (EBUSY);
+	vp = rootdir;
+
+	/*
+	 * The v_flag value for the mount point vp is permanently set
+	 * to VVFSLOCK so that no one bypasses the vn_vfs*locks routine
+	 * for mount point locking.
+	 */
+	mutex_enter(&vp->v_lock);
+	vp->v_flag |= VVFSLOCK;
+	mutex_exit(&vp->v_lock);
+
+	mnt_mntopts.mo_count = 0;
+
+	/*
+	 * Find the ops vector to use to invoke the file system-specific mount
+	 * method.  If the fsname argument is non-NULL, use it directly.
+	 */
+	if ((vswp = vfs_getvfssw(fsname)) == NULL) {
+		return (EINVAL);
+	}
+	if (!VFS_INSTALLED(vswp))
+		return (EINVAL);
+
+	// secpolicy_fs_allowed_mount(fsname)
+
+	vfsops = &vswp->vsw_vfsops;
+
+	vfs_copyopttbl(&vswp->vsw_optproto, &mnt_mntopts);
+
+	/*
+	 * Fetch mount options and parse them for generic vfs options
+	 */
+	if (uap->flags & MS_OPTIONSTR) {
+		/*
+		 * Limit the buffer size
+		 */
+		if (optlen < 0 || optlen > MAX_MNTOPT_STR) {
+			error = EINVAL;
+			goto errout;
+		}
+		if ((uap->flags & MS_SYSSPACE) == 0) {
+			inargs = kmem_alloc(MAX_MNTOPT_STR, KM_SLEEP);
+			inargs[0] = '\0';
+			if (optlen) {
+				error = copyinstr(opts, inargs, (size_t)optlen,
+				    NULL);
+				if (error) {
+					goto errout;
+				}
+			}
+		}
+		vfs_parsemntopts(&mnt_mntopts, inargs, 0);
+	}
+	/*
+	 * Flag bits override the options string.
+	 */
+	if (uap->flags & MS_REMOUNT)
+		vfs_setmntopt_nolock(&mnt_mntopts, MNTOPT_REMOUNT, NULL, 0, 0);
+	if (uap->flags & MS_RDONLY)
+		vfs_setmntopt_nolock(&mnt_mntopts, MNTOPT_RO, NULL, 0, 0);
+	if (uap->flags & MS_NOSUID)
+		vfs_setmntopt_nolock(&mnt_mntopts, MNTOPT_NOSUID, NULL, 0, 0);
+
+	/*
+	 * Check if this is a remount; must be set in the option string and
+	 * the file system must support a remount option.
+	 */
+	if (vfs_optionisset_nolock(&mnt_mntopts,
+	    MNTOPT_REMOUNT, NULL)) {
+		/* disallow here */
+		error = ENOTSUP;
+		goto errout;
+	}
+
+	/*
+	 * uap->flags and vfs_optionisset() should agree.
+	 */
+	if (vfs_optionisset_nolock(&mnt_mntopts, MNTOPT_RO, NULL)) {
+		uap->flags |= MS_RDONLY;
+	}
+	if (vfs_optionisset_nolock(&mnt_mntopts, MNTOPT_NOSUID, NULL)) {
+		uap->flags |= MS_NOSUID;
+	}
+	// nbmand ...
+
+	/*
+	 * If we are splicing the fs into the namespace,
+	 * perform mount point checks...
+	 * (always splice=0 here)
+	 */
+
+	if ((uap->flags & (MS_DATA | MS_OPTIONSTR)) == 0) {
+		uap->dataptr = NULL;
+		uap->datalen = 0;
+	}
+
+	/*
+	 * If this is a remount, ... (never here)
+	 */
+	vfsp = vfs_alloc(KM_SLEEP);
+	VFS_INIT(vfsp, vfsops, NULL);
+
+	VFS_HOLD(vfsp);
+
+	// lofi_add(fsname, vfsp, &mnt_mntopts, uap)
+
+	/*
+	 * PRIV_SYS_MOUNT doesn't mean you can become root.
+	 */
+	uap->flags |= MS_NOSUID;
+	vfs_setmntopt_nolock(&mnt_mntopts, MNTOPT_NOSUID, NULL, 0, 0);
+
+	/*
+	 * The vfs_reflock...
+	 */
+
+	/*
+	 * Lock the vfs...
+	 */
+	if ((error = vfs_lock(vfsp)) != 0) {
+		vfs_free(vfsp);
+		vfsp = NULL;
+		goto errout;
+	}
+
+	/*
+	 * Add device to mount in progress table...
+	 */
+	/*
+	 * Invalidate cached entry for the mount point.
+	 */
+
+	/*
+	 * If have an option string but the filesystem doesn't supply a
+	 * prototype options table, create a table...
+	 */
+
+	/*
+	 * Serialize with zone state transitions...
+	 */
+
+	// mount_in_progress(zone);
+
+	/*
+	 * Instantiate (or reinstantiate) the file system...
+	 */
+	vfs_swapopttbl(&mnt_mntopts, &vfsp->vfs_mntopts);
+
+	vfs_setresource(vfsp, uap->spec, 0);
+	vfs_setmntpoint(vfsp, uap->dir, 0);
+
+	/*
+	 * going to mount on this vnode, so notify.
+	 */
+	// vnevent_mountedover(vp, NULL);
+	error = VFS_MOUNT(vfsp, vp, uap, credp);
+
+	if (uap->flags & MS_RDONLY)
+		vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
+	if (uap->flags & MS_NOSUID)
+		vfs_setmntopt(vfsp, MNTOPT_NOSUID, NULL, 0);
+	if (uap->flags & MS_GLOBAL)
+		vfs_setmntopt(vfsp, MNTOPT_GLOBAL, NULL, 0);
+
+	if (error) {
+		// lofi_remove(vfsp);
+
+		// (remount == 0)
+		vfs_unlock(vfsp);
+		// vfs_freemnttab(vfsp);
+		vfs_free(vfsp);
+		vfsp = NULL;
+	} else {
+		/*
+		 * Set the mount time to now
+		 */
+		// vfsp->vfs_mtime = ddi_get_time();
+		// if (remount) ...
+		// else if (splice) vfs_add(vp, vfsp, flags)
+		// else VFS_HOLD(vfsp);
+
+		/*
+		 * Test jig specific:
+		 * Do sort of like vfs_add for vp=rootdir
+		 * Already have hold on vp.
+		 */
+		vfsp->vfs_vnodecovered = vp;
+		vfsp->vfs_flag |= (VFS_NOSETUID|VFS_NODEVICES);
+		VFS_HOLD(vfsp);
+		rootvfs = vfsp;
+
+		/*
+		 * Set flags for global options encountered
+		 */
+		if (vfs_optionisset(vfsp, MNTOPT_RO, NULL))
+			vfsp->vfs_flag |= VFS_RDONLY;
+		else
+			vfsp->vfs_flag &= ~VFS_RDONLY;
+		if (vfs_optionisset(vfsp, MNTOPT_NOSUID, NULL)) {
+			vfsp->vfs_flag |= (VFS_NOSETUID|VFS_NODEVICES);
+		} else {
+			if (vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL))
+				vfsp->vfs_flag |= VFS_NODEVICES;
+			else
+				vfsp->vfs_flag &= ~VFS_NODEVICES;
+			if (vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL))
+				vfsp->vfs_flag |= VFS_NOSETUID;
+			else
+				vfsp->vfs_flag &= ~VFS_NOSETUID;
+		}
+		if (vfs_optionisset(vfsp, MNTOPT_NBMAND, NULL))
+			vfsp->vfs_flag |= VFS_NBMAND;
+		else
+			vfsp->vfs_flag &= ~VFS_NBMAND;
+
+		if (vfs_optionisset(vfsp, MNTOPT_XATTR, NULL))
+			vfsp->vfs_flag |= VFS_XATTR;
+		else
+			vfsp->vfs_flag &= ~VFS_XATTR;
+
+		if (vfs_optionisset(vfsp, MNTOPT_NOEXEC, NULL))
+			vfsp->vfs_flag |= VFS_NOEXEC;
+		else
+			vfsp->vfs_flag &= ~VFS_NOEXEC;
+
+		/*
+		 * Now construct the output option string of options
+		 * we recognized.
+		 */
+		if (uap->flags & MS_OPTIONSTR) {
+			vfs_list_read_lock();
+			copyout_error = vfs_buildoptionstr(
+			    &vfsp->vfs_mntopts, inargs, optlen);
+			vfs_list_unlock();
+			if (copyout_error == 0 &&
+			    (uap->flags & MS_SYSSPACE) == 0) {
+				copyout_error = copyout(inargs, opts, optlen);
+			}
+		}
+
+		/*
+		 * If this isn't a remount, set up the vopstats...
+		 */
+		if (vswp->vsw_flag & VSW_XID)
+			vfsp->vfs_flag |= VFS_XID;
+
+		vfs_unlock(vfsp);
+
+		/*
+		 * Test jig specicific:
+		 * Replace rootdir with the mounted root.
+		 */
+		error = VFS_ROOT(vfsp, &rootdir);
+		if (error != 0) {
+			panic("fake_domount, get root %d\n", error);
+		}
+	}
+	// mount_completed(zone);
+	// zone_rele(zone);
+
+	// if (splice)
+	//	vn_vfsunlock(vp);
+
+	if ((error == 0) && (copyout_error == 0)) {
+		/* get_vskstat_anchor() */
+		/* Return vfsp to caller. */
+		*vfspp = vfsp;
+	}
+errout:
+	vfs_freeopttbl(&mnt_mntopts);
+	/* resource, mountpt not allocated */
+	/* no addmip, delmip */
+	ASSERT(vswp != NULL);
+	vfs_unrefvfssw(vswp);
+	if (inargs != opts)
+		kmem_free(inargs, MAX_MNTOPT_STR);
+	if (copyout_error) {
+		if (vfsp != NULL) {
+			// lofi_remove(vfsp);
+			VFS_RELE(vfsp);
+		}
+		error = copyout_error;
+	}
+	return (error);
+}
+
+
+static void
+vfs_setpath(
+    struct vfs *vfsp,		/* vfs being updated */
+    refstr_t **refp,		/* Ref-count string to contain the new path */
+    const char *newpath,	/* Path to add to refp (above) */
+    uint32_t flag)		/* flag */
+{
+	// size_t len;
+	refstr_t *ref;
+	// char *sp;
+	int have_list_lock = 0;
+
+	ASSERT(!VFS_ON_LIST(vfsp) || vfs_lock_held(vfsp));
+
+	/*
+	 * New path must be less than MAXPATHLEN because mntfs
+	 * will only display up to MAXPATHLEN bytes. This is currently
+	 * safe, because domount() uses pn_get(), and other callers
+	 * similarly cap the size to fewer than MAXPATHLEN bytes.
+	 */
+
+	ASSERT(strlen(newpath) < MAXPATHLEN);
+
+	/* mntfs requires consistency while vfs list lock is held */
+
+	if (VFS_ON_LIST(vfsp)) {
+		have_list_lock = 1;
+		vfs_list_lock();
+	}
+
+	if (*refp != NULL)
+		refstr_rele(*refp);
+
+	/*
+	 * If we are in a non-global zone... (do something else)
+	 */
+	ref = refstr_alloc(newpath);
+	*refp = ref;
+
+	if (have_list_lock) {
+		vfs_mnttab_modtimeupd();
+		vfs_list_unlock();
+	}
+}
+
+/*
+ * Record a mounted resource name in a vfs structure.
+ * If vfsp is already mounted, caller must hold the vfs lock.
+ */
+void
+vfs_setresource(struct vfs *vfsp, const char *resource, uint32_t flag)
+{
+	if (resource == NULL || resource[0] == '\0')
+		resource = VFS_NORESOURCE;
+	vfs_setpath(vfsp, &vfsp->vfs_resource, resource, flag);
+}
+
+/*
+ * Record a mount point name in a vfs structure.
+ * If vfsp is already mounted, caller must hold the vfs lock.
+ */
+void
+vfs_setmntpoint(struct vfs *vfsp, const char *mntpt, uint32_t flag)
+{
+	if (mntpt == NULL || mntpt[0] == '\0')
+		mntpt = VFS_NOMNTPT;
+	vfs_setpath(vfsp, &vfsp->vfs_mntpt, mntpt, flag);
+}
+
+/* Returns the vfs_resource. Caller must call refstr_rele() when finished. */
+
+refstr_t *
+vfs_getresource(const struct vfs *vfsp)
+{
+	refstr_t *resource;
+
+	vfs_list_read_lock();
+	resource = vfsp->vfs_resource;
+	refstr_hold(resource);
+	vfs_list_unlock();
+
+	return (resource);
+}
+
+/* Returns the vfs_mntpt. Caller must call refstr_rele() when finished. */
+
+refstr_t *
+vfs_getmntpoint(const struct vfs *vfsp)
+{
+	refstr_t *mntpt;
+
+	vfs_list_read_lock();
+	mntpt = vfsp->vfs_mntpt;
+	refstr_hold(mntpt);
+	vfs_list_unlock();
+
+	return (mntpt);
+}
+
+// vfs_createopttbl_extend
+// vfs_createopttbl
+
+/*
+ * Swap two mount options tables
+ */
+static void
+vfs_swapopttbl_nolock(mntopts_t *optbl1, mntopts_t *optbl2)
+{
+	uint_t tmpcnt;
+	mntopt_t *tmplist;
+
+	tmpcnt = optbl2->mo_count;
+	tmplist = optbl2->mo_list;
+	optbl2->mo_count = optbl1->mo_count;
+	optbl2->mo_list = optbl1->mo_list;
+	optbl1->mo_count = tmpcnt;
+	optbl1->mo_list = tmplist;
+}
+
+static void
+vfs_swapopttbl(mntopts_t *optbl1, mntopts_t *optbl2)
+{
+	vfs_list_lock();
+	vfs_swapopttbl_nolock(optbl1, optbl2);
+	vfs_mnttab_modtimeupd();
+	vfs_list_unlock();
+}
+
+static char **
+vfs_copycancelopt_extend(char **const moc, int extend)
+{
+	int i = 0;
+	int j;
+	char **result;
+
+	if (moc != NULL) {
+		for (; moc[i] != NULL; i++)
+			/* count number of options to cancel */;
+	}
+
+	if (i + extend == 0)
+		return (NULL);
+
+	result = kmem_alloc((i + extend + 1) * sizeof (char *), KM_SLEEP);
+
+	for (j = 0; j < i; j++) {
+		result[j] = kmem_alloc(strlen(moc[j]) + 1, KM_SLEEP);
+		(void) strcpy(result[j], moc[j]);
+	}
+	for (; j <= i + extend; j++)
+		result[j] = NULL;
+
+	return (result);
+}
+
+static void
+vfs_copyopt(const mntopt_t *s, mntopt_t *d)
+{
+	char *sp, *dp;
+
+	d->mo_flags = s->mo_flags;
+	d->mo_data = s->mo_data;
+	sp = s->mo_name;
+	if (sp != NULL) {
+		dp = kmem_alloc(strlen(sp) + 1, KM_SLEEP);
+		(void) strcpy(dp, sp);
+		d->mo_name = dp;
+	} else {
+		d->mo_name = NULL; /* should never happen */
+	}
+
+	d->mo_cancel = vfs_copycancelopt_extend(s->mo_cancel, 0);
+
+	sp = s->mo_arg;
+	if (sp != NULL) {
+		dp = kmem_alloc(strlen(sp) + 1, KM_SLEEP);
+		(void) strcpy(dp, sp);
+		d->mo_arg = dp;
+	} else {
+		d->mo_arg = NULL;
+	}
+}
+
+// vfs_copyopttbl_extend
+// vfs_copyopttbl
+
+/*
+ * Copy a mount options table, possibly allocating some spare
+ * slots at the end.  It is permissible to copy_extend the NULL table.
+ */
+static void
+vfs_copyopttbl_extend(const mntopts_t *smo, mntopts_t *dmo, int extra)
+{
+	uint_t i, count;
+	mntopt_t *motbl;
+
+	/*
+	 * Clear out any existing stuff in the options table being initialized
+	 */
+	vfs_freeopttbl(dmo);
+	count = (smo == NULL) ? 0 : smo->mo_count;
+	if ((count + extra) == 0)	/* nothing to do */
+		return;
+	dmo->mo_count = count + extra;
+	motbl = kmem_zalloc((count + extra) * sizeof (mntopt_t), KM_SLEEP);
+	dmo->mo_list = motbl;
+	for (i = 0; i < count; i++) {
+		vfs_copyopt(&smo->mo_list[i], &motbl[i]);
+	}
+	for (i = count; i < count + extra; i++) {
+		motbl[i].mo_flags = MO_EMPTY;
+	}
+}
+
+/*
+ * Copy a mount options table.
+ *
+ * This function is *not* for general use by filesystems.
+ *
+ * Note: caller is responsible for locking the vfs list, if needed,
+ *       to protect smo and dmo.
+ */
+void
+vfs_copyopttbl(const mntopts_t *smo, mntopts_t *dmo)
+{
+	vfs_copyopttbl_extend(smo, dmo, 0);
+}
+
+static char **
+vfs_mergecancelopts(const mntopt_t *mop1, const mntopt_t *mop2)
+{
+	int c1 = 0;
+	int c2 = 0;
+	char **result;
+	char **sp1, **sp2, **dp;
+
+	/*
+	 * First we count both lists of cancel options.
+	 * If either is NULL or has no elements, we return a copy of
+	 * the other.
+	 */
+	if (mop1->mo_cancel != NULL) {
+		for (; mop1->mo_cancel[c1] != NULL; c1++)
+			/* count cancel options in mop1 */;
+	}
+
+	if (c1 == 0)
+		return (vfs_copycancelopt_extend(mop2->mo_cancel, 0));
+
+	if (mop2->mo_cancel != NULL) {
+		for (; mop2->mo_cancel[c2] != NULL; c2++)
+			/* count cancel options in mop2 */;
+	}
+
+	result = vfs_copycancelopt_extend(mop1->mo_cancel, c2);
+
+	if (c2 == 0)
+		return (result);
+
+	/*
+	 * When we get here, we've got two sets of cancel options;
+	 * we need to merge the two sets.  We know that the result
+	 * array has "c1+c2+1" entries and in the end we might shrink
+	 * it.
+	 * Result now has a copy of the c1 entries from mop1; we'll
+	 * now lookup all the entries of mop2 in mop1 and copy it if
+	 * it is unique.
+	 * This operation is O(n^2) but it's only called once per
+	 * filesystem per duplicate option.  This is a situation
+	 * which doesn't arise with the filesystems in ON and
+	 * n is generally 1.
+	 */
+
+	dp = &result[c1];
+	for (sp2 = mop2->mo_cancel; *sp2 != NULL; sp2++) {
+		for (sp1 = mop1->mo_cancel; *sp1 != NULL; sp1++) {
+			if (strcmp(*sp1, *sp2) == 0)
+				break;
+		}
+		if (*sp1 == NULL) {
+			/*
+			 * Option *sp2 not found in mop1, so copy it.
+			 * The calls to vfs_copycancelopt_extend()
+			 * guarantee that there's enough room.
+			 */
+			*dp = kmem_alloc(strlen(*sp2) + 1, KM_SLEEP);
+			(void) strcpy(*dp++, *sp2);
+		}
+	}
+	if (dp != &result[c1+c2]) {
+		size_t bytes = (dp - result + 1) * sizeof (char *);
+		char **nres = kmem_alloc(bytes, KM_SLEEP);
+
+		bcopy(result, nres, bytes);
+		kmem_free(result, (c1 + c2 + 1) * sizeof (char *));
+		result = nres;
+	}
+	return (result);
+}
+
+/*
+ * Merge two mount option tables (outer and inner) into one.  This is very
+ * similar to "merging" global variables and automatic variables in C.
+ *
+ * This isn't (and doesn't have to be) fast.
+ *
+ * This function is *not* for general use by filesystems.
+ *
+ * Note: caller is responsible for locking the vfs list, if needed,
+ *       to protect omo, imo & dmo.
+ */
+void
+vfs_mergeopttbl(const mntopts_t *omo, const mntopts_t *imo, mntopts_t *dmo)
+{
+	uint_t i, count;
+	mntopt_t *mop, *motbl;
+	uint_t freeidx;
+
+	/*
+	 * First determine how much space we need to allocate.
+	 */
+	count = omo->mo_count;
+	for (i = 0; i < imo->mo_count; i++) {
+		if (imo->mo_list[i].mo_flags & MO_EMPTY)
+			continue;
+		if (vfs_hasopt(omo, imo->mo_list[i].mo_name) == NULL)
+			count++;
+	}
+	ASSERT(count >= omo->mo_count &&
+	    count <= omo->mo_count + imo->mo_count);
+	motbl = kmem_alloc(count * sizeof (mntopt_t), KM_SLEEP);
+	for (i = 0; i < omo->mo_count; i++)
+		vfs_copyopt(&omo->mo_list[i], &motbl[i]);
+	freeidx = omo->mo_count;
+	for (i = 0; i < imo->mo_count; i++) {
+		if (imo->mo_list[i].mo_flags & MO_EMPTY)
+			continue;
+		if ((mop = vfs_hasopt(omo, imo->mo_list[i].mo_name)) != NULL) {
+			char **newcanp;
+			uint_t index = mop - omo->mo_list;
+
+			newcanp = vfs_mergecancelopts(mop, &motbl[index]);
+
+			vfs_freeopt(&motbl[index]);
+			vfs_copyopt(&imo->mo_list[i], &motbl[index]);
+
+			vfs_freecancelopt(motbl[index].mo_cancel);
+			motbl[index].mo_cancel = newcanp;
+		} else {
+			/*
+			 * If it's a new option, just copy it over to the first
+			 * free location.
+			 */
+			vfs_copyopt(&imo->mo_list[i], &motbl[freeidx++]);
+		}
+	}
+	dmo->mo_count = count;
+	dmo->mo_list = motbl;
+}
+
+/*
+ * Functions to set and clear mount options in a mount options table.
+ */
+
+/*
+ * Clear a mount option, if it exists.
+ *
+ * The update_mnttab arg indicates whether mops is part of a vfs that is on
+ * the vfs list.
+ */
+static void
+vfs_clearmntopt_nolock(mntopts_t *mops, const char *opt, int update_mnttab)
+{
+	struct mntopt *mop;
+	uint_t i, count;
+
+	ASSERT(!update_mnttab || RW_WRITE_HELD(&vfslist));
+
+	count = mops->mo_count;
+	for (i = 0; i < count; i++) {
+		mop = &mops->mo_list[i];
+
+		if (mop->mo_flags & MO_EMPTY)
+			continue;
+		if (strcmp(opt, mop->mo_name))
+			continue;
+		mop->mo_flags &= ~MO_SET;
+		if (mop->mo_arg != NULL) {
+			kmem_free(mop->mo_arg, strlen(mop->mo_arg) + 1);
+		}
+		mop->mo_arg = NULL;
+		if (update_mnttab)
+			vfs_mnttab_modtimeupd();
+		break;
+	}
+}
+
+void
+vfs_clearmntopt(struct vfs *vfsp, const char *opt)
+{
+	int gotlock = 0;
+
+	if (VFS_ON_LIST(vfsp)) {
+		gotlock = 1;
+		vfs_list_lock();
+	}
+	vfs_clearmntopt_nolock(&vfsp->vfs_mntopts, opt, gotlock);
+	if (gotlock)
+		vfs_list_unlock();
+}
+
+
+/*
+ * Set a mount option on...
+ */
+static void
+vfs_setmntopt_nolock(mntopts_t *mops, const char *opt,
+    const char *arg, int flags, int update_mnttab)
+{
+	mntopt_t *mop;
+	uint_t i, count;
+	char *sp;
+
+	ASSERT(!update_mnttab || RW_WRITE_HELD(&vfslist));
+
+	if (flags & VFS_CREATEOPT) {
+		if (vfs_hasopt(mops, opt) != NULL) {
+			flags &= ~VFS_CREATEOPT;
+		}
+	}
+	count = mops->mo_count;
+	for (i = 0; i < count; i++) {
+		mop = &mops->mo_list[i];
+
+		if (mop->mo_flags & MO_EMPTY) {
+			if ((flags & VFS_CREATEOPT) == 0)
+				continue;
+			sp = kmem_alloc(strlen(opt) + 1, KM_SLEEP);
+			(void) strcpy(sp, opt);
+			mop->mo_name = sp;
+			if (arg != NULL)
+				mop->mo_flags = MO_HASVALUE;
+			else
+				mop->mo_flags = 0;
+		} else if (strcmp(opt, mop->mo_name)) {
+			continue;
+		}
+		if ((mop->mo_flags & MO_IGNORE) && (flags & VFS_NOFORCEOPT))
+			break;
+		if (arg != NULL && (mop->mo_flags & MO_HASVALUE) != 0) {
+			sp = kmem_alloc(strlen(arg) + 1, KM_SLEEP);
+			(void) strcpy(sp, arg);
+		} else {
+			sp = NULL;
+		}
+		if (mop->mo_arg != NULL)
+			kmem_free(mop->mo_arg, strlen(mop->mo_arg) + 1);
+		mop->mo_arg = sp;
+		if (flags & VFS_DISPLAY)
+			mop->mo_flags &= ~MO_NODISPLAY;
+		if (flags & VFS_NODISPLAY)
+			mop->mo_flags |= MO_NODISPLAY;
+		mop->mo_flags |= MO_SET;
+		if (mop->mo_cancel != NULL) {
+			char **cp;
+
+			for (cp = mop->mo_cancel; *cp != NULL; cp++)
+				vfs_clearmntopt_nolock(mops, *cp, 0);
+		}
+		if (update_mnttab)
+			vfs_mnttab_modtimeupd();
+		break;
+	}
+}
+
+void
+vfs_setmntopt(struct vfs *vfsp, const char *opt, const char *arg, int flags)
+{
+	int gotlock = 0;
+
+	if (VFS_ON_LIST(vfsp)) {
+		gotlock = 1;
+		vfs_list_lock();
+	}
+	vfs_setmntopt_nolock(&vfsp->vfs_mntopts, opt, arg, flags, gotlock);
+	if (gotlock)
+		vfs_list_unlock();
+}
+
+// vfs_addtag
+// vfs_settag
+// vfs_clrtag
+
+/*
+ * Function to parse an option string and fill in a mount options table.
+ * Unknown options are silently ignored.  The input option string is modified
+ * by replacing separators with nulls.  If the create flag is set, options
+ * not found in the table are just added on the fly.  The table must have
+ * an option slot marked MO_EMPTY to add an option on the fly.
+ *
+ * This function is *not* for general use by filesystems.
+ *
+ * Note: caller is responsible for locking the vfs list, if needed,
+ *       to protect mops..
+ */
+void
+vfs_parsemntopts(mntopts_t *mops, char *osp, int create)
+{
+	char *s = osp, *p, *nextop, *valp, *cp, *ep = NULL;
+	int setflg = VFS_NOFORCEOPT;
+
+	if (osp == NULL)
+		return;
+	while (*s != '\0') {
+		p = strchr(s, ',');	/* find next option */
+		if (p == NULL) {
+			cp = NULL;
+			p = s + strlen(s);
+		} else {
+			cp = p;		/* save location of comma */
+			*p++ = '\0';	/* mark end and point to next option */
+		}
+		nextop = p;
+		p = strchr(s, '=');	/* look for value */
+		if (p == NULL) {
+			valp = NULL;	/* no value supplied */
+			ep = NULL;
+		} else {
+			ep = p;		/* save location of equals */
+			*p++ = '\0';	/* end option and point to value */
+			valp = p;
+		}
+		/*
+		 * set option into options table
+		 */
+		if (create)
+			setflg |= VFS_CREATEOPT;
+		vfs_setmntopt_nolock(mops, s, valp, setflg, 0);
+		if (cp != NULL)
+			*cp = ',';	/* restore the comma */
+		if (valp != NULL)
+			*ep = '=';	/* restore the equals */
+		s = nextop;
+	}
+}
+
+/*
+ * Function to inquire if an option exists in a mount options table.
+ * Returns a pointer to the option if it exists, else NULL.
+ */
+struct mntopt *
+vfs_hasopt(const mntopts_t *mops, const char *opt)
+{
+	struct mntopt *mop;
+	uint_t i, count;
+
+	count = mops->mo_count;
+	for (i = 0; i < count; i++) {
+		mop = &mops->mo_list[i];
+
+		if (mop->mo_flags & MO_EMPTY)
+			continue;
+		if (strcmp(opt, mop->mo_name) == 0)
+			return (mop);
+	}
+	return (NULL);
+}
+
+/*
+ * Function to inquire if an option is set in a mount options table.
+ * Returns non-zero if set and fills in the arg pointer with a pointer to
+ * the argument string or NULL if there is no argument string.
+ */
+static int
+vfs_optionisset_nolock(const mntopts_t *mops, const char *opt, char **argp)
+{
+	struct mntopt *mop;
+	uint_t i, count;
+
+	count = mops->mo_count;
+	for (i = 0; i < count; i++) {
+		mop = &mops->mo_list[i];
+
+		if (mop->mo_flags & MO_EMPTY)
+			continue;
+		if (strcmp(opt, mop->mo_name))
+			continue;
+		if ((mop->mo_flags & MO_SET) == 0)
+			return (0);
+		if (argp != NULL && (mop->mo_flags & MO_HASVALUE) != 0)
+			*argp = mop->mo_arg;
+		return (1);
+	}
+	return (0);
+}
+
+
+int
+vfs_optionisset(const struct vfs *vfsp, const char *opt, char **argp)
+{
+	int ret;
+
+	vfs_list_read_lock();
+	ret = vfs_optionisset_nolock(&vfsp->vfs_mntopts, opt, argp);
+	vfs_list_unlock();
+	return (ret);
+}
+
+
+/*
+ * Construct a comma separated string of the options set in the given
+ * mount table, return the string in the given buffer.  Return non-zero if
+ * the buffer would overflow.
+ *
+ * This function is *not* for general use by filesystems.
+ *
+ * Note: caller is responsible for locking the vfs list, if needed,
+ *       to protect mp.
+ */
+int
+vfs_buildoptionstr(const mntopts_t *mp, char *buf, int len)
+{
+	char *cp;
+	uint_t i;
+
+	buf[0] = '\0';
+	cp = buf;
+	for (i = 0; i < mp->mo_count; i++) {
+		struct mntopt *mop;
+
+		mop = &mp->mo_list[i];
+		if (mop->mo_flags & MO_SET) {
+			int optlen, comma = 0;
+
+			if (buf[0] != '\0')
+				comma = 1;
+			optlen = strlen(mop->mo_name);
+			if (strlen(buf) + comma + optlen + 1 > len)
+				goto err;
+			if (comma)
+				*cp++ = ',';
+			(void) strcpy(cp, mop->mo_name);
+			cp += optlen;
+			/*
+			 * Append option value if there is one
+			 */
+			if (mop->mo_arg != NULL) {
+				int arglen;
+
+				arglen = strlen(mop->mo_arg);
+				if (strlen(buf) + arglen + 2 > len)
+					goto err;
+				*cp++ = '=';
+				(void) strcpy(cp, mop->mo_arg);
+				cp += arglen;
+			}
+		}
+	}
+	return (0);
+err:
+	return (EOVERFLOW);
+}
+
+static void
+vfs_freecancelopt(char **moc)
+{
+	if (moc != NULL) {
+		int ccnt = 0;
+		char **cp;
+
+		for (cp = moc; *cp != NULL; cp++) {
+			kmem_free(*cp, strlen(*cp) + 1);
+			ccnt++;
+		}
+		kmem_free(moc, (ccnt + 1) * sizeof (char *));
+	}
+}
+
+static void
+vfs_freeopt(mntopt_t *mop)
+{
+	if (mop->mo_name != NULL)
+		kmem_free(mop->mo_name, strlen(mop->mo_name) + 1);
+
+	vfs_freecancelopt(mop->mo_cancel);
+
+	if (mop->mo_arg != NULL)
+		kmem_free(mop->mo_arg, strlen(mop->mo_arg) + 1);
+}
+
+/*
+ * Free a mount options table
+ *
+ * This function is *not* for general use by filesystems.
+ *
+ * Note: caller is responsible for locking the vfs list, if needed,
+ *       to protect mp.
+ */
+void
+vfs_freeopttbl(mntopts_t *mp)
+{
+	uint_t i, count;
+
+	count = mp->mo_count;
+	for (i = 0; i < count; i++) {
+		vfs_freeopt(&mp->mo_list[i]);
+	}
+	if (count) {
+		kmem_free(mp->mo_list, sizeof (mntopt_t) * count);
+		mp->mo_count = 0;
+		mp->mo_list = NULL;
+	}
+}
+
+// vfs_mntdummyread
+// vfs_mntdummywrite
+// vfs_mntdummygetattr
+// vfs_mnttabvp_setup
+// vfs_mnttab_rwop
+// vfs_mnttab_writeop
+// vfs_mnttab_readop
+// vfs_freemnttab
+// vfs_mnttab_modtime
+// vfs_mnttab_poll
+// vfs_mono_time
+
+/*
+ * Update the mnttab modification time...
+ */
+void
+vfs_mnttab_modtimeupd()
+{
+}
+
+/*
+ * Unlike the real dounmount, we don't have
+ * vn_vfswlock_held(coveredvp)
+ */
+int
+fake_dounmount(struct vfs *vfsp, int flag)
+{
+	cred_t *cr = CRED();
+	vnode_t *coveredvp;
+	int error;
+
+	/*
+	 * Get covered vnode. This will be NULL if the vfs is not linked
+	 * into the file system name space (i.e., domount() with MNT_NOSPICE).
+	 */
+	coveredvp = vfsp->vfs_vnodecovered;
+
+	/* For forcible umount, skip VFS_SYNC() since it may hang */
+	if ((flag & MS_FORCE) == 0)
+		(void) VFS_SYNC(vfsp, 0, cr);
+
+	/*
+	 * Test-jig specific:
+	 * Need to release rootdir before unmount or VFS_UNMOUNT
+	 * may fail due to that node being active.
+	 */
+	if (rootdir != NULL) {
+		ASSERT(rootdir != coveredvp);
+		VN_RELE(rootdir);
+		rootdir = NULL;
+	}
+
+	/*
+	 * Lock the vfs to maintain fs status quo during unmount.  This
+	 * has to be done after the sync because ufs_update tries to acquire
+	 * the vfs_reflock.
+	 */
+	vfs_lock_wait(vfsp);
+
+	if ((error = VFS_UNMOUNT(vfsp, flag, cr)) != 0) {
+		int err2;
+		vfs_unlock(vfsp);
+		/* Get rootdir back */
+		err2 = VFS_ROOT(vfsp, &rootdir);
+		if (err2 != 0) {
+			panic("fake_dounmount, get root %d\n", err2);
+		}
+	} else {
+		/*
+		 * Real dounmount does vfs_remove.
+		 *
+		 * Test-jig specific:
+		 * Restore the covered rootdir,
+		 * release the rootvfs hold and clear.
+		 */
+		if (coveredvp != NULL) {
+			// vfs_list_remove(vfsp);
+			vfsp->vfs_vnodecovered = NULL;
+			rootdir = coveredvp;
+		}
+		if (rootvfs == vfsp) {
+			VFS_RELE(vfsp);
+			rootvfs = NULL;
+		}
+
+		/*
+		 * Release the (final) reference to vfs
+		 */
+		vfs_unlock(vfsp);
+		VFS_RELE(vfsp);
+	}
+	return (error);
+}
+
+// vfs_unmountall(void)
+// vfs_addmip
+// vfs_delmip
+// vfs_add
+// vfs_remove
+
+static krwlock_t vpvfsentry_ve_lock;
+
+/*
+ * Lock a filesystem to prevent access to it while mounting,
+ * unmounting and syncing.  Return EBUSY immediately if lock
+ * can't be acquired.
+ */
+int
+vfs_lock(vfs_t *vfsp)
+{
+
+	if (rw_tryenter(&vpvfsentry_ve_lock, RW_WRITER))
+		return (0);
+
+	return (EBUSY);
+}
+
+int
+vfs_rlock(vfs_t *vfsp)
+{
+
+	if (rw_tryenter(&vpvfsentry_ve_lock, RW_READER))
+		return (0);
+
+	return (EBUSY);
+}
+
+void
+vfs_lock_wait(vfs_t *vfsp)
+{
+
+	rw_enter(&vpvfsentry_ve_lock, RW_WRITER);
+}
+
+void
+vfs_rlock_wait(vfs_t *vfsp)
+{
+	rw_enter(&vpvfsentry_ve_lock, RW_READER);
+}
+
+/*
+ * Unlock a locked filesystem.
+ */
+void
+vfs_unlock(vfs_t *vfsp)
+{
+
+	rw_exit(&vpvfsentry_ve_lock);
+}
+
+/*
+ * Utility routine that allows a filesystem to construct its
+ * fsid in "the usual way" - by munging some underlying dev_t and
+ * the filesystem type number into the 64-bit fsid. ...
+ */
+void
+vfs_make_fsid(fsid_t *fsi, dev_t dev, int val)
+{
+	if (!cmpldev((dev32_t *)&fsi->val[0], dev))
+		panic("device number too big for fsid!");
+	fsi->val[1] = val;
+}
+
+int
+vfs_lock_held(vfs_t *vfsp)
+{
+	int held;
+
+	held = rw_write_held(&vpvfsentry_ve_lock);
+
+	return (held);
+}
+
+// vfs_lock_owner
+
+/*
+ * vfs list locking.
+ */
+
+void
+vfs_list_lock()
+{
+	rw_enter(&vfslist, RW_WRITER);
+}
+
+void
+vfs_list_read_lock()
+{
+	rw_enter(&vfslist, RW_READER);
+}
+
+void
+vfs_list_unlock()
+{
+	rw_exit(&vfslist);
+}
+
+/*
+ * Low level worker routines for adding entries to and removing entries from
+ * the vfs list.
+ */
+
+// vfs_hash_add
+// vfs_hash_remove
+// vfs_list_add
+// vfs_list_remove
+// getvfs
+// vfs_devmounting
+
+/*
+ * Search the vfs list for a specified device.  Returns 1, if entry is found
+ * or 0 if no suitable entry is found.
+ */
+
+int
+vfs_devismounted(dev_t dev)
+{
+	return (0);
+}
+
+// vfs_dev2vfsp
+// vfs_mntpoint2vfsp
+
+/*
+ * Search the vfs list for a specified vfsops.
+ * if vfs entry is found then return 1, else 0.
+ */
+int
+vfs_opsinuse(vfsops_t *ops)
+{
+	return (0);
+}
+
+/*
+ * Allocate an entry in vfssw for a file system type
+ */
+struct vfssw *
+allocate_vfssw(const char *type)
+{
+	struct vfssw *vswp;
+
+	if (type[0] == '\0' || strlen(type) + 1 > _ST_FSTYPSZ) {
+		/*
+		 * The vfssw table uses the empty string to identify an
+		 * available entry; we cannot add any type which has
+		 * a leading NUL. The string length is limited to
+		 * the size of the st_fstype array in struct stat.
+		 */
+		return (NULL);
+	}
+
+	ASSERT(VFSSW_WRITE_LOCKED());
+	for (vswp = &vfssw[1]; vswp < &vfssw[nfstype]; vswp++)
+		if (!ALLOCATED_VFSSW(vswp)) {
+			vswp->vsw_name = kmem_alloc(strlen(type) + 1, KM_SLEEP);
+			(void) strcpy(vswp->vsw_name, type);
+			ASSERT(vswp->vsw_count == 0);
+			vswp->vsw_count = 1;
+			mutex_init(&vswp->vsw_lock, NULL, MUTEX_DEFAULT, NULL);
+			return (vswp);
+		}
+	return (NULL);
+}
+
+// vfs_to_modname
+// vfs_getvfssw
+
+/*
+ * Find a vfssw entry given a file system type name.
+ */
+struct vfssw *
+vfs_getvfssw(const char *type)
+{
+	struct vfssw *vswp;
+
+	if (type == NULL || *type == '\0')
+		return (NULL);
+
+	for (vswp = &vfssw[1]; vswp < &vfssw[nfstype]; vswp++) {
+		if (strcmp(type, vswp->vsw_name) == 0) {
+			return (vswp);
+		}
+	}
+
+	return (NULL);
+
+}
+
+/*
+ * Find a vfssw entry given a file system type name.
+ */
+struct vfssw *
+vfs_getvfsswbyname(const char *type)
+{
+	struct vfssw *vswp;
+
+	ASSERT(VFSSW_LOCKED());
+	if (type == NULL || *type == '\0')
+		return (NULL);
+
+	for (vswp = &vfssw[1]; vswp < &vfssw[nfstype]; vswp++) {
+		if (strcmp(type, vswp->vsw_name) == 0) {
+			vfs_refvfssw(vswp);
+			return (vswp);
+		}
+	}
+
+	return (NULL);
+}
+
+// vfs_getvfsswbyvfsops
+
+/*
+ * Reference a vfssw entry.
+ */
+void
+vfs_refvfssw(struct vfssw *vswp)
+{
+
+	mutex_enter(&vswp->vsw_lock);
+	vswp->vsw_count++;
+	mutex_exit(&vswp->vsw_lock);
+}
+
+/*
+ * Unreference a vfssw entry.
+ */
+void
+vfs_unrefvfssw(struct vfssw *vswp)
+{
+
+	mutex_enter(&vswp->vsw_lock);
+	vswp->vsw_count--;
+	mutex_exit(&vswp->vsw_lock);
+}
+
+// vfs_syncall
+
+/*
+ * Map VFS flags to statvfs flags.  These shouldn't really be separate
+ * flags at all.
+ */
+uint_t
+vf_to_stf(uint_t vf)
+{
+	uint_t stf = 0;
+
+	if (vf & VFS_RDONLY)
+		stf |= ST_RDONLY;
+	if (vf & VFS_NOSETUID)
+		stf |= ST_NOSUID;
+	if (vf & VFS_NOTRUNC)
+		stf |= ST_NOTRUNC;
+
+	return (stf);
+}
+
+// vfsstray_sync
+// vfsstray
+// vfs_EIO
+// vfs_EIO_sync
+// EIO_vfs
+// EIO_vfsops
+
+#pragma init(vfsinit)
+
+/*
+ * Called from startup() to initialize all loaded vfs's
+ */
+void
+vfsinit(void)
+{
+	vn_create_cache();
+
+	/* Temporary, until we mount root */
+	rootdir = vn_alloc(KM_SLEEP);
+	rootdir->v_type = VDIR;
+}
+
+vfs_t *
+vfs_alloc(int kmflag)
+{
+	vfs_t *vfsp;
+
+	vfsp = kmem_alloc(sizeof (struct vfs), kmflag);
+
+	/*
+	 * Do the simplest initialization here.
+	 * Everything else gets done in vfs_init()
+	 */
+	bzero(vfsp, sizeof (vfs_t));
+	return (vfsp);
+}
+
+void
+vfs_free(vfs_t *vfsp)
+{
+	/*
+	 * One would be tempted to assert that "vfsp->vfs_count == 0".
+	 * Don't.  See fs/vfs.c
+	 */
+
+	/* If FEM was in use, make sure everything gets cleaned up */
+
+	if (vfsp->vfs_implp)
+		vfsimpl_teardown(vfsp);
+	sema_destroy(&vfsp->vfs_reflock);
+	kmem_free(vfsp, sizeof (struct vfs));
+}
+
+/*
+ * Increments the vfs reference count by one atomically.
+ */
+void
+vfs_hold(vfs_t *vfsp)
+{
+	atomic_inc_32(&vfsp->vfs_count);
+	ASSERT(vfsp->vfs_count != 0);
+}
+
+/*
+ * Decrements the vfs reference count by one atomically. When
+ * vfs reference count becomes zero, it calls the file system
+ * specific vfs_freevfs() to free up the resources.
+ */
+void
+vfs_rele(vfs_t *vfsp)
+{
+	ASSERT(vfsp->vfs_count != 0);
+	if (atomic_dec_32_nv(&vfsp->vfs_count) == 0) {
+		VFS_FREEVFS(vfsp);
+		// lofi_remove(vfsp);
+		// zone_rele_ref...
+		// vfs_freemnttab(vfsp);
+		vfs_free(vfsp);
+	}
+}
+
+/*
+ * Generic operations vector support.
+ */
+
+int
+fs_build_vector(void *vector, int *unused_ops,
+    const fs_operation_trans_def_t *translation,
+    const fs_operation_def_t *operations)
+{
+	int i, num_trans, num_ops, used;
+
+	/*
+	 * Count the number of translations and the number of supplied
+	 * operations.
+	 */
+
+	{
+		const fs_operation_trans_def_t *p;
+
+		for (num_trans = 0, p = translation;
+		    p->name != NULL;
+		    num_trans++, p++)
+			;
+	}
+
+	{
+		const fs_operation_def_t *p;
+
+		for (num_ops = 0, p = operations;
+		    p->name != NULL;
+		    num_ops++, p++)
+			;
+	}
+
+	/* Walk through each operation known to our caller.  There will be */
+	/* one entry in the supplied "translation table" for each. */
+
+	used = 0;
+
+	for (i = 0; i < num_trans; i++) {
+		int j, found;
+		char *curname;
+		fs_generic_func_p result;
+		fs_generic_func_p *location;
+
+		curname = translation[i].name;
+
+		/* Look for a matching operation in the list supplied by the */
+		/* file system. */
+
+		found = 0;
+
+		for (j = 0; j < num_ops; j++) {
+			if (strcmp(operations[j].name, curname) == 0) {
+				used++;
+				found = 1;
+				break;
+			}
+		}
+
+		/*
+		 * If the file system is using a "placeholder" for default
+		 * or error functions, grab the appropriate function out of
+		 * the translation table.  If the file system didn't supply
+		 * this operation at all, use the default function.
+		 */
+
+		if (found) {
+			result = operations[j].func.fs_generic;
+			if (result == fs_default) {
+				result = translation[i].defaultFunc;
+			} else if (result == fs_error) {
+				result = translation[i].errorFunc;
+			} else if (result == NULL) {
+				/* Null values are PROHIBITED */
+				return (EINVAL);
+			}
+		} else {
+			result = translation[i].defaultFunc;
+		}
+
+		/* Now store the function into the operations vector. */
+
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		location = (fs_generic_func_p *)
+		    (((char *)vector) + translation[i].offset);
+
+		*location = result;
+	}
+
+	*unused_ops = num_ops - used;
+
+	return (0);
+}
+
+/* Placeholder functions, should never be called. */
+
+int
+fs_error(void)
+{
+	cmn_err(CE_PANIC, "fs_error called");
+	return (0);
+}
+
+int
+fs_default(void)
+{
+	cmn_err(CE_PANIC, "fs_default called");
+	return (0);
+}
+
+// rootconf
+// getfsname
+// getrootfs
+
+/*
+ * VFS feature routines
+ */
+
+#define	VFTINDEX(feature)	(((feature) >> 32) & 0xFFFFFFFF)
+#define	VFTBITS(feature)	((feature) & 0xFFFFFFFFLL)
+
+/* Register a feature in the vfs */
+void
+vfs_set_feature(vfs_t *vfsp, vfs_feature_t feature)
+{
+	/* Note that vfs_featureset[] is found in *vfsp->vfs_implp */
+	if (vfsp->vfs_implp == NULL)
+		return;
+
+	vfsp->vfs_featureset[VFTINDEX(feature)] |= VFTBITS(feature);
+}
+
+void
+vfs_clear_feature(vfs_t *vfsp, vfs_feature_t feature)
+{
+	/* Note that vfs_featureset[] is found in *vfsp->vfs_implp */
+	if (vfsp->vfs_implp == NULL)
+		return;
+	vfsp->vfs_featureset[VFTINDEX(feature)] &= VFTBITS(~feature);
+}
+
+/*
+ * Query a vfs for a feature.
+ * Returns 1 if feature is present, 0 if not
+ */
+int
+vfs_has_feature(vfs_t *vfsp, vfs_feature_t feature)
+{
+	int	ret = 0;
+
+	/* Note that vfs_featureset[] is found in *vfsp->vfs_implp */
+	if (vfsp->vfs_implp == NULL)
+		return (ret);
+
+	if (vfsp->vfs_featureset[VFTINDEX(feature)] & VFTBITS(feature))
+		ret = 1;
+
+	return (ret);
+}
+
+// vfs_propagate_features
+// vfs_get_lofi
