xref: /titanic_51/usr/src/uts/common/fs/dev/sdev_subr.c (revision aab83bb83be7342f6cfccaed8d5fe0b2f404855d)
1facf4a8dSllai1 /*
2facf4a8dSllai1  * CDDL HEADER START
3facf4a8dSllai1  *
4facf4a8dSllai1  * The contents of this file are subject to the terms of the
5facf4a8dSllai1  * Common Development and Distribution License (the "License").
6facf4a8dSllai1  * You may not use this file except in compliance with the License.
7facf4a8dSllai1  *
8facf4a8dSllai1  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9facf4a8dSllai1  * or http://www.opensolaris.org/os/licensing.
10facf4a8dSllai1  * See the License for the specific language governing permissions
11facf4a8dSllai1  * and limitations under the License.
12facf4a8dSllai1  *
13facf4a8dSllai1  * When distributing Covered Code, include this CDDL HEADER in each
14facf4a8dSllai1  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15facf4a8dSllai1  * If applicable, add the following below this CDDL HEADER, with the
16facf4a8dSllai1  * fields enclosed by brackets "[]" replaced with your own identifying
17facf4a8dSllai1  * information: Portions Copyright [yyyy] [name of copyright owner]
18facf4a8dSllai1  *
19facf4a8dSllai1  * CDDL HEADER END
20facf4a8dSllai1  */
21facf4a8dSllai1 /*
220fbb751dSJohn Levon  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23*9e5aa9d8SRobert Mustacchi  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
24facf4a8dSllai1  */
25facf4a8dSllai1 
26facf4a8dSllai1 /*
27facf4a8dSllai1  * utility routines for the /dev fs
28facf4a8dSllai1  */
29facf4a8dSllai1 
30facf4a8dSllai1 #include <sys/types.h>
31facf4a8dSllai1 #include <sys/param.h>
32facf4a8dSllai1 #include <sys/t_lock.h>
33facf4a8dSllai1 #include <sys/systm.h>
34facf4a8dSllai1 #include <sys/sysmacros.h>
35facf4a8dSllai1 #include <sys/user.h>
36facf4a8dSllai1 #include <sys/time.h>
37facf4a8dSllai1 #include <sys/vfs.h>
38facf4a8dSllai1 #include <sys/vnode.h>
39facf4a8dSllai1 #include <sys/file.h>
40facf4a8dSllai1 #include <sys/fcntl.h>
41facf4a8dSllai1 #include <sys/flock.h>
42facf4a8dSllai1 #include <sys/kmem.h>
43facf4a8dSllai1 #include <sys/uio.h>
44facf4a8dSllai1 #include <sys/errno.h>
45facf4a8dSllai1 #include <sys/stat.h>
46facf4a8dSllai1 #include <sys/cred.h>
47facf4a8dSllai1 #include <sys/dirent.h>
48facf4a8dSllai1 #include <sys/pathname.h>
49facf4a8dSllai1 #include <sys/cmn_err.h>
50facf4a8dSllai1 #include <sys/debug.h>
51facf4a8dSllai1 #include <sys/mode.h>
52facf4a8dSllai1 #include <sys/policy.h>
53facf4a8dSllai1 #include <fs/fs_subr.h>
54facf4a8dSllai1 #include <sys/mount.h>
55facf4a8dSllai1 #include <sys/fs/snode.h>
56facf4a8dSllai1 #include <sys/fs/dv_node.h>
57facf4a8dSllai1 #include <sys/fs/sdev_impl.h>
58facf4a8dSllai1 #include <sys/sunndi.h>
59facf4a8dSllai1 #include <sys/sunmdi.h>
60facf4a8dSllai1 #include <sys/conf.h>
61facf4a8dSllai1 #include <sys/proc.h>
62facf4a8dSllai1 #include <sys/user.h>
63facf4a8dSllai1 #include <sys/modctl.h>
64facf4a8dSllai1 
65facf4a8dSllai1 #ifdef DEBUG
66facf4a8dSllai1 int sdev_debug = 0x00000001;
67facf4a8dSllai1 int sdev_debug_cache_flags = 0;
68facf4a8dSllai1 #endif
69facf4a8dSllai1 
70facf4a8dSllai1 /*
71facf4a8dSllai1  * globals
72facf4a8dSllai1  */
73facf4a8dSllai1 /* prototype memory vattrs */
74facf4a8dSllai1 vattr_t sdev_vattr_dir = {
75facf4a8dSllai1 	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
76facf4a8dSllai1 	VDIR,					/* va_type */
77facf4a8dSllai1 	SDEV_DIRMODE_DEFAULT,			/* va_mode */
78facf4a8dSllai1 	SDEV_UID_DEFAULT,			/* va_uid */
79facf4a8dSllai1 	SDEV_GID_DEFAULT,			/* va_gid */
80facf4a8dSllai1 	0,					/* va_fsid */
81facf4a8dSllai1 	0,					/* va_nodeid */
82facf4a8dSllai1 	0,					/* va_nlink */
83facf4a8dSllai1 	0,					/* va_size */
84facf4a8dSllai1 	0,					/* va_atime */
85facf4a8dSllai1 	0,					/* va_mtime */
86facf4a8dSllai1 	0,					/* va_ctime */
87facf4a8dSllai1 	0,					/* va_rdev */
88facf4a8dSllai1 	0,					/* va_blksize */
89facf4a8dSllai1 	0,					/* va_nblocks */
90facf4a8dSllai1 	0					/* va_vcode */
91facf4a8dSllai1 };
92facf4a8dSllai1 
93facf4a8dSllai1 vattr_t sdev_vattr_lnk = {
94facf4a8dSllai1 	AT_TYPE|AT_MODE,			/* va_mask */
95facf4a8dSllai1 	VLNK,					/* va_type */
96facf4a8dSllai1 	SDEV_LNKMODE_DEFAULT,			/* va_mode */
97facf4a8dSllai1 	SDEV_UID_DEFAULT,			/* va_uid */
98facf4a8dSllai1 	SDEV_GID_DEFAULT,			/* va_gid */
99facf4a8dSllai1 	0,					/* va_fsid */
100facf4a8dSllai1 	0,					/* va_nodeid */
101facf4a8dSllai1 	0,					/* va_nlink */
102facf4a8dSllai1 	0,					/* va_size */
103facf4a8dSllai1 	0,					/* va_atime */
104facf4a8dSllai1 	0,					/* va_mtime */
105facf4a8dSllai1 	0,					/* va_ctime */
106facf4a8dSllai1 	0,					/* va_rdev */
107facf4a8dSllai1 	0,					/* va_blksize */
108facf4a8dSllai1 	0,					/* va_nblocks */
109facf4a8dSllai1 	0					/* va_vcode */
110facf4a8dSllai1 };
111facf4a8dSllai1 
112facf4a8dSllai1 vattr_t sdev_vattr_blk = {
113facf4a8dSllai1 	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
114facf4a8dSllai1 	VBLK,					/* va_type */
115facf4a8dSllai1 	S_IFBLK | SDEV_DEVMODE_DEFAULT,		/* va_mode */
116facf4a8dSllai1 	SDEV_UID_DEFAULT,			/* va_uid */
117facf4a8dSllai1 	SDEV_GID_DEFAULT,			/* va_gid */
118facf4a8dSllai1 	0,					/* va_fsid */
119facf4a8dSllai1 	0,					/* va_nodeid */
120facf4a8dSllai1 	0,					/* va_nlink */
121facf4a8dSllai1 	0,					/* va_size */
122facf4a8dSllai1 	0,					/* va_atime */
123facf4a8dSllai1 	0,					/* va_mtime */
124facf4a8dSllai1 	0,					/* va_ctime */
125facf4a8dSllai1 	0,					/* va_rdev */
126facf4a8dSllai1 	0,					/* va_blksize */
127facf4a8dSllai1 	0,					/* va_nblocks */
128facf4a8dSllai1 	0					/* va_vcode */
129facf4a8dSllai1 };
130facf4a8dSllai1 
131facf4a8dSllai1 vattr_t sdev_vattr_chr = {
132facf4a8dSllai1 	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
133facf4a8dSllai1 	VCHR,					/* va_type */
134facf4a8dSllai1 	S_IFCHR | SDEV_DEVMODE_DEFAULT,		/* va_mode */
135facf4a8dSllai1 	SDEV_UID_DEFAULT,			/* va_uid */
136facf4a8dSllai1 	SDEV_GID_DEFAULT,			/* va_gid */
137facf4a8dSllai1 	0,					/* va_fsid */
138facf4a8dSllai1 	0,					/* va_nodeid */
139facf4a8dSllai1 	0,					/* va_nlink */
140facf4a8dSllai1 	0,					/* va_size */
141facf4a8dSllai1 	0,					/* va_atime */
142facf4a8dSllai1 	0,					/* va_mtime */
143facf4a8dSllai1 	0,					/* va_ctime */
144facf4a8dSllai1 	0,					/* va_rdev */
145facf4a8dSllai1 	0,					/* va_blksize */
146facf4a8dSllai1 	0,					/* va_nblocks */
147facf4a8dSllai1 	0					/* va_vcode */
148facf4a8dSllai1 };
149facf4a8dSllai1 
150facf4a8dSllai1 kmem_cache_t	*sdev_node_cache;	/* sdev_node cache */
151facf4a8dSllai1 int		devtype;		/* fstype */
152facf4a8dSllai1 
153facf4a8dSllai1 /* static */
154facf4a8dSllai1 static struct vnodeops *sdev_get_vop(struct sdev_node *);
155681d9761SEric Taylor static void sdev_set_no_negcache(struct sdev_node *);
156facf4a8dSllai1 static fs_operation_def_t *sdev_merge_vtab(const fs_operation_def_t []);
157facf4a8dSllai1 static void sdev_free_vtab(fs_operation_def_t *);
158facf4a8dSllai1 
159facf4a8dSllai1 static void
160facf4a8dSllai1 sdev_prof_free(struct sdev_node *dv)
161facf4a8dSllai1 {
162facf4a8dSllai1 	ASSERT(!SDEV_IS_GLOBAL(dv));
163facf4a8dSllai1 	nvlist_free(dv->sdev_prof.dev_name);
164facf4a8dSllai1 	nvlist_free(dv->sdev_prof.dev_map);
165facf4a8dSllai1 	nvlist_free(dv->sdev_prof.dev_symlink);
166facf4a8dSllai1 	nvlist_free(dv->sdev_prof.dev_glob_incdir);
167facf4a8dSllai1 	nvlist_free(dv->sdev_prof.dev_glob_excdir);
168facf4a8dSllai1 	bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
169facf4a8dSllai1 }
170facf4a8dSllai1 
171b5fca8f8Stomee /* sdev_node cache constructor */
172facf4a8dSllai1 /*ARGSUSED1*/
173facf4a8dSllai1 static int
174facf4a8dSllai1 i_sdev_node_ctor(void *buf, void *cfarg, int flag)
175facf4a8dSllai1 {
176facf4a8dSllai1 	struct sdev_node *dv = (struct sdev_node *)buf;
177facf4a8dSllai1 	struct vnode *vp;
178facf4a8dSllai1 
179facf4a8dSllai1 	bzero(buf, sizeof (struct sdev_node));
180b5fca8f8Stomee 	vp = dv->sdev_vnode = vn_alloc(flag);
181b5fca8f8Stomee 	if (vp == NULL) {
182b5fca8f8Stomee 		return (-1);
183b5fca8f8Stomee 	}
184b5fca8f8Stomee 	vp->v_data = dv;
185facf4a8dSllai1 	rw_init(&dv->sdev_contents, NULL, RW_DEFAULT, NULL);
186facf4a8dSllai1 	return (0);
187facf4a8dSllai1 }
188facf4a8dSllai1 
189b5fca8f8Stomee /* sdev_node cache destructor */
190facf4a8dSllai1 /*ARGSUSED1*/
191facf4a8dSllai1 static void
192facf4a8dSllai1 i_sdev_node_dtor(void *buf, void *arg)
193facf4a8dSllai1 {
194facf4a8dSllai1 	struct sdev_node *dv = (struct sdev_node *)buf;
195facf4a8dSllai1 	struct vnode *vp = SDEVTOV(dv);
196facf4a8dSllai1 
197facf4a8dSllai1 	rw_destroy(&dv->sdev_contents);
198facf4a8dSllai1 	vn_free(vp);
199facf4a8dSllai1 }
200facf4a8dSllai1 
201facf4a8dSllai1 /* initialize sdev_node cache */
202facf4a8dSllai1 void
203facf4a8dSllai1 sdev_node_cache_init()
204facf4a8dSllai1 {
205facf4a8dSllai1 	int flags = 0;
206facf4a8dSllai1 
207facf4a8dSllai1 #ifdef	DEBUG
208facf4a8dSllai1 	flags = sdev_debug_cache_flags;
209facf4a8dSllai1 	if (flags)
210facf4a8dSllai1 		sdcmn_err(("cache debug flags 0x%x\n", flags));
211facf4a8dSllai1 #endif	/* DEBUG */
212facf4a8dSllai1 
213facf4a8dSllai1 	ASSERT(sdev_node_cache == NULL);
214facf4a8dSllai1 	sdev_node_cache = kmem_cache_create("sdev_node_cache",
215facf4a8dSllai1 	    sizeof (struct sdev_node), 0, i_sdev_node_ctor, i_sdev_node_dtor,
216facf4a8dSllai1 	    NULL, NULL, NULL, flags);
217facf4a8dSllai1 }
218facf4a8dSllai1 
219facf4a8dSllai1 /* destroy sdev_node cache */
220facf4a8dSllai1 void
221facf4a8dSllai1 sdev_node_cache_fini()
222facf4a8dSllai1 {
223facf4a8dSllai1 	ASSERT(sdev_node_cache != NULL);
224facf4a8dSllai1 	kmem_cache_destroy(sdev_node_cache);
225facf4a8dSllai1 	sdev_node_cache = NULL;
226facf4a8dSllai1 }
227facf4a8dSllai1 
228aac43a5fSjg /*
229aac43a5fSjg  * Compare two nodes lexographically to balance avl tree
230aac43a5fSjg  */
231aac43a5fSjg static int
232aac43a5fSjg sdev_compare_nodes(const struct sdev_node *dv1, const struct sdev_node *dv2)
233aac43a5fSjg {
234aac43a5fSjg 	int rv;
235aac43a5fSjg 	if ((rv = strcmp(dv1->sdev_name, dv2->sdev_name)) == 0)
236aac43a5fSjg 		return (0);
237aac43a5fSjg 	return ((rv < 0) ? -1 : 1);
238aac43a5fSjg }
239aac43a5fSjg 
240facf4a8dSllai1 void
241facf4a8dSllai1 sdev_set_nodestate(struct sdev_node *dv, sdev_node_state_t state)
242facf4a8dSllai1 {
243facf4a8dSllai1 	ASSERT(dv);
244facf4a8dSllai1 	ASSERT(RW_WRITE_HELD(&dv->sdev_contents));
245facf4a8dSllai1 	dv->sdev_state = state;
246facf4a8dSllai1 }
247facf4a8dSllai1 
248facf4a8dSllai1 static void
249bb5fffbeSJerry Gilliam sdev_attr_update(struct sdev_node *dv, vattr_t *vap)
250facf4a8dSllai1 {
251facf4a8dSllai1 	timestruc_t	now;
252bb5fffbeSJerry Gilliam 	struct vattr	*attrp;
253bb5fffbeSJerry Gilliam 	uint_t		mask;
254facf4a8dSllai1 
255bb5fffbeSJerry Gilliam 	ASSERT(dv->sdev_attr);
256facf4a8dSllai1 	ASSERT(vap);
257facf4a8dSllai1 
258bb5fffbeSJerry Gilliam 	attrp = dv->sdev_attr;
259bb5fffbeSJerry Gilliam 	mask = vap->va_mask;
260bb5fffbeSJerry Gilliam 	if (mask & AT_TYPE)
261bb5fffbeSJerry Gilliam 		attrp->va_type = vap->va_type;
262bb5fffbeSJerry Gilliam 	if (mask & AT_MODE)
263bb5fffbeSJerry Gilliam 		attrp->va_mode = vap->va_mode;
264bb5fffbeSJerry Gilliam 	if (mask & AT_UID)
265bb5fffbeSJerry Gilliam 		attrp->va_uid = vap->va_uid;
266bb5fffbeSJerry Gilliam 	if (mask & AT_GID)
267bb5fffbeSJerry Gilliam 		attrp->va_gid = vap->va_gid;
268bb5fffbeSJerry Gilliam 	if (mask & AT_RDEV)
269bb5fffbeSJerry Gilliam 		attrp->va_rdev = vap->va_rdev;
270facf4a8dSllai1 
271facf4a8dSllai1 	gethrestime(&now);
272bb5fffbeSJerry Gilliam 	attrp->va_atime = (mask & AT_ATIME) ? vap->va_atime : now;
273bb5fffbeSJerry Gilliam 	attrp->va_mtime = (mask & AT_MTIME) ? vap->va_mtime : now;
274bb5fffbeSJerry Gilliam 	attrp->va_ctime = (mask & AT_CTIME) ? vap->va_ctime : now;
275bb5fffbeSJerry Gilliam }
276bb5fffbeSJerry Gilliam 
277bb5fffbeSJerry Gilliam static void
278bb5fffbeSJerry Gilliam sdev_attr_alloc(struct sdev_node *dv, vattr_t *vap)
279bb5fffbeSJerry Gilliam {
280bb5fffbeSJerry Gilliam 	ASSERT(dv->sdev_attr == NULL);
281bb5fffbeSJerry Gilliam 	ASSERT(vap->va_mask & AT_TYPE);
282bb5fffbeSJerry Gilliam 	ASSERT(vap->va_mask & AT_MODE);
283bb5fffbeSJerry Gilliam 
284bb5fffbeSJerry Gilliam 	dv->sdev_attr = kmem_zalloc(sizeof (struct vattr), KM_SLEEP);
285bb5fffbeSJerry Gilliam 	sdev_attr_update(dv, vap);
286facf4a8dSllai1 }
287facf4a8dSllai1 
288facf4a8dSllai1 /* alloc and initialize a sdev_node */
289facf4a8dSllai1 int
290facf4a8dSllai1 sdev_nodeinit(struct sdev_node *ddv, char *nm, struct sdev_node **newdv,
291facf4a8dSllai1     vattr_t *vap)
292facf4a8dSllai1 {
293facf4a8dSllai1 	struct sdev_node *dv = NULL;
294facf4a8dSllai1 	struct vnode *vp;
295facf4a8dSllai1 	size_t nmlen, len;
296facf4a8dSllai1 	devname_handle_t  *dhl;
297facf4a8dSllai1 
298facf4a8dSllai1 	nmlen = strlen(nm) + 1;
299facf4a8dSllai1 	if (nmlen > MAXNAMELEN) {
300facf4a8dSllai1 		sdcmn_err9(("sdev_nodeinit: node name %s"
301facf4a8dSllai1 		    " too long\n", nm));
302facf4a8dSllai1 		*newdv = NULL;
303facf4a8dSllai1 		return (ENAMETOOLONG);
304facf4a8dSllai1 	}
305facf4a8dSllai1 
306facf4a8dSllai1 	dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP);
307facf4a8dSllai1 
308facf4a8dSllai1 	dv->sdev_name = kmem_alloc(nmlen, KM_SLEEP);
309facf4a8dSllai1 	bcopy(nm, dv->sdev_name, nmlen);
310facf4a8dSllai1 	dv->sdev_namelen = nmlen - 1;	/* '\0' not included */
311facf4a8dSllai1 	len = strlen(ddv->sdev_path) + strlen(nm) + 2;
312facf4a8dSllai1 	dv->sdev_path = kmem_alloc(len, KM_SLEEP);
313facf4a8dSllai1 	(void) snprintf(dv->sdev_path, len, "%s/%s", ddv->sdev_path, nm);
314facf4a8dSllai1 	/* overwritten for VLNK nodes */
315facf4a8dSllai1 	dv->sdev_symlink = NULL;
316facf4a8dSllai1 
317facf4a8dSllai1 	vp = SDEVTOV(dv);
318facf4a8dSllai1 	vn_reinit(vp);
319facf4a8dSllai1 	vp->v_vfsp = SDEVTOV(ddv)->v_vfsp;
320facf4a8dSllai1 	if (vap)
321facf4a8dSllai1 		vp->v_type = vap->va_type;
322facf4a8dSllai1 
323facf4a8dSllai1 	/*
324facf4a8dSllai1 	 * initialized to the parent's vnodeops.
325facf4a8dSllai1 	 * maybe overwriten for a VDIR
326facf4a8dSllai1 	 */
327facf4a8dSllai1 	vn_setops(vp, vn_getops(SDEVTOV(ddv)));
328facf4a8dSllai1 	vn_exists(vp);
329facf4a8dSllai1 
330facf4a8dSllai1 	dv->sdev_dotdot = NULL;
331facf4a8dSllai1 	dv->sdev_attrvp = NULL;
332facf4a8dSllai1 	if (vap) {
333bb5fffbeSJerry Gilliam 		sdev_attr_alloc(dv, vap);
334facf4a8dSllai1 	} else {
335facf4a8dSllai1 		dv->sdev_attr = NULL;
336facf4a8dSllai1 	}
337facf4a8dSllai1 
338facf4a8dSllai1 	dv->sdev_ino = sdev_mkino(dv);
339facf4a8dSllai1 	dv->sdev_nlink = 0;		/* updated on insert */
340facf4a8dSllai1 	dv->sdev_flags = ddv->sdev_flags; /* inherit from the parent first */
341facf4a8dSllai1 	dv->sdev_flags |= SDEV_BUILD;
342facf4a8dSllai1 	mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL);
343facf4a8dSllai1 	cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL);
344facf4a8dSllai1 	if (SDEV_IS_GLOBAL(ddv)) {
345facf4a8dSllai1 		dv->sdev_flags |= SDEV_GLOBAL;
346facf4a8dSllai1 		dhl = &(dv->sdev_handle);
347facf4a8dSllai1 		dhl->dh_data = dv;
348facf4a8dSllai1 		dhl->dh_args = NULL;
349681d9761SEric Taylor 		sdev_set_no_negcache(dv);
350facf4a8dSllai1 		dv->sdev_gdir_gen = 0;
351facf4a8dSllai1 	} else {
352facf4a8dSllai1 		dv->sdev_flags &= ~SDEV_GLOBAL;
353facf4a8dSllai1 		dv->sdev_origin = NULL; /* set later */
354facf4a8dSllai1 		bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
355facf4a8dSllai1 		dv->sdev_ldir_gen = 0;
356facf4a8dSllai1 		dv->sdev_devtree_gen = 0;
357facf4a8dSllai1 	}
358facf4a8dSllai1 
359facf4a8dSllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
360facf4a8dSllai1 	sdev_set_nodestate(dv, SDEV_INIT);
361facf4a8dSllai1 	rw_exit(&dv->sdev_contents);
362facf4a8dSllai1 	*newdv = dv;
363facf4a8dSllai1 
364facf4a8dSllai1 	return (0);
365facf4a8dSllai1 }
366facf4a8dSllai1 
367facf4a8dSllai1 /*
368*9e5aa9d8SRobert Mustacchi  * Transition a sdev_node into SDEV_READY state. If this fails, it is up to the
369*9e5aa9d8SRobert Mustacchi  * caller to transition the node to the SDEV_ZOMBIE state.
370facf4a8dSllai1  */
371facf4a8dSllai1 int
372facf4a8dSllai1 sdev_nodeready(struct sdev_node *dv, struct vattr *vap, struct vnode *avp,
373facf4a8dSllai1     void *args, struct cred *cred)
374facf4a8dSllai1 {
375facf4a8dSllai1 	int error = 0;
376facf4a8dSllai1 	struct vnode *vp = SDEVTOV(dv);
377facf4a8dSllai1 	vtype_t type;
378facf4a8dSllai1 
379facf4a8dSllai1 	ASSERT(dv && (dv->sdev_state != SDEV_READY) && vap);
380facf4a8dSllai1 
381facf4a8dSllai1 	type = vap->va_type;
382facf4a8dSllai1 	vp->v_type = type;
383facf4a8dSllai1 	vp->v_rdev = vap->va_rdev;
384facf4a8dSllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
385facf4a8dSllai1 	if (type == VDIR) {
386facf4a8dSllai1 		dv->sdev_nlink = 2;
387facf4a8dSllai1 		dv->sdev_flags &= ~SDEV_PERSIST;
388facf4a8dSllai1 		dv->sdev_flags &= ~SDEV_DYNAMIC;
389facf4a8dSllai1 		vn_setops(vp, sdev_get_vop(dv)); /* from internal vtab */
390facf4a8dSllai1 		ASSERT(dv->sdev_dotdot);
391facf4a8dSllai1 		ASSERT(SDEVTOV(dv->sdev_dotdot)->v_type == VDIR);
392facf4a8dSllai1 		vp->v_rdev = SDEVTOV(dv->sdev_dotdot)->v_rdev;
393aac43a5fSjg 		avl_create(&dv->sdev_entries,
394aac43a5fSjg 		    (int (*)(const void *, const void *))sdev_compare_nodes,
395aac43a5fSjg 		    sizeof (struct sdev_node),
396aac43a5fSjg 		    offsetof(struct sdev_node, sdev_avllink));
397facf4a8dSllai1 	} else if (type == VLNK) {
398facf4a8dSllai1 		ASSERT(args);
399facf4a8dSllai1 		dv->sdev_nlink = 1;
400facf4a8dSllai1 		dv->sdev_symlink = i_ddi_strdup((char *)args, KM_SLEEP);
401facf4a8dSllai1 	} else {
402facf4a8dSllai1 		dv->sdev_nlink = 1;
403facf4a8dSllai1 	}
404facf4a8dSllai1 
405facf4a8dSllai1 	if (!(SDEV_IS_GLOBAL(dv))) {
406facf4a8dSllai1 		dv->sdev_origin = (struct sdev_node *)args;
407facf4a8dSllai1 		dv->sdev_flags &= ~SDEV_PERSIST;
408facf4a8dSllai1 	}
409facf4a8dSllai1 
410facf4a8dSllai1 	/*
411facf4a8dSllai1 	 * shadow node is created here OR
412facf4a8dSllai1 	 * if failed (indicated by dv->sdev_attrvp == NULL),
413facf4a8dSllai1 	 * created later in sdev_setattr
414facf4a8dSllai1 	 */
415facf4a8dSllai1 	if (avp) {
416facf4a8dSllai1 		dv->sdev_attrvp = avp;
417facf4a8dSllai1 	} else {
418bb5fffbeSJerry Gilliam 		if (dv->sdev_attr == NULL) {
419bb5fffbeSJerry Gilliam 			sdev_attr_alloc(dv, vap);
420bb5fffbeSJerry Gilliam 		} else {
421bb5fffbeSJerry Gilliam 			sdev_attr_update(dv, vap);
422bb5fffbeSJerry Gilliam 		}
423facf4a8dSllai1 
424681d9761SEric Taylor 		if ((dv->sdev_attrvp == NULL) && SDEV_IS_PERSIST(dv))
425facf4a8dSllai1 			error = sdev_shadow_node(dv, cred);
426facf4a8dSllai1 	}
427facf4a8dSllai1 
428b7beec95Sjg 	if (error == 0) {
429facf4a8dSllai1 		/* transition to READY state */
430facf4a8dSllai1 		sdev_set_nodestate(dv, SDEV_READY);
431facf4a8dSllai1 		sdev_nc_node_exists(dv);
432b7beec95Sjg 	}
433facf4a8dSllai1 	rw_exit(&dv->sdev_contents);
434facf4a8dSllai1 	return (error);
435facf4a8dSllai1 }
436facf4a8dSllai1 
437facf4a8dSllai1 /*
438facf4a8dSllai1  * Build the VROOT sdev_node.
439facf4a8dSllai1  */
440facf4a8dSllai1 /*ARGSUSED*/
441facf4a8dSllai1 struct sdev_node *
442facf4a8dSllai1 sdev_mkroot(struct vfs *vfsp, dev_t devdev, struct vnode *mvp,
443facf4a8dSllai1     struct vnode *avp, struct cred *cred)
444facf4a8dSllai1 {
445facf4a8dSllai1 	struct sdev_node *dv;
446facf4a8dSllai1 	struct vnode *vp;
447facf4a8dSllai1 	char devdir[] = "/dev";
448facf4a8dSllai1 
449facf4a8dSllai1 	ASSERT(sdev_node_cache != NULL);
450facf4a8dSllai1 	ASSERT(avp);
451facf4a8dSllai1 	dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP);
452facf4a8dSllai1 	vp = SDEVTOV(dv);
453facf4a8dSllai1 	vn_reinit(vp);
454facf4a8dSllai1 	vp->v_flag |= VROOT;
455facf4a8dSllai1 	vp->v_vfsp = vfsp;
456facf4a8dSllai1 	vp->v_type = VDIR;
457facf4a8dSllai1 	vp->v_rdev = devdev;
458facf4a8dSllai1 	vn_setops(vp, sdev_vnodeops); /* apply the default vnodeops at /dev */
459facf4a8dSllai1 	vn_exists(vp);
460facf4a8dSllai1 
461facf4a8dSllai1 	if (vfsp->vfs_mntpt)
462facf4a8dSllai1 		dv->sdev_name = i_ddi_strdup(
463facf4a8dSllai1 		    (char *)refstr_value(vfsp->vfs_mntpt), KM_SLEEP);
464facf4a8dSllai1 	else
465facf4a8dSllai1 		/* vfs_mountdev1 set mount point later */
466facf4a8dSllai1 		dv->sdev_name = i_ddi_strdup("/dev", KM_SLEEP);
467facf4a8dSllai1 	dv->sdev_namelen = strlen(dv->sdev_name); /* '\0' not included */
468facf4a8dSllai1 	dv->sdev_path = i_ddi_strdup(devdir, KM_SLEEP);
469facf4a8dSllai1 	dv->sdev_ino = SDEV_ROOTINO;
470facf4a8dSllai1 	dv->sdev_nlink = 2;		/* name + . (no sdev_insert) */
471facf4a8dSllai1 	dv->sdev_dotdot = dv;		/* .. == self */
472facf4a8dSllai1 	dv->sdev_attrvp = avp;
473facf4a8dSllai1 	dv->sdev_attr = NULL;
474facf4a8dSllai1 	mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL);
475facf4a8dSllai1 	cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL);
476facf4a8dSllai1 	if (strcmp(dv->sdev_name, "/dev") == 0) {
477facf4a8dSllai1 		dv->sdev_flags = SDEV_BUILD|SDEV_GLOBAL|SDEV_PERSIST;
478facf4a8dSllai1 		bzero(&dv->sdev_handle, sizeof (dv->sdev_handle));
479facf4a8dSllai1 		dv->sdev_gdir_gen = 0;
480facf4a8dSllai1 	} else {
481facf4a8dSllai1 		dv->sdev_flags = SDEV_BUILD;
482facf4a8dSllai1 		dv->sdev_flags &= ~SDEV_PERSIST;
483facf4a8dSllai1 		bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
484facf4a8dSllai1 		dv->sdev_ldir_gen = 0;
485facf4a8dSllai1 		dv->sdev_devtree_gen = 0;
486facf4a8dSllai1 	}
487facf4a8dSllai1 
488aac43a5fSjg 	avl_create(&dv->sdev_entries,
489aac43a5fSjg 	    (int (*)(const void *, const void *))sdev_compare_nodes,
490aac43a5fSjg 	    sizeof (struct sdev_node),
491aac43a5fSjg 	    offsetof(struct sdev_node, sdev_avllink));
492aac43a5fSjg 
493facf4a8dSllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
494facf4a8dSllai1 	sdev_set_nodestate(dv, SDEV_READY);
495facf4a8dSllai1 	rw_exit(&dv->sdev_contents);
496facf4a8dSllai1 	sdev_nc_node_exists(dv);
497facf4a8dSllai1 	return (dv);
498facf4a8dSllai1 }
499facf4a8dSllai1 
500facf4a8dSllai1 /* directory dependent vop table */
501facf4a8dSllai1 struct sdev_vop_table {
502facf4a8dSllai1 	char *vt_name;				/* subdirectory name */
503facf4a8dSllai1 	const fs_operation_def_t *vt_service;	/* vnodeops table */
504facf4a8dSllai1 	struct vnodeops *vt_vops;		/* constructed vop */
505facf4a8dSllai1 	struct vnodeops **vt_global_vops;	/* global container for vop */
506facf4a8dSllai1 	int (*vt_vtor)(struct sdev_node *);	/* validate sdev_node */
507facf4a8dSllai1 	int vt_flags;
508facf4a8dSllai1 };
509facf4a8dSllai1 
510facf4a8dSllai1 /*
511facf4a8dSllai1  * A nice improvement would be to provide a plug-in mechanism
512facf4a8dSllai1  * for this table instead of a const table.
513facf4a8dSllai1  */
514facf4a8dSllai1 static struct sdev_vop_table vtab[] =
515facf4a8dSllai1 {
516facf4a8dSllai1 	{ "pts", devpts_vnodeops_tbl, NULL, &devpts_vnodeops, devpts_validate,
517facf4a8dSllai1 	SDEV_DYNAMIC | SDEV_VTOR },
518facf4a8dSllai1 
519aecfc01dSrui zang - Sun Microsystems - Beijing China 	{ "vt", devvt_vnodeops_tbl, NULL, &devvt_vnodeops, devvt_validate,
520aecfc01dSrui zang - Sun Microsystems - Beijing China 	SDEV_DYNAMIC | SDEV_VTOR },
521aecfc01dSrui zang - Sun Microsystems - Beijing China 
522681d9761SEric Taylor 	{ "zvol", devzvol_vnodeops_tbl, NULL, &devzvol_vnodeops,
523dd9c3b29SJerry Jelinek 	devzvol_validate, SDEV_ZONED | SDEV_DYNAMIC | SDEV_VTOR | SDEV_SUBDIR },
524681d9761SEric Taylor 
525facf4a8dSllai1 	{ "zcons", NULL, NULL, NULL, NULL, SDEV_NO_NCACHE },
526facf4a8dSllai1 
527d62bc4baSyz147064 	{ "net", devnet_vnodeops_tbl, NULL, &devnet_vnodeops, devnet_validate,
528d62bc4baSyz147064 	SDEV_DYNAMIC | SDEV_VTOR },
529d62bc4baSyz147064 
530b127ac41SPhilip Kirk 	{ "ipnet", devipnet_vnodeops_tbl, NULL, &devipnet_vnodeops,
531b127ac41SPhilip Kirk 	devipnet_validate, SDEV_DYNAMIC | SDEV_VTOR | SDEV_NO_NCACHE },
532b127ac41SPhilip Kirk 
53367323fc4SJohn Levon 	/*
53467323fc4SJohn Levon 	 * SDEV_DYNAMIC: prevent calling out to devfsadm, since only the
53567323fc4SJohn Levon 	 * lofi driver controls child nodes.
53667323fc4SJohn Levon 	 *
53767323fc4SJohn Levon 	 * SDEV_PERSIST: ensure devfsadm knows to clean up any persisted
53867323fc4SJohn Levon 	 * stale nodes (e.g. from devfsadm -R).
53967323fc4SJohn Levon 	 *
54067323fc4SJohn Levon 	 * In addition, devfsadm knows not to attempt a rmdir: a zone
54167323fc4SJohn Levon 	 * may hold a reference, which would zombify the node,
54267323fc4SJohn Levon 	 * preventing a mkdir.
54367323fc4SJohn Levon 	 */
54467323fc4SJohn Levon 
54567323fc4SJohn Levon 	{ "lofi", NULL, NULL, NULL, NULL,
54667323fc4SJohn Levon 	    SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST },
54767323fc4SJohn Levon 	{ "rlofi", NULL, NULL, NULL, NULL,
54867323fc4SJohn Levon 	    SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST },
5490fbb751dSJohn Levon 
550facf4a8dSllai1 	{ NULL, NULL, NULL, NULL, NULL, 0}
551facf4a8dSllai1 };
552facf4a8dSllai1 
553*9e5aa9d8SRobert Mustacchi /*
554*9e5aa9d8SRobert Mustacchi  * We need to match off of the sdev_path, not the sdev_name. We are only allowed
555*9e5aa9d8SRobert Mustacchi  * to exist directly under /dev.
556*9e5aa9d8SRobert Mustacchi  */
557681d9761SEric Taylor struct sdev_vop_table *
558681d9761SEric Taylor sdev_match(struct sdev_node *dv)
559681d9761SEric Taylor {
560681d9761SEric Taylor 	int vlen;
561681d9761SEric Taylor 	int i;
562*9e5aa9d8SRobert Mustacchi 	const char *path;
563*9e5aa9d8SRobert Mustacchi 
564*9e5aa9d8SRobert Mustacchi 	if (strlen(dv->sdev_path) <= 5)
565*9e5aa9d8SRobert Mustacchi 		return (NULL);
566*9e5aa9d8SRobert Mustacchi 
567*9e5aa9d8SRobert Mustacchi 	if (strncmp(dv->sdev_path, "/dev/", 5) != 0)
568*9e5aa9d8SRobert Mustacchi 		return (NULL);
569*9e5aa9d8SRobert Mustacchi 	path = dv->sdev_path + 5;
570681d9761SEric Taylor 
571681d9761SEric Taylor 	for (i = 0; vtab[i].vt_name; i++) {
572*9e5aa9d8SRobert Mustacchi 		if (strcmp(vtab[i].vt_name, path) == 0)
573681d9761SEric Taylor 			return (&vtab[i]);
574681d9761SEric Taylor 		if (vtab[i].vt_flags & SDEV_SUBDIR) {
575681d9761SEric Taylor 			vlen = strlen(vtab[i].vt_name);
576*9e5aa9d8SRobert Mustacchi 			if ((strncmp(vtab[i].vt_name, path,
577*9e5aa9d8SRobert Mustacchi 			    vlen - 1) == 0) && path[vlen] == '/')
578681d9761SEric Taylor 				return (&vtab[i]);
579681d9761SEric Taylor 		}
580681d9761SEric Taylor 
581681d9761SEric Taylor 	}
582681d9761SEric Taylor 	return (NULL);
583681d9761SEric Taylor }
584facf4a8dSllai1 
585facf4a8dSllai1 /*
586facf4a8dSllai1  *  sets a directory's vnodeops if the directory is in the vtab;
587facf4a8dSllai1  */
588facf4a8dSllai1 static struct vnodeops *
589facf4a8dSllai1 sdev_get_vop(struct sdev_node *dv)
590facf4a8dSllai1 {
591681d9761SEric Taylor 	struct sdev_vop_table *vtp;
592facf4a8dSllai1 	char *path;
593facf4a8dSllai1 
594facf4a8dSllai1 	path = dv->sdev_path;
595facf4a8dSllai1 	ASSERT(path);
596facf4a8dSllai1 
597facf4a8dSllai1 	/* gets the relative path to /dev/ */
598facf4a8dSllai1 	path += 5;
599facf4a8dSllai1 
600681d9761SEric Taylor 	/* gets the vtab entry it matches */
601681d9761SEric Taylor 	if ((vtp = sdev_match(dv)) != NULL) {
602681d9761SEric Taylor 		dv->sdev_flags |= vtp->vt_flags;
603*9e5aa9d8SRobert Mustacchi 		if (SDEV_IS_PERSIST(dv->sdev_dotdot) &&
604*9e5aa9d8SRobert Mustacchi 		    (SDEV_IS_PERSIST(dv) || !SDEV_IS_DYNAMIC(dv)))
605*9e5aa9d8SRobert Mustacchi 			dv->sdev_flags |= SDEV_PERSIST;
606facf4a8dSllai1 
607681d9761SEric Taylor 		if (vtp->vt_vops) {
608681d9761SEric Taylor 			if (vtp->vt_global_vops)
609681d9761SEric Taylor 				*(vtp->vt_global_vops) = vtp->vt_vops;
610*9e5aa9d8SRobert Mustacchi 
611681d9761SEric Taylor 			return (vtp->vt_vops);
612facf4a8dSllai1 		}
613facf4a8dSllai1 
614681d9761SEric Taylor 		if (vtp->vt_service) {
615facf4a8dSllai1 			fs_operation_def_t *templ;
616681d9761SEric Taylor 			templ = sdev_merge_vtab(vtp->vt_service);
617681d9761SEric Taylor 			if (vn_make_ops(vtp->vt_name,
618facf4a8dSllai1 			    (const fs_operation_def_t *)templ,
619681d9761SEric Taylor 			    &vtp->vt_vops) != 0) {
620facf4a8dSllai1 				cmn_err(CE_PANIC, "%s: malformed vnode ops\n",
621681d9761SEric Taylor 				    vtp->vt_name);
622facf4a8dSllai1 				/*NOTREACHED*/
623facf4a8dSllai1 			}
624681d9761SEric Taylor 			if (vtp->vt_global_vops) {
625681d9761SEric Taylor 				*(vtp->vt_global_vops) = vtp->vt_vops;
626facf4a8dSllai1 			}
627facf4a8dSllai1 			sdev_free_vtab(templ);
628*9e5aa9d8SRobert Mustacchi 
629681d9761SEric Taylor 			return (vtp->vt_vops);
630facf4a8dSllai1 		}
631*9e5aa9d8SRobert Mustacchi 
632facf4a8dSllai1 		return (sdev_vnodeops);
633facf4a8dSllai1 	}
634facf4a8dSllai1 
635facf4a8dSllai1 	/* child inherits the persistence of the parent */
636facf4a8dSllai1 	if (SDEV_IS_PERSIST(dv->sdev_dotdot))
637facf4a8dSllai1 		dv->sdev_flags |= SDEV_PERSIST;
638facf4a8dSllai1 
639facf4a8dSllai1 	return (sdev_vnodeops);
640facf4a8dSllai1 }
641facf4a8dSllai1 
642facf4a8dSllai1 static void
643681d9761SEric Taylor sdev_set_no_negcache(struct sdev_node *dv)
644facf4a8dSllai1 {
645facf4a8dSllai1 	int i;
646facf4a8dSllai1 	char *path;
647facf4a8dSllai1 
648facf4a8dSllai1 	ASSERT(dv->sdev_path);
649facf4a8dSllai1 	path = dv->sdev_path + strlen("/dev/");
650facf4a8dSllai1 
651facf4a8dSllai1 	for (i = 0; vtab[i].vt_name; i++) {
652facf4a8dSllai1 		if (strcmp(vtab[i].vt_name, path) == 0) {
653facf4a8dSllai1 			if (vtab[i].vt_flags & SDEV_NO_NCACHE)
654facf4a8dSllai1 				dv->sdev_flags |= SDEV_NO_NCACHE;
655facf4a8dSllai1 			break;
656facf4a8dSllai1 		}
657facf4a8dSllai1 	}
658facf4a8dSllai1 }
659facf4a8dSllai1 
660facf4a8dSllai1 void *
661facf4a8dSllai1 sdev_get_vtor(struct sdev_node *dv)
662facf4a8dSllai1 {
663681d9761SEric Taylor 	struct sdev_vop_table *vtp;
664facf4a8dSllai1 
665681d9761SEric Taylor 	vtp = sdev_match(dv);
666681d9761SEric Taylor 	if (vtp)
667681d9761SEric Taylor 		return ((void *)vtp->vt_vtor);
668681d9761SEric Taylor 	else
669facf4a8dSllai1 		return (NULL);
670facf4a8dSllai1 }
671facf4a8dSllai1 
672facf4a8dSllai1 /*
673facf4a8dSllai1  * Build the base root inode
674facf4a8dSllai1  */
675facf4a8dSllai1 ino_t
676facf4a8dSllai1 sdev_mkino(struct sdev_node *dv)
677facf4a8dSllai1 {
678facf4a8dSllai1 	ino_t	ino;
679facf4a8dSllai1 
680facf4a8dSllai1 	/*
681facf4a8dSllai1 	 * for now, follow the lead of tmpfs here
682facf4a8dSllai1 	 * need to someday understand the requirements here
683facf4a8dSllai1 	 */
684facf4a8dSllai1 	ino = (ino_t)(uint32_t)((uintptr_t)dv >> 3);
685facf4a8dSllai1 	ino += SDEV_ROOTINO + 1;
686facf4a8dSllai1 
687facf4a8dSllai1 	return (ino);
688facf4a8dSllai1 }
689facf4a8dSllai1 
690681d9761SEric Taylor int
691facf4a8dSllai1 sdev_getlink(struct vnode *linkvp, char **link)
692facf4a8dSllai1 {
693facf4a8dSllai1 	int err;
694facf4a8dSllai1 	char *buf;
695facf4a8dSllai1 	struct uio uio = {0};
696facf4a8dSllai1 	struct iovec iov = {0};
697facf4a8dSllai1 
698facf4a8dSllai1 	if (linkvp == NULL)
699facf4a8dSllai1 		return (ENOENT);
700facf4a8dSllai1 	ASSERT(linkvp->v_type == VLNK);
701facf4a8dSllai1 
702facf4a8dSllai1 	buf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
703facf4a8dSllai1 	iov.iov_base = buf;
704facf4a8dSllai1 	iov.iov_len = MAXPATHLEN;
705facf4a8dSllai1 	uio.uio_iov = &iov;
706facf4a8dSllai1 	uio.uio_iovcnt = 1;
707facf4a8dSllai1 	uio.uio_resid = MAXPATHLEN;
708facf4a8dSllai1 	uio.uio_segflg = UIO_SYSSPACE;
709facf4a8dSllai1 	uio.uio_llimit = MAXOFFSET_T;
710facf4a8dSllai1 
711da6c28aaSamw 	err = VOP_READLINK(linkvp, &uio, kcred, NULL);
712facf4a8dSllai1 	if (err) {
713facf4a8dSllai1 		cmn_err(CE_WARN, "readlink %s failed in dev\n", buf);
714facf4a8dSllai1 		kmem_free(buf, MAXPATHLEN);
715facf4a8dSllai1 		return (ENOENT);
716facf4a8dSllai1 	}
717facf4a8dSllai1 
718facf4a8dSllai1 	/* mission complete */
719facf4a8dSllai1 	*link = i_ddi_strdup(buf, KM_SLEEP);
720facf4a8dSllai1 	kmem_free(buf, MAXPATHLEN);
721facf4a8dSllai1 	return (0);
722facf4a8dSllai1 }
723facf4a8dSllai1 
724facf4a8dSllai1 /*
725facf4a8dSllai1  * A convenient wrapper to get the devfs node vnode for a device
726facf4a8dSllai1  * minor functionality: readlink() of a /dev symlink
727facf4a8dSllai1  * Place the link into dv->sdev_symlink
728facf4a8dSllai1  */
729facf4a8dSllai1 static int
730facf4a8dSllai1 sdev_follow_link(struct sdev_node *dv)
731facf4a8dSllai1 {
732facf4a8dSllai1 	int err;
733facf4a8dSllai1 	struct vnode *linkvp;
734facf4a8dSllai1 	char *link = NULL;
735facf4a8dSllai1 
736facf4a8dSllai1 	linkvp = SDEVTOV(dv);
737facf4a8dSllai1 	if (linkvp == NULL)
738facf4a8dSllai1 		return (ENOENT);
739facf4a8dSllai1 	ASSERT(linkvp->v_type == VLNK);
740facf4a8dSllai1 	err = sdev_getlink(linkvp, &link);
741facf4a8dSllai1 	if (err) {
742facf4a8dSllai1 		dv->sdev_symlink = NULL;
743facf4a8dSllai1 		return (ENOENT);
744facf4a8dSllai1 	}
745facf4a8dSllai1 
746facf4a8dSllai1 	ASSERT(link != NULL);
747facf4a8dSllai1 	dv->sdev_symlink = link;
748facf4a8dSllai1 	return (0);
749facf4a8dSllai1 }
750facf4a8dSllai1 
751facf4a8dSllai1 static int
752facf4a8dSllai1 sdev_node_check(struct sdev_node *dv, struct vattr *nvap, void *nargs)
753facf4a8dSllai1 {
754facf4a8dSllai1 	vtype_t otype = SDEVTOV(dv)->v_type;
755facf4a8dSllai1 
756facf4a8dSllai1 	/*
757facf4a8dSllai1 	 * existing sdev_node has a different type.
758facf4a8dSllai1 	 */
759facf4a8dSllai1 	if (otype != nvap->va_type) {
760facf4a8dSllai1 		sdcmn_err9(("sdev_node_check: existing node "
761facf4a8dSllai1 		    "  %s type %d does not match new node type %d\n",
762facf4a8dSllai1 		    dv->sdev_name, otype, nvap->va_type));
763facf4a8dSllai1 		return (EEXIST);
764facf4a8dSllai1 	}
765facf4a8dSllai1 
766facf4a8dSllai1 	/*
767facf4a8dSllai1 	 * For a symlink, the target should be the same.
768facf4a8dSllai1 	 */
769facf4a8dSllai1 	if (otype == VLNK) {
770facf4a8dSllai1 		ASSERT(nargs != NULL);
771facf4a8dSllai1 		ASSERT(dv->sdev_symlink != NULL);
772facf4a8dSllai1 		if (strcmp(dv->sdev_symlink, (char *)nargs) != 0) {
773facf4a8dSllai1 			sdcmn_err9(("sdev_node_check: existing node "
774facf4a8dSllai1 			    " %s has different symlink %s as new node "
775facf4a8dSllai1 			    " %s\n", dv->sdev_name, dv->sdev_symlink,
776facf4a8dSllai1 			    (char *)nargs));
777facf4a8dSllai1 			return (EEXIST);
778facf4a8dSllai1 		}
779facf4a8dSllai1 	}
780facf4a8dSllai1 
781facf4a8dSllai1 	return (0);
782facf4a8dSllai1 }
783facf4a8dSllai1 
784facf4a8dSllai1 /*
785facf4a8dSllai1  * sdev_mknode - a wrapper for sdev_nodeinit(), sdev_nodeready()
786facf4a8dSllai1  *
787facf4a8dSllai1  * arguments:
788facf4a8dSllai1  *	- ddv (parent)
789facf4a8dSllai1  *	- nm (child name)
790facf4a8dSllai1  *	- newdv (sdev_node for nm is returned here)
791facf4a8dSllai1  *	- vap (vattr for the node to be created, va_type should be set.
792b7beec95Sjg  *	- avp (attribute vnode)
793facf4a8dSllai1  *	  the defaults should be used if unknown)
794facf4a8dSllai1  *	- cred
795facf4a8dSllai1  *	- args
796facf4a8dSllai1  *	    . tnm (for VLNK)
797facf4a8dSllai1  *	    . global sdev_node (for !SDEV_GLOBAL)
798facf4a8dSllai1  * 	- state: SDEV_INIT, SDEV_READY
799facf4a8dSllai1  *
800facf4a8dSllai1  * only ddv, nm, newddv, vap, cred are required for sdev_mknode(SDEV_INIT)
801facf4a8dSllai1  *
802facf4a8dSllai1  * NOTE:  directory contents writers lock needs to be held before
803facf4a8dSllai1  *	  calling this routine.
804facf4a8dSllai1  */
805facf4a8dSllai1 int
806facf4a8dSllai1 sdev_mknode(struct sdev_node *ddv, char *nm, struct sdev_node **newdv,
807facf4a8dSllai1     struct vattr *vap, struct vnode *avp, void *args, struct cred *cred,
808facf4a8dSllai1     sdev_node_state_t state)
809facf4a8dSllai1 {
810facf4a8dSllai1 	int error = 0;
811facf4a8dSllai1 	sdev_node_state_t node_state;
812facf4a8dSllai1 	struct sdev_node *dv = NULL;
813facf4a8dSllai1 
814facf4a8dSllai1 	ASSERT(state != SDEV_ZOMBIE);
815facf4a8dSllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
816facf4a8dSllai1 
817facf4a8dSllai1 	if (*newdv) {
818facf4a8dSllai1 		dv = *newdv;
819facf4a8dSllai1 	} else {
820facf4a8dSllai1 		/* allocate and initialize a sdev_node */
821facf4a8dSllai1 		if (ddv->sdev_state == SDEV_ZOMBIE) {
822facf4a8dSllai1 			sdcmn_err9(("sdev_mknode: parent %s ZOMBIEd\n",
823facf4a8dSllai1 			    ddv->sdev_path));
824facf4a8dSllai1 			return (ENOENT);
825facf4a8dSllai1 		}
826facf4a8dSllai1 
827facf4a8dSllai1 		error = sdev_nodeinit(ddv, nm, &dv, vap);
828facf4a8dSllai1 		if (error != 0) {
829facf4a8dSllai1 			sdcmn_err9(("sdev_mknode: error %d,"
830facf4a8dSllai1 			    " name %s can not be initialized\n",
831facf4a8dSllai1 			    error, nm));
832b7beec95Sjg 			return (error);
833facf4a8dSllai1 		}
834facf4a8dSllai1 		ASSERT(dv);
835facf4a8dSllai1 
836facf4a8dSllai1 		/* insert into the directory cache */
837*9e5aa9d8SRobert Mustacchi 		sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_ADD);
838facf4a8dSllai1 	}
839facf4a8dSllai1 
840facf4a8dSllai1 	ASSERT(dv);
841facf4a8dSllai1 	node_state = dv->sdev_state;
842facf4a8dSllai1 	ASSERT(node_state != SDEV_ZOMBIE);
843facf4a8dSllai1 
844facf4a8dSllai1 	if (state == SDEV_READY) {
845facf4a8dSllai1 		switch (node_state) {
846facf4a8dSllai1 		case SDEV_INIT:
847facf4a8dSllai1 			error = sdev_nodeready(dv, vap, avp, args, cred);
848facf4a8dSllai1 			if (error) {
849facf4a8dSllai1 				sdcmn_err9(("sdev_mknode: node %s can NOT"
850facf4a8dSllai1 				    " be transitioned into READY state, "
851facf4a8dSllai1 				    "error %d\n", nm, error));
852facf4a8dSllai1 			}
853facf4a8dSllai1 			break;
854facf4a8dSllai1 		case SDEV_READY:
855facf4a8dSllai1 			/*
856facf4a8dSllai1 			 * Do some sanity checking to make sure
857facf4a8dSllai1 			 * the existing sdev_node is what has been
858facf4a8dSllai1 			 * asked for.
859facf4a8dSllai1 			 */
860facf4a8dSllai1 			error = sdev_node_check(dv, vap, args);
861facf4a8dSllai1 			break;
862facf4a8dSllai1 		default:
863facf4a8dSllai1 			break;
864facf4a8dSllai1 		}
865facf4a8dSllai1 	}
866facf4a8dSllai1 
867facf4a8dSllai1 	if (!error) {
868facf4a8dSllai1 		*newdv = dv;
869facf4a8dSllai1 		ASSERT((*newdv)->sdev_state != SDEV_ZOMBIE);
870facf4a8dSllai1 	} else {
871*9e5aa9d8SRobert Mustacchi 		sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_DELETE);
872*9e5aa9d8SRobert Mustacchi 		/*
873*9e5aa9d8SRobert Mustacchi 		 * We created this node, it wasn't passed into us. Therefore it
874*9e5aa9d8SRobert Mustacchi 		 * is up to us to delete it.
875*9e5aa9d8SRobert Mustacchi 		 */
876*9e5aa9d8SRobert Mustacchi 		if (*newdv == NULL)
877facf4a8dSllai1 			SDEV_SIMPLE_RELE(dv);
878facf4a8dSllai1 		*newdv = NULL;
879facf4a8dSllai1 	}
880facf4a8dSllai1 
881facf4a8dSllai1 	return (error);
882facf4a8dSllai1 }
883facf4a8dSllai1 
884facf4a8dSllai1 /*
885b7beec95Sjg  * convenient wrapper to change vp's ATIME, CTIME and MTIME
886facf4a8dSllai1  */
887facf4a8dSllai1 void
888facf4a8dSllai1 sdev_update_timestamps(struct vnode *vp, cred_t *cred, uint_t mask)
889facf4a8dSllai1 {
890facf4a8dSllai1 	struct vattr attr;
891facf4a8dSllai1 	timestruc_t now;
892facf4a8dSllai1 	int err;
893facf4a8dSllai1 
894facf4a8dSllai1 	ASSERT(vp);
895facf4a8dSllai1 	gethrestime(&now);
896facf4a8dSllai1 	if (mask & AT_CTIME)
897facf4a8dSllai1 		attr.va_ctime = now;
898facf4a8dSllai1 	if (mask & AT_MTIME)
899facf4a8dSllai1 		attr.va_mtime = now;
900facf4a8dSllai1 	if (mask & AT_ATIME)
901facf4a8dSllai1 		attr.va_atime = now;
902facf4a8dSllai1 
903facf4a8dSllai1 	attr.va_mask = (mask & AT_TIMES);
904facf4a8dSllai1 	err = VOP_SETATTR(vp, &attr, 0, cred, NULL);
905facf4a8dSllai1 	if (err && (err != EROFS)) {
906facf4a8dSllai1 		sdcmn_err(("update timestamps error %d\n", err));
907facf4a8dSllai1 	}
908facf4a8dSllai1 }
909facf4a8dSllai1 
910facf4a8dSllai1 /*
911facf4a8dSllai1  * the backing store vnode is released here
912facf4a8dSllai1  */
913facf4a8dSllai1 /*ARGSUSED1*/
914facf4a8dSllai1 void
915facf4a8dSllai1 sdev_nodedestroy(struct sdev_node *dv, uint_t flags)
916facf4a8dSllai1 {
917facf4a8dSllai1 	/* no references */
918facf4a8dSllai1 	ASSERT(dv->sdev_nlink == 0);
919facf4a8dSllai1 
920facf4a8dSllai1 	if (dv->sdev_attrvp != NULLVP) {
921facf4a8dSllai1 		VN_RELE(dv->sdev_attrvp);
922facf4a8dSllai1 		/*
923facf4a8dSllai1 		 * reset the attrvp so that no more
924facf4a8dSllai1 		 * references can be made on this already
925facf4a8dSllai1 		 * vn_rele() vnode
926facf4a8dSllai1 		 */
927facf4a8dSllai1 		dv->sdev_attrvp = NULLVP;
928facf4a8dSllai1 	}
929facf4a8dSllai1 
930facf4a8dSllai1 	if (dv->sdev_attr != NULL) {
931facf4a8dSllai1 		kmem_free(dv->sdev_attr, sizeof (struct vattr));
932facf4a8dSllai1 		dv->sdev_attr = NULL;
933facf4a8dSllai1 	}
934facf4a8dSllai1 
935facf4a8dSllai1 	if (dv->sdev_name != NULL) {
936facf4a8dSllai1 		kmem_free(dv->sdev_name, dv->sdev_namelen + 1);
937facf4a8dSllai1 		dv->sdev_name = NULL;
938facf4a8dSllai1 	}
939facf4a8dSllai1 
940facf4a8dSllai1 	if (dv->sdev_symlink != NULL) {
941facf4a8dSllai1 		kmem_free(dv->sdev_symlink, strlen(dv->sdev_symlink) + 1);
942facf4a8dSllai1 		dv->sdev_symlink = NULL;
943facf4a8dSllai1 	}
944facf4a8dSllai1 
945facf4a8dSllai1 	if (dv->sdev_path) {
946facf4a8dSllai1 		kmem_free(dv->sdev_path, strlen(dv->sdev_path) + 1);
947facf4a8dSllai1 		dv->sdev_path = NULL;
948facf4a8dSllai1 	}
949facf4a8dSllai1 
950facf4a8dSllai1 	if (!SDEV_IS_GLOBAL(dv))
951facf4a8dSllai1 		sdev_prof_free(dv);
952facf4a8dSllai1 
953aac43a5fSjg 	if (SDEVTOV(dv)->v_type == VDIR) {
954aac43a5fSjg 		ASSERT(SDEV_FIRST_ENTRY(dv) == NULL);
955aac43a5fSjg 		avl_destroy(&dv->sdev_entries);
956aac43a5fSjg 	}
957aac43a5fSjg 
958facf4a8dSllai1 	mutex_destroy(&dv->sdev_lookup_lock);
959facf4a8dSllai1 	cv_destroy(&dv->sdev_lookup_cv);
960facf4a8dSllai1 
961facf4a8dSllai1 	/* return node to initial state as per constructor */
962facf4a8dSllai1 	(void) memset((void *)&dv->sdev_instance_data, 0,
963facf4a8dSllai1 	    sizeof (dv->sdev_instance_data));
964facf4a8dSllai1 	vn_invalid(SDEVTOV(dv));
965facf4a8dSllai1 	kmem_cache_free(sdev_node_cache, dv);
966facf4a8dSllai1 }
967facf4a8dSllai1 
968facf4a8dSllai1 /*
969facf4a8dSllai1  * DIRECTORY CACHE lookup
970facf4a8dSllai1  */
971facf4a8dSllai1 struct sdev_node *
972facf4a8dSllai1 sdev_findbyname(struct sdev_node *ddv, char *nm)
973facf4a8dSllai1 {
974facf4a8dSllai1 	struct sdev_node *dv;
975aac43a5fSjg 	struct sdev_node dvtmp;
976aac43a5fSjg 	avl_index_t	where;
977facf4a8dSllai1 
978facf4a8dSllai1 	ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
979facf4a8dSllai1 
980aac43a5fSjg 	dvtmp.sdev_name = nm;
981aac43a5fSjg 	dv = avl_find(&ddv->sdev_entries, &dvtmp, &where);
982aac43a5fSjg 	if (dv) {
983aac43a5fSjg 		ASSERT(dv->sdev_dotdot == ddv);
984aac43a5fSjg 		ASSERT(strcmp(dv->sdev_name, nm) == 0);
985*9e5aa9d8SRobert Mustacchi 		ASSERT(dv->sdev_state != SDEV_ZOMBIE);
986facf4a8dSllai1 		SDEV_HOLD(dv);
987facf4a8dSllai1 		return (dv);
988facf4a8dSllai1 	}
989facf4a8dSllai1 	return (NULL);
990facf4a8dSllai1 }
991facf4a8dSllai1 
992facf4a8dSllai1 /*
993facf4a8dSllai1  * Inserts a new sdev_node in a parent directory
994facf4a8dSllai1  */
995facf4a8dSllai1 void
996facf4a8dSllai1 sdev_direnter(struct sdev_node *ddv, struct sdev_node *dv)
997facf4a8dSllai1 {
998aac43a5fSjg 	avl_index_t where;
999aac43a5fSjg 
1000facf4a8dSllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1001facf4a8dSllai1 	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
1002facf4a8dSllai1 	ASSERT(ddv->sdev_nlink >= 2);
1003facf4a8dSllai1 	ASSERT(dv->sdev_nlink == 0);
1004*9e5aa9d8SRobert Mustacchi 	ASSERT(dv->sdev_state != SDEV_ZOMBIE);
1005facf4a8dSllai1 
1006facf4a8dSllai1 	dv->sdev_dotdot = ddv;
1007aac43a5fSjg 	VERIFY(avl_find(&ddv->sdev_entries, dv, &where) == NULL);
1008aac43a5fSjg 	avl_insert(&ddv->sdev_entries, dv, where);
1009facf4a8dSllai1 	ddv->sdev_nlink++;
1010facf4a8dSllai1 }
1011facf4a8dSllai1 
1012facf4a8dSllai1 /*
1013facf4a8dSllai1  * The following check is needed because while sdev_nodes are linked
1014facf4a8dSllai1  * in SDEV_INIT state, they have their link counts incremented only
1015facf4a8dSllai1  * in SDEV_READY state.
1016facf4a8dSllai1  */
1017facf4a8dSllai1 static void
1018facf4a8dSllai1 decr_link(struct sdev_node *dv)
1019facf4a8dSllai1 {
1020*9e5aa9d8SRobert Mustacchi 	VERIFY(RW_WRITE_HELD(&dv->sdev_contents));
1021*9e5aa9d8SRobert Mustacchi 	if (dv->sdev_state != SDEV_INIT) {
1022*9e5aa9d8SRobert Mustacchi 		VERIFY(dv->sdev_nlink >= 1);
1023facf4a8dSllai1 		dv->sdev_nlink--;
1024*9e5aa9d8SRobert Mustacchi 	} else {
1025*9e5aa9d8SRobert Mustacchi 		VERIFY(dv->sdev_nlink == 0);
1026*9e5aa9d8SRobert Mustacchi 	}
1027facf4a8dSllai1 }
1028facf4a8dSllai1 
1029facf4a8dSllai1 /*
1030facf4a8dSllai1  * Delete an existing dv from directory cache
1031facf4a8dSllai1  *
1032*9e5aa9d8SRobert Mustacchi  * In the case of a node is still held by non-zero reference count, the node is
1033*9e5aa9d8SRobert Mustacchi  * put into ZOMBIE state. The node is always unlinked from its parent, but it is
1034*9e5aa9d8SRobert Mustacchi  * not destroyed via sdev_inactive until its reference count reaches "0".
1035facf4a8dSllai1  */
1036*9e5aa9d8SRobert Mustacchi static void
1037facf4a8dSllai1 sdev_dirdelete(struct sdev_node *ddv, struct sdev_node *dv)
1038facf4a8dSllai1 {
1039facf4a8dSllai1 	struct vnode *vp;
1040*9e5aa9d8SRobert Mustacchi 	sdev_node_state_t os;
1041facf4a8dSllai1 
1042facf4a8dSllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1043facf4a8dSllai1 
1044facf4a8dSllai1 	vp = SDEVTOV(dv);
1045facf4a8dSllai1 	mutex_enter(&vp->v_lock);
1046facf4a8dSllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
1047*9e5aa9d8SRobert Mustacchi 	os = dv->sdev_state;
1048*9e5aa9d8SRobert Mustacchi 	ASSERT(os != SDEV_ZOMBIE);
1049facf4a8dSllai1 	dv->sdev_state = SDEV_ZOMBIE;
1050facf4a8dSllai1 
1051*9e5aa9d8SRobert Mustacchi 	/*
1052*9e5aa9d8SRobert Mustacchi 	 * unlink ourselves from the parent directory now to take care of the ..
1053*9e5aa9d8SRobert Mustacchi 	 * link. However, if we're a directory, we don't remove our reference to
1054*9e5aa9d8SRobert Mustacchi 	 * ourself eg. '.' until we are torn down in the inactive callback.
1055*9e5aa9d8SRobert Mustacchi 	 */
1056*9e5aa9d8SRobert Mustacchi 	decr_link(ddv);
1057aac43a5fSjg 	avl_remove(&ddv->sdev_entries, dv);
1058*9e5aa9d8SRobert Mustacchi 	/*
1059*9e5aa9d8SRobert Mustacchi 	 * sdev_inactive expects nodes to have a link to themselves when we're
1060*9e5aa9d8SRobert Mustacchi 	 * tearing them down. If we're transitioning from the initial state to
1061*9e5aa9d8SRobert Mustacchi 	 * zombie and not via ready, then we're not going to have this link that
1062*9e5aa9d8SRobert Mustacchi 	 * comes from the node being ready. As a result, we need to increment
1063*9e5aa9d8SRobert Mustacchi 	 * our link count by one to account for this.
1064*9e5aa9d8SRobert Mustacchi 	 */
1065*9e5aa9d8SRobert Mustacchi 	if (os == SDEV_INIT && dv->sdev_nlink == 0)
1066*9e5aa9d8SRobert Mustacchi 		dv->sdev_nlink++;
1067*9e5aa9d8SRobert Mustacchi 	rw_exit(&dv->sdev_contents);
1068facf4a8dSllai1 	mutex_exit(&vp->v_lock);
1069facf4a8dSllai1 }
1070facf4a8dSllai1 
1071facf4a8dSllai1 /*
1072facf4a8dSllai1  * check if the source is in the path of the target
1073facf4a8dSllai1  *
1074facf4a8dSllai1  * source and target are different
1075facf4a8dSllai1  */
1076facf4a8dSllai1 /*ARGSUSED2*/
1077facf4a8dSllai1 static int
1078facf4a8dSllai1 sdev_checkpath(struct sdev_node *sdv, struct sdev_node *tdv, struct cred *cred)
1079facf4a8dSllai1 {
1080facf4a8dSllai1 	int error = 0;
1081facf4a8dSllai1 	struct sdev_node *dotdot, *dir;
1082facf4a8dSllai1 
1083facf4a8dSllai1 	dotdot = tdv->sdev_dotdot;
1084facf4a8dSllai1 	ASSERT(dotdot);
1085facf4a8dSllai1 
1086facf4a8dSllai1 	/* fs root */
1087facf4a8dSllai1 	if (dotdot == tdv) {
1088facf4a8dSllai1 		return (0);
1089facf4a8dSllai1 	}
1090facf4a8dSllai1 
1091facf4a8dSllai1 	for (;;) {
1092facf4a8dSllai1 		/*
1093facf4a8dSllai1 		 * avoid error cases like
1094facf4a8dSllai1 		 *	mv a a/b
1095facf4a8dSllai1 		 *	mv a a/b/c
1096facf4a8dSllai1 		 *	etc.
1097facf4a8dSllai1 		 */
1098facf4a8dSllai1 		if (dotdot == sdv) {
1099facf4a8dSllai1 			error = EINVAL;
1100facf4a8dSllai1 			break;
1101facf4a8dSllai1 		}
1102facf4a8dSllai1 
1103facf4a8dSllai1 		dir = dotdot;
1104facf4a8dSllai1 		dotdot = dir->sdev_dotdot;
1105facf4a8dSllai1 
1106facf4a8dSllai1 		/* done checking because root is reached */
1107facf4a8dSllai1 		if (dir == dotdot) {
1108facf4a8dSllai1 			break;
1109facf4a8dSllai1 		}
1110facf4a8dSllai1 	}
1111facf4a8dSllai1 	return (error);
1112facf4a8dSllai1 }
1113facf4a8dSllai1 
1114facf4a8dSllai1 int
1115facf4a8dSllai1 sdev_rnmnode(struct sdev_node *oddv, struct sdev_node *odv,
1116facf4a8dSllai1     struct sdev_node *nddv, struct sdev_node **ndvp, char *nnm,
1117facf4a8dSllai1     struct cred *cred)
1118facf4a8dSllai1 {
1119facf4a8dSllai1 	int error = 0;
1120facf4a8dSllai1 	struct vnode *ovp = SDEVTOV(odv);
1121facf4a8dSllai1 	struct vnode *nvp;
1122facf4a8dSllai1 	struct vattr vattr;
1123facf4a8dSllai1 	int doingdir = (ovp->v_type == VDIR);
1124facf4a8dSllai1 	char *link = NULL;
11250bfaec69Sllai1 	int samedir = (oddv == nddv) ? 1 : 0;
11260bfaec69Sllai1 	int bkstore = 0;
11270bfaec69Sllai1 	struct sdev_node *idv = NULL;
11280bfaec69Sllai1 	struct sdev_node *ndv = NULL;
11290bfaec69Sllai1 	timestruc_t now;
11300bfaec69Sllai1 
1131bb5fffbeSJerry Gilliam 	vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID;
1132da6c28aaSamw 	error = VOP_GETATTR(ovp, &vattr, 0, cred, NULL);
11330bfaec69Sllai1 	if (error)
11340bfaec69Sllai1 		return (error);
11350bfaec69Sllai1 
11360bfaec69Sllai1 	if (!samedir)
11370bfaec69Sllai1 		rw_enter(&oddv->sdev_contents, RW_WRITER);
11380bfaec69Sllai1 	rw_enter(&nddv->sdev_contents, RW_WRITER);
11390bfaec69Sllai1 
11400bfaec69Sllai1 	/*
11410bfaec69Sllai1 	 * the source may have been deleted by another thread before
11420bfaec69Sllai1 	 * we gets here.
11430bfaec69Sllai1 	 */
11440bfaec69Sllai1 	if (odv->sdev_state != SDEV_READY) {
11450bfaec69Sllai1 		error = ENOENT;
11460bfaec69Sllai1 		goto err_out;
11470bfaec69Sllai1 	}
11480bfaec69Sllai1 
11490bfaec69Sllai1 	if (doingdir && (odv == nddv)) {
11500bfaec69Sllai1 		error = EINVAL;
11510bfaec69Sllai1 		goto err_out;
11520bfaec69Sllai1 	}
1153facf4a8dSllai1 
1154facf4a8dSllai1 	/*
1155facf4a8dSllai1 	 * If renaming a directory, and the parents are different (".." must be
1156facf4a8dSllai1 	 * changed) then the source dir must not be in the dir hierarchy above
1157facf4a8dSllai1 	 * the target since it would orphan everything below the source dir.
1158facf4a8dSllai1 	 */
1159facf4a8dSllai1 	if (doingdir && (oddv != nddv)) {
1160facf4a8dSllai1 		error = sdev_checkpath(odv, nddv, cred);
1161facf4a8dSllai1 		if (error)
11620bfaec69Sllai1 			goto err_out;
1163facf4a8dSllai1 	}
1164facf4a8dSllai1 
1165*9e5aa9d8SRobert Mustacchi 	/* fix the source for a symlink */
1166*9e5aa9d8SRobert Mustacchi 	if (vattr.va_type == VLNK) {
1167*9e5aa9d8SRobert Mustacchi 		if (odv->sdev_symlink == NULL) {
1168*9e5aa9d8SRobert Mustacchi 			error = sdev_follow_link(odv);
1169*9e5aa9d8SRobert Mustacchi 			if (error) {
1170*9e5aa9d8SRobert Mustacchi 				/*
1171*9e5aa9d8SRobert Mustacchi 				 * The underlying symlink doesn't exist. This
1172*9e5aa9d8SRobert Mustacchi 				 * node probably shouldn't even exist. While
1173*9e5aa9d8SRobert Mustacchi 				 * it's a bit jarring to consumers, we're going
1174*9e5aa9d8SRobert Mustacchi 				 * to remove the node from /dev.
1175*9e5aa9d8SRobert Mustacchi 				 */
1176*9e5aa9d8SRobert Mustacchi 				if (SDEV_IS_PERSIST((*ndvp)))
1177*9e5aa9d8SRobert Mustacchi 					bkstore = 1;
1178*9e5aa9d8SRobert Mustacchi 				sdev_dirdelete(oddv, odv);
1179*9e5aa9d8SRobert Mustacchi 				if (bkstore) {
1180*9e5aa9d8SRobert Mustacchi 					ASSERT(nddv->sdev_attrvp);
1181*9e5aa9d8SRobert Mustacchi 					error = VOP_REMOVE(nddv->sdev_attrvp,
1182*9e5aa9d8SRobert Mustacchi 					    nnm, cred, NULL, 0);
1183*9e5aa9d8SRobert Mustacchi 					if (error)
1184*9e5aa9d8SRobert Mustacchi 						goto err_out;
1185*9e5aa9d8SRobert Mustacchi 				}
1186*9e5aa9d8SRobert Mustacchi 				error = ENOENT;
1187*9e5aa9d8SRobert Mustacchi 				goto err_out;
1188*9e5aa9d8SRobert Mustacchi 			}
1189*9e5aa9d8SRobert Mustacchi 		}
1190*9e5aa9d8SRobert Mustacchi 		ASSERT(odv->sdev_symlink);
1191*9e5aa9d8SRobert Mustacchi 		link = i_ddi_strdup(odv->sdev_symlink, KM_SLEEP);
1192*9e5aa9d8SRobert Mustacchi 	}
1193*9e5aa9d8SRobert Mustacchi 
1194facf4a8dSllai1 	/* destination existing */
11950bfaec69Sllai1 	if (*ndvp) {
1196facf4a8dSllai1 		nvp = SDEVTOV(*ndvp);
1197facf4a8dSllai1 		ASSERT(nvp);
1198facf4a8dSllai1 
1199facf4a8dSllai1 		/* handling renaming to itself */
12000bfaec69Sllai1 		if (odv == *ndvp) {
12010bfaec69Sllai1 			error = 0;
12020bfaec69Sllai1 			goto err_out;
12030bfaec69Sllai1 		}
1204facf4a8dSllai1 
12050bfaec69Sllai1 		if (nvp->v_type == VDIR) {
12060bfaec69Sllai1 			if (!doingdir) {
12070bfaec69Sllai1 				error = EISDIR;
12080bfaec69Sllai1 				goto err_out;
12090bfaec69Sllai1 			}
12100bfaec69Sllai1 
12110bfaec69Sllai1 			if (vn_vfswlock(nvp)) {
12120bfaec69Sllai1 				error = EBUSY;
12130bfaec69Sllai1 				goto err_out;
12140bfaec69Sllai1 			}
12150bfaec69Sllai1 
12160bfaec69Sllai1 			if (vn_mountedvfs(nvp) != NULL) {
12170bfaec69Sllai1 				vn_vfsunlock(nvp);
12180bfaec69Sllai1 				error = EBUSY;
12190bfaec69Sllai1 				goto err_out;
12200bfaec69Sllai1 			}
12210bfaec69Sllai1 
12220bfaec69Sllai1 			/* in case dir1 exists in dir2 and "mv dir1 dir2" */
12230bfaec69Sllai1 			if ((*ndvp)->sdev_nlink > 2) {
12240bfaec69Sllai1 				vn_vfsunlock(nvp);
12250bfaec69Sllai1 				error = EEXIST;
12260bfaec69Sllai1 				goto err_out;
12270bfaec69Sllai1 			}
12280bfaec69Sllai1 			vn_vfsunlock(nvp);
12290bfaec69Sllai1 
1230*9e5aa9d8SRobert Mustacchi 			/*
1231*9e5aa9d8SRobert Mustacchi 			 * We did not place the hold on *ndvp, so even though
1232*9e5aa9d8SRobert Mustacchi 			 * we're deleting the node, we should not get rid of our
1233*9e5aa9d8SRobert Mustacchi 			 * reference.
1234*9e5aa9d8SRobert Mustacchi 			 */
1235*9e5aa9d8SRobert Mustacchi 			sdev_dirdelete(nddv, *ndvp);
12360bfaec69Sllai1 			*ndvp = NULL;
1237b7beec95Sjg 			ASSERT(nddv->sdev_attrvp);
12380bfaec69Sllai1 			error = VOP_RMDIR(nddv->sdev_attrvp, nnm,
1239da6c28aaSamw 			    nddv->sdev_attrvp, cred, NULL, 0);
12400bfaec69Sllai1 			if (error)
12410bfaec69Sllai1 				goto err_out;
12420bfaec69Sllai1 		} else {
1243facf4a8dSllai1 			if (doingdir) {
12440bfaec69Sllai1 				error = ENOTDIR;
12450bfaec69Sllai1 				goto err_out;
12460bfaec69Sllai1 			}
12470bfaec69Sllai1 
12480bfaec69Sllai1 			if (SDEV_IS_PERSIST((*ndvp))) {
12490bfaec69Sllai1 				bkstore = 1;
12500bfaec69Sllai1 			}
1251facf4a8dSllai1 
1252facf4a8dSllai1 			/*
1253*9e5aa9d8SRobert Mustacchi 			 * Get rid of the node from the directory cache note.
1254*9e5aa9d8SRobert Mustacchi 			 * Don't forget that it's not up to us to remove the vn
1255*9e5aa9d8SRobert Mustacchi 			 * ref on the sdev node, as we did not place it.
1256facf4a8dSllai1 			 */
1257*9e5aa9d8SRobert Mustacchi 			sdev_dirdelete(nddv, *ndvp);
12580bfaec69Sllai1 			*ndvp = NULL;
12590bfaec69Sllai1 			if (bkstore) {
1260b7beec95Sjg 				ASSERT(nddv->sdev_attrvp);
12610bfaec69Sllai1 				error = VOP_REMOVE(nddv->sdev_attrvp,
1262da6c28aaSamw 				    nnm, cred, NULL, 0);
1263facf4a8dSllai1 				if (error)
12640bfaec69Sllai1 					goto err_out;
12650bfaec69Sllai1 			}
12660bfaec69Sllai1 		}
1267facf4a8dSllai1 	}
1268facf4a8dSllai1 
12690bfaec69Sllai1 	/*
12700bfaec69Sllai1 	 * make a fresh node from the source attrs
12710bfaec69Sllai1 	 */
12720bfaec69Sllai1 	ASSERT(RW_WRITE_HELD(&nddv->sdev_contents));
12730bfaec69Sllai1 	error = sdev_mknode(nddv, nnm, ndvp, &vattr,
12740bfaec69Sllai1 	    NULL, (void *)link, cred, SDEV_READY);
1275facf4a8dSllai1 
1276*9e5aa9d8SRobert Mustacchi 	if (link != NULL) {
1277facf4a8dSllai1 		kmem_free(link, strlen(link) + 1);
1278*9e5aa9d8SRobert Mustacchi 		link = NULL;
1279*9e5aa9d8SRobert Mustacchi 	}
1280facf4a8dSllai1 
12810bfaec69Sllai1 	if (error)
12820bfaec69Sllai1 		goto err_out;
12830bfaec69Sllai1 	ASSERT(*ndvp);
12840bfaec69Sllai1 	ASSERT((*ndvp)->sdev_state == SDEV_READY);
12850bfaec69Sllai1 
12860bfaec69Sllai1 	/* move dir contents */
12870bfaec69Sllai1 	if (doingdir) {
1288aac43a5fSjg 		for (idv = SDEV_FIRST_ENTRY(odv); idv;
1289aac43a5fSjg 		    idv = SDEV_NEXT_ENTRY(odv, idv)) {
1290*9e5aa9d8SRobert Mustacchi 			SDEV_HOLD(idv);
12910bfaec69Sllai1 			error = sdev_rnmnode(odv, idv,
12920bfaec69Sllai1 			    (struct sdev_node *)(*ndvp), &ndv,
12930bfaec69Sllai1 			    idv->sdev_name, cred);
1294*9e5aa9d8SRobert Mustacchi 			SDEV_RELE(idv);
12950bfaec69Sllai1 			if (error)
12960bfaec69Sllai1 				goto err_out;
12970bfaec69Sllai1 			ndv = NULL;
12980bfaec69Sllai1 		}
12990bfaec69Sllai1 	}
13000bfaec69Sllai1 
13010bfaec69Sllai1 	if ((*ndvp)->sdev_attrvp) {
13020bfaec69Sllai1 		sdev_update_timestamps((*ndvp)->sdev_attrvp, kcred,
13030bfaec69Sllai1 		    AT_CTIME|AT_ATIME);
13040bfaec69Sllai1 	} else {
13050bfaec69Sllai1 		ASSERT((*ndvp)->sdev_attr);
13060bfaec69Sllai1 		gethrestime(&now);
13070bfaec69Sllai1 		(*ndvp)->sdev_attr->va_ctime = now;
13080bfaec69Sllai1 		(*ndvp)->sdev_attr->va_atime = now;
13090bfaec69Sllai1 	}
13100bfaec69Sllai1 
13110bfaec69Sllai1 	if (nddv->sdev_attrvp) {
13120bfaec69Sllai1 		sdev_update_timestamps(nddv->sdev_attrvp, kcred,
13130bfaec69Sllai1 		    AT_MTIME|AT_ATIME);
13140bfaec69Sllai1 	} else {
13150bfaec69Sllai1 		ASSERT(nddv->sdev_attr);
13160bfaec69Sllai1 		gethrestime(&now);
13170bfaec69Sllai1 		nddv->sdev_attr->va_mtime = now;
13180bfaec69Sllai1 		nddv->sdev_attr->va_atime = now;
13190bfaec69Sllai1 	}
13200bfaec69Sllai1 	rw_exit(&nddv->sdev_contents);
13210bfaec69Sllai1 	if (!samedir)
13220bfaec69Sllai1 		rw_exit(&oddv->sdev_contents);
13230bfaec69Sllai1 
1324facf4a8dSllai1 	SDEV_RELE(*ndvp);
13250bfaec69Sllai1 	return (error);
13260bfaec69Sllai1 
13270bfaec69Sllai1 err_out:
1328*9e5aa9d8SRobert Mustacchi 	if (link != NULL) {
1329*9e5aa9d8SRobert Mustacchi 		kmem_free(link, strlen(link) + 1);
1330*9e5aa9d8SRobert Mustacchi 		link = NULL;
1331*9e5aa9d8SRobert Mustacchi 	}
1332*9e5aa9d8SRobert Mustacchi 
13330bfaec69Sllai1 	rw_exit(&nddv->sdev_contents);
13340bfaec69Sllai1 	if (!samedir)
13350bfaec69Sllai1 		rw_exit(&oddv->sdev_contents);
13360bfaec69Sllai1 	return (error);
1337facf4a8dSllai1 }
1338facf4a8dSllai1 
1339facf4a8dSllai1 /*
1340facf4a8dSllai1  * Merge sdev_node specific information into an attribute structure.
1341facf4a8dSllai1  *
1342facf4a8dSllai1  * note: sdev_node is not locked here
1343facf4a8dSllai1  */
1344facf4a8dSllai1 void
1345facf4a8dSllai1 sdev_vattr_merge(struct sdev_node *dv, struct vattr *vap)
1346facf4a8dSllai1 {
1347facf4a8dSllai1 	struct vnode *vp = SDEVTOV(dv);
1348facf4a8dSllai1 
1349facf4a8dSllai1 	vap->va_nlink = dv->sdev_nlink;
1350facf4a8dSllai1 	vap->va_nodeid = dv->sdev_ino;
1351facf4a8dSllai1 	vap->va_fsid = SDEVTOV(dv->sdev_dotdot)->v_rdev;
1352facf4a8dSllai1 	vap->va_type = vp->v_type;
1353facf4a8dSllai1 
1354facf4a8dSllai1 	if (vp->v_type == VDIR) {
1355facf4a8dSllai1 		vap->va_rdev = 0;
1356facf4a8dSllai1 		vap->va_fsid = vp->v_rdev;
1357facf4a8dSllai1 	} else if (vp->v_type == VLNK) {
1358facf4a8dSllai1 		vap->va_rdev = 0;
1359facf4a8dSllai1 		vap->va_mode  &= ~S_IFMT;
1360facf4a8dSllai1 		vap->va_mode |= S_IFLNK;
1361facf4a8dSllai1 	} else if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
1362facf4a8dSllai1 		vap->va_rdev = vp->v_rdev;
1363facf4a8dSllai1 		vap->va_mode &= ~S_IFMT;
1364facf4a8dSllai1 		if (vap->va_type == VCHR)
1365facf4a8dSllai1 			vap->va_mode |= S_IFCHR;
1366facf4a8dSllai1 		else
1367facf4a8dSllai1 			vap->va_mode |= S_IFBLK;
1368facf4a8dSllai1 	} else {
1369facf4a8dSllai1 		vap->va_rdev = 0;
1370facf4a8dSllai1 	}
1371facf4a8dSllai1 }
1372facf4a8dSllai1 
1373681d9761SEric Taylor struct vattr *
1374facf4a8dSllai1 sdev_getdefault_attr(enum vtype type)
1375facf4a8dSllai1 {
1376facf4a8dSllai1 	if (type == VDIR)
1377facf4a8dSllai1 		return (&sdev_vattr_dir);
1378facf4a8dSllai1 	else if (type == VCHR)
1379facf4a8dSllai1 		return (&sdev_vattr_chr);
1380facf4a8dSllai1 	else if (type == VBLK)
1381facf4a8dSllai1 		return (&sdev_vattr_blk);
1382facf4a8dSllai1 	else if (type == VLNK)
1383facf4a8dSllai1 		return (&sdev_vattr_lnk);
1384facf4a8dSllai1 	else
1385facf4a8dSllai1 		return (NULL);
1386facf4a8dSllai1 }
1387facf4a8dSllai1 int
1388facf4a8dSllai1 sdev_to_vp(struct sdev_node *dv, struct vnode **vpp)
1389facf4a8dSllai1 {
1390facf4a8dSllai1 	int rv = 0;
1391facf4a8dSllai1 	struct vnode *vp = SDEVTOV(dv);
1392facf4a8dSllai1 
1393facf4a8dSllai1 	switch (vp->v_type) {
1394facf4a8dSllai1 	case VCHR:
1395facf4a8dSllai1 	case VBLK:
1396facf4a8dSllai1 		/*
1397facf4a8dSllai1 		 * If vnode is a device, return special vnode instead
1398facf4a8dSllai1 		 * (though it knows all about -us- via sp->s_realvp)
1399facf4a8dSllai1 		 */
1400facf4a8dSllai1 		*vpp = specvp(vp, vp->v_rdev, vp->v_type, kcred);
1401facf4a8dSllai1 		VN_RELE(vp);
1402facf4a8dSllai1 		if (*vpp == NULLVP)
1403facf4a8dSllai1 			rv = ENOSYS;
1404facf4a8dSllai1 		break;
1405facf4a8dSllai1 	default:	/* most types are returned as is */
1406facf4a8dSllai1 		*vpp = vp;
1407facf4a8dSllai1 		break;
1408facf4a8dSllai1 	}
1409facf4a8dSllai1 	return (rv);
1410facf4a8dSllai1 }
1411facf4a8dSllai1 
1412facf4a8dSllai1 /*
1413facf4a8dSllai1  * junction between devname and root file system, e.g. ufs
1414facf4a8dSllai1  */
1415facf4a8dSllai1 int
1416facf4a8dSllai1 devname_backstore_lookup(struct sdev_node *ddv, char *nm, struct vnode **rvp)
1417facf4a8dSllai1 {
1418facf4a8dSllai1 	struct vnode *rdvp = ddv->sdev_attrvp;
1419facf4a8dSllai1 	int rval = 0;
1420facf4a8dSllai1 
1421facf4a8dSllai1 	ASSERT(rdvp);
1422facf4a8dSllai1 
1423da6c28aaSamw 	rval = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, kcred, NULL, NULL,
1424da6c28aaSamw 	    NULL);
1425facf4a8dSllai1 	return (rval);
1426facf4a8dSllai1 }
1427facf4a8dSllai1 
1428facf4a8dSllai1 static int
1429facf4a8dSllai1 sdev_filldir_from_store(struct sdev_node *ddv, int dlen, struct cred *cred)
1430facf4a8dSllai1 {
1431facf4a8dSllai1 	struct sdev_node *dv = NULL;
1432facf4a8dSllai1 	char	*nm;
1433facf4a8dSllai1 	struct vnode *dirvp;
1434facf4a8dSllai1 	int	error;
1435facf4a8dSllai1 	vnode_t	*vp;
1436facf4a8dSllai1 	int eof;
1437facf4a8dSllai1 	struct iovec iov;
1438facf4a8dSllai1 	struct uio uio;
1439facf4a8dSllai1 	struct dirent64 *dp;
1440facf4a8dSllai1 	dirent64_t *dbuf;
1441facf4a8dSllai1 	size_t dbuflen;
1442facf4a8dSllai1 	struct vattr vattr;
1443facf4a8dSllai1 	char *link = NULL;
1444facf4a8dSllai1 
1445facf4a8dSllai1 	if (ddv->sdev_attrvp == NULL)
1446facf4a8dSllai1 		return (0);
1447facf4a8dSllai1 	if (!(ddv->sdev_flags & SDEV_BUILD))
1448facf4a8dSllai1 		return (0);
1449facf4a8dSllai1 
1450facf4a8dSllai1 	dirvp = ddv->sdev_attrvp;
1451facf4a8dSllai1 	VN_HOLD(dirvp);
1452facf4a8dSllai1 	dbuf = kmem_zalloc(dlen, KM_SLEEP);
1453facf4a8dSllai1 
1454facf4a8dSllai1 	uio.uio_iov = &iov;
1455facf4a8dSllai1 	uio.uio_iovcnt = 1;
1456facf4a8dSllai1 	uio.uio_segflg = UIO_SYSSPACE;
1457facf4a8dSllai1 	uio.uio_fmode = 0;
1458facf4a8dSllai1 	uio.uio_extflg = UIO_COPY_CACHED;
1459facf4a8dSllai1 	uio.uio_loffset = 0;
1460facf4a8dSllai1 	uio.uio_llimit = MAXOFFSET_T;
1461facf4a8dSllai1 
1462facf4a8dSllai1 	eof = 0;
1463facf4a8dSllai1 	error = 0;
1464facf4a8dSllai1 	while (!error && !eof) {
1465facf4a8dSllai1 		uio.uio_resid = dlen;
1466facf4a8dSllai1 		iov.iov_base = (char *)dbuf;
1467facf4a8dSllai1 		iov.iov_len = dlen;
1468facf4a8dSllai1 		(void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL);
1469da6c28aaSamw 		error = VOP_READDIR(dirvp, &uio, kcred, &eof, NULL, 0);
1470facf4a8dSllai1 		VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL);
1471facf4a8dSllai1 
1472facf4a8dSllai1 		dbuflen = dlen - uio.uio_resid;
1473facf4a8dSllai1 		if (error || dbuflen == 0)
1474facf4a8dSllai1 			break;
1475facf4a8dSllai1 
1476681d9761SEric Taylor 		if (!(ddv->sdev_flags & SDEV_BUILD))
1477facf4a8dSllai1 			break;
1478facf4a8dSllai1 
1479facf4a8dSllai1 		for (dp = dbuf; ((intptr_t)dp <
1480facf4a8dSllai1 		    (intptr_t)dbuf + dbuflen);
1481facf4a8dSllai1 		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
1482facf4a8dSllai1 			nm = dp->d_name;
1483facf4a8dSllai1 
1484facf4a8dSllai1 			if (strcmp(nm, ".") == 0 ||
1485facf4a8dSllai1 			    strcmp(nm, "..") == 0)
1486facf4a8dSllai1 				continue;
1487facf4a8dSllai1 
1488facf4a8dSllai1 			vp = NULLVP;
1489facf4a8dSllai1 			dv = sdev_cache_lookup(ddv, nm);
1490facf4a8dSllai1 			if (dv) {
1491*9e5aa9d8SRobert Mustacchi 				VERIFY(dv->sdev_state != SDEV_ZOMBIE);
1492facf4a8dSllai1 				SDEV_SIMPLE_RELE(dv);
1493facf4a8dSllai1 				continue;
1494facf4a8dSllai1 			}
1495facf4a8dSllai1 
1496facf4a8dSllai1 			/* refill the cache if not already */
1497facf4a8dSllai1 			error = devname_backstore_lookup(ddv, nm, &vp);
1498facf4a8dSllai1 			if (error)
1499facf4a8dSllai1 				continue;
1500facf4a8dSllai1 
1501bb5fffbeSJerry Gilliam 			vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID;
1502da6c28aaSamw 			error = VOP_GETATTR(vp, &vattr, 0, cred, NULL);
1503facf4a8dSllai1 			if (error)
1504facf4a8dSllai1 				continue;
1505facf4a8dSllai1 
1506facf4a8dSllai1 			if (vattr.va_type == VLNK) {
1507facf4a8dSllai1 				error = sdev_getlink(vp, &link);
1508facf4a8dSllai1 				if (error) {
1509facf4a8dSllai1 					continue;
1510facf4a8dSllai1 				}
1511facf4a8dSllai1 				ASSERT(link != NULL);
1512facf4a8dSllai1 			}
1513facf4a8dSllai1 
1514facf4a8dSllai1 			if (!rw_tryupgrade(&ddv->sdev_contents)) {
1515facf4a8dSllai1 				rw_exit(&ddv->sdev_contents);
1516facf4a8dSllai1 				rw_enter(&ddv->sdev_contents, RW_WRITER);
1517facf4a8dSllai1 			}
1518facf4a8dSllai1 			error = sdev_mknode(ddv, nm, &dv, &vattr, vp, link,
1519facf4a8dSllai1 			    cred, SDEV_READY);
1520facf4a8dSllai1 			rw_downgrade(&ddv->sdev_contents);
1521facf4a8dSllai1 
1522facf4a8dSllai1 			if (link != NULL) {
1523facf4a8dSllai1 				kmem_free(link, strlen(link) + 1);
1524facf4a8dSllai1 				link = NULL;
1525facf4a8dSllai1 			}
1526facf4a8dSllai1 
1527facf4a8dSllai1 			if (!error) {
1528facf4a8dSllai1 				ASSERT(dv);
1529facf4a8dSllai1 				ASSERT(dv->sdev_state != SDEV_ZOMBIE);
1530facf4a8dSllai1 				SDEV_SIMPLE_RELE(dv);
1531facf4a8dSllai1 			}
1532facf4a8dSllai1 			vp = NULL;
1533facf4a8dSllai1 			dv = NULL;
1534facf4a8dSllai1 		}
1535facf4a8dSllai1 	}
1536facf4a8dSllai1 
1537facf4a8dSllai1 done:
1538facf4a8dSllai1 	VN_RELE(dirvp);
1539facf4a8dSllai1 	kmem_free(dbuf, dlen);
1540facf4a8dSllai1 
1541facf4a8dSllai1 	return (error);
1542facf4a8dSllai1 }
1543facf4a8dSllai1 
15446b938478Sjg void
1545facf4a8dSllai1 sdev_filldir_dynamic(struct sdev_node *ddv)
1546facf4a8dSllai1 {
1547facf4a8dSllai1 	int error;
1548facf4a8dSllai1 	int i;
1549bb5fffbeSJerry Gilliam 	struct vattr vattr;
1550bb5fffbeSJerry Gilliam 	struct vattr *vap = &vattr;
1551facf4a8dSllai1 	char *nm = NULL;
1552facf4a8dSllai1 	struct sdev_node *dv = NULL;
1553facf4a8dSllai1 
15546b938478Sjg 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
15556b938478Sjg 	ASSERT((ddv->sdev_flags & SDEV_BUILD));
1556facf4a8dSllai1 
1557bb5fffbeSJerry Gilliam 	*vap = *sdev_getdefault_attr(VDIR);	/* note structure copy here */
1558681d9761SEric Taylor 	gethrestime(&vap->va_atime);
1559681d9761SEric Taylor 	vap->va_mtime = vap->va_atime;
1560681d9761SEric Taylor 	vap->va_ctime = vap->va_atime;
1561facf4a8dSllai1 	for (i = 0; vtab[i].vt_name != NULL; i++) {
156267323fc4SJohn Levon 		/*
1563*9e5aa9d8SRobert Mustacchi 		 * This early, we may be in a read-only /dev environment: leave
1564*9e5aa9d8SRobert Mustacchi 		 * the creation of any nodes we'd attempt to persist to
1565*9e5aa9d8SRobert Mustacchi 		 * devfsadm. Because /dev itself is normally persistent, any
1566*9e5aa9d8SRobert Mustacchi 		 * node which is not marked dynamic will end up being marked
1567*9e5aa9d8SRobert Mustacchi 		 * persistent. However, some nodes are both dynamic and
1568*9e5aa9d8SRobert Mustacchi 		 * persistent, mostly lofi and rlofi, so we need to be careful
1569*9e5aa9d8SRobert Mustacchi 		 * in our check.
157067323fc4SJohn Levon 		 */
1571*9e5aa9d8SRobert Mustacchi 		if ((vtab[i].vt_flags & SDEV_PERSIST) ||
1572*9e5aa9d8SRobert Mustacchi 		    !(vtab[i].vt_flags & SDEV_DYNAMIC))
157367323fc4SJohn Levon 			continue;
1574facf4a8dSllai1 		nm = vtab[i].vt_name;
1575facf4a8dSllai1 		ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
15766b938478Sjg 		dv = NULL;
1577facf4a8dSllai1 		error = sdev_mknode(ddv, nm, &dv, vap, NULL,
1578facf4a8dSllai1 		    NULL, kcred, SDEV_READY);
15796b938478Sjg 		if (error) {
15806b938478Sjg 			cmn_err(CE_WARN, "%s/%s: error %d\n",
15816b938478Sjg 			    ddv->sdev_name, nm, error);
15826b938478Sjg 		} else {
1583facf4a8dSllai1 			ASSERT(dv);
1584facf4a8dSllai1 			ASSERT(dv->sdev_state != SDEV_ZOMBIE);
1585facf4a8dSllai1 			SDEV_SIMPLE_RELE(dv);
1586facf4a8dSllai1 		}
15876b938478Sjg 	}
1588facf4a8dSllai1 }
1589facf4a8dSllai1 
1590facf4a8dSllai1 /*
1591facf4a8dSllai1  * Creating a backing store entry based on sdev_attr.
1592facf4a8dSllai1  * This is called either as part of node creation in a persistent directory
1593facf4a8dSllai1  * or from setattr/setsecattr to persist access attributes across reboot.
1594facf4a8dSllai1  */
1595facf4a8dSllai1 int
1596facf4a8dSllai1 sdev_shadow_node(struct sdev_node *dv, struct cred *cred)
1597facf4a8dSllai1 {
1598facf4a8dSllai1 	int error = 0;
1599facf4a8dSllai1 	struct vnode *dvp = SDEVTOV(dv->sdev_dotdot);
1600facf4a8dSllai1 	struct vnode *rdvp = VTOSDEV(dvp)->sdev_attrvp;
1601facf4a8dSllai1 	struct vattr *vap = dv->sdev_attr;
1602facf4a8dSllai1 	char *nm = dv->sdev_name;
1603facf4a8dSllai1 	struct vnode *tmpvp, **rvp = &tmpvp, *rrvp = NULL;
1604facf4a8dSllai1 
1605facf4a8dSllai1 	ASSERT(dv && dv->sdev_name && rdvp);
1606facf4a8dSllai1 	ASSERT(RW_WRITE_HELD(&dv->sdev_contents) && dv->sdev_attrvp == NULL);
1607facf4a8dSllai1 
1608facf4a8dSllai1 lookup:
1609facf4a8dSllai1 	/* try to find it in the backing store */
1610da6c28aaSamw 	error = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, cred, NULL, NULL,
1611da6c28aaSamw 	    NULL);
1612facf4a8dSllai1 	if (error == 0) {
1613da6c28aaSamw 		if (VOP_REALVP(*rvp, &rrvp, NULL) == 0) {
1614facf4a8dSllai1 			VN_HOLD(rrvp);
1615facf4a8dSllai1 			VN_RELE(*rvp);
1616facf4a8dSllai1 			*rvp = rrvp;
1617facf4a8dSllai1 		}
1618facf4a8dSllai1 
1619facf4a8dSllai1 		kmem_free(dv->sdev_attr, sizeof (vattr_t));
1620facf4a8dSllai1 		dv->sdev_attr = NULL;
1621facf4a8dSllai1 		dv->sdev_attrvp = *rvp;
1622facf4a8dSllai1 		return (0);
1623facf4a8dSllai1 	}
1624facf4a8dSllai1 
1625facf4a8dSllai1 	/* let's try to persist the node */
1626facf4a8dSllai1 	gethrestime(&vap->va_atime);
1627facf4a8dSllai1 	vap->va_mtime = vap->va_atime;
1628facf4a8dSllai1 	vap->va_ctime = vap->va_atime;
1629facf4a8dSllai1 	vap->va_mask |= AT_TYPE|AT_MODE;
1630facf4a8dSllai1 	switch (vap->va_type) {
1631facf4a8dSllai1 	case VDIR:
1632da6c28aaSamw 		error = VOP_MKDIR(rdvp, nm, vap, rvp, cred, NULL, 0, NULL);
1633facf4a8dSllai1 		sdcmn_err9(("sdev_shadow_node: mkdir vp %p error %d\n",
1634facf4a8dSllai1 		    (void *)(*rvp), error));
1635*9e5aa9d8SRobert Mustacchi 		if (!error)
1636*9e5aa9d8SRobert Mustacchi 			VN_RELE(*rvp);
1637facf4a8dSllai1 		break;
1638facf4a8dSllai1 	case VCHR:
1639facf4a8dSllai1 	case VBLK:
1640facf4a8dSllai1 	case VREG:
1641facf4a8dSllai1 	case VDOOR:
1642facf4a8dSllai1 		error = VOP_CREATE(rdvp, nm, vap, NONEXCL, VREAD|VWRITE,
1643da6c28aaSamw 		    rvp, cred, 0, NULL, NULL);
1644facf4a8dSllai1 		sdcmn_err9(("sdev_shadow_node: create vp %p, error %d\n",
1645facf4a8dSllai1 		    (void *)(*rvp), error));
1646facf4a8dSllai1 		if (!error)
1647facf4a8dSllai1 			VN_RELE(*rvp);
1648facf4a8dSllai1 		break;
1649facf4a8dSllai1 	case VLNK:
1650facf4a8dSllai1 		ASSERT(dv->sdev_symlink);
1651da6c28aaSamw 		error = VOP_SYMLINK(rdvp, nm, vap, dv->sdev_symlink, cred,
1652da6c28aaSamw 		    NULL, 0);
1653facf4a8dSllai1 		sdcmn_err9(("sdev_shadow_node: create symlink error %d\n",
1654facf4a8dSllai1 		    error));
1655facf4a8dSllai1 		break;
1656facf4a8dSllai1 	default:
1657facf4a8dSllai1 		cmn_err(CE_PANIC, "dev: %s: sdev_shadow_node "
1658facf4a8dSllai1 		    "create\n", nm);
1659facf4a8dSllai1 		/*NOTREACHED*/
1660facf4a8dSllai1 	}
1661facf4a8dSllai1 
1662facf4a8dSllai1 	/* go back to lookup to factor out spec node and set attrvp */
1663facf4a8dSllai1 	if (error == 0)
1664facf4a8dSllai1 		goto lookup;
1665facf4a8dSllai1 
1666b7beec95Sjg 	sdcmn_err(("cannot persist %s - error %d\n", dv->sdev_path, error));
1667facf4a8dSllai1 	return (error);
1668facf4a8dSllai1 }
1669facf4a8dSllai1 
1670*9e5aa9d8SRobert Mustacchi static void
1671facf4a8dSllai1 sdev_cache_add(struct sdev_node *ddv, struct sdev_node **dv, char *nm)
1672facf4a8dSllai1 {
1673facf4a8dSllai1 	struct sdev_node *dup = NULL;
1674facf4a8dSllai1 
1675facf4a8dSllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1676facf4a8dSllai1 	if ((dup = sdev_findbyname(ddv, nm)) == NULL) {
1677facf4a8dSllai1 		sdev_direnter(ddv, *dv);
1678facf4a8dSllai1 	} else {
1679*9e5aa9d8SRobert Mustacchi 		VERIFY(dup->sdev_state != SDEV_ZOMBIE);
1680facf4a8dSllai1 		SDEV_SIMPLE_RELE(*dv);
1681facf4a8dSllai1 		sdev_nodedestroy(*dv, 0);
1682facf4a8dSllai1 		*dv = dup;
1683facf4a8dSllai1 	}
1684facf4a8dSllai1 }
1685facf4a8dSllai1 
1686*9e5aa9d8SRobert Mustacchi static void
1687facf4a8dSllai1 sdev_cache_delete(struct sdev_node *ddv, struct sdev_node **dv)
1688facf4a8dSllai1 {
1689facf4a8dSllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1690*9e5aa9d8SRobert Mustacchi 	sdev_dirdelete(ddv, *dv);
1691facf4a8dSllai1 }
1692facf4a8dSllai1 
1693facf4a8dSllai1 /*
1694facf4a8dSllai1  * update the in-core directory cache
1695facf4a8dSllai1  */
1696*9e5aa9d8SRobert Mustacchi void
1697facf4a8dSllai1 sdev_cache_update(struct sdev_node *ddv, struct sdev_node **dv, char *nm,
1698facf4a8dSllai1     sdev_cache_ops_t ops)
1699facf4a8dSllai1 {
1700facf4a8dSllai1 	ASSERT((SDEV_HELD(*dv)));
1701facf4a8dSllai1 
1702facf4a8dSllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1703facf4a8dSllai1 	switch (ops) {
1704facf4a8dSllai1 	case SDEV_CACHE_ADD:
1705*9e5aa9d8SRobert Mustacchi 		sdev_cache_add(ddv, dv, nm);
1706facf4a8dSllai1 		break;
1707facf4a8dSllai1 	case SDEV_CACHE_DELETE:
1708*9e5aa9d8SRobert Mustacchi 		sdev_cache_delete(ddv, dv);
1709facf4a8dSllai1 		break;
1710facf4a8dSllai1 	default:
1711facf4a8dSllai1 		break;
1712facf4a8dSllai1 	}
1713facf4a8dSllai1 }
1714facf4a8dSllai1 
1715facf4a8dSllai1 /*
1716da6c28aaSamw  * retrieve the named entry from the directory cache
1717facf4a8dSllai1  */
1718facf4a8dSllai1 struct sdev_node *
1719facf4a8dSllai1 sdev_cache_lookup(struct sdev_node *ddv, char *nm)
1720facf4a8dSllai1 {
1721facf4a8dSllai1 	struct sdev_node *dv = NULL;
1722facf4a8dSllai1 
1723facf4a8dSllai1 	ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
1724facf4a8dSllai1 	dv = sdev_findbyname(ddv, nm);
1725facf4a8dSllai1 
1726facf4a8dSllai1 	return (dv);
1727facf4a8dSllai1 }
1728facf4a8dSllai1 
1729facf4a8dSllai1 /*
1730facf4a8dSllai1  * Implicit reconfig for nodes constructed by a link generator
1731facf4a8dSllai1  * Start devfsadm if needed, or if devfsadm is in progress,
1732facf4a8dSllai1  * prepare to block on devfsadm either completing or
1733facf4a8dSllai1  * constructing the desired node.  As devfsadmd is global
1734facf4a8dSllai1  * in scope, constructing all necessary nodes, we only
1735facf4a8dSllai1  * need to initiate it once.
1736facf4a8dSllai1  */
1737facf4a8dSllai1 static int
1738facf4a8dSllai1 sdev_call_devfsadmd(struct sdev_node *ddv, struct sdev_node *dv, char *nm)
1739facf4a8dSllai1 {
1740facf4a8dSllai1 	int error = 0;
1741facf4a8dSllai1 
1742facf4a8dSllai1 	if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) {
1743facf4a8dSllai1 		sdcmn_err6(("lookup: waiting for %s/%s, 0x%x\n",
1744facf4a8dSllai1 		    ddv->sdev_name, nm, devfsadm_state));
1745facf4a8dSllai1 		mutex_enter(&dv->sdev_lookup_lock);
1746facf4a8dSllai1 		SDEV_BLOCK_OTHERS(dv, (SDEV_LOOKUP | SDEV_LGWAITING));
1747facf4a8dSllai1 		mutex_exit(&dv->sdev_lookup_lock);
1748facf4a8dSllai1 		error = 0;
1749facf4a8dSllai1 	} else if (!DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state)) {
1750facf4a8dSllai1 		sdcmn_err6(("lookup %s/%s starting devfsadm, 0x%x\n",
1751facf4a8dSllai1 		    ddv->sdev_name, nm, devfsadm_state));
1752facf4a8dSllai1 
1753facf4a8dSllai1 		sdev_devfsadmd_thread(ddv, dv, kcred);
1754facf4a8dSllai1 		mutex_enter(&dv->sdev_lookup_lock);
1755facf4a8dSllai1 		SDEV_BLOCK_OTHERS(dv,
1756facf4a8dSllai1 		    (SDEV_LOOKUP | SDEV_LGWAITING));
1757facf4a8dSllai1 		mutex_exit(&dv->sdev_lookup_lock);
1758facf4a8dSllai1 		error = 0;
1759facf4a8dSllai1 	} else {
1760facf4a8dSllai1 		error = -1;
1761facf4a8dSllai1 	}
1762facf4a8dSllai1 
1763facf4a8dSllai1 	return (error);
1764facf4a8dSllai1 }
1765facf4a8dSllai1 
1766facf4a8dSllai1 /*
1767facf4a8dSllai1  *  Support for specialized device naming construction mechanisms
1768facf4a8dSllai1  */
1769facf4a8dSllai1 static int
1770facf4a8dSllai1 sdev_call_dircallback(struct sdev_node *ddv, struct sdev_node **dvp, char *nm,
1771facf4a8dSllai1     int (*callback)(struct sdev_node *, char *, void **, struct cred *,
1772facf4a8dSllai1     void *, char *), int flags, struct cred *cred)
1773facf4a8dSllai1 {
1774facf4a8dSllai1 	int rv = 0;
1775facf4a8dSllai1 	char *physpath = NULL;
1776facf4a8dSllai1 	struct vattr vattr;
1777bb5fffbeSJerry Gilliam 	struct vattr *vap = &vattr;
1778681d9761SEric Taylor 	struct sdev_node *dv = NULL;
1779facf4a8dSllai1 
1780681d9761SEric Taylor 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
1781681d9761SEric Taylor 	if (flags & SDEV_VLINK) {
1782aecfc01dSrui zang - Sun Microsystems - Beijing China 		physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
1783aecfc01dSrui zang - Sun Microsystems - Beijing China 		rv = callback(ddv, nm, (void *)&physpath, kcred, NULL,
1784aecfc01dSrui zang - Sun Microsystems - Beijing China 		    NULL);
1785aecfc01dSrui zang - Sun Microsystems - Beijing China 		if (rv) {
1786aecfc01dSrui zang - Sun Microsystems - Beijing China 			kmem_free(physpath, MAXPATHLEN);
1787aecfc01dSrui zang - Sun Microsystems - Beijing China 			return (-1);
1788aecfc01dSrui zang - Sun Microsystems - Beijing China 		}
1789aecfc01dSrui zang - Sun Microsystems - Beijing China 
1790bb5fffbeSJerry Gilliam 		*vap = *sdev_getdefault_attr(VLNK);	/* structure copy */
1791aecfc01dSrui zang - Sun Microsystems - Beijing China 		vap->va_size = strlen(physpath);
1792681d9761SEric Taylor 		gethrestime(&vap->va_atime);
1793681d9761SEric Taylor 		vap->va_mtime = vap->va_atime;
1794681d9761SEric Taylor 		vap->va_ctime = vap->va_atime;
1795aecfc01dSrui zang - Sun Microsystems - Beijing China 
1796aecfc01dSrui zang - Sun Microsystems - Beijing China 		rv = sdev_mknode(ddv, nm, &dv, vap, NULL,
1797aecfc01dSrui zang - Sun Microsystems - Beijing China 		    (void *)physpath, cred, SDEV_READY);
1798aecfc01dSrui zang - Sun Microsystems - Beijing China 		kmem_free(physpath, MAXPATHLEN);
1799aecfc01dSrui zang - Sun Microsystems - Beijing China 		if (rv)
1800aecfc01dSrui zang - Sun Microsystems - Beijing China 			return (rv);
1801facf4a8dSllai1 	} else if (flags & SDEV_VATTR) {
1802facf4a8dSllai1 		/*
1803facf4a8dSllai1 		 * /dev/pts
1804facf4a8dSllai1 		 *
1805facf4a8dSllai1 		 * callback is responsible to set the basic attributes,
1806facf4a8dSllai1 		 * e.g. va_type/va_uid/va_gid/
1807facf4a8dSllai1 		 *    dev_t if VCHR or VBLK/
1808facf4a8dSllai1 		 */
1809facf4a8dSllai1 		ASSERT(callback);
1810facf4a8dSllai1 		rv = callback(ddv, nm, (void *)&vattr, kcred, NULL, NULL);
1811facf4a8dSllai1 		if (rv) {
1812facf4a8dSllai1 			sdcmn_err3(("devname_lookup_func: SDEV_NONE "
1813facf4a8dSllai1 			    "callback failed \n"));
1814facf4a8dSllai1 			return (-1);
1815facf4a8dSllai1 		}
1816facf4a8dSllai1 
1817facf4a8dSllai1 		rv = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL,
1818facf4a8dSllai1 		    cred, SDEV_READY);
1819facf4a8dSllai1 
1820facf4a8dSllai1 		if (rv)
1821facf4a8dSllai1 			return (rv);
1822facf4a8dSllai1 
1823facf4a8dSllai1 	} else {
1824facf4a8dSllai1 		impossible(("lookup: %s/%s by %s not supported (%d)\n",
1825facf4a8dSllai1 		    SDEVTOV(ddv)->v_path, nm, curproc->p_user.u_comm,
1826facf4a8dSllai1 		    __LINE__));
1827facf4a8dSllai1 		rv = -1;
1828facf4a8dSllai1 	}
1829facf4a8dSllai1 
1830facf4a8dSllai1 	*dvp = dv;
1831facf4a8dSllai1 	return (rv);
1832facf4a8dSllai1 }
1833facf4a8dSllai1 
1834facf4a8dSllai1 static int
1835facf4a8dSllai1 is_devfsadm_thread(char *exec_name)
1836facf4a8dSllai1 {
1837facf4a8dSllai1 	/*
1838facf4a8dSllai1 	 * note: because devfsadmd -> /usr/sbin/devfsadm
1839facf4a8dSllai1 	 * it is safe to use "devfsadm" to capture the lookups
1840facf4a8dSllai1 	 * from devfsadm and its daemon version.
1841facf4a8dSllai1 	 */
1842facf4a8dSllai1 	if (strcmp(exec_name, "devfsadm") == 0)
1843facf4a8dSllai1 		return (1);
1844facf4a8dSllai1 	return (0);
1845facf4a8dSllai1 }
1846facf4a8dSllai1 
1847facf4a8dSllai1 /*
1848facf4a8dSllai1  * Lookup Order:
1849facf4a8dSllai1  *	sdev_node cache;
1850facf4a8dSllai1  *	backing store (SDEV_PERSIST);
1851facf4a8dSllai1  *	DBNR: a. dir_ops implemented in the loadable modules;
1852facf4a8dSllai1  *	      b. vnode ops in vtab.
1853facf4a8dSllai1  */
1854facf4a8dSllai1 int
1855facf4a8dSllai1 devname_lookup_func(struct sdev_node *ddv, char *nm, struct vnode **vpp,
1856facf4a8dSllai1     struct cred *cred, int (*callback)(struct sdev_node *, char *, void **,
1857facf4a8dSllai1     struct cred *, void *, char *), int flags)
1858facf4a8dSllai1 {
1859facf4a8dSllai1 	int rv = 0, nmlen;
1860facf4a8dSllai1 	struct vnode *rvp = NULL;
1861facf4a8dSllai1 	struct sdev_node *dv = NULL;
1862facf4a8dSllai1 	int	retried = 0;
1863facf4a8dSllai1 	int	error = 0;
1864facf4a8dSllai1 	struct vattr vattr;
1865facf4a8dSllai1 	char *lookup_thread = curproc->p_user.u_comm;
1866facf4a8dSllai1 	int failed_flags = 0;
1867facf4a8dSllai1 	int (*vtor)(struct sdev_node *) = NULL;
1868facf4a8dSllai1 	int state;
1869facf4a8dSllai1 	int parent_state;
1870facf4a8dSllai1 	char *link = NULL;
1871facf4a8dSllai1 
1872facf4a8dSllai1 	if (SDEVTOV(ddv)->v_type != VDIR)
1873facf4a8dSllai1 		return (ENOTDIR);
1874facf4a8dSllai1 
1875facf4a8dSllai1 	/*
1876facf4a8dSllai1 	 * Empty name or ., return node itself.
1877facf4a8dSllai1 	 */
1878facf4a8dSllai1 	nmlen = strlen(nm);
1879facf4a8dSllai1 	if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
1880facf4a8dSllai1 		*vpp = SDEVTOV(ddv);
1881facf4a8dSllai1 		VN_HOLD(*vpp);
1882facf4a8dSllai1 		return (0);
1883facf4a8dSllai1 	}
1884facf4a8dSllai1 
1885facf4a8dSllai1 	/*
1886facf4a8dSllai1 	 * .., return the parent directory
1887facf4a8dSllai1 	 */
1888facf4a8dSllai1 	if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
1889facf4a8dSllai1 		*vpp = SDEVTOV(ddv->sdev_dotdot);
1890facf4a8dSllai1 		VN_HOLD(*vpp);
1891facf4a8dSllai1 		return (0);
1892facf4a8dSllai1 	}
1893facf4a8dSllai1 
1894facf4a8dSllai1 	rw_enter(&ddv->sdev_contents, RW_READER);
1895facf4a8dSllai1 	if (ddv->sdev_flags & SDEV_VTOR) {
1896facf4a8dSllai1 		vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
1897facf4a8dSllai1 		ASSERT(vtor);
1898facf4a8dSllai1 	}
1899facf4a8dSllai1 
1900facf4a8dSllai1 tryagain:
1901facf4a8dSllai1 	/*
1902facf4a8dSllai1 	 * (a) directory cache lookup:
1903facf4a8dSllai1 	 */
1904facf4a8dSllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
1905facf4a8dSllai1 	parent_state = ddv->sdev_state;
1906facf4a8dSllai1 	dv = sdev_cache_lookup(ddv, nm);
1907facf4a8dSllai1 	if (dv) {
1908facf4a8dSllai1 		state = dv->sdev_state;
1909facf4a8dSllai1 		switch (state) {
1910facf4a8dSllai1 		case SDEV_INIT:
1911facf4a8dSllai1 			if (is_devfsadm_thread(lookup_thread))
1912facf4a8dSllai1 				break;
1913facf4a8dSllai1 
1914facf4a8dSllai1 			/* ZOMBIED parent won't allow node creation */
1915facf4a8dSllai1 			if (parent_state == SDEV_ZOMBIE) {
1916facf4a8dSllai1 				SD_TRACE_FAILED_LOOKUP(ddv, nm,
1917facf4a8dSllai1 				    retried);
1918facf4a8dSllai1 				goto nolock_notfound;
1919facf4a8dSllai1 			}
1920facf4a8dSllai1 
1921facf4a8dSllai1 			mutex_enter(&dv->sdev_lookup_lock);
1922facf4a8dSllai1 			/* compensate the threads started after devfsadm */
1923facf4a8dSllai1 			if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
1924facf4a8dSllai1 			    !(SDEV_IS_LOOKUP(dv)))
1925facf4a8dSllai1 				SDEV_BLOCK_OTHERS(dv,
1926facf4a8dSllai1 				    (SDEV_LOOKUP | SDEV_LGWAITING));
1927facf4a8dSllai1 
1928facf4a8dSllai1 			if (SDEV_IS_LOOKUP(dv)) {
1929facf4a8dSllai1 				failed_flags |= SLF_REBUILT;
1930facf4a8dSllai1 				rw_exit(&ddv->sdev_contents);
1931facf4a8dSllai1 				error = sdev_wait4lookup(dv, SDEV_LOOKUP);
1932facf4a8dSllai1 				mutex_exit(&dv->sdev_lookup_lock);
1933facf4a8dSllai1 				rw_enter(&ddv->sdev_contents, RW_READER);
1934facf4a8dSllai1 
1935facf4a8dSllai1 				if (error != 0) {
1936facf4a8dSllai1 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
1937facf4a8dSllai1 					    retried);
1938facf4a8dSllai1 					goto nolock_notfound;
1939facf4a8dSllai1 				}
1940facf4a8dSllai1 
1941facf4a8dSllai1 				state = dv->sdev_state;
1942facf4a8dSllai1 				if (state == SDEV_INIT) {
1943facf4a8dSllai1 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
1944facf4a8dSllai1 					    retried);
1945facf4a8dSllai1 					goto nolock_notfound;
1946facf4a8dSllai1 				} else if (state == SDEV_READY) {
1947facf4a8dSllai1 					goto found;
1948facf4a8dSllai1 				} else if (state == SDEV_ZOMBIE) {
1949facf4a8dSllai1 					rw_exit(&ddv->sdev_contents);
1950facf4a8dSllai1 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
1951facf4a8dSllai1 					    retried);
1952facf4a8dSllai1 					SDEV_RELE(dv);
1953facf4a8dSllai1 					goto lookup_failed;
1954facf4a8dSllai1 				}
1955facf4a8dSllai1 			} else {
1956facf4a8dSllai1 				mutex_exit(&dv->sdev_lookup_lock);
1957facf4a8dSllai1 			}
1958facf4a8dSllai1 			break;
1959facf4a8dSllai1 		case SDEV_READY:
1960facf4a8dSllai1 			goto found;
1961facf4a8dSllai1 		case SDEV_ZOMBIE:
1962facf4a8dSllai1 			rw_exit(&ddv->sdev_contents);
1963facf4a8dSllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1964facf4a8dSllai1 			SDEV_RELE(dv);
1965facf4a8dSllai1 			goto lookup_failed;
1966facf4a8dSllai1 		default:
1967facf4a8dSllai1 			rw_exit(&ddv->sdev_contents);
1968facf4a8dSllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1969facf4a8dSllai1 			sdev_lookup_failed(ddv, nm, failed_flags);
1970facf4a8dSllai1 			*vpp = NULLVP;
1971facf4a8dSllai1 			return (ENOENT);
1972facf4a8dSllai1 		}
1973facf4a8dSllai1 	}
1974facf4a8dSllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
1975facf4a8dSllai1 
1976facf4a8dSllai1 	/*
1977facf4a8dSllai1 	 * ZOMBIED parent does not allow new node creation.
1978facf4a8dSllai1 	 * bail out early
1979facf4a8dSllai1 	 */
1980facf4a8dSllai1 	if (parent_state == SDEV_ZOMBIE) {
1981facf4a8dSllai1 		rw_exit(&ddv->sdev_contents);
1982681d9761SEric Taylor 		*vpp = NULLVP;
1983facf4a8dSllai1 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
1984facf4a8dSllai1 		return (ENOENT);
1985facf4a8dSllai1 	}
1986facf4a8dSllai1 
1987facf4a8dSllai1 	/*
1988facf4a8dSllai1 	 * (b0): backing store lookup
1989facf4a8dSllai1 	 *	SDEV_PERSIST is default except:
1990facf4a8dSllai1 	 *		1) pts nodes
1991facf4a8dSllai1 	 *		2) non-chmod'ed local nodes
1992681d9761SEric Taylor 	 *		3) zvol nodes
1993facf4a8dSllai1 	 */
1994facf4a8dSllai1 	if (SDEV_IS_PERSIST(ddv)) {
1995facf4a8dSllai1 		error = devname_backstore_lookup(ddv, nm, &rvp);
1996facf4a8dSllai1 
1997facf4a8dSllai1 		if (!error) {
1998facf4a8dSllai1 
1999bb5fffbeSJerry Gilliam 			vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID;
2000da6c28aaSamw 			error = VOP_GETATTR(rvp, &vattr, 0, cred, NULL);
2001facf4a8dSllai1 			if (error) {
2002facf4a8dSllai1 				rw_exit(&ddv->sdev_contents);
2003facf4a8dSllai1 				if (dv)
2004facf4a8dSllai1 					SDEV_RELE(dv);
2005facf4a8dSllai1 				SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2006facf4a8dSllai1 				sdev_lookup_failed(ddv, nm, failed_flags);
2007facf4a8dSllai1 				*vpp = NULLVP;
2008facf4a8dSllai1 				return (ENOENT);
2009facf4a8dSllai1 			}
2010facf4a8dSllai1 
2011facf4a8dSllai1 			if (vattr.va_type == VLNK) {
2012facf4a8dSllai1 				error = sdev_getlink(rvp, &link);
2013facf4a8dSllai1 				if (error) {
2014facf4a8dSllai1 					rw_exit(&ddv->sdev_contents);
2015facf4a8dSllai1 					if (dv)
2016facf4a8dSllai1 						SDEV_RELE(dv);
2017facf4a8dSllai1 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
2018facf4a8dSllai1 					    retried);
2019facf4a8dSllai1 					sdev_lookup_failed(ddv, nm,
2020facf4a8dSllai1 					    failed_flags);
2021facf4a8dSllai1 					*vpp = NULLVP;
2022facf4a8dSllai1 					return (ENOENT);
2023facf4a8dSllai1 				}
2024facf4a8dSllai1 				ASSERT(link != NULL);
2025facf4a8dSllai1 			}
2026facf4a8dSllai1 
2027facf4a8dSllai1 			if (!rw_tryupgrade(&ddv->sdev_contents)) {
2028facf4a8dSllai1 				rw_exit(&ddv->sdev_contents);
2029facf4a8dSllai1 				rw_enter(&ddv->sdev_contents, RW_WRITER);
2030facf4a8dSllai1 			}
2031facf4a8dSllai1 			error = sdev_mknode(ddv, nm, &dv, &vattr,
2032facf4a8dSllai1 			    rvp, link, cred, SDEV_READY);
2033facf4a8dSllai1 			rw_downgrade(&ddv->sdev_contents);
2034facf4a8dSllai1 
2035facf4a8dSllai1 			if (link != NULL) {
2036facf4a8dSllai1 				kmem_free(link, strlen(link) + 1);
2037facf4a8dSllai1 				link = NULL;
2038facf4a8dSllai1 			}
2039facf4a8dSllai1 
2040facf4a8dSllai1 			if (error) {
2041facf4a8dSllai1 				SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2042facf4a8dSllai1 				rw_exit(&ddv->sdev_contents);
2043facf4a8dSllai1 				if (dv)
2044facf4a8dSllai1 					SDEV_RELE(dv);
2045facf4a8dSllai1 				goto lookup_failed;
2046facf4a8dSllai1 			} else {
2047facf4a8dSllai1 				goto found;
2048facf4a8dSllai1 			}
2049facf4a8dSllai1 		} else if (retried) {
2050facf4a8dSllai1 			rw_exit(&ddv->sdev_contents);
2051facf4a8dSllai1 			sdcmn_err3(("retry of lookup of %s/%s: failed\n",
2052facf4a8dSllai1 			    ddv->sdev_name, nm));
2053facf4a8dSllai1 			if (dv)
2054facf4a8dSllai1 				SDEV_RELE(dv);
2055facf4a8dSllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2056facf4a8dSllai1 			sdev_lookup_failed(ddv, nm, failed_flags);
2057facf4a8dSllai1 			*vpp = NULLVP;
2058facf4a8dSllai1 			return (ENOENT);
2059facf4a8dSllai1 		}
2060facf4a8dSllai1 	}
2061facf4a8dSllai1 
2062b127ac41SPhilip Kirk lookup_create_node:
2063facf4a8dSllai1 	/* first thread that is doing the lookup on this node */
2064681d9761SEric Taylor 	if (callback) {
2065681d9761SEric Taylor 		ASSERT(dv == NULL);
2066681d9761SEric Taylor 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
2067681d9761SEric Taylor 			rw_exit(&ddv->sdev_contents);
2068681d9761SEric Taylor 			rw_enter(&ddv->sdev_contents, RW_WRITER);
2069681d9761SEric Taylor 		}
2070681d9761SEric Taylor 		error = sdev_call_dircallback(ddv, &dv, nm, callback,
2071681d9761SEric Taylor 		    flags, cred);
2072681d9761SEric Taylor 		rw_downgrade(&ddv->sdev_contents);
2073681d9761SEric Taylor 		if (error == 0) {
2074681d9761SEric Taylor 			goto found;
2075681d9761SEric Taylor 		} else {
2076681d9761SEric Taylor 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2077681d9761SEric Taylor 			rw_exit(&ddv->sdev_contents);
2078681d9761SEric Taylor 			goto lookup_failed;
2079681d9761SEric Taylor 		}
2080681d9761SEric Taylor 	}
2081facf4a8dSllai1 	if (!dv) {
2082facf4a8dSllai1 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
2083facf4a8dSllai1 			rw_exit(&ddv->sdev_contents);
2084facf4a8dSllai1 			rw_enter(&ddv->sdev_contents, RW_WRITER);
2085facf4a8dSllai1 		}
2086facf4a8dSllai1 		error = sdev_mknode(ddv, nm, &dv, NULL, NULL, NULL,
2087facf4a8dSllai1 		    cred, SDEV_INIT);
2088facf4a8dSllai1 		if (!dv) {
2089facf4a8dSllai1 			rw_exit(&ddv->sdev_contents);
2090facf4a8dSllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2091facf4a8dSllai1 			sdev_lookup_failed(ddv, nm, failed_flags);
2092facf4a8dSllai1 			*vpp = NULLVP;
2093facf4a8dSllai1 			return (ENOENT);
2094facf4a8dSllai1 		}
2095facf4a8dSllai1 		rw_downgrade(&ddv->sdev_contents);
2096facf4a8dSllai1 	}
2097facf4a8dSllai1 
2098facf4a8dSllai1 	/*
2099facf4a8dSllai1 	 * (b1) invoking devfsadm once per life time for devfsadm nodes
2100facf4a8dSllai1 	 */
2101681d9761SEric Taylor 	ASSERT(SDEV_HELD(dv));
2102facf4a8dSllai1 
2103681d9761SEric Taylor 	if (SDEV_IS_NO_NCACHE(dv))
2104681d9761SEric Taylor 		failed_flags |= SLF_NO_NCACHE;
2105facf4a8dSllai1 	if (sdev_reconfig_boot || !i_ddi_io_initialized() ||
2106facf4a8dSllai1 	    SDEV_IS_DYNAMIC(ddv) || SDEV_IS_NO_NCACHE(dv) ||
2107facf4a8dSllai1 	    ((moddebug & MODDEBUG_FINI_EBUSY) != 0)) {
2108facf4a8dSllai1 		ASSERT(SDEV_HELD(dv));
2109facf4a8dSllai1 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2110facf4a8dSllai1 		goto nolock_notfound;
2111facf4a8dSllai1 	}
2112facf4a8dSllai1 
2113facf4a8dSllai1 	/*
2114facf4a8dSllai1 	 * filter out known non-existent devices recorded
2115facf4a8dSllai1 	 * during initial reconfiguration boot for which
2116facf4a8dSllai1 	 * reconfig should not be done and lookup may
2117facf4a8dSllai1 	 * be short-circuited now.
2118facf4a8dSllai1 	 */
2119facf4a8dSllai1 	if (sdev_lookup_filter(ddv, nm)) {
2120facf4a8dSllai1 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2121facf4a8dSllai1 		goto nolock_notfound;
2122facf4a8dSllai1 	}
2123facf4a8dSllai1 
2124facf4a8dSllai1 	/* bypassing devfsadm internal nodes */
2125facf4a8dSllai1 	if (is_devfsadm_thread(lookup_thread)) {
2126facf4a8dSllai1 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2127facf4a8dSllai1 		goto nolock_notfound;
2128facf4a8dSllai1 	}
2129facf4a8dSllai1 
2130facf4a8dSllai1 	if (sdev_reconfig_disable) {
2131facf4a8dSllai1 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2132facf4a8dSllai1 		goto nolock_notfound;
2133facf4a8dSllai1 	}
2134facf4a8dSllai1 
2135facf4a8dSllai1 	error = sdev_call_devfsadmd(ddv, dv, nm);
2136facf4a8dSllai1 	if (error == 0) {
2137facf4a8dSllai1 		sdcmn_err8(("lookup of %s/%s by %s: reconfig\n",
2138facf4a8dSllai1 		    ddv->sdev_name, nm, curproc->p_user.u_comm));
2139facf4a8dSllai1 		if (sdev_reconfig_verbose) {
2140facf4a8dSllai1 			cmn_err(CE_CONT,
2141facf4a8dSllai1 			    "?lookup of %s/%s by %s: reconfig\n",
2142facf4a8dSllai1 			    ddv->sdev_name, nm, curproc->p_user.u_comm);
2143facf4a8dSllai1 		}
2144facf4a8dSllai1 		retried = 1;
2145facf4a8dSllai1 		failed_flags |= SLF_REBUILT;
2146facf4a8dSllai1 		ASSERT(dv->sdev_state != SDEV_ZOMBIE);
2147facf4a8dSllai1 		SDEV_SIMPLE_RELE(dv);
2148facf4a8dSllai1 		goto tryagain;
2149facf4a8dSllai1 	} else {
2150facf4a8dSllai1 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2151facf4a8dSllai1 		goto nolock_notfound;
2152facf4a8dSllai1 	}
2153facf4a8dSllai1 
2154facf4a8dSllai1 found:
2155facf4a8dSllai1 	ASSERT(dv->sdev_state == SDEV_READY);
2156facf4a8dSllai1 	if (vtor) {
2157facf4a8dSllai1 		/*
2158facf4a8dSllai1 		 * Check validity of returned node
2159facf4a8dSllai1 		 */
2160facf4a8dSllai1 		switch (vtor(dv)) {
2161facf4a8dSllai1 		case SDEV_VTOR_VALID:
2162facf4a8dSllai1 			break;
2163b127ac41SPhilip Kirk 		case SDEV_VTOR_STALE:
2164b127ac41SPhilip Kirk 			/*
2165b127ac41SPhilip Kirk 			 * The name exists, but the cache entry is
2166b127ac41SPhilip Kirk 			 * stale and needs to be re-created.
2167b127ac41SPhilip Kirk 			 */
2168b127ac41SPhilip Kirk 			ASSERT(RW_READ_HELD(&ddv->sdev_contents));
2169b127ac41SPhilip Kirk 			if (rw_tryupgrade(&ddv->sdev_contents) == 0) {
2170b127ac41SPhilip Kirk 				rw_exit(&ddv->sdev_contents);
2171b127ac41SPhilip Kirk 				rw_enter(&ddv->sdev_contents, RW_WRITER);
2172b127ac41SPhilip Kirk 			}
2173*9e5aa9d8SRobert Mustacchi 			sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_DELETE);
2174b127ac41SPhilip Kirk 			rw_downgrade(&ddv->sdev_contents);
2175*9e5aa9d8SRobert Mustacchi 			SDEV_RELE(dv);
2176b127ac41SPhilip Kirk 			dv = NULL;
2177b127ac41SPhilip Kirk 			goto lookup_create_node;
2178b127ac41SPhilip Kirk 			/* FALLTHRU */
2179facf4a8dSllai1 		case SDEV_VTOR_INVALID:
2180facf4a8dSllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2181facf4a8dSllai1 			sdcmn_err7(("lookup: destroy invalid "
2182facf4a8dSllai1 			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
2183facf4a8dSllai1 			goto nolock_notfound;
2184facf4a8dSllai1 		case SDEV_VTOR_SKIP:
2185facf4a8dSllai1 			sdcmn_err7(("lookup: node not applicable - "
2186facf4a8dSllai1 			    "skipping: %s(%p)\n", dv->sdev_name, (void *)dv));
2187facf4a8dSllai1 			rw_exit(&ddv->sdev_contents);
2188facf4a8dSllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
2189facf4a8dSllai1 			SDEV_RELE(dv);
2190facf4a8dSllai1 			goto lookup_failed;
2191facf4a8dSllai1 		default:
2192facf4a8dSllai1 			cmn_err(CE_PANIC,
2193facf4a8dSllai1 			    "dev fs: validator failed: %s(%p)\n",
2194facf4a8dSllai1 			    dv->sdev_name, (void *)dv);
2195facf4a8dSllai1 			break;
2196facf4a8dSllai1 		}
2197facf4a8dSllai1 	}
2198facf4a8dSllai1 
2199facf4a8dSllai1 	rw_exit(&ddv->sdev_contents);
2200facf4a8dSllai1 	rv = sdev_to_vp(dv, vpp);
2201facf4a8dSllai1 	sdcmn_err3(("devname_lookup_func: returning vp %p v_count %d state %d "
2202facf4a8dSllai1 	    "for nm %s, error %d\n", (void *)*vpp, (*vpp)->v_count,
2203facf4a8dSllai1 	    dv->sdev_state, nm, rv));
2204facf4a8dSllai1 	return (rv);
2205facf4a8dSllai1 
2206facf4a8dSllai1 nolock_notfound:
2207facf4a8dSllai1 	/*
2208facf4a8dSllai1 	 * Destroy the node that is created for synchronization purposes.
2209facf4a8dSllai1 	 */
2210facf4a8dSllai1 	sdcmn_err3(("devname_lookup_func: %s with state %d\n",
2211facf4a8dSllai1 	    nm, dv->sdev_state));
2212facf4a8dSllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
2213facf4a8dSllai1 	if (dv->sdev_state == SDEV_INIT) {
2214facf4a8dSllai1 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
2215facf4a8dSllai1 			rw_exit(&ddv->sdev_contents);
2216facf4a8dSllai1 			rw_enter(&ddv->sdev_contents, RW_WRITER);
2217facf4a8dSllai1 		}
2218facf4a8dSllai1 
2219facf4a8dSllai1 		/*
2220facf4a8dSllai1 		 * Node state may have changed during the lock
2221facf4a8dSllai1 		 * changes. Re-check.
2222facf4a8dSllai1 		 */
2223facf4a8dSllai1 		if (dv->sdev_state == SDEV_INIT) {
2224*9e5aa9d8SRobert Mustacchi 			sdev_dirdelete(ddv, dv);
2225facf4a8dSllai1 			rw_exit(&ddv->sdev_contents);
2226facf4a8dSllai1 			sdev_lookup_failed(ddv, nm, failed_flags);
2227*9e5aa9d8SRobert Mustacchi 			SDEV_RELE(dv);
2228facf4a8dSllai1 			*vpp = NULL;
2229facf4a8dSllai1 			return (ENOENT);
2230facf4a8dSllai1 		}
2231facf4a8dSllai1 	}
2232facf4a8dSllai1 
2233facf4a8dSllai1 	rw_exit(&ddv->sdev_contents);
2234facf4a8dSllai1 	SDEV_RELE(dv);
2235facf4a8dSllai1 
2236facf4a8dSllai1 lookup_failed:
2237facf4a8dSllai1 	sdev_lookup_failed(ddv, nm, failed_flags);
2238facf4a8dSllai1 	*vpp = NULL;
2239facf4a8dSllai1 	return (ENOENT);
2240facf4a8dSllai1 }
2241facf4a8dSllai1 
2242facf4a8dSllai1 /*
2243facf4a8dSllai1  * Given a directory node, mark all nodes beneath as
2244facf4a8dSllai1  * STALE, i.e. nodes that don't exist as far as new
2245ecb4d93aSjg  * consumers are concerned.  Remove them from the
2246ecb4d93aSjg  * list of directory entries so that no lookup or
2247ecb4d93aSjg  * directory traversal will find them.  The node
2248ecb4d93aSjg  * not deallocated so existing holds are not affected.
2249facf4a8dSllai1  */
2250facf4a8dSllai1 void
2251facf4a8dSllai1 sdev_stale(struct sdev_node *ddv)
2252facf4a8dSllai1 {
2253facf4a8dSllai1 	struct sdev_node *dv;
2254facf4a8dSllai1 	struct vnode *vp;
2255facf4a8dSllai1 
2256facf4a8dSllai1 	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
2257facf4a8dSllai1 
2258facf4a8dSllai1 	rw_enter(&ddv->sdev_contents, RW_WRITER);
2259*9e5aa9d8SRobert Mustacchi 	while ((dv = SDEV_FIRST_ENTRY(ddv)) != NULL) {
2260facf4a8dSllai1 		vp = SDEVTOV(dv);
2261*9e5aa9d8SRobert Mustacchi 		SDEV_HOLD(dv);
2262facf4a8dSllai1 		if (vp->v_type == VDIR)
2263facf4a8dSllai1 			sdev_stale(dv);
2264facf4a8dSllai1 
2265*9e5aa9d8SRobert Mustacchi 		sdev_dirdelete(ddv, dv);
2266*9e5aa9d8SRobert Mustacchi 		SDEV_RELE(dv);
2267facf4a8dSllai1 	}
2268facf4a8dSllai1 	ddv->sdev_flags |= SDEV_BUILD;
2269facf4a8dSllai1 	rw_exit(&ddv->sdev_contents);
2270facf4a8dSllai1 }
2271facf4a8dSllai1 
2272facf4a8dSllai1 /*
2273facf4a8dSllai1  * Given a directory node, clean out all the nodes beneath.
2274facf4a8dSllai1  * If expr is specified, clean node with names matching expr.
2275facf4a8dSllai1  * If SDEV_ENFORCE is specified in flags, busy nodes are made stale,
2276facf4a8dSllai1  *	so they are excluded from future lookups.
2277facf4a8dSllai1  */
2278facf4a8dSllai1 int
2279facf4a8dSllai1 sdev_cleandir(struct sdev_node *ddv, char *expr, uint_t flags)
2280facf4a8dSllai1 {
2281facf4a8dSllai1 	int error = 0;
2282facf4a8dSllai1 	int busy = 0;
2283facf4a8dSllai1 	struct vnode *vp;
2284*9e5aa9d8SRobert Mustacchi 	struct sdev_node *dv;
2285facf4a8dSllai1 	int bkstore = 0;
2286facf4a8dSllai1 	int len = 0;
2287facf4a8dSllai1 	char *bks_name = NULL;
2288facf4a8dSllai1 
2289facf4a8dSllai1 	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
2290facf4a8dSllai1 
2291facf4a8dSllai1 	/*
2292facf4a8dSllai1 	 * We try our best to destroy all unused sdev_node's
2293facf4a8dSllai1 	 */
2294facf4a8dSllai1 	rw_enter(&ddv->sdev_contents, RW_WRITER);
2295*9e5aa9d8SRobert Mustacchi 	while ((dv = SDEV_FIRST_ENTRY(ddv)) != NULL) {
2296facf4a8dSllai1 		vp = SDEVTOV(dv);
2297facf4a8dSllai1 
2298facf4a8dSllai1 		if (expr && gmatch(dv->sdev_name, expr) == 0)
2299facf4a8dSllai1 			continue;
2300facf4a8dSllai1 
2301facf4a8dSllai1 		if (vp->v_type == VDIR &&
2302facf4a8dSllai1 		    sdev_cleandir(dv, NULL, flags) != 0) {
2303facf4a8dSllai1 			sdcmn_err9(("sdev_cleandir: dir %s busy\n",
2304facf4a8dSllai1 			    dv->sdev_name));
2305facf4a8dSllai1 			busy++;
2306facf4a8dSllai1 			continue;
2307facf4a8dSllai1 		}
2308facf4a8dSllai1 
2309facf4a8dSllai1 		if (vp->v_count > 0 && (flags & SDEV_ENFORCE) == 0) {
2310facf4a8dSllai1 			sdcmn_err9(("sdev_cleandir: dir %s busy\n",
2311facf4a8dSllai1 			    dv->sdev_name));
2312facf4a8dSllai1 			busy++;
2313facf4a8dSllai1 			continue;
2314facf4a8dSllai1 		}
2315facf4a8dSllai1 
2316facf4a8dSllai1 		/*
2317facf4a8dSllai1 		 * at this point, either dv is not held or SDEV_ENFORCE
2318facf4a8dSllai1 		 * is specified. In either case, dv needs to be deleted
2319facf4a8dSllai1 		 */
2320facf4a8dSllai1 		SDEV_HOLD(dv);
2321facf4a8dSllai1 
2322facf4a8dSllai1 		bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0;
2323facf4a8dSllai1 		if (bkstore && (vp->v_type == VDIR))
2324facf4a8dSllai1 			bkstore += 1;
2325facf4a8dSllai1 
2326facf4a8dSllai1 		if (bkstore) {
2327facf4a8dSllai1 			len = strlen(dv->sdev_name) + 1;
2328facf4a8dSllai1 			bks_name = kmem_alloc(len, KM_SLEEP);
2329facf4a8dSllai1 			bcopy(dv->sdev_name, bks_name, len);
2330facf4a8dSllai1 		}
2331facf4a8dSllai1 
2332*9e5aa9d8SRobert Mustacchi 		sdev_dirdelete(ddv, dv);
2333facf4a8dSllai1 
2334facf4a8dSllai1 		/* take care the backing store clean up */
2335*9e5aa9d8SRobert Mustacchi 		if (bkstore) {
2336facf4a8dSllai1 			ASSERT(bks_name);
2337facf4a8dSllai1 			ASSERT(ddv->sdev_attrvp);
2338facf4a8dSllai1 
2339facf4a8dSllai1 			if (bkstore == 1) {
2340facf4a8dSllai1 				error = VOP_REMOVE(ddv->sdev_attrvp,
2341da6c28aaSamw 				    bks_name, kcred, NULL, 0);
2342facf4a8dSllai1 			} else if (bkstore == 2) {
2343facf4a8dSllai1 				error = VOP_RMDIR(ddv->sdev_attrvp,
2344da6c28aaSamw 				    bks_name, ddv->sdev_attrvp, kcred, NULL, 0);
2345facf4a8dSllai1 			}
2346facf4a8dSllai1 
2347facf4a8dSllai1 			/* do not propagate the backing store errors */
2348facf4a8dSllai1 			if (error) {
2349facf4a8dSllai1 				sdcmn_err9(("sdev_cleandir: backing store"
2350facf4a8dSllai1 				    "not cleaned\n"));
2351facf4a8dSllai1 				error = 0;
2352facf4a8dSllai1 			}
2353facf4a8dSllai1 
2354facf4a8dSllai1 			bkstore = 0;
2355facf4a8dSllai1 			kmem_free(bks_name, len);
2356facf4a8dSllai1 			bks_name = NULL;
2357facf4a8dSllai1 			len = 0;
2358facf4a8dSllai1 		}
2359*9e5aa9d8SRobert Mustacchi 
2360*9e5aa9d8SRobert Mustacchi 		ddv->sdev_flags |= SDEV_BUILD;
2361*9e5aa9d8SRobert Mustacchi 		SDEV_RELE(dv);
2362facf4a8dSllai1 	}
2363facf4a8dSllai1 
2364facf4a8dSllai1 	ddv->sdev_flags |= SDEV_BUILD;
2365facf4a8dSllai1 	rw_exit(&ddv->sdev_contents);
2366facf4a8dSllai1 
2367facf4a8dSllai1 	if (busy) {
2368facf4a8dSllai1 		error = EBUSY;
2369facf4a8dSllai1 	}
2370facf4a8dSllai1 
2371facf4a8dSllai1 	return (error);
2372facf4a8dSllai1 }
2373facf4a8dSllai1 
2374facf4a8dSllai1 /*
2375facf4a8dSllai1  * a convenient wrapper for readdir() funcs
2376facf4a8dSllai1  */
2377facf4a8dSllai1 size_t
2378facf4a8dSllai1 add_dir_entry(dirent64_t *de, char *nm, size_t size, ino_t ino, offset_t off)
2379facf4a8dSllai1 {
2380facf4a8dSllai1 	size_t reclen = DIRENT64_RECLEN(strlen(nm));
2381facf4a8dSllai1 	if (reclen > size)
2382facf4a8dSllai1 		return (0);
2383facf4a8dSllai1 
2384facf4a8dSllai1 	de->d_ino = (ino64_t)ino;
2385facf4a8dSllai1 	de->d_off = (off64_t)off + 1;
2386facf4a8dSllai1 	de->d_reclen = (ushort_t)reclen;
2387facf4a8dSllai1 	(void) strncpy(de->d_name, nm, DIRENT64_NAMELEN(reclen));
2388facf4a8dSllai1 	return (reclen);
2389facf4a8dSllai1 }
2390facf4a8dSllai1 
2391facf4a8dSllai1 /*
2392facf4a8dSllai1  * sdev_mount service routines
2393facf4a8dSllai1  */
2394facf4a8dSllai1 int
2395facf4a8dSllai1 sdev_copyin_mountargs(struct mounta *uap, struct sdev_mountargs *args)
2396facf4a8dSllai1 {
2397facf4a8dSllai1 	int	error;
2398facf4a8dSllai1 
2399facf4a8dSllai1 	if (uap->datalen != sizeof (*args))
2400facf4a8dSllai1 		return (EINVAL);
2401facf4a8dSllai1 
2402facf4a8dSllai1 	if (error = copyin(uap->dataptr, args, sizeof (*args))) {
2403facf4a8dSllai1 		cmn_err(CE_WARN, "sdev_copyin_mountargs: can not"
2404facf4a8dSllai1 		    "get user data. error %d\n", error);
2405facf4a8dSllai1 		return (EFAULT);
2406facf4a8dSllai1 	}
2407facf4a8dSllai1 
2408facf4a8dSllai1 	return (0);
2409facf4a8dSllai1 }
2410facf4a8dSllai1 
2411facf4a8dSllai1 #ifdef nextdp
2412facf4a8dSllai1 #undef nextdp
2413facf4a8dSllai1 #endif
2414bc1009abSjg #define	nextdp(dp)	((struct dirent64 *) \
2415bc1009abSjg 			    (intptr_t)((char *)(dp) + (dp)->d_reclen))
2416facf4a8dSllai1 
2417facf4a8dSllai1 /*
2418facf4a8dSllai1  * readdir helper func
2419facf4a8dSllai1  */
2420facf4a8dSllai1 int
2421facf4a8dSllai1 devname_readdir_func(vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp,
2422facf4a8dSllai1     int flags)
2423facf4a8dSllai1 {
2424facf4a8dSllai1 	struct sdev_node *ddv = VTOSDEV(vp);
2425facf4a8dSllai1 	struct sdev_node *dv;
2426facf4a8dSllai1 	dirent64_t	*dp;
2427facf4a8dSllai1 	ulong_t		outcount = 0;
2428facf4a8dSllai1 	size_t		namelen;
2429facf4a8dSllai1 	ulong_t		alloc_count;
2430facf4a8dSllai1 	void		*outbuf;
2431facf4a8dSllai1 	struct iovec	*iovp;
2432facf4a8dSllai1 	int		error = 0;
2433facf4a8dSllai1 	size_t		reclen;
2434facf4a8dSllai1 	offset_t	diroff;
2435facf4a8dSllai1 	offset_t	soff;
2436facf4a8dSllai1 	int		this_reclen;
2437facf4a8dSllai1 	int (*vtor)(struct sdev_node *) = NULL;
2438facf4a8dSllai1 	struct vattr attr;
2439facf4a8dSllai1 	timestruc_t now;
2440facf4a8dSllai1 
2441facf4a8dSllai1 	ASSERT(ddv->sdev_attr || ddv->sdev_attrvp);
2442facf4a8dSllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
2443facf4a8dSllai1 
2444facf4a8dSllai1 	if (uiop->uio_loffset >= MAXOFF_T) {
2445facf4a8dSllai1 		if (eofp)
2446facf4a8dSllai1 			*eofp = 1;
2447facf4a8dSllai1 		return (0);
2448facf4a8dSllai1 	}
2449facf4a8dSllai1 
2450facf4a8dSllai1 	if (uiop->uio_iovcnt != 1)
2451facf4a8dSllai1 		return (EINVAL);
2452facf4a8dSllai1 
2453facf4a8dSllai1 	if (vp->v_type != VDIR)
2454facf4a8dSllai1 		return (ENOTDIR);
2455facf4a8dSllai1 
2456facf4a8dSllai1 	if (ddv->sdev_flags & SDEV_VTOR) {
2457facf4a8dSllai1 		vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
2458facf4a8dSllai1 		ASSERT(vtor);
2459facf4a8dSllai1 	}
2460facf4a8dSllai1 
2461facf4a8dSllai1 	if (eofp != NULL)
2462facf4a8dSllai1 		*eofp = 0;
2463facf4a8dSllai1 
2464bc1009abSjg 	soff = uiop->uio_loffset;
2465facf4a8dSllai1 	iovp = uiop->uio_iov;
2466facf4a8dSllai1 	alloc_count = iovp->iov_len;
2467facf4a8dSllai1 	dp = outbuf = kmem_alloc(alloc_count, KM_SLEEP);
2468facf4a8dSllai1 	outcount = 0;
2469facf4a8dSllai1 
2470facf4a8dSllai1 	if (ddv->sdev_state == SDEV_ZOMBIE)
2471facf4a8dSllai1 		goto get_cache;
2472facf4a8dSllai1 
2473b774fca8Sszhou 	if (SDEV_IS_GLOBAL(ddv)) {
2474facf4a8dSllai1 
24753c5e027bSEric Taylor 		if ((sdev_boot_state == SDEV_BOOT_STATE_COMPLETE) &&
2476facf4a8dSllai1 		    !sdev_reconfig_boot && (flags & SDEV_BROWSE) &&
2477facf4a8dSllai1 		    !SDEV_IS_DYNAMIC(ddv) && !SDEV_IS_NO_NCACHE(ddv) &&
2478facf4a8dSllai1 		    ((moddebug & MODDEBUG_FINI_EBUSY) == 0) &&
2479facf4a8dSllai1 		    !DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state) &&
2480facf4a8dSllai1 		    !DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
2481facf4a8dSllai1 		    !sdev_reconfig_disable) {
2482facf4a8dSllai1 			/*
2483facf4a8dSllai1 			 * invoking "devfsadm" to do system device reconfig
2484facf4a8dSllai1 			 */
2485facf4a8dSllai1 			mutex_enter(&ddv->sdev_lookup_lock);
2486facf4a8dSllai1 			SDEV_BLOCK_OTHERS(ddv,
2487facf4a8dSllai1 			    (SDEV_READDIR|SDEV_LGWAITING));
2488facf4a8dSllai1 			mutex_exit(&ddv->sdev_lookup_lock);
2489facf4a8dSllai1 
2490facf4a8dSllai1 			sdcmn_err8(("readdir of %s by %s: reconfig\n",
2491facf4a8dSllai1 			    ddv->sdev_path, curproc->p_user.u_comm));
2492facf4a8dSllai1 			if (sdev_reconfig_verbose) {
2493facf4a8dSllai1 				cmn_err(CE_CONT,
2494facf4a8dSllai1 				    "?readdir of %s by %s: reconfig\n",
2495facf4a8dSllai1 				    ddv->sdev_path, curproc->p_user.u_comm);
2496facf4a8dSllai1 			}
2497facf4a8dSllai1 
2498facf4a8dSllai1 			sdev_devfsadmd_thread(ddv, NULL, kcred);
2499facf4a8dSllai1 		} else if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) {
2500facf4a8dSllai1 			/*
2501facf4a8dSllai1 			 * compensate the "ls" started later than "devfsadm"
2502facf4a8dSllai1 			 */
2503facf4a8dSllai1 			mutex_enter(&ddv->sdev_lookup_lock);
2504facf4a8dSllai1 			SDEV_BLOCK_OTHERS(ddv, (SDEV_READDIR|SDEV_LGWAITING));
2505facf4a8dSllai1 			mutex_exit(&ddv->sdev_lookup_lock);
2506facf4a8dSllai1 		}
2507facf4a8dSllai1 
2508facf4a8dSllai1 		/*
2509facf4a8dSllai1 		 * release the contents lock so that
2510facf4a8dSllai1 		 * the cache may be updated by devfsadmd
2511facf4a8dSllai1 		 */
2512facf4a8dSllai1 		rw_exit(&ddv->sdev_contents);
2513facf4a8dSllai1 		mutex_enter(&ddv->sdev_lookup_lock);
2514facf4a8dSllai1 		if (SDEV_IS_READDIR(ddv))
2515facf4a8dSllai1 			(void) sdev_wait4lookup(ddv, SDEV_READDIR);
2516facf4a8dSllai1 		mutex_exit(&ddv->sdev_lookup_lock);
2517facf4a8dSllai1 		rw_enter(&ddv->sdev_contents, RW_READER);
2518facf4a8dSllai1 
2519facf4a8dSllai1 		sdcmn_err4(("readdir of directory %s by %s\n",
2520facf4a8dSllai1 		    ddv->sdev_name, curproc->p_user.u_comm));
25216b938478Sjg 		if (ddv->sdev_flags & SDEV_BUILD) {
2522facf4a8dSllai1 			if (SDEV_IS_PERSIST(ddv)) {
2523facf4a8dSllai1 				error = sdev_filldir_from_store(ddv,
2524facf4a8dSllai1 				    alloc_count, cred);
2525facf4a8dSllai1 			}
2526facf4a8dSllai1 			ddv->sdev_flags &= ~SDEV_BUILD;
2527facf4a8dSllai1 		}
2528facf4a8dSllai1 	}
2529facf4a8dSllai1 
2530facf4a8dSllai1 get_cache:
2531facf4a8dSllai1 	/* handle "." and ".." */
2532facf4a8dSllai1 	diroff = 0;
2533facf4a8dSllai1 	if (soff == 0) {
2534facf4a8dSllai1 		/* first time */
2535facf4a8dSllai1 		this_reclen = DIRENT64_RECLEN(1);
2536facf4a8dSllai1 		if (alloc_count < this_reclen) {
2537facf4a8dSllai1 			error = EINVAL;
2538facf4a8dSllai1 			goto done;
2539facf4a8dSllai1 		}
2540facf4a8dSllai1 
2541facf4a8dSllai1 		dp->d_ino = (ino64_t)ddv->sdev_ino;
2542facf4a8dSllai1 		dp->d_off = (off64_t)1;
2543facf4a8dSllai1 		dp->d_reclen = (ushort_t)this_reclen;
2544facf4a8dSllai1 
2545facf4a8dSllai1 		(void) strncpy(dp->d_name, ".",
2546facf4a8dSllai1 		    DIRENT64_NAMELEN(this_reclen));
2547facf4a8dSllai1 		outcount += dp->d_reclen;
2548facf4a8dSllai1 		dp = nextdp(dp);
2549facf4a8dSllai1 	}
2550facf4a8dSllai1 
2551facf4a8dSllai1 	diroff++;
2552facf4a8dSllai1 	if (soff <= 1) {
2553facf4a8dSllai1 		this_reclen = DIRENT64_RECLEN(2);
2554facf4a8dSllai1 		if (alloc_count < outcount + this_reclen) {
2555facf4a8dSllai1 			error = EINVAL;
2556facf4a8dSllai1 			goto done;
2557facf4a8dSllai1 		}
2558facf4a8dSllai1 
2559facf4a8dSllai1 		dp->d_reclen = (ushort_t)this_reclen;
2560facf4a8dSllai1 		dp->d_ino = (ino64_t)ddv->sdev_dotdot->sdev_ino;
2561facf4a8dSllai1 		dp->d_off = (off64_t)2;
2562facf4a8dSllai1 
2563facf4a8dSllai1 		(void) strncpy(dp->d_name, "..",
2564facf4a8dSllai1 		    DIRENT64_NAMELEN(this_reclen));
2565facf4a8dSllai1 		outcount += dp->d_reclen;
2566facf4a8dSllai1 
2567facf4a8dSllai1 		dp = nextdp(dp);
2568facf4a8dSllai1 	}
2569facf4a8dSllai1 
2570facf4a8dSllai1 
2571facf4a8dSllai1 	/* gets the cache */
2572facf4a8dSllai1 	diroff++;
2573aac43a5fSjg 	for (dv = SDEV_FIRST_ENTRY(ddv); dv;
2574aac43a5fSjg 	    dv = SDEV_NEXT_ENTRY(ddv, dv), diroff++) {
2575facf4a8dSllai1 		sdcmn_err3(("sdev_readdir: diroff %lld soff %lld for '%s' \n",
2576facf4a8dSllai1 		    diroff, soff, dv->sdev_name));
2577facf4a8dSllai1 
2578facf4a8dSllai1 		/* bypassing pre-matured nodes */
2579facf4a8dSllai1 		if (diroff < soff || (dv->sdev_state != SDEV_READY)) {
2580facf4a8dSllai1 			sdcmn_err3(("sdev_readdir: pre-mature node  "
2581681d9761SEric Taylor 			    "%s %d\n", dv->sdev_name, dv->sdev_state));
2582facf4a8dSllai1 			continue;
2583facf4a8dSllai1 		}
2584facf4a8dSllai1 
2585facf4a8dSllai1 		/*
2586facf4a8dSllai1 		 * Check validity of node
25872b080a34SJerry Gilliam 		 * Drop invalid and nodes to be skipped.
25882b080a34SJerry Gilliam 		 * A node the validator indicates as stale needs
25892b080a34SJerry Gilliam 		 * to be returned as presumably the node name itself
25902b080a34SJerry Gilliam 		 * is valid and the node data itself will be refreshed
25912b080a34SJerry Gilliam 		 * on lookup.  An application performing a readdir then
25922b080a34SJerry Gilliam 		 * stat on each entry should thus always see consistent
25932b080a34SJerry Gilliam 		 * data.  In any case, it is not possible to synchronize
25942b080a34SJerry Gilliam 		 * with dynamic kernel state, and any view we return can
25952b080a34SJerry Gilliam 		 * never be anything more than a snapshot at a point in time.
2596facf4a8dSllai1 		 */
2597facf4a8dSllai1 		if (vtor) {
2598facf4a8dSllai1 			switch (vtor(dv)) {
2599facf4a8dSllai1 			case SDEV_VTOR_VALID:
2600facf4a8dSllai1 				break;
2601facf4a8dSllai1 			case SDEV_VTOR_INVALID:
2602facf4a8dSllai1 			case SDEV_VTOR_SKIP:
2603facf4a8dSllai1 				continue;
26042b080a34SJerry Gilliam 			case SDEV_VTOR_STALE:
26052b080a34SJerry Gilliam 				sdcmn_err3(("sdev_readir: %s stale\n",
26062b080a34SJerry Gilliam 				    dv->sdev_name));
26072b080a34SJerry Gilliam 				break;
2608facf4a8dSllai1 			default:
2609facf4a8dSllai1 				cmn_err(CE_PANIC,
2610facf4a8dSllai1 				    "dev fs: validator failed: %s(%p)\n",
2611facf4a8dSllai1 				    dv->sdev_name, (void *)dv);
2612facf4a8dSllai1 				break;
2613facf4a8dSllai1 			/*NOTREACHED*/
2614facf4a8dSllai1 			}
2615facf4a8dSllai1 		}
2616facf4a8dSllai1 
2617facf4a8dSllai1 		namelen = strlen(dv->sdev_name);
2618facf4a8dSllai1 		reclen = DIRENT64_RECLEN(namelen);
2619facf4a8dSllai1 		if (outcount + reclen > alloc_count) {
2620facf4a8dSllai1 			goto full;
2621facf4a8dSllai1 		}
2622facf4a8dSllai1 		dp->d_reclen = (ushort_t)reclen;
2623facf4a8dSllai1 		dp->d_ino = (ino64_t)dv->sdev_ino;
2624facf4a8dSllai1 		dp->d_off = (off64_t)diroff + 1;
2625facf4a8dSllai1 		(void) strncpy(dp->d_name, dv->sdev_name,
2626facf4a8dSllai1 		    DIRENT64_NAMELEN(reclen));
2627facf4a8dSllai1 		outcount += reclen;
2628facf4a8dSllai1 		dp = nextdp(dp);
2629facf4a8dSllai1 	}
2630facf4a8dSllai1 
2631facf4a8dSllai1 full:
2632facf4a8dSllai1 	sdcmn_err4(("sdev_readdir: moving %lu bytes: "
2633facf4a8dSllai1 	    "diroff %lld, soff %lld, dv %p\n", outcount, diroff, soff,
2634facf4a8dSllai1 	    (void *)dv));
2635facf4a8dSllai1 
2636facf4a8dSllai1 	if (outcount)
2637facf4a8dSllai1 		error = uiomove(outbuf, outcount, UIO_READ, uiop);
2638facf4a8dSllai1 
2639facf4a8dSllai1 	if (!error) {
2640bc1009abSjg 		uiop->uio_loffset = diroff;
2641facf4a8dSllai1 		if (eofp)
2642facf4a8dSllai1 			*eofp = dv ? 0 : 1;
2643facf4a8dSllai1 	}
2644facf4a8dSllai1 
2645facf4a8dSllai1 
2646facf4a8dSllai1 	if (ddv->sdev_attrvp) {
2647facf4a8dSllai1 		gethrestime(&now);
2648facf4a8dSllai1 		attr.va_ctime = now;
2649facf4a8dSllai1 		attr.va_atime = now;
2650facf4a8dSllai1 		attr.va_mask = AT_CTIME|AT_ATIME;
2651facf4a8dSllai1 
2652facf4a8dSllai1 		(void) VOP_SETATTR(ddv->sdev_attrvp, &attr, 0, kcred, NULL);
2653facf4a8dSllai1 	}
2654facf4a8dSllai1 done:
2655facf4a8dSllai1 	kmem_free(outbuf, alloc_count);
2656facf4a8dSllai1 	return (error);
2657facf4a8dSllai1 }
2658facf4a8dSllai1 
2659facf4a8dSllai1 static int
2660facf4a8dSllai1 sdev_modctl_lookup(const char *path, vnode_t **r_vp)
2661facf4a8dSllai1 {
2662facf4a8dSllai1 	vnode_t *vp;
2663facf4a8dSllai1 	vnode_t *cvp;
2664facf4a8dSllai1 	struct sdev_node *svp;
2665facf4a8dSllai1 	char *nm;
2666facf4a8dSllai1 	struct pathname pn;
2667facf4a8dSllai1 	int error;
2668facf4a8dSllai1 	int persisted = 0;
2669facf4a8dSllai1 
267074bb9a80SJerry Gilliam 	ASSERT(INGLOBALZONE(curproc));
267174bb9a80SJerry Gilliam 
2672facf4a8dSllai1 	if (error = pn_get((char *)path, UIO_SYSSPACE, &pn))
2673facf4a8dSllai1 		return (error);
2674facf4a8dSllai1 	nm = kmem_alloc(MAXNAMELEN, KM_SLEEP);
2675facf4a8dSllai1 
2676facf4a8dSllai1 	vp = rootdir;
2677facf4a8dSllai1 	VN_HOLD(vp);
2678facf4a8dSllai1 
2679facf4a8dSllai1 	while (pn_pathleft(&pn)) {
268074bb9a80SJerry Gilliam 		ASSERT(vp->v_type == VDIR || vp->v_type == VLNK);
2681facf4a8dSllai1 		(void) pn_getcomponent(&pn, nm);
268274bb9a80SJerry Gilliam 
268374bb9a80SJerry Gilliam 		/*
268474bb9a80SJerry Gilliam 		 * Deal with the .. special case where we may be
268574bb9a80SJerry Gilliam 		 * traversing up across a mount point, to the
268674bb9a80SJerry Gilliam 		 * root of this filesystem or global root.
268774bb9a80SJerry Gilliam 		 */
268874bb9a80SJerry Gilliam 		if (nm[0] == '.' && nm[1] == '.' && nm[2] == 0) {
268974bb9a80SJerry Gilliam checkforroot:
269074bb9a80SJerry Gilliam 			if (VN_CMP(vp, rootdir)) {
269174bb9a80SJerry Gilliam 				nm[1] = 0;
269274bb9a80SJerry Gilliam 			} else if (vp->v_flag & VROOT) {
269374bb9a80SJerry Gilliam 				vfs_t *vfsp;
269474bb9a80SJerry Gilliam 				cvp = vp;
269574bb9a80SJerry Gilliam 				vfsp = cvp->v_vfsp;
269674bb9a80SJerry Gilliam 				vfs_rlock_wait(vfsp);
269774bb9a80SJerry Gilliam 				vp = cvp->v_vfsp->vfs_vnodecovered;
269874bb9a80SJerry Gilliam 				if (vp == NULL ||
269974bb9a80SJerry Gilliam 				    (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) {
270074bb9a80SJerry Gilliam 					vfs_unlock(vfsp);
270174bb9a80SJerry Gilliam 					VN_RELE(cvp);
270274bb9a80SJerry Gilliam 					error = EIO;
270374bb9a80SJerry Gilliam 					break;
270474bb9a80SJerry Gilliam 				}
270574bb9a80SJerry Gilliam 				VN_HOLD(vp);
270674bb9a80SJerry Gilliam 				vfs_unlock(vfsp);
270774bb9a80SJerry Gilliam 				VN_RELE(cvp);
270874bb9a80SJerry Gilliam 				cvp = NULL;
270974bb9a80SJerry Gilliam 				goto checkforroot;
271074bb9a80SJerry Gilliam 			}
271174bb9a80SJerry Gilliam 		}
271274bb9a80SJerry Gilliam 
2713da6c28aaSamw 		error = VOP_LOOKUP(vp, nm, &cvp, NULL, 0, NULL, kcred, NULL,
2714da6c28aaSamw 		    NULL, NULL);
271574bb9a80SJerry Gilliam 		if (error) {
2716facf4a8dSllai1 			VN_RELE(vp);
2717facf4a8dSllai1 			break;
271874bb9a80SJerry Gilliam 		}
2719facf4a8dSllai1 
2720facf4a8dSllai1 		/* traverse mount points encountered on our journey */
2721facf4a8dSllai1 		if (vn_ismntpt(cvp) && (error = traverse(&cvp)) != 0) {
272274bb9a80SJerry Gilliam 			VN_RELE(vp);
2723facf4a8dSllai1 			VN_RELE(cvp);
2724facf4a8dSllai1 			break;
2725facf4a8dSllai1 		}
2726facf4a8dSllai1 
2727facf4a8dSllai1 		/*
272874bb9a80SJerry Gilliam 		 * symbolic link, can be either relative and absolute
272974bb9a80SJerry Gilliam 		 */
273074bb9a80SJerry Gilliam 		if ((cvp->v_type == VLNK) && pn_pathleft(&pn)) {
273174bb9a80SJerry Gilliam 			struct pathname linkpath;
273274bb9a80SJerry Gilliam 			pn_alloc(&linkpath);
273374bb9a80SJerry Gilliam 			if (error = pn_getsymlink(cvp, &linkpath, kcred)) {
273474bb9a80SJerry Gilliam 				pn_free(&linkpath);
273574bb9a80SJerry Gilliam 				break;
273674bb9a80SJerry Gilliam 			}
273774bb9a80SJerry Gilliam 			if (pn_pathleft(&linkpath) == 0)
273874bb9a80SJerry Gilliam 				(void) pn_set(&linkpath, ".");
273974bb9a80SJerry Gilliam 			error = pn_insert(&pn, &linkpath, strlen(nm));
274074bb9a80SJerry Gilliam 			pn_free(&linkpath);
274174bb9a80SJerry Gilliam 			if (pn.pn_pathlen == 0) {
274274bb9a80SJerry Gilliam 				VN_RELE(vp);
274374bb9a80SJerry Gilliam 				return (ENOENT);
274474bb9a80SJerry Gilliam 			}
274574bb9a80SJerry Gilliam 			if (pn.pn_path[0] == '/') {
274674bb9a80SJerry Gilliam 				pn_skipslash(&pn);
274774bb9a80SJerry Gilliam 				VN_RELE(vp);
274874bb9a80SJerry Gilliam 				VN_RELE(cvp);
274974bb9a80SJerry Gilliam 				vp = rootdir;
275074bb9a80SJerry Gilliam 				VN_HOLD(vp);
275174bb9a80SJerry Gilliam 			} else {
275274bb9a80SJerry Gilliam 				VN_RELE(cvp);
275374bb9a80SJerry Gilliam 			}
275474bb9a80SJerry Gilliam 			continue;
275574bb9a80SJerry Gilliam 		}
275674bb9a80SJerry Gilliam 
275774bb9a80SJerry Gilliam 		VN_RELE(vp);
275874bb9a80SJerry Gilliam 
275974bb9a80SJerry Gilliam 		/*
2760facf4a8dSllai1 		 * Direct the operation to the persisting filesystem
2761facf4a8dSllai1 		 * underlying /dev.  Bail if we encounter a
2762facf4a8dSllai1 		 * non-persistent dev entity here.
2763facf4a8dSllai1 		 */
2764facf4a8dSllai1 		if (cvp->v_vfsp->vfs_fstype == devtype) {
2765facf4a8dSllai1 
2766facf4a8dSllai1 			if ((VTOSDEV(cvp)->sdev_flags & SDEV_PERSIST) == 0) {
2767facf4a8dSllai1 				error = ENOENT;
2768facf4a8dSllai1 				VN_RELE(cvp);
2769facf4a8dSllai1 				break;
2770facf4a8dSllai1 			}
2771facf4a8dSllai1 
2772facf4a8dSllai1 			if (VTOSDEV(cvp) == NULL) {
2773facf4a8dSllai1 				error = ENOENT;
2774facf4a8dSllai1 				VN_RELE(cvp);
2775facf4a8dSllai1 				break;
2776facf4a8dSllai1 			}
2777facf4a8dSllai1 			svp = VTOSDEV(cvp);
2778facf4a8dSllai1 			if ((vp = svp->sdev_attrvp) == NULL) {
2779facf4a8dSllai1 				error = ENOENT;
2780facf4a8dSllai1 				VN_RELE(cvp);
2781facf4a8dSllai1 				break;
2782facf4a8dSllai1 			}
2783facf4a8dSllai1 			persisted = 1;
2784facf4a8dSllai1 			VN_HOLD(vp);
2785facf4a8dSllai1 			VN_RELE(cvp);
2786facf4a8dSllai1 			cvp = vp;
2787facf4a8dSllai1 		}
2788facf4a8dSllai1 
2789facf4a8dSllai1 		vp = cvp;
2790facf4a8dSllai1 		pn_skipslash(&pn);
2791facf4a8dSllai1 	}
2792facf4a8dSllai1 
2793facf4a8dSllai1 	kmem_free(nm, MAXNAMELEN);
2794facf4a8dSllai1 	pn_free(&pn);
2795facf4a8dSllai1 
2796facf4a8dSllai1 	if (error)
2797facf4a8dSllai1 		return (error);
2798facf4a8dSllai1 
2799facf4a8dSllai1 	/*
2800facf4a8dSllai1 	 * Only return persisted nodes in the filesystem underlying /dev.
2801facf4a8dSllai1 	 */
2802facf4a8dSllai1 	if (!persisted) {
2803facf4a8dSllai1 		VN_RELE(vp);
2804facf4a8dSllai1 		return (ENOENT);
2805facf4a8dSllai1 	}
2806facf4a8dSllai1 
2807facf4a8dSllai1 	*r_vp = vp;
2808facf4a8dSllai1 	return (0);
2809facf4a8dSllai1 }
2810facf4a8dSllai1 
2811facf4a8dSllai1 int
2812facf4a8dSllai1 sdev_modctl_readdir(const char *dir, char ***dirlistp,
2813e37c6c37Scth 	int *npathsp, int *npathsp_alloc, int checking_empty)
2814facf4a8dSllai1 {
2815facf4a8dSllai1 	char	**pathlist = NULL;
2816facf4a8dSllai1 	char	**newlist = NULL;
2817facf4a8dSllai1 	int	npaths = 0;
2818facf4a8dSllai1 	int	npaths_alloc = 0;
2819facf4a8dSllai1 	dirent64_t *dbuf = NULL;
2820facf4a8dSllai1 	int	n;
2821facf4a8dSllai1 	char	*s;
2822facf4a8dSllai1 	int error;
2823facf4a8dSllai1 	vnode_t *vp;
2824facf4a8dSllai1 	int eof;
2825facf4a8dSllai1 	struct iovec iov;
2826facf4a8dSllai1 	struct uio uio;
2827facf4a8dSllai1 	struct dirent64 *dp;
2828facf4a8dSllai1 	size_t dlen;
2829facf4a8dSllai1 	size_t dbuflen;
2830facf4a8dSllai1 	int ndirents = 64;
2831facf4a8dSllai1 	char *nm;
2832facf4a8dSllai1 
2833facf4a8dSllai1 	error = sdev_modctl_lookup(dir, &vp);
2834facf4a8dSllai1 	sdcmn_err11(("modctl readdir: %s by %s: %s\n",
2835facf4a8dSllai1 	    dir, curproc->p_user.u_comm,
2836facf4a8dSllai1 	    (error == 0) ? "ok" : "failed"));
2837facf4a8dSllai1 	if (error)
2838facf4a8dSllai1 		return (error);
2839facf4a8dSllai1 
2840facf4a8dSllai1 	dlen = ndirents * (sizeof (*dbuf));
2841facf4a8dSllai1 	dbuf = kmem_alloc(dlen, KM_SLEEP);
2842facf4a8dSllai1 
2843facf4a8dSllai1 	uio.uio_iov = &iov;
2844facf4a8dSllai1 	uio.uio_iovcnt = 1;
2845facf4a8dSllai1 	uio.uio_segflg = UIO_SYSSPACE;
2846facf4a8dSllai1 	uio.uio_fmode = 0;
2847facf4a8dSllai1 	uio.uio_extflg = UIO_COPY_CACHED;
2848facf4a8dSllai1 	uio.uio_loffset = 0;
2849facf4a8dSllai1 	uio.uio_llimit = MAXOFFSET_T;
2850facf4a8dSllai1 
2851facf4a8dSllai1 	eof = 0;
2852facf4a8dSllai1 	error = 0;
2853facf4a8dSllai1 	while (!error && !eof) {
2854facf4a8dSllai1 		uio.uio_resid = dlen;
2855facf4a8dSllai1 		iov.iov_base = (char *)dbuf;
2856facf4a8dSllai1 		iov.iov_len = dlen;
2857facf4a8dSllai1 
2858facf4a8dSllai1 		(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
2859da6c28aaSamw 		error = VOP_READDIR(vp, &uio, kcred, &eof, NULL, 0);
2860facf4a8dSllai1 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
2861facf4a8dSllai1 
2862facf4a8dSllai1 		dbuflen = dlen - uio.uio_resid;
2863facf4a8dSllai1 
2864facf4a8dSllai1 		if (error || dbuflen == 0)
2865facf4a8dSllai1 			break;
2866facf4a8dSllai1 
2867facf4a8dSllai1 		for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen);
2868facf4a8dSllai1 		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
2869facf4a8dSllai1 
2870facf4a8dSllai1 			nm = dp->d_name;
2871facf4a8dSllai1 
2872facf4a8dSllai1 			if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0)
2873facf4a8dSllai1 				continue;
2874facf4a8dSllai1 			if (npaths == npaths_alloc) {
2875facf4a8dSllai1 				npaths_alloc += 64;
2876facf4a8dSllai1 				newlist = (char **)
2877facf4a8dSllai1 				    kmem_zalloc((npaths_alloc + 1) *
2878facf4a8dSllai1 				    sizeof (char *), KM_SLEEP);
2879facf4a8dSllai1 				if (pathlist) {
2880facf4a8dSllai1 					bcopy(pathlist, newlist,
2881facf4a8dSllai1 					    npaths * sizeof (char *));
2882facf4a8dSllai1 					kmem_free(pathlist,
2883facf4a8dSllai1 					    (npaths + 1) * sizeof (char *));
2884facf4a8dSllai1 				}
2885facf4a8dSllai1 				pathlist = newlist;
2886facf4a8dSllai1 			}
2887facf4a8dSllai1 			n = strlen(nm) + 1;
2888facf4a8dSllai1 			s = kmem_alloc(n, KM_SLEEP);
2889facf4a8dSllai1 			bcopy(nm, s, n);
2890facf4a8dSllai1 			pathlist[npaths++] = s;
2891facf4a8dSllai1 			sdcmn_err11(("  %s/%s\n", dir, s));
2892e37c6c37Scth 
2893e37c6c37Scth 			/* if checking empty, one entry is as good as many */
2894e37c6c37Scth 			if (checking_empty) {
2895e37c6c37Scth 				eof = 1;
2896e37c6c37Scth 				break;
2897e37c6c37Scth 			}
2898facf4a8dSllai1 		}
2899facf4a8dSllai1 	}
2900facf4a8dSllai1 
2901facf4a8dSllai1 exit:
2902facf4a8dSllai1 	VN_RELE(vp);
2903facf4a8dSllai1 
2904facf4a8dSllai1 	if (dbuf)
2905facf4a8dSllai1 		kmem_free(dbuf, dlen);
2906facf4a8dSllai1 
2907facf4a8dSllai1 	if (error)
2908facf4a8dSllai1 		return (error);
2909facf4a8dSllai1 
2910facf4a8dSllai1 	*dirlistp = pathlist;
2911facf4a8dSllai1 	*npathsp = npaths;
2912facf4a8dSllai1 	*npathsp_alloc = npaths_alloc;
2913facf4a8dSllai1 
2914facf4a8dSllai1 	return (0);
2915facf4a8dSllai1 }
2916facf4a8dSllai1 
2917facf4a8dSllai1 void
2918facf4a8dSllai1 sdev_modctl_readdir_free(char **pathlist, int npaths, int npaths_alloc)
2919facf4a8dSllai1 {
2920facf4a8dSllai1 	int	i, n;
2921facf4a8dSllai1 
2922facf4a8dSllai1 	for (i = 0; i < npaths; i++) {
2923facf4a8dSllai1 		n = strlen(pathlist[i]) + 1;
2924facf4a8dSllai1 		kmem_free(pathlist[i], n);
2925facf4a8dSllai1 	}
2926facf4a8dSllai1 
2927facf4a8dSllai1 	kmem_free(pathlist, (npaths_alloc + 1) * sizeof (char *));
2928facf4a8dSllai1 }
2929facf4a8dSllai1 
2930facf4a8dSllai1 int
2931facf4a8dSllai1 sdev_modctl_devexists(const char *path)
2932facf4a8dSllai1 {
2933facf4a8dSllai1 	vnode_t *vp;
2934facf4a8dSllai1 	int error;
2935facf4a8dSllai1 
2936facf4a8dSllai1 	error = sdev_modctl_lookup(path, &vp);
2937facf4a8dSllai1 	sdcmn_err11(("modctl dev exists: %s by %s: %s\n",
2938facf4a8dSllai1 	    path, curproc->p_user.u_comm,
2939facf4a8dSllai1 	    (error == 0) ? "ok" : "failed"));
2940facf4a8dSllai1 	if (error == 0)
2941facf4a8dSllai1 		VN_RELE(vp);
2942facf4a8dSllai1 
2943facf4a8dSllai1 	return (error);
2944facf4a8dSllai1 }
2945facf4a8dSllai1 
2946facf4a8dSllai1 extern int sdev_vnodeops_tbl_size;
2947facf4a8dSllai1 
2948facf4a8dSllai1 /*
2949facf4a8dSllai1  * construct a new template with overrides from vtab
2950facf4a8dSllai1  */
2951facf4a8dSllai1 static fs_operation_def_t *
2952facf4a8dSllai1 sdev_merge_vtab(const fs_operation_def_t tab[])
2953facf4a8dSllai1 {
2954facf4a8dSllai1 	fs_operation_def_t *new;
2955facf4a8dSllai1 	const fs_operation_def_t *tab_entry;
2956facf4a8dSllai1 
2957facf4a8dSllai1 	/* make a copy of standard vnode ops table */
2958facf4a8dSllai1 	new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP);
2959facf4a8dSllai1 	bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size);
2960facf4a8dSllai1 
2961facf4a8dSllai1 	/* replace the overrides from tab */
2962facf4a8dSllai1 	for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) {
2963facf4a8dSllai1 		fs_operation_def_t *std_entry = new;
2964facf4a8dSllai1 		while (std_entry->name) {
2965facf4a8dSllai1 			if (strcmp(tab_entry->name, std_entry->name) == 0) {
2966facf4a8dSllai1 				std_entry->func = tab_entry->func;
2967facf4a8dSllai1 				break;
2968facf4a8dSllai1 			}
2969facf4a8dSllai1 			std_entry++;
2970facf4a8dSllai1 		}
2971facf4a8dSllai1 		if (std_entry->name == NULL)
2972facf4a8dSllai1 			cmn_err(CE_NOTE, "sdev_merge_vtab: entry %s unused.",
2973facf4a8dSllai1 			    tab_entry->name);
2974facf4a8dSllai1 	}
2975facf4a8dSllai1 
2976facf4a8dSllai1 	return (new);
2977facf4a8dSllai1 }
2978facf4a8dSllai1 
2979facf4a8dSllai1 /* free memory allocated by sdev_merge_vtab */
2980facf4a8dSllai1 static void
2981facf4a8dSllai1 sdev_free_vtab(fs_operation_def_t *new)
2982facf4a8dSllai1 {
2983facf4a8dSllai1 	kmem_free(new, sdev_vnodeops_tbl_size);
2984facf4a8dSllai1 }
2985facf4a8dSllai1 
2986facf4a8dSllai1 /*
2987facf4a8dSllai1  * a generic setattr() function
2988facf4a8dSllai1  *
2989facf4a8dSllai1  * note: flags only supports AT_UID and AT_GID.
2990facf4a8dSllai1  *	 Future enhancements can be done for other types, e.g. AT_MODE
2991facf4a8dSllai1  */
2992facf4a8dSllai1 int
2993facf4a8dSllai1 devname_setattr_func(struct vnode *vp, struct vattr *vap, int flags,
2994facf4a8dSllai1     struct cred *cred, int (*callback)(struct sdev_node *, struct vattr *,
2995facf4a8dSllai1     int), int protocol)
2996facf4a8dSllai1 {
2997facf4a8dSllai1 	struct sdev_node	*dv = VTOSDEV(vp);
2998facf4a8dSllai1 	struct sdev_node	*parent = dv->sdev_dotdot;
2999facf4a8dSllai1 	struct vattr		*get;
3000facf4a8dSllai1 	uint_t			mask = vap->va_mask;
3001facf4a8dSllai1 	int 			error;
3002facf4a8dSllai1 
3003facf4a8dSllai1 	/* some sanity checks */
3004facf4a8dSllai1 	if (vap->va_mask & AT_NOSET)
3005facf4a8dSllai1 		return (EINVAL);
3006facf4a8dSllai1 
3007facf4a8dSllai1 	if (vap->va_mask & AT_SIZE) {
3008facf4a8dSllai1 		if (vp->v_type == VDIR) {
3009facf4a8dSllai1 			return (EISDIR);
3010facf4a8dSllai1 		}
3011facf4a8dSllai1 	}
3012facf4a8dSllai1 
3013facf4a8dSllai1 	/* no need to set attribute, but do not fail either */
3014facf4a8dSllai1 	ASSERT(parent);
3015facf4a8dSllai1 	rw_enter(&parent->sdev_contents, RW_READER);
3016facf4a8dSllai1 	if (dv->sdev_state == SDEV_ZOMBIE) {
3017facf4a8dSllai1 		rw_exit(&parent->sdev_contents);
3018facf4a8dSllai1 		return (0);
3019facf4a8dSllai1 	}
3020facf4a8dSllai1 
3021facf4a8dSllai1 	/* If backing store exists, just set it. */
3022facf4a8dSllai1 	if (dv->sdev_attrvp) {
3023facf4a8dSllai1 		rw_exit(&parent->sdev_contents);
3024facf4a8dSllai1 		return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL));
3025facf4a8dSllai1 	}
3026facf4a8dSllai1 
3027facf4a8dSllai1 	/*
3028facf4a8dSllai1 	 * Otherwise, for nodes with the persistence attribute, create it.
3029facf4a8dSllai1 	 */
3030facf4a8dSllai1 	ASSERT(dv->sdev_attr);
3031facf4a8dSllai1 	if (SDEV_IS_PERSIST(dv) ||
3032facf4a8dSllai1 	    ((vap->va_mask & ~AT_TIMES) != 0 && !SDEV_IS_DYNAMIC(dv))) {
3033facf4a8dSllai1 		sdev_vattr_merge(dv, vap);
3034facf4a8dSllai1 		rw_enter(&dv->sdev_contents, RW_WRITER);
3035facf4a8dSllai1 		error = sdev_shadow_node(dv, cred);
3036facf4a8dSllai1 		rw_exit(&dv->sdev_contents);
3037facf4a8dSllai1 		rw_exit(&parent->sdev_contents);
3038facf4a8dSllai1 
3039facf4a8dSllai1 		if (error)
3040facf4a8dSllai1 			return (error);
3041facf4a8dSllai1 		return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL));
3042facf4a8dSllai1 	}
3043facf4a8dSllai1 
3044facf4a8dSllai1 
3045facf4a8dSllai1 	/*
3046facf4a8dSllai1 	 * sdev_attr was allocated in sdev_mknode
3047facf4a8dSllai1 	 */
3048facf4a8dSllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
3049da6c28aaSamw 	error = secpolicy_vnode_setattr(cred, vp, vap,
3050da6c28aaSamw 	    dv->sdev_attr, flags, sdev_unlocked_access, dv);
3051facf4a8dSllai1 	if (error) {
3052facf4a8dSllai1 		rw_exit(&dv->sdev_contents);
3053facf4a8dSllai1 		rw_exit(&parent->sdev_contents);
3054facf4a8dSllai1 		return (error);
3055facf4a8dSllai1 	}
3056facf4a8dSllai1 
3057facf4a8dSllai1 	get = dv->sdev_attr;
3058facf4a8dSllai1 	if (mask & AT_MODE) {
3059facf4a8dSllai1 		get->va_mode &= S_IFMT;
3060facf4a8dSllai1 		get->va_mode |= vap->va_mode & ~S_IFMT;
3061facf4a8dSllai1 	}
3062facf4a8dSllai1 
3063facf4a8dSllai1 	if ((mask & AT_UID) || (mask & AT_GID)) {
3064facf4a8dSllai1 		if (mask & AT_UID)
3065facf4a8dSllai1 			get->va_uid = vap->va_uid;
3066facf4a8dSllai1 		if (mask & AT_GID)
3067facf4a8dSllai1 			get->va_gid = vap->va_gid;
3068facf4a8dSllai1 		/*
3069facf4a8dSllai1 		 * a callback must be provided if the protocol is set
3070facf4a8dSllai1 		 */
3071facf4a8dSllai1 		if ((protocol & AT_UID) || (protocol & AT_GID)) {
3072facf4a8dSllai1 			ASSERT(callback);
3073facf4a8dSllai1 			error = callback(dv, get, protocol);
3074facf4a8dSllai1 			if (error) {
3075facf4a8dSllai1 				rw_exit(&dv->sdev_contents);
3076facf4a8dSllai1 				rw_exit(&parent->sdev_contents);
3077facf4a8dSllai1 				return (error);
3078facf4a8dSllai1 			}
3079facf4a8dSllai1 		}
3080facf4a8dSllai1 	}
3081facf4a8dSllai1 
3082facf4a8dSllai1 	if (mask & AT_ATIME)
3083facf4a8dSllai1 		get->va_atime = vap->va_atime;
3084facf4a8dSllai1 	if (mask & AT_MTIME)
3085facf4a8dSllai1 		get->va_mtime = vap->va_mtime;
3086facf4a8dSllai1 	if (mask & (AT_MODE | AT_UID | AT_GID | AT_CTIME)) {
3087facf4a8dSllai1 		gethrestime(&get->va_ctime);
3088facf4a8dSllai1 	}
3089facf4a8dSllai1 
3090facf4a8dSllai1 	sdev_vattr_merge(dv, get);
3091facf4a8dSllai1 	rw_exit(&dv->sdev_contents);
3092facf4a8dSllai1 	rw_exit(&parent->sdev_contents);
3093facf4a8dSllai1 	return (0);
3094facf4a8dSllai1 }
3095d62bc4baSyz147064 
3096d62bc4baSyz147064 /*
3097d62bc4baSyz147064  * a generic inactive() function
3098d62bc4baSyz147064  */
30993c5e027bSEric Taylor /*ARGSUSED*/
3100d62bc4baSyz147064 void
3101d62bc4baSyz147064 devname_inactive_func(struct vnode *vp, struct cred *cred,
3102d62bc4baSyz147064     void (*callback)(struct vnode *))
3103d62bc4baSyz147064 {
3104d62bc4baSyz147064 	int clean;
3105d62bc4baSyz147064 	struct sdev_node *dv = VTOSDEV(vp);
3106d62bc4baSyz147064 	int state;
3107d62bc4baSyz147064 
3108d62bc4baSyz147064 	mutex_enter(&vp->v_lock);
3109d62bc4baSyz147064 	ASSERT(vp->v_count >= 1);
3110d62bc4baSyz147064 
3111*9e5aa9d8SRobert Mustacchi 
3112d62bc4baSyz147064 	if (vp->v_count == 1 && callback != NULL)
3113d62bc4baSyz147064 		callback(vp);
3114d62bc4baSyz147064 
3115*9e5aa9d8SRobert Mustacchi 	rw_enter(&dv->sdev_contents, RW_WRITER);
3116*9e5aa9d8SRobert Mustacchi 	state = dv->sdev_state;
3117*9e5aa9d8SRobert Mustacchi 
3118d62bc4baSyz147064 	clean = (vp->v_count == 1) && (state == SDEV_ZOMBIE);
3119d62bc4baSyz147064 
3120d62bc4baSyz147064 	/*
3121*9e5aa9d8SRobert Mustacchi 	 * sdev is a rather bad public citizen. It violates the general
3122*9e5aa9d8SRobert Mustacchi 	 * agreement that in memory nodes should always have a valid reference
3123*9e5aa9d8SRobert Mustacchi 	 * count on their vnode. But that's not the case here. This means that
3124*9e5aa9d8SRobert Mustacchi 	 * we do actually have to distinguish between getting inactive callbacks
3125*9e5aa9d8SRobert Mustacchi 	 * for zombies and otherwise. This should probably be fixed.
3126d62bc4baSyz147064 	 */
3127d62bc4baSyz147064 	if (clean) {
3128*9e5aa9d8SRobert Mustacchi 		/* Remove the . entry to ourselves */
3129d62bc4baSyz147064 		if (vp->v_type == VDIR) {
3130*9e5aa9d8SRobert Mustacchi 			decr_link(dv);
3131d62bc4baSyz147064 		}
3132*9e5aa9d8SRobert Mustacchi 		VERIFY(dv->sdev_nlink == 1);
3133*9e5aa9d8SRobert Mustacchi 		decr_link(dv);
3134d62bc4baSyz147064 		--vp->v_count;
3135*9e5aa9d8SRobert Mustacchi 		rw_exit(&dv->sdev_contents);
3136d62bc4baSyz147064 		mutex_exit(&vp->v_lock);
3137d62bc4baSyz147064 		sdev_nodedestroy(dv, 0);
3138d62bc4baSyz147064 	} else {
3139d62bc4baSyz147064 		--vp->v_count;
3140*9e5aa9d8SRobert Mustacchi 		rw_exit(&dv->sdev_contents);
3141d62bc4baSyz147064 		mutex_exit(&vp->v_lock);
3142d62bc4baSyz147064 	}
3143d62bc4baSyz147064 }
3144