xref: /illumos-gate/usr/src/uts/common/fs/dev/sdev_subr.c (revision f8927fa6)
1facf4a8dSllai /*
2facf4a8dSllai  * CDDL HEADER START
3facf4a8dSllai  *
4facf4a8dSllai  * The contents of this file are subject to the terms of the
5facf4a8dSllai  * Common Development and Distribution License (the "License").
6facf4a8dSllai  * You may not use this file except in compliance with the License.
7facf4a8dSllai  *
8facf4a8dSllai  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9facf4a8dSllai  * or http://www.opensolaris.org/os/licensing.
10facf4a8dSllai  * See the License for the specific language governing permissions
11facf4a8dSllai  * and limitations under the License.
12facf4a8dSllai  *
13facf4a8dSllai  * When distributing Covered Code, include this CDDL HEADER in each
14facf4a8dSllai  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15facf4a8dSllai  * If applicable, add the following below this CDDL HEADER, with the
16facf4a8dSllai  * fields enclosed by brackets "[]" replaced with your own identifying
17facf4a8dSllai  * information: Portions Copyright [yyyy] [name of copyright owner]
18facf4a8dSllai  *
19facf4a8dSllai  * CDDL HEADER END
20facf4a8dSllai  */
21facf4a8dSllai /*
220fbb751dSJohn Levon  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
2345b17475SAlex Wilson  * Copyright (c) 2013, 2016 Joyent, Inc. All rights reserved.
24ade42b55SSebastien Roy  * Copyright (c) 2017 by Delphix. All rights reserved.
25facf4a8dSllai  */
26facf4a8dSllai 
27facf4a8dSllai /*
28facf4a8dSllai  * utility routines for the /dev fs
29facf4a8dSllai  */
30facf4a8dSllai 
31facf4a8dSllai #include <sys/types.h>
32facf4a8dSllai #include <sys/param.h>
33facf4a8dSllai #include <sys/t_lock.h>
34facf4a8dSllai #include <sys/systm.h>
35facf4a8dSllai #include <sys/sysmacros.h>
36facf4a8dSllai #include <sys/user.h>
37facf4a8dSllai #include <sys/time.h>
38facf4a8dSllai #include <sys/vfs.h>
39facf4a8dSllai #include <sys/vnode.h>
40facf4a8dSllai #include <sys/file.h>
41facf4a8dSllai #include <sys/fcntl.h>
42facf4a8dSllai #include <sys/flock.h>
43facf4a8dSllai #include <sys/kmem.h>
44facf4a8dSllai #include <sys/uio.h>
45facf4a8dSllai #include <sys/errno.h>
46facf4a8dSllai #include <sys/stat.h>
47facf4a8dSllai #include <sys/cred.h>
48facf4a8dSllai #include <sys/dirent.h>
49facf4a8dSllai #include <sys/pathname.h>
50facf4a8dSllai #include <sys/cmn_err.h>
51facf4a8dSllai #include <sys/debug.h>
52facf4a8dSllai #include <sys/mode.h>
53facf4a8dSllai #include <sys/policy.h>
54facf4a8dSllai #include <fs/fs_subr.h>
55facf4a8dSllai #include <sys/mount.h>
56facf4a8dSllai #include <sys/fs/snode.h>
57facf4a8dSllai #include <sys/fs/dv_node.h>
58facf4a8dSllai #include <sys/fs/sdev_impl.h>
59facf4a8dSllai #include <sys/sunndi.h>
60facf4a8dSllai #include <sys/sunmdi.h>
61facf4a8dSllai #include <sys/conf.h>
62facf4a8dSllai #include <sys/proc.h>
63facf4a8dSllai #include <sys/user.h>
64facf4a8dSllai #include <sys/modctl.h>
65facf4a8dSllai 
66facf4a8dSllai #ifdef DEBUG
67facf4a8dSllai int sdev_debug = 0x00000001;
68facf4a8dSllai int sdev_debug_cache_flags = 0;
69facf4a8dSllai #endif
70facf4a8dSllai 
71facf4a8dSllai /*
72facf4a8dSllai  * globals
73facf4a8dSllai  */
74facf4a8dSllai /* prototype memory vattrs */
75facf4a8dSllai vattr_t sdev_vattr_dir = {
76facf4a8dSllai 	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
77facf4a8dSllai 	VDIR,					/* va_type */
78facf4a8dSllai 	SDEV_DIRMODE_DEFAULT,			/* va_mode */
79facf4a8dSllai 	SDEV_UID_DEFAULT,			/* va_uid */
80facf4a8dSllai 	SDEV_GID_DEFAULT,			/* va_gid */
81facf4a8dSllai 	0,					/* va_fsid */
82facf4a8dSllai 	0,					/* va_nodeid */
83facf4a8dSllai 	0,					/* va_nlink */
84facf4a8dSllai 	0,					/* va_size */
85facf4a8dSllai 	0,					/* va_atime */
86facf4a8dSllai 	0,					/* va_mtime */
87facf4a8dSllai 	0,					/* va_ctime */
88facf4a8dSllai 	0,					/* va_rdev */
89facf4a8dSllai 	0,					/* va_blksize */
90facf4a8dSllai 	0,					/* va_nblocks */
91facf4a8dSllai 	0					/* va_vcode */
92facf4a8dSllai };
93facf4a8dSllai 
94facf4a8dSllai vattr_t sdev_vattr_lnk = {
95facf4a8dSllai 	AT_TYPE|AT_MODE,			/* va_mask */
96facf4a8dSllai 	VLNK,					/* va_type */
97facf4a8dSllai 	SDEV_LNKMODE_DEFAULT,			/* va_mode */
98facf4a8dSllai 	SDEV_UID_DEFAULT,			/* va_uid */
99facf4a8dSllai 	SDEV_GID_DEFAULT,			/* va_gid */
100facf4a8dSllai 	0,					/* va_fsid */
101facf4a8dSllai 	0,					/* va_nodeid */
102facf4a8dSllai 	0,					/* va_nlink */
103facf4a8dSllai 	0,					/* va_size */
104facf4a8dSllai 	0,					/* va_atime */
105facf4a8dSllai 	0,					/* va_mtime */
106facf4a8dSllai 	0,					/* va_ctime */
107facf4a8dSllai 	0,					/* va_rdev */
108facf4a8dSllai 	0,					/* va_blksize */
109facf4a8dSllai 	0,					/* va_nblocks */
110facf4a8dSllai 	0					/* va_vcode */
111facf4a8dSllai };
112facf4a8dSllai 
113facf4a8dSllai vattr_t sdev_vattr_blk = {
114facf4a8dSllai 	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
115facf4a8dSllai 	VBLK,					/* va_type */
116facf4a8dSllai 	S_IFBLK | SDEV_DEVMODE_DEFAULT,		/* va_mode */
117facf4a8dSllai 	SDEV_UID_DEFAULT,			/* va_uid */
118facf4a8dSllai 	SDEV_GID_DEFAULT,			/* va_gid */
119facf4a8dSllai 	0,					/* va_fsid */
120facf4a8dSllai 	0,					/* va_nodeid */
121facf4a8dSllai 	0,					/* va_nlink */
122facf4a8dSllai 	0,					/* va_size */
123facf4a8dSllai 	0,					/* va_atime */
124facf4a8dSllai 	0,					/* va_mtime */
125facf4a8dSllai 	0,					/* va_ctime */
126facf4a8dSllai 	0,					/* va_rdev */
127facf4a8dSllai 	0,					/* va_blksize */
128facf4a8dSllai 	0,					/* va_nblocks */
129facf4a8dSllai 	0					/* va_vcode */
130facf4a8dSllai };
131facf4a8dSllai 
132facf4a8dSllai vattr_t sdev_vattr_chr = {
133facf4a8dSllai 	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
134facf4a8dSllai 	VCHR,					/* va_type */
135facf4a8dSllai 	S_IFCHR | SDEV_DEVMODE_DEFAULT,		/* va_mode */
136facf4a8dSllai 	SDEV_UID_DEFAULT,			/* va_uid */
137facf4a8dSllai 	SDEV_GID_DEFAULT,			/* va_gid */
138facf4a8dSllai 	0,					/* va_fsid */
139facf4a8dSllai 	0,					/* va_nodeid */
140facf4a8dSllai 	0,					/* va_nlink */
141facf4a8dSllai 	0,					/* va_size */
142facf4a8dSllai 	0,					/* va_atime */
143facf4a8dSllai 	0,					/* va_mtime */
144facf4a8dSllai 	0,					/* va_ctime */
145facf4a8dSllai 	0,					/* va_rdev */
146facf4a8dSllai 	0,					/* va_blksize */
147facf4a8dSllai 	0,					/* va_nblocks */
148facf4a8dSllai 	0					/* va_vcode */
149facf4a8dSllai };
150facf4a8dSllai 
151facf4a8dSllai kmem_cache_t	*sdev_node_cache;	/* sdev_node cache */
152facf4a8dSllai int		devtype;		/* fstype */
153facf4a8dSllai 
154facf4a8dSllai static void
sdev_prof_free(struct sdev_node * dv)155facf4a8dSllai sdev_prof_free(struct sdev_node *dv)
156facf4a8dSllai {
157facf4a8dSllai 	ASSERT(!SDEV_IS_GLOBAL(dv));
158aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(dv->sdev_prof.dev_name);
159aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(dv->sdev_prof.dev_map);
160aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(dv->sdev_prof.dev_symlink);
161aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(dv->sdev_prof.dev_glob_incdir);
162aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(dv->sdev_prof.dev_glob_excdir);
163facf4a8dSllai 	bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
164facf4a8dSllai }
165facf4a8dSllai 
166b5fca8f8Stomee /* sdev_node cache constructor */
167facf4a8dSllai /*ARGSUSED1*/
168facf4a8dSllai static int
i_sdev_node_ctor(void * buf,void * cfarg,int flag)169facf4a8dSllai i_sdev_node_ctor(void *buf, void *cfarg, int flag)
170facf4a8dSllai {
171facf4a8dSllai 	struct sdev_node *dv = (struct sdev_node *)buf;
172facf4a8dSllai 	struct vnode *vp;
173facf4a8dSllai 
174facf4a8dSllai 	bzero(buf, sizeof (struct sdev_node));
175b5fca8f8Stomee 	vp = dv->sdev_vnode = vn_alloc(flag);
176b5fca8f8Stomee 	if (vp == NULL) {
177b5fca8f8Stomee 		return (-1);
178b5fca8f8Stomee 	}
179b5fca8f8Stomee 	vp->v_data = dv;
180facf4a8dSllai 	rw_init(&dv->sdev_contents, NULL, RW_DEFAULT, NULL);
181facf4a8dSllai 	return (0);
182facf4a8dSllai }
183facf4a8dSllai 
184b5fca8f8Stomee /* sdev_node cache destructor */
185facf4a8dSllai /*ARGSUSED1*/
186facf4a8dSllai static void
i_sdev_node_dtor(void * buf,void * arg)187facf4a8dSllai i_sdev_node_dtor(void *buf, void *arg)
188facf4a8dSllai {
189facf4a8dSllai 	struct sdev_node *dv = (struct sdev_node *)buf;
190facf4a8dSllai 	struct vnode *vp = SDEVTOV(dv);
191facf4a8dSllai 
192facf4a8dSllai 	rw_destroy(&dv->sdev_contents);
193facf4a8dSllai 	vn_free(vp);
194facf4a8dSllai }
195facf4a8dSllai 
196facf4a8dSllai /* initialize sdev_node cache */
197facf4a8dSllai void
sdev_node_cache_init()198facf4a8dSllai sdev_node_cache_init()
199facf4a8dSllai {
200facf4a8dSllai 	int flags = 0;
201facf4a8dSllai 
202facf4a8dSllai #ifdef	DEBUG
203facf4a8dSllai 	flags = sdev_debug_cache_flags;
204facf4a8dSllai 	if (flags)
205facf4a8dSllai 		sdcmn_err(("cache debug flags 0x%x\n", flags));
206facf4a8dSllai #endif	/* DEBUG */
207facf4a8dSllai 
208facf4a8dSllai 	ASSERT(sdev_node_cache == NULL);
209facf4a8dSllai 	sdev_node_cache = kmem_cache_create("sdev_node_cache",
210facf4a8dSllai 	    sizeof (struct sdev_node), 0, i_sdev_node_ctor, i_sdev_node_dtor,
211facf4a8dSllai 	    NULL, NULL, NULL, flags);
212facf4a8dSllai }
213facf4a8dSllai 
214facf4a8dSllai /* destroy sdev_node cache */
215facf4a8dSllai void
sdev_node_cache_fini()216facf4a8dSllai sdev_node_cache_fini()
217facf4a8dSllai {
218facf4a8dSllai 	ASSERT(sdev_node_cache != NULL);
219facf4a8dSllai 	kmem_cache_destroy(sdev_node_cache);
220facf4a8dSllai 	sdev_node_cache = NULL;
221facf4a8dSllai }
222facf4a8dSllai 
223aac43a5fSjg /*
224aac43a5fSjg  * Compare two nodes lexographically to balance avl tree
225aac43a5fSjg  */
226aac43a5fSjg static int
sdev_compare_nodes(const struct sdev_node * dv1,const struct sdev_node * dv2)227aac43a5fSjg sdev_compare_nodes(const struct sdev_node *dv1, const struct sdev_node *dv2)
228aac43a5fSjg {
229aac43a5fSjg 	int rv;
230aac43a5fSjg 	if ((rv = strcmp(dv1->sdev_name, dv2->sdev_name)) == 0)
231aac43a5fSjg 		return (0);
232aac43a5fSjg 	return ((rv < 0) ? -1 : 1);
233aac43a5fSjg }
234aac43a5fSjg 
235facf4a8dSllai void
sdev_set_nodestate(struct sdev_node * dv,sdev_node_state_t state)236facf4a8dSllai sdev_set_nodestate(struct sdev_node *dv, sdev_node_state_t state)
237facf4a8dSllai {
238facf4a8dSllai 	ASSERT(dv);
239facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&dv->sdev_contents));
240facf4a8dSllai 	dv->sdev_state = state;
241facf4a8dSllai }
242facf4a8dSllai 
243facf4a8dSllai static void
sdev_attr_update(struct sdev_node * dv,vattr_t * vap)244bb5fffbeSJerry Gilliam sdev_attr_update(struct sdev_node *dv, vattr_t *vap)
245facf4a8dSllai {
246bb5fffbeSJerry Gilliam 	timestruc_t	now;
247bb5fffbeSJerry Gilliam 	struct vattr	*attrp;
248bb5fffbeSJerry Gilliam 	uint_t		mask;
249facf4a8dSllai 
250bb5fffbeSJerry Gilliam 	ASSERT(dv->sdev_attr);
251facf4a8dSllai 	ASSERT(vap);
252facf4a8dSllai 
253bb5fffbeSJerry Gilliam 	attrp = dv->sdev_attr;
254bb5fffbeSJerry Gilliam 	mask = vap->va_mask;
255bb5fffbeSJerry Gilliam 	if (mask & AT_TYPE)
256bb5fffbeSJerry Gilliam 		attrp->va_type = vap->va_type;
257bb5fffbeSJerry Gilliam 	if (mask & AT_MODE)
258bb5fffbeSJerry Gilliam 		attrp->va_mode = vap->va_mode;
259bb5fffbeSJerry Gilliam 	if (mask & AT_UID)
260bb5fffbeSJerry Gilliam 		attrp->va_uid = vap->va_uid;
261bb5fffbeSJerry Gilliam 	if (mask & AT_GID)
262bb5fffbeSJerry Gilliam 		attrp->va_gid = vap->va_gid;
263bb5fffbeSJerry Gilliam 	if (mask & AT_RDEV)
264bb5fffbeSJerry Gilliam 		attrp->va_rdev = vap->va_rdev;
265facf4a8dSllai 
266facf4a8dSllai 	gethrestime(&now);
267bb5fffbeSJerry Gilliam 	attrp->va_atime = (mask & AT_ATIME) ? vap->va_atime : now;
268bb5fffbeSJerry Gilliam 	attrp->va_mtime = (mask & AT_MTIME) ? vap->va_mtime : now;
269bb5fffbeSJerry Gilliam 	attrp->va_ctime = (mask & AT_CTIME) ? vap->va_ctime : now;
270bb5fffbeSJerry Gilliam }
271bb5fffbeSJerry Gilliam 
272bb5fffbeSJerry Gilliam static void
sdev_attr_alloc(struct sdev_node * dv,vattr_t * vap)273bb5fffbeSJerry Gilliam sdev_attr_alloc(struct sdev_node *dv, vattr_t *vap)
274bb5fffbeSJerry Gilliam {
275bb5fffbeSJerry Gilliam 	ASSERT(dv->sdev_attr == NULL);
276bb5fffbeSJerry Gilliam 	ASSERT(vap->va_mask & AT_TYPE);
277bb5fffbeSJerry Gilliam 	ASSERT(vap->va_mask & AT_MODE);
278bb5fffbeSJerry Gilliam 
279bb5fffbeSJerry Gilliam 	dv->sdev_attr = kmem_zalloc(sizeof (struct vattr), KM_SLEEP);
280bb5fffbeSJerry Gilliam 	sdev_attr_update(dv, vap);
281facf4a8dSllai }
282facf4a8dSllai 
283facf4a8dSllai /* alloc and initialize a sdev_node */
284facf4a8dSllai int
sdev_nodeinit(struct sdev_node * ddv,char * nm,struct sdev_node ** newdv,vattr_t * vap)285facf4a8dSllai sdev_nodeinit(struct sdev_node *ddv, char *nm, struct sdev_node **newdv,
286facf4a8dSllai     vattr_t *vap)
287facf4a8dSllai {
288facf4a8dSllai 	struct sdev_node *dv = NULL;
289facf4a8dSllai 	struct vnode *vp;
290facf4a8dSllai 	size_t nmlen, len;
291facf4a8dSllai 	devname_handle_t  *dhl;
292facf4a8dSllai 
293facf4a8dSllai 	nmlen = strlen(nm) + 1;
294facf4a8dSllai 	if (nmlen > MAXNAMELEN) {
295facf4a8dSllai 		sdcmn_err9(("sdev_nodeinit: node name %s"
296facf4a8dSllai 		    " too long\n", nm));
297facf4a8dSllai 		*newdv = NULL;
298facf4a8dSllai 		return (ENAMETOOLONG);
299facf4a8dSllai 	}
300facf4a8dSllai 
301facf4a8dSllai 	dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP);
302facf4a8dSllai 
303facf4a8dSllai 	dv->sdev_name = kmem_alloc(nmlen, KM_SLEEP);
304facf4a8dSllai 	bcopy(nm, dv->sdev_name, nmlen);
305facf4a8dSllai 	dv->sdev_namelen = nmlen - 1;	/* '\0' not included */
306facf4a8dSllai 	len = strlen(ddv->sdev_path) + strlen(nm) + 2;
307facf4a8dSllai 	dv->sdev_path = kmem_alloc(len, KM_SLEEP);
308facf4a8dSllai 	(void) snprintf(dv->sdev_path, len, "%s/%s", ddv->sdev_path, nm);
309facf4a8dSllai 	/* overwritten for VLNK nodes */
310facf4a8dSllai 	dv->sdev_symlink = NULL;
311*f8927fa6SRobert Mustacchi 	list_link_init(&dv->sdev_plist);
312facf4a8dSllai 
313facf4a8dSllai 	vp = SDEVTOV(dv);
314facf4a8dSllai 	vn_reinit(vp);
315facf4a8dSllai 	vp->v_vfsp = SDEVTOV(ddv)->v_vfsp;
316facf4a8dSllai 	if (vap)
317facf4a8dSllai 		vp->v_type = vap->va_type;
318facf4a8dSllai 
319facf4a8dSllai 	/*
320facf4a8dSllai 	 * initialized to the parent's vnodeops.
321facf4a8dSllai 	 * maybe overwriten for a VDIR
322facf4a8dSllai 	 */
323facf4a8dSllai 	vn_setops(vp, vn_getops(SDEVTOV(ddv)));
324facf4a8dSllai 	vn_exists(vp);
325facf4a8dSllai 
326facf4a8dSllai 	dv->sdev_dotdot = NULL;
327facf4a8dSllai 	dv->sdev_attrvp = NULL;
328facf4a8dSllai 	if (vap) {
329bb5fffbeSJerry Gilliam 		sdev_attr_alloc(dv, vap);
330facf4a8dSllai 	} else {
331facf4a8dSllai 		dv->sdev_attr = NULL;
332facf4a8dSllai 	}
333facf4a8dSllai 
334facf4a8dSllai 	dv->sdev_ino = sdev_mkino(dv);
335facf4a8dSllai 	dv->sdev_nlink = 0;		/* updated on insert */
336facf4a8dSllai 	dv->sdev_flags = ddv->sdev_flags; /* inherit from the parent first */
337facf4a8dSllai 	dv->sdev_flags |= SDEV_BUILD;
338facf4a8dSllai 	mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL);
339facf4a8dSllai 	cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL);
340facf4a8dSllai 	if (SDEV_IS_GLOBAL(ddv)) {
341facf4a8dSllai 		dv->sdev_flags |= SDEV_GLOBAL;
342facf4a8dSllai 		dhl = &(dv->sdev_handle);
343facf4a8dSllai 		dhl->dh_data = dv;
344facf4a8dSllai 		dhl->dh_args = NULL;
345681d9761SEric Taylor 		sdev_set_no_negcache(dv);
346facf4a8dSllai 		dv->sdev_gdir_gen = 0;
347facf4a8dSllai 	} else {
348facf4a8dSllai 		dv->sdev_flags &= ~SDEV_GLOBAL;
349facf4a8dSllai 		dv->sdev_origin = NULL; /* set later */
350facf4a8dSllai 		bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
351facf4a8dSllai 		dv->sdev_ldir_gen = 0;
352facf4a8dSllai 		dv->sdev_devtree_gen = 0;
353facf4a8dSllai 	}
354facf4a8dSllai 
355facf4a8dSllai 	rw_enter(&dv->sdev_contents, RW_WRITER);
356facf4a8dSllai 	sdev_set_nodestate(dv, SDEV_INIT);
357facf4a8dSllai 	rw_exit(&dv->sdev_contents);
358facf4a8dSllai 	*newdv = dv;
359facf4a8dSllai 
360facf4a8dSllai 	return (0);
361facf4a8dSllai }
362facf4a8dSllai 
363facf4a8dSllai /*
3649e5aa9d8SRobert Mustacchi  * Transition a sdev_node into SDEV_READY state. If this fails, it is up to the
3659e5aa9d8SRobert Mustacchi  * caller to transition the node to the SDEV_ZOMBIE state.
366facf4a8dSllai  */
367facf4a8dSllai int
sdev_nodeready(struct sdev_node * dv,struct vattr * vap,struct vnode * avp,void * args,struct cred * cred)368facf4a8dSllai sdev_nodeready(struct sdev_node *dv, struct vattr *vap, struct vnode *avp,
369facf4a8dSllai     void *args, struct cred *cred)
370facf4a8dSllai {
371facf4a8dSllai 	int error = 0;
372facf4a8dSllai 	struct vnode *vp = SDEVTOV(dv);
373facf4a8dSllai 	vtype_t type;
374facf4a8dSllai 
375facf4a8dSllai 	ASSERT(dv && (dv->sdev_state != SDEV_READY) && vap);
376facf4a8dSllai 
377facf4a8dSllai 	type = vap->va_type;
378facf4a8dSllai 	vp->v_type = type;
379facf4a8dSllai 	vp->v_rdev = vap->va_rdev;
380facf4a8dSllai 	rw_enter(&dv->sdev_contents, RW_WRITER);
381facf4a8dSllai 	if (type == VDIR) {
382facf4a8dSllai 		dv->sdev_nlink = 2;
383facf4a8dSllai 		dv->sdev_flags &= ~SDEV_PERSIST;
384facf4a8dSllai 		dv->sdev_flags &= ~SDEV_DYNAMIC;
385facf4a8dSllai 		vn_setops(vp, sdev_get_vop(dv)); /* from internal vtab */
386facf4a8dSllai 		ASSERT(dv->sdev_dotdot);
387facf4a8dSllai 		ASSERT(SDEVTOV(dv->sdev_dotdot)->v_type == VDIR);
388facf4a8dSllai 		vp->v_rdev = SDEVTOV(dv->sdev_dotdot)->v_rdev;
389aac43a5fSjg 		avl_create(&dv->sdev_entries,
390aac43a5fSjg 		    (int (*)(const void *, const void *))sdev_compare_nodes,
391aac43a5fSjg 		    sizeof (struct sdev_node),
392aac43a5fSjg 		    offsetof(struct sdev_node, sdev_avllink));
393facf4a8dSllai 	} else if (type == VLNK) {
394facf4a8dSllai 		ASSERT(args);
395facf4a8dSllai 		dv->sdev_nlink = 1;
396facf4a8dSllai 		dv->sdev_symlink = i_ddi_strdup((char *)args, KM_SLEEP);
397facf4a8dSllai 	} else {
398facf4a8dSllai 		dv->sdev_nlink = 1;
399facf4a8dSllai 	}
400*f8927fa6SRobert Mustacchi 	sdev_plugin_nodeready(dv);
401facf4a8dSllai 
402facf4a8dSllai 	if (!(SDEV_IS_GLOBAL(dv))) {
403facf4a8dSllai 		dv->sdev_origin = (struct sdev_node *)args;
404facf4a8dSllai 		dv->sdev_flags &= ~SDEV_PERSIST;
405facf4a8dSllai 	}
406facf4a8dSllai 
407facf4a8dSllai 	/*
408facf4a8dSllai 	 * shadow node is created here OR
409facf4a8dSllai 	 * if failed (indicated by dv->sdev_attrvp == NULL),
410facf4a8dSllai 	 * created later in sdev_setattr
411facf4a8dSllai 	 */
412facf4a8dSllai 	if (avp) {
413facf4a8dSllai 		dv->sdev_attrvp = avp;
414facf4a8dSllai 	} else {
415bb5fffbeSJerry Gilliam 		if (dv->sdev_attr == NULL) {
416bb5fffbeSJerry Gilliam 			sdev_attr_alloc(dv, vap);
417bb5fffbeSJerry Gilliam 		} else {
418bb5fffbeSJerry Gilliam 			sdev_attr_update(dv, vap);
419bb5fffbeSJerry Gilliam 		}
420facf4a8dSllai 
421681d9761SEric Taylor 		if ((dv->sdev_attrvp == NULL) && SDEV_IS_PERSIST(dv))
422facf4a8dSllai 			error = sdev_shadow_node(dv, cred);
423facf4a8dSllai 	}
424facf4a8dSllai 
425b7beec95Sjg 	if (error == 0) {
426b7beec95Sjg 		/* transition to READY state */
427b7beec95Sjg 		sdev_set_nodestate(dv, SDEV_READY);
428b7beec95Sjg 		sdev_nc_node_exists(dv);
429b7beec95Sjg 	}
430facf4a8dSllai 	rw_exit(&dv->sdev_contents);
431facf4a8dSllai 	return (error);
432facf4a8dSllai }
433facf4a8dSllai 
434facf4a8dSllai /*
435facf4a8dSllai  * Build the VROOT sdev_node.
436facf4a8dSllai  */
437facf4a8dSllai /*ARGSUSED*/
438facf4a8dSllai struct sdev_node *
sdev_mkroot(struct vfs * vfsp,dev_t devdev,struct vnode * mvp,struct vnode * avp,struct cred * cred)439facf4a8dSllai sdev_mkroot(struct vfs *vfsp, dev_t devdev, struct vnode *mvp,
440facf4a8dSllai     struct vnode *avp, struct cred *cred)
441facf4a8dSllai {
442facf4a8dSllai 	struct sdev_node *dv;
443facf4a8dSllai 	struct vnode *vp;
444facf4a8dSllai 	char devdir[] = "/dev";
445facf4a8dSllai 
446facf4a8dSllai 	ASSERT(sdev_node_cache != NULL);
447facf4a8dSllai 	ASSERT(avp);
448facf4a8dSllai 	dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP);
449facf4a8dSllai 	vp = SDEVTOV(dv);
450facf4a8dSllai 	vn_reinit(vp);
451facf4a8dSllai 	vp->v_flag |= VROOT;
452facf4a8dSllai 	vp->v_vfsp = vfsp;
453facf4a8dSllai 	vp->v_type = VDIR;
454facf4a8dSllai 	vp->v_rdev = devdev;
455facf4a8dSllai 	vn_setops(vp, sdev_vnodeops); /* apply the default vnodeops at /dev */
456facf4a8dSllai 	vn_exists(vp);
457facf4a8dSllai 
458facf4a8dSllai 	if (vfsp->vfs_mntpt)
459facf4a8dSllai 		dv->sdev_name = i_ddi_strdup(
460facf4a8dSllai 		    (char *)refstr_value(vfsp->vfs_mntpt), KM_SLEEP);
461facf4a8dSllai 	else
462facf4a8dSllai 		/* vfs_mountdev1 set mount point later */
463facf4a8dSllai 		dv->sdev_name = i_ddi_strdup("/dev", KM_SLEEP);
464facf4a8dSllai 	dv->sdev_namelen = strlen(dv->sdev_name); /* '\0' not included */
465facf4a8dSllai 	dv->sdev_path = i_ddi_strdup(devdir, KM_SLEEP);
466facf4a8dSllai 	dv->sdev_ino = SDEV_ROOTINO;
467facf4a8dSllai 	dv->sdev_nlink = 2;		/* name + . (no sdev_insert) */
468facf4a8dSllai 	dv->sdev_dotdot = dv;		/* .. == self */
469facf4a8dSllai 	dv->sdev_attrvp = avp;
470facf4a8dSllai 	dv->sdev_attr = NULL;
471facf4a8dSllai 	mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL);
472facf4a8dSllai 	cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL);
473facf4a8dSllai 	if (strcmp(dv->sdev_name, "/dev") == 0) {
474facf4a8dSllai 		dv->sdev_flags = SDEV_BUILD|SDEV_GLOBAL|SDEV_PERSIST;
475facf4a8dSllai 		bzero(&dv->sdev_handle, sizeof (dv->sdev_handle));
476facf4a8dSllai 		dv->sdev_gdir_gen = 0;
477facf4a8dSllai 	} else {
478facf4a8dSllai 		dv->sdev_flags = SDEV_BUILD;
479facf4a8dSllai 		dv->sdev_flags &= ~SDEV_PERSIST;
480facf4a8dSllai 		bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
481facf4a8dSllai 		dv->sdev_ldir_gen = 0;
482facf4a8dSllai 		dv->sdev_devtree_gen = 0;
483facf4a8dSllai 	}
484facf4a8dSllai 
485aac43a5fSjg 	avl_create(&dv->sdev_entries,
486aac43a5fSjg 	    (int (*)(const void *, const void *))sdev_compare_nodes,
487aac43a5fSjg 	    sizeof (struct sdev_node),
488aac43a5fSjg 	    offsetof(struct sdev_node, sdev_avllink));
489aac43a5fSjg 
490facf4a8dSllai 	rw_enter(&dv->sdev_contents, RW_WRITER);
491facf4a8dSllai 	sdev_set_nodestate(dv, SDEV_READY);
492facf4a8dSllai 	rw_exit(&dv->sdev_contents);
493facf4a8dSllai 	sdev_nc_node_exists(dv);
494facf4a8dSllai 	return (dv);
495facf4a8dSllai }
496facf4a8dSllai 
497*f8927fa6SRobert Mustacchi struct sdev_vop_table vtab[] = {
498*f8927fa6SRobert Mustacchi 	{ "pts", devpts_vnodeops_tbl, &devpts_vnodeops, devpts_validate,
499facf4a8dSllai 	SDEV_DYNAMIC | SDEV_VTOR },
500facf4a8dSllai 
501*f8927fa6SRobert Mustacchi 	{ "vt", devvt_vnodeops_tbl, &devvt_vnodeops, devvt_validate,
502aecfc01dSrui zang - Sun Microsystems - Beijing China 	SDEV_DYNAMIC | SDEV_VTOR },
503aecfc01dSrui zang - Sun Microsystems - Beijing China 
504*f8927fa6SRobert Mustacchi 	{ "zvol", devzvol_vnodeops_tbl, &devzvol_vnodeops,
505dd9c3b29SJerry Jelinek 	devzvol_validate, SDEV_ZONED | SDEV_DYNAMIC | SDEV_VTOR | SDEV_SUBDIR },
506681d9761SEric Taylor 
507*f8927fa6SRobert Mustacchi 	{ "zcons", NULL, NULL, NULL, SDEV_NO_NCACHE },
508facf4a8dSllai 
509*f8927fa6SRobert Mustacchi 	{ "net", devnet_vnodeops_tbl, &devnet_vnodeops, devnet_validate,
510*f8927fa6SRobert Mustacchi 	SDEV_DYNAMIC | SDEV_VTOR | SDEV_SUBDIR },
511d62bc4baSyz 
512*f8927fa6SRobert Mustacchi 	{ "ipnet", devipnet_vnodeops_tbl, &devipnet_vnodeops,
513b127ac41SPhilip Kirk 	devipnet_validate, SDEV_DYNAMIC | SDEV_VTOR | SDEV_NO_NCACHE },
514b127ac41SPhilip Kirk 
51567323fc4SJohn Levon 	/*
51667323fc4SJohn Levon 	 * SDEV_DYNAMIC: prevent calling out to devfsadm, since only the
51767323fc4SJohn Levon 	 * lofi driver controls child nodes.
51867323fc4SJohn Levon 	 *
51967323fc4SJohn Levon 	 * SDEV_PERSIST: ensure devfsadm knows to clean up any persisted
52067323fc4SJohn Levon 	 * stale nodes (e.g. from devfsadm -R).
52167323fc4SJohn Levon 	 *
52267323fc4SJohn Levon 	 * In addition, devfsadm knows not to attempt a rmdir: a zone
52367323fc4SJohn Levon 	 * may hold a reference, which would zombify the node,
52467323fc4SJohn Levon 	 * preventing a mkdir.
52567323fc4SJohn Levon 	 */
52667323fc4SJohn Levon 
527*f8927fa6SRobert Mustacchi 	{ "lofi", NULL, NULL, NULL,
52867323fc4SJohn Levon 	    SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST },
529*f8927fa6SRobert Mustacchi 	{ "rlofi", NULL, NULL, NULL,
53067323fc4SJohn Levon 	    SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST },
5310fbb751dSJohn Levon 
532*f8927fa6SRobert Mustacchi 	{ NULL, NULL, NULL, NULL, 0}
533facf4a8dSllai };
534facf4a8dSllai 
535facf4a8dSllai 
536facf4a8dSllai /*
537facf4a8dSllai  * Build the base root inode
538facf4a8dSllai  */
539facf4a8dSllai ino_t
sdev_mkino(struct sdev_node * dv)540facf4a8dSllai sdev_mkino(struct sdev_node *dv)
541facf4a8dSllai {
542facf4a8dSllai 	ino_t	ino;
543facf4a8dSllai 
544facf4a8dSllai 	/*
545facf4a8dSllai 	 * for now, follow the lead of tmpfs here
546facf4a8dSllai 	 * need to someday understand the requirements here
547facf4a8dSllai 	 */
548facf4a8dSllai 	ino = (ino_t)(uint32_t)((uintptr_t)dv >> 3);
549facf4a8dSllai 	ino += SDEV_ROOTINO + 1;
550facf4a8dSllai 
551facf4a8dSllai 	return (ino);
552facf4a8dSllai }
553facf4a8dSllai 
554681d9761SEric Taylor int
sdev_getlink(struct vnode * linkvp,char ** link)555facf4a8dSllai sdev_getlink(struct vnode *linkvp, char **link)
556facf4a8dSllai {
557facf4a8dSllai 	int err;
558facf4a8dSllai 	char *buf;
559facf4a8dSllai 	struct uio uio = {0};
560facf4a8dSllai 	struct iovec iov = {0};
561facf4a8dSllai 
562facf4a8dSllai 	if (linkvp == NULL)
563facf4a8dSllai 		return (ENOENT);
564facf4a8dSllai 	ASSERT(linkvp->v_type == VLNK);
565facf4a8dSllai 
566facf4a8dSllai 	buf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
567facf4a8dSllai 	iov.iov_base = buf;
568facf4a8dSllai 	iov.iov_len = MAXPATHLEN;
569facf4a8dSllai 	uio.uio_iov = &iov;
570facf4a8dSllai 	uio.uio_iovcnt = 1;
571facf4a8dSllai 	uio.uio_resid = MAXPATHLEN;
572facf4a8dSllai 	uio.uio_segflg = UIO_SYSSPACE;
573facf4a8dSllai 	uio.uio_llimit = MAXOFFSET_T;
574facf4a8dSllai 
575da6c28aaSamw 	err = VOP_READLINK(linkvp, &uio, kcred, NULL);
576facf4a8dSllai 	if (err) {
577facf4a8dSllai 		cmn_err(CE_WARN, "readlink %s failed in dev\n", buf);
578facf4a8dSllai 		kmem_free(buf, MAXPATHLEN);
579facf4a8dSllai 		return (ENOENT);
580facf4a8dSllai 	}
581facf4a8dSllai 
582facf4a8dSllai 	/* mission complete */
583facf4a8dSllai 	*link = i_ddi_strdup(buf, KM_SLEEP);
584facf4a8dSllai 	kmem_free(buf, MAXPATHLEN);
585facf4a8dSllai 	return (0);
586facf4a8dSllai }
587facf4a8dSllai 
588facf4a8dSllai /*
589facf4a8dSllai  * A convenient wrapper to get the devfs node vnode for a device
590facf4a8dSllai  * minor functionality: readlink() of a /dev symlink
591facf4a8dSllai  * Place the link into dv->sdev_symlink
592facf4a8dSllai  */
593facf4a8dSllai static int
sdev_follow_link(struct sdev_node * dv)594facf4a8dSllai sdev_follow_link(struct sdev_node *dv)
595facf4a8dSllai {
596facf4a8dSllai 	int err;
597facf4a8dSllai 	struct vnode *linkvp;
598facf4a8dSllai 	char *link = NULL;
599facf4a8dSllai 
600facf4a8dSllai 	linkvp = SDEVTOV(dv);
601facf4a8dSllai 	if (linkvp == NULL)
602facf4a8dSllai 		return (ENOENT);
603facf4a8dSllai 	ASSERT(linkvp->v_type == VLNK);
604facf4a8dSllai 	err = sdev_getlink(linkvp, &link);
605facf4a8dSllai 	if (err) {
606facf4a8dSllai 		dv->sdev_symlink = NULL;
607facf4a8dSllai 		return (ENOENT);
608facf4a8dSllai 	}
609facf4a8dSllai 
610facf4a8dSllai 	ASSERT(link != NULL);
611facf4a8dSllai 	dv->sdev_symlink = link;
612facf4a8dSllai 	return (0);
613facf4a8dSllai }
614facf4a8dSllai 
615facf4a8dSllai static int
sdev_node_check(struct sdev_node * dv,struct vattr * nvap,void * nargs)616facf4a8dSllai sdev_node_check(struct sdev_node *dv, struct vattr *nvap, void *nargs)
617facf4a8dSllai {
618facf4a8dSllai 	vtype_t otype = SDEVTOV(dv)->v_type;
619facf4a8dSllai 
620facf4a8dSllai 	/*
621facf4a8dSllai 	 * existing sdev_node has a different type.
622facf4a8dSllai 	 */
623facf4a8dSllai 	if (otype != nvap->va_type) {
624facf4a8dSllai 		sdcmn_err9(("sdev_node_check: existing node "
625facf4a8dSllai 		    "  %s type %d does not match new node type %d\n",
626facf4a8dSllai 		    dv->sdev_name, otype, nvap->va_type));
627facf4a8dSllai 		return (EEXIST);
628facf4a8dSllai 	}
629facf4a8dSllai 
630facf4a8dSllai 	/*
631facf4a8dSllai 	 * For a symlink, the target should be the same.
632facf4a8dSllai 	 */
633facf4a8dSllai 	if (otype == VLNK) {
634facf4a8dSllai 		ASSERT(nargs != NULL);
635facf4a8dSllai 		ASSERT(dv->sdev_symlink != NULL);
636facf4a8dSllai 		if (strcmp(dv->sdev_symlink, (char *)nargs) != 0) {
637facf4a8dSllai 			sdcmn_err9(("sdev_node_check: existing node "
638facf4a8dSllai 			    " %s has different symlink %s as new node "
639facf4a8dSllai 			    " %s\n", dv->sdev_name, dv->sdev_symlink,
640facf4a8dSllai 			    (char *)nargs));
641facf4a8dSllai 			return (EEXIST);
642facf4a8dSllai 		}
643facf4a8dSllai 	}
644facf4a8dSllai 
645facf4a8dSllai 	return (0);
646facf4a8dSllai }
647facf4a8dSllai 
648facf4a8dSllai /*
649facf4a8dSllai  * sdev_mknode - a wrapper for sdev_nodeinit(), sdev_nodeready()
650facf4a8dSllai  *
651facf4a8dSllai  * arguments:
652facf4a8dSllai  *	- ddv (parent)
653facf4a8dSllai  *	- nm (child name)
654facf4a8dSllai  *	- newdv (sdev_node for nm is returned here)
655facf4a8dSllai  *	- vap (vattr for the node to be created, va_type should be set.
656b7beec95Sjg  *	- avp (attribute vnode)
657facf4a8dSllai  *	  the defaults should be used if unknown)
658facf4a8dSllai  *	- cred
659facf4a8dSllai  *	- args
660facf4a8dSllai  *	    . tnm (for VLNK)
661facf4a8dSllai  *	    . global sdev_node (for !SDEV_GLOBAL)
662facf4a8dSllai  * 	- state: SDEV_INIT, SDEV_READY
663facf4a8dSllai  *
664facf4a8dSllai  * only ddv, nm, newddv, vap, cred are required for sdev_mknode(SDEV_INIT)
665facf4a8dSllai  *
666facf4a8dSllai  * NOTE:  directory contents writers lock needs to be held before
667facf4a8dSllai  *	  calling this routine.
668facf4a8dSllai  */
669facf4a8dSllai int
sdev_mknode(struct sdev_node * ddv,char * nm,struct sdev_node ** newdv,struct vattr * vap,struct vnode * avp,void * args,struct cred * cred,sdev_node_state_t state)670facf4a8dSllai sdev_mknode(struct sdev_node *ddv, char *nm, struct sdev_node **newdv,
671facf4a8dSllai     struct vattr *vap, struct vnode *avp, void *args, struct cred *cred,
672facf4a8dSllai     sdev_node_state_t state)
673facf4a8dSllai {
674facf4a8dSllai 	int error = 0;
675facf4a8dSllai 	sdev_node_state_t node_state;
676facf4a8dSllai 	struct sdev_node *dv = NULL;
677facf4a8dSllai 
678facf4a8dSllai 	ASSERT(state != SDEV_ZOMBIE);
679facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
680facf4a8dSllai 
681facf4a8dSllai 	if (*newdv) {
682facf4a8dSllai 		dv = *newdv;
683facf4a8dSllai 	} else {
684facf4a8dSllai 		/* allocate and initialize a sdev_node */
685facf4a8dSllai 		if (ddv->sdev_state == SDEV_ZOMBIE) {
686facf4a8dSllai 			sdcmn_err9(("sdev_mknode: parent %s ZOMBIEd\n",
687facf4a8dSllai 			    ddv->sdev_path));
688facf4a8dSllai 			return (ENOENT);
689facf4a8dSllai 		}
690facf4a8dSllai 
691facf4a8dSllai 		error = sdev_nodeinit(ddv, nm, &dv, vap);
692facf4a8dSllai 		if (error != 0) {
693facf4a8dSllai 			sdcmn_err9(("sdev_mknode: error %d,"
694facf4a8dSllai 			    " name %s can not be initialized\n",
695facf4a8dSllai 			    error, nm));
696b7beec95Sjg 			return (error);
697facf4a8dSllai 		}
698facf4a8dSllai 		ASSERT(dv);
699facf4a8dSllai 
700facf4a8dSllai 		/* insert into the directory cache */
7019e5aa9d8SRobert Mustacchi 		sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_ADD);
702facf4a8dSllai 	}
703facf4a8dSllai 
704facf4a8dSllai 	ASSERT(dv);
705facf4a8dSllai 	node_state = dv->sdev_state;
706facf4a8dSllai 	ASSERT(node_state != SDEV_ZOMBIE);
707facf4a8dSllai 
708facf4a8dSllai 	if (state == SDEV_READY) {
709facf4a8dSllai 		switch (node_state) {
710facf4a8dSllai 		case SDEV_INIT:
711facf4a8dSllai 			error = sdev_nodeready(dv, vap, avp, args, cred);
712facf4a8dSllai 			if (error) {
713facf4a8dSllai 				sdcmn_err9(("sdev_mknode: node %s can NOT"
714facf4a8dSllai 				    " be transitioned into READY state, "
715facf4a8dSllai 				    "error %d\n", nm, error));
716facf4a8dSllai 			}
717facf4a8dSllai 			break;
718facf4a8dSllai 		case SDEV_READY:
719facf4a8dSllai 			/*
720facf4a8dSllai 			 * Do some sanity checking to make sure
721facf4a8dSllai 			 * the existing sdev_node is what has been
722facf4a8dSllai 			 * asked for.
723facf4a8dSllai 			 */
724facf4a8dSllai 			error = sdev_node_check(dv, vap, args);
725facf4a8dSllai 			break;
726facf4a8dSllai 		default:
727facf4a8dSllai 			break;
728facf4a8dSllai 		}
729facf4a8dSllai 	}
730facf4a8dSllai 
731facf4a8dSllai 	if (!error) {
732facf4a8dSllai 		*newdv = dv;
733facf4a8dSllai 		ASSERT((*newdv)->sdev_state != SDEV_ZOMBIE);
734facf4a8dSllai 	} else {
7359e5aa9d8SRobert Mustacchi 		sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_DELETE);
7369e5aa9d8SRobert Mustacchi 		/*
7379e5aa9d8SRobert Mustacchi 		 * We created this node, it wasn't passed into us. Therefore it
7389e5aa9d8SRobert Mustacchi 		 * is up to us to delete it.
7399e5aa9d8SRobert Mustacchi 		 */
7409e5aa9d8SRobert Mustacchi 		if (*newdv == NULL)
7419e5aa9d8SRobert Mustacchi 			SDEV_SIMPLE_RELE(dv);
742facf4a8dSllai 		*newdv = NULL;
743facf4a8dSllai 	}
744facf4a8dSllai 
745facf4a8dSllai 	return (error);
746facf4a8dSllai }
747facf4a8dSllai 
748facf4a8dSllai /*
749b7beec95Sjg  * convenient wrapper to change vp's ATIME, CTIME and MTIME
750facf4a8dSllai  */
751facf4a8dSllai void
sdev_update_timestamps(struct vnode * vp,cred_t * cred,uint_t mask)752facf4a8dSllai sdev_update_timestamps(struct vnode *vp, cred_t *cred, uint_t mask)
753facf4a8dSllai {
754facf4a8dSllai 	struct vattr attr;
755facf4a8dSllai 	timestruc_t now;
756facf4a8dSllai 	int err;
757facf4a8dSllai 
758facf4a8dSllai 	ASSERT(vp);
759facf4a8dSllai 	gethrestime(&now);
760facf4a8dSllai 	if (mask & AT_CTIME)
761facf4a8dSllai 		attr.va_ctime = now;
762facf4a8dSllai 	if (mask & AT_MTIME)
763facf4a8dSllai 		attr.va_mtime = now;
764facf4a8dSllai 	if (mask & AT_ATIME)
765facf4a8dSllai 		attr.va_atime = now;
766facf4a8dSllai 
767facf4a8dSllai 	attr.va_mask = (mask & AT_TIMES);
768facf4a8dSllai 	err = VOP_SETATTR(vp, &attr, 0, cred, NULL);
769facf4a8dSllai 	if (err && (err != EROFS)) {
770facf4a8dSllai 		sdcmn_err(("update timestamps error %d\n", err));
771facf4a8dSllai 	}
772facf4a8dSllai }
773facf4a8dSllai 
774facf4a8dSllai /*
775facf4a8dSllai  * the backing store vnode is released here
776facf4a8dSllai  */
777facf4a8dSllai /*ARGSUSED1*/
778facf4a8dSllai void
sdev_nodedestroy(struct sdev_node * dv,uint_t flags)779facf4a8dSllai sdev_nodedestroy(struct sdev_node *dv, uint_t flags)
780facf4a8dSllai {
781facf4a8dSllai 	/* no references */
782facf4a8dSllai 	ASSERT(dv->sdev_nlink == 0);
783facf4a8dSllai 
784facf4a8dSllai 	if (dv->sdev_attrvp != NULLVP) {
785facf4a8dSllai 		VN_RELE(dv->sdev_attrvp);
786facf4a8dSllai 		/*
787facf4a8dSllai 		 * reset the attrvp so that no more
788facf4a8dSllai 		 * references can be made on this already
789facf4a8dSllai 		 * vn_rele() vnode
790facf4a8dSllai 		 */
791facf4a8dSllai 		dv->sdev_attrvp = NULLVP;
792facf4a8dSllai 	}
793facf4a8dSllai 
794facf4a8dSllai 	if (dv->sdev_attr != NULL) {
795facf4a8dSllai 		kmem_free(dv->sdev_attr, sizeof (struct vattr));
796facf4a8dSllai 		dv->sdev_attr = NULL;
797facf4a8dSllai 	}
798facf4a8dSllai 
799facf4a8dSllai 	if (dv->sdev_name != NULL) {
800facf4a8dSllai 		kmem_free(dv->sdev_name, dv->sdev_namelen + 1);
801facf4a8dSllai 		dv->sdev_name = NULL;
802facf4a8dSllai 	}
803facf4a8dSllai 
804facf4a8dSllai 	if (dv->sdev_symlink != NULL) {
805facf4a8dSllai 		kmem_free(dv->sdev_symlink, strlen(dv->sdev_symlink) + 1);
806facf4a8dSllai 		dv->sdev_symlink = NULL;
807facf4a8dSllai 	}
808facf4a8dSllai 
809facf4a8dSllai 	if (dv->sdev_path) {
810facf4a8dSllai 		kmem_free(dv->sdev_path, strlen(dv->sdev_path) + 1);
811facf4a8dSllai 		dv->sdev_path = NULL;
812facf4a8dSllai 	}
813facf4a8dSllai 
814*f8927fa6SRobert Mustacchi 	if (!SDEV_IS_GLOBAL(dv)) {
815facf4a8dSllai 		sdev_prof_free(dv);
816*f8927fa6SRobert Mustacchi 		if (dv->sdev_vnode->v_type != VLNK && dv->sdev_origin != NULL)
817*f8927fa6SRobert Mustacchi 			SDEV_RELE(dv->sdev_origin);
818*f8927fa6SRobert Mustacchi 	}
819facf4a8dSllai 
820aac43a5fSjg 	if (SDEVTOV(dv)->v_type == VDIR) {
821aac43a5fSjg 		ASSERT(SDEV_FIRST_ENTRY(dv) == NULL);
822aac43a5fSjg 		avl_destroy(&dv->sdev_entries);
823aac43a5fSjg 	}
824aac43a5fSjg 
825facf4a8dSllai 	mutex_destroy(&dv->sdev_lookup_lock);
826facf4a8dSllai 	cv_destroy(&dv->sdev_lookup_cv);
827facf4a8dSllai 
828facf4a8dSllai 	/* return node to initial state as per constructor */
829facf4a8dSllai 	(void) memset((void *)&dv->sdev_instance_data, 0,
830facf4a8dSllai 	    sizeof (dv->sdev_instance_data));
831facf4a8dSllai 	vn_invalid(SDEVTOV(dv));
832*f8927fa6SRobert Mustacchi 	dv->sdev_private = NULL;
833facf4a8dSllai 	kmem_cache_free(sdev_node_cache, dv);
834facf4a8dSllai }
835facf4a8dSllai 
836facf4a8dSllai /*
837facf4a8dSllai  * DIRECTORY CACHE lookup
838facf4a8dSllai  */
839facf4a8dSllai struct sdev_node *
sdev_findbyname(struct sdev_node * ddv,char * nm)840facf4a8dSllai sdev_findbyname(struct sdev_node *ddv, char *nm)
841facf4a8dSllai {
842facf4a8dSllai 	struct sdev_node *dv;
843aac43a5fSjg 	struct sdev_node dvtmp;
844aac43a5fSjg 	avl_index_t	where;
845facf4a8dSllai 
846facf4a8dSllai 	ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
847facf4a8dSllai 
848aac43a5fSjg 	dvtmp.sdev_name = nm;
849aac43a5fSjg 	dv = avl_find(&ddv->sdev_entries, &dvtmp, &where);
850aac43a5fSjg 	if (dv) {
851aac43a5fSjg 		ASSERT(dv->sdev_dotdot == ddv);
852aac43a5fSjg 		ASSERT(strcmp(dv->sdev_name, nm) == 0);
8539e5aa9d8SRobert Mustacchi 		ASSERT(dv->sdev_state != SDEV_ZOMBIE);
854ecb4d93aSjg 		SDEV_HOLD(dv);
855ecb4d93aSjg 		return (dv);
856facf4a8dSllai 	}
857facf4a8dSllai 	return (NULL);
858facf4a8dSllai }
859facf4a8dSllai 
860facf4a8dSllai /*
861facf4a8dSllai  * Inserts a new sdev_node in a parent directory
862facf4a8dSllai  */
863facf4a8dSllai void
sdev_direnter(struct sdev_node * ddv,struct sdev_node * dv)864facf4a8dSllai sdev_direnter(struct sdev_node *ddv, struct sdev_node *dv)
865facf4a8dSllai {
866aac43a5fSjg 	avl_index_t where;
867aac43a5fSjg 
868facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
869facf4a8dSllai 	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
870facf4a8dSllai 	ASSERT(ddv->sdev_nlink >= 2);
871facf4a8dSllai 	ASSERT(dv->sdev_nlink == 0);
8729e5aa9d8SRobert Mustacchi 	ASSERT(dv->sdev_state != SDEV_ZOMBIE);
873facf4a8dSllai 
874facf4a8dSllai 	dv->sdev_dotdot = ddv;
875aac43a5fSjg 	VERIFY(avl_find(&ddv->sdev_entries, dv, &where) == NULL);
876aac43a5fSjg 	avl_insert(&ddv->sdev_entries, dv, where);
877facf4a8dSllai 	ddv->sdev_nlink++;
878facf4a8dSllai }
879facf4a8dSllai 
880facf4a8dSllai /*
881facf4a8dSllai  * The following check is needed because while sdev_nodes are linked
882facf4a8dSllai  * in SDEV_INIT state, they have their link counts incremented only
883facf4a8dSllai  * in SDEV_READY state.
884facf4a8dSllai  */
885facf4a8dSllai static void
decr_link(struct sdev_node * dv)886facf4a8dSllai decr_link(struct sdev_node *dv)
887facf4a8dSllai {
8889e5aa9d8SRobert Mustacchi 	VERIFY(RW_WRITE_HELD(&dv->sdev_contents));
8899e5aa9d8SRobert Mustacchi 	if (dv->sdev_state != SDEV_INIT) {
8909e5aa9d8SRobert Mustacchi 		VERIFY(dv->sdev_nlink >= 1);
891facf4a8dSllai 		dv->sdev_nlink--;
8929e5aa9d8SRobert Mustacchi 	} else {
8939e5aa9d8SRobert Mustacchi 		VERIFY(dv->sdev_nlink == 0);
8949e5aa9d8SRobert Mustacchi 	}
895facf4a8dSllai }
896facf4a8dSllai 
897facf4a8dSllai /*
898facf4a8dSllai  * Delete an existing dv from directory cache
899facf4a8dSllai  *
9009e5aa9d8SRobert Mustacchi  * In the case of a node is still held by non-zero reference count, the node is
9019e5aa9d8SRobert Mustacchi  * put into ZOMBIE state. The node is always unlinked from its parent, but it is
9029e5aa9d8SRobert Mustacchi  * not destroyed via sdev_inactive until its reference count reaches "0".
903facf4a8dSllai  */
9049e5aa9d8SRobert Mustacchi static void
sdev_dirdelete(struct sdev_node * ddv,struct sdev_node * dv)905facf4a8dSllai sdev_dirdelete(struct sdev_node *ddv, struct sdev_node *dv)
906facf4a8dSllai {
907facf4a8dSllai 	struct vnode *vp;
9089e5aa9d8SRobert Mustacchi 	sdev_node_state_t os;
909facf4a8dSllai 
910facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
911facf4a8dSllai 
912facf4a8dSllai 	vp = SDEVTOV(dv);
913facf4a8dSllai 	mutex_enter(&vp->v_lock);
9149e5aa9d8SRobert Mustacchi 	rw_enter(&dv->sdev_contents, RW_WRITER);
9159e5aa9d8SRobert Mustacchi 	os = dv->sdev_state;
9169e5aa9d8SRobert Mustacchi 	ASSERT(os != SDEV_ZOMBIE);
9179e5aa9d8SRobert Mustacchi 	dv->sdev_state = SDEV_ZOMBIE;
918facf4a8dSllai 
9199e5aa9d8SRobert Mustacchi 	/*
9209e5aa9d8SRobert Mustacchi 	 * unlink ourselves from the parent directory now to take care of the ..
9219e5aa9d8SRobert Mustacchi 	 * link. However, if we're a directory, we don't remove our reference to
9229e5aa9d8SRobert Mustacchi 	 * ourself eg. '.' until we are torn down in the inactive callback.
9239e5aa9d8SRobert Mustacchi 	 */
9249e5aa9d8SRobert Mustacchi 	decr_link(ddv);
925aac43a5fSjg 	avl_remove(&ddv->sdev_entries, dv);
9269e5aa9d8SRobert Mustacchi 	/*
9279e5aa9d8SRobert Mustacchi 	 * sdev_inactive expects nodes to have a link to themselves when we're
9289e5aa9d8SRobert Mustacchi 	 * tearing them down. If we're transitioning from the initial state to
9299e5aa9d8SRobert Mustacchi 	 * zombie and not via ready, then we're not going to have this link that
9309e5aa9d8SRobert Mustacchi 	 * comes from the node being ready. As a result, we need to increment
9319e5aa9d8SRobert Mustacchi 	 * our link count by one to account for this.
9329e5aa9d8SRobert Mustacchi 	 */
9339e5aa9d8SRobert Mustacchi 	if (os == SDEV_INIT && dv->sdev_nlink == 0)
9349e5aa9d8SRobert Mustacchi 		dv->sdev_nlink++;
9359e5aa9d8SRobert Mustacchi 	rw_exit(&dv->sdev_contents);
936facf4a8dSllai 	mutex_exit(&vp->v_lock);
937facf4a8dSllai }
938facf4a8dSllai 
939facf4a8dSllai /*
940facf4a8dSllai  * check if the source is in the path of the target
941facf4a8dSllai  *
942facf4a8dSllai  * source and target are different
943facf4a8dSllai  */
944facf4a8dSllai /*ARGSUSED2*/
945facf4a8dSllai static int
sdev_checkpath(struct sdev_node * sdv,struct sdev_node * tdv,struct cred * cred)946facf4a8dSllai sdev_checkpath(struct sdev_node *sdv, struct sdev_node *tdv, struct cred *cred)
947facf4a8dSllai {
948facf4a8dSllai 	int error = 0;
949facf4a8dSllai 	struct sdev_node *dotdot, *dir;
950facf4a8dSllai 
951facf4a8dSllai 	dotdot = tdv->sdev_dotdot;
952facf4a8dSllai 	ASSERT(dotdot);
953facf4a8dSllai 
954facf4a8dSllai 	/* fs root */
955facf4a8dSllai 	if (dotdot == tdv) {
956facf4a8dSllai 		return (0);
957facf4a8dSllai 	}
958facf4a8dSllai 
959facf4a8dSllai 	for (;;) {
960facf4a8dSllai 		/*
961facf4a8dSllai 		 * avoid error cases like
962facf4a8dSllai 		 *	mv a a/b
963facf4a8dSllai 		 *	mv a a/b/c
964facf4a8dSllai 		 *	etc.
965facf4a8dSllai 		 */
966facf4a8dSllai 		if (dotdot == sdv) {
967facf4a8dSllai 			error = EINVAL;
968facf4a8dSllai 			break;
969facf4a8dSllai 		}
970facf4a8dSllai 
971facf4a8dSllai 		dir = dotdot;
972facf4a8dSllai 		dotdot = dir->sdev_dotdot;
973facf4a8dSllai 
974facf4a8dSllai 		/* done checking because root is reached */
975facf4a8dSllai 		if (dir == dotdot) {
976facf4a8dSllai 			break;
977facf4a8dSllai 		}
978facf4a8dSllai 	}
979facf4a8dSllai 	return (error);
980facf4a8dSllai }
981facf4a8dSllai 
982facf4a8dSllai int
sdev_rnmnode(struct sdev_node * oddv,struct sdev_node * odv,struct sdev_node * nddv,struct sdev_node ** ndvp,char * nnm,struct cred * cred)983facf4a8dSllai sdev_rnmnode(struct sdev_node *oddv, struct sdev_node *odv,
984facf4a8dSllai     struct sdev_node *nddv, struct sdev_node **ndvp, char *nnm,
985facf4a8dSllai     struct cred *cred)
986facf4a8dSllai {
987facf4a8dSllai 	int error = 0;
988facf4a8dSllai 	struct vnode *ovp = SDEVTOV(odv);
989facf4a8dSllai 	struct vnode *nvp;
990facf4a8dSllai 	struct vattr vattr;
991facf4a8dSllai 	int doingdir = (ovp->v_type == VDIR);
992facf4a8dSllai 	char *link = NULL;
9930bfaec69Sllai 	int samedir = (oddv == nddv) ? 1 : 0;
9940bfaec69Sllai 	int bkstore = 0;
9950bfaec69Sllai 	struct sdev_node *idv = NULL;
9960bfaec69Sllai 	struct sdev_node *ndv = NULL;
9970bfaec69Sllai 	timestruc_t now;
9980bfaec69Sllai 
999bb5fffbeSJerry Gilliam 	vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID;
1000da6c28aaSamw 	error = VOP_GETATTR(ovp, &vattr, 0, cred, NULL);
10010bfaec69Sllai 	if (error)
10020bfaec69Sllai 		return (error);
10030bfaec69Sllai 
10040bfaec69Sllai 	if (!samedir)
10050bfaec69Sllai 		rw_enter(&oddv->sdev_contents, RW_WRITER);
10060bfaec69Sllai 	rw_enter(&nddv->sdev_contents, RW_WRITER);
10070bfaec69Sllai 
10080bfaec69Sllai 	/*
10090bfaec69Sllai 	 * the source may have been deleted by another thread before
10100bfaec69Sllai 	 * we gets here.
10110bfaec69Sllai 	 */
10120bfaec69Sllai 	if (odv->sdev_state != SDEV_READY) {
10130bfaec69Sllai 		error = ENOENT;
10140bfaec69Sllai 		goto err_out;
10150bfaec69Sllai 	}
10160bfaec69Sllai 
10170bfaec69Sllai 	if (doingdir && (odv == nddv)) {
10180bfaec69Sllai 		error = EINVAL;
10190bfaec69Sllai 		goto err_out;
10200bfaec69Sllai 	}
1021facf4a8dSllai 
1022facf4a8dSllai 	/*
1023facf4a8dSllai 	 * If renaming a directory, and the parents are different (".." must be
1024facf4a8dSllai 	 * changed) then the source dir must not be in the dir hierarchy above
1025facf4a8dSllai 	 * the target since it would orphan everything below the source dir.
1026facf4a8dSllai 	 */
1027facf4a8dSllai 	if (doingdir && (oddv != nddv)) {
1028facf4a8dSllai 		error = sdev_checkpath(odv, nddv, cred);
1029facf4a8dSllai 		if (error)
10300bfaec69Sllai 			goto err_out;
1031facf4a8dSllai 	}
1032facf4a8dSllai 
10339e5aa9d8SRobert Mustacchi 	/* fix the source for a symlink */
10349e5aa9d8SRobert Mustacchi 	if (vattr.va_type == VLNK) {
10359e5aa9d8SRobert Mustacchi 		if (odv->sdev_symlink == NULL) {
10369e5aa9d8SRobert Mustacchi 			error = sdev_follow_link(odv);
10379e5aa9d8SRobert Mustacchi 			if (error) {
10389e5aa9d8SRobert Mustacchi 				/*
10399e5aa9d8SRobert Mustacchi 				 * The underlying symlink doesn't exist. This
10409e5aa9d8SRobert Mustacchi 				 * node probably shouldn't even exist. While
10419e5aa9d8SRobert Mustacchi 				 * it's a bit jarring to consumers, we're going
10429e5aa9d8SRobert Mustacchi 				 * to remove the node from /dev.
10439e5aa9d8SRobert Mustacchi 				 */
10449e5aa9d8SRobert Mustacchi 				if (SDEV_IS_PERSIST((*ndvp)))
10459e5aa9d8SRobert Mustacchi 					bkstore = 1;
10469e5aa9d8SRobert Mustacchi 				sdev_dirdelete(oddv, odv);
10479e5aa9d8SRobert Mustacchi 				if (bkstore) {
10489e5aa9d8SRobert Mustacchi 					ASSERT(nddv->sdev_attrvp);
10499e5aa9d8SRobert Mustacchi 					error = VOP_REMOVE(nddv->sdev_attrvp,
10509e5aa9d8SRobert Mustacchi 					    nnm, cred, NULL, 0);
10519e5aa9d8SRobert Mustacchi 					if (error)
10529e5aa9d8SRobert Mustacchi 						goto err_out;
10539e5aa9d8SRobert Mustacchi 				}
10549e5aa9d8SRobert Mustacchi 				error = ENOENT;
10559e5aa9d8SRobert Mustacchi 				goto err_out;
10569e5aa9d8SRobert Mustacchi 			}
10579e5aa9d8SRobert Mustacchi 		}
10589e5aa9d8SRobert Mustacchi 		ASSERT(odv->sdev_symlink);
10599e5aa9d8SRobert Mustacchi 		link = i_ddi_strdup(odv->sdev_symlink, KM_SLEEP);
10609e5aa9d8SRobert Mustacchi 	}
10619e5aa9d8SRobert Mustacchi 
10620bfaec69Sllai 	/* destination existing */
1063facf4a8dSllai 	if (*ndvp) {
1064facf4a8dSllai 		nvp = SDEVTOV(*ndvp);
1065facf4a8dSllai 		ASSERT(nvp);
1066facf4a8dSllai 
1067facf4a8dSllai 		/* handling renaming to itself */
10680bfaec69Sllai 		if (odv == *ndvp) {
10690bfaec69Sllai 			error = 0;
10700bfaec69Sllai 			goto err_out;
10710bfaec69Sllai 		}
10720bfaec69Sllai 
10730bfaec69Sllai 		if (nvp->v_type == VDIR) {
10740bfaec69Sllai 			if (!doingdir) {
10750bfaec69Sllai 				error = EISDIR;
10760bfaec69Sllai 				goto err_out;
10770bfaec69Sllai 			}
10780bfaec69Sllai 
10790bfaec69Sllai 			if (vn_vfswlock(nvp)) {
10800bfaec69Sllai 				error = EBUSY;
10810bfaec69Sllai 				goto err_out;
10820bfaec69Sllai 			}
10830bfaec69Sllai 
10840bfaec69Sllai 			if (vn_mountedvfs(nvp) != NULL) {
10850bfaec69Sllai 				vn_vfsunlock(nvp);
10860bfaec69Sllai 				error = EBUSY;
10870bfaec69Sllai 				goto err_out;
10880bfaec69Sllai 			}
10890bfaec69Sllai 
10900bfaec69Sllai 			/* in case dir1 exists in dir2 and "mv dir1 dir2" */
10910bfaec69Sllai 			if ((*ndvp)->sdev_nlink > 2) {
10920bfaec69Sllai 				vn_vfsunlock(nvp);
10930bfaec69Sllai 				error = EEXIST;
10940bfaec69Sllai 				goto err_out;
10950bfaec69Sllai 			}
10960bfaec69Sllai 			vn_vfsunlock(nvp);
1097facf4a8dSllai 
10989e5aa9d8SRobert Mustacchi 			/*
10999e5aa9d8SRobert Mustacchi 			 * We did not place the hold on *ndvp, so even though
11009e5aa9d8SRobert Mustacchi 			 * we're deleting the node, we should not get rid of our
11019e5aa9d8SRobert Mustacchi 			 * reference.
11029e5aa9d8SRobert Mustacchi 			 */
11039e5aa9d8SRobert Mustacchi 			sdev_dirdelete(nddv, *ndvp);
11040bfaec69Sllai 			*ndvp = NULL;
1105b7beec95Sjg 			ASSERT(nddv->sdev_attrvp);
11060bfaec69Sllai 			error = VOP_RMDIR(nddv->sdev_attrvp, nnm,
1107e37c6c37Scth 			    nddv->sdev_attrvp, cred, NULL, 0);
11080bfaec69Sllai 			if (error)
11090bfaec69Sllai 				goto err_out;
11100bfaec69Sllai 		} else {
11110bfaec69Sllai 			if (doingdir) {
11120bfaec69Sllai 				error = ENOTDIR;
11130bfaec69Sllai 				goto err_out;
11140bfaec69Sllai 			}
11150bfaec69Sllai 
11160bfaec69Sllai 			if (SDEV_IS_PERSIST((*ndvp))) {
11170bfaec69Sllai 				bkstore = 1;
11180bfaec69Sllai 			}
1119facf4a8dSllai 
1120facf4a8dSllai 			/*
11219e5aa9d8SRobert Mustacchi 			 * Get rid of the node from the directory cache note.
11229e5aa9d8SRobert Mustacchi 			 * Don't forget that it's not up to us to remove the vn
11239e5aa9d8SRobert Mustacchi 			 * ref on the sdev node, as we did not place it.
1124facf4a8dSllai 			 */
11259e5aa9d8SRobert Mustacchi 			sdev_dirdelete(nddv, *ndvp);
11260bfaec69Sllai 			*ndvp = NULL;
11270bfaec69Sllai 			if (bkstore) {
1128b7beec95Sjg 				ASSERT(nddv->sdev_attrvp);
11290bfaec69Sllai 				error = VOP_REMOVE(nddv->sdev_attrvp,
1130da6c28aaSamw 				    nnm, cred, NULL, 0);
11310bfaec69Sllai 				if (error)
1132e37c6c37Scth 					goto err_out;
1133facf4a8dSllai 			}
1134facf4a8dSllai 		}
1135facf4a8dSllai 	}
1136facf4a8dSllai 
11370bfaec69Sllai 	/*
11380bfaec69Sllai 	 * make a fresh node from the source attrs
11390bfaec69Sllai 	 */
11400bfaec69Sllai 	ASSERT(RW_WRITE_HELD(&nddv->sdev_contents));
11410bfaec69Sllai 	error = sdev_mknode(nddv, nnm, ndvp, &vattr,
11420bfaec69Sllai 	    NULL, (void *)link, cred, SDEV_READY);
1143facf4a8dSllai 
11449e5aa9d8SRobert Mustacchi 	if (link != NULL) {
1145facf4a8dSllai 		kmem_free(link, strlen(link) + 1);
11469e5aa9d8SRobert Mustacchi 		link = NULL;
11479e5aa9d8SRobert Mustacchi 	}
1148facf4a8dSllai 
11490bfaec69Sllai 	if (error)
11500bfaec69Sllai 		goto err_out;
11510bfaec69Sllai 	ASSERT(*ndvp);
11520bfaec69Sllai 	ASSERT((*ndvp)->sdev_state == SDEV_READY);
11530bfaec69Sllai 
11540bfaec69Sllai 	/* move dir contents */
11550bfaec69Sllai 	if (doingdir) {
1156aac43a5fSjg 		for (idv = SDEV_FIRST_ENTRY(odv); idv;
1157aac43a5fSjg 		    idv = SDEV_NEXT_ENTRY(odv, idv)) {
11589e5aa9d8SRobert Mustacchi 			SDEV_HOLD(idv);
11590bfaec69Sllai 			error = sdev_rnmnode(odv, idv,
11600bfaec69Sllai 			    (struct sdev_node *)(*ndvp), &ndv,
11610bfaec69Sllai 			    idv->sdev_name, cred);
11629e5aa9d8SRobert Mustacchi 			SDEV_RELE(idv);
11630bfaec69Sllai 			if (error)
11640bfaec69Sllai 				goto err_out;
11650bfaec69Sllai 			ndv = NULL;
11660bfaec69Sllai 		}
11670bfaec69Sllai 	}
11680bfaec69Sllai 
11690bfaec69Sllai 	if ((*ndvp)->sdev_attrvp) {
11700bfaec69Sllai 		sdev_update_timestamps((*ndvp)->sdev_attrvp, kcred,
11710bfaec69Sllai 		    AT_CTIME|AT_ATIME);
11720bfaec69Sllai 	} else {
11730bfaec69Sllai 		ASSERT((*ndvp)->sdev_attr);
11740bfaec69Sllai 		gethrestime(&now);
11750bfaec69Sllai 		(*ndvp)->sdev_attr->va_ctime = now;
11760bfaec69Sllai 		(*ndvp)->sdev_attr->va_atime = now;
11770bfaec69Sllai 	}
11780bfaec69Sllai 
11790bfaec69Sllai 	if (nddv->sdev_attrvp) {
11800bfaec69Sllai 		sdev_update_timestamps(nddv->sdev_attrvp, kcred,
11810bfaec69Sllai 		    AT_MTIME|AT_ATIME);
11820bfaec69Sllai 	} else {
11830bfaec69Sllai 		ASSERT(nddv->sdev_attr);
11840bfaec69Sllai 		gethrestime(&now);
11850bfaec69Sllai 		nddv->sdev_attr->va_mtime = now;
11860bfaec69Sllai 		nddv->sdev_attr->va_atime = now;
11870bfaec69Sllai 	}
11880bfaec69Sllai 	rw_exit(&nddv->sdev_contents);
11890bfaec69Sllai 	if (!samedir)
11900bfaec69Sllai 		rw_exit(&oddv->sdev_contents);
11910bfaec69Sllai 
1192facf4a8dSllai 	SDEV_RELE(*ndvp);
11930bfaec69Sllai 	return (error);
11940bfaec69Sllai 
11950bfaec69Sllai err_out:
11969e5aa9d8SRobert Mustacchi 	if (link != NULL) {
11979e5aa9d8SRobert Mustacchi 		kmem_free(link, strlen(link) + 1);
11989e5aa9d8SRobert Mustacchi 		link = NULL;
11999e5aa9d8SRobert Mustacchi 	}
12009e5aa9d8SRobert Mustacchi 
12010bfaec69Sllai 	rw_exit(&nddv->sdev_contents);
12020bfaec69Sllai 	if (!samedir)
12030bfaec69Sllai 		rw_exit(&oddv->sdev_contents);
12040bfaec69Sllai 	return (error);
1205facf4a8dSllai }
1206facf4a8dSllai 
1207facf4a8dSllai /*
1208facf4a8dSllai  * Merge sdev_node specific information into an attribute structure.
1209facf4a8dSllai  *
1210facf4a8dSllai  * note: sdev_node is not locked here
1211facf4a8dSllai  */
1212facf4a8dSllai void
sdev_vattr_merge(struct sdev_node * dv,struct vattr * vap)1213facf4a8dSllai sdev_vattr_merge(struct sdev_node *dv, struct vattr *vap)
1214facf4a8dSllai {
1215facf4a8dSllai 	struct vnode *vp = SDEVTOV(dv);
1216facf4a8dSllai 
1217facf4a8dSllai 	vap->va_nlink = dv->sdev_nlink;
1218facf4a8dSllai 	vap->va_nodeid = dv->sdev_ino;
1219facf4a8dSllai 	vap->va_fsid = SDEVTOV(dv->sdev_dotdot)->v_rdev;
1220facf4a8dSllai 	vap->va_type = vp->v_type;
1221facf4a8dSllai 
1222facf4a8dSllai 	if (vp->v_type == VDIR) {
1223facf4a8dSllai 		vap->va_rdev = 0;
1224facf4a8dSllai 		vap->va_fsid = vp->v_rdev;
1225facf4a8dSllai 	} else if (vp->v_type == VLNK) {
1226facf4a8dSllai 		vap->va_rdev = 0;
1227facf4a8dSllai 		vap->va_mode  &= ~S_IFMT;
1228facf4a8dSllai 		vap->va_mode |= S_IFLNK;
1229facf4a8dSllai 	} else if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
1230facf4a8dSllai 		vap->va_rdev = vp->v_rdev;
1231facf4a8dSllai 		vap->va_mode &= ~S_IFMT;
1232facf4a8dSllai 		if (vap->va_type == VCHR)
1233facf4a8dSllai 			vap->va_mode |= S_IFCHR;
1234facf4a8dSllai 		else
1235facf4a8dSllai 			vap->va_mode |= S_IFBLK;
1236facf4a8dSllai 	} else {
1237facf4a8dSllai 		vap->va_rdev = 0;
1238facf4a8dSllai 	}
1239facf4a8dSllai }
1240facf4a8dSllai 
1241681d9761SEric Taylor struct vattr *
sdev_getdefault_attr(enum vtype type)1242facf4a8dSllai sdev_getdefault_attr(enum vtype type)
1243facf4a8dSllai {
1244facf4a8dSllai 	if (type == VDIR)
1245facf4a8dSllai 		return (&sdev_vattr_dir);
1246facf4a8dSllai 	else if (type == VCHR)
1247facf4a8dSllai 		return (&sdev_vattr_chr);
1248facf4a8dSllai 	else if (type == VBLK)
1249facf4a8dSllai 		return (&sdev_vattr_blk);
1250facf4a8dSllai 	else if (type == VLNK)
1251facf4a8dSllai 		return (&sdev_vattr_lnk);
1252facf4a8dSllai 	else
1253facf4a8dSllai 		return (NULL);
1254facf4a8dSllai }
1255facf4a8dSllai int
sdev_to_vp(struct sdev_node * dv,struct vnode ** vpp)1256facf4a8dSllai sdev_to_vp(struct sdev_node *dv, struct vnode **vpp)
1257facf4a8dSllai {
1258facf4a8dSllai 	int rv = 0;
1259facf4a8dSllai 	struct vnode *vp = SDEVTOV(dv);
1260facf4a8dSllai 
1261facf4a8dSllai 	switch (vp->v_type) {
1262facf4a8dSllai 	case VCHR:
1263facf4a8dSllai 	case VBLK:
1264facf4a8dSllai 		/*
1265facf4a8dSllai 		 * If vnode is a device, return special vnode instead
1266facf4a8dSllai 		 * (though it knows all about -us- via sp->s_realvp)
1267facf4a8dSllai 		 */
1268facf4a8dSllai 		*vpp = specvp(vp, vp->v_rdev, vp->v_type, kcred);
1269facf4a8dSllai 		VN_RELE(vp);
1270facf4a8dSllai 		if (*vpp == NULLVP)
1271facf4a8dSllai 			rv = ENOSYS;
1272facf4a8dSllai 		break;
1273facf4a8dSllai 	default:	/* most types are returned as is */
1274facf4a8dSllai 		*vpp = vp;
1275facf4a8dSllai 		break;
1276facf4a8dSllai 	}
1277facf4a8dSllai 	return (rv);
1278facf4a8dSllai }
1279facf4a8dSllai 
1280facf4a8dSllai /*
1281facf4a8dSllai  * junction between devname and root file system, e.g. ufs
1282facf4a8dSllai  */
1283facf4a8dSllai int
devname_backstore_lookup(struct sdev_node * ddv,char * nm,struct vnode ** rvp)1284facf4a8dSllai devname_backstore_lookup(struct sdev_node *ddv, char *nm, struct vnode **rvp)
1285facf4a8dSllai {
1286facf4a8dSllai 	struct vnode *rdvp = ddv->sdev_attrvp;
1287facf4a8dSllai 	int rval = 0;
1288facf4a8dSllai 
1289facf4a8dSllai 	ASSERT(rdvp);
1290facf4a8dSllai 
1291da6c28aaSamw 	rval = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, kcred, NULL, NULL,
1292da6c28aaSamw 	    NULL);
1293facf4a8dSllai 	return (rval);
1294facf4a8dSllai }
1295facf4a8dSllai 
1296facf4a8dSllai static int
sdev_filldir_from_store(struct sdev_node * ddv,int dlen,struct cred * cred)1297facf4a8dSllai sdev_filldir_from_store(struct sdev_node *ddv, int dlen, struct cred *cred)
1298facf4a8dSllai {
1299facf4a8dSllai 	struct sdev_node *dv = NULL;
1300facf4a8dSllai 	char	*nm;
1301facf4a8dSllai 	struct vnode *dirvp;
1302facf4a8dSllai 	int	error;
1303facf4a8dSllai 	vnode_t	*vp;
1304facf4a8dSllai 	int eof;
1305facf4a8dSllai 	struct iovec iov;
1306facf4a8dSllai 	struct uio uio;
1307facf4a8dSllai 	struct dirent64 *dp;
1308facf4a8dSllai 	dirent64_t *dbuf;
1309facf4a8dSllai 	size_t dbuflen;
1310facf4a8dSllai 	struct vattr vattr;
1311facf4a8dSllai 	char *link = NULL;
1312facf4a8dSllai 
1313facf4a8dSllai 	if (ddv->sdev_attrvp == NULL)
1314facf4a8dSllai 		return (0);
1315facf4a8dSllai 	if (!(ddv->sdev_flags & SDEV_BUILD))
1316facf4a8dSllai 		return (0);
1317facf4a8dSllai 
1318facf4a8dSllai 	dirvp = ddv->sdev_attrvp;
1319facf4a8dSllai 	VN_HOLD(dirvp);
1320facf4a8dSllai 	dbuf = kmem_zalloc(dlen, KM_SLEEP);
1321facf4a8dSllai 
1322facf4a8dSllai 	uio.uio_iov = &iov;
1323facf4a8dSllai 	uio.uio_iovcnt = 1;
1324facf4a8dSllai 	uio.uio_segflg = UIO_SYSSPACE;
1325facf4a8dSllai 	uio.uio_fmode = 0;
1326facf4a8dSllai 	uio.uio_extflg = UIO_COPY_CACHED;
1327facf4a8dSllai 	uio.uio_loffset = 0;
1328facf4a8dSllai 	uio.uio_llimit = MAXOFFSET_T;
1329facf4a8dSllai 
1330facf4a8dSllai 	eof = 0;
1331facf4a8dSllai 	error = 0;
1332facf4a8dSllai 	while (!error && !eof) {
1333facf4a8dSllai 		uio.uio_resid = dlen;
1334facf4a8dSllai 		iov.iov_base = (char *)dbuf;
1335facf4a8dSllai 		iov.iov_len = dlen;
1336facf4a8dSllai 		(void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL);
1337da6c28aaSamw 		error = VOP_READDIR(dirvp, &uio, kcred, &eof, NULL, 0);
1338facf4a8dSllai 		VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL);
1339facf4a8dSllai 
1340facf4a8dSllai 		dbuflen = dlen - uio.uio_resid;
1341facf4a8dSllai 		if (error || dbuflen == 0)
1342facf4a8dSllai 			break;
1343facf4a8dSllai 
1344681d9761SEric Taylor 		if (!(ddv->sdev_flags & SDEV_BUILD))
1345facf4a8dSllai 			break;
1346facf4a8dSllai 
1347facf4a8dSllai 		for (dp = dbuf; ((intptr_t)dp <
1348facf4a8dSllai 		    (intptr_t)dbuf + dbuflen);
1349facf4a8dSllai 		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
1350facf4a8dSllai 			nm = dp->d_name;
1351facf4a8dSllai 
1352facf4a8dSllai 			if (strcmp(nm, ".") == 0 ||
1353facf4a8dSllai 			    strcmp(nm, "..") == 0)
1354facf4a8dSllai 				continue;
1355facf4a8dSllai 
1356facf4a8dSllai 			vp = NULLVP;
1357facf4a8dSllai 			dv = sdev_cache_lookup(ddv, nm);
1358facf4a8dSllai 			if (dv) {
13599e5aa9d8SRobert Mustacchi 				VERIFY(dv->sdev_state != SDEV_ZOMBIE);
13609e5aa9d8SRobert Mustacchi 				SDEV_SIMPLE_RELE(dv);
1361facf4a8dSllai 				continue;
1362facf4a8dSllai 			}
1363facf4a8dSllai 
1364facf4a8dSllai 			/* refill the cache if not already */
1365facf4a8dSllai 			error = devname_backstore_lookup(ddv, nm, &vp);
1366facf4a8dSllai 			if (error)
1367facf4a8dSllai 				continue;
1368facf4a8dSllai 
1369bb5fffbeSJerry Gilliam 			vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID;
1370da6c28aaSamw 			error = VOP_GETATTR(vp, &vattr, 0, cred, NULL);
1371facf4a8dSllai 			if (error)
1372facf4a8dSllai 				continue;
1373facf4a8dSllai 
1374facf4a8dSllai 			if (vattr.va_type == VLNK) {
1375facf4a8dSllai 				error = sdev_getlink(vp, &link);
1376facf4a8dSllai 				if (error) {
1377facf4a8dSllai 					continue;
1378facf4a8dSllai 				}
1379facf4a8dSllai 				ASSERT(link != NULL);
1380facf4a8dSllai 			}
1381facf4a8dSllai 
1382facf4a8dSllai 			if (!rw_tryupgrade(&ddv->sdev_contents)) {
1383facf4a8dSllai 				rw_exit(&ddv->sdev_contents);
1384facf4a8dSllai 				rw_enter(&ddv->sdev_contents, RW_WRITER);
1385facf4a8dSllai 			}
1386facf4a8dSllai 			error = sdev_mknode(ddv, nm, &dv, &vattr, vp, link,
1387facf4a8dSllai 			    cred, SDEV_READY);
1388facf4a8dSllai 			rw_downgrade(&ddv->sdev_contents);
1389facf4a8dSllai 
1390facf4a8dSllai 			if (link != NULL) {
1391facf4a8dSllai 				kmem_free(link, strlen(link) + 1);
1392facf4a8dSllai 				link = NULL;
1393facf4a8dSllai 			}
1394facf4a8dSllai 
1395facf4a8dSllai 			if (!error) {
1396facf4a8dSllai 				ASSERT(dv);
1397facf4a8dSllai 				ASSERT(dv->sdev_state != SDEV_ZOMBIE);
1398facf4a8dSllai 				SDEV_SIMPLE_RELE(dv);
1399facf4a8dSllai 			}
1400facf4a8dSllai 			vp = NULL;
1401facf4a8dSllai 			dv = NULL;
1402facf4a8dSllai 		}
1403facf4a8dSllai 	}
1404facf4a8dSllai 
1405facf4a8dSllai done:
1406facf4a8dSllai 	VN_RELE(dirvp);
1407facf4a8dSllai 	kmem_free(dbuf, dlen);
1408facf4a8dSllai 
1409facf4a8dSllai 	return (error);
1410facf4a8dSllai }
1411facf4a8dSllai 
14126b938478Sjg void
sdev_filldir_dynamic(struct sdev_node * ddv)1413facf4a8dSllai sdev_filldir_dynamic(struct sdev_node *ddv)
1414facf4a8dSllai {
1415facf4a8dSllai 	int error;
1416facf4a8dSllai 	int i;
1417bb5fffbeSJerry Gilliam 	struct vattr vattr;
1418bb5fffbeSJerry Gilliam 	struct vattr *vap = &vattr;
1419facf4a8dSllai 	char *nm = NULL;
1420facf4a8dSllai 	struct sdev_node *dv = NULL;
1421facf4a8dSllai 
14226b938478Sjg 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
14236b938478Sjg 	ASSERT((ddv->sdev_flags & SDEV_BUILD));
1424facf4a8dSllai 
1425bb5fffbeSJerry Gilliam 	*vap = *sdev_getdefault_attr(VDIR);	/* note structure copy here */
1426681d9761SEric Taylor 	gethrestime(&vap->va_atime);
1427681d9761SEric Taylor 	vap->va_mtime = vap->va_atime;
1428681d9761SEric Taylor 	vap->va_ctime = vap->va_atime;
1429facf4a8dSllai 	for (i = 0; vtab[i].vt_name != NULL; i++) {
143067323fc4SJohn Levon 		/*
14319e5aa9d8SRobert Mustacchi 		 * This early, we may be in a read-only /dev environment: leave
14329e5aa9d8SRobert Mustacchi 		 * the creation of any nodes we'd attempt to persist to
14339e5aa9d8SRobert Mustacchi 		 * devfsadm. Because /dev itself is normally persistent, any
14349e5aa9d8SRobert Mustacchi 		 * node which is not marked dynamic will end up being marked
14359e5aa9d8SRobert Mustacchi 		 * persistent. However, some nodes are both dynamic and
14369e5aa9d8SRobert Mustacchi 		 * persistent, mostly lofi and rlofi, so we need to be careful
14379e5aa9d8SRobert Mustacchi 		 * in our check.
143867323fc4SJohn Levon 		 */
14399e5aa9d8SRobert Mustacchi 		if ((vtab[i].vt_flags & SDEV_PERSIST) ||
14409e5aa9d8SRobert Mustacchi 		    !(vtab[i].vt_flags & SDEV_DYNAMIC))
144167323fc4SJohn Levon 			continue;
1442facf4a8dSllai 		nm = vtab[i].vt_name;
1443facf4a8dSllai 		ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
14446b938478Sjg 		dv = NULL;
1445facf4a8dSllai 		error = sdev_mknode(ddv, nm, &dv, vap, NULL,
1446facf4a8dSllai 		    NULL, kcred, SDEV_READY);
14476b938478Sjg 		if (error) {
14486b938478Sjg 			cmn_err(CE_WARN, "%s/%s: error %d\n",
14496b938478Sjg 			    ddv->sdev_name, nm, error);
14506b938478Sjg 		} else {
14516b938478Sjg 			ASSERT(dv);
14526b938478Sjg 			ASSERT(dv->sdev_state != SDEV_ZOMBIE);
14536b938478Sjg 			SDEV_SIMPLE_RELE(dv);
14546b938478Sjg 		}
1455facf4a8dSllai 	}
1456facf4a8dSllai }
1457facf4a8dSllai 
1458facf4a8dSllai /*
1459facf4a8dSllai  * Creating a backing store entry based on sdev_attr.
1460facf4a8dSllai  * This is called either as part of node creation in a persistent directory
1461facf4a8dSllai  * or from setattr/setsecattr to persist access attributes across reboot.
1462facf4a8dSllai  */
1463facf4a8dSllai int
sdev_shadow_node(struct sdev_node * dv,struct cred * cred)1464facf4a8dSllai sdev_shadow_node(struct sdev_node *dv, struct cred *cred)
1465facf4a8dSllai {
1466facf4a8dSllai 	int error = 0;
1467facf4a8dSllai 	struct vnode *dvp = SDEVTOV(dv->sdev_dotdot);
1468facf4a8dSllai 	struct vnode *rdvp = VTOSDEV(dvp)->sdev_attrvp;
1469facf4a8dSllai 	struct vattr *vap = dv->sdev_attr;
1470facf4a8dSllai 	char *nm = dv->sdev_name;
1471facf4a8dSllai 	struct vnode *tmpvp, **rvp = &tmpvp, *rrvp = NULL;
1472facf4a8dSllai 
1473facf4a8dSllai 	ASSERT(dv && dv->sdev_name && rdvp);
1474facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&dv->sdev_contents) && dv->sdev_attrvp == NULL);
1475facf4a8dSllai 
1476facf4a8dSllai lookup:
1477facf4a8dSllai 	/* try to find it in the backing store */
1478da6c28aaSamw 	error = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, cred, NULL, NULL,
1479da6c28aaSamw 	    NULL);
1480facf4a8dSllai 	if (error == 0) {
1481da6c28aaSamw 		if (VOP_REALVP(*rvp, &rrvp, NULL) == 0) {
1482facf4a8dSllai 			VN_HOLD(rrvp);
1483facf4a8dSllai 			VN_RELE(*rvp);
1484facf4a8dSllai 			*rvp = rrvp;
1485facf4a8dSllai 		}
1486facf4a8dSllai 
1487facf4a8dSllai 		kmem_free(dv->sdev_attr, sizeof (vattr_t));
1488facf4a8dSllai 		dv->sdev_attr = NULL;
1489facf4a8dSllai 		dv->sdev_attrvp = *rvp;
1490facf4a8dSllai 		return (0);
1491facf4a8dSllai 	}
1492facf4a8dSllai 
1493facf4a8dSllai 	/* let's try to persist the node */
1494facf4a8dSllai 	gethrestime(&vap->va_atime);
1495facf4a8dSllai 	vap->va_mtime = vap->va_atime;
1496facf4a8dSllai 	vap->va_ctime = vap->va_atime;
1497facf4a8dSllai 	vap->va_mask |= AT_TYPE|AT_MODE;
1498facf4a8dSllai 	switch (vap->va_type) {
1499facf4a8dSllai 	case VDIR:
1500da6c28aaSamw 		error = VOP_MKDIR(rdvp, nm, vap, rvp, cred, NULL, 0, NULL);
1501facf4a8dSllai 		sdcmn_err9(("sdev_shadow_node: mkdir vp %p error %d\n",
1502facf4a8dSllai 		    (void *)(*rvp), error));
15039e5aa9d8SRobert Mustacchi 		if (!error)
15049e5aa9d8SRobert Mustacchi 			VN_RELE(*rvp);
1505facf4a8dSllai 		break;
1506facf4a8dSllai 	case VCHR:
1507facf4a8dSllai 	case VBLK:
1508facf4a8dSllai 	case VREG:
1509facf4a8dSllai 	case VDOOR:
1510facf4a8dSllai 		error = VOP_CREATE(rdvp, nm, vap, NONEXCL, VREAD|VWRITE,
1511da6c28aaSamw 		    rvp, cred, 0, NULL, NULL);
1512facf4a8dSllai 		sdcmn_err9(("sdev_shadow_node: create vp %p, error %d\n",
1513facf4a8dSllai 		    (void *)(*rvp), error));
1514facf4a8dSllai 		if (!error)
1515facf4a8dSllai 			VN_RELE(*rvp);
1516facf4a8dSllai 		break;
1517facf4a8dSllai 	case VLNK:
1518facf4a8dSllai 		ASSERT(dv->sdev_symlink);
1519da6c28aaSamw 		error = VOP_SYMLINK(rdvp, nm, vap, dv->sdev_symlink, cred,
1520da6c28aaSamw 		    NULL, 0);
1521facf4a8dSllai 		sdcmn_err9(("sdev_shadow_node: create symlink error %d\n",
1522facf4a8dSllai 		    error));
1523facf4a8dSllai 		break;
1524facf4a8dSllai 	default:
1525facf4a8dSllai 		cmn_err(CE_PANIC, "dev: %s: sdev_shadow_node "
1526facf4a8dSllai 		    "create\n", nm);
1527facf4a8dSllai 		/*NOTREACHED*/
1528facf4a8dSllai 	}
1529facf4a8dSllai 
1530facf4a8dSllai 	/* go back to lookup to factor out spec node and set attrvp */
1531facf4a8dSllai 	if (error == 0)
1532facf4a8dSllai 		goto lookup;
1533facf4a8dSllai 
1534b7beec95Sjg 	sdcmn_err(("cannot persist %s - error %d\n", dv->sdev_path, error));
1535facf4a8dSllai 	return (error);
1536facf4a8dSllai }
1537facf4a8dSllai 
15389e5aa9d8SRobert Mustacchi static void
sdev_cache_add(struct sdev_node * ddv,struct sdev_node ** dv,char * nm)1539facf4a8dSllai sdev_cache_add(struct sdev_node *ddv, struct sdev_node **dv, char *nm)
1540facf4a8dSllai {
1541facf4a8dSllai 	struct sdev_node *dup = NULL;
1542facf4a8dSllai 
1543facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1544facf4a8dSllai 	if ((dup = sdev_findbyname(ddv, nm)) == NULL) {
1545facf4a8dSllai 		sdev_direnter(ddv, *dv);
1546facf4a8dSllai 	} else {
15479e5aa9d8SRobert Mustacchi 		VERIFY(dup->sdev_state != SDEV_ZOMBIE);
15489e5aa9d8SRobert Mustacchi 		SDEV_SIMPLE_RELE(*dv);
15499e5aa9d8SRobert Mustacchi 		sdev_nodedestroy(*dv, 0);
15509e5aa9d8SRobert Mustacchi 		*dv = dup;
1551facf4a8dSllai 	}
1552facf4a8dSllai }
1553facf4a8dSllai 
15549e5aa9d8SRobert Mustacchi static void
sdev_cache_delete(struct sdev_node * ddv,struct sdev_node ** dv)1555facf4a8dSllai sdev_cache_delete(struct sdev_node *ddv, struct sdev_node **dv)
1556facf4a8dSllai {
1557facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
15589e5aa9d8SRobert Mustacchi 	sdev_dirdelete(ddv, *dv);
1559facf4a8dSllai }
1560facf4a8dSllai 
1561facf4a8dSllai /*
1562facf4a8dSllai  * update the in-core directory cache
1563facf4a8dSllai  */
15649e5aa9d8SRobert Mustacchi void
sdev_cache_update(struct sdev_node * ddv,struct sdev_node ** dv,char * nm,sdev_cache_ops_t ops)1565facf4a8dSllai sdev_cache_update(struct sdev_node *ddv, struct sdev_node **dv, char *nm,
1566facf4a8dSllai     sdev_cache_ops_t ops)
1567facf4a8dSllai {
1568facf4a8dSllai 	ASSERT((SDEV_HELD(*dv)));
1569facf4a8dSllai 
1570facf4a8dSllai 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1571facf4a8dSllai 	switch (ops) {
1572facf4a8dSllai 	case SDEV_CACHE_ADD:
15739e5aa9d8SRobert Mustacchi 		sdev_cache_add(ddv, dv, nm);
1574facf4a8dSllai 		break;
1575facf4a8dSllai 	case SDEV_CACHE_DELETE:
15769e5aa9d8SRobert Mustacchi 		sdev_cache_delete(ddv, dv);
1577facf4a8dSllai 		break;
1578facf4a8dSllai 	default:
1579facf4a8dSllai 		break;
1580facf4a8dSllai 	}
1581facf4a8dSllai }
1582facf4a8dSllai 
1583facf4a8dSllai /*
1584da6c28aaSamw  * retrieve the named entry from the directory cache
1585facf4a8dSllai  */
1586facf4a8dSllai struct sdev_node *
sdev_cache_lookup(struct sdev_node * ddv,char * nm)1587facf4a8dSllai sdev_cache_lookup(struct sdev_node *ddv, char *nm)
1588facf4a8dSllai {
1589facf4a8dSllai 	struct sdev_node *dv = NULL;
1590facf4a8dSllai 
1591facf4a8dSllai 	ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
1592facf4a8dSllai 	dv = sdev_findbyname(ddv, nm);
1593facf4a8dSllai 
1594facf4a8dSllai 	return (dv);
1595facf4a8dSllai }
1596facf4a8dSllai 
1597facf4a8dSllai /*
1598facf4a8dSllai  * Implicit reconfig for nodes constructed by a link generator
1599facf4a8dSllai  * Start devfsadm if needed, or if devfsadm is in progress,
1600facf4a8dSllai  * prepare to block on devfsadm either completing or
1601facf4a8dSllai  * constructing the desired node.  As devfsadmd is global
1602facf4a8dSllai  * in scope, constructing all necessary nodes, we only
1603facf4a8dSllai  * need to initiate it once.
1604facf4a8dSllai  */
1605facf4a8dSllai static int
sdev_call_devfsadmd(struct sdev_node * ddv,struct sdev_node * dv,char * nm)1606facf4a8dSllai sdev_call_devfsadmd(struct sdev_node *ddv, struct sdev_node *dv, char *nm)
1607facf4a8dSllai {
1608facf4a8dSllai 	int error = 0;
1609facf4a8dSllai 
1610facf4a8dSllai 	if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) {
1611facf4a8dSllai 		sdcmn_err6(("lookup: waiting for %s/%s, 0x%x\n",
1612facf4a8dSllai 		    ddv->sdev_name, nm, devfsadm_state));
1613facf4a8dSllai 		mutex_enter(&dv->sdev_lookup_lock);
1614facf4a8dSllai 		SDEV_BLOCK_OTHERS(dv, (SDEV_LOOKUP | SDEV_LGWAITING));
1615facf4a8dSllai 		mutex_exit(&dv->sdev_lookup_lock);
1616facf4a8dSllai 		error = 0;
1617facf4a8dSllai 	} else if (!DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state)) {
1618facf4a8dSllai 		sdcmn_err6(("lookup %s/%s starting devfsadm, 0x%x\n",
1619e37c6c37Scth 		    ddv->sdev_name, nm, devfsadm_state));
1620facf4a8dSllai 
1621facf4a8dSllai 		sdev_devfsadmd_thread(ddv, dv, kcred);
1622facf4a8dSllai 		mutex_enter(&dv->sdev_lookup_lock);
1623facf4a8dSllai 		SDEV_BLOCK_OTHERS(dv,
1624facf4a8dSllai 		    (SDEV_LOOKUP | SDEV_LGWAITING));
1625facf4a8dSllai 		mutex_exit(&dv->sdev_lookup_lock);
1626facf4a8dSllai 		error = 0;
1627facf4a8dSllai 	} else {
1628facf4a8dSllai 		error = -1;
1629facf4a8dSllai 	}
1630facf4a8dSllai 
1631facf4a8dSllai 	return (error);
1632facf4a8dSllai }
1633facf4a8dSllai 
1634facf4a8dSllai /*
1635facf4a8dSllai  *  Support for specialized device naming construction mechanisms
1636facf4a8dSllai  */
1637facf4a8dSllai static int
sdev_call_dircallback(struct sdev_node * ddv,struct sdev_node ** dvp,char * nm,int (* callback)(struct sdev_node *,char *,void **,struct cred *,void *,char *),int flags,struct cred * cred)1638facf4a8dSllai sdev_call_dircallback(struct sdev_node *ddv, struct sdev_node **dvp, char *nm,
1639facf4a8dSllai     int (*callback)(struct sdev_node *, char *, void **, struct cred *,
1640facf4a8dSllai     void *, char *), int flags, struct cred *cred)
1641facf4a8dSllai {
1642facf4a8dSllai 	int rv = 0;
1643facf4a8dSllai 	char *physpath = NULL;
1644facf4a8dSllai 	struct vattr vattr;
1645bb5fffbeSJerry Gilliam 	struct vattr *vap = &vattr;
1646681d9761SEric Taylor 	struct sdev_node *dv = NULL;
1647facf4a8dSllai 
1648681d9761SEric Taylor 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1649681d9761SEric Taylor 	if (flags & SDEV_VLINK) {
1650aecfc01dSrui zang - Sun Microsystems - Beijing China 		physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
1651aecfc01dSrui zang - Sun Microsystems - Beijing China 		rv = callback(ddv, nm, (void *)&physpath, kcred, NULL,
1652aecfc01dSrui zang - Sun Microsystems - Beijing China 		    NULL);
1653aecfc01dSrui zang - Sun Microsystems - Beijing China 		if (rv) {
1654aecfc01dSrui zang - Sun Microsystems - Beijing China 			kmem_free(physpath, MAXPATHLEN);
1655aecfc01dSrui zang - Sun Microsystems - Beijing China 			return (-1);
1656aecfc01dSrui zang - Sun Microsystems - Beijing China 		}
1657aecfc01dSrui zang - Sun Microsystems - Beijing China 
1658bb5fffbeSJerry Gilliam 		*vap = *sdev_getdefault_attr(VLNK);	/* structure copy */
1659aecfc01dSrui zang - Sun Microsystems - Beijing China 		vap->va_size = strlen(physpath);
1660681d9761SEric Taylor 		gethrestime(&vap->va_atime);
1661681d9761SEric Taylor 		vap->va_mtime = vap->va_atime;
1662681d9761SEric Taylor 		vap->va_ctime = vap->va_atime;
1663aecfc01dSrui zang - Sun Microsystems - Beijing China 
1664aecfc01dSrui zang - Sun Microsystems - Beijing China 		rv = sdev_mknode(ddv, nm, &dv, vap, NULL,
1665aecfc01dSrui zang - Sun Microsystems - Beijing China 		    (void *)physpath, cred, SDEV_READY);
1666aecfc01dSrui zang - Sun Microsystems - Beijing China 		kmem_free(physpath, MAXPATHLEN);
1667aecfc01dSrui zang - Sun Microsystems - Beijing China 		if (rv)
1668aecfc01dSrui zang - Sun Microsystems - Beijing China 			return (rv);
1669facf4a8dSllai 	} else if (flags & SDEV_VATTR) {
1670facf4a8dSllai 		/*
1671facf4a8dSllai 		 * /dev/pts
1672facf4a8dSllai 		 *
1673facf4a8dSllai 		 * callback is responsible to set the basic attributes,
1674facf4a8dSllai 		 * e.g. va_type/va_uid/va_gid/
1675facf4a8dSllai 		 *    dev_t if VCHR or VBLK/
1676facf4a8dSllai 		 */
1677facf4a8dSllai 		ASSERT(callback);
1678facf4a8dSllai 		rv = callback(ddv, nm, (void *)&vattr, kcred, NULL, NULL);
1679facf4a8dSllai 		if (rv) {
1680facf4a8dSllai 			sdcmn_err3(("devname_lookup_func: SDEV_NONE "
1681facf4a8dSllai 			    "callback failed \n"));
1682facf4a8dSllai 			return (-1);
1683facf4a8dSllai 		}
1684facf4a8dSllai 
1685facf4a8dSllai 		rv = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL,
1686facf4a8dSllai 		    cred, SDEV_READY);
1687facf4a8dSllai 
1688facf4a8dSllai 		if (rv)
1689facf4a8dSllai 			return (rv);
1690facf4a8dSllai 
1691facf4a8dSllai 	} else {
1692facf4a8dSllai 		impossible(("lookup: %s/%s by %s not supported (%d)\n",
1693facf4a8dSllai 		    SDEVTOV(ddv)->v_path, nm, curproc->p_user.u_comm,
1694facf4a8dSllai 		    __LINE__));
1695facf4a8dSllai 		rv = -1;
1696facf4a8dSllai 	}
1697facf4a8dSllai 
1698facf4a8dSllai 	*dvp = dv;
1699facf4a8dSllai 	return (rv);
1700facf4a8dSllai }
1701facf4a8dSllai 
1702facf4a8dSllai static int
is_devfsadm_thread(char * exec_name)1703facf4a8dSllai is_devfsadm_thread(char *exec_name)
1704facf4a8dSllai {
1705facf4a8dSllai 	/*
1706facf4a8dSllai 	 * note: because devfsadmd -> /usr/sbin/devfsadm
1707facf4a8dSllai 	 * it is safe to use "devfsadm" to capture the lookups
1708facf4a8dSllai 	 * from devfsadm and its daemon version.
1709facf4a8dSllai 	 */
1710facf4a8dSllai 	if (strcmp(exec_name, "devfsadm") == 0)
1711facf4a8dSllai 		return (1);
1712facf4a8dSllai 	return (0);
1713facf4a8dSllai }
1714facf4a8dSllai 
1715facf4a8dSllai /*
1716facf4a8dSllai  * Lookup Order:
1717facf4a8dSllai  *	sdev_node cache;
1718facf4a8dSllai  *	backing store (SDEV_PERSIST);
1719facf4a8dSllai  *	DBNR: a. dir_ops implemented in the loadable modules;
1720facf4a8dSllai  *	      b. vnode ops in vtab.
1721facf4a8dSllai  */
1722facf4a8dSllai int
devname_lookup_func(struct sdev_node * ddv,char * nm,struct vnode ** vpp,struct cred * cred,int (* callback)(struct sdev_node *,char *,void **,struct cred *,void *,char *),int flags)1723facf4a8dSllai devname_lookup_func(struct sdev_node *ddv, char *nm, struct vnode **vpp,
1724facf4a8dSllai     struct cred *cred, int (*callback)(struct sdev_node *, char *, void **,
1725facf4a8dSllai     struct cred *, void *, char *), int flags)
1726facf4a8dSllai {
1727facf4a8dSllai 	int rv = 0, nmlen;
1728facf4a8dSllai 	struct vnode *rvp = NULL;
1729facf4a8dSllai 	struct sdev_node *dv = NULL;
1730facf4a8dSllai 	int	retried = 0;
1731facf4a8dSllai 	int	error = 0;
1732facf4a8dSllai 	struct vattr vattr;
1733facf4a8dSllai 	char *lookup_thread = curproc->p_user.u_comm;
1734facf4a8dSllai 	int failed_flags = 0;
1735facf4a8dSllai 	int (*vtor)(struct sdev_node *) = NULL;
1736facf4a8dSllai 	int state;
1737facf4a8dSllai 	int parent_state;
1738facf4a8dSllai 	char *link = NULL;
1739facf4a8dSllai 
1740facf4a8dSllai 	if (SDEVTOV(ddv)->v_type != VDIR)
1741facf4a8dSllai 		return (ENOTDIR);
1742facf4a8dSllai 
1743facf4a8dSllai 	/*
1744facf4a8dSllai 	 * Empty name or ., return node itself.
1745facf4a8dSllai 	 */
1746facf4a8dSllai 	nmlen = strlen(nm);
1747facf4a8dSllai 	if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
1748facf4a8dSllai 		*vpp = SDEVTOV(ddv);
1749facf4a8dSllai 		VN_HOLD(*vpp);
1750facf4a8dSllai 		return (0);
1751facf4a8dSllai 	}
1752facf4a8dSllai 
1753facf4a8dSllai 	/*
1754facf4a8dSllai 	 * .., return the parent directory
1755facf4a8dSllai 	 */
1756facf4a8dSllai 	if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
1757facf4a8dSllai 		*vpp = SDEVTOV(ddv->sdev_dotdot);
1758facf4a8dSllai 		VN_HOLD(*vpp);
1759facf4a8dSllai 		return (0);
1760facf4a8dSllai 	}
1761facf4a8dSllai 
1762facf4a8dSllai 	rw_enter(&ddv->sdev_contents, RW_READER);
1763facf4a8dSllai 	if (ddv->sdev_flags & SDEV_VTOR) {
1764facf4a8dSllai 		vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
1765facf4a8dSllai 		ASSERT(vtor);
1766facf4a8dSllai 	}
1767facf4a8dSllai 
1768facf4a8dSllai tryagain:
1769facf4a8dSllai 	/*
1770facf4a8dSllai 	 * (a) directory cache lookup:
1771facf4a8dSllai 	 */
1772facf4a8dSllai 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
1773facf4a8dSllai 	parent_state = ddv->sdev_state;
1774facf4a8dSllai 	dv = sdev_cache_lookup(ddv, nm);
1775facf4a8dSllai 	if (dv) {
1776facf4a8dSllai 		state = dv->sdev_state;
1777facf4a8dSllai 		switch (state) {
1778facf4a8dSllai 		case SDEV_INIT:
1779facf4a8dSllai 			if (is_devfsadm_thread(lookup_thread))
1780facf4a8dSllai 				break;
1781facf4a8dSllai 
1782facf4a8dSllai 			/* ZOMBIED parent won't allow node creation */
1783facf4a8dSllai 			if (parent_state == SDEV_ZOMBIE) {
1784facf4a8dSllai 				SD_TRACE_FAILED_LOOKUP(ddv, nm,
1785facf4a8dSllai 				    retried);
1786facf4a8dSllai 				goto nolock_notfound;
1787facf4a8dSllai 			}
1788facf4a8dSllai 
1789facf4a8dSllai 			mutex_enter(&dv->sdev_lookup_lock);
1790facf4a8dSllai 			/* compensate the threads started after devfsadm */
1791facf4a8dSllai 			if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
1792facf4a8dSllai 			    !(SDEV_IS_LOOKUP(dv)))
1793facf4a8dSllai 				SDEV_BLOCK_OTHERS(dv,
1794facf4a8dSllai 				    (SDEV_LOOKUP | SDEV_LGWAITING));
1795facf4a8dSllai 
1796facf4a8dSllai 			if (SDEV_IS_LOOKUP(dv)) {
1797facf4a8dSllai 				failed_flags |= SLF_REBUILT;
1798facf4a8dSllai 				rw_exit(&ddv->sdev_contents);
1799facf4a8dSllai 				error = sdev_wait4lookup(dv, SDEV_LOOKUP);
1800facf4a8dSllai 				mutex_exit(&dv->sdev_lookup_lock);
1801facf4a8dSllai 				rw_enter(&ddv->sdev_contents, RW_READER);
1802facf4a8dSllai 
1803facf4a8dSllai 				if (error != 0) {
1804facf4a8dSllai 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
1805facf4a8dSllai 					    retried);
1806facf4a8dSllai 					goto nolock_notfound;
1807facf4a8dSllai 				}
1808facf4a8dSllai 
1809facf4a8dSllai 				state = dv->sdev_state;
1810facf4a8dSllai 				if (state == SDEV_INIT) {
1811facf4a8dSllai 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
1812facf4a8dSllai 					    retried);
1813facf4a8dSllai 					goto nolock_notfound;
1814facf4a8dSllai 				} else if (state == SDEV_READY) {
1815facf4a8dSllai 					goto found;
1816facf4a8dSllai 				} else if (state == SDEV_ZOMBIE) {
1817facf4a8dSllai 					rw_exit(&ddv->sdev_contents);
1818facf4a8dSllai 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
1819facf4a8dSllai 					    retried);
1820facf4a8dSllai 					SDEV_RELE(dv);
1821facf4a8dSllai 					goto lookup_failed;
1822facf4a8dSllai 				}
1823facf4a8dSllai 			} else {
1824facf4a8dSllai 				mutex_exit(&dv->sdev_lookup_lock);
1825facf4a8dSllai 			}
1826facf4a8dSllai 			break;
1827facf4a8dSllai 		case SDEV_READY:
1828facf4a8dSllai 			goto found;
1829facf4a8dSllai 		case SDEV_ZOMBIE:
1830facf4a8dSllai 			rw_exit(&ddv->sdev_contents);
1831facf4a8dSllai 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1832facf4a8dSllai 			SDEV_RELE(dv);
1833facf4a8dSllai 			goto lookup_failed;
1834facf4a8dSllai 		default:
1835facf4a8dSllai 			rw_exit(&ddv->sdev_contents);
1836facf4a8dSllai 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1837facf4a8dSllai 			sdev_lookup_failed(ddv, nm, failed_flags);
1838facf4a8dSllai 			*vpp = NULLVP;
1839facf4a8dSllai 			return (ENOENT);
1840facf4a8dSllai 		}
1841facf4a8dSllai 	}
1842facf4a8dSllai 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
1843facf4a8dSllai 
1844facf4a8dSllai 	/*
1845facf4a8dSllai 	 * ZOMBIED parent does not allow new node creation.
1846facf4a8dSllai 	 * bail out early
1847facf4a8dSllai 	 */
1848facf4a8dSllai 	if (parent_state == SDEV_ZOMBIE) {
1849facf4a8dSllai 		rw_exit(&ddv->sdev_contents);
1850681d9761SEric Taylor 		*vpp = NULLVP;
1851facf4a8dSllai 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1852facf4a8dSllai 		return (ENOENT);
1853facf4a8dSllai 	}
1854facf4a8dSllai 
1855facf4a8dSllai 	/*
1856facf4a8dSllai 	 * (b0): backing store lookup
1857facf4a8dSllai 	 *	SDEV_PERSIST is default except:
1858facf4a8dSllai 	 *		1) pts nodes
1859facf4a8dSllai 	 *		2) non-chmod'ed local nodes
1860681d9761SEric Taylor 	 *		3) zvol nodes
1861facf4a8dSllai 	 */
1862facf4a8dSllai 	if (SDEV_IS_PERSIST(ddv)) {
1863facf4a8dSllai 		error = devname_backstore_lookup(ddv, nm, &rvp);
1864facf4a8dSllai 
1865facf4a8dSllai 		if (!error) {
1866facf4a8dSllai 
1867bb5fffbeSJerry Gilliam 			vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID;
1868da6c28aaSamw 			error = VOP_GETATTR(rvp, &vattr, 0, cred, NULL);
1869facf4a8dSllai 			if (error) {
1870facf4a8dSllai 				rw_exit(&ddv->sdev_contents);
1871facf4a8dSllai 				if (dv)
1872facf4a8dSllai 					SDEV_RELE(dv);
1873facf4a8dSllai 				SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1874facf4a8dSllai 				sdev_lookup_failed(ddv, nm, failed_flags);
1875facf4a8dSllai 				*vpp = NULLVP;
1876facf4a8dSllai 				return (ENOENT);
1877facf4a8dSllai 			}
1878facf4a8dSllai 
1879facf4a8dSllai 			if (vattr.va_type == VLNK) {
1880facf4a8dSllai 				error = sdev_getlink(rvp, &link);
1881facf4a8dSllai 				if (error) {
1882facf4a8dSllai 					rw_exit(&ddv->sdev_contents);
1883facf4a8dSllai 					if (dv)
1884facf4a8dSllai 						SDEV_RELE(dv);
1885facf4a8dSllai 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
1886facf4a8dSllai 					    retried);
1887facf4a8dSllai 					sdev_lookup_failed(ddv, nm,
1888facf4a8dSllai 					    failed_flags);
1889facf4a8dSllai 					*vpp = NULLVP;
1890facf4a8dSllai 					return (ENOENT);
1891facf4a8dSllai 				}
1892facf4a8dSllai 				ASSERT(link != NULL);
1893facf4a8dSllai 			}
1894facf4a8dSllai 
1895facf4a8dSllai 			if (!rw_tryupgrade(&ddv->sdev_contents)) {
1896facf4a8dSllai 				rw_exit(&ddv->sdev_contents);
1897facf4a8dSllai 				rw_enter(&ddv->sdev_contents, RW_WRITER);
1898facf4a8dSllai 			}
1899facf4a8dSllai 			error = sdev_mknode(ddv, nm, &dv, &vattr,
1900facf4a8dSllai 			    rvp, link, cred, SDEV_READY);
1901facf4a8dSllai 			rw_downgrade(&ddv->sdev_contents);
1902facf4a8dSllai 
1903facf4a8dSllai 			if (link != NULL) {
1904facf4a8dSllai 				kmem_free(link, strlen(link) + 1);
1905facf4a8dSllai 				link = NULL;
1906facf4a8dSllai 			}
1907facf4a8dSllai 
1908facf4a8dSllai 			if (error) {
1909facf4a8dSllai 				SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1910facf4a8dSllai 				rw_exit(&ddv->sdev_contents);
1911facf4a8dSllai 				if (dv)
1912facf4a8dSllai 					SDEV_RELE(dv);
1913facf4a8dSllai 				goto lookup_failed;
1914facf4a8dSllai 			} else {
1915facf4a8dSllai 				goto found;
1916facf4a8dSllai 			}
1917facf4a8dSllai 		} else if (retried) {
1918facf4a8dSllai 			rw_exit(&ddv->sdev_contents);
1919facf4a8dSllai 			sdcmn_err3(("retry of lookup of %s/%s: failed\n",
1920facf4a8dSllai 			    ddv->sdev_name, nm));
1921facf4a8dSllai 			if (dv)
1922facf4a8dSllai 				SDEV_RELE(dv);
1923facf4a8dSllai 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1924facf4a8dSllai 			sdev_lookup_failed(ddv, nm, failed_flags);
1925facf4a8dSllai 			*vpp = NULLVP;
1926facf4a8dSllai 			return (ENOENT);
1927facf4a8dSllai 		}
1928facf4a8dSllai 	}
1929facf4a8dSllai 
1930b127ac41SPhilip Kirk lookup_create_node:
1931facf4a8dSllai 	/* first thread that is doing the lookup on this node */
1932681d9761SEric Taylor 	if (callback) {
1933681d9761SEric Taylor 		ASSERT(dv == NULL);
1934681d9761SEric Taylor 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
1935681d9761SEric Taylor 			rw_exit(&ddv->sdev_contents);
1936681d9761SEric Taylor 			rw_enter(&ddv->sdev_contents, RW_WRITER);
1937681d9761SEric Taylor 		}
1938681d9761SEric Taylor 		error = sdev_call_dircallback(ddv, &dv, nm, callback,
1939681d9761SEric Taylor 		    flags, cred);
1940681d9761SEric Taylor 		rw_downgrade(&ddv->sdev_contents);
1941681d9761SEric Taylor 		if (error == 0) {
1942681d9761SEric Taylor 			goto found;
1943681d9761SEric Taylor 		} else {
1944681d9761SEric Taylor 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1945681d9761SEric Taylor 			rw_exit(&ddv->sdev_contents);
1946681d9761SEric Taylor 			goto lookup_failed;
1947681d9761SEric Taylor 		}
1948681d9761SEric Taylor 	}
1949facf4a8dSllai 	if (!dv) {
1950facf4a8dSllai 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
1951facf4a8dSllai 			rw_exit(&ddv->sdev_contents);
1952facf4a8dSllai 			rw_enter(&ddv->sdev_contents, RW_WRITER);
1953facf4a8dSllai 		}
1954facf4a8dSllai 		error = sdev_mknode(ddv, nm, &dv, NULL, NULL, NULL,
1955facf4a8dSllai 		    cred, SDEV_INIT);
1956facf4a8dSllai 		if (!dv) {
1957facf4a8dSllai 			rw_exit(&ddv->sdev_contents);
1958facf4a8dSllai 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1959facf4a8dSllai 			sdev_lookup_failed(ddv, nm, failed_flags);
1960facf4a8dSllai 			*vpp = NULLVP;
1961facf4a8dSllai 			return (ENOENT);
1962facf4a8dSllai 		}
1963facf4a8dSllai 		rw_downgrade(&ddv->sdev_contents);
1964facf4a8dSllai 	}
1965681d9761SEric Taylor 
1966681d9761SEric Taylor 	/*
1967681d9761SEric Taylor 	 * (b1) invoking devfsadm once per life time for devfsadm nodes
1968681d9761SEric Taylor 	 */
1969facf4a8dSllai 	ASSERT(SDEV_HELD(dv));
1970facf4a8dSllai 
1971681d9761SEric Taylor 	if (SDEV_IS_NO_NCACHE(dv))
1972facf4a8dSllai 		failed_flags |= SLF_NO_NCACHE;
1973681d9761SEric Taylor 	if (sdev_reconfig_boot || !i_ddi_io_initialized() ||
1974681d9761SEric Taylor 	    SDEV_IS_DYNAMIC(ddv) || SDEV_IS_NO_NCACHE(dv) ||
1975681d9761SEric Taylor 	    ((moddebug & MODDEBUG_FINI_EBUSY) != 0)) {
1976681d9761SEric Taylor 		ASSERT(SDEV_HELD(dv));
1977681d9761SEric Taylor 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1978681d9761SEric Taylor 		goto nolock_notfound;
1979facf4a8dSllai 	}
1980facf4a8dSllai 
1981facf4a8dSllai 	/*
1982681d9761SEric Taylor 	 * filter out known non-existent devices recorded
1983681d9761SEric Taylor 	 * during initial reconfiguration boot for which
1984681d9761SEric Taylor 	 * reconfig should not be done and lookup may
1985681d9761SEric Taylor 	 * be short-circuited now.
1986facf4a8dSllai 	 */
1987681d9761SEric Taylor 	if (sdev_lookup_filter(ddv, nm)) {
1988681d9761SEric Taylor 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1989681d9761SEric Taylor 		goto nolock_notfound;
1990681d9761SEric Taylor 	}
1991facf4a8dSllai 
1992681d9761SEric Taylor 	/* bypassing devfsadm internal nodes */
1993681d9761SEric Taylor 	if (is_devfsadm_thread(lookup_thread)) {
1994681d9761SEric Taylor 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1995681d9761SEric Taylor 		goto nolock_notfound;
1996681d9761SEric Taylor 	}
1997facf4a8dSllai 
1998681d9761SEric Taylor 	if (sdev_reconfig_disable) {
1999681d9761SEric Taylor 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2000681d9761SEric Taylor 		goto nolock_notfound;
2001facf4a8dSllai 	}
2002facf4a8dSllai 
2003681d9761SEric Taylor 	error = sdev_call_devfsadmd(ddv, dv, nm);
2004681d9761SEric Taylor 	if (error == 0) {
2005681d9761SEric Taylor 		sdcmn_err8(("lookup of %s/%s by %s: reconfig\n",
2006681d9761SEric Taylor 		    ddv->sdev_name, nm, curproc->p_user.u_comm));
2007681d9761SEric Taylor 		if (sdev_reconfig_verbose) {
2008681d9761SEric Taylor 			cmn_err(CE_CONT,
2009681d9761SEric Taylor 			    "?lookup of %s/%s by %s: reconfig\n",
2010681d9761SEric Taylor 			    ddv->sdev_name, nm, curproc->p_user.u_comm);
2011facf4a8dSllai 		}
2012681d9761SEric Taylor 		retried = 1;
2013681d9761SEric Taylor 		failed_flags |= SLF_REBUILT;
2014681d9761SEric Taylor 		ASSERT(dv->sdev_state != SDEV_ZOMBIE);
2015681d9761SEric Taylor 		SDEV_SIMPLE_RELE(dv);
2016681d9761SEric Taylor 		goto tryagain;
2017681d9761SEric Taylor 	} else {
2018681d9761SEric Taylor 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2019681d9761SEric Taylor 		goto nolock_notfound;
2020facf4a8dSllai 	}
2021facf4a8dSllai 
2022facf4a8dSllai found:
2023facf4a8dSllai 	ASSERT(dv->sdev_state == SDEV_READY);
2024facf4a8dSllai 	if (vtor) {
2025facf4a8dSllai 		/*
2026facf4a8dSllai 		 * Check validity of returned node
2027facf4a8dSllai 		 */
2028facf4a8dSllai 		switch (vtor(dv)) {
2029facf4a8dSllai 		case SDEV_VTOR_VALID:
2030facf4a8dSllai 			break;
2031b127ac41SPhilip Kirk 		case SDEV_VTOR_STALE:
2032b127ac41SPhilip Kirk 			/*
2033b127ac41SPhilip Kirk 			 * The name exists, but the cache entry is
2034b127ac41SPhilip Kirk 			 * stale and needs to be re-created.
2035b127ac41SPhilip Kirk 			 */
2036b127ac41SPhilip Kirk 			ASSERT(RW_READ_HELD(&ddv->sdev_contents));
2037b127ac41SPhilip Kirk 			if (rw_tryupgrade(&ddv->sdev_contents) == 0) {
2038b127ac41SPhilip Kirk 				rw_exit(&ddv->sdev_contents);
2039b127ac41SPhilip Kirk 				rw_enter(&ddv->sdev_contents, RW_WRITER);
2040b127ac41SPhilip Kirk 			}
20419e5aa9d8SRobert Mustacchi 			sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_DELETE);
2042b127ac41SPhilip Kirk 			rw_downgrade(&ddv->sdev_contents);
20439e5aa9d8SRobert Mustacchi 			SDEV_RELE(dv);
20449e5aa9d8SRobert Mustacchi 			dv = NULL;
20459e5aa9d8SRobert Mustacchi 			goto lookup_create_node;
2046b127ac41SPhilip Kirk 			/* FALLTHRU */
2047facf4a8dSllai 		case SDEV_VTOR_INVALID:
2048facf4a8dSllai 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2049facf4a8dSllai 			sdcmn_err7(("lookup: destroy invalid "
2050facf4a8dSllai 			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
2051facf4a8dSllai 			goto nolock_notfound;
2052facf4a8dSllai 		case SDEV_VTOR_SKIP:
2053facf4a8dSllai 			sdcmn_err7(("lookup: node not applicable - "
2054facf4a8dSllai 			    "skipping: %s(%p)\n", dv->sdev_name, (void *)dv));
2055facf4a8dSllai 			rw_exit(&ddv->sdev_contents);
2056facf4a8dSllai 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2057facf4a8dSllai 			SDEV_RELE(dv);
2058facf4a8dSllai 			goto lookup_failed;
2059facf4a8dSllai 		default:
2060facf4a8dSllai 			cmn_err(CE_PANIC,
2061facf4a8dSllai 			    "dev fs: validator failed: %s(%p)\n",
2062facf4a8dSllai 			    dv->sdev_name, (void *)dv);
2063facf4a8dSllai 			break;
2064facf4a8dSllai 		}
2065facf4a8dSllai 	}
2066facf4a8dSllai 
2067facf4a8dSllai 	rw_exit(&ddv->sdev_contents);
2068facf4a8dSllai 	rv = sdev_to_vp(dv, vpp);
2069facf4a8dSllai 	sdcmn_err3(("devname_lookup_func: returning vp %p v_count %d state %d "
2070facf4a8dSllai 	    "for nm %s, error %d\n", (void *)*vpp, (*vpp)->v_count,
2071facf4a8dSllai 	    dv->sdev_state, nm, rv));
2072facf4a8dSllai 	return (rv);
2073facf4a8dSllai 
2074facf4a8dSllai nolock_notfound:
2075facf4a8dSllai 	/*
2076facf4a8dSllai 	 * Destroy the node that is created for synchronization purposes.
2077facf4a8dSllai 	 */
2078facf4a8dSllai 	sdcmn_err3(("devname_lookup_func: %s with state %d\n",
2079facf4a8dSllai 	    nm, dv->sdev_state));
2080facf4a8dSllai 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
2081facf4a8dSllai 	if (dv->sdev_state == SDEV_INIT) {
2082facf4a8dSllai 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
2083facf4a8dSllai 			rw_exit(&ddv->sdev_contents);
2084facf4a8dSllai 			rw_enter(&ddv->sdev_contents, RW_WRITER);
2085facf4a8dSllai 		}
2086facf4a8dSllai 
2087facf4a8dSllai 		/*
2088facf4a8dSllai 		 * Node state may have changed during the lock
2089facf4a8dSllai 		 * changes. Re-check.
2090facf4a8dSllai 		 */
2091facf4a8dSllai 		if (dv->sdev_state == SDEV_INIT) {
20929e5aa9d8SRobert Mustacchi 			sdev_dirdelete(ddv, dv);
2093facf4a8dSllai 			rw_exit(&ddv->sdev_contents);
2094facf4a8dSllai 			sdev_lookup_failed(ddv, nm, failed_flags);
20959e5aa9d8SRobert Mustacchi 			SDEV_RELE(dv);
2096facf4a8dSllai 			*vpp = NULL;
2097facf4a8dSllai 			return (ENOENT);
2098facf4a8dSllai 		}
2099facf4a8dSllai 	}
2100facf4a8dSllai 
2101facf4a8dSllai 	rw_exit(&ddv->sdev_contents);
2102facf4a8dSllai 	SDEV_RELE(dv);
2103facf4a8dSllai 
2104facf4a8dSllai lookup_failed:
2105facf4a8dSllai 	sdev_lookup_failed(ddv, nm, failed_flags);
2106facf4a8dSllai 	*vpp = NULL;
2107facf4a8dSllai 	return (ENOENT);
2108facf4a8dSllai }
2109facf4a8dSllai 
2110facf4a8dSllai /*
2111facf4a8dSllai  * Given a directory node, mark all nodes beneath as
2112facf4a8dSllai  * STALE, i.e. nodes that don't exist as far as new
2113ecb4d93aSjg  * consumers are concerned.  Remove them from the
2114ecb4d93aSjg  * list of directory entries so that no lookup or
2115ecb4d93aSjg  * directory traversal will find them.  The node
2116ecb4d93aSjg  * not deallocated so existing holds are not affected.
2117facf4a8dSllai  */
2118facf4a8dSllai void
sdev_stale(struct sdev_node * ddv)2119facf4a8dSllai sdev_stale(struct sdev_node *ddv)
2120facf4a8dSllai {
2121facf4a8dSllai 	struct sdev_node *dv;
2122facf4a8dSllai 	struct vnode *vp;
2123facf4a8dSllai 
2124facf4a8dSllai 	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
2125facf4a8dSllai 
2126facf4a8dSllai 	rw_enter(&ddv->sdev_contents, RW_WRITER);
21279e5aa9d8SRobert Mustacchi 	while ((dv = SDEV_FIRST_ENTRY(ddv)) != NULL) {
2128facf4a8dSllai 		vp = SDEVTOV(dv);
21299e5aa9d8SRobert Mustacchi 		SDEV_HOLD(dv);
2130facf4a8dSllai 		if (vp->v_type == VDIR)
2131facf4a8dSllai 			sdev_stale(dv);
2132facf4a8dSllai 
21339e5aa9d8SRobert Mustacchi 		sdev_dirdelete(ddv, dv);
21349e5aa9d8SRobert Mustacchi 		SDEV_RELE(dv);
2135facf4a8dSllai 	}
2136facf4a8dSllai 	ddv->sdev_flags |= SDEV_BUILD;
2137facf4a8dSllai 	rw_exit(&ddv->sdev_contents);
2138facf4a8dSllai }
2139facf4a8dSllai 
2140facf4a8dSllai /*
2141facf4a8dSllai  * Given a directory node, clean out all the nodes beneath.
2142facf4a8dSllai  * If expr is specified, clean node with names matching expr.
2143facf4a8dSllai  * If SDEV_ENFORCE is specified in flags, busy nodes are made stale,
2144facf4a8dSllai  *	so they are excluded from future lookups.
2145facf4a8dSllai  */
2146facf4a8dSllai int
sdev_cleandir(struct sdev_node * ddv,char * expr,uint_t flags)2147facf4a8dSllai sdev_cleandir(struct sdev_node *ddv, char *expr, uint_t flags)
2148facf4a8dSllai {
2149facf4a8dSllai 	int error = 0;
2150facf4a8dSllai 	int busy = 0;
2151facf4a8dSllai 	struct vnode *vp;
215245b17475SAlex Wilson 	struct sdev_node *dv, *next;
2153facf4a8dSllai 	int bkstore = 0;
2154facf4a8dSllai 	int len = 0;
2155facf4a8dSllai 	char *bks_name = NULL;
2156facf4a8dSllai 
2157facf4a8dSllai 	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
2158facf4a8dSllai 
2159facf4a8dSllai 	/*
2160facf4a8dSllai 	 * We try our best to destroy all unused sdev_node's
2161facf4a8dSllai 	 */
2162facf4a8dSllai 	rw_enter(&ddv->sdev_contents, RW_WRITER);
216345b17475SAlex Wilson 	for (dv = SDEV_FIRST_ENTRY(ddv); dv != NULL; dv = next) {
216445b17475SAlex Wilson 		next = SDEV_NEXT_ENTRY(ddv, dv);
2165facf4a8dSllai 		vp = SDEVTOV(dv);
2166facf4a8dSllai 
2167facf4a8dSllai 		if (expr && gmatch(dv->sdev_name, expr) == 0)
2168facf4a8dSllai 			continue;
2169facf4a8dSllai 
2170facf4a8dSllai 		if (vp->v_type == VDIR &&
2171facf4a8dSllai 		    sdev_cleandir(dv, NULL, flags) != 0) {
2172facf4a8dSllai 			sdcmn_err9(("sdev_cleandir: dir %s busy\n",
2173facf4a8dSllai 			    dv->sdev_name));
2174facf4a8dSllai 			busy++;
2175facf4a8dSllai 			continue;
2176facf4a8dSllai 		}
2177facf4a8dSllai 
2178facf4a8dSllai 		if (vp->v_count > 0 && (flags & SDEV_ENFORCE) == 0) {
2179facf4a8dSllai 			sdcmn_err9(("sdev_cleandir: dir %s busy\n",
2180facf4a8dSllai 			    dv->sdev_name));
2181facf4a8dSllai 			busy++;
2182facf4a8dSllai 			continue;
2183facf4a8dSllai 		}
2184facf4a8dSllai 
2185facf4a8dSllai 		/*
2186facf4a8dSllai 		 * at this point, either dv is not held or SDEV_ENFORCE
2187facf4a8dSllai 		 * is specified. In either case, dv needs to be deleted
2188facf4a8dSllai 		 */
2189facf4a8dSllai 		SDEV_HOLD(dv);
2190facf4a8dSllai 
2191facf4a8dSllai 		bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0;
2192facf4a8dSllai 		if (bkstore && (vp->v_type == VDIR))
2193facf4a8dSllai 			bkstore += 1;
2194facf4a8dSllai 
2195facf4a8dSllai 		if (bkstore) {
2196facf4a8dSllai 			len = strlen(dv->sdev_name) + 1;
2197facf4a8dSllai 			bks_name = kmem_alloc(len, KM_SLEEP);
2198facf4a8dSllai 			bcopy(dv->sdev_name, bks_name, len);
2199facf4a8dSllai 		}
2200facf4a8dSllai 
22019e5aa9d8SRobert Mustacchi 		sdev_dirdelete(ddv, dv);
2202facf4a8dSllai 
2203facf4a8dSllai 		/* take care the backing store clean up */
22049e5aa9d8SRobert Mustacchi 		if (bkstore) {
2205facf4a8dSllai 			ASSERT(bks_name);
2206facf4a8dSllai 			ASSERT(ddv->sdev_attrvp);
2207facf4a8dSllai 
2208facf4a8dSllai 			if (bkstore == 1) {
2209facf4a8dSllai 				error = VOP_REMOVE(ddv->sdev_attrvp,
2210da6c28aaSamw 				    bks_name, kcred, NULL, 0);
2211facf4a8dSllai 			} else if (bkstore == 2) {
2212facf4a8dSllai 				error = VOP_RMDIR(ddv->sdev_attrvp,
2213da6c28aaSamw 				    bks_name, ddv->sdev_attrvp, kcred, NULL, 0);
2214facf4a8dSllai 			}
2215facf4a8dSllai 
2216facf4a8dSllai 			/* do not propagate the backing store errors */
2217facf4a8dSllai 			if (error) {
2218facf4a8dSllai 				sdcmn_err9(("sdev_cleandir: backing store"
2219facf4a8dSllai 				    "not cleaned\n"));
2220facf4a8dSllai 				error = 0;
2221facf4a8dSllai 			}
2222facf4a8dSllai 
2223facf4a8dSllai 			bkstore = 0;
2224facf4a8dSllai 			kmem_free(bks_name, len);
2225facf4a8dSllai 			bks_name = NULL;
2226facf4a8dSllai 			len = 0;
2227facf4a8dSllai 		}
22289e5aa9d8SRobert Mustacchi 
22299e5aa9d8SRobert Mustacchi 		ddv->sdev_flags |= SDEV_BUILD;
22309e5aa9d8SRobert Mustacchi 		SDEV_RELE(dv);
2231facf4a8dSllai 	}
2232facf4a8dSllai 
2233facf4a8dSllai 	ddv->sdev_flags |= SDEV_BUILD;
2234facf4a8dSllai 	rw_exit(&ddv->sdev_contents);
2235facf4a8dSllai 
2236facf4a8dSllai 	if (busy) {
2237facf4a8dSllai 		error = EBUSY;
2238facf4a8dSllai 	}
2239facf4a8dSllai 
2240facf4a8dSllai 	return (error);
2241facf4a8dSllai }
2242facf4a8dSllai 
2243facf4a8dSllai /*
2244facf4a8dSllai  * a convenient wrapper for readdir() funcs
2245facf4a8dSllai  */
2246facf4a8dSllai size_t
add_dir_entry(dirent64_t * de,char * nm,size_t size,ino_t ino,offset_t off)2247facf4a8dSllai add_dir_entry(dirent64_t *de, char *nm, size_t size, ino_t ino, offset_t off)
2248facf4a8dSllai {
2249facf4a8dSllai 	size_t reclen = DIRENT64_RECLEN(strlen(nm));
2250facf4a8dSllai 	if (reclen > size)
2251facf4a8dSllai 		return (0);
2252facf4a8dSllai 
2253facf4a8dSllai 	de->d_ino = (ino64_t)ino;
2254facf4a8dSllai 	de->d_off = (off64_t)off + 1;
2255facf4a8dSllai 	de->d_reclen = (ushort_t)reclen;
2256facf4a8dSllai 	(void) strncpy(de->d_name, nm, DIRENT64_NAMELEN(reclen));
2257facf4a8dSllai 	return (reclen);
2258facf4a8dSllai }
2259facf4a8dSllai 
2260facf4a8dSllai /*
2261facf4a8dSllai  * sdev_mount service routines
2262facf4a8dSllai  */
2263facf4a8dSllai int
sdev_copyin_mountargs(struct mounta * uap,struct sdev_mountargs * args)2264facf4a8dSllai sdev_copyin_mountargs(struct mounta *uap, struct sdev_mountargs *args)
2265facf4a8dSllai {
2266facf4a8dSllai 	int	error;
2267facf4a8dSllai 
2268facf4a8dSllai 	if (uap->datalen != sizeof (*args))
2269facf4a8dSllai 		return (EINVAL);
2270facf4a8dSllai 
2271facf4a8dSllai 	if (error = copyin(uap->dataptr, args, sizeof (*args))) {
2272facf4a8dSllai 		cmn_err(CE_WARN, "sdev_copyin_mountargs: can not"
2273facf4a8dSllai 		    "get user data. error %d\n", error);
2274facf4a8dSllai 		return (EFAULT);
2275facf4a8dSllai 	}
2276facf4a8dSllai 
2277facf4a8dSllai 	return (0);
2278facf4a8dSllai }
2279facf4a8dSllai 
2280facf4a8dSllai #ifdef nextdp
2281facf4a8dSllai #undef nextdp
2282facf4a8dSllai #endif
2283bc1009abSjg #define	nextdp(dp)	((struct dirent64 *) \
2284bc1009abSjg 			    (intptr_t)((char *)(dp) + (dp)->d_reclen))
2285facf4a8dSllai 
2286facf4a8dSllai /*
2287facf4a8dSllai  * readdir helper func
2288facf4a8dSllai  */
2289facf4a8dSllai int
devname_readdir_func(vnode_t * vp,uio_t * uiop,cred_t * cred,int * eofp,int flags)2290facf4a8dSllai devname_readdir_func(vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp,
2291facf4a8dSllai     int flags)
2292facf4a8dSllai {
2293facf4a8dSllai 	struct sdev_node *ddv = VTOSDEV(vp);
2294facf4a8dSllai 	struct sdev_node *dv;
2295facf4a8dSllai 	dirent64_t	*dp;
2296facf4a8dSllai 	ulong_t		outcount = 0;
2297facf4a8dSllai 	size_t		namelen;
2298facf4a8dSllai 	ulong_t		alloc_count;
2299facf4a8dSllai 	void		*outbuf;
2300facf4a8dSllai 	struct iovec	*iovp;
2301facf4a8dSllai 	int		error = 0;
2302facf4a8dSllai 	size_t		reclen;
2303facf4a8dSllai 	offset_t	diroff;
2304facf4a8dSllai 	offset_t	soff;
2305facf4a8dSllai 	int		this_reclen;
2306facf4a8dSllai 	int (*vtor)(struct sdev_node *) = NULL;
2307facf4a8dSllai 	struct vattr attr;
2308facf4a8dSllai 	timestruc_t now;
2309facf4a8dSllai 
2310facf4a8dSllai 	ASSERT(ddv->sdev_attr || ddv->sdev_attrvp);
2311facf4a8dSllai 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
2312facf4a8dSllai 
2313facf4a8dSllai 	if (uiop->uio_loffset >= MAXOFF_T) {
2314facf4a8dSllai 		if (eofp)
2315facf4a8dSllai 			*eofp = 1;
2316facf4a8dSllai 		return (0);
2317facf4a8dSllai 	}
2318facf4a8dSllai 
2319facf4a8dSllai 	if (uiop->uio_iovcnt != 1)
2320facf4a8dSllai 		return (EINVAL);
2321facf4a8dSllai 
2322facf4a8dSllai 	if (vp->v_type != VDIR)
2323facf4a8dSllai 		return (ENOTDIR);
2324facf4a8dSllai 
2325facf4a8dSllai 	if (ddv->sdev_flags & SDEV_VTOR) {
2326facf4a8dSllai 		vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
2327facf4a8dSllai 		ASSERT(vtor);
2328facf4a8dSllai 	}
2329facf4a8dSllai 
2330facf4a8dSllai 	if (eofp != NULL)
2331facf4a8dSllai 		*eofp = 0;
2332facf4a8dSllai 
2333bc1009abSjg 	soff = uiop->uio_loffset;
2334facf4a8dSllai 	iovp = uiop->uio_iov;
2335facf4a8dSllai 	alloc_count = iovp->iov_len;
2336facf4a8dSllai 	dp = outbuf = kmem_alloc(alloc_count, KM_SLEEP);
2337facf4a8dSllai 	outcount = 0;
2338facf4a8dSllai 
2339facf4a8dSllai 	if (ddv->sdev_state == SDEV_ZOMBIE)
2340facf4a8dSllai 		goto get_cache;
2341facf4a8dSllai 
2342b774fca8Sszhou 	if (SDEV_IS_GLOBAL(ddv)) {
2343facf4a8dSllai 
23443c5e027bSEric Taylor 		if ((sdev_boot_state == SDEV_BOOT_STATE_COMPLETE) &&
2345facf4a8dSllai 		    !sdev_reconfig_boot && (flags & SDEV_BROWSE) &&
2346facf4a8dSllai 		    !SDEV_IS_DYNAMIC(ddv) && !SDEV_IS_NO_NCACHE(ddv) &&
2347facf4a8dSllai 		    ((moddebug & MODDEBUG_FINI_EBUSY) == 0) &&
2348facf4a8dSllai 		    !DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state) &&
2349facf4a8dSllai 		    !DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
2350facf4a8dSllai 		    !sdev_reconfig_disable) {
2351facf4a8dSllai 			/*
2352facf4a8dSllai 			 * invoking "devfsadm" to do system device reconfig
2353facf4a8dSllai 			 */
2354facf4a8dSllai 			mutex_enter(&ddv->sdev_lookup_lock);
2355facf4a8dSllai 			SDEV_BLOCK_OTHERS(ddv,
2356facf4a8dSllai 			    (SDEV_READDIR|SDEV_LGWAITING));
2357facf4a8dSllai 			mutex_exit(&ddv->sdev_lookup_lock);
2358facf4a8dSllai 
2359facf4a8dSllai 			sdcmn_err8(("readdir of %s by %s: reconfig\n",
2360facf4a8dSllai 			    ddv->sdev_path, curproc->p_user.u_comm));
2361facf4a8dSllai 			if (sdev_reconfig_verbose) {
2362facf4a8dSllai 				cmn_err(CE_CONT,
2363facf4a8dSllai 				    "?readdir of %s by %s: reconfig\n",
2364facf4a8dSllai 				    ddv->sdev_path, curproc->p_user.u_comm);
2365facf4a8dSllai 			}
2366facf4a8dSllai 
2367facf4a8dSllai 			sdev_devfsadmd_thread(ddv, NULL, kcred);
2368facf4a8dSllai 		} else if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) {
2369facf4a8dSllai 			/*
2370facf4a8dSllai 			 * compensate the "ls" started later than "devfsadm"
2371facf4a8dSllai 			 */
2372facf4a8dSllai 			mutex_enter(&ddv->sdev_lookup_lock);
2373facf4a8dSllai 			SDEV_BLOCK_OTHERS(ddv, (SDEV_READDIR|SDEV_LGWAITING));
2374facf4a8dSllai 			mutex_exit(&ddv->sdev_lookup_lock);
2375facf4a8dSllai 		}
2376facf4a8dSllai 
2377facf4a8dSllai 		/*
2378facf4a8dSllai 		 * release the contents lock so that
23796b938478Sjg 		 * the cache may be updated by devfsadmd
2380facf4a8dSllai 		 */
2381facf4a8dSllai 		rw_exit(&ddv->sdev_contents);
2382facf4a8dSllai 		mutex_enter(&ddv->sdev_lookup_lock);
2383facf4a8dSllai 		if (SDEV_IS_READDIR(ddv))
2384facf4a8dSllai 			(void) sdev_wait4lookup(ddv, SDEV_READDIR);
2385facf4a8dSllai 		mutex_exit(&ddv->sdev_lookup_lock);
2386facf4a8dSllai 		rw_enter(&ddv->sdev_contents, RW_READER);
2387facf4a8dSllai 
2388facf4a8dSllai 		sdcmn_err4(("readdir of directory %s by %s\n",
2389facf4a8dSllai 		    ddv->sdev_name, curproc->p_user.u_comm));
23906b938478Sjg 		if (ddv->sdev_flags & SDEV_BUILD) {
2391facf4a8dSllai 			if (SDEV_IS_PERSIST(ddv)) {
2392facf4a8dSllai 				error = sdev_filldir_from_store(ddv,
2393facf4a8dSllai 				    alloc_count, cred);
2394facf4a8dSllai 			}
23956b938478Sjg 			ddv->sdev_flags &= ~SDEV_BUILD;
2396facf4a8dSllai 		}
2397facf4a8dSllai 	}
2398facf4a8dSllai 
2399facf4a8dSllai get_cache:
2400facf4a8dSllai 	/* handle "." and ".." */
2401facf4a8dSllai 	diroff = 0;
2402facf4a8dSllai 	if (soff == 0) {
2403facf4a8dSllai 		/* first time */
2404facf4a8dSllai 		this_reclen = DIRENT64_RECLEN(1);
2405facf4a8dSllai 		if (alloc_count < this_reclen) {
2406facf4a8dSllai 			error = EINVAL;
2407facf4a8dSllai 			goto done;
2408facf4a8dSllai 		}
2409facf4a8dSllai 
2410facf4a8dSllai 		dp->d_ino = (ino64_t)ddv->sdev_ino;
2411facf4a8dSllai 		dp->d_off = (off64_t)1;
2412facf4a8dSllai 		dp->d_reclen = (ushort_t)this_reclen;
2413facf4a8dSllai 
2414facf4a8dSllai 		(void) strncpy(dp->d_name, ".",
2415facf4a8dSllai 		    DIRENT64_NAMELEN(this_reclen));
2416facf4a8dSllai 		outcount += dp->d_reclen;
2417facf4a8dSllai 		dp = nextdp(dp);
2418facf4a8dSllai 	}
2419facf4a8dSllai 
2420facf4a8dSllai 	diroff++;
2421facf4a8dSllai 	if (soff <= 1) {
2422facf4a8dSllai 		this_reclen = DIRENT64_RECLEN(2);
2423facf4a8dSllai 		if (alloc_count < outcount + this_reclen) {
2424facf4a8dSllai 			error = EINVAL;
2425facf4a8dSllai 			goto done;
2426facf4a8dSllai 		}
2427facf4a8dSllai 
2428facf4a8dSllai 		dp->d_reclen = (ushort_t)this_reclen;
2429facf4a8dSllai 		dp->d_ino = (ino64_t)ddv->sdev_dotdot->sdev_ino;
2430facf4a8dSllai 		dp->d_off = (off64_t)2;
2431facf4a8dSllai 
2432facf4a8dSllai 		(void) strncpy(dp->d_name, "..",
2433facf4a8dSllai 		    DIRENT64_NAMELEN(this_reclen));
2434facf4a8dSllai 		outcount += dp->d_reclen;
2435facf4a8dSllai 
2436facf4a8dSllai 		dp = nextdp(dp);
2437facf4a8dSllai 	}
2438facf4a8dSllai 
2439facf4a8dSllai 
2440facf4a8dSllai 	/* gets the cache */
2441facf4a8dSllai 	diroff++;
2442aac43a5fSjg 	for (dv = SDEV_FIRST_ENTRY(ddv); dv;
2443aac43a5fSjg 	    dv = SDEV_NEXT_ENTRY(ddv, dv), diroff++) {
2444facf4a8dSllai 		sdcmn_err3(("sdev_readdir: diroff %lld soff %lld for '%s' \n",
2445facf4a8dSllai 		    diroff, soff, dv->sdev_name));
2446facf4a8dSllai 
2447facf4a8dSllai 		/* bypassing pre-matured nodes */
2448facf4a8dSllai 		if (diroff < soff || (dv->sdev_state != SDEV_READY)) {
2449facf4a8dSllai 			sdcmn_err3(("sdev_readdir: pre-mature node  "
2450681d9761SEric Taylor 			    "%s %d\n", dv->sdev_name, dv->sdev_state));
2451facf4a8dSllai 			continue;
2452facf4a8dSllai 		}
2453facf4a8dSllai 
2454facf4a8dSllai 		/*
2455facf4a8dSllai 		 * Check validity of node
24562b080a34SJerry Gilliam 		 * Drop invalid and nodes to be skipped.
24572b080a34SJerry Gilliam 		 * A node the validator indicates as stale needs
24582b080a34SJerry Gilliam 		 * to be returned as presumably the node name itself
24592b080a34SJerry Gilliam 		 * is valid and the node data itself will be refreshed
24602b080a34SJerry Gilliam 		 * on lookup.  An application performing a readdir then
24612b080a34SJerry Gilliam 		 * stat on each entry should thus always see consistent
24622b080a34SJerry Gilliam 		 * data.  In any case, it is not possible to synchronize
24632b080a34SJerry Gilliam 		 * with dynamic kernel state, and any view we return can
24642b080a34SJerry Gilliam 		 * never be anything more than a snapshot at a point in time.
2465facf4a8dSllai 		 */
2466facf4a8dSllai 		if (vtor) {
2467facf4a8dSllai 			switch (vtor(dv)) {
2468facf4a8dSllai 			case SDEV_VTOR_VALID:
2469facf4a8dSllai 				break;
2470facf4a8dSllai 			case SDEV_VTOR_INVALID:
2471facf4a8dSllai 			case SDEV_VTOR_SKIP:
2472facf4a8dSllai 				continue;
24732b080a34SJerry Gilliam 			case SDEV_VTOR_STALE:
24742b080a34SJerry Gilliam 				sdcmn_err3(("sdev_readir: %s stale\n",
24752b080a34SJerry Gilliam 				    dv->sdev_name));
24762b080a34SJerry Gilliam 				break;
2477facf4a8dSllai 			default:
2478facf4a8dSllai 				cmn_err(CE_PANIC,
2479facf4a8dSllai 				    "dev fs: validator failed: %s(%p)\n",
2480facf4a8dSllai 				    dv->sdev_name, (void *)dv);
2481facf4a8dSllai 				break;
2482facf4a8dSllai 			/*NOTREACHED*/
2483facf4a8dSllai 			}
2484facf4a8dSllai 		}
2485facf4a8dSllai 
2486facf4a8dSllai 		namelen = strlen(dv->sdev_name);
2487facf4a8dSllai 		reclen = DIRENT64_RECLEN(namelen);
2488facf4a8dSllai 		if (outcount + reclen > alloc_count) {
2489facf4a8dSllai 			goto full;
2490facf4a8dSllai 		}
2491facf4a8dSllai 		dp->d_reclen = (ushort_t)reclen;
2492facf4a8dSllai 		dp->d_ino = (ino64_t)dv->sdev_ino;
2493facf4a8dSllai 		dp->d_off = (off64_t)diroff + 1;
2494facf4a8dSllai 		(void) strncpy(dp->d_name, dv->sdev_name,
2495facf4a8dSllai 		    DIRENT64_NAMELEN(reclen));
2496facf4a8dSllai 		outcount += reclen;
2497facf4a8dSllai 		dp = nextdp(dp);
2498facf4a8dSllai 	}
2499facf4a8dSllai 
2500facf4a8dSllai full:
2501facf4a8dSllai 	sdcmn_err4(("sdev_readdir: moving %lu bytes: "
2502facf4a8dSllai 	    "diroff %lld, soff %lld, dv %p\n", outcount, diroff, soff,
2503facf4a8dSllai 	    (void *)dv));
2504facf4a8dSllai 
2505facf4a8dSllai 	if (outcount)
2506facf4a8dSllai 		error = uiomove(outbuf, outcount, UIO_READ, uiop);
2507facf4a8dSllai 
2508facf4a8dSllai 	if (!error) {
2509bc1009abSjg 		uiop->uio_loffset = diroff;
2510facf4a8dSllai 		if (eofp)
2511facf4a8dSllai 			*eofp = dv ? 0 : 1;
2512facf4a8dSllai 	}
2513facf4a8dSllai 
2514facf4a8dSllai 
2515facf4a8dSllai 	if (ddv->sdev_attrvp) {
2516facf4a8dSllai 		gethrestime(&now);
2517facf4a8dSllai 		attr.va_ctime = now;
2518facf4a8dSllai 		attr.va_atime = now;
2519facf4a8dSllai 		attr.va_mask = AT_CTIME|AT_ATIME;
2520facf4a8dSllai 
2521facf4a8dSllai 		(void) VOP_SETATTR(ddv->sdev_attrvp, &attr, 0, kcred, NULL);
2522facf4a8dSllai 	}
2523facf4a8dSllai done:
2524facf4a8dSllai 	kmem_free(outbuf, alloc_count);
2525facf4a8dSllai 	return (error);
2526facf4a8dSllai }
2527facf4a8dSllai 
2528facf4a8dSllai static int
sdev_modctl_lookup(const char * path,vnode_t ** r_vp)2529facf4a8dSllai sdev_modctl_lookup(const char *path, vnode_t **r_vp)
2530facf4a8dSllai {
2531facf4a8dSllai 	vnode_t *vp;
2532facf4a8dSllai 	vnode_t *cvp;
2533facf4a8dSllai 	struct sdev_node *svp;
2534facf4a8dSllai 	char *nm;
2535facf4a8dSllai 	struct pathname pn;
2536facf4a8dSllai 	int error;
2537facf4a8dSllai 	int persisted = 0;
2538facf4a8dSllai 
253974bb9a80SJerry Gilliam 	ASSERT(INGLOBALZONE(curproc));
254074bb9a80SJerry Gilliam 
2541facf4a8dSllai 	if (error = pn_get((char *)path, UIO_SYSSPACE, &pn))
2542facf4a8dSllai 		return (error);
2543facf4a8dSllai 	nm = kmem_alloc(MAXNAMELEN, KM_SLEEP);
2544facf4a8dSllai 
2545facf4a8dSllai 	vp = rootdir;
2546facf4a8dSllai 	VN_HOLD(vp);
2547facf4a8dSllai 
2548facf4a8dSllai 	while (pn_pathleft(&pn)) {
254974bb9a80SJerry Gilliam 		ASSERT(vp->v_type == VDIR || vp->v_type == VLNK);
2550facf4a8dSllai 		(void) pn_getcomponent(&pn, nm);
255174bb9a80SJerry Gilliam 
255274bb9a80SJerry Gilliam 		/*
255374bb9a80SJerry Gilliam 		 * Deal with the .. special case where we may be
255474bb9a80SJerry Gilliam 		 * traversing up across a mount point, to the
255574bb9a80SJerry Gilliam 		 * root of this filesystem or global root.
255674bb9a80SJerry Gilliam 		 */
255774bb9a80SJerry Gilliam 		if (nm[0] == '.' && nm[1] == '.' && nm[2] == 0) {
255874bb9a80SJerry Gilliam checkforroot:
255974bb9a80SJerry Gilliam 			if (VN_CMP(vp, rootdir)) {
256074bb9a80SJerry Gilliam 				nm[1] = 0;
256174bb9a80SJerry Gilliam 			} else if (vp->v_flag & VROOT) {
256274bb9a80SJerry Gilliam 				vfs_t *vfsp;
256374bb9a80SJerry Gilliam 				cvp = vp;
256474bb9a80SJerry Gilliam 				vfsp = cvp->v_vfsp;
256574bb9a80SJerry Gilliam 				vfs_rlock_wait(vfsp);
256674bb9a80SJerry Gilliam 				vp = cvp->v_vfsp->vfs_vnodecovered;
256774bb9a80SJerry Gilliam 				if (vp == NULL ||
256874bb9a80SJerry Gilliam 				    (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) {
256974bb9a80SJerry Gilliam 					vfs_unlock(vfsp);
257074bb9a80SJerry Gilliam 					VN_RELE(cvp);
257174bb9a80SJerry Gilliam 					error = EIO;
257274bb9a80SJerry Gilliam 					break;
257374bb9a80SJerry Gilliam 				}
257474bb9a80SJerry Gilliam 				VN_HOLD(vp);
257574bb9a80SJerry Gilliam 				vfs_unlock(vfsp);
257674bb9a80SJerry Gilliam 				VN_RELE(cvp);
257774bb9a80SJerry Gilliam 				cvp = NULL;
257874bb9a80SJerry Gilliam 				goto checkforroot;
257974bb9a80SJerry Gilliam 			}
258074bb9a80SJerry Gilliam 		}
258174bb9a80SJerry Gilliam 
2582da6c28aaSamw 		error = VOP_LOOKUP(vp, nm, &cvp, NULL, 0, NULL, kcred, NULL,
2583da6c28aaSamw 		    NULL, NULL);
258474bb9a80SJerry Gilliam 		if (error) {
258574bb9a80SJerry Gilliam 			VN_RELE(vp);
2586facf4a8dSllai 			break;
258774bb9a80SJerry Gilliam 		}
2588facf4a8dSllai 
2589facf4a8dSllai 		/* traverse mount points encountered on our journey */
2590facf4a8dSllai 		if (vn_ismntpt(cvp) && (error = traverse(&cvp)) != 0) {
259174bb9a80SJerry Gilliam 			VN_RELE(vp);
2592facf4a8dSllai 			VN_RELE(cvp);
2593facf4a8dSllai 			break;
2594facf4a8dSllai 		}
2595facf4a8dSllai 
259674bb9a80SJerry Gilliam 		/*
259774bb9a80SJerry Gilliam 		 * symbolic link, can be either relative and absolute
259874bb9a80SJerry Gilliam 		 */
259974bb9a80SJerry Gilliam 		if ((cvp->v_type == VLNK) && pn_pathleft(&pn)) {
260074bb9a80SJerry Gilliam 			struct pathname linkpath;
260174bb9a80SJerry Gilliam 			pn_alloc(&linkpath);
260274bb9a80SJerry Gilliam 			if (error = pn_getsymlink(cvp, &linkpath, kcred)) {
260374bb9a80SJerry Gilliam 				pn_free(&linkpath);
260474bb9a80SJerry Gilliam 				break;
260574bb9a80SJerry Gilliam 			}
260674bb9a80SJerry Gilliam 			if (pn_pathleft(&linkpath) == 0)
260774bb9a80SJerry Gilliam 				(void) pn_set(&linkpath, ".");
260874bb9a80SJerry Gilliam 			error = pn_insert(&pn, &linkpath, strlen(nm));
260974bb9a80SJerry Gilliam 			pn_free(&linkpath);
261074bb9a80SJerry Gilliam 			if (pn.pn_pathlen == 0) {
261174bb9a80SJerry Gilliam 				VN_RELE(vp);
261274bb9a80SJerry Gilliam 				return (ENOENT);
261374bb9a80SJerry Gilliam 			}
261474bb9a80SJerry Gilliam 			if (pn.pn_path[0] == '/') {
261574bb9a80SJerry Gilliam 				pn_skipslash(&pn);
261674bb9a80SJerry Gilliam 				VN_RELE(vp);
261774bb9a80SJerry Gilliam 				VN_RELE(cvp);
261874bb9a80SJerry Gilliam 				vp = rootdir;
261974bb9a80SJerry Gilliam 				VN_HOLD(vp);
262074bb9a80SJerry Gilliam 			} else {
262174bb9a80SJerry Gilliam 				VN_RELE(cvp);
262274bb9a80SJerry Gilliam 			}
262374bb9a80SJerry Gilliam 			continue;
262474bb9a80SJerry Gilliam 		}
262574bb9a80SJerry Gilliam 
262674bb9a80SJerry Gilliam 		VN_RELE(vp);
262774bb9a80SJerry Gilliam 
2628facf4a8dSllai 		/*
2629facf4a8dSllai 		 * Direct the operation to the persisting filesystem
2630facf4a8dSllai 		 * underlying /dev.  Bail if we encounter a
2631facf4a8dSllai 		 * non-persistent dev entity here.
2632facf4a8dSllai 		 */
2633facf4a8dSllai 		if (cvp->v_vfsp->vfs_fstype == devtype) {
2634facf4a8dSllai 
2635facf4a8dSllai 			if ((VTOSDEV(cvp)->sdev_flags & SDEV_PERSIST) == 0) {
2636facf4a8dSllai 				error = ENOENT;
2637facf4a8dSllai 				VN_RELE(cvp);
2638facf4a8dSllai 				break;
2639facf4a8dSllai 			}
2640facf4a8dSllai 
2641facf4a8dSllai 			if (VTOSDEV(cvp) == NULL) {
2642facf4a8dSllai 				error = ENOENT;
2643facf4a8dSllai 				VN_RELE(cvp);
2644facf4a8dSllai 				break;
2645facf4a8dSllai 			}
2646facf4a8dSllai 			svp = VTOSDEV(cvp);
2647facf4a8dSllai 			if ((vp = svp->sdev_attrvp) == NULL) {
2648facf4a8dSllai 				error = ENOENT;
2649facf4a8dSllai 				VN_RELE(cvp);
2650facf4a8dSllai 				break;
2651facf4a8dSllai 			}
2652facf4a8dSllai 			persisted = 1;
2653facf4a8dSllai 			VN_HOLD(vp);
2654facf4a8dSllai 			VN_RELE(cvp);
2655facf4a8dSllai 			cvp = vp;
2656facf4a8dSllai 		}
2657facf4a8dSllai 
2658facf4a8dSllai 		vp = cvp;
2659facf4a8dSllai 		pn_skipslash(&pn);
2660facf4a8dSllai 	}
2661facf4a8dSllai 
2662facf4a8dSllai 	kmem_free(nm, MAXNAMELEN);
2663facf4a8dSllai 	pn_free(&pn);
2664facf4a8dSllai 
2665facf4a8dSllai 	if (error)
2666facf4a8dSllai 		return (error);
2667facf4a8dSllai 
2668facf4a8dSllai 	/*
2669facf4a8dSllai 	 * Only return persisted nodes in the filesystem underlying /dev.
2670facf4a8dSllai 	 */
2671facf4a8dSllai 	if (!persisted) {
2672facf4a8dSllai 		VN_RELE(vp);
2673facf4a8dSllai 		return (ENOENT);
2674facf4a8dSllai 	}
2675facf4a8dSllai 
2676facf4a8dSllai 	*r_vp = vp;
2677facf4a8dSllai 	return (0);
2678facf4a8dSllai }
2679facf4a8dSllai 
2680facf4a8dSllai int
sdev_modctl_readdir(const char * dir,char *** dirlistp,int * npathsp,int * npathsp_alloc,int checking_empty)2681ade42b55SSebastien Roy sdev_modctl_readdir(const char *dir, char ***dirlistp, int *npathsp,
2682ade42b55SSebastien Roy     int *npathsp_alloc, int checking_empty)
2683facf4a8dSllai {
2684facf4a8dSllai 	char	**pathlist = NULL;
2685facf4a8dSllai 	char	**newlist = NULL;
2686facf4a8dSllai 	int	npaths = 0;
2687facf4a8dSllai 	int	npaths_alloc = 0;
2688facf4a8dSllai 	dirent64_t *dbuf = NULL;
2689facf4a8dSllai 	int	n;
2690facf4a8dSllai 	char	*s;
2691facf4a8dSllai 	int error;
2692facf4a8dSllai 	vnode_t *vp;
2693facf4a8dSllai 	int eof;
2694facf4a8dSllai 	struct iovec iov;
2695facf4a8dSllai 	struct uio uio;
2696facf4a8dSllai 	struct dirent64 *dp;
2697facf4a8dSllai 	size_t dlen;
2698facf4a8dSllai 	size_t dbuflen;
2699facf4a8dSllai 	int ndirents = 64;
2700facf4a8dSllai 	char *nm;
2701facf4a8dSllai 
2702facf4a8dSllai 	error = sdev_modctl_lookup(dir, &vp);
2703facf4a8dSllai 	sdcmn_err11(("modctl readdir: %s by %s: %s\n",
2704facf4a8dSllai 	    dir, curproc->p_user.u_comm,
2705facf4a8dSllai 	    (error == 0) ? "ok" : "failed"));
2706facf4a8dSllai 	if (error)
2707facf4a8dSllai 		return (error);
2708facf4a8dSllai 
2709facf4a8dSllai 	dlen = ndirents * (sizeof (*dbuf));
2710facf4a8dSllai 	dbuf = kmem_alloc(dlen, KM_SLEEP);
2711facf4a8dSllai 
2712facf4a8dSllai 	uio.uio_iov = &iov;
2713facf4a8dSllai 	uio.uio_iovcnt = 1;
2714facf4a8dSllai 	uio.uio_segflg = UIO_SYSSPACE;
2715facf4a8dSllai 	uio.uio_fmode = 0;
2716facf4a8dSllai 	uio.uio_extflg = UIO_COPY_CACHED;
2717facf4a8dSllai 	uio.uio_loffset = 0;
2718facf4a8dSllai 	uio.uio_llimit = MAXOFFSET_T;
2719facf4a8dSllai 
2720facf4a8dSllai 	eof = 0;
2721facf4a8dSllai 	error = 0;
2722facf4a8dSllai 	while (!error && !eof) {
2723facf4a8dSllai 		uio.uio_resid = dlen;
2724facf4a8dSllai 		iov.iov_base = (char *)dbuf;
2725facf4a8dSllai 		iov.iov_len = dlen;
2726facf4a8dSllai 
2727facf4a8dSllai 		(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
2728da6c28aaSamw 		error = VOP_READDIR(vp, &uio, kcred, &eof, NULL, 0);
2729facf4a8dSllai 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
2730facf4a8dSllai 
2731facf4a8dSllai 		dbuflen = dlen - uio.uio_resid;
2732facf4a8dSllai 
2733facf4a8dSllai 		if (error || dbuflen == 0)
2734facf4a8dSllai 			break;
2735facf4a8dSllai 
2736facf4a8dSllai 		for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen);
2737e37c6c37Scth 		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
2738facf4a8dSllai 
2739facf4a8dSllai 			nm = dp->d_name;
2740facf4a8dSllai 
2741facf4a8dSllai 			if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0)
2742facf4a8dSllai 				continue;
2743facf4a8dSllai 			if (npaths == npaths_alloc) {
2744facf4a8dSllai 				npaths_alloc += 64;
2745facf4a8dSllai 				newlist = (char **)
2746facf4a8dSllai 				    kmem_zalloc((npaths_alloc + 1) *
2747e37c6c37Scth 				    sizeof (char *), KM_SLEEP);
2748facf4a8dSllai 				if (pathlist) {
2749facf4a8dSllai 					bcopy(pathlist, newlist,
2750facf4a8dSllai 					    npaths * sizeof (char *));
2751facf4a8dSllai 					kmem_free(pathlist,
2752facf4a8dSllai 					    (npaths + 1) * sizeof (char *));
2753facf4a8dSllai 				}
2754facf4a8dSllai 				pathlist = newlist;
2755facf4a8dSllai 			}
2756facf4a8dSllai 			n = strlen(nm) + 1;
2757facf4a8dSllai 			s = kmem_alloc(n, KM_SLEEP);
2758facf4a8dSllai 			bcopy(nm, s, n);
2759facf4a8dSllai 			pathlist[npaths++] = s;
2760facf4a8dSllai 			sdcmn_err11(("  %s/%s\n", dir, s));
2761e37c6c37Scth 
2762e37c6c37Scth 			/* if checking empty, one entry is as good as many */
2763e37c6c37Scth 			if (checking_empty) {
2764e37c6c37Scth 				eof = 1;
2765e37c6c37Scth 				break;
2766e37c6c37Scth 			}
2767facf4a8dSllai 		}
2768facf4a8dSllai 	}
2769facf4a8dSllai 
2770facf4a8dSllai exit:
2771facf4a8dSllai 	VN_RELE(vp);
2772facf4a8dSllai 
2773facf4a8dSllai 	if (dbuf)
2774facf4a8dSllai 		kmem_free(dbuf, dlen);
2775facf4a8dSllai 
2776facf4a8dSllai 	if (error)
2777facf4a8dSllai 		return (error);
2778facf4a8dSllai 
2779facf4a8dSllai 	*dirlistp = pathlist;
2780facf4a8dSllai 	*npathsp = npaths;
2781facf4a8dSllai 	*npathsp_alloc = npaths_alloc;
2782facf4a8dSllai 
2783facf4a8dSllai 	return (0);
2784facf4a8dSllai }
2785facf4a8dSllai 
2786facf4a8dSllai void
sdev_modctl_readdir_free(char ** pathlist,int npaths,int npaths_alloc)2787facf4a8dSllai sdev_modctl_readdir_free(char **pathlist, int npaths, int npaths_alloc)
2788facf4a8dSllai {
2789facf4a8dSllai 	int	i, n;
2790facf4a8dSllai 
2791facf4a8dSllai 	for (i = 0; i < npaths; i++) {
2792facf4a8dSllai 		n = strlen(pathlist[i]) + 1;
2793facf4a8dSllai 		kmem_free(pathlist[i], n);
2794facf4a8dSllai 	}
2795facf4a8dSllai 
2796facf4a8dSllai 	kmem_free(pathlist, (npaths_alloc + 1) * sizeof (char *));
2797facf4a8dSllai }
2798facf4a8dSllai 
2799facf4a8dSllai int
sdev_modctl_devexists(const char * path)2800facf4a8dSllai sdev_modctl_devexists(const char *path)
2801facf4a8dSllai {
2802facf4a8dSllai 	vnode_t *vp;
2803facf4a8dSllai 	int error;
2804facf4a8dSllai 
2805facf4a8dSllai 	error = sdev_modctl_lookup(path, &vp);
2806facf4a8dSllai 	sdcmn_err11(("modctl dev exists: %s by %s: %s\n",
2807facf4a8dSllai 	    path, curproc->p_user.u_comm,
2808facf4a8dSllai 	    (error == 0) ? "ok" : "failed"));
2809facf4a8dSllai 	if (error == 0)
2810facf4a8dSllai 		VN_RELE(vp);
2811facf4a8dSllai 
2812facf4a8dSllai 	return (error);
2813facf4a8dSllai }
2814facf4a8dSllai 
2815facf4a8dSllai /*
2816facf4a8dSllai  * a generic setattr() function
2817facf4a8dSllai  *
2818facf4a8dSllai  * note: flags only supports AT_UID and AT_GID.
2819facf4a8dSllai  *	 Future enhancements can be done for other types, e.g. AT_MODE
2820facf4a8dSllai  */
2821facf4a8dSllai int
devname_setattr_func(struct vnode * vp,struct vattr * vap,int flags,struct cred * cred,int (* callback)(struct sdev_node *,struct vattr *,int),int protocol)2822facf4a8dSllai devname_setattr_func(struct vnode *vp, struct vattr *vap, int flags,
2823facf4a8dSllai     struct cred *cred, int (*callback)(struct sdev_node *, struct vattr *,
2824facf4a8dSllai     int), int protocol)
2825facf4a8dSllai {
2826facf4a8dSllai 	struct sdev_node	*dv = VTOSDEV(vp);
2827facf4a8dSllai 	struct sdev_node	*parent = dv->sdev_dotdot;
2828facf4a8dSllai 	struct vattr		*get;
2829facf4a8dSllai 	uint_t			mask = vap->va_mask;
2830facf4a8dSllai 	int 			error;
2831facf4a8dSllai 
2832facf4a8dSllai 	/* some sanity checks */
2833facf4a8dSllai 	if (vap->va_mask & AT_NOSET)
2834facf4a8dSllai 		return (EINVAL);
2835facf4a8dSllai 
2836facf4a8dSllai 	if (vap->va_mask & AT_SIZE) {
2837facf4a8dSllai 		if (vp->v_type == VDIR) {
2838facf4a8dSllai 			return (EISDIR);
2839facf4a8dSllai 		}
2840facf4a8dSllai 	}
2841facf4a8dSllai 
2842facf4a8dSllai 	/* no need to set attribute, but do not fail either */
2843facf4a8dSllai 	ASSERT(parent);
2844facf4a8dSllai 	rw_enter(&parent->sdev_contents, RW_READER);
2845facf4a8dSllai 	if (dv->sdev_state == SDEV_ZOMBIE) {
2846facf4a8dSllai 		rw_exit(&parent->sdev_contents);
2847facf4a8dSllai 		return (0);
2848facf4a8dSllai 	}
2849facf4a8dSllai 
2850facf4a8dSllai 	/* If backing store exists, just set it. */
2851facf4a8dSllai 	if (dv->sdev_attrvp) {
2852facf4a8dSllai 		rw_exit(&parent->sdev_contents);
2853facf4a8dSllai 		return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL));
2854facf4a8dSllai 	}
2855facf4a8dSllai 
2856facf4a8dSllai 	/*
2857facf4a8dSllai 	 * Otherwise, for nodes with the persistence attribute, create it.
2858facf4a8dSllai 	 */
2859facf4a8dSllai 	ASSERT(dv->sdev_attr);
2860facf4a8dSllai 	if (SDEV_IS_PERSIST(dv) ||
2861facf4a8dSllai 	    ((vap->va_mask & ~AT_TIMES) != 0 && !SDEV_IS_DYNAMIC(dv))) {
2862facf4a8dSllai 		sdev_vattr_merge(dv, vap);
2863facf4a8dSllai 		rw_enter(&dv->sdev_contents, RW_WRITER);
2864facf4a8dSllai 		error = sdev_shadow_node(dv, cred);
2865facf4a8dSllai 		rw_exit(&dv->sdev_contents);
2866facf4a8dSllai 		rw_exit(&parent->sdev_contents);
2867facf4a8dSllai 
2868facf4a8dSllai 		if (error)
2869facf4a8dSllai 			return (error);
2870facf4a8dSllai 		return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL));
2871facf4a8dSllai 	}
2872facf4a8dSllai 
2873facf4a8dSllai 
2874facf4a8dSllai 	/*
2875facf4a8dSllai 	 * sdev_attr was allocated in sdev_mknode
2876facf4a8dSllai 	 */
2877facf4a8dSllai 	rw_enter(&dv->sdev_contents, RW_WRITER);
2878da6c28aaSamw 	error = secpolicy_vnode_setattr(cred, vp, vap,
2879da6c28aaSamw 	    dv->sdev_attr, flags, sdev_unlocked_access, dv);
2880facf4a8dSllai 	if (error) {
2881facf4a8dSllai 		rw_exit(&dv->sdev_contents);
2882facf4a8dSllai 		rw_exit(&parent->sdev_contents);
2883facf4a8dSllai 		return (error);
2884facf4a8dSllai 	}
2885facf4a8dSllai 
2886facf4a8dSllai 	get = dv->sdev_attr;
2887facf4a8dSllai 	if (mask & AT_MODE) {
2888facf4a8dSllai 		get->va_mode &= S_IFMT;
2889facf4a8dSllai 		get->va_mode |= vap->va_mode & ~S_IFMT;
2890facf4a8dSllai 	}
2891facf4a8dSllai 
2892facf4a8dSllai 	if ((mask & AT_UID) || (mask & AT_GID)) {
2893facf4a8dSllai 		if (mask & AT_UID)
2894facf4a8dSllai 			get->va_uid = vap->va_uid;
2895facf4a8dSllai 		if (mask & AT_GID)
2896facf4a8dSllai 			get->va_gid = vap->va_gid;
2897facf4a8dSllai 		/*
2898facf4a8dSllai 		 * a callback must be provided if the protocol is set
2899facf4a8dSllai 		 */
2900facf4a8dSllai 		if ((protocol & AT_UID) || (protocol & AT_GID)) {
2901facf4a8dSllai 			ASSERT(callback);
2902facf4a8dSllai 			error = callback(dv, get, protocol);
2903facf4a8dSllai 			if (error) {
2904facf4a8dSllai 				rw_exit(&dv->sdev_contents);
2905facf4a8dSllai 				rw_exit(&parent->sdev_contents);
2906facf4a8dSllai 				return (error);
2907facf4a8dSllai 			}
2908facf4a8dSllai 		}
2909facf4a8dSllai 	}
2910facf4a8dSllai 
2911facf4a8dSllai 	if (mask & AT_ATIME)
2912facf4a8dSllai 		get->va_atime = vap->va_atime;
2913facf4a8dSllai 	if (mask & AT_MTIME)
2914facf4a8dSllai 		get->va_mtime = vap->va_mtime;
2915facf4a8dSllai 	if (mask & (AT_MODE | AT_UID | AT_GID | AT_CTIME)) {
2916facf4a8dSllai 		gethrestime(&get->va_ctime);
2917facf4a8dSllai 	}
2918facf4a8dSllai 
2919facf4a8dSllai 	sdev_vattr_merge(dv, get);
2920facf4a8dSllai 	rw_exit(&dv->sdev_contents);
2921facf4a8dSllai 	rw_exit(&parent->sdev_contents);
2922facf4a8dSllai 	return (0);
2923facf4a8dSllai }
2924d62bc4baSyz 
2925d62bc4baSyz /*
2926d62bc4baSyz  * a generic inactive() function
2927d62bc4baSyz  */
29283c5e027bSEric Taylor /*ARGSUSED*/
2929d62bc4baSyz void
devname_inactive_func(struct vnode * vp,struct cred * cred,void (* callback)(struct vnode *))2930d62bc4baSyz devname_inactive_func(struct vnode *vp, struct cred *cred,
2931d62bc4baSyz     void (*callback)(struct vnode *))
2932d62bc4baSyz {
2933d62bc4baSyz 	int clean;
2934d62bc4baSyz 	struct sdev_node *dv = VTOSDEV(vp);
2935d62bc4baSyz 	int state;
2936d62bc4baSyz 
2937d62bc4baSyz 	mutex_enter(&vp->v_lock);
2938d62bc4baSyz 	ASSERT(vp->v_count >= 1);
2939d62bc4baSyz 
29409e5aa9d8SRobert Mustacchi 
2941d62bc4baSyz 	if (vp->v_count == 1 && callback != NULL)
2942d62bc4baSyz 		callback(vp);
2943d62bc4baSyz 
29449e5aa9d8SRobert Mustacchi 	rw_enter(&dv->sdev_contents, RW_WRITER);
29459e5aa9d8SRobert Mustacchi 	state = dv->sdev_state;
29469e5aa9d8SRobert Mustacchi 
2947d62bc4baSyz 	clean = (vp->v_count == 1) && (state == SDEV_ZOMBIE);
2948d62bc4baSyz 
2949d62bc4baSyz 	/*
29509e5aa9d8SRobert Mustacchi 	 * sdev is a rather bad public citizen. It violates the general
29519e5aa9d8SRobert Mustacchi 	 * agreement that in memory nodes should always have a valid reference
29529e5aa9d8SRobert Mustacchi 	 * count on their vnode. But that's not the case here. This means that
29539e5aa9d8SRobert Mustacchi 	 * we do actually have to distinguish between getting inactive callbacks
29549e5aa9d8SRobert Mustacchi 	 * for zombies and otherwise. This should probably be fixed.
2955d62bc4baSyz 	 */
2956d62bc4baSyz 	if (clean) {
29579e5aa9d8SRobert Mustacchi 		/* Remove the . entry to ourselves */
2958d62bc4baSyz 		if (vp->v_type == VDIR) {
29599e5aa9d8SRobert Mustacchi 			decr_link(dv);
2960d62bc4baSyz 		}
29619e5aa9d8SRobert Mustacchi 		VERIFY(dv->sdev_nlink == 1);
29629e5aa9d8SRobert Mustacchi 		decr_link(dv);
2963ade42b55SSebastien Roy 		VN_RELE_LOCKED(vp);
29649e5aa9d8SRobert Mustacchi 		rw_exit(&dv->sdev_contents);
2965d62bc4baSyz 		mutex_exit(&vp->v_lock);
2966d62bc4baSyz 		sdev_nodedestroy(dv, 0);
2967d62bc4baSyz 	} else {
2968ade42b55SSebastien Roy 		VN_RELE_LOCKED(vp);
29699e5aa9d8SRobert Mustacchi 		rw_exit(&dv->sdev_contents);
2970d62bc4baSyz 		mutex_exit(&vp->v_lock);
2971d62bc4baSyz 	}
2972d62bc4baSyz }
2973