xref: /titanic_51/usr/src/uts/common/fs/dev/sdev_vfsops.c (revision 0ad555ad6a787635be8c8a424168dc59cfbce6c7)
1facf4a8dSllai1 /*
2facf4a8dSllai1  * CDDL HEADER START
3facf4a8dSllai1  *
4facf4a8dSllai1  * The contents of this file are subject to the terms of the
5facf4a8dSllai1  * Common Development and Distribution License (the "License").
6facf4a8dSllai1  * You may not use this file except in compliance with the License.
7facf4a8dSllai1  *
8facf4a8dSllai1  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9facf4a8dSllai1  * or http://www.opensolaris.org/os/licensing.
10facf4a8dSllai1  * See the License for the specific language governing permissions
11facf4a8dSllai1  * and limitations under the License.
12facf4a8dSllai1  *
13facf4a8dSllai1  * When distributing Covered Code, include this CDDL HEADER in each
14facf4a8dSllai1  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15facf4a8dSllai1  * If applicable, add the following below this CDDL HEADER, with the
16facf4a8dSllai1  * fields enclosed by brackets "[]" replaced with your own identifying
17facf4a8dSllai1  * information: Portions Copyright [yyyy] [name of copyright owner]
18facf4a8dSllai1  *
19facf4a8dSllai1  * CDDL HEADER END
20facf4a8dSllai1  */
21facf4a8dSllai1 /*
223c5e027bSEric Taylor  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23facf4a8dSllai1  * Use is subject to license terms.
24*0ad555adSAlex Wilson  * Copyright 2015 Joyent, Inc.  All rights reserved.
25facf4a8dSllai1  */
26facf4a8dSllai1 
27facf4a8dSllai1 /*
28facf4a8dSllai1  * This is the /dev (hence, the sdev_ prefix) filesystem.
29facf4a8dSllai1  */
30facf4a8dSllai1 
31facf4a8dSllai1 #include <sys/types.h>
32facf4a8dSllai1 #include <sys/param.h>
33facf4a8dSllai1 #include <sys/sysmacros.h>
34facf4a8dSllai1 #include <sys/systm.h>
35facf4a8dSllai1 #include <sys/kmem.h>
36facf4a8dSllai1 #include <sys/time.h>
37facf4a8dSllai1 #include <sys/pathname.h>
38facf4a8dSllai1 #include <sys/vfs.h>
39aa59c4cbSrsb #include <sys/vfs_opreg.h>
40facf4a8dSllai1 #include <sys/vnode.h>
41facf4a8dSllai1 #include <sys/file.h>
42facf4a8dSllai1 #include <sys/stat.h>
43facf4a8dSllai1 #include <sys/uio.h>
44facf4a8dSllai1 #include <sys/stat.h>
45facf4a8dSllai1 #include <sys/errno.h>
46facf4a8dSllai1 #include <sys/cmn_err.h>
47facf4a8dSllai1 #include <sys/cred.h>
48facf4a8dSllai1 #include <sys/statvfs.h>
49facf4a8dSllai1 #include <sys/policy.h>
50facf4a8dSllai1 #include <sys/mount.h>
51facf4a8dSllai1 #include <sys/debug.h>
52facf4a8dSllai1 #include <sys/modctl.h>
53facf4a8dSllai1 #include <sys/mkdev.h>
54facf4a8dSllai1 #include <fs/fs_subr.h>
55facf4a8dSllai1 #include <sys/fs/sdev_impl.h>
56facf4a8dSllai1 #include <sys/fs/snode.h>
57facf4a8dSllai1 #include <sys/fs/dv_node.h>
58facf4a8dSllai1 #include <sys/sunndi.h>
59facf4a8dSllai1 #include <sys/mntent.h>
60*0ad555adSAlex Wilson #include <sys/disp.h>
61facf4a8dSllai1 
62facf4a8dSllai1 /*
63facf4a8dSllai1  * /dev vfs operations.
64facf4a8dSllai1  */
65facf4a8dSllai1 
66facf4a8dSllai1 /*
67facf4a8dSllai1  * globals
68facf4a8dSllai1  */
69facf4a8dSllai1 struct sdev_data *sdev_origins; /* mount info for origins under /dev */
700bfaec69Sllai1 kmutex_t sdev_lock; /* used for mount/unmount/rename synchronization */
71*0ad555adSAlex Wilson taskq_t *sdev_taskq = NULL;
72facf4a8dSllai1 
73facf4a8dSllai1 /*
74facf4a8dSllai1  * static
75facf4a8dSllai1  */
76facf4a8dSllai1 static major_t devmajor;	/* the fictitious major we live on */
77facf4a8dSllai1 static major_t devminor;	/* the fictitious minor of this instance */
78facf4a8dSllai1 static struct sdev_data *sdev_mntinfo = NULL;	/* linked list of instances */
79bc1009abSjg 
80bc1009abSjg /* LINTED E_STATIC_UNUSED */		/* useful for debugging */
81facf4a8dSllai1 static struct vnode *sdev_stale_attrvp; /* stale root attrvp after remount */
82facf4a8dSllai1 
83facf4a8dSllai1 static int sdev_mount(struct vfs *, struct vnode *, struct mounta *,
84facf4a8dSllai1     struct cred *);
85facf4a8dSllai1 static int sdev_unmount(struct vfs *, int, struct cred *);
86facf4a8dSllai1 static int sdev_root(struct vfs *, struct vnode **);
87facf4a8dSllai1 static int sdev_statvfs(struct vfs *, struct statvfs64 *);
88facf4a8dSllai1 static void sdev_insert_mntinfo(struct sdev_data *);
89facf4a8dSllai1 static int devinit(int, char *);
90facf4a8dSllai1 
91facf4a8dSllai1 static vfsdef_t sdev_vfssw = {
92facf4a8dSllai1 	VFSDEF_VERSION,
93facf4a8dSllai1 	"dev",		/* type name string */
94facf4a8dSllai1 	devinit,	/* init routine */
95facf4a8dSllai1 	VSW_CANREMOUNT,	/* flags */
96facf4a8dSllai1 	NULL		/* mount options table prototype */
97facf4a8dSllai1 };
98facf4a8dSllai1 
99facf4a8dSllai1 
100facf4a8dSllai1 /*
101facf4a8dSllai1  * Module linkage information
102facf4a8dSllai1  */
103facf4a8dSllai1 static struct modlfs modlfs = {
10439b361b2SRichard Bean 	&mod_fsops, "/dev filesystem", &sdev_vfssw
105facf4a8dSllai1 };
106facf4a8dSllai1 
107facf4a8dSllai1 static struct modlinkage modlinkage = {
108facf4a8dSllai1 	MODREV_1, (void *)&modlfs, NULL
109facf4a8dSllai1 };
110facf4a8dSllai1 
111facf4a8dSllai1 int
112facf4a8dSllai1 _init(void)
113facf4a8dSllai1 {
114facf4a8dSllai1 	int e;
115facf4a8dSllai1 
116facf4a8dSllai1 	mutex_init(&sdev_lock, NULL, MUTEX_DEFAULT, NULL);
117facf4a8dSllai1 	sdev_node_cache_init();
118facf4a8dSllai1 	sdev_devfsadm_lockinit();
119facf4a8dSllai1 	if ((e = mod_install(&modlinkage)) != 0) {
120facf4a8dSllai1 		sdev_devfsadm_lockdestroy();
121facf4a8dSllai1 		sdev_node_cache_fini();
122facf4a8dSllai1 		mutex_destroy(&sdev_lock);
123facf4a8dSllai1 		return (e);
124facf4a8dSllai1 	}
125facf4a8dSllai1 	return (0);
126facf4a8dSllai1 }
127facf4a8dSllai1 
128facf4a8dSllai1 /*
129facf4a8dSllai1  * dev module remained loaded for the global /dev instance
130facf4a8dSllai1  */
131facf4a8dSllai1 int
132facf4a8dSllai1 _fini(void)
133facf4a8dSllai1 {
134facf4a8dSllai1 	return (EBUSY);
135facf4a8dSllai1 }
136facf4a8dSllai1 
137facf4a8dSllai1 int
138facf4a8dSllai1 _info(struct modinfo *modinfop)
139facf4a8dSllai1 {
140facf4a8dSllai1 	return (mod_info(&modlinkage, modinfop));
141facf4a8dSllai1 }
142facf4a8dSllai1 
143facf4a8dSllai1 /*ARGSUSED*/
144facf4a8dSllai1 static int
145facf4a8dSllai1 devinit(int fstype, char *name)
146facf4a8dSllai1 {
147facf4a8dSllai1 	static const fs_operation_def_t dev_vfsops_tbl[] = {
148aa59c4cbSrsb 		VFSNAME_MOUNT,		{ .vfs_mount = sdev_mount },
149aa59c4cbSrsb 		VFSNAME_UNMOUNT,	{ .vfs_unmount = sdev_unmount },
150aa59c4cbSrsb 		VFSNAME_ROOT, 		{ .vfs_root = sdev_root },
151aa59c4cbSrsb 		VFSNAME_STATVFS,	{ .vfs_statvfs = sdev_statvfs },
152facf4a8dSllai1 		NULL,			NULL
153facf4a8dSllai1 	};
154facf4a8dSllai1 
155facf4a8dSllai1 	int	error;
156facf4a8dSllai1 	extern major_t getudev(void);
157facf4a8dSllai1 
158facf4a8dSllai1 	devtype = fstype;
159facf4a8dSllai1 
160facf4a8dSllai1 	error = vfs_setfsops(fstype, dev_vfsops_tbl, NULL);
161facf4a8dSllai1 	if (error != 0) {
162facf4a8dSllai1 		cmn_err(CE_WARN, "devinit: bad vfs ops tbl");
163facf4a8dSllai1 		return (error);
164facf4a8dSllai1 	}
165facf4a8dSllai1 
166facf4a8dSllai1 	error = vn_make_ops("dev", sdev_vnodeops_tbl, &sdev_vnodeops);
167facf4a8dSllai1 	if (error != 0) {
168facf4a8dSllai1 		(void) vfs_freevfsops_by_type(fstype);
169facf4a8dSllai1 		cmn_err(CE_WARN, "devinit: bad vnode ops tbl");
170facf4a8dSllai1 		return (error);
171facf4a8dSllai1 	}
172facf4a8dSllai1 
173facf4a8dSllai1 	if ((devmajor = getudev()) == (major_t)-1) {
174facf4a8dSllai1 		cmn_err(CE_WARN, "%s: can't get unique dev", sdev_vfssw.name);
175facf4a8dSllai1 		return (1);
176facf4a8dSllai1 	}
177facf4a8dSllai1 
178facf4a8dSllai1 	/* initialize negative cache */
179facf4a8dSllai1 	sdev_ncache_init();
180facf4a8dSllai1 
181facf4a8dSllai1 	return (0);
182facf4a8dSllai1 }
183facf4a8dSllai1 
184facf4a8dSllai1 /*
185facf4a8dSllai1  * Both mount point and backing store directory name are
186facf4a8dSllai1  * passed in from userland
187facf4a8dSllai1  */
188facf4a8dSllai1 static int
189facf4a8dSllai1 sdev_mount(struct vfs *vfsp, struct vnode *mvp, struct mounta *uap,
190facf4a8dSllai1     struct cred *cr)
191facf4a8dSllai1 {
192facf4a8dSllai1 	struct sdev_data *sdev_data;
193facf4a8dSllai1 	struct vnode *avp;
194facf4a8dSllai1 	struct sdev_node *dv;
195facf4a8dSllai1 	struct sdev_mountargs *args = NULL;
196facf4a8dSllai1 	int	error = 0;
197facf4a8dSllai1 	dev_t	devdev;
198facf4a8dSllai1 
199facf4a8dSllai1 	/*
200facf4a8dSllai1 	 * security check
201facf4a8dSllai1 	 */
202facf4a8dSllai1 	if ((secpolicy_fs_mount(cr, mvp, vfsp) != 0) ||
203facf4a8dSllai1 	    (secpolicy_sys_devices(cr) != 0))
204facf4a8dSllai1 		return (EPERM);
205facf4a8dSllai1 
206facf4a8dSllai1 	/*
207facf4a8dSllai1 	 * Sanity check the mount point
208facf4a8dSllai1 	 */
209facf4a8dSllai1 	if (mvp->v_type != VDIR)
210facf4a8dSllai1 		return (ENOTDIR);
211facf4a8dSllai1 
212facf4a8dSllai1 	/*
213facf4a8dSllai1 	 * Sanity Check for overlay mount.
214facf4a8dSllai1 	 */
215facf4a8dSllai1 	mutex_enter(&mvp->v_lock);
216facf4a8dSllai1 	if ((uap->flags & MS_OVERLAY) == 0 &&
217facf4a8dSllai1 	    (uap->flags & MS_REMOUNT) == 0 &&
218facf4a8dSllai1 	    (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
219facf4a8dSllai1 		mutex_exit(&mvp->v_lock);
220facf4a8dSllai1 		return (EBUSY);
221facf4a8dSllai1 	}
222facf4a8dSllai1 	mutex_exit(&mvp->v_lock);
223facf4a8dSllai1 
224facf4a8dSllai1 	args = kmem_zalloc(sizeof (*args), KM_SLEEP);
225facf4a8dSllai1 
226facf4a8dSllai1 	if ((uap->flags & MS_DATA) &&
227facf4a8dSllai1 	    (uap->datalen != 0 && uap->dataptr != NULL)) {
228facf4a8dSllai1 		/* copy in the arguments */
229facf4a8dSllai1 		if (error = sdev_copyin_mountargs(uap, args))
230facf4a8dSllai1 			goto cleanup;
231facf4a8dSllai1 	}
232facf4a8dSllai1 
233facf4a8dSllai1 	/*
234facf4a8dSllai1 	 * Sanity check the backing store
235facf4a8dSllai1 	 */
236facf4a8dSllai1 	if (args->sdev_attrdir) {
237facf4a8dSllai1 		/* user supplied an attribute store */
238facf4a8dSllai1 		if (error = lookupname((char *)(uintptr_t)args->sdev_attrdir,
239facf4a8dSllai1 		    UIO_USERSPACE, FOLLOW, NULLVPP, &avp)) {
240facf4a8dSllai1 			cmn_err(CE_NOTE, "/dev fs: lookup on attribute "
241facf4a8dSllai1 			    "directory %s failed",
242facf4a8dSllai1 			    (char *)(uintptr_t)args->sdev_attrdir);
243facf4a8dSllai1 			goto cleanup;
244facf4a8dSllai1 		}
245facf4a8dSllai1 
246facf4a8dSllai1 		if (avp->v_type != VDIR) {
247facf4a8dSllai1 			VN_RELE(avp);
248facf4a8dSllai1 			error = ENOTDIR;
249facf4a8dSllai1 			goto cleanup;
250facf4a8dSllai1 		}
251facf4a8dSllai1 	} else {
252facf4a8dSllai1 		/* use mountp as the attribute store */
253facf4a8dSllai1 		avp = mvp;
254facf4a8dSllai1 		VN_HOLD(avp);
255facf4a8dSllai1 	}
256facf4a8dSllai1 
257facf4a8dSllai1 	mutex_enter(&sdev_lock);
258facf4a8dSllai1 
259facf4a8dSllai1 	/*
260*0ad555adSAlex Wilson 	 * Check that the taskq has been created. We can't do this in our
261*0ad555adSAlex Wilson 	 * _init or devinit because they run too early for ddi_taskq_create.
262*0ad555adSAlex Wilson 	 */
263*0ad555adSAlex Wilson 	if (sdev_taskq == NULL) {
264*0ad555adSAlex Wilson 		sdev_taskq = taskq_create("sdev", 1, minclsyspri, 1, 1, 0);
265*0ad555adSAlex Wilson 		if (sdev_taskq == NULL) {
266*0ad555adSAlex Wilson 			error = ENOMEM;
267*0ad555adSAlex Wilson 			mutex_exit(&sdev_lock);
268*0ad555adSAlex Wilson 			VN_RELE(avp);
269*0ad555adSAlex Wilson 			goto cleanup;
270*0ad555adSAlex Wilson 		}
271*0ad555adSAlex Wilson 	}
272*0ad555adSAlex Wilson 
273*0ad555adSAlex Wilson 	/*
274facf4a8dSllai1 	 * handling installation
275facf4a8dSllai1 	 */
276facf4a8dSllai1 	if (uap->flags & MS_REMOUNT) {
277facf4a8dSllai1 		sdev_data = (struct sdev_data *)vfsp->vfs_data;
278facf4a8dSllai1 		ASSERT(sdev_data);
279facf4a8dSllai1 
280facf4a8dSllai1 		dv = sdev_data->sdev_root;
281facf4a8dSllai1 		ASSERT(dv == dv->sdev_dotdot);
282facf4a8dSllai1 
283facf4a8dSllai1 		/*
284facf4a8dSllai1 		 * mark all existing sdev_nodes (except root node) stale
285facf4a8dSllai1 		 */
286facf4a8dSllai1 		sdev_stale(dv);
287facf4a8dSllai1 
288facf4a8dSllai1 		/* Reset previous mountargs */
289facf4a8dSllai1 		if (sdev_data->sdev_mountargs) {
290facf4a8dSllai1 			kmem_free(sdev_data->sdev_mountargs,
291facf4a8dSllai1 			    sizeof (struct sdev_mountargs));
292facf4a8dSllai1 		}
293facf4a8dSllai1 		sdev_data->sdev_mountargs = args;
294facf4a8dSllai1 		args = NULL;		/* so it won't be freed below */
295facf4a8dSllai1 
296facf4a8dSllai1 		sdev_stale_attrvp = dv->sdev_attrvp;
297facf4a8dSllai1 		dv->sdev_attrvp = avp;
298facf4a8dSllai1 		vfsp->vfs_mtime = ddi_get_time();
299facf4a8dSllai1 
300facf4a8dSllai1 		mutex_exit(&sdev_lock);
301facf4a8dSllai1 		goto cleanup;				/* we're done */
302facf4a8dSllai1 	}
303facf4a8dSllai1 
304facf4a8dSllai1 	/*
305facf4a8dSllai1 	 * Create and initialize the vfs-private data.
306facf4a8dSllai1 	 */
307facf4a8dSllai1 	devdev = makedevice(devmajor, devminor);
308facf4a8dSllai1 	while (vfs_devismounted(devdev)) {
309facf4a8dSllai1 		devminor = (devminor + 1) & MAXMIN32;
310facf4a8dSllai1 
311facf4a8dSllai1 		/*
312facf4a8dSllai1 		 * All the minor numbers are used up.
313facf4a8dSllai1 		 */
314facf4a8dSllai1 		if (devminor == 0) {
315facf4a8dSllai1 			mutex_exit(&sdev_lock);
316facf4a8dSllai1 			VN_RELE(avp);
317facf4a8dSllai1 			error = ENODEV;
318facf4a8dSllai1 			goto cleanup;
319facf4a8dSllai1 		}
320facf4a8dSllai1 
321facf4a8dSllai1 		devdev = makedevice(devmajor, devminor);
322facf4a8dSllai1 	}
323facf4a8dSllai1 
324facf4a8dSllai1 	dv = sdev_mkroot(vfsp, devdev, mvp, avp, cr);
325facf4a8dSllai1 	sdev_data = kmem_zalloc(sizeof (struct sdev_data), KM_SLEEP);
326facf4a8dSllai1 	vfsp->vfs_dev = devdev;
327facf4a8dSllai1 	vfsp->vfs_data = (caddr_t)sdev_data;
328facf4a8dSllai1 	vfsp->vfs_fstype = devtype;
329facf4a8dSllai1 	vfsp->vfs_bsize = DEV_BSIZE;
330facf4a8dSllai1 	vfsp->vfs_mtime = ddi_get_time();
331facf4a8dSllai1 	vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, devtype);
332facf4a8dSllai1 
333facf4a8dSllai1 	ASSERT(dv == dv->sdev_dotdot);
334facf4a8dSllai1 
335facf4a8dSllai1 	sdev_data->sdev_vfsp = vfsp;
336facf4a8dSllai1 	sdev_data->sdev_root = dv;
337facf4a8dSllai1 	sdev_data->sdev_mountargs = args;
338facf4a8dSllai1 
339facf4a8dSllai1 	/* get acl flavor from attribute dir */
340facf4a8dSllai1 	if (VOP_PATHCONF(avp, _PC_ACL_ENABLED, &sdev_data->sdev_acl_flavor,
341da6c28aaSamw 	    kcred, NULL) != 0 || sdev_data->sdev_acl_flavor == 0)
342facf4a8dSllai1 		sdev_data->sdev_acl_flavor = _ACL_ACLENT_ENABLED;
343facf4a8dSllai1 
344facf4a8dSllai1 	args = NULL;			/* so it won't be freed below */
345facf4a8dSllai1 	sdev_insert_mntinfo(sdev_data);
346facf4a8dSllai1 	mutex_exit(&sdev_lock);
347facf4a8dSllai1 
348facf4a8dSllai1 	if (!SDEV_IS_GLOBAL(dv)) {
349facf4a8dSllai1 		ASSERT(sdev_origins);
350facf4a8dSllai1 		dv->sdev_flags &= ~SDEV_GLOBAL;
351facf4a8dSllai1 		dv->sdev_origin = sdev_origins->sdev_root;
352facf4a8dSllai1 	} else {
353facf4a8dSllai1 		sdev_ncache_setup();
3546b938478Sjg 		rw_enter(&dv->sdev_contents, RW_WRITER);
3556b938478Sjg 		sdev_filldir_dynamic(dv);
3566b938478Sjg 		rw_exit(&dv->sdev_contents);
357facf4a8dSllai1 	}
358facf4a8dSllai1 
359facf4a8dSllai1 	sdev_update_timestamps(dv->sdev_attrvp,
360facf4a8dSllai1 	    cr, AT_CTIME|AT_MTIME|AT_ATIME);
361facf4a8dSllai1 
362facf4a8dSllai1 cleanup:
363facf4a8dSllai1 	if (args)
364facf4a8dSllai1 		kmem_free(args, sizeof (*args));
365facf4a8dSllai1 	return (error);
366facf4a8dSllai1 }
367facf4a8dSllai1 
368facf4a8dSllai1 /*
369facf4a8dSllai1  * unmounting the non-global /dev instances, e.g. when deleting a Kevlar zone.
370facf4a8dSllai1  */
371facf4a8dSllai1 static int
372facf4a8dSllai1 sdev_unmount(struct vfs *vfsp, int flag, struct cred *cr)
373facf4a8dSllai1 {
374facf4a8dSllai1 	struct sdev_node *dv;
375facf4a8dSllai1 	int error;
376facf4a8dSllai1 	struct sdev_data *sdev_data, *prev, *next;
377facf4a8dSllai1 
378facf4a8dSllai1 	/*
379facf4a8dSllai1 	 * enforce the security policies
380facf4a8dSllai1 	 */
381facf4a8dSllai1 	if ((secpolicy_fs_unmount(cr, vfsp) != 0) ||
382facf4a8dSllai1 	    (secpolicy_sys_devices(cr) != 0))
383facf4a8dSllai1 		return (EPERM);
384facf4a8dSllai1 
385facf4a8dSllai1 	if (flag & MS_FORCE)
386facf4a8dSllai1 		return (ENOTSUP);
387facf4a8dSllai1 
388facf4a8dSllai1 	mutex_enter(&sdev_lock);
389facf4a8dSllai1 	dv = VFSTOSDEVFS(vfsp)->sdev_root;
390facf4a8dSllai1 	ASSERT(dv == dv->sdev_dotdot);
391facf4a8dSllai1 	if (SDEVTOV(dv)->v_count > 1) {
392facf4a8dSllai1 		mutex_exit(&sdev_lock);
393facf4a8dSllai1 		return (EBUSY);
394facf4a8dSllai1 	}
395facf4a8dSllai1 
396facf4a8dSllai1 	/*
397facf4a8dSllai1 	 * global instance remains mounted
398facf4a8dSllai1 	 */
399facf4a8dSllai1 	if (SDEV_IS_GLOBAL(dv)) {
400facf4a8dSllai1 		mutex_exit(&sdev_lock);
401facf4a8dSllai1 		return (EBUSY);
402facf4a8dSllai1 	}
403facf4a8dSllai1 	mutex_exit(&sdev_lock);
404facf4a8dSllai1 
405facf4a8dSllai1 	/* verify the v_count */
406facf4a8dSllai1 	if ((error = sdev_cleandir(dv, NULL, 0)) != 0) {
407facf4a8dSllai1 		return (error);
408facf4a8dSllai1 	}
409facf4a8dSllai1 	ASSERT(SDEVTOV(dv)->v_count == 1);
410facf4a8dSllai1 
411facf4a8dSllai1 	/* release hold on root node and destroy it */
412facf4a8dSllai1 	SDEV_RELE(dv);
413facf4a8dSllai1 	dv->sdev_nlink -= 2;
414facf4a8dSllai1 	sdev_nodedestroy(dv, 0);
415facf4a8dSllai1 
416facf4a8dSllai1 	sdev_data = (struct sdev_data *)vfsp->vfs_data;
417facf4a8dSllai1 	vfsp->vfs_data = (caddr_t)0;
418facf4a8dSllai1 
419facf4a8dSllai1 	/*
420facf4a8dSllai1 	 * XXX separate it into sdev_delete_mntinfo() if useful
421facf4a8dSllai1 	 */
422facf4a8dSllai1 	mutex_enter(&sdev_lock);
423facf4a8dSllai1 	prev = sdev_data->sdev_prev;
424facf4a8dSllai1 	next = sdev_data->sdev_next;
425facf4a8dSllai1 	if (prev)
426facf4a8dSllai1 		prev->sdev_next = next;
427facf4a8dSllai1 	else
428facf4a8dSllai1 		sdev_mntinfo = next;
429facf4a8dSllai1 	if (next)
430facf4a8dSllai1 		next->sdev_prev = prev;
431facf4a8dSllai1 	mutex_exit(&sdev_lock);
432facf4a8dSllai1 
433facf4a8dSllai1 	if (sdev_data->sdev_mountargs) {
434facf4a8dSllai1 		kmem_free(sdev_data->sdev_mountargs,
435facf4a8dSllai1 		    sizeof (struct sdev_mountargs));
436facf4a8dSllai1 	}
437facf4a8dSllai1 	kmem_free(sdev_data, sizeof (struct sdev_data));
438facf4a8dSllai1 	return (0);
439facf4a8dSllai1 }
440facf4a8dSllai1 
441facf4a8dSllai1 /*
442facf4a8dSllai1  * return root vnode for given vfs
443facf4a8dSllai1  */
444facf4a8dSllai1 static int
445facf4a8dSllai1 sdev_root(struct vfs *vfsp, struct vnode **vpp)
446facf4a8dSllai1 {
447facf4a8dSllai1 	*vpp = SDEVTOV(VFSTOSDEVFS(vfsp)->sdev_root);
448facf4a8dSllai1 	VN_HOLD(*vpp);
449facf4a8dSllai1 	return (0);
450facf4a8dSllai1 }
451facf4a8dSllai1 
452facf4a8dSllai1 /*
453facf4a8dSllai1  * return 'generic superblock' information to userland.
454facf4a8dSllai1  *
455facf4a8dSllai1  * not much that we can usefully admit to here
456facf4a8dSllai1  */
457facf4a8dSllai1 static int
458facf4a8dSllai1 sdev_statvfs(struct vfs *vfsp, struct statvfs64 *sbp)
459facf4a8dSllai1 {
460facf4a8dSllai1 	dev32_t d32;
461facf4a8dSllai1 
462facf4a8dSllai1 	bzero(sbp, sizeof (*sbp));
463facf4a8dSllai1 	sbp->f_frsize = sbp->f_bsize = vfsp->vfs_bsize;
464facf4a8dSllai1 	sbp->f_files = kmem_cache_stat(sdev_node_cache, "alloc");
465facf4a8dSllai1 
466facf4a8dSllai1 	/* no illusions that free/avail files is relevant to dev */
467facf4a8dSllai1 	sbp->f_ffree = 0;
468facf4a8dSllai1 	sbp->f_favail = 0;
469facf4a8dSllai1 
470facf4a8dSllai1 	/* no illusions that blocks are relevant to devfs */
471facf4a8dSllai1 	sbp->f_bfree = 0;
472facf4a8dSllai1 	sbp->f_bavail = 0;
473facf4a8dSllai1 	sbp->f_blocks = 0;
474facf4a8dSllai1 
475facf4a8dSllai1 	(void) cmpldev(&d32, vfsp->vfs_dev);
476facf4a8dSllai1 	sbp->f_fsid = d32;
477facf4a8dSllai1 	(void) strcpy(sbp->f_basetype, vfssw[devtype].vsw_name);
478facf4a8dSllai1 	sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
479facf4a8dSllai1 	sbp->f_namemax = MAXNAMELEN - 1;
480facf4a8dSllai1 	(void) strcpy(sbp->f_fstr, "dev");
481facf4a8dSllai1 
482facf4a8dSllai1 	return (0);
483facf4a8dSllai1 }
484facf4a8dSllai1 
485facf4a8dSllai1 static void
486facf4a8dSllai1 sdev_insert_mntinfo(struct sdev_data *data)
487facf4a8dSllai1 {
488facf4a8dSllai1 	ASSERT(mutex_owned(&sdev_lock));
489facf4a8dSllai1 	data->sdev_next = sdev_mntinfo;
490facf4a8dSllai1 	data->sdev_prev = NULL;
491facf4a8dSllai1 	if (sdev_mntinfo) {
492facf4a8dSllai1 		sdev_mntinfo->sdev_prev = data;
493facf4a8dSllai1 	} else {
494facf4a8dSllai1 		sdev_origins = data;
495facf4a8dSllai1 	}
496facf4a8dSllai1 	sdev_mntinfo = data;
497facf4a8dSllai1 }
498facf4a8dSllai1 
499facf4a8dSllai1 struct sdev_data *
500facf4a8dSllai1 sdev_find_mntinfo(char *mntpt)
501facf4a8dSllai1 {
502facf4a8dSllai1 	struct sdev_data *mntinfo;
503facf4a8dSllai1 
504facf4a8dSllai1 	mutex_enter(&sdev_lock);
505facf4a8dSllai1 	mntinfo = sdev_mntinfo;
506facf4a8dSllai1 	while (mntinfo) {
507facf4a8dSllai1 		if (strcmp(mntpt, mntinfo->sdev_root->sdev_name) == 0) {
508facf4a8dSllai1 			SDEVTOV(mntinfo->sdev_root)->v_count++;
509facf4a8dSllai1 			break;
510facf4a8dSllai1 		}
511facf4a8dSllai1 		mntinfo = mntinfo->sdev_next;
512facf4a8dSllai1 	}
513facf4a8dSllai1 	mutex_exit(&sdev_lock);
514facf4a8dSllai1 	return (mntinfo);
515facf4a8dSllai1 }
516facf4a8dSllai1 
517facf4a8dSllai1 void
518facf4a8dSllai1 sdev_mntinfo_rele(struct sdev_data *mntinfo)
519facf4a8dSllai1 {
520facf4a8dSllai1 	mutex_enter(&sdev_lock);
521facf4a8dSllai1 	SDEVTOV(mntinfo->sdev_root)->v_count--;
522facf4a8dSllai1 	mutex_exit(&sdev_lock);
523facf4a8dSllai1 }
524