1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * This is the device filesystem.
31*0Sstevel@tonic-gate  *
32*0Sstevel@tonic-gate  * It is a combination of a namer to drive autoconfiguration,
33*0Sstevel@tonic-gate  * plus the access methods for the device drivers of the system.
34*0Sstevel@tonic-gate  *
35*0Sstevel@tonic-gate  * The prototype is fairly dependent on specfs for the latter part
36*0Sstevel@tonic-gate  * of its implementation, though a final version would integrate the two.
37*0Sstevel@tonic-gate  */
38*0Sstevel@tonic-gate #include <sys/types.h>
39*0Sstevel@tonic-gate #include <sys/param.h>
40*0Sstevel@tonic-gate #include <sys/sysmacros.h>
41*0Sstevel@tonic-gate #include <sys/systm.h>
42*0Sstevel@tonic-gate #include <sys/kmem.h>
43*0Sstevel@tonic-gate #include <sys/time.h>
44*0Sstevel@tonic-gate #include <sys/pathname.h>
45*0Sstevel@tonic-gate #include <sys/vfs.h>
46*0Sstevel@tonic-gate #include <sys/vnode.h>
47*0Sstevel@tonic-gate #include <sys/stat.h>
48*0Sstevel@tonic-gate #include <sys/uio.h>
49*0Sstevel@tonic-gate #include <sys/stat.h>
50*0Sstevel@tonic-gate #include <sys/errno.h>
51*0Sstevel@tonic-gate #include <sys/cmn_err.h>
52*0Sstevel@tonic-gate #include <sys/cred.h>
53*0Sstevel@tonic-gate #include <sys/statvfs.h>
54*0Sstevel@tonic-gate #include <sys/mount.h>
55*0Sstevel@tonic-gate #include <sys/debug.h>
56*0Sstevel@tonic-gate #include <sys/modctl.h>
57*0Sstevel@tonic-gate #include <fs/fs_subr.h>
58*0Sstevel@tonic-gate #include <sys/fs/dv_node.h>
59*0Sstevel@tonic-gate #include <sys/fs/snode.h>
60*0Sstevel@tonic-gate #include <sys/sunndi.h>
61*0Sstevel@tonic-gate #include <sys/policy.h>
62*0Sstevel@tonic-gate 
63*0Sstevel@tonic-gate /*
64*0Sstevel@tonic-gate  * devfs vfs operations.
65*0Sstevel@tonic-gate  */
66*0Sstevel@tonic-gate static int devfs_mount(struct vfs *, struct vnode *, struct mounta *,
67*0Sstevel@tonic-gate     struct cred *);
68*0Sstevel@tonic-gate static int devfs_unmount(struct vfs *, int, struct cred *);
69*0Sstevel@tonic-gate static int devfs_root(struct vfs *, struct vnode **);
70*0Sstevel@tonic-gate static int devfs_statvfs(struct vfs *, struct statvfs64 *);
71*0Sstevel@tonic-gate static int devfs_mountroot(struct vfs *, enum whymountroot);
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate static int devfsinit(int, char *);
74*0Sstevel@tonic-gate 
75*0Sstevel@tonic-gate static vfsdef_t devfs_vfssw = {
76*0Sstevel@tonic-gate 	VFSDEF_VERSION,
77*0Sstevel@tonic-gate 	"devfs",	/* type name string */
78*0Sstevel@tonic-gate 	devfsinit,	/* init routine */
79*0Sstevel@tonic-gate 	0,		/* flags */
80*0Sstevel@tonic-gate 	NULL		/* mount options table prototype */
81*0Sstevel@tonic-gate };
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate static kmutex_t devfs_lock;	/* protects global data */
84*0Sstevel@tonic-gate static int devfstype;		/* fstype */
85*0Sstevel@tonic-gate static dev_t devfsdev;		/* the fictious 'device' we live on */
86*0Sstevel@tonic-gate static struct devfs_data *devfs_mntinfo;	/* linked list of instances */
87*0Sstevel@tonic-gate 
88*0Sstevel@tonic-gate /*
89*0Sstevel@tonic-gate  * Module linkage information
90*0Sstevel@tonic-gate  */
91*0Sstevel@tonic-gate static struct modlfs modlfs = {
92*0Sstevel@tonic-gate 	&mod_fsops, "devices filesystem %I%", &devfs_vfssw
93*0Sstevel@tonic-gate };
94*0Sstevel@tonic-gate 
95*0Sstevel@tonic-gate static struct modlinkage modlinkage = {
96*0Sstevel@tonic-gate 	MODREV_1, (void *)&modlfs, NULL
97*0Sstevel@tonic-gate };
98*0Sstevel@tonic-gate 
99*0Sstevel@tonic-gate int
100*0Sstevel@tonic-gate _init(void)
101*0Sstevel@tonic-gate {
102*0Sstevel@tonic-gate 	int e;
103*0Sstevel@tonic-gate 
104*0Sstevel@tonic-gate 	mutex_init(&devfs_lock, "devfs lock", MUTEX_DEFAULT, NULL);
105*0Sstevel@tonic-gate 	dv_node_cache_init();
106*0Sstevel@tonic-gate 	if ((e = mod_install(&modlinkage)) != 0) {
107*0Sstevel@tonic-gate 		dv_node_cache_fini();
108*0Sstevel@tonic-gate 		mutex_destroy(&devfs_lock);
109*0Sstevel@tonic-gate 		return (e);
110*0Sstevel@tonic-gate 	}
111*0Sstevel@tonic-gate 	dcmn_err(("devfs loaded\n"));
112*0Sstevel@tonic-gate 	return (0);
113*0Sstevel@tonic-gate }
114*0Sstevel@tonic-gate 
115*0Sstevel@tonic-gate int
116*0Sstevel@tonic-gate _fini(void)
117*0Sstevel@tonic-gate {
118*0Sstevel@tonic-gate 	return (EBUSY);
119*0Sstevel@tonic-gate }
120*0Sstevel@tonic-gate 
121*0Sstevel@tonic-gate int
122*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
123*0Sstevel@tonic-gate {
124*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
125*0Sstevel@tonic-gate }
126*0Sstevel@tonic-gate 
127*0Sstevel@tonic-gate /*ARGSUSED1*/
128*0Sstevel@tonic-gate static int
129*0Sstevel@tonic-gate devfsinit(int fstype, char *name)
130*0Sstevel@tonic-gate {
131*0Sstevel@tonic-gate 	static const fs_operation_def_t devfs_vfsops_template[] = {
132*0Sstevel@tonic-gate 		VFSNAME_MOUNT, devfs_mount,
133*0Sstevel@tonic-gate 		VFSNAME_UNMOUNT, devfs_unmount,
134*0Sstevel@tonic-gate 		VFSNAME_ROOT, devfs_root,
135*0Sstevel@tonic-gate 		VFSNAME_STATVFS, devfs_statvfs,
136*0Sstevel@tonic-gate 		VFSNAME_SYNC, (fs_generic_func_p) fs_sync,
137*0Sstevel@tonic-gate 		VFSNAME_MOUNTROOT, devfs_mountroot,
138*0Sstevel@tonic-gate 		NULL, NULL
139*0Sstevel@tonic-gate 	};
140*0Sstevel@tonic-gate 	int error;
141*0Sstevel@tonic-gate 	int dev;
142*0Sstevel@tonic-gate 	extern major_t getudev(void);	/* gack - what a function */
143*0Sstevel@tonic-gate 
144*0Sstevel@tonic-gate 	devfstype = fstype;
145*0Sstevel@tonic-gate 	/*
146*0Sstevel@tonic-gate 	 * Associate VFS ops vector with this fstype
147*0Sstevel@tonic-gate 	 */
148*0Sstevel@tonic-gate 	error = vfs_setfsops(fstype, devfs_vfsops_template, NULL);
149*0Sstevel@tonic-gate 	if (error != 0) {
150*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "devfsinit: bad vfs ops template");
151*0Sstevel@tonic-gate 		return (error);
152*0Sstevel@tonic-gate 	}
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate 	error = vn_make_ops("dev fs", dv_vnodeops_template, &dv_vnodeops);
155*0Sstevel@tonic-gate 	if (error != 0) {
156*0Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
157*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "devfsinit: bad vnode ops template");
158*0Sstevel@tonic-gate 		return (error);
159*0Sstevel@tonic-gate 	}
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate 	/*
162*0Sstevel@tonic-gate 	 * Invent a dev_t (sigh).
163*0Sstevel@tonic-gate 	 */
164*0Sstevel@tonic-gate 	if ((dev = getudev()) == (major_t)-1) {
165*0Sstevel@tonic-gate 		cmn_err(CE_NOTE, "%s: can't get unique dev", devfs_vfssw.name);
166*0Sstevel@tonic-gate 		dev = 0;
167*0Sstevel@tonic-gate 	}
168*0Sstevel@tonic-gate 	devfsdev = makedevice(dev, 0);
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate 	return (0);
171*0Sstevel@tonic-gate }
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate /*
174*0Sstevel@tonic-gate  * The name of the mount point and the name of the attribute
175*0Sstevel@tonic-gate  * filesystem are passed down from userland for now.
176*0Sstevel@tonic-gate  */
177*0Sstevel@tonic-gate static int
178*0Sstevel@tonic-gate devfs_mount(struct vfs *vfsp, struct vnode *mvp, struct mounta *uap,
179*0Sstevel@tonic-gate     struct cred *cr)
180*0Sstevel@tonic-gate {
181*0Sstevel@tonic-gate 	struct devfs_data *devfs_data;
182*0Sstevel@tonic-gate 	struct vnode *avp;
183*0Sstevel@tonic-gate 	struct dv_node *dv;
184*0Sstevel@tonic-gate 	struct vattr va;
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate 	dcmn_err(("devfs_mount\n"));
187*0Sstevel@tonic-gate 
188*0Sstevel@tonic-gate 	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
189*0Sstevel@tonic-gate 		return (EPERM);
190*0Sstevel@tonic-gate 
191*0Sstevel@tonic-gate 	/*
192*0Sstevel@tonic-gate 	 * check that the mount point is sane
193*0Sstevel@tonic-gate 	 */
194*0Sstevel@tonic-gate 	if (mvp->v_type != VDIR)
195*0Sstevel@tonic-gate 		return (ENOTDIR);
196*0Sstevel@tonic-gate 
197*0Sstevel@tonic-gate 	ASSERT(uap->flags & MS_SYSSPACE);
198*0Sstevel@tonic-gate 	/*
199*0Sstevel@tonic-gate 	 * Devfs can only be mounted from kernel during boot.
200*0Sstevel@tonic-gate 	 * avp is the existing /devices, the same as the mount point.
201*0Sstevel@tonic-gate 	 */
202*0Sstevel@tonic-gate 	avp = mvp;
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate 	/*
205*0Sstevel@tonic-gate 	 * Create and initialize the vfs-private data.
206*0Sstevel@tonic-gate 	 * This includes a hand-crafted root vnode (we build
207*0Sstevel@tonic-gate 	 * this here mostly so that traverse() doesn't sleep
208*0Sstevel@tonic-gate 	 * in VFS_ROOT()).
209*0Sstevel@tonic-gate 	 */
210*0Sstevel@tonic-gate 	mutex_enter(&devfs_lock);
211*0Sstevel@tonic-gate 	ASSERT(devfs_mntinfo == NULL);
212*0Sstevel@tonic-gate 	dv = dv_mkroot(vfsp, devfsdev);
213*0Sstevel@tonic-gate 	dv->dv_attrvp = avp;		/* attribute root vp */
214*0Sstevel@tonic-gate 
215*0Sstevel@tonic-gate 	ASSERT(dv == dv->dv_dotdot);
216*0Sstevel@tonic-gate 
217*0Sstevel@tonic-gate 	devfs_data = kmem_zalloc(sizeof (struct devfs_data), KM_SLEEP);
218*0Sstevel@tonic-gate 	devfs_data->devfs_vfsp = vfsp;
219*0Sstevel@tonic-gate 	devfs_data->devfs_root = dv;
220*0Sstevel@tonic-gate 
221*0Sstevel@tonic-gate 	vfsp->vfs_data = (caddr_t)devfs_data;
222*0Sstevel@tonic-gate 	vfsp->vfs_fstype = devfstype;
223*0Sstevel@tonic-gate 	vfsp->vfs_dev = devfsdev;
224*0Sstevel@tonic-gate 	vfsp->vfs_bsize = DEV_BSIZE;
225*0Sstevel@tonic-gate 	vfsp->vfs_mtime = ddi_get_time();
226*0Sstevel@tonic-gate 	vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, devfstype);
227*0Sstevel@tonic-gate 
228*0Sstevel@tonic-gate 	/* We're there. */
229*0Sstevel@tonic-gate 	devfs_mntinfo = devfs_data;
230*0Sstevel@tonic-gate 	mutex_exit(&devfs_lock);
231*0Sstevel@tonic-gate 
232*0Sstevel@tonic-gate 	va.va_mask = AT_ATIME|AT_MTIME;
233*0Sstevel@tonic-gate 	gethrestime(&va.va_atime);
234*0Sstevel@tonic-gate 	gethrestime(&va.va_mtime);
235*0Sstevel@tonic-gate 	(void) VOP_SETATTR(DVTOV(dv), &va, 0, cr, NULL);
236*0Sstevel@tonic-gate 	return (0);
237*0Sstevel@tonic-gate }
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate /*
241*0Sstevel@tonic-gate  * We never unmount devfs in a real production system.
242*0Sstevel@tonic-gate  */
243*0Sstevel@tonic-gate /*ARGSUSED*/
244*0Sstevel@tonic-gate static int
245*0Sstevel@tonic-gate devfs_unmount(struct vfs *vfsp, int flag, struct cred *cr)
246*0Sstevel@tonic-gate {
247*0Sstevel@tonic-gate 	return (EBUSY);
248*0Sstevel@tonic-gate }
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate /*
251*0Sstevel@tonic-gate  * return root vnode for given vfs
252*0Sstevel@tonic-gate  */
253*0Sstevel@tonic-gate static int
254*0Sstevel@tonic-gate devfs_root(struct vfs *vfsp, struct vnode **vpp)
255*0Sstevel@tonic-gate {
256*0Sstevel@tonic-gate 	dcmn_err(("devfs_root\n"));
257*0Sstevel@tonic-gate 	*vpp = DVTOV(VFSTODVFS(vfsp)->devfs_root);
258*0Sstevel@tonic-gate 	VN_HOLD(*vpp);
259*0Sstevel@tonic-gate 	return (0);
260*0Sstevel@tonic-gate }
261*0Sstevel@tonic-gate 
262*0Sstevel@tonic-gate /*
263*0Sstevel@tonic-gate  * return 'generic superblock' information to userland.
264*0Sstevel@tonic-gate  *
265*0Sstevel@tonic-gate  * not much that we can usefully admit to here
266*0Sstevel@tonic-gate  */
267*0Sstevel@tonic-gate static int
268*0Sstevel@tonic-gate devfs_statvfs(struct vfs *vfsp, struct statvfs64 *sbp)
269*0Sstevel@tonic-gate {
270*0Sstevel@tonic-gate 	extern kmem_cache_t *dv_node_cache;
271*0Sstevel@tonic-gate 
272*0Sstevel@tonic-gate 	dev32_t d32;
273*0Sstevel@tonic-gate 
274*0Sstevel@tonic-gate 	dcmn_err(("devfs_statvfs\n"));
275*0Sstevel@tonic-gate 	bzero(sbp, sizeof (*sbp));
276*0Sstevel@tonic-gate 	sbp->f_frsize = sbp->f_bsize = vfsp->vfs_bsize;
277*0Sstevel@tonic-gate 	/*
278*0Sstevel@tonic-gate 	 * We could compute the number of devfsnodes here .. but since
279*0Sstevel@tonic-gate 	 * it's dynamic anyway, it's not clear how useful this is.
280*0Sstevel@tonic-gate 	 */
281*0Sstevel@tonic-gate 	sbp->f_files = kmem_cache_stat(dv_node_cache, "alloc");
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate 	/* no illusions that free/avail files is relevant to devfs */
284*0Sstevel@tonic-gate 	sbp->f_ffree = 0;
285*0Sstevel@tonic-gate 	sbp->f_favail = 0;
286*0Sstevel@tonic-gate 
287*0Sstevel@tonic-gate 	/* no illusions that blocks are relevant to devfs */
288*0Sstevel@tonic-gate 	sbp->f_bfree = 0;
289*0Sstevel@tonic-gate 	sbp->f_bavail = 0;
290*0Sstevel@tonic-gate 	sbp->f_blocks = 0;
291*0Sstevel@tonic-gate 
292*0Sstevel@tonic-gate 	(void) cmpldev(&d32, vfsp->vfs_dev);
293*0Sstevel@tonic-gate 	sbp->f_fsid = d32;
294*0Sstevel@tonic-gate 	(void) strcpy(sbp->f_basetype, vfssw[devfstype].vsw_name);
295*0Sstevel@tonic-gate 	sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
296*0Sstevel@tonic-gate 	sbp->f_namemax = MAXNAMELEN - 1;
297*0Sstevel@tonic-gate 	(void) strcpy(sbp->f_fstr, "devices");
298*0Sstevel@tonic-gate 
299*0Sstevel@tonic-gate 	return (0);
300*0Sstevel@tonic-gate }
301*0Sstevel@tonic-gate 
302*0Sstevel@tonic-gate /*
303*0Sstevel@tonic-gate  * devfs always mount after root is mounted, so this should never
304*0Sstevel@tonic-gate  * be invoked.
305*0Sstevel@tonic-gate  */
306*0Sstevel@tonic-gate /*ARGSUSED*/
307*0Sstevel@tonic-gate static int
308*0Sstevel@tonic-gate devfs_mountroot(struct vfs *vfsp, enum whymountroot why)
309*0Sstevel@tonic-gate {
310*0Sstevel@tonic-gate 	dcmn_err(("devfs_mountroot\n"));
311*0Sstevel@tonic-gate 
312*0Sstevel@tonic-gate 	return (EINVAL);
313*0Sstevel@tonic-gate }
314*0Sstevel@tonic-gate 
315*0Sstevel@tonic-gate struct dv_node *
316*0Sstevel@tonic-gate devfs_dip_to_dvnode(dev_info_t *dip)
317*0Sstevel@tonic-gate {
318*0Sstevel@tonic-gate 	char *dirpath;
319*0Sstevel@tonic-gate 	struct vnode *dirvp;
320*0Sstevel@tonic-gate 
321*0Sstevel@tonic-gate 	ASSERT(dip != NULL);
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate 	/* no-op if devfs not mounted yet */
324*0Sstevel@tonic-gate 	if (devfs_mntinfo == NULL)
325*0Sstevel@tonic-gate 		return (NULL);
326*0Sstevel@tonic-gate 
327*0Sstevel@tonic-gate 	/*
328*0Sstevel@tonic-gate 	 * The lookupname below only looks up cached dv_nodes
329*0Sstevel@tonic-gate 	 * because devfs_clean_key is set in thread specific data.
330*0Sstevel@tonic-gate 	 */
331*0Sstevel@tonic-gate 	dirpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
332*0Sstevel@tonic-gate 	(void) ddi_pathname(dip, dirpath);
333*0Sstevel@tonic-gate 	if (devfs_lookupname(dirpath, NULLVPP, &dirvp)) {
334*0Sstevel@tonic-gate 		dcmn_err(("directory %s not found\n", dirpath));
335*0Sstevel@tonic-gate 		kmem_free(dirpath, MAXPATHLEN);
336*0Sstevel@tonic-gate 		return (NULL);
337*0Sstevel@tonic-gate 	}
338*0Sstevel@tonic-gate 
339*0Sstevel@tonic-gate 	kmem_free(dirpath, MAXPATHLEN);
340*0Sstevel@tonic-gate 	return (VTODV(dirvp));
341*0Sstevel@tonic-gate }
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate /*
344*0Sstevel@tonic-gate  * devfs_clean()
345*0Sstevel@tonic-gate  *
346*0Sstevel@tonic-gate  * Destroy unreferenced dv_node's and detach devices.
347*0Sstevel@tonic-gate  * Returns 0 on success, error if failed to unconfigure node.
348*0Sstevel@tonic-gate  *
349*0Sstevel@tonic-gate  * devfs caches unreferenced dv_node to speed by the performance
350*0Sstevel@tonic-gate  * of ls, find, etc. devfs_clean() is invoked to cleanup cached
351*0Sstevel@tonic-gate  * dv_nodes to reclaim memory as well as to facilitate device
352*0Sstevel@tonic-gate  * removal (dv_node reference devinfo nodes, which prevents driver
353*0Sstevel@tonic-gate  * detach).
354*0Sstevel@tonic-gate  *
355*0Sstevel@tonic-gate  * If a shell parks in a /devices directory, the dv_node will be
356*0Sstevel@tonic-gate  * held, preventing the corresponding device to be detached.
357*0Sstevel@tonic-gate  * This would be a denial of service against DR. To prevent this,
358*0Sstevel@tonic-gate  * DR code calls devfs_clean() with the DV_CLEAN_FORCE flag.
359*0Sstevel@tonic-gate  * The dv_cleandir() implementation does the right thing to ensure
360*0Sstevel@tonic-gate  * successful DR.
361*0Sstevel@tonic-gate  */
362*0Sstevel@tonic-gate int
363*0Sstevel@tonic-gate devfs_clean(dev_info_t *dip, char *devnm, uint_t flags)
364*0Sstevel@tonic-gate {
365*0Sstevel@tonic-gate 	struct dv_node *dvp;
366*0Sstevel@tonic-gate 
367*0Sstevel@tonic-gate 	dcmn_err(("devfs_unconfigure: dip = 0x%p, flags = 0x%x",
368*0Sstevel@tonic-gate 		(void *)dip, flags));
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate 	/* avoid recursion back into the device tree */
371*0Sstevel@tonic-gate 	(void) tsd_set(devfs_clean_key, (void *)1);
372*0Sstevel@tonic-gate 	dvp = devfs_dip_to_dvnode(dip);
373*0Sstevel@tonic-gate 	(void) tsd_set(devfs_clean_key, NULL);
374*0Sstevel@tonic-gate 	if (dvp == NULL)
375*0Sstevel@tonic-gate 		return (0);
376*0Sstevel@tonic-gate 
377*0Sstevel@tonic-gate 	if (dv_cleandir(dvp, devnm, flags) != 0) {
378*0Sstevel@tonic-gate 		VN_RELE(DVTOV(dvp));
379*0Sstevel@tonic-gate 		return (EBUSY);
380*0Sstevel@tonic-gate 	}
381*0Sstevel@tonic-gate 	VN_RELE(DVTOV(dvp));
382*0Sstevel@tonic-gate 	return (0);
383*0Sstevel@tonic-gate }
384*0Sstevel@tonic-gate 
385*0Sstevel@tonic-gate /*
386*0Sstevel@tonic-gate  * lookup a devfs relative pathname, returning held vnodes for the final
387*0Sstevel@tonic-gate  * component and the containing directory (if requested).
388*0Sstevel@tonic-gate  *
389*0Sstevel@tonic-gate  * NOTE: We can't use lookupname because this would use the current
390*0Sstevel@tonic-gate  *	processes credentials (CRED) in the call lookuppnvp instead
391*0Sstevel@tonic-gate  *	of kcred.  It also does not give you the flexibility so
392*0Sstevel@tonic-gate  * 	specify the directory to start the resolution in (devicesdir).
393*0Sstevel@tonic-gate  */
394*0Sstevel@tonic-gate int
395*0Sstevel@tonic-gate devfs_lookupname(
396*0Sstevel@tonic-gate 	char	*pathname,		/* user pathname */
397*0Sstevel@tonic-gate 	vnode_t **dirvpp,		/* ret for ptr to parent dir vnode */
398*0Sstevel@tonic-gate 	vnode_t **compvpp)		/* ret for ptr to component vnode */
399*0Sstevel@tonic-gate {
400*0Sstevel@tonic-gate 	struct pathname	pn;
401*0Sstevel@tonic-gate 	int		error;
402*0Sstevel@tonic-gate 
403*0Sstevel@tonic-gate 	ASSERT(devicesdir);		/* devfs must be initialized */
404*0Sstevel@tonic-gate 	ASSERT(pathname);		/* must have some path */
405*0Sstevel@tonic-gate 
406*0Sstevel@tonic-gate 	if (error = pn_get(pathname, UIO_SYSSPACE, &pn))
407*0Sstevel@tonic-gate 		return (error);
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate 	/* make the path relative to /devices. */
410*0Sstevel@tonic-gate 	pn_skipslash(&pn);
411*0Sstevel@tonic-gate 	if (pn_pathleft(&pn) == 0) {
412*0Sstevel@tonic-gate 		/* all we had was "\0" or "/" (which skipslash skiped) */
413*0Sstevel@tonic-gate 		if (dirvpp)
414*0Sstevel@tonic-gate 			*dirvpp = NULL;
415*0Sstevel@tonic-gate 		if (compvpp) {
416*0Sstevel@tonic-gate 			VN_HOLD(devicesdir);
417*0Sstevel@tonic-gate 			*compvpp = devicesdir;
418*0Sstevel@tonic-gate 		}
419*0Sstevel@tonic-gate 	} else {
420*0Sstevel@tonic-gate 		/*
421*0Sstevel@tonic-gate 		 * Use devfs lookup to resolve pathname to the vnode for
422*0Sstevel@tonic-gate 		 * the device via relative lookup in devfs. Extra holds for
423*0Sstevel@tonic-gate 		 * using devicesdir as directory we are searching and for
424*0Sstevel@tonic-gate 		 * being our root without being == rootdir.
425*0Sstevel@tonic-gate 		 */
426*0Sstevel@tonic-gate 		VN_HOLD(devicesdir);
427*0Sstevel@tonic-gate 		VN_HOLD(devicesdir);
428*0Sstevel@tonic-gate 		error = lookuppnvp(&pn, NULL, FOLLOW, dirvpp, compvpp,
429*0Sstevel@tonic-gate 		    devicesdir, devicesdir, kcred);
430*0Sstevel@tonic-gate 	}
431*0Sstevel@tonic-gate 	pn_free(&pn);
432*0Sstevel@tonic-gate 
433*0Sstevel@tonic-gate 	return (error);
434*0Sstevel@tonic-gate }
435*0Sstevel@tonic-gate 
436*0Sstevel@tonic-gate /*
437*0Sstevel@tonic-gate  * Given a devfs path (without the /devices prefix), walk
438*0Sstevel@tonic-gate  * the dv_node sub-tree rooted at the path.
439*0Sstevel@tonic-gate  */
440*0Sstevel@tonic-gate int
441*0Sstevel@tonic-gate devfs_walk(
442*0Sstevel@tonic-gate 	char		*path,
443*0Sstevel@tonic-gate 	void		(*callback)(struct dv_node *, void *),
444*0Sstevel@tonic-gate 	void		*arg)
445*0Sstevel@tonic-gate {
446*0Sstevel@tonic-gate 	char *dirpath, *devnm;
447*0Sstevel@tonic-gate 	struct vnode	*dirvp;
448*0Sstevel@tonic-gate 
449*0Sstevel@tonic-gate 	ASSERT(path && callback);
450*0Sstevel@tonic-gate 
451*0Sstevel@tonic-gate 	if (*path != '/' || devfs_mntinfo == NULL)
452*0Sstevel@tonic-gate 		return (ENXIO);
453*0Sstevel@tonic-gate 
454*0Sstevel@tonic-gate 	dcmn_err(("devfs_walk: path = %s", path));
455*0Sstevel@tonic-gate 
456*0Sstevel@tonic-gate 	dirpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
457*0Sstevel@tonic-gate 
458*0Sstevel@tonic-gate 	(void) snprintf(dirpath, MAXPATHLEN, "/devices%s", path);
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 	devnm = strrchr(dirpath, '/');
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate 	ASSERT(devnm);
463*0Sstevel@tonic-gate 
464*0Sstevel@tonic-gate 	*devnm++ = '\0';
465*0Sstevel@tonic-gate 
466*0Sstevel@tonic-gate 	if (lookupname(dirpath, UIO_SYSSPACE, 0, NULL, &dirvp)) {
467*0Sstevel@tonic-gate 		dcmn_err(("directory %s not found\n", dirpath));
468*0Sstevel@tonic-gate 		kmem_free(dirpath, MAXPATHLEN);
469*0Sstevel@tonic-gate 		return (ENXIO);
470*0Sstevel@tonic-gate 	}
471*0Sstevel@tonic-gate 
472*0Sstevel@tonic-gate 	/*
473*0Sstevel@tonic-gate 	 * if path == "/", visit the root dv_node
474*0Sstevel@tonic-gate 	 */
475*0Sstevel@tonic-gate 	if (*devnm == '\0') {
476*0Sstevel@tonic-gate 		callback(VTODV(dirvp), arg);
477*0Sstevel@tonic-gate 		devnm = NULL;
478*0Sstevel@tonic-gate 	}
479*0Sstevel@tonic-gate 
480*0Sstevel@tonic-gate 	dv_walk(VTODV(dirvp), devnm, callback, arg);
481*0Sstevel@tonic-gate 
482*0Sstevel@tonic-gate 	VN_RELE(dirvp);
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate 	kmem_free(dirpath, MAXPATHLEN);
485*0Sstevel@tonic-gate 
486*0Sstevel@tonic-gate 	return (0);
487*0Sstevel@tonic-gate }
488*0Sstevel@tonic-gate 
489*0Sstevel@tonic-gate int
490*0Sstevel@tonic-gate devfs_devpolicy(vnode_t *vp, devplcy_t **dpp)
491*0Sstevel@tonic-gate {
492*0Sstevel@tonic-gate 	struct vnode *rvp;
493*0Sstevel@tonic-gate 	struct dv_node *dvp;
494*0Sstevel@tonic-gate 	int rval = -1;
495*0Sstevel@tonic-gate 
496*0Sstevel@tonic-gate 	/* fail if devfs not mounted yet */
497*0Sstevel@tonic-gate 	if (devfs_mntinfo == NULL)
498*0Sstevel@tonic-gate 		return (rval);
499*0Sstevel@tonic-gate 
500*0Sstevel@tonic-gate 	if (VOP_REALVP(vp, &rvp) == 0 && vn_matchops(rvp, dv_vnodeops)) {
501*0Sstevel@tonic-gate 		dvp = VTODV(rvp);
502*0Sstevel@tonic-gate 		rw_enter(&dvp->dv_contents, RW_READER);
503*0Sstevel@tonic-gate 		if (dvp->dv_priv) {
504*0Sstevel@tonic-gate 			dphold(dvp->dv_priv);
505*0Sstevel@tonic-gate 			*dpp = dvp->dv_priv;
506*0Sstevel@tonic-gate 			rval = 0;
507*0Sstevel@tonic-gate 		}
508*0Sstevel@tonic-gate 		rw_exit(&dvp->dv_contents);
509*0Sstevel@tonic-gate 	}
510*0Sstevel@tonic-gate 	return (rval);
511*0Sstevel@tonic-gate }
512