1*2621Sllai1 /*
2*2621Sllai1  * CDDL HEADER START
3*2621Sllai1  *
4*2621Sllai1  * The contents of this file are subject to the terms of the
5*2621Sllai1  * Common Development and Distribution License (the "License").
6*2621Sllai1  * You may not use this file except in compliance with the License.
7*2621Sllai1  *
8*2621Sllai1  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*2621Sllai1  * or http://www.opensolaris.org/os/licensing.
10*2621Sllai1  * See the License for the specific language governing permissions
11*2621Sllai1  * and limitations under the License.
12*2621Sllai1  *
13*2621Sllai1  * When distributing Covered Code, include this CDDL HEADER in each
14*2621Sllai1  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*2621Sllai1  * If applicable, add the following below this CDDL HEADER, with the
16*2621Sllai1  * fields enclosed by brackets "[]" replaced with your own identifying
17*2621Sllai1  * information: Portions Copyright [yyyy] [name of copyright owner]
18*2621Sllai1  *
19*2621Sllai1  * CDDL HEADER END
20*2621Sllai1  */
21*2621Sllai1 /*
22*2621Sllai1  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*2621Sllai1  * Use is subject to license terms.
24*2621Sllai1  */
25*2621Sllai1 
26*2621Sllai1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*2621Sllai1 
28*2621Sllai1 /*
29*2621Sllai1  * utility routines for the /dev fs
30*2621Sllai1  */
31*2621Sllai1 
32*2621Sllai1 #include <sys/types.h>
33*2621Sllai1 #include <sys/param.h>
34*2621Sllai1 #include <sys/t_lock.h>
35*2621Sllai1 #include <sys/systm.h>
36*2621Sllai1 #include <sys/sysmacros.h>
37*2621Sllai1 #include <sys/user.h>
38*2621Sllai1 #include <sys/time.h>
39*2621Sllai1 #include <sys/vfs.h>
40*2621Sllai1 #include <sys/vnode.h>
41*2621Sllai1 #include <sys/file.h>
42*2621Sllai1 #include <sys/fcntl.h>
43*2621Sllai1 #include <sys/flock.h>
44*2621Sllai1 #include <sys/kmem.h>
45*2621Sllai1 #include <sys/uio.h>
46*2621Sllai1 #include <sys/errno.h>
47*2621Sllai1 #include <sys/stat.h>
48*2621Sllai1 #include <sys/cred.h>
49*2621Sllai1 #include <sys/dirent.h>
50*2621Sllai1 #include <sys/pathname.h>
51*2621Sllai1 #include <sys/cmn_err.h>
52*2621Sllai1 #include <sys/debug.h>
53*2621Sllai1 #include <sys/mode.h>
54*2621Sllai1 #include <sys/policy.h>
55*2621Sllai1 #include <fs/fs_subr.h>
56*2621Sllai1 #include <sys/mount.h>
57*2621Sllai1 #include <sys/fs/snode.h>
58*2621Sllai1 #include <sys/fs/dv_node.h>
59*2621Sllai1 #include <sys/fs/sdev_impl.h>
60*2621Sllai1 #include <sys/fs/sdev_node.h>
61*2621Sllai1 #include <sys/sunndi.h>
62*2621Sllai1 #include <sys/sunmdi.h>
63*2621Sllai1 #include <sys/conf.h>
64*2621Sllai1 #include <sys/proc.h>
65*2621Sllai1 #include <sys/user.h>
66*2621Sllai1 #include <sys/modctl.h>
67*2621Sllai1 
68*2621Sllai1 #ifdef DEBUG
69*2621Sllai1 int sdev_debug = 0x00000001;
70*2621Sllai1 int sdev_debug_cache_flags = 0;
71*2621Sllai1 #endif
72*2621Sllai1 
73*2621Sllai1 /*
74*2621Sllai1  * globals
75*2621Sllai1  */
76*2621Sllai1 /* prototype memory vattrs */
77*2621Sllai1 vattr_t sdev_vattr_dir = {
78*2621Sllai1 	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
79*2621Sllai1 	VDIR,					/* va_type */
80*2621Sllai1 	SDEV_DIRMODE_DEFAULT,			/* va_mode */
81*2621Sllai1 	SDEV_UID_DEFAULT,			/* va_uid */
82*2621Sllai1 	SDEV_GID_DEFAULT,			/* va_gid */
83*2621Sllai1 	0,					/* va_fsid */
84*2621Sllai1 	0,					/* va_nodeid */
85*2621Sllai1 	0,					/* va_nlink */
86*2621Sllai1 	0,					/* va_size */
87*2621Sllai1 	0,					/* va_atime */
88*2621Sllai1 	0,					/* va_mtime */
89*2621Sllai1 	0,					/* va_ctime */
90*2621Sllai1 	0,					/* va_rdev */
91*2621Sllai1 	0,					/* va_blksize */
92*2621Sllai1 	0,					/* va_nblocks */
93*2621Sllai1 	0					/* va_vcode */
94*2621Sllai1 };
95*2621Sllai1 
96*2621Sllai1 vattr_t sdev_vattr_lnk = {
97*2621Sllai1 	AT_TYPE|AT_MODE,			/* va_mask */
98*2621Sllai1 	VLNK,					/* va_type */
99*2621Sllai1 	SDEV_LNKMODE_DEFAULT,			/* va_mode */
100*2621Sllai1 	SDEV_UID_DEFAULT,			/* va_uid */
101*2621Sllai1 	SDEV_GID_DEFAULT,			/* va_gid */
102*2621Sllai1 	0,					/* va_fsid */
103*2621Sllai1 	0,					/* va_nodeid */
104*2621Sllai1 	0,					/* va_nlink */
105*2621Sllai1 	0,					/* va_size */
106*2621Sllai1 	0,					/* va_atime */
107*2621Sllai1 	0,					/* va_mtime */
108*2621Sllai1 	0,					/* va_ctime */
109*2621Sllai1 	0,					/* va_rdev */
110*2621Sllai1 	0,					/* va_blksize */
111*2621Sllai1 	0,					/* va_nblocks */
112*2621Sllai1 	0					/* va_vcode */
113*2621Sllai1 };
114*2621Sllai1 
115*2621Sllai1 vattr_t sdev_vattr_blk = {
116*2621Sllai1 	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
117*2621Sllai1 	VBLK,					/* va_type */
118*2621Sllai1 	S_IFBLK | SDEV_DEVMODE_DEFAULT,		/* va_mode */
119*2621Sllai1 	SDEV_UID_DEFAULT,			/* va_uid */
120*2621Sllai1 	SDEV_GID_DEFAULT,			/* va_gid */
121*2621Sllai1 	0,					/* va_fsid */
122*2621Sllai1 	0,					/* va_nodeid */
123*2621Sllai1 	0,					/* va_nlink */
124*2621Sllai1 	0,					/* va_size */
125*2621Sllai1 	0,					/* va_atime */
126*2621Sllai1 	0,					/* va_mtime */
127*2621Sllai1 	0,					/* va_ctime */
128*2621Sllai1 	0,					/* va_rdev */
129*2621Sllai1 	0,					/* va_blksize */
130*2621Sllai1 	0,					/* va_nblocks */
131*2621Sllai1 	0					/* va_vcode */
132*2621Sllai1 };
133*2621Sllai1 
134*2621Sllai1 vattr_t sdev_vattr_chr = {
135*2621Sllai1 	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
136*2621Sllai1 	VCHR,					/* va_type */
137*2621Sllai1 	S_IFCHR | SDEV_DEVMODE_DEFAULT,		/* va_mode */
138*2621Sllai1 	SDEV_UID_DEFAULT,			/* va_uid */
139*2621Sllai1 	SDEV_GID_DEFAULT,			/* va_gid */
140*2621Sllai1 	0,					/* va_fsid */
141*2621Sllai1 	0,					/* va_nodeid */
142*2621Sllai1 	0,					/* va_nlink */
143*2621Sllai1 	0,					/* va_size */
144*2621Sllai1 	0,					/* va_atime */
145*2621Sllai1 	0,					/* va_mtime */
146*2621Sllai1 	0,					/* va_ctime */
147*2621Sllai1 	0,					/* va_rdev */
148*2621Sllai1 	0,					/* va_blksize */
149*2621Sllai1 	0,					/* va_nblocks */
150*2621Sllai1 	0					/* va_vcode */
151*2621Sllai1 };
152*2621Sllai1 
153*2621Sllai1 kmem_cache_t	*sdev_node_cache;	/* sdev_node cache */
154*2621Sllai1 int		devtype;		/* fstype */
155*2621Sllai1 
156*2621Sllai1 struct devname_ops *devname_ns_ops;	/* default name service directory ops */
157*2621Sllai1 kmutex_t devname_nsmaps_lock;	/* protect devname_nsmaps */
158*2621Sllai1 
159*2621Sllai1 /* static */
160*2621Sllai1 static struct devname_nsmap *devname_nsmaps = NULL;
161*2621Sllai1 				/* contents from /etc/dev/devname_master */
162*2621Sllai1 static int devname_nsmaps_invalidated = 0; /* "devfsadm -m" has run */
163*2621Sllai1 
164*2621Sllai1 static struct vnodeops *sdev_get_vop(struct sdev_node *);
165*2621Sllai1 static void sdev_set_no_nocache(struct sdev_node *);
166*2621Sllai1 static int sdev_get_moduleops(struct sdev_node *);
167*2621Sllai1 static void sdev_handle_alloc(struct sdev_node *);
168*2621Sllai1 static fs_operation_def_t *sdev_merge_vtab(const fs_operation_def_t []);
169*2621Sllai1 static void sdev_free_vtab(fs_operation_def_t *);
170*2621Sllai1 
171*2621Sllai1 static void
172*2621Sllai1 sdev_prof_free(struct sdev_node *dv)
173*2621Sllai1 {
174*2621Sllai1 	ASSERT(!SDEV_IS_GLOBAL(dv));
175*2621Sllai1 	if (dv->sdev_prof.dev_name)
176*2621Sllai1 		nvlist_free(dv->sdev_prof.dev_name);
177*2621Sllai1 	if (dv->sdev_prof.dev_map)
178*2621Sllai1 		nvlist_free(dv->sdev_prof.dev_map);
179*2621Sllai1 	if (dv->sdev_prof.dev_symlink)
180*2621Sllai1 		nvlist_free(dv->sdev_prof.dev_symlink);
181*2621Sllai1 	if (dv->sdev_prof.dev_glob_incdir)
182*2621Sllai1 		nvlist_free(dv->sdev_prof.dev_glob_incdir);
183*2621Sllai1 	if (dv->sdev_prof.dev_glob_excdir)
184*2621Sllai1 		nvlist_free(dv->sdev_prof.dev_glob_excdir);
185*2621Sllai1 	bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
186*2621Sllai1 }
187*2621Sllai1 
188*2621Sllai1 /*
189*2621Sllai1  * sdev_node cache constructor
190*2621Sllai1  */
191*2621Sllai1 /*ARGSUSED1*/
192*2621Sllai1 static int
193*2621Sllai1 i_sdev_node_ctor(void *buf, void *cfarg, int flag)
194*2621Sllai1 {
195*2621Sllai1 	struct sdev_node *dv = (struct sdev_node *)buf;
196*2621Sllai1 	struct vnode *vp;
197*2621Sllai1 
198*2621Sllai1 	ASSERT(flag == KM_SLEEP);
199*2621Sllai1 
200*2621Sllai1 	bzero(buf, sizeof (struct sdev_node));
201*2621Sllai1 	rw_init(&dv->sdev_contents, NULL, RW_DEFAULT, NULL);
202*2621Sllai1 	dv->sdev_vnode = vn_alloc(KM_SLEEP);
203*2621Sllai1 	vp = SDEVTOV(dv);
204*2621Sllai1 	vp->v_data = (caddr_t)dv;
205*2621Sllai1 	return (0);
206*2621Sllai1 }
207*2621Sllai1 
208*2621Sllai1 /* sdev_node destructor for kmem cache */
209*2621Sllai1 /*ARGSUSED1*/
210*2621Sllai1 static void
211*2621Sllai1 i_sdev_node_dtor(void *buf, void *arg)
212*2621Sllai1 {
213*2621Sllai1 	struct sdev_node *dv = (struct sdev_node *)buf;
214*2621Sllai1 	struct vnode *vp = SDEVTOV(dv);
215*2621Sllai1 
216*2621Sllai1 	rw_destroy(&dv->sdev_contents);
217*2621Sllai1 	vn_free(vp);
218*2621Sllai1 }
219*2621Sllai1 
220*2621Sllai1 /* initialize sdev_node cache */
221*2621Sllai1 void
222*2621Sllai1 sdev_node_cache_init()
223*2621Sllai1 {
224*2621Sllai1 	int flags = 0;
225*2621Sllai1 
226*2621Sllai1 #ifdef	DEBUG
227*2621Sllai1 	flags = sdev_debug_cache_flags;
228*2621Sllai1 	if (flags)
229*2621Sllai1 		sdcmn_err(("cache debug flags 0x%x\n", flags));
230*2621Sllai1 #endif	/* DEBUG */
231*2621Sllai1 
232*2621Sllai1 	ASSERT(sdev_node_cache == NULL);
233*2621Sllai1 	sdev_node_cache = kmem_cache_create("sdev_node_cache",
234*2621Sllai1 	    sizeof (struct sdev_node), 0, i_sdev_node_ctor, i_sdev_node_dtor,
235*2621Sllai1 	    NULL, NULL, NULL, flags);
236*2621Sllai1 }
237*2621Sllai1 
238*2621Sllai1 /* destroy sdev_node cache */
239*2621Sllai1 void
240*2621Sllai1 sdev_node_cache_fini()
241*2621Sllai1 {
242*2621Sllai1 	ASSERT(sdev_node_cache != NULL);
243*2621Sllai1 	kmem_cache_destroy(sdev_node_cache);
244*2621Sllai1 	sdev_node_cache = NULL;
245*2621Sllai1 }
246*2621Sllai1 
247*2621Sllai1 void
248*2621Sllai1 sdev_set_nodestate(struct sdev_node *dv, sdev_node_state_t state)
249*2621Sllai1 {
250*2621Sllai1 	ASSERT(dv);
251*2621Sllai1 	ASSERT(RW_WRITE_HELD(&dv->sdev_contents));
252*2621Sllai1 	dv->sdev_state = state;
253*2621Sllai1 }
254*2621Sllai1 
255*2621Sllai1 static void
256*2621Sllai1 sdev_attrinit(struct sdev_node *dv, vattr_t *vap)
257*2621Sllai1 {
258*2621Sllai1 	timestruc_t now;
259*2621Sllai1 
260*2621Sllai1 	ASSERT(vap);
261*2621Sllai1 
262*2621Sllai1 	dv->sdev_attr = kmem_zalloc(sizeof (struct vattr), KM_SLEEP);
263*2621Sllai1 	*dv->sdev_attr = *vap;
264*2621Sllai1 
265*2621Sllai1 	dv->sdev_attr->va_mode = MAKEIMODE(vap->va_type, vap->va_mode);
266*2621Sllai1 
267*2621Sllai1 	gethrestime(&now);
268*2621Sllai1 	dv->sdev_attr->va_atime = now;
269*2621Sllai1 	dv->sdev_attr->va_mtime = now;
270*2621Sllai1 	dv->sdev_attr->va_ctime = now;
271*2621Sllai1 }
272*2621Sllai1 
273*2621Sllai1 /* alloc and initialize a sdev_node */
274*2621Sllai1 int
275*2621Sllai1 sdev_nodeinit(struct sdev_node *ddv, char *nm, struct sdev_node **newdv,
276*2621Sllai1     vattr_t *vap)
277*2621Sllai1 {
278*2621Sllai1 	struct sdev_node *dv = NULL;
279*2621Sllai1 	struct vnode *vp;
280*2621Sllai1 	size_t nmlen, len;
281*2621Sllai1 	devname_handle_t  *dhl;
282*2621Sllai1 
283*2621Sllai1 	nmlen = strlen(nm) + 1;
284*2621Sllai1 	if (nmlen > MAXNAMELEN) {
285*2621Sllai1 		sdcmn_err9(("sdev_nodeinit: node name %s"
286*2621Sllai1 		    " too long\n", nm));
287*2621Sllai1 		*newdv = NULL;
288*2621Sllai1 		return (ENAMETOOLONG);
289*2621Sllai1 	}
290*2621Sllai1 
291*2621Sllai1 	dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP);
292*2621Sllai1 
293*2621Sllai1 	dv->sdev_name = kmem_alloc(nmlen, KM_SLEEP);
294*2621Sllai1 	bcopy(nm, dv->sdev_name, nmlen);
295*2621Sllai1 	dv->sdev_namelen = nmlen - 1;	/* '\0' not included */
296*2621Sllai1 	len = strlen(ddv->sdev_path) + strlen(nm) + 2;
297*2621Sllai1 	dv->sdev_path = kmem_alloc(len, KM_SLEEP);
298*2621Sllai1 	(void) snprintf(dv->sdev_path, len, "%s/%s", ddv->sdev_path, nm);
299*2621Sllai1 	/* overwritten for VLNK nodes */
300*2621Sllai1 	dv->sdev_symlink = NULL;
301*2621Sllai1 
302*2621Sllai1 	vp = SDEVTOV(dv);
303*2621Sllai1 	vn_reinit(vp);
304*2621Sllai1 	vp->v_vfsp = SDEVTOV(ddv)->v_vfsp;
305*2621Sllai1 	if (vap)
306*2621Sllai1 		vp->v_type = vap->va_type;
307*2621Sllai1 
308*2621Sllai1 	/*
309*2621Sllai1 	 * initialized to the parent's vnodeops.
310*2621Sllai1 	 * maybe overwriten for a VDIR
311*2621Sllai1 	 */
312*2621Sllai1 	vn_setops(vp, vn_getops(SDEVTOV(ddv)));
313*2621Sllai1 	vn_exists(vp);
314*2621Sllai1 
315*2621Sllai1 	dv->sdev_dotdot = NULL;
316*2621Sllai1 	dv->sdev_dot = NULL;
317*2621Sllai1 	dv->sdev_next = NULL;
318*2621Sllai1 	dv->sdev_attrvp = NULL;
319*2621Sllai1 	if (vap) {
320*2621Sllai1 		sdev_attrinit(dv, vap);
321*2621Sllai1 	} else {
322*2621Sllai1 		dv->sdev_attr = NULL;
323*2621Sllai1 	}
324*2621Sllai1 
325*2621Sllai1 	dv->sdev_ino = sdev_mkino(dv);
326*2621Sllai1 	dv->sdev_nlink = 0;		/* updated on insert */
327*2621Sllai1 	dv->sdev_flags = ddv->sdev_flags; /* inherit from the parent first */
328*2621Sllai1 	dv->sdev_flags |= SDEV_BUILD;
329*2621Sllai1 	mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL);
330*2621Sllai1 	cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL);
331*2621Sllai1 	if (SDEV_IS_GLOBAL(ddv)) {
332*2621Sllai1 		dv->sdev_flags |= SDEV_GLOBAL;
333*2621Sllai1 		dv->sdev_mapinfo = NULL;
334*2621Sllai1 		dhl = &(dv->sdev_handle);
335*2621Sllai1 		dhl->dh_data = dv;
336*2621Sllai1 		dhl->dh_spec = DEVNAME_NS_NONE;
337*2621Sllai1 		dhl->dh_args = NULL;
338*2621Sllai1 		sdev_set_no_nocache(dv);
339*2621Sllai1 		dv->sdev_gdir_gen = 0;
340*2621Sllai1 	} else {
341*2621Sllai1 		dv->sdev_flags &= ~SDEV_GLOBAL;
342*2621Sllai1 		dv->sdev_origin = NULL; /* set later */
343*2621Sllai1 		bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
344*2621Sllai1 		dv->sdev_ldir_gen = 0;
345*2621Sllai1 		dv->sdev_devtree_gen = 0;
346*2621Sllai1 	}
347*2621Sllai1 
348*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
349*2621Sllai1 	sdev_set_nodestate(dv, SDEV_INIT);
350*2621Sllai1 	rw_exit(&dv->sdev_contents);
351*2621Sllai1 	*newdv = dv;
352*2621Sllai1 
353*2621Sllai1 	return (0);
354*2621Sllai1 }
355*2621Sllai1 
356*2621Sllai1 /*
357*2621Sllai1  * transition a sdev_node into SDEV_READY state
358*2621Sllai1  */
359*2621Sllai1 int
360*2621Sllai1 sdev_nodeready(struct sdev_node *dv, struct vattr *vap, struct vnode *avp,
361*2621Sllai1     void *args, struct cred *cred)
362*2621Sllai1 {
363*2621Sllai1 	int error = 0;
364*2621Sllai1 	struct vnode *vp = SDEVTOV(dv);
365*2621Sllai1 	vtype_t type;
366*2621Sllai1 
367*2621Sllai1 	ASSERT(dv && (dv->sdev_state != SDEV_READY) && vap);
368*2621Sllai1 
369*2621Sllai1 	type = vap->va_type;
370*2621Sllai1 	vp->v_type = type;
371*2621Sllai1 	vp->v_rdev = vap->va_rdev;
372*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
373*2621Sllai1 	if (type == VDIR) {
374*2621Sllai1 		dv->sdev_nlink = 2;
375*2621Sllai1 		dv->sdev_flags &= ~SDEV_PERSIST;
376*2621Sllai1 		dv->sdev_flags &= ~SDEV_DYNAMIC;
377*2621Sllai1 		vn_setops(vp, sdev_get_vop(dv)); /* from internal vtab */
378*2621Sllai1 		error = sdev_get_moduleops(dv); /* from plug-in module */
379*2621Sllai1 		ASSERT(dv->sdev_dotdot);
380*2621Sllai1 		ASSERT(SDEVTOV(dv->sdev_dotdot)->v_type == VDIR);
381*2621Sllai1 		vp->v_rdev = SDEVTOV(dv->sdev_dotdot)->v_rdev;
382*2621Sllai1 	} else if (type == VLNK) {
383*2621Sllai1 		ASSERT(args);
384*2621Sllai1 		dv->sdev_nlink = 1;
385*2621Sllai1 		dv->sdev_symlink = i_ddi_strdup((char *)args, KM_SLEEP);
386*2621Sllai1 	} else {
387*2621Sllai1 		dv->sdev_nlink = 1;
388*2621Sllai1 	}
389*2621Sllai1 
390*2621Sllai1 	if (!(SDEV_IS_GLOBAL(dv))) {
391*2621Sllai1 		dv->sdev_origin = (struct sdev_node *)args;
392*2621Sllai1 		dv->sdev_flags &= ~SDEV_PERSIST;
393*2621Sllai1 	}
394*2621Sllai1 
395*2621Sllai1 	/*
396*2621Sllai1 	 * shadow node is created here OR
397*2621Sllai1 	 * if failed (indicated by dv->sdev_attrvp == NULL),
398*2621Sllai1 	 * created later in sdev_setattr
399*2621Sllai1 	 */
400*2621Sllai1 	if (avp) {
401*2621Sllai1 		dv->sdev_attrvp = avp;
402*2621Sllai1 	} else {
403*2621Sllai1 		if (dv->sdev_attr == NULL)
404*2621Sllai1 			sdev_attrinit(dv, vap);
405*2621Sllai1 		else
406*2621Sllai1 			*dv->sdev_attr = *vap;
407*2621Sllai1 
408*2621Sllai1 		if ((SDEV_IS_PERSIST(dv) && (dv->sdev_attrvp == NULL)) ||
409*2621Sllai1 		    ((SDEVTOV(dv)->v_type == VDIR) &&
410*2621Sllai1 		    (dv->sdev_attrvp == NULL)))
411*2621Sllai1 			error = sdev_shadow_node(dv, cred);
412*2621Sllai1 	}
413*2621Sllai1 
414*2621Sllai1 	/* transition to READY state */
415*2621Sllai1 	sdev_set_nodestate(dv, SDEV_READY);
416*2621Sllai1 	sdev_nc_node_exists(dv);
417*2621Sllai1 	rw_exit(&dv->sdev_contents);
418*2621Sllai1 	return (error);
419*2621Sllai1 }
420*2621Sllai1 
421*2621Sllai1 /*
422*2621Sllai1  * setting ZOMBIE state
423*2621Sllai1  */
424*2621Sllai1 static int
425*2621Sllai1 sdev_nodezombied(struct sdev_node *dv)
426*2621Sllai1 {
427*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
428*2621Sllai1 	sdev_set_nodestate(dv, SDEV_ZOMBIE);
429*2621Sllai1 	rw_exit(&dv->sdev_contents);
430*2621Sllai1 	return (0);
431*2621Sllai1 }
432*2621Sllai1 
433*2621Sllai1 /*
434*2621Sllai1  * Build the VROOT sdev_node.
435*2621Sllai1  */
436*2621Sllai1 /*ARGSUSED*/
437*2621Sllai1 struct sdev_node *
438*2621Sllai1 sdev_mkroot(struct vfs *vfsp, dev_t devdev, struct vnode *mvp,
439*2621Sllai1     struct vnode *avp, struct cred *cred)
440*2621Sllai1 {
441*2621Sllai1 	struct sdev_node *dv;
442*2621Sllai1 	struct vnode *vp;
443*2621Sllai1 	char devdir[] = "/dev";
444*2621Sllai1 
445*2621Sllai1 	ASSERT(sdev_node_cache != NULL);
446*2621Sllai1 	ASSERT(avp);
447*2621Sllai1 	dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP);
448*2621Sllai1 	vp = SDEVTOV(dv);
449*2621Sllai1 	vn_reinit(vp);
450*2621Sllai1 	vp->v_flag |= VROOT;
451*2621Sllai1 	vp->v_vfsp = vfsp;
452*2621Sllai1 	vp->v_type = VDIR;
453*2621Sllai1 	vp->v_rdev = devdev;
454*2621Sllai1 	vn_setops(vp, sdev_vnodeops); /* apply the default vnodeops at /dev */
455*2621Sllai1 	vn_exists(vp);
456*2621Sllai1 
457*2621Sllai1 	if (vfsp->vfs_mntpt)
458*2621Sllai1 		dv->sdev_name = i_ddi_strdup(
459*2621Sllai1 		    (char *)refstr_value(vfsp->vfs_mntpt), KM_SLEEP);
460*2621Sllai1 	else
461*2621Sllai1 		/* vfs_mountdev1 set mount point later */
462*2621Sllai1 		dv->sdev_name = i_ddi_strdup("/dev", KM_SLEEP);
463*2621Sllai1 	dv->sdev_namelen = strlen(dv->sdev_name); /* '\0' not included */
464*2621Sllai1 	dv->sdev_path = i_ddi_strdup(devdir, KM_SLEEP);
465*2621Sllai1 	dv->sdev_ino = SDEV_ROOTINO;
466*2621Sllai1 	dv->sdev_nlink = 2;		/* name + . (no sdev_insert) */
467*2621Sllai1 	dv->sdev_dotdot = dv;		/* .. == self */
468*2621Sllai1 	dv->sdev_attrvp = avp;
469*2621Sllai1 	dv->sdev_attr = NULL;
470*2621Sllai1 	mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL);
471*2621Sllai1 	cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL);
472*2621Sllai1 	if (strcmp(dv->sdev_name, "/dev") == 0) {
473*2621Sllai1 		mutex_init(&devname_nsmaps_lock, NULL, MUTEX_DEFAULT, NULL);
474*2621Sllai1 		dv->sdev_mapinfo = NULL;
475*2621Sllai1 		dv->sdev_flags = SDEV_BUILD|SDEV_GLOBAL|SDEV_PERSIST;
476*2621Sllai1 		bzero(&dv->sdev_handle, sizeof (dv->sdev_handle));
477*2621Sllai1 		dv->sdev_gdir_gen = 0;
478*2621Sllai1 	} else {
479*2621Sllai1 		dv->sdev_flags = SDEV_BUILD;
480*2621Sllai1 		dv->sdev_flags &= ~SDEV_PERSIST;
481*2621Sllai1 		bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
482*2621Sllai1 		dv->sdev_ldir_gen = 0;
483*2621Sllai1 		dv->sdev_devtree_gen = 0;
484*2621Sllai1 	}
485*2621Sllai1 
486*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
487*2621Sllai1 	sdev_set_nodestate(dv, SDEV_READY);
488*2621Sllai1 	rw_exit(&dv->sdev_contents);
489*2621Sllai1 	sdev_nc_node_exists(dv);
490*2621Sllai1 	return (dv);
491*2621Sllai1 }
492*2621Sllai1 
493*2621Sllai1 /*
494*2621Sllai1  *  1. load the module
495*2621Sllai1  *  2. modload invokes sdev_module_register, which in turn sets
496*2621Sllai1  *     the dv->sdev_mapinfo->dir_ops
497*2621Sllai1  *
498*2621Sllai1  * note: locking order:
499*2621Sllai1  *	dv->sdev_contents -> map->dir_lock
500*2621Sllai1  */
501*2621Sllai1 static int
502*2621Sllai1 sdev_get_moduleops(struct sdev_node *dv)
503*2621Sllai1 {
504*2621Sllai1 	int error = 0;
505*2621Sllai1 	struct devname_nsmap *map = NULL;
506*2621Sllai1 	char *module;
507*2621Sllai1 	char *path;
508*2621Sllai1 	int load = 1;
509*2621Sllai1 
510*2621Sllai1 	ASSERT(SDEVTOV(dv)->v_type == VDIR);
511*2621Sllai1 
512*2621Sllai1 	if (devname_nsmaps == NULL)
513*2621Sllai1 		return (0);
514*2621Sllai1 
515*2621Sllai1 	if (!sdev_nsmaps_loaded() && !sdev_nsmaps_reloaded())
516*2621Sllai1 		return (0);
517*2621Sllai1 
518*2621Sllai1 
519*2621Sllai1 	path = dv->sdev_path;
520*2621Sllai1 	if ((map = sdev_get_nsmap_by_dir(path, 0))) {
521*2621Sllai1 		rw_enter(&map->dir_lock, RW_READER);
522*2621Sllai1 		if (map->dir_invalid) {
523*2621Sllai1 			if (map->dir_module && map->dir_newmodule &&
524*2621Sllai1 			    (strcmp(map->dir_module,
525*2621Sllai1 					map->dir_newmodule) == 0)) {
526*2621Sllai1 				load = 0;
527*2621Sllai1 			}
528*2621Sllai1 			sdev_replace_nsmap(map, map->dir_newmodule,
529*2621Sllai1 			    map->dir_newmap);
530*2621Sllai1 		}
531*2621Sllai1 
532*2621Sllai1 		module = map->dir_module;
533*2621Sllai1 		if (module && load) {
534*2621Sllai1 			sdcmn_err6(("sdev_get_moduleops: "
535*2621Sllai1 			    "load module %s", module));
536*2621Sllai1 			rw_exit(&map->dir_lock);
537*2621Sllai1 			error = modload("devname", module);
538*2621Sllai1 			sdcmn_err6(("sdev_get_moduleops: error %d\n", error));
539*2621Sllai1 			if (error < 0) {
540*2621Sllai1 				return (-1);
541*2621Sllai1 			}
542*2621Sllai1 		} else if (module == NULL) {
543*2621Sllai1 			/*
544*2621Sllai1 			 * loading the module ops for name services
545*2621Sllai1 			 */
546*2621Sllai1 			if (devname_ns_ops == NULL) {
547*2621Sllai1 				sdcmn_err6((
548*2621Sllai1 				    "sdev_get_moduleops: modload default\n"));
549*2621Sllai1 				error = modload("devname", DEVNAME_NSCONFIG);
550*2621Sllai1 				sdcmn_err6((
551*2621Sllai1 				    "sdev_get_moduleops: error %d\n", error));
552*2621Sllai1 				if (error < 0) {
553*2621Sllai1 					return (-1);
554*2621Sllai1 				}
555*2621Sllai1 			}
556*2621Sllai1 
557*2621Sllai1 			if (!rw_tryupgrade(&map->dir_lock)) {
558*2621Sllai1 				rw_exit(&map->dir_lock);
559*2621Sllai1 				rw_enter(&map->dir_lock, RW_WRITER);
560*2621Sllai1 			}
561*2621Sllai1 			ASSERT(devname_ns_ops);
562*2621Sllai1 			map->dir_ops = devname_ns_ops;
563*2621Sllai1 			rw_exit(&map->dir_lock);
564*2621Sllai1 		}
565*2621Sllai1 	}
566*2621Sllai1 
567*2621Sllai1 	dv->sdev_mapinfo = map;
568*2621Sllai1 	return (0);
569*2621Sllai1 }
570*2621Sllai1 
571*2621Sllai1 /* directory dependent vop table */
572*2621Sllai1 struct sdev_vop_table {
573*2621Sllai1 	char *vt_name;				/* subdirectory name */
574*2621Sllai1 	const fs_operation_def_t *vt_service;	/* vnodeops table */
575*2621Sllai1 	struct vnodeops *vt_vops;		/* constructed vop */
576*2621Sllai1 	struct vnodeops **vt_global_vops;	/* global container for vop */
577*2621Sllai1 	int (*vt_vtor)(struct sdev_node *);	/* validate sdev_node */
578*2621Sllai1 	int vt_flags;
579*2621Sllai1 };
580*2621Sllai1 
581*2621Sllai1 /*
582*2621Sllai1  * A nice improvement would be to provide a plug-in mechanism
583*2621Sllai1  * for this table instead of a const table.
584*2621Sllai1  */
585*2621Sllai1 static struct sdev_vop_table vtab[] =
586*2621Sllai1 {
587*2621Sllai1 	{ "pts", devpts_vnodeops_tbl, NULL, &devpts_vnodeops, devpts_validate,
588*2621Sllai1 	SDEV_DYNAMIC | SDEV_VTOR },
589*2621Sllai1 
590*2621Sllai1 	{ "zcons", NULL, NULL, NULL, NULL, SDEV_NO_NCACHE },
591*2621Sllai1 
592*2621Sllai1 	{ NULL, NULL, NULL, NULL, NULL, 0}
593*2621Sllai1 };
594*2621Sllai1 
595*2621Sllai1 
596*2621Sllai1 /*
597*2621Sllai1  *  sets a directory's vnodeops if the directory is in the vtab;
598*2621Sllai1  */
599*2621Sllai1 static struct vnodeops *
600*2621Sllai1 sdev_get_vop(struct sdev_node *dv)
601*2621Sllai1 {
602*2621Sllai1 	int i;
603*2621Sllai1 	char *path;
604*2621Sllai1 
605*2621Sllai1 	path = dv->sdev_path;
606*2621Sllai1 	ASSERT(path);
607*2621Sllai1 
608*2621Sllai1 	/* gets the relative path to /dev/ */
609*2621Sllai1 	path += 5;
610*2621Sllai1 
611*2621Sllai1 	/* gets the vtab entry if matches */
612*2621Sllai1 	for (i = 0; vtab[i].vt_name; i++) {
613*2621Sllai1 		if (strcmp(vtab[i].vt_name, path) != 0)
614*2621Sllai1 			continue;
615*2621Sllai1 		dv->sdev_flags |= vtab[i].vt_flags;
616*2621Sllai1 
617*2621Sllai1 		if (vtab[i].vt_vops) {
618*2621Sllai1 			if (vtab[i].vt_global_vops)
619*2621Sllai1 				*(vtab[i].vt_global_vops) = vtab[i].vt_vops;
620*2621Sllai1 			return (vtab[i].vt_vops);
621*2621Sllai1 		}
622*2621Sllai1 
623*2621Sllai1 		if (vtab[i].vt_service) {
624*2621Sllai1 			fs_operation_def_t *templ;
625*2621Sllai1 			templ = sdev_merge_vtab(vtab[i].vt_service);
626*2621Sllai1 			if (vn_make_ops(vtab[i].vt_name,
627*2621Sllai1 			    (const fs_operation_def_t *)templ,
628*2621Sllai1 			    &vtab[i].vt_vops) != 0) {
629*2621Sllai1 				cmn_err(CE_PANIC, "%s: malformed vnode ops\n",
630*2621Sllai1 				    vtab[i].vt_name);
631*2621Sllai1 				/*NOTREACHED*/
632*2621Sllai1 			}
633*2621Sllai1 			if (vtab[i].vt_global_vops) {
634*2621Sllai1 				*(vtab[i].vt_global_vops) = vtab[i].vt_vops;
635*2621Sllai1 			}
636*2621Sllai1 			sdev_free_vtab(templ);
637*2621Sllai1 			return (vtab[i].vt_vops);
638*2621Sllai1 		}
639*2621Sllai1 		return (sdev_vnodeops);
640*2621Sllai1 	}
641*2621Sllai1 
642*2621Sllai1 	/* child inherits the persistence of the parent */
643*2621Sllai1 	if (SDEV_IS_PERSIST(dv->sdev_dotdot))
644*2621Sllai1 		dv->sdev_flags |= SDEV_PERSIST;
645*2621Sllai1 
646*2621Sllai1 	return (sdev_vnodeops);
647*2621Sllai1 }
648*2621Sllai1 
649*2621Sllai1 static void
650*2621Sllai1 sdev_set_no_nocache(struct sdev_node *dv)
651*2621Sllai1 {
652*2621Sllai1 	int i;
653*2621Sllai1 	char *path;
654*2621Sllai1 
655*2621Sllai1 	ASSERT(dv->sdev_path);
656*2621Sllai1 	path = dv->sdev_path + strlen("/dev/");
657*2621Sllai1 
658*2621Sllai1 	for (i = 0; vtab[i].vt_name; i++) {
659*2621Sllai1 		if (strcmp(vtab[i].vt_name, path) == 0) {
660*2621Sllai1 			if (vtab[i].vt_flags & SDEV_NO_NCACHE)
661*2621Sllai1 				dv->sdev_flags |= SDEV_NO_NCACHE;
662*2621Sllai1 			break;
663*2621Sllai1 		}
664*2621Sllai1 	}
665*2621Sllai1 }
666*2621Sllai1 
667*2621Sllai1 void *
668*2621Sllai1 sdev_get_vtor(struct sdev_node *dv)
669*2621Sllai1 {
670*2621Sllai1 	int i;
671*2621Sllai1 
672*2621Sllai1 	for (i = 0; vtab[i].vt_name; i++) {
673*2621Sllai1 		if (strcmp(vtab[i].vt_name, dv->sdev_name) != 0)
674*2621Sllai1 			continue;
675*2621Sllai1 		return ((void *)vtab[i].vt_vtor);
676*2621Sllai1 	}
677*2621Sllai1 	return (NULL);
678*2621Sllai1 }
679*2621Sllai1 
680*2621Sllai1 /*
681*2621Sllai1  * Build the base root inode
682*2621Sllai1  */
683*2621Sllai1 ino_t
684*2621Sllai1 sdev_mkino(struct sdev_node *dv)
685*2621Sllai1 {
686*2621Sllai1 	ino_t	ino;
687*2621Sllai1 
688*2621Sllai1 	/*
689*2621Sllai1 	 * for now, follow the lead of tmpfs here
690*2621Sllai1 	 * need to someday understand the requirements here
691*2621Sllai1 	 */
692*2621Sllai1 	ino = (ino_t)(uint32_t)((uintptr_t)dv >> 3);
693*2621Sllai1 	ino += SDEV_ROOTINO + 1;
694*2621Sllai1 
695*2621Sllai1 	return (ino);
696*2621Sllai1 }
697*2621Sllai1 
698*2621Sllai1 static int
699*2621Sllai1 sdev_getlink(struct vnode *linkvp, char **link)
700*2621Sllai1 {
701*2621Sllai1 	int err;
702*2621Sllai1 	char *buf;
703*2621Sllai1 	struct uio uio = {0};
704*2621Sllai1 	struct iovec iov = {0};
705*2621Sllai1 
706*2621Sllai1 	if (linkvp == NULL)
707*2621Sllai1 		return (ENOENT);
708*2621Sllai1 	ASSERT(linkvp->v_type == VLNK);
709*2621Sllai1 
710*2621Sllai1 	buf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
711*2621Sllai1 	iov.iov_base = buf;
712*2621Sllai1 	iov.iov_len = MAXPATHLEN;
713*2621Sllai1 	uio.uio_iov = &iov;
714*2621Sllai1 	uio.uio_iovcnt = 1;
715*2621Sllai1 	uio.uio_resid = MAXPATHLEN;
716*2621Sllai1 	uio.uio_segflg = UIO_SYSSPACE;
717*2621Sllai1 	uio.uio_llimit = MAXOFFSET_T;
718*2621Sllai1 
719*2621Sllai1 	err = VOP_READLINK(linkvp, &uio, kcred);
720*2621Sllai1 	if (err) {
721*2621Sllai1 		cmn_err(CE_WARN, "readlink %s failed in dev\n", buf);
722*2621Sllai1 		kmem_free(buf, MAXPATHLEN);
723*2621Sllai1 		return (ENOENT);
724*2621Sllai1 	}
725*2621Sllai1 
726*2621Sllai1 	/* mission complete */
727*2621Sllai1 	*link = i_ddi_strdup(buf, KM_SLEEP);
728*2621Sllai1 	kmem_free(buf, MAXPATHLEN);
729*2621Sllai1 	return (0);
730*2621Sllai1 }
731*2621Sllai1 
732*2621Sllai1 /*
733*2621Sllai1  * A convenient wrapper to get the devfs node vnode for a device
734*2621Sllai1  * minor functionality: readlink() of a /dev symlink
735*2621Sllai1  * Place the link into dv->sdev_symlink
736*2621Sllai1  */
737*2621Sllai1 static int
738*2621Sllai1 sdev_follow_link(struct sdev_node *dv)
739*2621Sllai1 {
740*2621Sllai1 	int err;
741*2621Sllai1 	struct vnode *linkvp;
742*2621Sllai1 	char *link = NULL;
743*2621Sllai1 
744*2621Sllai1 	linkvp = SDEVTOV(dv);
745*2621Sllai1 	if (linkvp == NULL)
746*2621Sllai1 		return (ENOENT);
747*2621Sllai1 	ASSERT(linkvp->v_type == VLNK);
748*2621Sllai1 	err = sdev_getlink(linkvp, &link);
749*2621Sllai1 	if (err) {
750*2621Sllai1 		(void) sdev_nodezombied(dv);
751*2621Sllai1 		dv->sdev_symlink = NULL;
752*2621Sllai1 		return (ENOENT);
753*2621Sllai1 	}
754*2621Sllai1 
755*2621Sllai1 	ASSERT(link != NULL);
756*2621Sllai1 	dv->sdev_symlink = link;
757*2621Sllai1 	return (0);
758*2621Sllai1 }
759*2621Sllai1 
760*2621Sllai1 static int
761*2621Sllai1 sdev_node_check(struct sdev_node *dv, struct vattr *nvap, void *nargs)
762*2621Sllai1 {
763*2621Sllai1 	vtype_t otype = SDEVTOV(dv)->v_type;
764*2621Sllai1 
765*2621Sllai1 	/*
766*2621Sllai1 	 * existing sdev_node has a different type.
767*2621Sllai1 	 */
768*2621Sllai1 	if (otype != nvap->va_type) {
769*2621Sllai1 		sdcmn_err9(("sdev_node_check: existing node "
770*2621Sllai1 		    "  %s type %d does not match new node type %d\n",
771*2621Sllai1 		    dv->sdev_name, otype, nvap->va_type));
772*2621Sllai1 		return (EEXIST);
773*2621Sllai1 	}
774*2621Sllai1 
775*2621Sllai1 	/*
776*2621Sllai1 	 * For a symlink, the target should be the same.
777*2621Sllai1 	 */
778*2621Sllai1 	if (otype == VLNK) {
779*2621Sllai1 		ASSERT(nargs != NULL);
780*2621Sllai1 		ASSERT(dv->sdev_symlink != NULL);
781*2621Sllai1 		if (strcmp(dv->sdev_symlink, (char *)nargs) != 0) {
782*2621Sllai1 			sdcmn_err9(("sdev_node_check: existing node "
783*2621Sllai1 			    " %s has different symlink %s as new node "
784*2621Sllai1 			    " %s\n", dv->sdev_name, dv->sdev_symlink,
785*2621Sllai1 			    (char *)nargs));
786*2621Sllai1 			return (EEXIST);
787*2621Sllai1 		}
788*2621Sllai1 	}
789*2621Sllai1 
790*2621Sllai1 	return (0);
791*2621Sllai1 }
792*2621Sllai1 
793*2621Sllai1 /*
794*2621Sllai1  * sdev_mknode - a wrapper for sdev_nodeinit(), sdev_nodeready()
795*2621Sllai1  *
796*2621Sllai1  * arguments:
797*2621Sllai1  *	- ddv (parent)
798*2621Sllai1  *	- nm (child name)
799*2621Sllai1  *	- newdv (sdev_node for nm is returned here)
800*2621Sllai1  *	- vap (vattr for the node to be created, va_type should be set.
801*2621Sllai1  *	  the defaults should be used if unknown)
802*2621Sllai1  *	- cred
803*2621Sllai1  *	- args
804*2621Sllai1  *	    . tnm (for VLNK)
805*2621Sllai1  *	    . global sdev_node (for !SDEV_GLOBAL)
806*2621Sllai1  * 	- state: SDEV_INIT, SDEV_READY
807*2621Sllai1  *
808*2621Sllai1  * only ddv, nm, newddv, vap, cred are required for sdev_mknode(SDEV_INIT)
809*2621Sllai1  *
810*2621Sllai1  * NOTE:  directory contents writers lock needs to be held before
811*2621Sllai1  *	  calling this routine.
812*2621Sllai1  */
813*2621Sllai1 int
814*2621Sllai1 sdev_mknode(struct sdev_node *ddv, char *nm, struct sdev_node **newdv,
815*2621Sllai1     struct vattr *vap, struct vnode *avp, void *args, struct cred *cred,
816*2621Sllai1     sdev_node_state_t state)
817*2621Sllai1 {
818*2621Sllai1 	int error = 0;
819*2621Sllai1 	sdev_node_state_t node_state;
820*2621Sllai1 	struct sdev_node *dv = NULL;
821*2621Sllai1 
822*2621Sllai1 	ASSERT(state != SDEV_ZOMBIE);
823*2621Sllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
824*2621Sllai1 
825*2621Sllai1 	if (*newdv) {
826*2621Sllai1 		dv = *newdv;
827*2621Sllai1 	} else {
828*2621Sllai1 		/* allocate and initialize a sdev_node */
829*2621Sllai1 		if (ddv->sdev_state == SDEV_ZOMBIE) {
830*2621Sllai1 			sdcmn_err9(("sdev_mknode: parent %s ZOMBIEd\n",
831*2621Sllai1 			    ddv->sdev_path));
832*2621Sllai1 			return (ENOENT);
833*2621Sllai1 		}
834*2621Sllai1 
835*2621Sllai1 		error = sdev_nodeinit(ddv, nm, &dv, vap);
836*2621Sllai1 		if (error != 0) {
837*2621Sllai1 			sdcmn_err9(("sdev_mknode: error %d,"
838*2621Sllai1 			    " name %s can not be initialized\n",
839*2621Sllai1 			    error, nm));
840*2621Sllai1 			return (ENOENT);
841*2621Sllai1 		}
842*2621Sllai1 		ASSERT(dv);
843*2621Sllai1 
844*2621Sllai1 		/* insert into the directory cache */
845*2621Sllai1 		error = sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_ADD);
846*2621Sllai1 		if (error) {
847*2621Sllai1 			sdcmn_err9(("sdev_mknode: node %s can not"
848*2621Sllai1 			    " be added into directory cache\n", nm));
849*2621Sllai1 			return (ENOENT);
850*2621Sllai1 		}
851*2621Sllai1 	}
852*2621Sllai1 
853*2621Sllai1 	ASSERT(dv);
854*2621Sllai1 	node_state = dv->sdev_state;
855*2621Sllai1 	ASSERT(node_state != SDEV_ZOMBIE);
856*2621Sllai1 
857*2621Sllai1 	if (state == SDEV_READY) {
858*2621Sllai1 		switch (node_state) {
859*2621Sllai1 		case SDEV_INIT:
860*2621Sllai1 			error = sdev_nodeready(dv, vap, avp, args, cred);
861*2621Sllai1 			/*
862*2621Sllai1 			 * masking the errors with ENOENT
863*2621Sllai1 			 */
864*2621Sllai1 			if (error) {
865*2621Sllai1 				sdcmn_err9(("sdev_mknode: node %s can NOT"
866*2621Sllai1 				    " be transitioned into READY state, "
867*2621Sllai1 				    "error %d\n", nm, error));
868*2621Sllai1 				error = ENOENT;
869*2621Sllai1 			}
870*2621Sllai1 			break;
871*2621Sllai1 		case SDEV_READY:
872*2621Sllai1 			/*
873*2621Sllai1 			 * Do some sanity checking to make sure
874*2621Sllai1 			 * the existing sdev_node is what has been
875*2621Sllai1 			 * asked for.
876*2621Sllai1 			 */
877*2621Sllai1 			error = sdev_node_check(dv, vap, args);
878*2621Sllai1 			break;
879*2621Sllai1 		default:
880*2621Sllai1 			break;
881*2621Sllai1 		}
882*2621Sllai1 	}
883*2621Sllai1 
884*2621Sllai1 	if (!error) {
885*2621Sllai1 		*newdv = dv;
886*2621Sllai1 		ASSERT((*newdv)->sdev_state != SDEV_ZOMBIE);
887*2621Sllai1 	} else {
888*2621Sllai1 		SDEV_SIMPLE_RELE(dv);
889*2621Sllai1 		*newdv = NULL;
890*2621Sllai1 	}
891*2621Sllai1 
892*2621Sllai1 	return (error);
893*2621Sllai1 }
894*2621Sllai1 
895*2621Sllai1 /*
896*2621Sllai1  * convenient wrapper to change vp's ATIME, CTIME and ATIME
897*2621Sllai1  */
898*2621Sllai1 void
899*2621Sllai1 sdev_update_timestamps(struct vnode *vp, cred_t *cred, uint_t mask)
900*2621Sllai1 {
901*2621Sllai1 	struct vattr attr;
902*2621Sllai1 	timestruc_t now;
903*2621Sllai1 	int err;
904*2621Sllai1 
905*2621Sllai1 	ASSERT(vp);
906*2621Sllai1 	gethrestime(&now);
907*2621Sllai1 	if (mask & AT_CTIME)
908*2621Sllai1 		attr.va_ctime = now;
909*2621Sllai1 	if (mask & AT_MTIME)
910*2621Sllai1 		attr.va_mtime = now;
911*2621Sllai1 	if (mask & AT_ATIME)
912*2621Sllai1 		attr.va_atime = now;
913*2621Sllai1 
914*2621Sllai1 	attr.va_mask = (mask & AT_TIMES);
915*2621Sllai1 	err = VOP_SETATTR(vp, &attr, 0, cred, NULL);
916*2621Sllai1 	if (err && (err != EROFS)) {
917*2621Sllai1 		sdcmn_err(("update timestamps error %d\n", err));
918*2621Sllai1 	}
919*2621Sllai1 }
920*2621Sllai1 
921*2621Sllai1 /*
922*2621Sllai1  * the backing store vnode is released here
923*2621Sllai1  */
924*2621Sllai1 /*ARGSUSED1*/
925*2621Sllai1 void
926*2621Sllai1 sdev_nodedestroy(struct sdev_node *dv, uint_t flags)
927*2621Sllai1 {
928*2621Sllai1 	/* no references */
929*2621Sllai1 	ASSERT(dv->sdev_nlink == 0);
930*2621Sllai1 
931*2621Sllai1 	if (dv->sdev_attrvp != NULLVP) {
932*2621Sllai1 		VN_RELE(dv->sdev_attrvp);
933*2621Sllai1 		/*
934*2621Sllai1 		 * reset the attrvp so that no more
935*2621Sllai1 		 * references can be made on this already
936*2621Sllai1 		 * vn_rele() vnode
937*2621Sllai1 		 */
938*2621Sllai1 		dv->sdev_attrvp = NULLVP;
939*2621Sllai1 	}
940*2621Sllai1 
941*2621Sllai1 	if (dv->sdev_attr != NULL) {
942*2621Sllai1 		kmem_free(dv->sdev_attr, sizeof (struct vattr));
943*2621Sllai1 		dv->sdev_attr = NULL;
944*2621Sllai1 	}
945*2621Sllai1 
946*2621Sllai1 	if (dv->sdev_name != NULL) {
947*2621Sllai1 		kmem_free(dv->sdev_name, dv->sdev_namelen + 1);
948*2621Sllai1 		dv->sdev_name = NULL;
949*2621Sllai1 	}
950*2621Sllai1 
951*2621Sllai1 	if (dv->sdev_symlink != NULL) {
952*2621Sllai1 		kmem_free(dv->sdev_symlink, strlen(dv->sdev_symlink) + 1);
953*2621Sllai1 		dv->sdev_symlink = NULL;
954*2621Sllai1 	}
955*2621Sllai1 
956*2621Sllai1 	if (dv->sdev_path) {
957*2621Sllai1 		kmem_free(dv->sdev_path, strlen(dv->sdev_path) + 1);
958*2621Sllai1 		dv->sdev_path = NULL;
959*2621Sllai1 	}
960*2621Sllai1 
961*2621Sllai1 	if (!SDEV_IS_GLOBAL(dv))
962*2621Sllai1 		sdev_prof_free(dv);
963*2621Sllai1 
964*2621Sllai1 	mutex_destroy(&dv->sdev_lookup_lock);
965*2621Sllai1 	cv_destroy(&dv->sdev_lookup_cv);
966*2621Sllai1 
967*2621Sllai1 	/* return node to initial state as per constructor */
968*2621Sllai1 	(void) memset((void *)&dv->sdev_instance_data, 0,
969*2621Sllai1 	    sizeof (dv->sdev_instance_data));
970*2621Sllai1 
971*2621Sllai1 	vn_invalid(SDEVTOV(dv));
972*2621Sllai1 	kmem_cache_free(sdev_node_cache, dv);
973*2621Sllai1 }
974*2621Sllai1 
975*2621Sllai1 /*
976*2621Sllai1  * DIRECTORY CACHE lookup
977*2621Sllai1  */
978*2621Sllai1 struct sdev_node *
979*2621Sllai1 sdev_findbyname(struct sdev_node *ddv, char *nm)
980*2621Sllai1 {
981*2621Sllai1 	struct sdev_node *dv;
982*2621Sllai1 	size_t	nmlen = strlen(nm);
983*2621Sllai1 
984*2621Sllai1 	ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
985*2621Sllai1 	for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next) {
986*2621Sllai1 		if (dv->sdev_namelen != nmlen) {
987*2621Sllai1 			continue;
988*2621Sllai1 		}
989*2621Sllai1 
990*2621Sllai1 		/*
991*2621Sllai1 		 * Can't lookup stale nodes
992*2621Sllai1 		 */
993*2621Sllai1 		if (dv->sdev_flags & SDEV_STALE) {
994*2621Sllai1 			sdcmn_err9((
995*2621Sllai1 			    "sdev_findbyname: skipped stale node: %s\n",
996*2621Sllai1 			    dv->sdev_name));
997*2621Sllai1 			continue;
998*2621Sllai1 		}
999*2621Sllai1 
1000*2621Sllai1 		if (strcmp(dv->sdev_name, nm) == 0) {
1001*2621Sllai1 			SDEV_HOLD(dv);
1002*2621Sllai1 			return (dv);
1003*2621Sllai1 		}
1004*2621Sllai1 	}
1005*2621Sllai1 	return (NULL);
1006*2621Sllai1 }
1007*2621Sllai1 
1008*2621Sllai1 /*
1009*2621Sllai1  * Inserts a new sdev_node in a parent directory
1010*2621Sllai1  */
1011*2621Sllai1 void
1012*2621Sllai1 sdev_direnter(struct sdev_node *ddv, struct sdev_node *dv)
1013*2621Sllai1 {
1014*2621Sllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1015*2621Sllai1 	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
1016*2621Sllai1 	ASSERT(ddv->sdev_nlink >= 2);
1017*2621Sllai1 	ASSERT(dv->sdev_nlink == 0);
1018*2621Sllai1 
1019*2621Sllai1 	dv->sdev_dotdot = ddv;
1020*2621Sllai1 	dv->sdev_next = ddv->sdev_dot;
1021*2621Sllai1 	ddv->sdev_dot = dv;
1022*2621Sllai1 	ddv->sdev_nlink++;
1023*2621Sllai1 }
1024*2621Sllai1 
1025*2621Sllai1 /*
1026*2621Sllai1  * The following check is needed because while sdev_nodes are linked
1027*2621Sllai1  * in SDEV_INIT state, they have their link counts incremented only
1028*2621Sllai1  * in SDEV_READY state.
1029*2621Sllai1  */
1030*2621Sllai1 static void
1031*2621Sllai1 decr_link(struct sdev_node *dv)
1032*2621Sllai1 {
1033*2621Sllai1 	if (dv->sdev_state != SDEV_INIT)
1034*2621Sllai1 		dv->sdev_nlink--;
1035*2621Sllai1 	else
1036*2621Sllai1 		ASSERT(dv->sdev_nlink == 0);
1037*2621Sllai1 }
1038*2621Sllai1 
1039*2621Sllai1 /*
1040*2621Sllai1  * Delete an existing dv from directory cache
1041*2621Sllai1  *
1042*2621Sllai1  * In the case of a node is still held by non-zero reference count,
1043*2621Sllai1  *     the node is put into ZOMBIE state. Once the reference count
1044*2621Sllai1  *     reaches "0", the node is unlinked and destroyed,
1045*2621Sllai1  *     in sdev_inactive().
1046*2621Sllai1  */
1047*2621Sllai1 static int
1048*2621Sllai1 sdev_dirdelete(struct sdev_node *ddv, struct sdev_node *dv)
1049*2621Sllai1 {
1050*2621Sllai1 	struct sdev_node *idv;
1051*2621Sllai1 	struct sdev_node *prev = NULL;
1052*2621Sllai1 	struct vnode *vp;
1053*2621Sllai1 
1054*2621Sllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1055*2621Sllai1 
1056*2621Sllai1 	vp = SDEVTOV(dv);
1057*2621Sllai1 	mutex_enter(&vp->v_lock);
1058*2621Sllai1 
1059*2621Sllai1 	/* dv is held still */
1060*2621Sllai1 	if (vp->v_count > 1) {
1061*2621Sllai1 		rw_enter(&dv->sdev_contents, RW_WRITER);
1062*2621Sllai1 		if (dv->sdev_state == SDEV_READY) {
1063*2621Sllai1 			sdcmn_err9((
1064*2621Sllai1 			    "sdev_delete: node %s busy with count %d\n",
1065*2621Sllai1 			    dv->sdev_name, vp->v_count));
1066*2621Sllai1 			dv->sdev_state = SDEV_ZOMBIE;
1067*2621Sllai1 		}
1068*2621Sllai1 		rw_exit(&dv->sdev_contents);
1069*2621Sllai1 		--vp->v_count;
1070*2621Sllai1 		mutex_exit(&vp->v_lock);
1071*2621Sllai1 		return (EBUSY);
1072*2621Sllai1 	}
1073*2621Sllai1 	ASSERT(vp->v_count == 1);
1074*2621Sllai1 
1075*2621Sllai1 	/* unlink from the memory cache */
1076*2621Sllai1 	ddv->sdev_nlink--;	/* .. to above */
1077*2621Sllai1 	if (vp->v_type == VDIR) {
1078*2621Sllai1 		decr_link(dv);		/* . to self */
1079*2621Sllai1 	}
1080*2621Sllai1 
1081*2621Sllai1 	for (idv = ddv->sdev_dot; idv && idv != dv;
1082*2621Sllai1 	    prev = idv, idv = idv->sdev_next)
1083*2621Sllai1 		;
1084*2621Sllai1 	ASSERT(idv == dv);	/* node to be deleted must exist */
1085*2621Sllai1 	if (prev == NULL)
1086*2621Sllai1 		ddv->sdev_dot = dv->sdev_next;
1087*2621Sllai1 	else
1088*2621Sllai1 		prev->sdev_next = dv->sdev_next;
1089*2621Sllai1 	dv->sdev_next = NULL;
1090*2621Sllai1 	decr_link(dv);	/* name, back to zero */
1091*2621Sllai1 	vp->v_count--;
1092*2621Sllai1 	mutex_exit(&vp->v_lock);
1093*2621Sllai1 
1094*2621Sllai1 	/* destroy the node */
1095*2621Sllai1 	sdev_nodedestroy(dv, 0);
1096*2621Sllai1 	return (0);
1097*2621Sllai1 }
1098*2621Sllai1 
1099*2621Sllai1 /*
1100*2621Sllai1  * check if the source is in the path of the target
1101*2621Sllai1  *
1102*2621Sllai1  * source and target are different
1103*2621Sllai1  */
1104*2621Sllai1 /*ARGSUSED2*/
1105*2621Sllai1 static int
1106*2621Sllai1 sdev_checkpath(struct sdev_node *sdv, struct sdev_node *tdv, struct cred *cred)
1107*2621Sllai1 {
1108*2621Sllai1 	int error = 0;
1109*2621Sllai1 	struct sdev_node *dotdot, *dir;
1110*2621Sllai1 
1111*2621Sllai1 	rw_enter(&tdv->sdev_contents, RW_READER);
1112*2621Sllai1 	dotdot = tdv->sdev_dotdot;
1113*2621Sllai1 	ASSERT(dotdot);
1114*2621Sllai1 
1115*2621Sllai1 	/* fs root */
1116*2621Sllai1 	if (dotdot == tdv) {
1117*2621Sllai1 		rw_exit(&tdv->sdev_contents);
1118*2621Sllai1 		return (0);
1119*2621Sllai1 	}
1120*2621Sllai1 
1121*2621Sllai1 	for (;;) {
1122*2621Sllai1 		/*
1123*2621Sllai1 		 * avoid error cases like
1124*2621Sllai1 		 *	mv a a/b
1125*2621Sllai1 		 *	mv a a/b/c
1126*2621Sllai1 		 *	etc.
1127*2621Sllai1 		 */
1128*2621Sllai1 		if (dotdot == sdv) {
1129*2621Sllai1 			error = EINVAL;
1130*2621Sllai1 			break;
1131*2621Sllai1 		}
1132*2621Sllai1 
1133*2621Sllai1 		dir = dotdot;
1134*2621Sllai1 		dotdot = dir->sdev_dotdot;
1135*2621Sllai1 
1136*2621Sllai1 		/* done checking because root is reached */
1137*2621Sllai1 		if (dir == dotdot) {
1138*2621Sllai1 			break;
1139*2621Sllai1 		}
1140*2621Sllai1 	}
1141*2621Sllai1 	rw_exit(&tdv->sdev_contents);
1142*2621Sllai1 	return (error);
1143*2621Sllai1 }
1144*2621Sllai1 
1145*2621Sllai1 /*
1146*2621Sllai1  * Renaming a directory to a different parent
1147*2621Sllai1  * requires modifying the ".." reference.
1148*2621Sllai1  */
1149*2621Sllai1 static void
1150*2621Sllai1 sdev_fixdotdot(struct sdev_node *dv, struct sdev_node *oparent,
1151*2621Sllai1     struct sdev_node *nparent)
1152*2621Sllai1 {
1153*2621Sllai1 	ASSERT(SDEVTOV(dv)->v_type == VDIR);
1154*2621Sllai1 	ASSERT(nparent);
1155*2621Sllai1 	ASSERT(oparent);
1156*2621Sllai1 
1157*2621Sllai1 	rw_enter(&nparent->sdev_contents, RW_WRITER);
1158*2621Sllai1 	nparent->sdev_nlink++;
1159*2621Sllai1 	ASSERT(dv->sdev_dotdot == oparent);
1160*2621Sllai1 	dv->sdev_dotdot = nparent;
1161*2621Sllai1 	rw_exit(&nparent->sdev_contents);
1162*2621Sllai1 
1163*2621Sllai1 	rw_enter(&oparent->sdev_contents, RW_WRITER);
1164*2621Sllai1 	oparent->sdev_nlink--;
1165*2621Sllai1 	rw_exit(&oparent->sdev_contents);
1166*2621Sllai1 }
1167*2621Sllai1 
1168*2621Sllai1 int
1169*2621Sllai1 sdev_rnmnode(struct sdev_node *oddv, struct sdev_node *odv,
1170*2621Sllai1     struct sdev_node *nddv, struct sdev_node **ndvp, char *nnm,
1171*2621Sllai1     struct cred *cred)
1172*2621Sllai1 {
1173*2621Sllai1 	int error = 0;
1174*2621Sllai1 	struct vnode *ovp = SDEVTOV(odv);
1175*2621Sllai1 	struct vnode *nvp;
1176*2621Sllai1 	struct vattr vattr;
1177*2621Sllai1 	int doingdir = (ovp->v_type == VDIR);
1178*2621Sllai1 	char *link = NULL;
1179*2621Sllai1 
1180*2621Sllai1 	/*
1181*2621Sllai1 	 * If renaming a directory, and the parents are different (".." must be
1182*2621Sllai1 	 * changed) then the source dir must not be in the dir hierarchy above
1183*2621Sllai1 	 * the target since it would orphan everything below the source dir.
1184*2621Sllai1 	 */
1185*2621Sllai1 	if (doingdir && (oddv != nddv)) {
1186*2621Sllai1 		error = sdev_checkpath(odv, nddv, cred);
1187*2621Sllai1 		if (error)
1188*2621Sllai1 			return (error);
1189*2621Sllai1 	}
1190*2621Sllai1 
1191*2621Sllai1 	vattr.va_mask = AT_MODE|AT_UID|AT_GID;
1192*2621Sllai1 	error = VOP_GETATTR(ovp, &vattr, 0, cred);
1193*2621Sllai1 	if (error)
1194*2621Sllai1 		return (error);
1195*2621Sllai1 
1196*2621Sllai1 	if (*ndvp) {
1197*2621Sllai1 		/* destination existing */
1198*2621Sllai1 		nvp = SDEVTOV(*ndvp);
1199*2621Sllai1 		ASSERT(nvp);
1200*2621Sllai1 
1201*2621Sllai1 		/* handling renaming to itself */
1202*2621Sllai1 		if (odv == *ndvp)
1203*2621Sllai1 			return (0);
1204*2621Sllai1 
1205*2621Sllai1 		/* special handling directory renaming */
1206*2621Sllai1 		if (doingdir) {
1207*2621Sllai1 			if (nvp->v_type != VDIR)
1208*2621Sllai1 				return (ENOTDIR);
1209*2621Sllai1 
1210*2621Sllai1 			/*
1211*2621Sllai1 			 * Renaming a directory with the parent different
1212*2621Sllai1 			 * requires that ".." be re-written.
1213*2621Sllai1 			 */
1214*2621Sllai1 			if (oddv != nddv) {
1215*2621Sllai1 				sdev_fixdotdot(*ndvp, oddv, nddv);
1216*2621Sllai1 			}
1217*2621Sllai1 		}
1218*2621Sllai1 	} else {
1219*2621Sllai1 		/* creating the destination node with the source attr */
1220*2621Sllai1 		rw_enter(&nddv->sdev_contents, RW_WRITER);
1221*2621Sllai1 		error = sdev_mknode(nddv, nnm, ndvp, &vattr, NULL, NULL,
1222*2621Sllai1 		    cred, SDEV_INIT);
1223*2621Sllai1 		rw_exit(&nddv->sdev_contents);
1224*2621Sllai1 		if (error)
1225*2621Sllai1 			return (error);
1226*2621Sllai1 
1227*2621Sllai1 		ASSERT(*ndvp);
1228*2621Sllai1 		nvp = SDEVTOV(*ndvp);
1229*2621Sllai1 	}
1230*2621Sllai1 
1231*2621Sllai1 	/* fix the source for a symlink */
1232*2621Sllai1 	if (vattr.va_type == VLNK) {
1233*2621Sllai1 		if (odv->sdev_symlink == NULL) {
1234*2621Sllai1 			error = sdev_follow_link(odv);
1235*2621Sllai1 			if (error)
1236*2621Sllai1 				return (ENOENT);
1237*2621Sllai1 		}
1238*2621Sllai1 		ASSERT(odv->sdev_symlink);
1239*2621Sllai1 		link = i_ddi_strdup(odv->sdev_symlink, KM_SLEEP);
1240*2621Sllai1 	}
1241*2621Sllai1 
1242*2621Sllai1 	rw_enter(&nddv->sdev_contents, RW_WRITER);
1243*2621Sllai1 	error = sdev_mknode(nddv, nnm, ndvp, &vattr, NULL, (void *)link,
1244*2621Sllai1 	    cred, SDEV_READY);
1245*2621Sllai1 	rw_exit(&nddv->sdev_contents);
1246*2621Sllai1 
1247*2621Sllai1 	if (link)
1248*2621Sllai1 		kmem_free(link, strlen(link) + 1);
1249*2621Sllai1 
1250*2621Sllai1 	/* update timestamps */
1251*2621Sllai1 	sdev_update_timestamps(nvp, kcred, AT_CTIME|AT_ATIME);
1252*2621Sllai1 	sdev_update_timestamps(SDEVTOV(nddv), kcred, AT_MTIME|AT_ATIME);
1253*2621Sllai1 	SDEV_RELE(*ndvp);
1254*2621Sllai1 	return (0);
1255*2621Sllai1 }
1256*2621Sllai1 
1257*2621Sllai1 /*
1258*2621Sllai1  * Merge sdev_node specific information into an attribute structure.
1259*2621Sllai1  *
1260*2621Sllai1  * note: sdev_node is not locked here
1261*2621Sllai1  */
1262*2621Sllai1 void
1263*2621Sllai1 sdev_vattr_merge(struct sdev_node *dv, struct vattr *vap)
1264*2621Sllai1 {
1265*2621Sllai1 	struct vnode *vp = SDEVTOV(dv);
1266*2621Sllai1 
1267*2621Sllai1 	vap->va_nlink = dv->sdev_nlink;
1268*2621Sllai1 	vap->va_nodeid = dv->sdev_ino;
1269*2621Sllai1 	vap->va_fsid = SDEVTOV(dv->sdev_dotdot)->v_rdev;
1270*2621Sllai1 	vap->va_type = vp->v_type;
1271*2621Sllai1 
1272*2621Sllai1 	if (vp->v_type == VDIR) {
1273*2621Sllai1 		vap->va_rdev = 0;
1274*2621Sllai1 		vap->va_fsid = vp->v_rdev;
1275*2621Sllai1 	} else if (vp->v_type == VLNK) {
1276*2621Sllai1 		vap->va_rdev = 0;
1277*2621Sllai1 		vap->va_mode  &= ~S_IFMT;
1278*2621Sllai1 		vap->va_mode |= S_IFLNK;
1279*2621Sllai1 	} else if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
1280*2621Sllai1 		vap->va_rdev = vp->v_rdev;
1281*2621Sllai1 		vap->va_mode &= ~S_IFMT;
1282*2621Sllai1 		if (vap->va_type == VCHR)
1283*2621Sllai1 			vap->va_mode |= S_IFCHR;
1284*2621Sllai1 		else
1285*2621Sllai1 			vap->va_mode |= S_IFBLK;
1286*2621Sllai1 	} else {
1287*2621Sllai1 		vap->va_rdev = 0;
1288*2621Sllai1 	}
1289*2621Sllai1 }
1290*2621Sllai1 
1291*2621Sllai1 static struct vattr *
1292*2621Sllai1 sdev_getdefault_attr(enum vtype type)
1293*2621Sllai1 {
1294*2621Sllai1 	if (type == VDIR)
1295*2621Sllai1 		return (&sdev_vattr_dir);
1296*2621Sllai1 	else if (type == VCHR)
1297*2621Sllai1 		return (&sdev_vattr_chr);
1298*2621Sllai1 	else if (type == VBLK)
1299*2621Sllai1 		return (&sdev_vattr_blk);
1300*2621Sllai1 	else if (type == VLNK)
1301*2621Sllai1 		return (&sdev_vattr_lnk);
1302*2621Sllai1 	else
1303*2621Sllai1 		return (NULL);
1304*2621Sllai1 }
1305*2621Sllai1 int
1306*2621Sllai1 sdev_to_vp(struct sdev_node *dv, struct vnode **vpp)
1307*2621Sllai1 {
1308*2621Sllai1 	int rv = 0;
1309*2621Sllai1 	struct vnode *vp = SDEVTOV(dv);
1310*2621Sllai1 
1311*2621Sllai1 	switch (vp->v_type) {
1312*2621Sllai1 	case VCHR:
1313*2621Sllai1 	case VBLK:
1314*2621Sllai1 		/*
1315*2621Sllai1 		 * If vnode is a device, return special vnode instead
1316*2621Sllai1 		 * (though it knows all about -us- via sp->s_realvp)
1317*2621Sllai1 		 */
1318*2621Sllai1 		*vpp = specvp(vp, vp->v_rdev, vp->v_type, kcred);
1319*2621Sllai1 		VN_RELE(vp);
1320*2621Sllai1 		if (*vpp == NULLVP)
1321*2621Sllai1 			rv = ENOSYS;
1322*2621Sllai1 		break;
1323*2621Sllai1 	default:	/* most types are returned as is */
1324*2621Sllai1 		*vpp = vp;
1325*2621Sllai1 		break;
1326*2621Sllai1 	}
1327*2621Sllai1 	return (rv);
1328*2621Sllai1 }
1329*2621Sllai1 
1330*2621Sllai1 /*
1331*2621Sllai1  * loopback into sdev_lookup()
1332*2621Sllai1  */
1333*2621Sllai1 static struct vnode *
1334*2621Sllai1 devname_find_by_devpath(char *devpath, struct vattr *vattr)
1335*2621Sllai1 {
1336*2621Sllai1 	int error = 0;
1337*2621Sllai1 	struct vnode *vp;
1338*2621Sllai1 
1339*2621Sllai1 	error = lookupname(devpath, UIO_SYSSPACE, NO_FOLLOW, NULLVPP, &vp);
1340*2621Sllai1 	if (error) {
1341*2621Sllai1 		return (NULL);
1342*2621Sllai1 	}
1343*2621Sllai1 
1344*2621Sllai1 	if (vattr)
1345*2621Sllai1 		(void) VOP_GETATTR(vp, vattr, 0, kcred);
1346*2621Sllai1 	return (vp);
1347*2621Sllai1 }
1348*2621Sllai1 
1349*2621Sllai1 /*
1350*2621Sllai1  * the junction between devname and devfs
1351*2621Sllai1  */
1352*2621Sllai1 static struct vnode *
1353*2621Sllai1 devname_configure_by_path(char *physpath, struct vattr *vattr)
1354*2621Sllai1 {
1355*2621Sllai1 	int error = 0;
1356*2621Sllai1 	struct vnode *vp;
1357*2621Sllai1 
1358*2621Sllai1 	ASSERT(strncmp(physpath, "/devices/", sizeof ("/devices/" - 1))
1359*2621Sllai1 	    == 0);
1360*2621Sllai1 
1361*2621Sllai1 	error = devfs_lookupname(physpath + sizeof ("/devices/") - 1,
1362*2621Sllai1 	    NULLVPP, &vp);
1363*2621Sllai1 	if (error != 0) {
1364*2621Sllai1 		if (error == ENODEV) {
1365*2621Sllai1 			cmn_err(CE_CONT, "%s: not found (line %d)\n",
1366*2621Sllai1 			    physpath, __LINE__);
1367*2621Sllai1 		}
1368*2621Sllai1 
1369*2621Sllai1 		return (NULL);
1370*2621Sllai1 	}
1371*2621Sllai1 
1372*2621Sllai1 	if (vattr)
1373*2621Sllai1 		(void) VOP_GETATTR(vp, vattr, 0, kcred);
1374*2621Sllai1 	return (vp);
1375*2621Sllai1 }
1376*2621Sllai1 
1377*2621Sllai1 /*
1378*2621Sllai1  * junction between devname and root file system, e.g. ufs
1379*2621Sllai1  */
1380*2621Sllai1 int
1381*2621Sllai1 devname_backstore_lookup(struct sdev_node *ddv, char *nm, struct vnode **rvp)
1382*2621Sllai1 {
1383*2621Sllai1 	struct vnode *rdvp = ddv->sdev_attrvp;
1384*2621Sllai1 	int rval = 0;
1385*2621Sllai1 
1386*2621Sllai1 	ASSERT(rdvp);
1387*2621Sllai1 
1388*2621Sllai1 	rval = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, kcred);
1389*2621Sllai1 	return (rval);
1390*2621Sllai1 }
1391*2621Sllai1 
1392*2621Sllai1 static int
1393*2621Sllai1 sdev_filldir_from_store(struct sdev_node *ddv, int dlen, struct cred *cred)
1394*2621Sllai1 {
1395*2621Sllai1 	struct sdev_node *dv = NULL;
1396*2621Sllai1 	char	*nm;
1397*2621Sllai1 	struct vnode *dirvp;
1398*2621Sllai1 	int	error;
1399*2621Sllai1 	vnode_t	*vp;
1400*2621Sllai1 	int eof;
1401*2621Sllai1 	struct iovec iov;
1402*2621Sllai1 	struct uio uio;
1403*2621Sllai1 	struct dirent64 *dp;
1404*2621Sllai1 	dirent64_t *dbuf;
1405*2621Sllai1 	size_t dbuflen;
1406*2621Sllai1 	struct vattr vattr;
1407*2621Sllai1 	char *link = NULL;
1408*2621Sllai1 
1409*2621Sllai1 	if (ddv->sdev_attrvp == NULL)
1410*2621Sllai1 		return (0);
1411*2621Sllai1 	if (!(ddv->sdev_flags & SDEV_BUILD))
1412*2621Sllai1 		return (0);
1413*2621Sllai1 
1414*2621Sllai1 	dirvp = ddv->sdev_attrvp;
1415*2621Sllai1 	VN_HOLD(dirvp);
1416*2621Sllai1 	dbuf = kmem_zalloc(dlen, KM_SLEEP);
1417*2621Sllai1 
1418*2621Sllai1 	uio.uio_iov = &iov;
1419*2621Sllai1 	uio.uio_iovcnt = 1;
1420*2621Sllai1 	uio.uio_segflg = UIO_SYSSPACE;
1421*2621Sllai1 	uio.uio_fmode = 0;
1422*2621Sllai1 	uio.uio_extflg = UIO_COPY_CACHED;
1423*2621Sllai1 	uio.uio_loffset = 0;
1424*2621Sllai1 	uio.uio_llimit = MAXOFFSET_T;
1425*2621Sllai1 
1426*2621Sllai1 	eof = 0;
1427*2621Sllai1 	error = 0;
1428*2621Sllai1 	while (!error && !eof) {
1429*2621Sllai1 		uio.uio_resid = dlen;
1430*2621Sllai1 		iov.iov_base = (char *)dbuf;
1431*2621Sllai1 		iov.iov_len = dlen;
1432*2621Sllai1 		(void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL);
1433*2621Sllai1 		error = VOP_READDIR(dirvp, &uio, kcred, &eof);
1434*2621Sllai1 		VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL);
1435*2621Sllai1 
1436*2621Sllai1 		dbuflen = dlen - uio.uio_resid;
1437*2621Sllai1 		if (error || dbuflen == 0)
1438*2621Sllai1 			break;
1439*2621Sllai1 
1440*2621Sllai1 		if (!(ddv->sdev_flags & SDEV_BUILD)) {
1441*2621Sllai1 			error = 0;
1442*2621Sllai1 			break;
1443*2621Sllai1 		}
1444*2621Sllai1 
1445*2621Sllai1 		for (dp = dbuf; ((intptr_t)dp <
1446*2621Sllai1 		    (intptr_t)dbuf + dbuflen);
1447*2621Sllai1 		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
1448*2621Sllai1 			nm = dp->d_name;
1449*2621Sllai1 
1450*2621Sllai1 			if (strcmp(nm, ".") == 0 ||
1451*2621Sllai1 			    strcmp(nm, "..") == 0)
1452*2621Sllai1 				continue;
1453*2621Sllai1 
1454*2621Sllai1 			vp = NULLVP;
1455*2621Sllai1 			dv = sdev_cache_lookup(ddv, nm);
1456*2621Sllai1 			if (dv) {
1457*2621Sllai1 				if (dv->sdev_state != SDEV_ZOMBIE) {
1458*2621Sllai1 					SDEV_SIMPLE_RELE(dv);
1459*2621Sllai1 				} else {
1460*2621Sllai1 					/*
1461*2621Sllai1 					 * A ZOMBIE node may not have been
1462*2621Sllai1 					 * cleaned up from the backing store,
1463*2621Sllai1 					 * bypass this entry in this case,
1464*2621Sllai1 					 * and clean it up from the directory
1465*2621Sllai1 					 * cache if this is the last call.
1466*2621Sllai1 					 */
1467*2621Sllai1 					(void) sdev_dirdelete(ddv, dv);
1468*2621Sllai1 				}
1469*2621Sllai1 				continue;
1470*2621Sllai1 			}
1471*2621Sllai1 
1472*2621Sllai1 			/* refill the cache if not already */
1473*2621Sllai1 			error = devname_backstore_lookup(ddv, nm, &vp);
1474*2621Sllai1 			if (error)
1475*2621Sllai1 				continue;
1476*2621Sllai1 
1477*2621Sllai1 			vattr.va_mask = AT_MODE|AT_UID|AT_GID;
1478*2621Sllai1 			error = VOP_GETATTR(vp, &vattr, 0, cred);
1479*2621Sllai1 			if (error)
1480*2621Sllai1 				continue;
1481*2621Sllai1 
1482*2621Sllai1 			if (vattr.va_type == VLNK) {
1483*2621Sllai1 				error = sdev_getlink(vp, &link);
1484*2621Sllai1 				if (error) {
1485*2621Sllai1 					continue;
1486*2621Sllai1 				}
1487*2621Sllai1 				ASSERT(link != NULL);
1488*2621Sllai1 			}
1489*2621Sllai1 
1490*2621Sllai1 			if (!rw_tryupgrade(&ddv->sdev_contents)) {
1491*2621Sllai1 				rw_exit(&ddv->sdev_contents);
1492*2621Sllai1 				rw_enter(&ddv->sdev_contents, RW_WRITER);
1493*2621Sllai1 			}
1494*2621Sllai1 			error = sdev_mknode(ddv, nm, &dv, &vattr, vp, link,
1495*2621Sllai1 			    cred, SDEV_READY);
1496*2621Sllai1 			rw_downgrade(&ddv->sdev_contents);
1497*2621Sllai1 
1498*2621Sllai1 			if (link != NULL) {
1499*2621Sllai1 				kmem_free(link, strlen(link) + 1);
1500*2621Sllai1 				link = NULL;
1501*2621Sllai1 			}
1502*2621Sllai1 
1503*2621Sllai1 			if (!error) {
1504*2621Sllai1 				ASSERT(dv);
1505*2621Sllai1 				ASSERT(dv->sdev_state != SDEV_ZOMBIE);
1506*2621Sllai1 				SDEV_SIMPLE_RELE(dv);
1507*2621Sllai1 			}
1508*2621Sllai1 			vp = NULL;
1509*2621Sllai1 			dv = NULL;
1510*2621Sllai1 		}
1511*2621Sllai1 	}
1512*2621Sllai1 
1513*2621Sllai1 done:
1514*2621Sllai1 	VN_RELE(dirvp);
1515*2621Sllai1 	kmem_free(dbuf, dlen);
1516*2621Sllai1 
1517*2621Sllai1 	return (error);
1518*2621Sllai1 }
1519*2621Sllai1 
1520*2621Sllai1 static int
1521*2621Sllai1 sdev_filldir_dynamic(struct sdev_node *ddv)
1522*2621Sllai1 {
1523*2621Sllai1 	int error;
1524*2621Sllai1 	int i;
1525*2621Sllai1 	struct vattr *vap;
1526*2621Sllai1 	char *nm = NULL;
1527*2621Sllai1 	struct sdev_node *dv = NULL;
1528*2621Sllai1 
1529*2621Sllai1 	if (!(ddv->sdev_flags & SDEV_BUILD)) {
1530*2621Sllai1 		return (0);
1531*2621Sllai1 	}
1532*2621Sllai1 
1533*2621Sllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
1534*2621Sllai1 	if (!rw_tryupgrade(&ddv->sdev_contents)) {
1535*2621Sllai1 		rw_exit(&ddv->sdev_contents);
1536*2621Sllai1 		rw_enter(&ddv->sdev_contents, RW_WRITER);
1537*2621Sllai1 	}
1538*2621Sllai1 
1539*2621Sllai1 	vap = sdev_getdefault_attr(VDIR);
1540*2621Sllai1 	for (i = 0; vtab[i].vt_name != NULL; i++) {
1541*2621Sllai1 		nm = vtab[i].vt_name;
1542*2621Sllai1 		ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1543*2621Sllai1 		error = sdev_mknode(ddv, nm, &dv, vap, NULL,
1544*2621Sllai1 		    NULL, kcred, SDEV_READY);
1545*2621Sllai1 		if (error)
1546*2621Sllai1 			continue;
1547*2621Sllai1 		ASSERT(dv);
1548*2621Sllai1 		ASSERT(dv->sdev_state != SDEV_ZOMBIE);
1549*2621Sllai1 		SDEV_SIMPLE_RELE(dv);
1550*2621Sllai1 		dv = NULL;
1551*2621Sllai1 	}
1552*2621Sllai1 	rw_downgrade(&ddv->sdev_contents);
1553*2621Sllai1 	return (0);
1554*2621Sllai1 }
1555*2621Sllai1 
1556*2621Sllai1 /*
1557*2621Sllai1  * Creating a backing store entry based on sdev_attr.
1558*2621Sllai1  * This is called either as part of node creation in a persistent directory
1559*2621Sllai1  * or from setattr/setsecattr to persist access attributes across reboot.
1560*2621Sllai1  */
1561*2621Sllai1 int
1562*2621Sllai1 sdev_shadow_node(struct sdev_node *dv, struct cred *cred)
1563*2621Sllai1 {
1564*2621Sllai1 	int error = 0;
1565*2621Sllai1 	struct vnode *dvp = SDEVTOV(dv->sdev_dotdot);
1566*2621Sllai1 	struct vnode *rdvp = VTOSDEV(dvp)->sdev_attrvp;
1567*2621Sllai1 	struct vattr *vap = dv->sdev_attr;
1568*2621Sllai1 	char *nm = dv->sdev_name;
1569*2621Sllai1 	struct vnode *tmpvp, **rvp = &tmpvp, *rrvp = NULL;
1570*2621Sllai1 
1571*2621Sllai1 	ASSERT(dv && dv->sdev_name && rdvp);
1572*2621Sllai1 	ASSERT(RW_WRITE_HELD(&dv->sdev_contents) && dv->sdev_attrvp == NULL);
1573*2621Sllai1 
1574*2621Sllai1 lookup:
1575*2621Sllai1 	/* try to find it in the backing store */
1576*2621Sllai1 	error = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, cred);
1577*2621Sllai1 	if (error == 0) {
1578*2621Sllai1 		if (VOP_REALVP(*rvp, &rrvp) == 0) {
1579*2621Sllai1 			VN_HOLD(rrvp);
1580*2621Sllai1 			VN_RELE(*rvp);
1581*2621Sllai1 			*rvp = rrvp;
1582*2621Sllai1 		}
1583*2621Sllai1 
1584*2621Sllai1 		kmem_free(dv->sdev_attr, sizeof (vattr_t));
1585*2621Sllai1 		dv->sdev_attr = NULL;
1586*2621Sllai1 		dv->sdev_attrvp = *rvp;
1587*2621Sllai1 		return (0);
1588*2621Sllai1 	}
1589*2621Sllai1 
1590*2621Sllai1 	/* let's try to persist the node */
1591*2621Sllai1 	gethrestime(&vap->va_atime);
1592*2621Sllai1 	vap->va_mtime = vap->va_atime;
1593*2621Sllai1 	vap->va_ctime = vap->va_atime;
1594*2621Sllai1 	vap->va_mask |= AT_TYPE|AT_MODE;
1595*2621Sllai1 	switch (vap->va_type) {
1596*2621Sllai1 	case VDIR:
1597*2621Sllai1 		error = VOP_MKDIR(rdvp, nm, vap, rvp, cred);
1598*2621Sllai1 		sdcmn_err9(("sdev_shadow_node: mkdir vp %p error %d\n",
1599*2621Sllai1 		    (void *)(*rvp), error));
1600*2621Sllai1 		break;
1601*2621Sllai1 	case VCHR:
1602*2621Sllai1 	case VBLK:
1603*2621Sllai1 	case VREG:
1604*2621Sllai1 	case VDOOR:
1605*2621Sllai1 		error = VOP_CREATE(rdvp, nm, vap, NONEXCL, VREAD|VWRITE,
1606*2621Sllai1 		    rvp, cred, 0);
1607*2621Sllai1 		sdcmn_err9(("sdev_shadow_node: create vp %p, error %d\n",
1608*2621Sllai1 		    (void *)(*rvp), error));
1609*2621Sllai1 		if (!error)
1610*2621Sllai1 			VN_RELE(*rvp);
1611*2621Sllai1 		break;
1612*2621Sllai1 	case VLNK:
1613*2621Sllai1 		ASSERT(dv->sdev_symlink);
1614*2621Sllai1 		error = VOP_SYMLINK(rdvp, nm, vap, dv->sdev_symlink, cred);
1615*2621Sllai1 		sdcmn_err9(("sdev_shadow_node: create symlink error %d\n",
1616*2621Sllai1 		    error));
1617*2621Sllai1 		break;
1618*2621Sllai1 	default:
1619*2621Sllai1 		cmn_err(CE_PANIC, "dev: %s: sdev_shadow_node "
1620*2621Sllai1 		    "create\n", nm);
1621*2621Sllai1 		/*NOTREACHED*/
1622*2621Sllai1 	}
1623*2621Sllai1 
1624*2621Sllai1 	/* go back to lookup to factor out spec node and set attrvp */
1625*2621Sllai1 	if (error == 0)
1626*2621Sllai1 		goto lookup;
1627*2621Sllai1 
1628*2621Sllai1 	return (error);
1629*2621Sllai1 }
1630*2621Sllai1 
1631*2621Sllai1 static int
1632*2621Sllai1 sdev_cache_add(struct sdev_node *ddv, struct sdev_node **dv, char *nm)
1633*2621Sllai1 {
1634*2621Sllai1 	int error = 0;
1635*2621Sllai1 	struct sdev_node *dup = NULL;
1636*2621Sllai1 
1637*2621Sllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1638*2621Sllai1 	if ((dup = sdev_findbyname(ddv, nm)) == NULL) {
1639*2621Sllai1 		sdev_direnter(ddv, *dv);
1640*2621Sllai1 	} else {
1641*2621Sllai1 		if (dup->sdev_state == SDEV_ZOMBIE) {
1642*2621Sllai1 			error = sdev_dirdelete(ddv, dup);
1643*2621Sllai1 			/*
1644*2621Sllai1 			 * The ZOMBIE node is still hanging
1645*2621Sllai1 			 * around with more than one reference counts.
1646*2621Sllai1 			 * Fail the new node creation so that
1647*2621Sllai1 			 * the directory cache won't have
1648*2621Sllai1 			 * duplicate entries for the same named node
1649*2621Sllai1 			 */
1650*2621Sllai1 			if (error == EBUSY) {
1651*2621Sllai1 				SDEV_SIMPLE_RELE(*dv);
1652*2621Sllai1 				sdev_nodedestroy(*dv, 0);
1653*2621Sllai1 				*dv = NULL;
1654*2621Sllai1 				return (error);
1655*2621Sllai1 			}
1656*2621Sllai1 			sdev_direnter(ddv, *dv);
1657*2621Sllai1 		} else {
1658*2621Sllai1 			ASSERT((*dv)->sdev_state != SDEV_ZOMBIE);
1659*2621Sllai1 			SDEV_SIMPLE_RELE(*dv);
1660*2621Sllai1 			sdev_nodedestroy(*dv, 0);
1661*2621Sllai1 			*dv = dup;
1662*2621Sllai1 		}
1663*2621Sllai1 	}
1664*2621Sllai1 
1665*2621Sllai1 	return (0);
1666*2621Sllai1 }
1667*2621Sllai1 
1668*2621Sllai1 static int
1669*2621Sllai1 sdev_cache_delete(struct sdev_node *ddv, struct sdev_node **dv)
1670*2621Sllai1 {
1671*2621Sllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1672*2621Sllai1 	return (sdev_dirdelete(ddv, *dv));
1673*2621Sllai1 }
1674*2621Sllai1 
1675*2621Sllai1 /*
1676*2621Sllai1  * update the in-core directory cache
1677*2621Sllai1  */
1678*2621Sllai1 int
1679*2621Sllai1 sdev_cache_update(struct sdev_node *ddv, struct sdev_node **dv, char *nm,
1680*2621Sllai1     sdev_cache_ops_t ops)
1681*2621Sllai1 {
1682*2621Sllai1 	int error = 0;
1683*2621Sllai1 
1684*2621Sllai1 	ASSERT((SDEV_HELD(*dv)));
1685*2621Sllai1 
1686*2621Sllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1687*2621Sllai1 	switch (ops) {
1688*2621Sllai1 	case SDEV_CACHE_ADD:
1689*2621Sllai1 		error = sdev_cache_add(ddv, dv, nm);
1690*2621Sllai1 		break;
1691*2621Sllai1 	case SDEV_CACHE_DELETE:
1692*2621Sllai1 		error = sdev_cache_delete(ddv, dv);
1693*2621Sllai1 		break;
1694*2621Sllai1 	default:
1695*2621Sllai1 		break;
1696*2621Sllai1 	}
1697*2621Sllai1 
1698*2621Sllai1 	return (error);
1699*2621Sllai1 }
1700*2621Sllai1 
1701*2621Sllai1 /*
1702*2621Sllai1  * retrive the named entry from the directory cache
1703*2621Sllai1  */
1704*2621Sllai1 struct sdev_node *
1705*2621Sllai1 sdev_cache_lookup(struct sdev_node *ddv, char *nm)
1706*2621Sllai1 {
1707*2621Sllai1 	struct sdev_node *dv = NULL;
1708*2621Sllai1 
1709*2621Sllai1 	ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
1710*2621Sllai1 	dv = sdev_findbyname(ddv, nm);
1711*2621Sllai1 
1712*2621Sllai1 	return (dv);
1713*2621Sllai1 }
1714*2621Sllai1 
1715*2621Sllai1 /*
1716*2621Sllai1  * Implicit reconfig for nodes constructed by a link generator
1717*2621Sllai1  * Start devfsadm if needed, or if devfsadm is in progress,
1718*2621Sllai1  * prepare to block on devfsadm either completing or
1719*2621Sllai1  * constructing the desired node.  As devfsadmd is global
1720*2621Sllai1  * in scope, constructing all necessary nodes, we only
1721*2621Sllai1  * need to initiate it once.
1722*2621Sllai1  */
1723*2621Sllai1 static int
1724*2621Sllai1 sdev_call_devfsadmd(struct sdev_node *ddv, struct sdev_node *dv, char *nm)
1725*2621Sllai1 {
1726*2621Sllai1 	int error = 0;
1727*2621Sllai1 
1728*2621Sllai1 	if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) {
1729*2621Sllai1 		sdcmn_err6(("lookup: waiting for %s/%s, 0x%x\n",
1730*2621Sllai1 		    ddv->sdev_name, nm, devfsadm_state));
1731*2621Sllai1 		mutex_enter(&dv->sdev_lookup_lock);
1732*2621Sllai1 		SDEV_BLOCK_OTHERS(dv, (SDEV_LOOKUP | SDEV_LGWAITING));
1733*2621Sllai1 		mutex_exit(&dv->sdev_lookup_lock);
1734*2621Sllai1 		error = 0;
1735*2621Sllai1 	} else if (!DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state)) {
1736*2621Sllai1 		sdcmn_err6(("lookup %s/%s starting devfsadm, 0x%x\n",
1737*2621Sllai1 			ddv->sdev_name, nm, devfsadm_state));
1738*2621Sllai1 
1739*2621Sllai1 		sdev_devfsadmd_thread(ddv, dv, kcred);
1740*2621Sllai1 		mutex_enter(&dv->sdev_lookup_lock);
1741*2621Sllai1 		SDEV_BLOCK_OTHERS(dv,
1742*2621Sllai1 		    (SDEV_LOOKUP | SDEV_LGWAITING));
1743*2621Sllai1 		mutex_exit(&dv->sdev_lookup_lock);
1744*2621Sllai1 		error = 0;
1745*2621Sllai1 	} else {
1746*2621Sllai1 		error = -1;
1747*2621Sllai1 	}
1748*2621Sllai1 
1749*2621Sllai1 	return (error);
1750*2621Sllai1 }
1751*2621Sllai1 
1752*2621Sllai1 static int
1753*2621Sllai1 sdev_call_modulelookup(struct sdev_node *ddv, struct sdev_node **dvp, char *nm,
1754*2621Sllai1     int (*fn)(char *, devname_handle_t *, struct cred *), struct cred *cred)
1755*2621Sllai1 {
1756*2621Sllai1 	struct vnode *rvp = NULL;
1757*2621Sllai1 	int error = 0;
1758*2621Sllai1 	struct vattr *vap;
1759*2621Sllai1 	devname_spec_t spec;
1760*2621Sllai1 	devname_handle_t *hdl;
1761*2621Sllai1 	void *args = NULL;
1762*2621Sllai1 	struct sdev_node *dv = *dvp;
1763*2621Sllai1 
1764*2621Sllai1 	ASSERT(dv && ddv);
1765*2621Sllai1 	hdl = &(dv->sdev_handle);
1766*2621Sllai1 	ASSERT(hdl->dh_data == dv);
1767*2621Sllai1 	mutex_enter(&dv->sdev_lookup_lock);
1768*2621Sllai1 	SDEV_BLOCK_OTHERS(dv, SDEV_LOOKUP);
1769*2621Sllai1 	mutex_exit(&dv->sdev_lookup_lock);
1770*2621Sllai1 	error = (*fn)(nm, hdl, cred);
1771*2621Sllai1 	if (error) {
1772*2621Sllai1 		return (error);
1773*2621Sllai1 	}
1774*2621Sllai1 
1775*2621Sllai1 	spec = hdl->dh_spec;
1776*2621Sllai1 	args = hdl->dh_args;
1777*2621Sllai1 	ASSERT(args);
1778*2621Sllai1 
1779*2621Sllai1 	switch (spec) {
1780*2621Sllai1 	case DEVNAME_NS_PATH:
1781*2621Sllai1 		/*
1782*2621Sllai1 		 * symlink of:
1783*2621Sllai1 		 *	/dev/dir/nm -> /device/...
1784*2621Sllai1 		 */
1785*2621Sllai1 		rvp = devname_configure_by_path((char *)args, NULL);
1786*2621Sllai1 		break;
1787*2621Sllai1 	case DEVNAME_NS_DEV:
1788*2621Sllai1 		/*
1789*2621Sllai1 		 * symlink of:
1790*2621Sllai1 		 *	/dev/dir/nm -> /dev/...
1791*2621Sllai1 		 */
1792*2621Sllai1 		rvp = devname_find_by_devpath((char *)args, NULL);
1793*2621Sllai1 		break;
1794*2621Sllai1 	default:
1795*2621Sllai1 		if (args)
1796*2621Sllai1 			kmem_free((char *)args, strlen(args) + 1);
1797*2621Sllai1 		return (ENOENT);
1798*2621Sllai1 
1799*2621Sllai1 	}
1800*2621Sllai1 
1801*2621Sllai1 	if (rvp == NULL) {
1802*2621Sllai1 		if (args)
1803*2621Sllai1 			kmem_free((char *)args, strlen(args) + 1);
1804*2621Sllai1 		return (ENOENT);
1805*2621Sllai1 	} else {
1806*2621Sllai1 		vap = sdev_getdefault_attr(VLNK);
1807*2621Sllai1 		ASSERT(RW_READ_HELD(&ddv->sdev_contents));
1808*2621Sllai1 		/*
1809*2621Sllai1 		 * Could sdev_mknode return a different dv_node
1810*2621Sllai1 		 * once the lock is dropped?
1811*2621Sllai1 		 */
1812*2621Sllai1 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
1813*2621Sllai1 			rw_exit(&ddv->sdev_contents);
1814*2621Sllai1 			rw_enter(&ddv->sdev_contents, RW_WRITER);
1815*2621Sllai1 		}
1816*2621Sllai1 		error = sdev_mknode(ddv, nm, &dv, vap, NULL, args, cred,
1817*2621Sllai1 		    SDEV_READY);
1818*2621Sllai1 		rw_downgrade(&ddv->sdev_contents);
1819*2621Sllai1 		if (error) {
1820*2621Sllai1 			if (args)
1821*2621Sllai1 				kmem_free((char *)args, strlen(args) + 1);
1822*2621Sllai1 			return (error);
1823*2621Sllai1 		} else {
1824*2621Sllai1 			mutex_enter(&dv->sdev_lookup_lock);
1825*2621Sllai1 			SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
1826*2621Sllai1 			mutex_exit(&dv->sdev_lookup_lock);
1827*2621Sllai1 			error = 0;
1828*2621Sllai1 		}
1829*2621Sllai1 	}
1830*2621Sllai1 
1831*2621Sllai1 	if (args)
1832*2621Sllai1 		kmem_free((char *)args, strlen(args) + 1);
1833*2621Sllai1 
1834*2621Sllai1 	*dvp = dv;
1835*2621Sllai1 	return (0);
1836*2621Sllai1 }
1837*2621Sllai1 
1838*2621Sllai1 /*
1839*2621Sllai1  *  Support for specialized device naming construction mechanisms
1840*2621Sllai1  */
1841*2621Sllai1 static int
1842*2621Sllai1 sdev_call_dircallback(struct sdev_node *ddv, struct sdev_node **dvp, char *nm,
1843*2621Sllai1     int (*callback)(struct sdev_node *, char *, void **, struct cred *,
1844*2621Sllai1     void *, char *), int flags, struct cred *cred)
1845*2621Sllai1 {
1846*2621Sllai1 	int rv = 0;
1847*2621Sllai1 	char *physpath = NULL;
1848*2621Sllai1 	struct vnode *rvp = NULL;
1849*2621Sllai1 	struct vattr vattr;
1850*2621Sllai1 	struct vattr *vap;
1851*2621Sllai1 	struct sdev_node *dv = *dvp;
1852*2621Sllai1 
1853*2621Sllai1 	mutex_enter(&dv->sdev_lookup_lock);
1854*2621Sllai1 	SDEV_BLOCK_OTHERS(dv, SDEV_LOOKUP);
1855*2621Sllai1 	mutex_exit(&dv->sdev_lookup_lock);
1856*2621Sllai1 
1857*2621Sllai1 	/* for non-devfsadm devices */
1858*2621Sllai1 	if (flags & SDEV_PATH) {
1859*2621Sllai1 		physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
1860*2621Sllai1 		rv = callback(ddv, nm, (void *)&physpath, kcred, NULL,
1861*2621Sllai1 		    NULL);
1862*2621Sllai1 		if (rv) {
1863*2621Sllai1 			kmem_free(physpath, MAXPATHLEN);
1864*2621Sllai1 			return (-1);
1865*2621Sllai1 		}
1866*2621Sllai1 
1867*2621Sllai1 		ASSERT(physpath);
1868*2621Sllai1 		rvp = devname_configure_by_path(physpath, NULL);
1869*2621Sllai1 		if (rvp == NULL) {
1870*2621Sllai1 			sdcmn_err3(("devname_configure_by_path: "
1871*2621Sllai1 			    "failed for /dev/%s/%s\n",
1872*2621Sllai1 			    ddv->sdev_name, nm));
1873*2621Sllai1 			kmem_free(physpath, MAXPATHLEN);
1874*2621Sllai1 			rv = -1;
1875*2621Sllai1 		} else {
1876*2621Sllai1 			vap = sdev_getdefault_attr(VLNK);
1877*2621Sllai1 			ASSERT(RW_READ_HELD(&ddv->sdev_contents));
1878*2621Sllai1 
1879*2621Sllai1 			/*
1880*2621Sllai1 			 * Sdev_mknode may return back a different sdev_node
1881*2621Sllai1 			 * that was created by another thread that
1882*2621Sllai1 			 * raced to the directroy cache before this thread.
1883*2621Sllai1 			 *
1884*2621Sllai1 			 * With current directory cache mechanism
1885*2621Sllai1 			 * (linked list with the sdev_node name as
1886*2621Sllai1 			 * the entity key), this is a way to make sure
1887*2621Sllai1 			 * only one entry exists for the same name
1888*2621Sllai1 			 * in the same directory. The outcome is
1889*2621Sllai1 			 * the winner wins.
1890*2621Sllai1 			 */
1891*2621Sllai1 			if (!rw_tryupgrade(&ddv->sdev_contents)) {
1892*2621Sllai1 				rw_exit(&ddv->sdev_contents);
1893*2621Sllai1 				rw_enter(&ddv->sdev_contents, RW_WRITER);
1894*2621Sllai1 			}
1895*2621Sllai1 			rv = sdev_mknode(ddv, nm, &dv, vap, NULL,
1896*2621Sllai1 			    (void *)physpath, cred, SDEV_READY);
1897*2621Sllai1 			rw_downgrade(&ddv->sdev_contents);
1898*2621Sllai1 			kmem_free(physpath, MAXPATHLEN);
1899*2621Sllai1 			if (rv) {
1900*2621Sllai1 				return (rv);
1901*2621Sllai1 			} else {
1902*2621Sllai1 				mutex_enter(&dv->sdev_lookup_lock);
1903*2621Sllai1 				SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
1904*2621Sllai1 				mutex_exit(&dv->sdev_lookup_lock);
1905*2621Sllai1 				return (0);
1906*2621Sllai1 			}
1907*2621Sllai1 		}
1908*2621Sllai1 	} else if (flags & SDEV_VNODE) {
1909*2621Sllai1 		/*
1910*2621Sllai1 		 * DBNR has its own way to create the device
1911*2621Sllai1 		 * and return a backing store vnode in rvp
1912*2621Sllai1 		 */
1913*2621Sllai1 		ASSERT(callback);
1914*2621Sllai1 		rv = callback(ddv, nm, (void *)&rvp, kcred, NULL, NULL);
1915*2621Sllai1 		if (rv || (rvp == NULL)) {
1916*2621Sllai1 			sdcmn_err3(("devname_lookup_func: SDEV_VNODE "
1917*2621Sllai1 			    "callback failed \n"));
1918*2621Sllai1 			return (-1);
1919*2621Sllai1 		}
1920*2621Sllai1 		vap = sdev_getdefault_attr(rvp->v_type);
1921*2621Sllai1 		if (vap == NULL)
1922*2621Sllai1 			return (-1);
1923*2621Sllai1 
1924*2621Sllai1 		ASSERT(RW_READ_HELD(&ddv->sdev_contents));
1925*2621Sllai1 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
1926*2621Sllai1 			rw_exit(&ddv->sdev_contents);
1927*2621Sllai1 			rw_enter(&ddv->sdev_contents, RW_WRITER);
1928*2621Sllai1 		}
1929*2621Sllai1 		rv = sdev_mknode(ddv, nm, &dv, vap, rvp, NULL,
1930*2621Sllai1 		    cred, SDEV_READY);
1931*2621Sllai1 		rw_downgrade(&ddv->sdev_contents);
1932*2621Sllai1 		if (rv)
1933*2621Sllai1 			return (rv);
1934*2621Sllai1 
1935*2621Sllai1 		mutex_enter(&dv->sdev_lookup_lock);
1936*2621Sllai1 		SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
1937*2621Sllai1 		mutex_exit(&dv->sdev_lookup_lock);
1938*2621Sllai1 		return (0);
1939*2621Sllai1 	} else if (flags & SDEV_VATTR) {
1940*2621Sllai1 		/*
1941*2621Sllai1 		 * /dev/pts
1942*2621Sllai1 		 *
1943*2621Sllai1 		 * DBNR has its own way to create the device
1944*2621Sllai1 		 * "0" is returned upon success.
1945*2621Sllai1 		 *
1946*2621Sllai1 		 * callback is responsible to set the basic attributes,
1947*2621Sllai1 		 * e.g. va_type/va_uid/va_gid/
1948*2621Sllai1 		 *    dev_t if VCHR or VBLK/
1949*2621Sllai1 		 */
1950*2621Sllai1 		ASSERT(callback);
1951*2621Sllai1 		rv = callback(ddv, nm, (void *)&vattr, kcred, NULL, NULL);
1952*2621Sllai1 		if (rv) {
1953*2621Sllai1 			sdcmn_err3(("devname_lookup_func: SDEV_NONE "
1954*2621Sllai1 			    "callback failed \n"));
1955*2621Sllai1 			return (-1);
1956*2621Sllai1 		}
1957*2621Sllai1 
1958*2621Sllai1 		ASSERT(RW_READ_HELD(&ddv->sdev_contents));
1959*2621Sllai1 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
1960*2621Sllai1 			rw_exit(&ddv->sdev_contents);
1961*2621Sllai1 			rw_enter(&ddv->sdev_contents, RW_WRITER);
1962*2621Sllai1 		}
1963*2621Sllai1 		rv = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL,
1964*2621Sllai1 		    cred, SDEV_READY);
1965*2621Sllai1 		rw_downgrade(&ddv->sdev_contents);
1966*2621Sllai1 
1967*2621Sllai1 		if (rv)
1968*2621Sllai1 			return (rv);
1969*2621Sllai1 
1970*2621Sllai1 		mutex_enter(&dv->sdev_lookup_lock);
1971*2621Sllai1 		SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
1972*2621Sllai1 		mutex_exit(&dv->sdev_lookup_lock);
1973*2621Sllai1 		return (0);
1974*2621Sllai1 	} else {
1975*2621Sllai1 		impossible(("lookup: %s/%s by %s not supported (%d)\n",
1976*2621Sllai1 		    SDEVTOV(ddv)->v_path, nm, curproc->p_user.u_comm,
1977*2621Sllai1 		    __LINE__));
1978*2621Sllai1 		rv = -1;
1979*2621Sllai1 	}
1980*2621Sllai1 
1981*2621Sllai1 	*dvp = dv;
1982*2621Sllai1 	return (rv);
1983*2621Sllai1 }
1984*2621Sllai1 
1985*2621Sllai1 static int
1986*2621Sllai1 is_devfsadm_thread(char *exec_name)
1987*2621Sllai1 {
1988*2621Sllai1 	/*
1989*2621Sllai1 	 * note: because devfsadmd -> /usr/sbin/devfsadm
1990*2621Sllai1 	 * it is safe to use "devfsadm" to capture the lookups
1991*2621Sllai1 	 * from devfsadm and its daemon version.
1992*2621Sllai1 	 */
1993*2621Sllai1 	if (strcmp(exec_name, "devfsadm") == 0)
1994*2621Sllai1 		return (1);
1995*2621Sllai1 	return (0);
1996*2621Sllai1 }
1997*2621Sllai1 
1998*2621Sllai1 
1999*2621Sllai1 /*
2000*2621Sllai1  * Lookup Order:
2001*2621Sllai1  *	sdev_node cache;
2002*2621Sllai1  *	backing store (SDEV_PERSIST);
2003*2621Sllai1  *	DBNR: a. dir_ops implemented in the loadable modules;
2004*2621Sllai1  *	      b. vnode ops in vtab.
2005*2621Sllai1  */
2006*2621Sllai1 int
2007*2621Sllai1 devname_lookup_func(struct sdev_node *ddv, char *nm, struct vnode **vpp,
2008*2621Sllai1     struct cred *cred, int (*callback)(struct sdev_node *, char *, void **,
2009*2621Sllai1     struct cred *, void *, char *), int flags)
2010*2621Sllai1 {
2011*2621Sllai1 	int rv = 0, nmlen;
2012*2621Sllai1 	struct vnode *rvp = NULL;
2013*2621Sllai1 	struct sdev_node *dv = NULL;
2014*2621Sllai1 	int	retried = 0;
2015*2621Sllai1 	int	error = 0;
2016*2621Sllai1 	struct devname_nsmap *map = NULL;
2017*2621Sllai1 	struct devname_ops *dirops = NULL;
2018*2621Sllai1 	int (*fn)(char *, devname_handle_t *, struct cred *) = NULL;
2019*2621Sllai1 	struct vattr vattr;
2020*2621Sllai1 	char *lookup_thread = curproc->p_user.u_comm;
2021*2621Sllai1 	int failed_flags = 0;
2022*2621Sllai1 	int (*vtor)(struct sdev_node *) = NULL;
2023*2621Sllai1 	int state;
2024*2621Sllai1 	int parent_state;
2025*2621Sllai1 	char *link = NULL;
2026*2621Sllai1 
2027*2621Sllai1 	if (SDEVTOV(ddv)->v_type != VDIR)
2028*2621Sllai1 		return (ENOTDIR);
2029*2621Sllai1 
2030*2621Sllai1 	/*
2031*2621Sllai1 	 * Empty name or ., return node itself.
2032*2621Sllai1 	 */
2033*2621Sllai1 	nmlen = strlen(nm);
2034*2621Sllai1 	if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
2035*2621Sllai1 		*vpp = SDEVTOV(ddv);
2036*2621Sllai1 		VN_HOLD(*vpp);
2037*2621Sllai1 		return (0);
2038*2621Sllai1 	}
2039*2621Sllai1 
2040*2621Sllai1 	/*
2041*2621Sllai1 	 * .., return the parent directory
2042*2621Sllai1 	 */
2043*2621Sllai1 	if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
2044*2621Sllai1 		*vpp = SDEVTOV(ddv->sdev_dotdot);
2045*2621Sllai1 		VN_HOLD(*vpp);
2046*2621Sllai1 		return (0);
2047*2621Sllai1 	}
2048*2621Sllai1 
2049*2621Sllai1 	rw_enter(&ddv->sdev_contents, RW_READER);
2050*2621Sllai1 	if (ddv->sdev_flags & SDEV_VTOR) {
2051*2621Sllai1 		vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
2052*2621Sllai1 		ASSERT(vtor);
2053*2621Sllai1 	}
2054*2621Sllai1 
2055*2621Sllai1 tryagain:
2056*2621Sllai1 	/*
2057*2621Sllai1 	 * (a) directory cache lookup:
2058*2621Sllai1 	 */
2059*2621Sllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
2060*2621Sllai1 	parent_state = ddv->sdev_state;
2061*2621Sllai1 	dv = sdev_cache_lookup(ddv, nm);
2062*2621Sllai1 	if (dv) {
2063*2621Sllai1 		state = dv->sdev_state;
2064*2621Sllai1 		switch (state) {
2065*2621Sllai1 		case SDEV_INIT:
2066*2621Sllai1 			if (is_devfsadm_thread(lookup_thread))
2067*2621Sllai1 				break;
2068*2621Sllai1 
2069*2621Sllai1 			/* ZOMBIED parent won't allow node creation */
2070*2621Sllai1 			if (parent_state == SDEV_ZOMBIE) {
2071*2621Sllai1 				SD_TRACE_FAILED_LOOKUP(ddv, nm,
2072*2621Sllai1 				    retried);
2073*2621Sllai1 				goto nolock_notfound;
2074*2621Sllai1 			}
2075*2621Sllai1 
2076*2621Sllai1 			mutex_enter(&dv->sdev_lookup_lock);
2077*2621Sllai1 			/* compensate the threads started after devfsadm */
2078*2621Sllai1 			if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
2079*2621Sllai1 			    !(SDEV_IS_LOOKUP(dv)))
2080*2621Sllai1 				SDEV_BLOCK_OTHERS(dv,
2081*2621Sllai1 				    (SDEV_LOOKUP | SDEV_LGWAITING));
2082*2621Sllai1 
2083*2621Sllai1 			if (SDEV_IS_LOOKUP(dv)) {
2084*2621Sllai1 				failed_flags |= SLF_REBUILT;
2085*2621Sllai1 				rw_exit(&ddv->sdev_contents);
2086*2621Sllai1 				error = sdev_wait4lookup(dv, SDEV_LOOKUP);
2087*2621Sllai1 				mutex_exit(&dv->sdev_lookup_lock);
2088*2621Sllai1 				rw_enter(&ddv->sdev_contents, RW_READER);
2089*2621Sllai1 
2090*2621Sllai1 				if (error != 0) {
2091*2621Sllai1 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
2092*2621Sllai1 					    retried);
2093*2621Sllai1 					goto nolock_notfound;
2094*2621Sllai1 				}
2095*2621Sllai1 
2096*2621Sllai1 				state = dv->sdev_state;
2097*2621Sllai1 				if (state == SDEV_INIT) {
2098*2621Sllai1 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
2099*2621Sllai1 					    retried);
2100*2621Sllai1 					goto nolock_notfound;
2101*2621Sllai1 				} else if (state == SDEV_READY) {
2102*2621Sllai1 					goto found;
2103*2621Sllai1 				} else if (state == SDEV_ZOMBIE) {
2104*2621Sllai1 					rw_exit(&ddv->sdev_contents);
2105*2621Sllai1 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
2106*2621Sllai1 					    retried);
2107*2621Sllai1 					SDEV_RELE(dv);
2108*2621Sllai1 					goto lookup_failed;
2109*2621Sllai1 				}
2110*2621Sllai1 			} else {
2111*2621Sllai1 				mutex_exit(&dv->sdev_lookup_lock);
2112*2621Sllai1 			}
2113*2621Sllai1 			break;
2114*2621Sllai1 		case SDEV_READY:
2115*2621Sllai1 			goto found;
2116*2621Sllai1 		case SDEV_ZOMBIE:
2117*2621Sllai1 			rw_exit(&ddv->sdev_contents);
2118*2621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2119*2621Sllai1 			SDEV_RELE(dv);
2120*2621Sllai1 			goto lookup_failed;
2121*2621Sllai1 		default:
2122*2621Sllai1 			rw_exit(&ddv->sdev_contents);
2123*2621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2124*2621Sllai1 			sdev_lookup_failed(ddv, nm, failed_flags);
2125*2621Sllai1 			*vpp = NULLVP;
2126*2621Sllai1 			return (ENOENT);
2127*2621Sllai1 		}
2128*2621Sllai1 	}
2129*2621Sllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
2130*2621Sllai1 
2131*2621Sllai1 	/*
2132*2621Sllai1 	 * ZOMBIED parent does not allow new node creation.
2133*2621Sllai1 	 * bail out early
2134*2621Sllai1 	 */
2135*2621Sllai1 	if (parent_state == SDEV_ZOMBIE) {
2136*2621Sllai1 		rw_exit(&ddv->sdev_contents);
2137*2621Sllai1 		*vpp = NULL;
2138*2621Sllai1 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2139*2621Sllai1 		return (ENOENT);
2140*2621Sllai1 	}
2141*2621Sllai1 
2142*2621Sllai1 	/*
2143*2621Sllai1 	 * (b0): backing store lookup
2144*2621Sllai1 	 *	SDEV_PERSIST is default except:
2145*2621Sllai1 	 *		1) pts nodes
2146*2621Sllai1 	 *		2) non-chmod'ed local nodes
2147*2621Sllai1 	 */
2148*2621Sllai1 	if (SDEV_IS_PERSIST(ddv)) {
2149*2621Sllai1 		error = devname_backstore_lookup(ddv, nm, &rvp);
2150*2621Sllai1 
2151*2621Sllai1 		if (!error) {
2152*2621Sllai1 			sdcmn_err3(("devname_backstore_lookup: "
2153*2621Sllai1 			    "found attrvp %p for %s\n", (void *)rvp, nm));
2154*2621Sllai1 
2155*2621Sllai1 			vattr.va_mask = AT_MODE|AT_UID|AT_GID;
2156*2621Sllai1 			error = VOP_GETATTR(rvp, &vattr, 0, cred);
2157*2621Sllai1 			if (error) {
2158*2621Sllai1 				rw_exit(&ddv->sdev_contents);
2159*2621Sllai1 				if (dv)
2160*2621Sllai1 					SDEV_RELE(dv);
2161*2621Sllai1 				SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2162*2621Sllai1 				sdev_lookup_failed(ddv, nm, failed_flags);
2163*2621Sllai1 				*vpp = NULLVP;
2164*2621Sllai1 				return (ENOENT);
2165*2621Sllai1 			}
2166*2621Sllai1 
2167*2621Sllai1 			if (vattr.va_type == VLNK) {
2168*2621Sllai1 				error = sdev_getlink(rvp, &link);
2169*2621Sllai1 				if (error) {
2170*2621Sllai1 					rw_exit(&ddv->sdev_contents);
2171*2621Sllai1 					if (dv)
2172*2621Sllai1 						SDEV_RELE(dv);
2173*2621Sllai1 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
2174*2621Sllai1 					    retried);
2175*2621Sllai1 					sdev_lookup_failed(ddv, nm,
2176*2621Sllai1 					    failed_flags);
2177*2621Sllai1 					*vpp = NULLVP;
2178*2621Sllai1 					return (ENOENT);
2179*2621Sllai1 				}
2180*2621Sllai1 				ASSERT(link != NULL);
2181*2621Sllai1 			}
2182*2621Sllai1 
2183*2621Sllai1 			if (!rw_tryupgrade(&ddv->sdev_contents)) {
2184*2621Sllai1 				rw_exit(&ddv->sdev_contents);
2185*2621Sllai1 				rw_enter(&ddv->sdev_contents, RW_WRITER);
2186*2621Sllai1 			}
2187*2621Sllai1 			error = sdev_mknode(ddv, nm, &dv, &vattr,
2188*2621Sllai1 			    rvp, link, cred, SDEV_READY);
2189*2621Sllai1 			rw_downgrade(&ddv->sdev_contents);
2190*2621Sllai1 
2191*2621Sllai1 			if (link != NULL) {
2192*2621Sllai1 				kmem_free(link, strlen(link) + 1);
2193*2621Sllai1 				link = NULL;
2194*2621Sllai1 			}
2195*2621Sllai1 
2196*2621Sllai1 			if (error) {
2197*2621Sllai1 				SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2198*2621Sllai1 				rw_exit(&ddv->sdev_contents);
2199*2621Sllai1 				if (dv)
2200*2621Sllai1 					SDEV_RELE(dv);
2201*2621Sllai1 				goto lookup_failed;
2202*2621Sllai1 			} else {
2203*2621Sllai1 				goto found;
2204*2621Sllai1 			}
2205*2621Sllai1 		} else if (retried) {
2206*2621Sllai1 			rw_exit(&ddv->sdev_contents);
2207*2621Sllai1 			sdcmn_err3(("retry of lookup of %s/%s: failed\n",
2208*2621Sllai1 			    ddv->sdev_name, nm));
2209*2621Sllai1 			if (dv)
2210*2621Sllai1 				SDEV_RELE(dv);
2211*2621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2212*2621Sllai1 			sdev_lookup_failed(ddv, nm, failed_flags);
2213*2621Sllai1 			*vpp = NULLVP;
2214*2621Sllai1 			return (ENOENT);
2215*2621Sllai1 		}
2216*2621Sllai1 	}
2217*2621Sllai1 
2218*2621Sllai1 
2219*2621Sllai1 	/* first thread that is doing the lookup on this node */
2220*2621Sllai1 	if (!dv) {
2221*2621Sllai1 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
2222*2621Sllai1 			rw_exit(&ddv->sdev_contents);
2223*2621Sllai1 			rw_enter(&ddv->sdev_contents, RW_WRITER);
2224*2621Sllai1 		}
2225*2621Sllai1 		error = sdev_mknode(ddv, nm, &dv, NULL, NULL, NULL,
2226*2621Sllai1 		    cred, SDEV_INIT);
2227*2621Sllai1 		if (!dv) {
2228*2621Sllai1 			rw_exit(&ddv->sdev_contents);
2229*2621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2230*2621Sllai1 			sdev_lookup_failed(ddv, nm, failed_flags);
2231*2621Sllai1 			*vpp = NULLVP;
2232*2621Sllai1 			return (ENOENT);
2233*2621Sllai1 		}
2234*2621Sllai1 		rw_downgrade(&ddv->sdev_contents);
2235*2621Sllai1 	}
2236*2621Sllai1 	ASSERT(dv);
2237*2621Sllai1 	ASSERT(SDEV_HELD(dv));
2238*2621Sllai1 
2239*2621Sllai1 	if (SDEV_IS_NO_NCACHE(dv)) {
2240*2621Sllai1 		failed_flags |= SLF_NO_NCACHE;
2241*2621Sllai1 	}
2242*2621Sllai1 
2243*2621Sllai1 	if (SDEV_IS_GLOBAL(ddv)) {
2244*2621Sllai1 		map = sdev_get_map(ddv, 1);
2245*2621Sllai1 		dirops = map ? map->dir_ops : NULL;
2246*2621Sllai1 		fn = dirops ? dirops->devnops_lookup : NULL;
2247*2621Sllai1 	}
2248*2621Sllai1 
2249*2621Sllai1 	/*
2250*2621Sllai1 	 * (b1) invoking devfsadm once per life time for devfsadm nodes
2251*2621Sllai1 	 */
2252*2621Sllai1 	if ((fn == NULL) && !callback) {
2253*2621Sllai1 
2254*2621Sllai1 		if (sdev_reconfig_boot || !i_ddi_io_initialized() ||
2255*2621Sllai1 		    SDEV_IS_DYNAMIC(ddv) || SDEV_IS_NO_NCACHE(dv) ||
2256*2621Sllai1 		    ((moddebug & MODDEBUG_FINI_EBUSY) != 0)) {
2257*2621Sllai1 			ASSERT(SDEV_HELD(dv));
2258*2621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2259*2621Sllai1 			goto nolock_notfound;
2260*2621Sllai1 		}
2261*2621Sllai1 
2262*2621Sllai1 		/*
2263*2621Sllai1 		 * filter out known non-existent devices recorded
2264*2621Sllai1 		 * during initial reconfiguration boot for which
2265*2621Sllai1 		 * reconfig should not be done and lookup may
2266*2621Sllai1 		 * be short-circuited now.
2267*2621Sllai1 		 */
2268*2621Sllai1 		if (sdev_lookup_filter(ddv, nm)) {
2269*2621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2270*2621Sllai1 			goto nolock_notfound;
2271*2621Sllai1 		}
2272*2621Sllai1 
2273*2621Sllai1 		/* bypassing devfsadm internal nodes */
2274*2621Sllai1 		if (is_devfsadm_thread(lookup_thread)) {
2275*2621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2276*2621Sllai1 			goto nolock_notfound;
2277*2621Sllai1 		}
2278*2621Sllai1 
2279*2621Sllai1 		if (sdev_reconfig_disable) {
2280*2621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2281*2621Sllai1 			goto nolock_notfound;
2282*2621Sllai1 		}
2283*2621Sllai1 
2284*2621Sllai1 		error = sdev_call_devfsadmd(ddv, dv, nm);
2285*2621Sllai1 		if (error == 0) {
2286*2621Sllai1 			sdcmn_err8(("lookup of %s/%s by %s: reconfig\n",
2287*2621Sllai1 			    ddv->sdev_name, nm, curproc->p_user.u_comm));
2288*2621Sllai1 			if (sdev_reconfig_verbose) {
2289*2621Sllai1 				cmn_err(CE_CONT,
2290*2621Sllai1 				    "?lookup of %s/%s by %s: reconfig\n",
2291*2621Sllai1 				    ddv->sdev_name, nm, curproc->p_user.u_comm);
2292*2621Sllai1 			}
2293*2621Sllai1 			retried = 1;
2294*2621Sllai1 			failed_flags |= SLF_REBUILT;
2295*2621Sllai1 			ASSERT(dv->sdev_state != SDEV_ZOMBIE);
2296*2621Sllai1 			SDEV_SIMPLE_RELE(dv);
2297*2621Sllai1 			goto tryagain;
2298*2621Sllai1 		} else {
2299*2621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2300*2621Sllai1 			goto nolock_notfound;
2301*2621Sllai1 		}
2302*2621Sllai1 	}
2303*2621Sllai1 
2304*2621Sllai1 	/*
2305*2621Sllai1 	 * (b2) Directory Based Name Resolution (DBNR):
2306*2621Sllai1 	 *	ddv	- parent
2307*2621Sllai1 	 *	nm	- /dev/(ddv->sdev_name)/nm
2308*2621Sllai1 	 *
2309*2621Sllai1 	 *	note: module vnode ops take precedence than the build-in ones
2310*2621Sllai1 	 */
2311*2621Sllai1 	if (fn) {
2312*2621Sllai1 		error = sdev_call_modulelookup(ddv, &dv, nm, fn, cred);
2313*2621Sllai1 		if (error) {
2314*2621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2315*2621Sllai1 			goto notfound;
2316*2621Sllai1 		} else {
2317*2621Sllai1 			goto found;
2318*2621Sllai1 		}
2319*2621Sllai1 	} else if (callback) {
2320*2621Sllai1 		error = sdev_call_dircallback(ddv, &dv, nm, callback,
2321*2621Sllai1 		    flags, cred);
2322*2621Sllai1 		if (error == 0) {
2323*2621Sllai1 			goto found;
2324*2621Sllai1 		} else {
2325*2621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2326*2621Sllai1 			goto notfound;
2327*2621Sllai1 		}
2328*2621Sllai1 	}
2329*2621Sllai1 	ASSERT(rvp);
2330*2621Sllai1 
2331*2621Sllai1 found:
2332*2621Sllai1 	ASSERT(!(dv->sdev_flags & SDEV_STALE));
2333*2621Sllai1 	ASSERT(dv->sdev_state == SDEV_READY);
2334*2621Sllai1 	if (vtor) {
2335*2621Sllai1 		/*
2336*2621Sllai1 		 * Check validity of returned node
2337*2621Sllai1 		 */
2338*2621Sllai1 		switch (vtor(dv)) {
2339*2621Sllai1 		case SDEV_VTOR_VALID:
2340*2621Sllai1 			break;
2341*2621Sllai1 		case SDEV_VTOR_INVALID:
2342*2621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2343*2621Sllai1 			sdcmn_err7(("lookup: destroy invalid "
2344*2621Sllai1 			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
2345*2621Sllai1 			goto nolock_notfound;
2346*2621Sllai1 		case SDEV_VTOR_SKIP:
2347*2621Sllai1 			sdcmn_err7(("lookup: node not applicable - "
2348*2621Sllai1 			    "skipping: %s(%p)\n", dv->sdev_name, (void *)dv));
2349*2621Sllai1 			rw_exit(&ddv->sdev_contents);
2350*2621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2351*2621Sllai1 			SDEV_RELE(dv);
2352*2621Sllai1 			goto lookup_failed;
2353*2621Sllai1 		default:
2354*2621Sllai1 			cmn_err(CE_PANIC,
2355*2621Sllai1 			    "dev fs: validator failed: %s(%p)\n",
2356*2621Sllai1 			    dv->sdev_name, (void *)dv);
2357*2621Sllai1 			break;
2358*2621Sllai1 			/*NOTREACHED*/
2359*2621Sllai1 		}
2360*2621Sllai1 	}
2361*2621Sllai1 
2362*2621Sllai1 	if ((SDEVTOV(dv)->v_type == VDIR) && SDEV_IS_GLOBAL(dv)) {
2363*2621Sllai1 		rw_enter(&dv->sdev_contents, RW_READER);
2364*2621Sllai1 		(void) sdev_get_map(dv, 1);
2365*2621Sllai1 		rw_exit(&dv->sdev_contents);
2366*2621Sllai1 	}
2367*2621Sllai1 	rw_exit(&ddv->sdev_contents);
2368*2621Sllai1 	rv = sdev_to_vp(dv, vpp);
2369*2621Sllai1 	sdcmn_err3(("devname_lookup_func: returning vp %p v_count %d state %d "
2370*2621Sllai1 	    "for nm %s, error %d\n", (void *)*vpp, (*vpp)->v_count,
2371*2621Sllai1 	    dv->sdev_state, nm, rv));
2372*2621Sllai1 	return (rv);
2373*2621Sllai1 
2374*2621Sllai1 notfound:
2375*2621Sllai1 	mutex_enter(&dv->sdev_lookup_lock);
2376*2621Sllai1 	SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
2377*2621Sllai1 	mutex_exit(&dv->sdev_lookup_lock);
2378*2621Sllai1 nolock_notfound:
2379*2621Sllai1 	/*
2380*2621Sllai1 	 * Destroy the node that is created for synchronization purposes.
2381*2621Sllai1 	 */
2382*2621Sllai1 	sdcmn_err3(("devname_lookup_func: %s with state %d\n",
2383*2621Sllai1 	    nm, dv->sdev_state));
2384*2621Sllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
2385*2621Sllai1 	if (dv->sdev_state == SDEV_INIT) {
2386*2621Sllai1 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
2387*2621Sllai1 			rw_exit(&ddv->sdev_contents);
2388*2621Sllai1 			rw_enter(&ddv->sdev_contents, RW_WRITER);
2389*2621Sllai1 		}
2390*2621Sllai1 
2391*2621Sllai1 		/*
2392*2621Sllai1 		 * Node state may have changed during the lock
2393*2621Sllai1 		 * changes. Re-check.
2394*2621Sllai1 		 */
2395*2621Sllai1 		if (dv->sdev_state == SDEV_INIT) {
2396*2621Sllai1 			(void) sdev_dirdelete(ddv, dv);
2397*2621Sllai1 			rw_exit(&ddv->sdev_contents);
2398*2621Sllai1 			sdev_lookup_failed(ddv, nm, failed_flags);
2399*2621Sllai1 			*vpp = NULL;
2400*2621Sllai1 			return (ENOENT);
2401*2621Sllai1 		}
2402*2621Sllai1 	}
2403*2621Sllai1 
2404*2621Sllai1 	rw_exit(&ddv->sdev_contents);
2405*2621Sllai1 	SDEV_RELE(dv);
2406*2621Sllai1 
2407*2621Sllai1 lookup_failed:
2408*2621Sllai1 	sdev_lookup_failed(ddv, nm, failed_flags);
2409*2621Sllai1 	*vpp = NULL;
2410*2621Sllai1 	return (ENOENT);
2411*2621Sllai1 }
2412*2621Sllai1 
2413*2621Sllai1 /*
2414*2621Sllai1  * Given a directory node, mark all nodes beneath as
2415*2621Sllai1  * STALE, i.e. nodes that don't exist as far as new
2416*2621Sllai1  * consumers are concerned
2417*2621Sllai1  */
2418*2621Sllai1 void
2419*2621Sllai1 sdev_stale(struct sdev_node *ddv)
2420*2621Sllai1 {
2421*2621Sllai1 	struct sdev_node *dv;
2422*2621Sllai1 	struct vnode *vp;
2423*2621Sllai1 
2424*2621Sllai1 	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
2425*2621Sllai1 
2426*2621Sllai1 	rw_enter(&ddv->sdev_contents, RW_WRITER);
2427*2621Sllai1 	for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next) {
2428*2621Sllai1 		vp = SDEVTOV(dv);
2429*2621Sllai1 		if (vp->v_type == VDIR)
2430*2621Sllai1 			sdev_stale(dv);
2431*2621Sllai1 
2432*2621Sllai1 		sdcmn_err9(("sdev_stale: setting stale %s\n",
2433*2621Sllai1 		    dv->sdev_name));
2434*2621Sllai1 		dv->sdev_flags |= SDEV_STALE;
2435*2621Sllai1 	}
2436*2621Sllai1 	ddv->sdev_flags |= SDEV_BUILD;
2437*2621Sllai1 	rw_exit(&ddv->sdev_contents);
2438*2621Sllai1 }
2439*2621Sllai1 
2440*2621Sllai1 /*
2441*2621Sllai1  * Given a directory node, clean out all the nodes beneath.
2442*2621Sllai1  * If expr is specified, clean node with names matching expr.
2443*2621Sllai1  * If SDEV_ENFORCE is specified in flags, busy nodes are made stale,
2444*2621Sllai1  *	so they are excluded from future lookups.
2445*2621Sllai1  */
2446*2621Sllai1 int
2447*2621Sllai1 sdev_cleandir(struct sdev_node *ddv, char *expr, uint_t flags)
2448*2621Sllai1 {
2449*2621Sllai1 	int error = 0;
2450*2621Sllai1 	int busy = 0;
2451*2621Sllai1 	struct vnode *vp;
2452*2621Sllai1 	struct sdev_node *dv, *next = NULL;
2453*2621Sllai1 	int bkstore = 0;
2454*2621Sllai1 	int len = 0;
2455*2621Sllai1 	char *bks_name = NULL;
2456*2621Sllai1 
2457*2621Sllai1 	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
2458*2621Sllai1 
2459*2621Sllai1 	/*
2460*2621Sllai1 	 * We try our best to destroy all unused sdev_node's
2461*2621Sllai1 	 */
2462*2621Sllai1 	rw_enter(&ddv->sdev_contents, RW_WRITER);
2463*2621Sllai1 	for (dv = ddv->sdev_dot; dv; dv = next) {
2464*2621Sllai1 		next = dv->sdev_next;
2465*2621Sllai1 		vp = SDEVTOV(dv);
2466*2621Sllai1 
2467*2621Sllai1 		if (expr && gmatch(dv->sdev_name, expr) == 0)
2468*2621Sllai1 			continue;
2469*2621Sllai1 
2470*2621Sllai1 		if (vp->v_type == VDIR &&
2471*2621Sllai1 		    sdev_cleandir(dv, NULL, flags) != 0) {
2472*2621Sllai1 			sdcmn_err9(("sdev_cleandir: dir %s busy\n",
2473*2621Sllai1 			    dv->sdev_name));
2474*2621Sllai1 			busy++;
2475*2621Sllai1 			continue;
2476*2621Sllai1 		}
2477*2621Sllai1 
2478*2621Sllai1 		if (vp->v_count > 0 && (flags & SDEV_ENFORCE) == 0) {
2479*2621Sllai1 			sdcmn_err9(("sdev_cleandir: dir %s busy\n",
2480*2621Sllai1 			    dv->sdev_name));
2481*2621Sllai1 			busy++;
2482*2621Sllai1 			continue;
2483*2621Sllai1 		}
2484*2621Sllai1 
2485*2621Sllai1 		/*
2486*2621Sllai1 		 * at this point, either dv is not held or SDEV_ENFORCE
2487*2621Sllai1 		 * is specified. In either case, dv needs to be deleted
2488*2621Sllai1 		 */
2489*2621Sllai1 		SDEV_HOLD(dv);
2490*2621Sllai1 
2491*2621Sllai1 		bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0;
2492*2621Sllai1 		if (bkstore && (vp->v_type == VDIR))
2493*2621Sllai1 			bkstore += 1;
2494*2621Sllai1 
2495*2621Sllai1 		if (bkstore) {
2496*2621Sllai1 			len = strlen(dv->sdev_name) + 1;
2497*2621Sllai1 			bks_name = kmem_alloc(len, KM_SLEEP);
2498*2621Sllai1 			bcopy(dv->sdev_name, bks_name, len);
2499*2621Sllai1 		}
2500*2621Sllai1 
2501*2621Sllai1 		error = sdev_dirdelete(ddv, dv);
2502*2621Sllai1 
2503*2621Sllai1 		if (error == EBUSY) {
2504*2621Sllai1 			sdcmn_err9(("sdev_cleandir: dir busy\n"));
2505*2621Sllai1 			busy++;
2506*2621Sllai1 		}
2507*2621Sllai1 
2508*2621Sllai1 		/* take care the backing store clean up */
2509*2621Sllai1 		if (bkstore && (error == 0)) {
2510*2621Sllai1 			ASSERT(bks_name);
2511*2621Sllai1 			ASSERT(ddv->sdev_attrvp);
2512*2621Sllai1 
2513*2621Sllai1 			if (bkstore == 1) {
2514*2621Sllai1 				error = VOP_REMOVE(ddv->sdev_attrvp,
2515*2621Sllai1 				    bks_name, kcred);
2516*2621Sllai1 			} else if (bkstore == 2) {
2517*2621Sllai1 				error = VOP_RMDIR(ddv->sdev_attrvp,
2518*2621Sllai1 				    bks_name, ddv->sdev_attrvp, kcred);
2519*2621Sllai1 			}
2520*2621Sllai1 
2521*2621Sllai1 			/* do not propagate the backing store errors */
2522*2621Sllai1 			if (error) {
2523*2621Sllai1 				sdcmn_err9(("sdev_cleandir: backing store"
2524*2621Sllai1 				    "not cleaned\n"));
2525*2621Sllai1 				error = 0;
2526*2621Sllai1 			}
2527*2621Sllai1 
2528*2621Sllai1 			bkstore = 0;
2529*2621Sllai1 			kmem_free(bks_name, len);
2530*2621Sllai1 			bks_name = NULL;
2531*2621Sllai1 			len = 0;
2532*2621Sllai1 		}
2533*2621Sllai1 	}
2534*2621Sllai1 
2535*2621Sllai1 	ddv->sdev_flags |= SDEV_BUILD;
2536*2621Sllai1 	rw_exit(&ddv->sdev_contents);
2537*2621Sllai1 
2538*2621Sllai1 	if (busy) {
2539*2621Sllai1 		error = EBUSY;
2540*2621Sllai1 	}
2541*2621Sllai1 
2542*2621Sllai1 	return (error);
2543*2621Sllai1 }
2544*2621Sllai1 
2545*2621Sllai1 /*
2546*2621Sllai1  * a convenient wrapper for readdir() funcs
2547*2621Sllai1  */
2548*2621Sllai1 size_t
2549*2621Sllai1 add_dir_entry(dirent64_t *de, char *nm, size_t size, ino_t ino, offset_t off)
2550*2621Sllai1 {
2551*2621Sllai1 	size_t reclen = DIRENT64_RECLEN(strlen(nm));
2552*2621Sllai1 	if (reclen > size)
2553*2621Sllai1 		return (0);
2554*2621Sllai1 
2555*2621Sllai1 	de->d_ino = (ino64_t)ino;
2556*2621Sllai1 	de->d_off = (off64_t)off + 1;
2557*2621Sllai1 	de->d_reclen = (ushort_t)reclen;
2558*2621Sllai1 	(void) strncpy(de->d_name, nm, DIRENT64_NAMELEN(reclen));
2559*2621Sllai1 	return (reclen);
2560*2621Sllai1 }
2561*2621Sllai1 
2562*2621Sllai1 /*
2563*2621Sllai1  * sdev_mount service routines
2564*2621Sllai1  */
2565*2621Sllai1 int
2566*2621Sllai1 sdev_copyin_mountargs(struct mounta *uap, struct sdev_mountargs *args)
2567*2621Sllai1 {
2568*2621Sllai1 	int	error;
2569*2621Sllai1 
2570*2621Sllai1 	if (uap->datalen != sizeof (*args))
2571*2621Sllai1 		return (EINVAL);
2572*2621Sllai1 
2573*2621Sllai1 	if (error = copyin(uap->dataptr, args, sizeof (*args))) {
2574*2621Sllai1 		cmn_err(CE_WARN, "sdev_copyin_mountargs: can not"
2575*2621Sllai1 		    "get user data. error %d\n", error);
2576*2621Sllai1 		return (EFAULT);
2577*2621Sllai1 	}
2578*2621Sllai1 
2579*2621Sllai1 	return (0);
2580*2621Sllai1 }
2581*2621Sllai1 
2582*2621Sllai1 #ifdef nextdp
2583*2621Sllai1 #undef nextdp
2584*2621Sllai1 #endif
2585*2621Sllai1 #define	nextdp(dp)	((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
2586*2621Sllai1 
2587*2621Sllai1 /*
2588*2621Sllai1  * readdir helper func
2589*2621Sllai1  */
2590*2621Sllai1 int
2591*2621Sllai1 devname_readdir_func(vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp,
2592*2621Sllai1     int flags)
2593*2621Sllai1 {
2594*2621Sllai1 	struct sdev_node *ddv = VTOSDEV(vp);
2595*2621Sllai1 	struct sdev_node *dv;
2596*2621Sllai1 	dirent64_t	*dp;
2597*2621Sllai1 	ulong_t		outcount = 0;
2598*2621Sllai1 	size_t		namelen;
2599*2621Sllai1 	ulong_t		alloc_count;
2600*2621Sllai1 	void		*outbuf;
2601*2621Sllai1 	struct iovec	*iovp;
2602*2621Sllai1 	int		error = 0;
2603*2621Sllai1 	size_t		reclen;
2604*2621Sllai1 	offset_t	diroff;
2605*2621Sllai1 	offset_t	soff;
2606*2621Sllai1 	int		this_reclen;
2607*2621Sllai1 	struct devname_nsmap	*map = NULL;
2608*2621Sllai1 	struct devname_ops	*dirops = NULL;
2609*2621Sllai1 	int (*fn)(devname_handle_t *, struct cred *) = NULL;
2610*2621Sllai1 	int (*vtor)(struct sdev_node *) = NULL;
2611*2621Sllai1 	struct vattr attr;
2612*2621Sllai1 	timestruc_t now;
2613*2621Sllai1 
2614*2621Sllai1 	ASSERT(ddv->sdev_attr || ddv->sdev_attrvp);
2615*2621Sllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
2616*2621Sllai1 
2617*2621Sllai1 	if (uiop->uio_loffset >= MAXOFF_T) {
2618*2621Sllai1 		if (eofp)
2619*2621Sllai1 			*eofp = 1;
2620*2621Sllai1 		return (0);
2621*2621Sllai1 	}
2622*2621Sllai1 
2623*2621Sllai1 	if (uiop->uio_iovcnt != 1)
2624*2621Sllai1 		return (EINVAL);
2625*2621Sllai1 
2626*2621Sllai1 	if (vp->v_type != VDIR)
2627*2621Sllai1 		return (ENOTDIR);
2628*2621Sllai1 
2629*2621Sllai1 	if (ddv->sdev_flags & SDEV_VTOR) {
2630*2621Sllai1 		vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
2631*2621Sllai1 		ASSERT(vtor);
2632*2621Sllai1 	}
2633*2621Sllai1 
2634*2621Sllai1 	if (eofp != NULL)
2635*2621Sllai1 		*eofp = 0;
2636*2621Sllai1 
2637*2621Sllai1 	soff = uiop->uio_offset;
2638*2621Sllai1 	iovp = uiop->uio_iov;
2639*2621Sllai1 	alloc_count = iovp->iov_len;
2640*2621Sllai1 	dp = outbuf = kmem_alloc(alloc_count, KM_SLEEP);
2641*2621Sllai1 	outcount = 0;
2642*2621Sllai1 
2643*2621Sllai1 	if (ddv->sdev_state == SDEV_ZOMBIE)
2644*2621Sllai1 		goto get_cache;
2645*2621Sllai1 
2646*2621Sllai1 	if (!SDEV_IS_GLOBAL(ddv)) {
2647*2621Sllai1 		/* make sure directory content is up to date */
2648*2621Sllai1 		prof_filldir(ddv);
2649*2621Sllai1 	} else {
2650*2621Sllai1 		map = sdev_get_map(ddv, 0);
2651*2621Sllai1 		dirops = map ? map->dir_ops : NULL;
2652*2621Sllai1 		fn = dirops ? dirops->devnops_readdir : NULL;
2653*2621Sllai1 
2654*2621Sllai1 		if (map && map->dir_map) {
2655*2621Sllai1 			/*
2656*2621Sllai1 			 * load the name mapping rule database
2657*2621Sllai1 			 * through invoking devfsadm and symlink
2658*2621Sllai1 			 * all the entries in the map
2659*2621Sllai1 			 */
2660*2621Sllai1 			devname_rdr_result_t rdr_result;
2661*2621Sllai1 			int do_thread = 0;
2662*2621Sllai1 
2663*2621Sllai1 			rw_enter(&map->dir_lock, RW_READER);
2664*2621Sllai1 			do_thread = map->dir_maploaded ? 0 : 1;
2665*2621Sllai1 			rw_exit(&map->dir_lock);
2666*2621Sllai1 
2667*2621Sllai1 			if (do_thread) {
2668*2621Sllai1 				mutex_enter(&ddv->sdev_lookup_lock);
2669*2621Sllai1 				SDEV_BLOCK_OTHERS(ddv, SDEV_READDIR);
2670*2621Sllai1 				mutex_exit(&ddv->sdev_lookup_lock);
2671*2621Sllai1 
2672*2621Sllai1 				sdev_dispatch_to_nsrdr_thread(ddv,
2673*2621Sllai1 				    map->dir_map, &rdr_result);
2674*2621Sllai1 			}
2675*2621Sllai1 		} else if ((sdev_boot_state == SDEV_BOOT_STATE_COMPLETE) &&
2676*2621Sllai1 		    !sdev_reconfig_boot && (flags & SDEV_BROWSE) &&
2677*2621Sllai1 		    !SDEV_IS_DYNAMIC(ddv) && !SDEV_IS_NO_NCACHE(ddv) &&
2678*2621Sllai1 		    ((moddebug & MODDEBUG_FINI_EBUSY) == 0) &&
2679*2621Sllai1 		    !DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state) &&
2680*2621Sllai1 		    !DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
2681*2621Sllai1 		    !sdev_reconfig_disable) {
2682*2621Sllai1 			/*
2683*2621Sllai1 			 * invoking "devfsadm" to do system device reconfig
2684*2621Sllai1 			 */
2685*2621Sllai1 			mutex_enter(&ddv->sdev_lookup_lock);
2686*2621Sllai1 			SDEV_BLOCK_OTHERS(ddv,
2687*2621Sllai1 			    (SDEV_READDIR|SDEV_LGWAITING));
2688*2621Sllai1 			mutex_exit(&ddv->sdev_lookup_lock);
2689*2621Sllai1 
2690*2621Sllai1 			sdcmn_err8(("readdir of %s by %s: reconfig\n",
2691*2621Sllai1 			    ddv->sdev_path, curproc->p_user.u_comm));
2692*2621Sllai1 			if (sdev_reconfig_verbose) {
2693*2621Sllai1 				cmn_err(CE_CONT,
2694*2621Sllai1 				    "?readdir of %s by %s: reconfig\n",
2695*2621Sllai1 				    ddv->sdev_path, curproc->p_user.u_comm);
2696*2621Sllai1 			}
2697*2621Sllai1 
2698*2621Sllai1 			sdev_devfsadmd_thread(ddv, NULL, kcred);
2699*2621Sllai1 		} else if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) {
2700*2621Sllai1 			/*
2701*2621Sllai1 			 * compensate the "ls" started later than "devfsadm"
2702*2621Sllai1 			 */
2703*2621Sllai1 			mutex_enter(&ddv->sdev_lookup_lock);
2704*2621Sllai1 			SDEV_BLOCK_OTHERS(ddv, (SDEV_READDIR|SDEV_LGWAITING));
2705*2621Sllai1 			mutex_exit(&ddv->sdev_lookup_lock);
2706*2621Sllai1 		}
2707*2621Sllai1 
2708*2621Sllai1 		/*
2709*2621Sllai1 		 * release the contents lock so that
2710*2621Sllai1 		 * the cache maybe updated by devfsadmd
2711*2621Sllai1 		 */
2712*2621Sllai1 		rw_exit(&ddv->sdev_contents);
2713*2621Sllai1 		mutex_enter(&ddv->sdev_lookup_lock);
2714*2621Sllai1 		if (SDEV_IS_READDIR(ddv))
2715*2621Sllai1 			(void) sdev_wait4lookup(ddv, SDEV_READDIR);
2716*2621Sllai1 		mutex_exit(&ddv->sdev_lookup_lock);
2717*2621Sllai1 		rw_enter(&ddv->sdev_contents, RW_READER);
2718*2621Sllai1 
2719*2621Sllai1 		sdcmn_err4(("readdir of directory %s by %s\n",
2720*2621Sllai1 		    ddv->sdev_name, curproc->p_user.u_comm));
2721*2621Sllai1 		while (ddv->sdev_flags & SDEV_BUILD) {
2722*2621Sllai1 			if (SDEV_IS_PERSIST(ddv)) {
2723*2621Sllai1 				error = sdev_filldir_from_store(ddv,
2724*2621Sllai1 				    alloc_count, cred);
2725*2621Sllai1 			}
2726*2621Sllai1 
2727*2621Sllai1 			/*
2728*2621Sllai1 			 * pre-creating the directories
2729*2621Sllai1 			 * defined in vtab
2730*2621Sllai1 			 */
2731*2621Sllai1 			if (SDEVTOV(ddv)->v_flag & VROOT) {
2732*2621Sllai1 				error = sdev_filldir_dynamic(ddv);
2733*2621Sllai1 			}
2734*2621Sllai1 
2735*2621Sllai1 			if (!error)
2736*2621Sllai1 				ddv->sdev_flags &= ~SDEV_BUILD;
2737*2621Sllai1 		}
2738*2621Sllai1 	}
2739*2621Sllai1 
2740*2621Sllai1 get_cache:
2741*2621Sllai1 	/* handle "." and ".." */
2742*2621Sllai1 	diroff = 0;
2743*2621Sllai1 	if (soff == 0) {
2744*2621Sllai1 		/* first time */
2745*2621Sllai1 		this_reclen = DIRENT64_RECLEN(1);
2746*2621Sllai1 		if (alloc_count < this_reclen) {
2747*2621Sllai1 			error = EINVAL;
2748*2621Sllai1 			goto done;
2749*2621Sllai1 		}
2750*2621Sllai1 
2751*2621Sllai1 		dp->d_ino = (ino64_t)ddv->sdev_ino;
2752*2621Sllai1 		dp->d_off = (off64_t)1;
2753*2621Sllai1 		dp->d_reclen = (ushort_t)this_reclen;
2754*2621Sllai1 
2755*2621Sllai1 		(void) strncpy(dp->d_name, ".",
2756*2621Sllai1 		    DIRENT64_NAMELEN(this_reclen));
2757*2621Sllai1 		outcount += dp->d_reclen;
2758*2621Sllai1 		dp = nextdp(dp);
2759*2621Sllai1 	}
2760*2621Sllai1 
2761*2621Sllai1 	diroff++;
2762*2621Sllai1 	if (soff <= 1) {
2763*2621Sllai1 		this_reclen = DIRENT64_RECLEN(2);
2764*2621Sllai1 		if (alloc_count < outcount + this_reclen) {
2765*2621Sllai1 			error = EINVAL;
2766*2621Sllai1 			goto done;
2767*2621Sllai1 		}
2768*2621Sllai1 
2769*2621Sllai1 		dp->d_reclen = (ushort_t)this_reclen;
2770*2621Sllai1 		dp->d_ino = (ino64_t)ddv->sdev_dotdot->sdev_ino;
2771*2621Sllai1 		dp->d_off = (off64_t)2;
2772*2621Sllai1 
2773*2621Sllai1 		(void) strncpy(dp->d_name, "..",
2774*2621Sllai1 		    DIRENT64_NAMELEN(this_reclen));
2775*2621Sllai1 		outcount += dp->d_reclen;
2776*2621Sllai1 
2777*2621Sllai1 		dp = nextdp(dp);
2778*2621Sllai1 	}
2779*2621Sllai1 
2780*2621Sllai1 
2781*2621Sllai1 	/* gets the cache */
2782*2621Sllai1 	diroff++;
2783*2621Sllai1 	for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next, diroff++) {
2784*2621Sllai1 		sdcmn_err3(("sdev_readdir: diroff %lld soff %lld for '%s' \n",
2785*2621Sllai1 		    diroff, soff, dv->sdev_name));
2786*2621Sllai1 
2787*2621Sllai1 		/* bypassing pre-matured nodes */
2788*2621Sllai1 		if (diroff < soff || (dv->sdev_state != SDEV_READY)) {
2789*2621Sllai1 			sdcmn_err3(("sdev_readdir: pre-mature node  "
2790*2621Sllai1 			    "%s\n", dv->sdev_name));
2791*2621Sllai1 			continue;
2792*2621Sllai1 		}
2793*2621Sllai1 
2794*2621Sllai1 		/* don't list stale nodes */
2795*2621Sllai1 		if (dv->sdev_flags & SDEV_STALE) {
2796*2621Sllai1 			sdcmn_err4(("sdev_readdir: STALE node  "
2797*2621Sllai1 			    "%s\n", dv->sdev_name));
2798*2621Sllai1 			continue;
2799*2621Sllai1 		}
2800*2621Sllai1 
2801*2621Sllai1 		/*
2802*2621Sllai1 		 * Check validity of node
2803*2621Sllai1 		 */
2804*2621Sllai1 		if (vtor) {
2805*2621Sllai1 			switch (vtor(dv)) {
2806*2621Sllai1 			case SDEV_VTOR_VALID:
2807*2621Sllai1 				break;
2808*2621Sllai1 			case SDEV_VTOR_INVALID:
2809*2621Sllai1 			case SDEV_VTOR_SKIP:
2810*2621Sllai1 				continue;
2811*2621Sllai1 			default:
2812*2621Sllai1 				cmn_err(CE_PANIC,
2813*2621Sllai1 				    "dev fs: validator failed: %s(%p)\n",
2814*2621Sllai1 				    dv->sdev_name, (void *)dv);
2815*2621Sllai1 				break;
2816*2621Sllai1 			/*NOTREACHED*/
2817*2621Sllai1 			}
2818*2621Sllai1 		}
2819*2621Sllai1 
2820*2621Sllai1 		/*
2821*2621Sllai1 		 * call back into the module for the validity/bookkeeping
2822*2621Sllai1 		 * of this entry
2823*2621Sllai1 		 */
2824*2621Sllai1 		if (fn) {
2825*2621Sllai1 			error = (*fn)(&(dv->sdev_handle), cred);
2826*2621Sllai1 			if (error) {
2827*2621Sllai1 				sdcmn_err4(("sdev_readdir: module did not "
2828*2621Sllai1 				    "validate %s\n", dv->sdev_name));
2829*2621Sllai1 				continue;
2830*2621Sllai1 			}
2831*2621Sllai1 		}
2832*2621Sllai1 
2833*2621Sllai1 		namelen = strlen(dv->sdev_name);
2834*2621Sllai1 		reclen = DIRENT64_RECLEN(namelen);
2835*2621Sllai1 		if (outcount + reclen > alloc_count) {
2836*2621Sllai1 			goto full;
2837*2621Sllai1 		}
2838*2621Sllai1 		dp->d_reclen = (ushort_t)reclen;
2839*2621Sllai1 		dp->d_ino = (ino64_t)dv->sdev_ino;
2840*2621Sllai1 		dp->d_off = (off64_t)diroff + 1;
2841*2621Sllai1 		(void) strncpy(dp->d_name, dv->sdev_name,
2842*2621Sllai1 		    DIRENT64_NAMELEN(reclen));
2843*2621Sllai1 		outcount += reclen;
2844*2621Sllai1 		dp = nextdp(dp);
2845*2621Sllai1 	}
2846*2621Sllai1 
2847*2621Sllai1 full:
2848*2621Sllai1 	sdcmn_err4(("sdev_readdir: moving %lu bytes: "
2849*2621Sllai1 	    "diroff %lld, soff %lld, dv %p\n", outcount, diroff, soff,
2850*2621Sllai1 	    (void *)dv));
2851*2621Sllai1 
2852*2621Sllai1 	if (outcount)
2853*2621Sllai1 		error = uiomove(outbuf, outcount, UIO_READ, uiop);
2854*2621Sllai1 
2855*2621Sllai1 	if (!error) {
2856*2621Sllai1 		uiop->uio_offset = diroff;
2857*2621Sllai1 		if (eofp)
2858*2621Sllai1 			*eofp = dv ? 0 : 1;
2859*2621Sllai1 	}
2860*2621Sllai1 
2861*2621Sllai1 
2862*2621Sllai1 	if (ddv->sdev_attrvp) {
2863*2621Sllai1 		gethrestime(&now);
2864*2621Sllai1 		attr.va_ctime = now;
2865*2621Sllai1 		attr.va_atime = now;
2866*2621Sllai1 		attr.va_mask = AT_CTIME|AT_ATIME;
2867*2621Sllai1 
2868*2621Sllai1 		(void) VOP_SETATTR(ddv->sdev_attrvp, &attr, 0, kcred, NULL);
2869*2621Sllai1 	}
2870*2621Sllai1 done:
2871*2621Sllai1 	kmem_free(outbuf, alloc_count);
2872*2621Sllai1 	return (error);
2873*2621Sllai1 }
2874*2621Sllai1 
2875*2621Sllai1 
2876*2621Sllai1 static int
2877*2621Sllai1 sdev_modctl_lookup(const char *path, vnode_t **r_vp)
2878*2621Sllai1 {
2879*2621Sllai1 	vnode_t *vp;
2880*2621Sllai1 	vnode_t *cvp;
2881*2621Sllai1 	struct sdev_node *svp;
2882*2621Sllai1 	char *nm;
2883*2621Sllai1 	struct pathname pn;
2884*2621Sllai1 	int error;
2885*2621Sllai1 	int persisted = 0;
2886*2621Sllai1 
2887*2621Sllai1 	if (error = pn_get((char *)path, UIO_SYSSPACE, &pn))
2888*2621Sllai1 		return (error);
2889*2621Sllai1 	nm = kmem_alloc(MAXNAMELEN, KM_SLEEP);
2890*2621Sllai1 
2891*2621Sllai1 	vp = rootdir;
2892*2621Sllai1 	VN_HOLD(vp);
2893*2621Sllai1 
2894*2621Sllai1 	while (pn_pathleft(&pn)) {
2895*2621Sllai1 		ASSERT(vp->v_type == VDIR);
2896*2621Sllai1 		(void) pn_getcomponent(&pn, nm);
2897*2621Sllai1 		error = VOP_LOOKUP(vp, nm, &cvp, NULL, 0, NULL, kcred);
2898*2621Sllai1 		VN_RELE(vp);
2899*2621Sllai1 
2900*2621Sllai1 		if (error)
2901*2621Sllai1 			break;
2902*2621Sllai1 
2903*2621Sllai1 		/* traverse mount points encountered on our journey */
2904*2621Sllai1 		if (vn_ismntpt(cvp) && (error = traverse(&cvp)) != 0) {
2905*2621Sllai1 			VN_RELE(cvp);
2906*2621Sllai1 			break;
2907*2621Sllai1 		}
2908*2621Sllai1 
2909*2621Sllai1 		/*
2910*2621Sllai1 		 * Direct the operation to the persisting filesystem
2911*2621Sllai1 		 * underlying /dev.  Bail if we encounter a
2912*2621Sllai1 		 * non-persistent dev entity here.
2913*2621Sllai1 		 */
2914*2621Sllai1 		if (cvp->v_vfsp->vfs_fstype == devtype) {
2915*2621Sllai1 
2916*2621Sllai1 			if ((VTOSDEV(cvp)->sdev_flags & SDEV_PERSIST) == 0) {
2917*2621Sllai1 				error = ENOENT;
2918*2621Sllai1 				VN_RELE(cvp);
2919*2621Sllai1 				break;
2920*2621Sllai1 			}
2921*2621Sllai1 
2922*2621Sllai1 			if (VTOSDEV(cvp) == NULL) {
2923*2621Sllai1 				error = ENOENT;
2924*2621Sllai1 				VN_RELE(cvp);
2925*2621Sllai1 				break;
2926*2621Sllai1 			}
2927*2621Sllai1 			svp = VTOSDEV(cvp);
2928*2621Sllai1 			if ((vp = svp->sdev_attrvp) == NULL) {
2929*2621Sllai1 				error = ENOENT;
2930*2621Sllai1 				VN_RELE(cvp);
2931*2621Sllai1 				break;
2932*2621Sllai1 			}
2933*2621Sllai1 			persisted = 1;
2934*2621Sllai1 			VN_HOLD(vp);
2935*2621Sllai1 			VN_RELE(cvp);
2936*2621Sllai1 			cvp = vp;
2937*2621Sllai1 		}
2938*2621Sllai1 
2939*2621Sllai1 		vp = cvp;
2940*2621Sllai1 		pn_skipslash(&pn);
2941*2621Sllai1 	}
2942*2621Sllai1 
2943*2621Sllai1 	kmem_free(nm, MAXNAMELEN);
2944*2621Sllai1 	pn_free(&pn);
2945*2621Sllai1 
2946*2621Sllai1 	if (error)
2947*2621Sllai1 		return (error);
2948*2621Sllai1 
2949*2621Sllai1 	/*
2950*2621Sllai1 	 * Only return persisted nodes in the filesystem underlying /dev.
2951*2621Sllai1 	 */
2952*2621Sllai1 	if (!persisted) {
2953*2621Sllai1 		VN_RELE(vp);
2954*2621Sllai1 		return (ENOENT);
2955*2621Sllai1 	}
2956*2621Sllai1 
2957*2621Sllai1 	*r_vp = vp;
2958*2621Sllai1 	return (0);
2959*2621Sllai1 }
2960*2621Sllai1 
2961*2621Sllai1 int
2962*2621Sllai1 sdev_modctl_readdir(const char *dir, char ***dirlistp,
2963*2621Sllai1 	int *npathsp, int *npathsp_alloc)
2964*2621Sllai1 {
2965*2621Sllai1 	char	**pathlist = NULL;
2966*2621Sllai1 	char	**newlist = NULL;
2967*2621Sllai1 	int	npaths = 0;
2968*2621Sllai1 	int	npaths_alloc = 0;
2969*2621Sllai1 	dirent64_t *dbuf = NULL;
2970*2621Sllai1 	int	n;
2971*2621Sllai1 	char	*s;
2972*2621Sllai1 	int error;
2973*2621Sllai1 	vnode_t *vp;
2974*2621Sllai1 	int eof;
2975*2621Sllai1 	struct iovec iov;
2976*2621Sllai1 	struct uio uio;
2977*2621Sllai1 	struct dirent64 *dp;
2978*2621Sllai1 	size_t dlen;
2979*2621Sllai1 	size_t dbuflen;
2980*2621Sllai1 	int ndirents = 64;
2981*2621Sllai1 	char *nm;
2982*2621Sllai1 
2983*2621Sllai1 	error = sdev_modctl_lookup(dir, &vp);
2984*2621Sllai1 	sdcmn_err11(("modctl readdir: %s by %s: %s\n",
2985*2621Sllai1 	    dir, curproc->p_user.u_comm,
2986*2621Sllai1 	    (error == 0) ? "ok" : "failed"));
2987*2621Sllai1 	if (error)
2988*2621Sllai1 		return (error);
2989*2621Sllai1 
2990*2621Sllai1 	dlen = ndirents * (sizeof (*dbuf));
2991*2621Sllai1 	dbuf = kmem_alloc(dlen, KM_SLEEP);
2992*2621Sllai1 
2993*2621Sllai1 	uio.uio_iov = &iov;
2994*2621Sllai1 	uio.uio_iovcnt = 1;
2995*2621Sllai1 	uio.uio_segflg = UIO_SYSSPACE;
2996*2621Sllai1 	uio.uio_fmode = 0;
2997*2621Sllai1 	uio.uio_extflg = UIO_COPY_CACHED;
2998*2621Sllai1 	uio.uio_loffset = 0;
2999*2621Sllai1 	uio.uio_llimit = MAXOFFSET_T;
3000*2621Sllai1 
3001*2621Sllai1 	eof = 0;
3002*2621Sllai1 	error = 0;
3003*2621Sllai1 	while (!error && !eof) {
3004*2621Sllai1 		uio.uio_resid = dlen;
3005*2621Sllai1 		iov.iov_base = (char *)dbuf;
3006*2621Sllai1 		iov.iov_len = dlen;
3007*2621Sllai1 
3008*2621Sllai1 		(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
3009*2621Sllai1 		error = VOP_READDIR(vp, &uio, kcred, &eof);
3010*2621Sllai1 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
3011*2621Sllai1 
3012*2621Sllai1 		dbuflen = dlen - uio.uio_resid;
3013*2621Sllai1 
3014*2621Sllai1 		if (error || dbuflen == 0)
3015*2621Sllai1 			break;
3016*2621Sllai1 
3017*2621Sllai1 		for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen);
3018*2621Sllai1 			dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
3019*2621Sllai1 
3020*2621Sllai1 			nm = dp->d_name;
3021*2621Sllai1 
3022*2621Sllai1 			if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0)
3023*2621Sllai1 				continue;
3024*2621Sllai1 
3025*2621Sllai1 			if (npaths == npaths_alloc) {
3026*2621Sllai1 				npaths_alloc += 64;
3027*2621Sllai1 				newlist = (char **)
3028*2621Sllai1 				    kmem_zalloc((npaths_alloc + 1) *
3029*2621Sllai1 					sizeof (char *), KM_SLEEP);
3030*2621Sllai1 				if (pathlist) {
3031*2621Sllai1 					bcopy(pathlist, newlist,
3032*2621Sllai1 					    npaths * sizeof (char *));
3033*2621Sllai1 					kmem_free(pathlist,
3034*2621Sllai1 					    (npaths + 1) * sizeof (char *));
3035*2621Sllai1 				}
3036*2621Sllai1 				pathlist = newlist;
3037*2621Sllai1 			}
3038*2621Sllai1 			n = strlen(nm) + 1;
3039*2621Sllai1 			s = kmem_alloc(n, KM_SLEEP);
3040*2621Sllai1 			bcopy(nm, s, n);
3041*2621Sllai1 			pathlist[npaths++] = s;
3042*2621Sllai1 			sdcmn_err11(("  %s/%s\n", dir, s));
3043*2621Sllai1 		}
3044*2621Sllai1 	}
3045*2621Sllai1 
3046*2621Sllai1 exit:
3047*2621Sllai1 	VN_RELE(vp);
3048*2621Sllai1 
3049*2621Sllai1 	if (dbuf)
3050*2621Sllai1 		kmem_free(dbuf, dlen);
3051*2621Sllai1 
3052*2621Sllai1 	if (error)
3053*2621Sllai1 		return (error);
3054*2621Sllai1 
3055*2621Sllai1 	*dirlistp = pathlist;
3056*2621Sllai1 	*npathsp = npaths;
3057*2621Sllai1 	*npathsp_alloc = npaths_alloc;
3058*2621Sllai1 
3059*2621Sllai1 	return (0);
3060*2621Sllai1 }
3061*2621Sllai1 
3062*2621Sllai1 void
3063*2621Sllai1 sdev_modctl_readdir_free(char **pathlist, int npaths, int npaths_alloc)
3064*2621Sllai1 {
3065*2621Sllai1 	int	i, n;
3066*2621Sllai1 
3067*2621Sllai1 	for (i = 0; i < npaths; i++) {
3068*2621Sllai1 		n = strlen(pathlist[i]) + 1;
3069*2621Sllai1 		kmem_free(pathlist[i], n);
3070*2621Sllai1 	}
3071*2621Sllai1 
3072*2621Sllai1 	kmem_free(pathlist, (npaths_alloc + 1) * sizeof (char *));
3073*2621Sllai1 }
3074*2621Sllai1 
3075*2621Sllai1 int
3076*2621Sllai1 sdev_modctl_devexists(const char *path)
3077*2621Sllai1 {
3078*2621Sllai1 	vnode_t *vp;
3079*2621Sllai1 	int error;
3080*2621Sllai1 
3081*2621Sllai1 	error = sdev_modctl_lookup(path, &vp);
3082*2621Sllai1 	sdcmn_err11(("modctl dev exists: %s by %s: %s\n",
3083*2621Sllai1 	    path, curproc->p_user.u_comm,
3084*2621Sllai1 	    (error == 0) ? "ok" : "failed"));
3085*2621Sllai1 	if (error == 0)
3086*2621Sllai1 		VN_RELE(vp);
3087*2621Sllai1 
3088*2621Sllai1 	return (error);
3089*2621Sllai1 }
3090*2621Sllai1 
3091*2621Sllai1 void
3092*2621Sllai1 sdev_update_newnsmap(struct devname_nsmap *map, char *module, char *mapname)
3093*2621Sllai1 {
3094*2621Sllai1 	rw_enter(&map->dir_lock, RW_WRITER);
3095*2621Sllai1 	if (module) {
3096*2621Sllai1 		ASSERT(map->dir_newmodule == NULL);
3097*2621Sllai1 		map->dir_newmodule = i_ddi_strdup(module, KM_SLEEP);
3098*2621Sllai1 	}
3099*2621Sllai1 	if (mapname) {
3100*2621Sllai1 		ASSERT(map->dir_newmap == NULL);
3101*2621Sllai1 		map->dir_newmap = i_ddi_strdup(mapname, KM_SLEEP);
3102*2621Sllai1 	}
3103*2621Sllai1 
3104*2621Sllai1 	map->dir_invalid = 1;
3105*2621Sllai1 	rw_exit(&map->dir_lock);
3106*2621Sllai1 }
3107*2621Sllai1 
3108*2621Sllai1 void
3109*2621Sllai1 sdev_replace_nsmap(struct devname_nsmap *map, char *module, char *mapname)
3110*2621Sllai1 {
3111*2621Sllai1 	char *old_module = NULL;
3112*2621Sllai1 	char *old_map = NULL;
3113*2621Sllai1 
3114*2621Sllai1 	ASSERT(RW_LOCK_HELD(&map->dir_lock));
3115*2621Sllai1 	if (!rw_tryupgrade(&map->dir_lock)) {
3116*2621Sllai1 		rw_exit(&map->dir_lock);
3117*2621Sllai1 		rw_enter(&map->dir_lock, RW_WRITER);
3118*2621Sllai1 	}
3119*2621Sllai1 
3120*2621Sllai1 	old_module = map->dir_module;
3121*2621Sllai1 	if (module) {
3122*2621Sllai1 		if (old_module && strcmp(old_module, module) != 0) {
3123*2621Sllai1 			kmem_free(old_module, strlen(old_module) + 1);
3124*2621Sllai1 		}
3125*2621Sllai1 		map->dir_module = module;
3126*2621Sllai1 		map->dir_newmodule = NULL;
3127*2621Sllai1 	}
3128*2621Sllai1 
3129*2621Sllai1 	old_map = map->dir_map;
3130*2621Sllai1 	if (mapname) {
3131*2621Sllai1 		if (old_map && strcmp(old_map, mapname) != 0) {
3132*2621Sllai1 			kmem_free(old_map, strlen(old_map) + 1);
3133*2621Sllai1 		}
3134*2621Sllai1 
3135*2621Sllai1 		map->dir_map = mapname;
3136*2621Sllai1 		map->dir_newmap = NULL;
3137*2621Sllai1 	}
3138*2621Sllai1 	map->dir_maploaded = 0;
3139*2621Sllai1 	map->dir_invalid = 0;
3140*2621Sllai1 	rw_downgrade(&map->dir_lock);
3141*2621Sllai1 }
3142*2621Sllai1 
3143*2621Sllai1 /*
3144*2621Sllai1  * dir_name should have at least one attribute,
3145*2621Sllai1  *	dir_module
3146*2621Sllai1  *	or dir_map
3147*2621Sllai1  *	or both
3148*2621Sllai1  * caller holds the devname_nsmaps_lock
3149*2621Sllai1  */
3150*2621Sllai1 void
3151*2621Sllai1 sdev_insert_nsmap(char *dir_name, char *dir_module, char *dir_map)
3152*2621Sllai1 {
3153*2621Sllai1 	struct devname_nsmap *map;
3154*2621Sllai1 	int len = 0;
3155*2621Sllai1 
3156*2621Sllai1 	ASSERT(dir_name);
3157*2621Sllai1 	ASSERT(dir_module || dir_map);
3158*2621Sllai1 	ASSERT(MUTEX_HELD(&devname_nsmaps_lock));
3159*2621Sllai1 
3160*2621Sllai1 	if (map = sdev_get_nsmap_by_dir(dir_name, 1)) {
3161*2621Sllai1 		sdev_update_newnsmap(map, dir_module, dir_map);
3162*2621Sllai1 		return;
3163*2621Sllai1 	}
3164*2621Sllai1 
3165*2621Sllai1 	map = (struct devname_nsmap *)kmem_zalloc(sizeof (*map), KM_SLEEP);
3166*2621Sllai1 	map->dir_name = i_ddi_strdup(dir_name, KM_SLEEP);
3167*2621Sllai1 	if (dir_module) {
3168*2621Sllai1 		map->dir_module = i_ddi_strdup(dir_module, KM_SLEEP);
3169*2621Sllai1 	}
3170*2621Sllai1 
3171*2621Sllai1 	if (dir_map) {
3172*2621Sllai1 		if (dir_map[0] != '/') {
3173*2621Sllai1 			len = strlen(ETC_DEV_DIR) + strlen(dir_map) + 2;
3174*2621Sllai1 			map->dir_map = kmem_zalloc(len, KM_SLEEP);
3175*2621Sllai1 			(void) snprintf(map->dir_map, len, "%s/%s", ETC_DEV_DIR,
3176*2621Sllai1 			    dir_map);
3177*2621Sllai1 		} else {
3178*2621Sllai1 			map->dir_map = i_ddi_strdup(dir_map, KM_SLEEP);
3179*2621Sllai1 		}
3180*2621Sllai1 	}
3181*2621Sllai1 
3182*2621Sllai1 	map->dir_ops = NULL;
3183*2621Sllai1 	map->dir_maploaded = 0;
3184*2621Sllai1 	map->dir_invalid = 0;
3185*2621Sllai1 	rw_init(&map->dir_lock, NULL, RW_DEFAULT, NULL);
3186*2621Sllai1 
3187*2621Sllai1 	map->next = devname_nsmaps;
3188*2621Sllai1 	map->prev = NULL;
3189*2621Sllai1 	if (devname_nsmaps) {
3190*2621Sllai1 		devname_nsmaps->prev = map;
3191*2621Sllai1 	}
3192*2621Sllai1 	devname_nsmaps = map;
3193*2621Sllai1 }
3194*2621Sllai1 
3195*2621Sllai1 struct devname_nsmap *
3196*2621Sllai1 sdev_get_nsmap_by_dir(char *dir_path, int locked)
3197*2621Sllai1 {
3198*2621Sllai1 	struct devname_nsmap *map = NULL;
3199*2621Sllai1 
3200*2621Sllai1 	if (!locked)
3201*2621Sllai1 		mutex_enter(&devname_nsmaps_lock);
3202*2621Sllai1 	for (map = devname_nsmaps; map; map = map->next) {
3203*2621Sllai1 		sdcmn_err6(("sdev_get_nsmap_by_dir: dir %s\n", map->dir_name));
3204*2621Sllai1 		if (strcmp(map->dir_name, dir_path) == 0) {
3205*2621Sllai1 			if (!locked)
3206*2621Sllai1 				mutex_exit(&devname_nsmaps_lock);
3207*2621Sllai1 			return (map);
3208*2621Sllai1 		}
3209*2621Sllai1 	}
3210*2621Sllai1 	if (!locked)
3211*2621Sllai1 		mutex_exit(&devname_nsmaps_lock);
3212*2621Sllai1 	return (NULL);
3213*2621Sllai1 }
3214*2621Sllai1 
3215*2621Sllai1 struct devname_nsmap *
3216*2621Sllai1 sdev_get_nsmap_by_module(char *mod_name)
3217*2621Sllai1 {
3218*2621Sllai1 	struct devname_nsmap *map = NULL;
3219*2621Sllai1 
3220*2621Sllai1 	mutex_enter(&devname_nsmaps_lock);
3221*2621Sllai1 	for (map = devname_nsmaps; map; map = map->next) {
3222*2621Sllai1 		sdcmn_err7(("sdev_get_nsmap_by_module: module %s\n",
3223*2621Sllai1 		    map->dir_module));
3224*2621Sllai1 		if (map->dir_module && strcmp(map->dir_module, mod_name) == 0) {
3225*2621Sllai1 			mutex_exit(&devname_nsmaps_lock);
3226*2621Sllai1 			return (map);
3227*2621Sllai1 		}
3228*2621Sllai1 	}
3229*2621Sllai1 	mutex_exit(&devname_nsmaps_lock);
3230*2621Sllai1 	return (NULL);
3231*2621Sllai1 }
3232*2621Sllai1 
3233*2621Sllai1 void
3234*2621Sllai1 sdev_invalidate_nsmaps()
3235*2621Sllai1 {
3236*2621Sllai1 	struct devname_nsmap *map = NULL;
3237*2621Sllai1 
3238*2621Sllai1 	ASSERT(MUTEX_HELD(&devname_nsmaps_lock));
3239*2621Sllai1 
3240*2621Sllai1 	if (devname_nsmaps == NULL)
3241*2621Sllai1 		return;
3242*2621Sllai1 
3243*2621Sllai1 	for (map = devname_nsmaps; map; map = map->next) {
3244*2621Sllai1 		rw_enter(&map->dir_lock, RW_WRITER);
3245*2621Sllai1 		map->dir_invalid = 1;
3246*2621Sllai1 		rw_exit(&map->dir_lock);
3247*2621Sllai1 	}
3248*2621Sllai1 	devname_nsmaps_invalidated = 1;
3249*2621Sllai1 }
3250*2621Sllai1 
3251*2621Sllai1 
3252*2621Sllai1 int
3253*2621Sllai1 sdev_nsmaps_loaded()
3254*2621Sllai1 {
3255*2621Sllai1 	int ret = 0;
3256*2621Sllai1 
3257*2621Sllai1 	mutex_enter(&devname_nsmaps_lock);
3258*2621Sllai1 	if (devname_nsmaps_loaded)
3259*2621Sllai1 		ret = 1;
3260*2621Sllai1 
3261*2621Sllai1 	mutex_exit(&devname_nsmaps_lock);
3262*2621Sllai1 	return (ret);
3263*2621Sllai1 }
3264*2621Sllai1 
3265*2621Sllai1 int
3266*2621Sllai1 sdev_nsmaps_reloaded()
3267*2621Sllai1 {
3268*2621Sllai1 	int ret = 0;
3269*2621Sllai1 
3270*2621Sllai1 	mutex_enter(&devname_nsmaps_lock);
3271*2621Sllai1 	if (devname_nsmaps_invalidated)
3272*2621Sllai1 		ret = 1;
3273*2621Sllai1 
3274*2621Sllai1 	mutex_exit(&devname_nsmaps_lock);
3275*2621Sllai1 	return (ret);
3276*2621Sllai1 }
3277*2621Sllai1 
3278*2621Sllai1 static void
3279*2621Sllai1 sdev_free_nsmap(struct devname_nsmap *map)
3280*2621Sllai1 {
3281*2621Sllai1 	ASSERT(map);
3282*2621Sllai1 	if (map->dir_name)
3283*2621Sllai1 		kmem_free(map->dir_name, strlen(map->dir_name) + 1);
3284*2621Sllai1 	if (map->dir_module)
3285*2621Sllai1 		kmem_free(map->dir_module, strlen(map->dir_module) + 1);
3286*2621Sllai1 	if (map->dir_map)
3287*2621Sllai1 		kmem_free(map->dir_map, strlen(map->dir_map) + 1);
3288*2621Sllai1 	rw_destroy(&map->dir_lock);
3289*2621Sllai1 	kmem_free(map, sizeof (*map));
3290*2621Sllai1 }
3291*2621Sllai1 
3292*2621Sllai1 void
3293*2621Sllai1 sdev_validate_nsmaps()
3294*2621Sllai1 {
3295*2621Sllai1 	struct devname_nsmap *map = NULL;
3296*2621Sllai1 	struct devname_nsmap *oldmap = NULL;
3297*2621Sllai1 
3298*2621Sllai1 	ASSERT(MUTEX_HELD(&devname_nsmaps_lock));
3299*2621Sllai1 	map = devname_nsmaps;
3300*2621Sllai1 	while (map) {
3301*2621Sllai1 		rw_enter(&map->dir_lock, RW_READER);
3302*2621Sllai1 		if ((map->dir_invalid == 1) && (map->dir_newmodule == NULL) &&
3303*2621Sllai1 		    (map->dir_newmap == NULL)) {
3304*2621Sllai1 			oldmap = map;
3305*2621Sllai1 			rw_exit(&map->dir_lock);
3306*2621Sllai1 			if (map->prev)
3307*2621Sllai1 				map->prev->next = oldmap->next;
3308*2621Sllai1 			if (map == devname_nsmaps)
3309*2621Sllai1 				devname_nsmaps = oldmap->next;
3310*2621Sllai1 
3311*2621Sllai1 			map = oldmap->next;
3312*2621Sllai1 			if (map)
3313*2621Sllai1 				map->prev = oldmap->prev;
3314*2621Sllai1 			sdev_free_nsmap(oldmap);
3315*2621Sllai1 			oldmap = NULL;
3316*2621Sllai1 		} else {
3317*2621Sllai1 			rw_exit(&map->dir_lock);
3318*2621Sllai1 			map = map->next;
3319*2621Sllai1 		}
3320*2621Sllai1 	}
3321*2621Sllai1 	devname_nsmaps_invalidated = 0;
3322*2621Sllai1 }
3323*2621Sllai1 
3324*2621Sllai1 static int
3325*2621Sllai1 sdev_map_is_invalid(struct devname_nsmap *map)
3326*2621Sllai1 {
3327*2621Sllai1 	int ret = 0;
3328*2621Sllai1 
3329*2621Sllai1 	ASSERT(map);
3330*2621Sllai1 	rw_enter(&map->dir_lock, RW_READER);
3331*2621Sllai1 	if (map->dir_invalid)
3332*2621Sllai1 		ret = 1;
3333*2621Sllai1 	rw_exit(&map->dir_lock);
3334*2621Sllai1 	return (ret);
3335*2621Sllai1 }
3336*2621Sllai1 
3337*2621Sllai1 static int
3338*2621Sllai1 sdev_check_map(struct devname_nsmap *map)
3339*2621Sllai1 {
3340*2621Sllai1 	struct devname_nsmap *mapp;
3341*2621Sllai1 
3342*2621Sllai1 	mutex_enter(&devname_nsmaps_lock);
3343*2621Sllai1 	if (devname_nsmaps == NULL) {
3344*2621Sllai1 		mutex_exit(&devname_nsmaps_lock);
3345*2621Sllai1 		return (1);
3346*2621Sllai1 	}
3347*2621Sllai1 
3348*2621Sllai1 	for (mapp = devname_nsmaps; mapp; mapp = mapp->next) {
3349*2621Sllai1 		if (mapp == map) {
3350*2621Sllai1 			mutex_exit(&devname_nsmaps_lock);
3351*2621Sllai1 			return (0);
3352*2621Sllai1 		}
3353*2621Sllai1 	}
3354*2621Sllai1 
3355*2621Sllai1 	mutex_exit(&devname_nsmaps_lock);
3356*2621Sllai1 	return (1);
3357*2621Sllai1 
3358*2621Sllai1 }
3359*2621Sllai1 
3360*2621Sllai1 struct devname_nsmap *
3361*2621Sllai1 sdev_get_map(struct sdev_node *dv, int validate)
3362*2621Sllai1 {
3363*2621Sllai1 	struct devname_nsmap *map;
3364*2621Sllai1 	int error;
3365*2621Sllai1 
3366*2621Sllai1 	ASSERT(RW_READ_HELD(&dv->sdev_contents));
3367*2621Sllai1 	map = dv->sdev_mapinfo;
3368*2621Sllai1 	if (map && sdev_check_map(map)) {
3369*2621Sllai1 		if (!rw_tryupgrade(&dv->sdev_contents)) {
3370*2621Sllai1 			rw_exit(&dv->sdev_contents);
3371*2621Sllai1 			rw_enter(&dv->sdev_contents, RW_WRITER);
3372*2621Sllai1 		}
3373*2621Sllai1 		dv->sdev_mapinfo = NULL;
3374*2621Sllai1 		rw_downgrade(&dv->sdev_contents);
3375*2621Sllai1 		return (NULL);
3376*2621Sllai1 	}
3377*2621Sllai1 
3378*2621Sllai1 	if (validate && (!map || (map && sdev_map_is_invalid(map)))) {
3379*2621Sllai1 		if (!rw_tryupgrade(&dv->sdev_contents)) {
3380*2621Sllai1 			rw_exit(&dv->sdev_contents);
3381*2621Sllai1 			rw_enter(&dv->sdev_contents, RW_WRITER);
3382*2621Sllai1 		}
3383*2621Sllai1 		error = sdev_get_moduleops(dv);
3384*2621Sllai1 		if (!error)
3385*2621Sllai1 			map = dv->sdev_mapinfo;
3386*2621Sllai1 		rw_downgrade(&dv->sdev_contents);
3387*2621Sllai1 	}
3388*2621Sllai1 	return (map);
3389*2621Sllai1 }
3390*2621Sllai1 
3391*2621Sllai1 void
3392*2621Sllai1 sdev_handle_alloc(struct sdev_node *dv)
3393*2621Sllai1 {
3394*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
3395*2621Sllai1 	dv->sdev_handle.dh_data = dv;
3396*2621Sllai1 	rw_exit(&dv->sdev_contents);
3397*2621Sllai1 }
3398*2621Sllai1 
3399*2621Sllai1 
3400*2621Sllai1 extern int sdev_vnodeops_tbl_size;
3401*2621Sllai1 
3402*2621Sllai1 /*
3403*2621Sllai1  * construct a new template with overrides from vtab
3404*2621Sllai1  */
3405*2621Sllai1 static fs_operation_def_t *
3406*2621Sllai1 sdev_merge_vtab(const fs_operation_def_t tab[])
3407*2621Sllai1 {
3408*2621Sllai1 	fs_operation_def_t *new;
3409*2621Sllai1 	const fs_operation_def_t *tab_entry;
3410*2621Sllai1 
3411*2621Sllai1 	/* make a copy of standard vnode ops table */
3412*2621Sllai1 	new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP);
3413*2621Sllai1 	bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size);
3414*2621Sllai1 
3415*2621Sllai1 	/* replace the overrides from tab */
3416*2621Sllai1 	for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) {
3417*2621Sllai1 		fs_operation_def_t *std_entry = new;
3418*2621Sllai1 		while (std_entry->name) {
3419*2621Sllai1 			if (strcmp(tab_entry->name, std_entry->name) == 0) {
3420*2621Sllai1 				std_entry->func = tab_entry->func;
3421*2621Sllai1 				break;
3422*2621Sllai1 			}
3423*2621Sllai1 			std_entry++;
3424*2621Sllai1 		}
3425*2621Sllai1 		if (std_entry->name == NULL)
3426*2621Sllai1 			cmn_err(CE_NOTE, "sdev_merge_vtab: entry %s unused.",
3427*2621Sllai1 			    tab_entry->name);
3428*2621Sllai1 	}
3429*2621Sllai1 
3430*2621Sllai1 	return (new);
3431*2621Sllai1 }
3432*2621Sllai1 
3433*2621Sllai1 /* free memory allocated by sdev_merge_vtab */
3434*2621Sllai1 static void
3435*2621Sllai1 sdev_free_vtab(fs_operation_def_t *new)
3436*2621Sllai1 {
3437*2621Sllai1 	kmem_free(new, sdev_vnodeops_tbl_size);
3438*2621Sllai1 }
3439*2621Sllai1 
3440*2621Sllai1 void
3441*2621Sllai1 devname_get_vnode(devname_handle_t *hdl, vnode_t **vpp)
3442*2621Sllai1 {
3443*2621Sllai1 	struct sdev_node *dv = hdl->dh_data;
3444*2621Sllai1 
3445*2621Sllai1 	ASSERT(dv);
3446*2621Sllai1 
3447*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
3448*2621Sllai1 	*vpp = SDEVTOV(dv);
3449*2621Sllai1 	rw_exit(&dv->sdev_contents);
3450*2621Sllai1 }
3451*2621Sllai1 
3452*2621Sllai1 int
3453*2621Sllai1 devname_get_path(devname_handle_t *hdl, char **path)
3454*2621Sllai1 {
3455*2621Sllai1 	struct sdev_node *dv = hdl->dh_data;
3456*2621Sllai1 
3457*2621Sllai1 	ASSERT(dv);
3458*2621Sllai1 
3459*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
3460*2621Sllai1 	*path = dv->sdev_path;
3461*2621Sllai1 	rw_exit(&dv->sdev_contents);
3462*2621Sllai1 	return (0);
3463*2621Sllai1 }
3464*2621Sllai1 
3465*2621Sllai1 int
3466*2621Sllai1 devname_get_name(devname_handle_t *hdl, char **entry)
3467*2621Sllai1 {
3468*2621Sllai1 	struct sdev_node *dv = hdl->dh_data;
3469*2621Sllai1 
3470*2621Sllai1 	ASSERT(dv);
3471*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
3472*2621Sllai1 	*entry = dv->sdev_name;
3473*2621Sllai1 	rw_exit(&dv->sdev_contents);
3474*2621Sllai1 	return (0);
3475*2621Sllai1 }
3476*2621Sllai1 
3477*2621Sllai1 void
3478*2621Sllai1 devname_get_dir_vnode(devname_handle_t *hdl, vnode_t **vpp)
3479*2621Sllai1 {
3480*2621Sllai1 	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
3481*2621Sllai1 
3482*2621Sllai1 	ASSERT(dv);
3483*2621Sllai1 
3484*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
3485*2621Sllai1 	*vpp = SDEVTOV(dv);
3486*2621Sllai1 	rw_exit(&dv->sdev_contents);
3487*2621Sllai1 }
3488*2621Sllai1 
3489*2621Sllai1 int
3490*2621Sllai1 devname_get_dir_path(devname_handle_t *hdl, char **path)
3491*2621Sllai1 {
3492*2621Sllai1 	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
3493*2621Sllai1 
3494*2621Sllai1 	ASSERT(dv);
3495*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
3496*2621Sllai1 	*path = dv->sdev_path;
3497*2621Sllai1 	rw_exit(&dv->sdev_contents);
3498*2621Sllai1 	return (0);
3499*2621Sllai1 }
3500*2621Sllai1 
3501*2621Sllai1 int
3502*2621Sllai1 devname_get_dir_name(devname_handle_t *hdl, char **entry)
3503*2621Sllai1 {
3504*2621Sllai1 	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
3505*2621Sllai1 
3506*2621Sllai1 	ASSERT(dv);
3507*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
3508*2621Sllai1 	*entry = dv->sdev_name;
3509*2621Sllai1 	rw_exit(&dv->sdev_contents);
3510*2621Sllai1 	return (0);
3511*2621Sllai1 }
3512*2621Sllai1 
3513*2621Sllai1 int
3514*2621Sllai1 devname_get_dir_nsmap(devname_handle_t *hdl, struct devname_nsmap **map)
3515*2621Sllai1 {
3516*2621Sllai1 	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
3517*2621Sllai1 
3518*2621Sllai1 	ASSERT(dv);
3519*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
3520*2621Sllai1 	*map = dv->sdev_mapinfo;
3521*2621Sllai1 	rw_exit(&dv->sdev_contents);
3522*2621Sllai1 	return (0);
3523*2621Sllai1 }
3524*2621Sllai1 
3525*2621Sllai1 int
3526*2621Sllai1 devname_get_dir_handle(devname_handle_t *hdl, devname_handle_t **dir_hdl)
3527*2621Sllai1 {
3528*2621Sllai1 	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
3529*2621Sllai1 
3530*2621Sllai1 	ASSERT(dv);
3531*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
3532*2621Sllai1 	*dir_hdl = &(dv->sdev_handle);
3533*2621Sllai1 	rw_exit(&dv->sdev_contents);
3534*2621Sllai1 	return (0);
3535*2621Sllai1 }
3536*2621Sllai1 
3537*2621Sllai1 void
3538*2621Sllai1 devname_set_nodetype(devname_handle_t *hdl, void *args, int spec)
3539*2621Sllai1 {
3540*2621Sllai1 	struct sdev_node *dv = hdl->dh_data;
3541*2621Sllai1 
3542*2621Sllai1 	ASSERT(dv);
3543*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
3544*2621Sllai1 	hdl->dh_spec = (devname_spec_t)spec;
3545*2621Sllai1 	hdl->dh_args = (void *)i_ddi_strdup((char *)args, KM_SLEEP);
3546*2621Sllai1 	rw_exit(&dv->sdev_contents);
3547*2621Sllai1 }
3548*2621Sllai1 
3549*2621Sllai1 /*
3550*2621Sllai1  * a generic setattr() function
3551*2621Sllai1  *
3552*2621Sllai1  * note: flags only supports AT_UID and AT_GID.
3553*2621Sllai1  *	 Future enhancements can be done for other types, e.g. AT_MODE
3554*2621Sllai1  */
3555*2621Sllai1 int
3556*2621Sllai1 devname_setattr_func(struct vnode *vp, struct vattr *vap, int flags,
3557*2621Sllai1     struct cred *cred, int (*callback)(struct sdev_node *, struct vattr *,
3558*2621Sllai1     int), int protocol)
3559*2621Sllai1 {
3560*2621Sllai1 	struct sdev_node	*dv = VTOSDEV(vp);
3561*2621Sllai1 	struct sdev_node	*parent = dv->sdev_dotdot;
3562*2621Sllai1 	struct vattr		*get;
3563*2621Sllai1 	uint_t			mask = vap->va_mask;
3564*2621Sllai1 	int 			error;
3565*2621Sllai1 
3566*2621Sllai1 	/* some sanity checks */
3567*2621Sllai1 	if (vap->va_mask & AT_NOSET)
3568*2621Sllai1 		return (EINVAL);
3569*2621Sllai1 
3570*2621Sllai1 	if (vap->va_mask & AT_SIZE) {
3571*2621Sllai1 		if (vp->v_type == VDIR) {
3572*2621Sllai1 			return (EISDIR);
3573*2621Sllai1 		}
3574*2621Sllai1 	}
3575*2621Sllai1 
3576*2621Sllai1 	/* no need to set attribute, but do not fail either */
3577*2621Sllai1 	ASSERT(parent);
3578*2621Sllai1 	rw_enter(&parent->sdev_contents, RW_READER);
3579*2621Sllai1 	if (dv->sdev_state == SDEV_ZOMBIE) {
3580*2621Sllai1 		rw_exit(&parent->sdev_contents);
3581*2621Sllai1 		return (0);
3582*2621Sllai1 	}
3583*2621Sllai1 
3584*2621Sllai1 	/* If backing store exists, just set it. */
3585*2621Sllai1 	if (dv->sdev_attrvp) {
3586*2621Sllai1 		rw_exit(&parent->sdev_contents);
3587*2621Sllai1 		return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL));
3588*2621Sllai1 	}
3589*2621Sllai1 
3590*2621Sllai1 	/*
3591*2621Sllai1 	 * Otherwise, for nodes with the persistence attribute, create it.
3592*2621Sllai1 	 */
3593*2621Sllai1 	ASSERT(dv->sdev_attr);
3594*2621Sllai1 	if (SDEV_IS_PERSIST(dv) ||
3595*2621Sllai1 	    ((vap->va_mask & ~AT_TIMES) != 0 && !SDEV_IS_DYNAMIC(dv))) {
3596*2621Sllai1 		sdev_vattr_merge(dv, vap);
3597*2621Sllai1 		rw_enter(&dv->sdev_contents, RW_WRITER);
3598*2621Sllai1 		error = sdev_shadow_node(dv, cred);
3599*2621Sllai1 		rw_exit(&dv->sdev_contents);
3600*2621Sllai1 		rw_exit(&parent->sdev_contents);
3601*2621Sllai1 
3602*2621Sllai1 		if (error)
3603*2621Sllai1 			return (error);
3604*2621Sllai1 		return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL));
3605*2621Sllai1 	}
3606*2621Sllai1 
3607*2621Sllai1 
3608*2621Sllai1 	/*
3609*2621Sllai1 	 * sdev_attr was allocated in sdev_mknode
3610*2621Sllai1 	 */
3611*2621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
3612*2621Sllai1 	error = secpolicy_vnode_setattr(cred, vp, vap, dv->sdev_attr,
3613*2621Sllai1 	    flags, sdev_unlocked_access, dv);
3614*2621Sllai1 	if (error) {
3615*2621Sllai1 		rw_exit(&dv->sdev_contents);
3616*2621Sllai1 		rw_exit(&parent->sdev_contents);
3617*2621Sllai1 		return (error);
3618*2621Sllai1 	}
3619*2621Sllai1 
3620*2621Sllai1 	get = dv->sdev_attr;
3621*2621Sllai1 	if (mask & AT_MODE) {
3622*2621Sllai1 		get->va_mode &= S_IFMT;
3623*2621Sllai1 		get->va_mode |= vap->va_mode & ~S_IFMT;
3624*2621Sllai1 	}
3625*2621Sllai1 
3626*2621Sllai1 	if ((mask & AT_UID) || (mask & AT_GID)) {
3627*2621Sllai1 		if (mask & AT_UID)
3628*2621Sllai1 			get->va_uid = vap->va_uid;
3629*2621Sllai1 		if (mask & AT_GID)
3630*2621Sllai1 			get->va_gid = vap->va_gid;
3631*2621Sllai1 		/*
3632*2621Sllai1 		 * a callback must be provided if the protocol is set
3633*2621Sllai1 		 */
3634*2621Sllai1 		if ((protocol & AT_UID) || (protocol & AT_GID)) {
3635*2621Sllai1 			ASSERT(callback);
3636*2621Sllai1 			error = callback(dv, get, protocol);
3637*2621Sllai1 			if (error) {
3638*2621Sllai1 				rw_exit(&dv->sdev_contents);
3639*2621Sllai1 				rw_exit(&parent->sdev_contents);
3640*2621Sllai1 				return (error);
3641*2621Sllai1 			}
3642*2621Sllai1 		}
3643*2621Sllai1 	}
3644*2621Sllai1 
3645*2621Sllai1 	if (mask & AT_ATIME)
3646*2621Sllai1 		get->va_atime = vap->va_atime;
3647*2621Sllai1 	if (mask & AT_MTIME)
3648*2621Sllai1 		get->va_mtime = vap->va_mtime;
3649*2621Sllai1 	if (mask & (AT_MODE | AT_UID | AT_GID | AT_CTIME)) {
3650*2621Sllai1 		gethrestime(&get->va_ctime);
3651*2621Sllai1 	}
3652*2621Sllai1 
3653*2621Sllai1 	sdev_vattr_merge(dv, get);
3654*2621Sllai1 	rw_exit(&dv->sdev_contents);
3655*2621Sllai1 	rw_exit(&parent->sdev_contents);
3656*2621Sllai1 	return (0);
3657*2621Sllai1 }
3658