176ca3cb0SRobert Mustacchi /*
276ca3cb0SRobert Mustacchi  * This file and its contents are supplied under the terms of the
376ca3cb0SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
476ca3cb0SRobert Mustacchi  * You may only use this file in accordance with the terms of version
576ca3cb0SRobert Mustacchi  * 1.0 of the CDDL.
676ca3cb0SRobert Mustacchi  *
776ca3cb0SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
876ca3cb0SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
976ca3cb0SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
1076ca3cb0SRobert Mustacchi  */
1176ca3cb0SRobert Mustacchi 
1276ca3cb0SRobert Mustacchi /*
1376ca3cb0SRobert Mustacchi  * Copyright (c) 2015 Joyent, Inc.
1476ca3cb0SRobert Mustacchi  */
1576ca3cb0SRobert Mustacchi 
1676ca3cb0SRobert Mustacchi #include <sys/errno.h>
1776ca3cb0SRobert Mustacchi #include <sys/modctl.h>
1876ca3cb0SRobert Mustacchi #include <sys/types.h>
1976ca3cb0SRobert Mustacchi #include <sys/mkdev.h>
2076ca3cb0SRobert Mustacchi #include <sys/ddi.h>
2176ca3cb0SRobert Mustacchi #include <sys/sunddi.h>
2276ca3cb0SRobert Mustacchi #include <sys/vfs.h>
2376ca3cb0SRobert Mustacchi #include <sys/vfs_opreg.h>
2476ca3cb0SRobert Mustacchi #include <sys/systm.h>
2576ca3cb0SRobert Mustacchi #include <sys/id_space.h>
2676ca3cb0SRobert Mustacchi #include <sys/cmn_err.h>
2776ca3cb0SRobert Mustacchi #include <sys/ksynch.h>
2876ca3cb0SRobert Mustacchi #include <sys/policy.h>
2976ca3cb0SRobert Mustacchi #include <sys/mount.h>
3076ca3cb0SRobert Mustacchi #include <sys/sysmacros.h>
3176ca3cb0SRobert Mustacchi 
3276ca3cb0SRobert Mustacchi #include <sys/fs/bootfs_impl.h>
3376ca3cb0SRobert Mustacchi 
3476ca3cb0SRobert Mustacchi /*
3576ca3cb0SRobert Mustacchi  * While booting, additional types of modules and files can be passed in to the
3676ca3cb0SRobert Mustacchi  * loader. These include the familiar boot archive, as well as, a module hash
3776ca3cb0SRobert Mustacchi  * and additional modules that are interpreted as files. As part of the handoff
3876ca3cb0SRobert Mustacchi  * in early boot, information about these modules are saved as properties on the
3976ca3cb0SRobert Mustacchi  * root of the devinfo tree, similar to other boot-time properties.
4076ca3cb0SRobert Mustacchi  *
4176ca3cb0SRobert Mustacchi  * This file system provides a read-only view of those additional files. Due to
4276ca3cb0SRobert Mustacchi  * its limited scope, it has a slightly simpler construction than several other
4376ca3cb0SRobert Mustacchi  * file systems. When mounted, it looks for the corresponding properties and
4476ca3cb0SRobert Mustacchi  * creates bootfs_node_t's and vnodes for all of the corresponding files and
4576ca3cb0SRobert Mustacchi  * directories that exist along the way. At this time, there are currently a
4676ca3cb0SRobert Mustacchi  * rather small number of files passed in this way.
4776ca3cb0SRobert Mustacchi  *
4876ca3cb0SRobert Mustacchi  * This does lead to one behavior that folks used to other file systems might
4976ca3cb0SRobert Mustacchi  * find peculiar. Because we are not always actively creating and destroying the
5076ca3cb0SRobert Mustacchi  * required vnodes on demand, the count on the root vnode will not be going up
5176ca3cb0SRobert Mustacchi  * accordingly with the existence of other vnodes. This means that a bootfs file
5276ca3cb0SRobert Mustacchi  * system that is not in use will have all of its vnodes exist with a v_count of
5376ca3cb0SRobert Mustacchi  * one.
5476ca3cb0SRobert Mustacchi  */
5576ca3cb0SRobert Mustacchi 
5676ca3cb0SRobert Mustacchi major_t bootfs_major;
5776ca3cb0SRobert Mustacchi static int bootfs_fstype;
5876ca3cb0SRobert Mustacchi static id_space_t *bootfs_idspace;
5976ca3cb0SRobert Mustacchi static uint64_t bootfs_nactive;
6076ca3cb0SRobert Mustacchi static kmutex_t bootfs_lock;
6176ca3cb0SRobert Mustacchi 
6276ca3cb0SRobert Mustacchi static const char *bootfs_name = "bootfs";
6376ca3cb0SRobert Mustacchi 
6476ca3cb0SRobert Mustacchi static int
bootfs_mount(vfs_t * vfsp,vnode_t * mvp,struct mounta * uap,cred_t * cr)6576ca3cb0SRobert Mustacchi bootfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
6676ca3cb0SRobert Mustacchi {
6776ca3cb0SRobert Mustacchi 	int ret;
6876ca3cb0SRobert Mustacchi 	bootfs_t *bfs;
6976ca3cb0SRobert Mustacchi 	struct pathname dpn;
7076ca3cb0SRobert Mustacchi 	dev_t fsdev;
7176ca3cb0SRobert Mustacchi 
7276ca3cb0SRobert Mustacchi 	if ((ret = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
7376ca3cb0SRobert Mustacchi 		return (ret);
7476ca3cb0SRobert Mustacchi 
7576ca3cb0SRobert Mustacchi 	if (mvp->v_type != VDIR)
7676ca3cb0SRobert Mustacchi 		return (ENOTDIR);
7776ca3cb0SRobert Mustacchi 
7876ca3cb0SRobert Mustacchi 	if (uap->flags & MS_REMOUNT)
7976ca3cb0SRobert Mustacchi 		return (EBUSY);
8076ca3cb0SRobert Mustacchi 
8176ca3cb0SRobert Mustacchi 	mutex_enter(&mvp->v_lock);
8276ca3cb0SRobert Mustacchi 	if ((uap->flags & MS_OVERLAY) == 0 &&
8376ca3cb0SRobert Mustacchi 	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
8476ca3cb0SRobert Mustacchi 		mutex_exit(&mvp->v_lock);
8576ca3cb0SRobert Mustacchi 		return (EBUSY);
8676ca3cb0SRobert Mustacchi 	}
8776ca3cb0SRobert Mustacchi 	mutex_exit(&mvp->v_lock);
8876ca3cb0SRobert Mustacchi 
8976ca3cb0SRobert Mustacchi 	/*
9076ca3cb0SRobert Mustacchi 	 * We indicate that the backing store is bootfs. We don't want to use
9176ca3cb0SRobert Mustacchi 	 * swap, because folks might think that this is putting all the data
9276ca3cb0SRobert Mustacchi 	 * into memory ala tmpfs. Rather these modules are always in memory and
9376ca3cb0SRobert Mustacchi 	 * there's nothing to be done about that.
9476ca3cb0SRobert Mustacchi 	 */
9576ca3cb0SRobert Mustacchi 	vfs_setresource(vfsp, bootfs_name, 0);
96*ca783257SDan McDonald 	bfs = kmem_zalloc(sizeof (bootfs_t), KM_NOSLEEP_LAZY);
9776ca3cb0SRobert Mustacchi 	if (bfs == NULL)
9876ca3cb0SRobert Mustacchi 		return (ENOMEM);
9976ca3cb0SRobert Mustacchi 
10076ca3cb0SRobert Mustacchi 	ret = pn_get(uap->dir,
10176ca3cb0SRobert Mustacchi 	    (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, &dpn);
10276ca3cb0SRobert Mustacchi 	if (ret != 0) {
10376ca3cb0SRobert Mustacchi 		kmem_free(bfs, sizeof (bfs));
10476ca3cb0SRobert Mustacchi 		return (ret);
10576ca3cb0SRobert Mustacchi 	}
10676ca3cb0SRobert Mustacchi 
10776ca3cb0SRobert Mustacchi 	bfs->bfs_minor = id_alloc(bootfs_idspace);
10876ca3cb0SRobert Mustacchi 	bfs->bfs_kstat = kstat_create_zone("bootfs", bfs->bfs_minor, "bootfs",
10976ca3cb0SRobert Mustacchi 	    "fs", KSTAT_TYPE_NAMED,
11076ca3cb0SRobert Mustacchi 	    sizeof (bootfs_stat_t) / sizeof (kstat_named_t),
11176ca3cb0SRobert Mustacchi 	    KSTAT_FLAG_VIRTUAL, GLOBAL_ZONEID);
11276ca3cb0SRobert Mustacchi 	if (bfs->bfs_kstat == NULL) {
11376ca3cb0SRobert Mustacchi 		id_free(bootfs_idspace, bfs->bfs_minor);
11476ca3cb0SRobert Mustacchi 		pn_free(&dpn);
11576ca3cb0SRobert Mustacchi 		kmem_free(bfs, sizeof (bfs));
11676ca3cb0SRobert Mustacchi 		return (ENOMEM);
11776ca3cb0SRobert Mustacchi 	}
11876ca3cb0SRobert Mustacchi 	bfs->bfs_kstat->ks_data = &bfs->bfs_stat;
11976ca3cb0SRobert Mustacchi 
12076ca3cb0SRobert Mustacchi 	fsdev = makedevice(bootfs_major, bfs->bfs_minor);
12176ca3cb0SRobert Mustacchi 	bfs->bfs_vfsp = vfsp;
12276ca3cb0SRobert Mustacchi 
12376ca3cb0SRobert Mustacchi 	vfsp->vfs_data = (caddr_t)bfs;
12476ca3cb0SRobert Mustacchi 	vfsp->vfs_fstype = bootfs_fstype;
12576ca3cb0SRobert Mustacchi 	vfsp->vfs_dev = fsdev;
12676ca3cb0SRobert Mustacchi 	vfsp->vfs_bsize = PAGESIZE;
12776ca3cb0SRobert Mustacchi 	vfsp->vfs_flag |= VFS_RDONLY | VFS_NOSETUID | VFS_NOTRUNC |
12876ca3cb0SRobert Mustacchi 	    VFS_UNLINKABLE;
12976ca3cb0SRobert Mustacchi 	vfs_make_fsid(&vfsp->vfs_fsid, fsdev, bootfs_fstype);
13076ca3cb0SRobert Mustacchi 	bfs->bfs_mntpath = kmem_alloc(dpn.pn_pathlen + 1, KM_SLEEP);
13176ca3cb0SRobert Mustacchi 	bcopy(dpn.pn_path, bfs->bfs_mntpath, dpn.pn_pathlen);
13276ca3cb0SRobert Mustacchi 	bfs->bfs_mntpath[dpn.pn_pathlen] = '\0';
13376ca3cb0SRobert Mustacchi 	pn_free(&dpn);
13476ca3cb0SRobert Mustacchi 	list_create(&bfs->bfs_nodes, sizeof (bootfs_node_t),
13576ca3cb0SRobert Mustacchi 	    offsetof(bootfs_node_t, bvn_alink));
13676ca3cb0SRobert Mustacchi 
13776ca3cb0SRobert Mustacchi 	kstat_named_init(&bfs->bfs_stat.bfss_nfiles, "nfiles",
13876ca3cb0SRobert Mustacchi 	    KSTAT_DATA_UINT32);
13976ca3cb0SRobert Mustacchi 	kstat_named_init(&bfs->bfs_stat.bfss_ndirs, "ndirs",
14076ca3cb0SRobert Mustacchi 	    KSTAT_DATA_UINT32);
14176ca3cb0SRobert Mustacchi 	kstat_named_init(&bfs->bfs_stat.bfss_nbytes, "nbytes",
14276ca3cb0SRobert Mustacchi 	    KSTAT_DATA_UINT64);
14376ca3cb0SRobert Mustacchi 	kstat_named_init(&bfs->bfs_stat.bfss_ndups, "ndup",
14476ca3cb0SRobert Mustacchi 	    KSTAT_DATA_UINT32);
14576ca3cb0SRobert Mustacchi 	kstat_named_init(&bfs->bfs_stat.bfss_ndiscards, "ndiscard",
14676ca3cb0SRobert Mustacchi 	    KSTAT_DATA_UINT32);
14776ca3cb0SRobert Mustacchi 
14876ca3cb0SRobert Mustacchi 	bootfs_construct(bfs);
14976ca3cb0SRobert Mustacchi 
15076ca3cb0SRobert Mustacchi 	kstat_install(bfs->bfs_kstat);
15176ca3cb0SRobert Mustacchi 
15276ca3cb0SRobert Mustacchi 	return (0);
15376ca3cb0SRobert Mustacchi }
15476ca3cb0SRobert Mustacchi 
15576ca3cb0SRobert Mustacchi static int
bootfs_unmount(vfs_t * vfsp,int flag,cred_t * cr)15676ca3cb0SRobert Mustacchi bootfs_unmount(vfs_t *vfsp, int flag, cred_t *cr)
15776ca3cb0SRobert Mustacchi {
15876ca3cb0SRobert Mustacchi 	int ret;
15976ca3cb0SRobert Mustacchi 	bootfs_t *bfs = vfsp->vfs_data;
16076ca3cb0SRobert Mustacchi 	bootfs_node_t *bnp;
16176ca3cb0SRobert Mustacchi 
16276ca3cb0SRobert Mustacchi 	if ((ret = secpolicy_fs_unmount(cr, vfsp)) != 0)
16376ca3cb0SRobert Mustacchi 		return (ret);
16476ca3cb0SRobert Mustacchi 
16576ca3cb0SRobert Mustacchi 	if (flag & MS_FORCE)
16676ca3cb0SRobert Mustacchi 		return (ENOTSUP);
16776ca3cb0SRobert Mustacchi 
16876ca3cb0SRobert Mustacchi 	for (bnp = list_head(&bfs->bfs_nodes); bnp != NULL;
16976ca3cb0SRobert Mustacchi 	    bnp = list_next(&bfs->bfs_nodes, bnp)) {
17076ca3cb0SRobert Mustacchi 		mutex_enter(&bnp->bvn_vnp->v_lock);
17176ca3cb0SRobert Mustacchi 		if (bnp->bvn_vnp->v_count > 1) {
17276ca3cb0SRobert Mustacchi 			mutex_exit(&bnp->bvn_vnp->v_lock);
17376ca3cb0SRobert Mustacchi 			return (EBUSY);
17476ca3cb0SRobert Mustacchi 		}
17576ca3cb0SRobert Mustacchi 		mutex_exit(&bnp->bvn_vnp->v_lock);
17676ca3cb0SRobert Mustacchi 	}
17776ca3cb0SRobert Mustacchi 
17876ca3cb0SRobert Mustacchi 	kstat_delete(bfs->bfs_kstat);
17976ca3cb0SRobert Mustacchi 	bootfs_destruct(bfs);
18076ca3cb0SRobert Mustacchi 	list_destroy(&bfs->bfs_nodes);
18176ca3cb0SRobert Mustacchi 	kmem_free(bfs->bfs_mntpath, strlen(bfs->bfs_mntpath) + 1);
18276ca3cb0SRobert Mustacchi 	id_free(bootfs_idspace, bfs->bfs_minor);
18376ca3cb0SRobert Mustacchi 	kmem_free(bfs, sizeof (bootfs_t));
18476ca3cb0SRobert Mustacchi 	return (0);
18576ca3cb0SRobert Mustacchi }
18676ca3cb0SRobert Mustacchi 
18776ca3cb0SRobert Mustacchi static int
bootfs_root(vfs_t * vfsp,vnode_t ** vpp)18876ca3cb0SRobert Mustacchi bootfs_root(vfs_t *vfsp, vnode_t **vpp)
18976ca3cb0SRobert Mustacchi {
19076ca3cb0SRobert Mustacchi 	bootfs_t *bfs;
19176ca3cb0SRobert Mustacchi 
19276ca3cb0SRobert Mustacchi 	bfs = (bootfs_t *)vfsp->vfs_data;
19376ca3cb0SRobert Mustacchi 	*vpp = bfs->bfs_rootvn->bvn_vnp;
19476ca3cb0SRobert Mustacchi 	VN_HOLD(*vpp)
19576ca3cb0SRobert Mustacchi 
19676ca3cb0SRobert Mustacchi 	return (0);
19776ca3cb0SRobert Mustacchi }
19876ca3cb0SRobert Mustacchi 
19976ca3cb0SRobert Mustacchi static int
bootfs_statvfs(vfs_t * vfsp,struct statvfs64 * sbp)20076ca3cb0SRobert Mustacchi bootfs_statvfs(vfs_t *vfsp, struct statvfs64 *sbp)
20176ca3cb0SRobert Mustacchi {
20276ca3cb0SRobert Mustacchi 	const bootfs_t *bfs = (bootfs_t *)vfsp;
20376ca3cb0SRobert Mustacchi 	dev32_t d32;
20476ca3cb0SRobert Mustacchi 
20576ca3cb0SRobert Mustacchi 	sbp->f_bsize = PAGESIZE;
20676ca3cb0SRobert Mustacchi 	sbp->f_frsize = PAGESIZE;
20776ca3cb0SRobert Mustacchi 
20876ca3cb0SRobert Mustacchi 	sbp->f_blocks = bfs->bfs_stat.bfss_nbytes.value.ui64 >> PAGESHIFT;
20976ca3cb0SRobert Mustacchi 	sbp->f_bfree = 0;
21076ca3cb0SRobert Mustacchi 	sbp->f_bavail = 0;
21176ca3cb0SRobert Mustacchi 
21276ca3cb0SRobert Mustacchi 	sbp->f_files = bfs->bfs_stat.bfss_nfiles.value.ui32 +
21376ca3cb0SRobert Mustacchi 	    bfs->bfs_stat.bfss_ndirs.value.ui32;
21476ca3cb0SRobert Mustacchi 	sbp->f_ffree = 0;
21576ca3cb0SRobert Mustacchi 	sbp->f_favail = 0;
21676ca3cb0SRobert Mustacchi 
21776ca3cb0SRobert Mustacchi 	(void) cmpldev(&d32, vfsp->vfs_dev);
21876ca3cb0SRobert Mustacchi 	sbp->f_fsid = d32;
21976ca3cb0SRobert Mustacchi 	(void) strlcpy(sbp->f_basetype, bootfs_name, FSTYPSZ);
22076ca3cb0SRobert Mustacchi 	bzero(sbp->f_fstr, sizeof (sbp->f_fstr));
22176ca3cb0SRobert Mustacchi 
22276ca3cb0SRobert Mustacchi 	return (0);
22376ca3cb0SRobert Mustacchi }
22476ca3cb0SRobert Mustacchi 
22576ca3cb0SRobert Mustacchi static const fs_operation_def_t bootfs_vfsops_tmpl[] = {
22676ca3cb0SRobert Mustacchi 	VFSNAME_MOUNT,		{ .vfs_mount = bootfs_mount },
22776ca3cb0SRobert Mustacchi 	VFSNAME_UNMOUNT,	{ .vfs_unmount = bootfs_unmount },
22876ca3cb0SRobert Mustacchi 	VFSNAME_ROOT,		{ .vfs_root = bootfs_root },
22976ca3cb0SRobert Mustacchi 	VFSNAME_STATVFS,	{ .vfs_statvfs = bootfs_statvfs },
23076ca3cb0SRobert Mustacchi 	NULL,			NULL
23176ca3cb0SRobert Mustacchi };
23276ca3cb0SRobert Mustacchi 
23376ca3cb0SRobert Mustacchi static int
bootfs_init(int fstype,char * name)23476ca3cb0SRobert Mustacchi bootfs_init(int fstype, char *name)
23576ca3cb0SRobert Mustacchi {
23676ca3cb0SRobert Mustacchi 	int ret;
23776ca3cb0SRobert Mustacchi 
23876ca3cb0SRobert Mustacchi 	bootfs_fstype = fstype;
23976ca3cb0SRobert Mustacchi 	ASSERT(bootfs_fstype != 0);
24076ca3cb0SRobert Mustacchi 
24176ca3cb0SRobert Mustacchi 	ret = vfs_setfsops(fstype, bootfs_vfsops_tmpl, NULL);
24276ca3cb0SRobert Mustacchi 	if (ret != 0)
24376ca3cb0SRobert Mustacchi 		return (ret);
24476ca3cb0SRobert Mustacchi 
24576ca3cb0SRobert Mustacchi 	ret = vn_make_ops(name, bootfs_vnodeops_template, &bootfs_vnodeops);
24676ca3cb0SRobert Mustacchi 	if (ret != 0) {
24776ca3cb0SRobert Mustacchi 		(void) vfs_freevfsops_by_type(bootfs_fstype);
24876ca3cb0SRobert Mustacchi 		return (ret);
24976ca3cb0SRobert Mustacchi 	}
25076ca3cb0SRobert Mustacchi 
25176ca3cb0SRobert Mustacchi 	bootfs_major = getudev();
25276ca3cb0SRobert Mustacchi 	if (bootfs_major == (major_t)-1) {
25376ca3cb0SRobert Mustacchi 		cmn_err(CE_WARN, "bootfs_init: Can't get unique device number");
25476ca3cb0SRobert Mustacchi 		bootfs_major = 0;
25576ca3cb0SRobert Mustacchi 	}
25676ca3cb0SRobert Mustacchi 
25776ca3cb0SRobert Mustacchi 	bootfs_nactive = 0;
25876ca3cb0SRobert Mustacchi 	return (0);
25976ca3cb0SRobert Mustacchi }
26076ca3cb0SRobert Mustacchi 
26176ca3cb0SRobert Mustacchi static mntopts_t bootfs_mntopts = {
26276ca3cb0SRobert Mustacchi 	0, NULL
26376ca3cb0SRobert Mustacchi };
26476ca3cb0SRobert Mustacchi 
26576ca3cb0SRobert Mustacchi static vfsdef_t bootfs_vfsdef = {
26676ca3cb0SRobert Mustacchi 	VFSDEF_VERSION,
26776ca3cb0SRobert Mustacchi 	"bootfs",
26876ca3cb0SRobert Mustacchi 	bootfs_init,
26976ca3cb0SRobert Mustacchi 	VSW_HASPROTO|VSW_STATS,
27076ca3cb0SRobert Mustacchi 	&bootfs_mntopts
27176ca3cb0SRobert Mustacchi };
27276ca3cb0SRobert Mustacchi 
27376ca3cb0SRobert Mustacchi static struct modlfs bootfs_modlfs = {
27476ca3cb0SRobert Mustacchi 	&mod_fsops, "boot-time modules file system", &bootfs_vfsdef
27576ca3cb0SRobert Mustacchi };
27676ca3cb0SRobert Mustacchi 
27776ca3cb0SRobert Mustacchi static struct modlinkage bootfs_modlinkage = {
27876ca3cb0SRobert Mustacchi 	MODREV_1, &bootfs_modlfs, NULL
27976ca3cb0SRobert Mustacchi };
28076ca3cb0SRobert Mustacchi 
28176ca3cb0SRobert Mustacchi int
_init(void)28276ca3cb0SRobert Mustacchi _init(void)
28376ca3cb0SRobert Mustacchi {
28476ca3cb0SRobert Mustacchi 	bootfs_node_cache = kmem_cache_create("bootfs_node_cache",
28576ca3cb0SRobert Mustacchi 	    sizeof (bootfs_node_t), 0, bootfs_node_constructor,
28676ca3cb0SRobert Mustacchi 	    bootfs_node_destructor, NULL, NULL, NULL, 0);
28776ca3cb0SRobert Mustacchi 	bootfs_idspace = id_space_create("bootfs_minors", 1, INT32_MAX);
28876ca3cb0SRobert Mustacchi 	mutex_init(&bootfs_lock, NULL, MUTEX_DEFAULT, NULL);
28976ca3cb0SRobert Mustacchi 
29076ca3cb0SRobert Mustacchi 	return (mod_install(&bootfs_modlinkage));
29176ca3cb0SRobert Mustacchi }
29276ca3cb0SRobert Mustacchi 
29376ca3cb0SRobert Mustacchi int
_info(struct modinfo * modinfop)29476ca3cb0SRobert Mustacchi _info(struct modinfo *modinfop)
29576ca3cb0SRobert Mustacchi {
29676ca3cb0SRobert Mustacchi 	return (mod_info(&bootfs_modlinkage, modinfop));
29776ca3cb0SRobert Mustacchi }
29876ca3cb0SRobert Mustacchi 
29976ca3cb0SRobert Mustacchi int
_fini(void)30076ca3cb0SRobert Mustacchi _fini(void)
30176ca3cb0SRobert Mustacchi {
30276ca3cb0SRobert Mustacchi 	int err;
30376ca3cb0SRobert Mustacchi 
30476ca3cb0SRobert Mustacchi 	mutex_enter(&bootfs_lock);
30576ca3cb0SRobert Mustacchi 	if (bootfs_nactive > 0) {
30676ca3cb0SRobert Mustacchi 		mutex_exit(&bootfs_lock);
30776ca3cb0SRobert Mustacchi 		return (EBUSY);
30876ca3cb0SRobert Mustacchi 	}
30976ca3cb0SRobert Mustacchi 	mutex_exit(&bootfs_lock);
31076ca3cb0SRobert Mustacchi 
31176ca3cb0SRobert Mustacchi 	err = mod_remove(&bootfs_modlinkage);
31276ca3cb0SRobert Mustacchi 	if (err != 0)
31376ca3cb0SRobert Mustacchi 		return (err);
31476ca3cb0SRobert Mustacchi 
31576ca3cb0SRobert Mustacchi 	(void) vfs_freevfsops_by_type(bootfs_fstype);
31676ca3cb0SRobert Mustacchi 	vn_freevnodeops(bootfs_vnodeops);
31776ca3cb0SRobert Mustacchi 	id_space_destroy(bootfs_idspace);
31876ca3cb0SRobert Mustacchi 	mutex_destroy(&bootfs_lock);
31976ca3cb0SRobert Mustacchi 	kmem_cache_destroy(bootfs_node_cache);
32076ca3cb0SRobert Mustacchi 	return (err);
32176ca3cb0SRobert Mustacchi }
322