12621Sllai1 /*
22621Sllai1  * CDDL HEADER START
32621Sllai1  *
42621Sllai1  * The contents of this file are subject to the terms of the
52621Sllai1  * Common Development and Distribution License (the "License").
62621Sllai1  * You may not use this file except in compliance with the License.
72621Sllai1  *
82621Sllai1  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92621Sllai1  * or http://www.opensolaris.org/os/licensing.
102621Sllai1  * See the License for the specific language governing permissions
112621Sllai1  * and limitations under the License.
122621Sllai1  *
132621Sllai1  * When distributing Covered Code, include this CDDL HEADER in each
142621Sllai1  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152621Sllai1  * If applicable, add the following below this CDDL HEADER, with the
162621Sllai1  * fields enclosed by brackets "[]" replaced with your own identifying
172621Sllai1  * information: Portions Copyright [yyyy] [name of copyright owner]
182621Sllai1  *
192621Sllai1  * CDDL HEADER END
202621Sllai1  */
212621Sllai1 /*
222621Sllai1  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
232621Sllai1  * Use is subject to license terms.
242621Sllai1  */
252621Sllai1 
262621Sllai1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
272621Sllai1 
282621Sllai1 /*
292621Sllai1  * utility routines for the /dev fs
302621Sllai1  */
312621Sllai1 
322621Sllai1 #include <sys/types.h>
332621Sllai1 #include <sys/param.h>
342621Sllai1 #include <sys/t_lock.h>
352621Sllai1 #include <sys/systm.h>
362621Sllai1 #include <sys/sysmacros.h>
372621Sllai1 #include <sys/user.h>
382621Sllai1 #include <sys/time.h>
392621Sllai1 #include <sys/vfs.h>
402621Sllai1 #include <sys/vnode.h>
412621Sllai1 #include <sys/file.h>
422621Sllai1 #include <sys/fcntl.h>
432621Sllai1 #include <sys/flock.h>
442621Sllai1 #include <sys/kmem.h>
452621Sllai1 #include <sys/uio.h>
462621Sllai1 #include <sys/errno.h>
472621Sllai1 #include <sys/stat.h>
482621Sllai1 #include <sys/cred.h>
492621Sllai1 #include <sys/dirent.h>
502621Sllai1 #include <sys/pathname.h>
512621Sllai1 #include <sys/cmn_err.h>
522621Sllai1 #include <sys/debug.h>
532621Sllai1 #include <sys/mode.h>
542621Sllai1 #include <sys/policy.h>
552621Sllai1 #include <fs/fs_subr.h>
562621Sllai1 #include <sys/mount.h>
572621Sllai1 #include <sys/fs/snode.h>
582621Sllai1 #include <sys/fs/dv_node.h>
592621Sllai1 #include <sys/fs/sdev_impl.h>
602621Sllai1 #include <sys/fs/sdev_node.h>
612621Sllai1 #include <sys/sunndi.h>
622621Sllai1 #include <sys/sunmdi.h>
632621Sllai1 #include <sys/conf.h>
642621Sllai1 #include <sys/proc.h>
652621Sllai1 #include <sys/user.h>
662621Sllai1 #include <sys/modctl.h>
672621Sllai1 
682621Sllai1 #ifdef DEBUG
692621Sllai1 int sdev_debug = 0x00000001;
702621Sllai1 int sdev_debug_cache_flags = 0;
712621Sllai1 #endif
722621Sllai1 
732621Sllai1 /*
742621Sllai1  * globals
752621Sllai1  */
762621Sllai1 /* prototype memory vattrs */
772621Sllai1 vattr_t sdev_vattr_dir = {
782621Sllai1 	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
792621Sllai1 	VDIR,					/* va_type */
802621Sllai1 	SDEV_DIRMODE_DEFAULT,			/* va_mode */
812621Sllai1 	SDEV_UID_DEFAULT,			/* va_uid */
822621Sllai1 	SDEV_GID_DEFAULT,			/* va_gid */
832621Sllai1 	0,					/* va_fsid */
842621Sllai1 	0,					/* va_nodeid */
852621Sllai1 	0,					/* va_nlink */
862621Sllai1 	0,					/* va_size */
872621Sllai1 	0,					/* va_atime */
882621Sllai1 	0,					/* va_mtime */
892621Sllai1 	0,					/* va_ctime */
902621Sllai1 	0,					/* va_rdev */
912621Sllai1 	0,					/* va_blksize */
922621Sllai1 	0,					/* va_nblocks */
932621Sllai1 	0					/* va_vcode */
942621Sllai1 };
952621Sllai1 
962621Sllai1 vattr_t sdev_vattr_lnk = {
972621Sllai1 	AT_TYPE|AT_MODE,			/* va_mask */
982621Sllai1 	VLNK,					/* va_type */
992621Sllai1 	SDEV_LNKMODE_DEFAULT,			/* va_mode */
1002621Sllai1 	SDEV_UID_DEFAULT,			/* va_uid */
1012621Sllai1 	SDEV_GID_DEFAULT,			/* va_gid */
1022621Sllai1 	0,					/* va_fsid */
1032621Sllai1 	0,					/* va_nodeid */
1042621Sllai1 	0,					/* va_nlink */
1052621Sllai1 	0,					/* va_size */
1062621Sllai1 	0,					/* va_atime */
1072621Sllai1 	0,					/* va_mtime */
1082621Sllai1 	0,					/* va_ctime */
1092621Sllai1 	0,					/* va_rdev */
1102621Sllai1 	0,					/* va_blksize */
1112621Sllai1 	0,					/* va_nblocks */
1122621Sllai1 	0					/* va_vcode */
1132621Sllai1 };
1142621Sllai1 
1152621Sllai1 vattr_t sdev_vattr_blk = {
1162621Sllai1 	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
1172621Sllai1 	VBLK,					/* va_type */
1182621Sllai1 	S_IFBLK | SDEV_DEVMODE_DEFAULT,		/* va_mode */
1192621Sllai1 	SDEV_UID_DEFAULT,			/* va_uid */
1202621Sllai1 	SDEV_GID_DEFAULT,			/* va_gid */
1212621Sllai1 	0,					/* va_fsid */
1222621Sllai1 	0,					/* va_nodeid */
1232621Sllai1 	0,					/* va_nlink */
1242621Sllai1 	0,					/* va_size */
1252621Sllai1 	0,					/* va_atime */
1262621Sllai1 	0,					/* va_mtime */
1272621Sllai1 	0,					/* va_ctime */
1282621Sllai1 	0,					/* va_rdev */
1292621Sllai1 	0,					/* va_blksize */
1302621Sllai1 	0,					/* va_nblocks */
1312621Sllai1 	0					/* va_vcode */
1322621Sllai1 };
1332621Sllai1 
1342621Sllai1 vattr_t sdev_vattr_chr = {
1352621Sllai1 	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
1362621Sllai1 	VCHR,					/* va_type */
1372621Sllai1 	S_IFCHR | SDEV_DEVMODE_DEFAULT,		/* va_mode */
1382621Sllai1 	SDEV_UID_DEFAULT,			/* va_uid */
1392621Sllai1 	SDEV_GID_DEFAULT,			/* va_gid */
1402621Sllai1 	0,					/* va_fsid */
1412621Sllai1 	0,					/* va_nodeid */
1422621Sllai1 	0,					/* va_nlink */
1432621Sllai1 	0,					/* va_size */
1442621Sllai1 	0,					/* va_atime */
1452621Sllai1 	0,					/* va_mtime */
1462621Sllai1 	0,					/* va_ctime */
1472621Sllai1 	0,					/* va_rdev */
1482621Sllai1 	0,					/* va_blksize */
1492621Sllai1 	0,					/* va_nblocks */
1502621Sllai1 	0					/* va_vcode */
1512621Sllai1 };
1522621Sllai1 
1532621Sllai1 kmem_cache_t	*sdev_node_cache;	/* sdev_node cache */
1542621Sllai1 int		devtype;		/* fstype */
1552621Sllai1 
1562621Sllai1 struct devname_ops *devname_ns_ops;	/* default name service directory ops */
1572621Sllai1 kmutex_t devname_nsmaps_lock;	/* protect devname_nsmaps */
1582621Sllai1 
1592621Sllai1 /* static */
1602621Sllai1 static struct devname_nsmap *devname_nsmaps = NULL;
1612621Sllai1 				/* contents from /etc/dev/devname_master */
1622621Sllai1 static int devname_nsmaps_invalidated = 0; /* "devfsadm -m" has run */
1632621Sllai1 
1642621Sllai1 static struct vnodeops *sdev_get_vop(struct sdev_node *);
1652621Sllai1 static void sdev_set_no_nocache(struct sdev_node *);
1662621Sllai1 static int sdev_get_moduleops(struct sdev_node *);
1672621Sllai1 static void sdev_handle_alloc(struct sdev_node *);
1682621Sllai1 static fs_operation_def_t *sdev_merge_vtab(const fs_operation_def_t []);
1692621Sllai1 static void sdev_free_vtab(fs_operation_def_t *);
1702621Sllai1 
1712621Sllai1 static void
1722621Sllai1 sdev_prof_free(struct sdev_node *dv)
1732621Sllai1 {
1742621Sllai1 	ASSERT(!SDEV_IS_GLOBAL(dv));
1752621Sllai1 	if (dv->sdev_prof.dev_name)
1762621Sllai1 		nvlist_free(dv->sdev_prof.dev_name);
1772621Sllai1 	if (dv->sdev_prof.dev_map)
1782621Sllai1 		nvlist_free(dv->sdev_prof.dev_map);
1792621Sllai1 	if (dv->sdev_prof.dev_symlink)
1802621Sllai1 		nvlist_free(dv->sdev_prof.dev_symlink);
1812621Sllai1 	if (dv->sdev_prof.dev_glob_incdir)
1822621Sllai1 		nvlist_free(dv->sdev_prof.dev_glob_incdir);
1832621Sllai1 	if (dv->sdev_prof.dev_glob_excdir)
1842621Sllai1 		nvlist_free(dv->sdev_prof.dev_glob_excdir);
1852621Sllai1 	bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
1862621Sllai1 }
1872621Sllai1 
1882621Sllai1 /*
1892621Sllai1  * sdev_node cache constructor
1902621Sllai1  */
1912621Sllai1 /*ARGSUSED1*/
1922621Sllai1 static int
1932621Sllai1 i_sdev_node_ctor(void *buf, void *cfarg, int flag)
1942621Sllai1 {
1952621Sllai1 	struct sdev_node *dv = (struct sdev_node *)buf;
1962621Sllai1 	struct vnode *vp;
1972621Sllai1 
1982621Sllai1 	ASSERT(flag == KM_SLEEP);
1992621Sllai1 
2002621Sllai1 	bzero(buf, sizeof (struct sdev_node));
2012621Sllai1 	rw_init(&dv->sdev_contents, NULL, RW_DEFAULT, NULL);
2022621Sllai1 	dv->sdev_vnode = vn_alloc(KM_SLEEP);
2032621Sllai1 	vp = SDEVTOV(dv);
2042621Sllai1 	vp->v_data = (caddr_t)dv;
2052621Sllai1 	return (0);
2062621Sllai1 }
2072621Sllai1 
2082621Sllai1 /* sdev_node destructor for kmem cache */
2092621Sllai1 /*ARGSUSED1*/
2102621Sllai1 static void
2112621Sllai1 i_sdev_node_dtor(void *buf, void *arg)
2122621Sllai1 {
2132621Sllai1 	struct sdev_node *dv = (struct sdev_node *)buf;
2142621Sllai1 	struct vnode *vp = SDEVTOV(dv);
2152621Sllai1 
2162621Sllai1 	rw_destroy(&dv->sdev_contents);
2172621Sllai1 	vn_free(vp);
2182621Sllai1 }
2192621Sllai1 
2202621Sllai1 /* initialize sdev_node cache */
2212621Sllai1 void
2222621Sllai1 sdev_node_cache_init()
2232621Sllai1 {
2242621Sllai1 	int flags = 0;
2252621Sllai1 
2262621Sllai1 #ifdef	DEBUG
2272621Sllai1 	flags = sdev_debug_cache_flags;
2282621Sllai1 	if (flags)
2292621Sllai1 		sdcmn_err(("cache debug flags 0x%x\n", flags));
2302621Sllai1 #endif	/* DEBUG */
2312621Sllai1 
2322621Sllai1 	ASSERT(sdev_node_cache == NULL);
2332621Sllai1 	sdev_node_cache = kmem_cache_create("sdev_node_cache",
2342621Sllai1 	    sizeof (struct sdev_node), 0, i_sdev_node_ctor, i_sdev_node_dtor,
2352621Sllai1 	    NULL, NULL, NULL, flags);
2362621Sllai1 }
2372621Sllai1 
2382621Sllai1 /* destroy sdev_node cache */
2392621Sllai1 void
2402621Sllai1 sdev_node_cache_fini()
2412621Sllai1 {
2422621Sllai1 	ASSERT(sdev_node_cache != NULL);
2432621Sllai1 	kmem_cache_destroy(sdev_node_cache);
2442621Sllai1 	sdev_node_cache = NULL;
2452621Sllai1 }
2462621Sllai1 
2472621Sllai1 void
2482621Sllai1 sdev_set_nodestate(struct sdev_node *dv, sdev_node_state_t state)
2492621Sllai1 {
2502621Sllai1 	ASSERT(dv);
2512621Sllai1 	ASSERT(RW_WRITE_HELD(&dv->sdev_contents));
2522621Sllai1 	dv->sdev_state = state;
2532621Sllai1 }
2542621Sllai1 
2552621Sllai1 static void
2562621Sllai1 sdev_attrinit(struct sdev_node *dv, vattr_t *vap)
2572621Sllai1 {
2582621Sllai1 	timestruc_t now;
2592621Sllai1 
2602621Sllai1 	ASSERT(vap);
2612621Sllai1 
2622621Sllai1 	dv->sdev_attr = kmem_zalloc(sizeof (struct vattr), KM_SLEEP);
2632621Sllai1 	*dv->sdev_attr = *vap;
2642621Sllai1 
2652621Sllai1 	dv->sdev_attr->va_mode = MAKEIMODE(vap->va_type, vap->va_mode);
2662621Sllai1 
2672621Sllai1 	gethrestime(&now);
2682621Sllai1 	dv->sdev_attr->va_atime = now;
2692621Sllai1 	dv->sdev_attr->va_mtime = now;
2702621Sllai1 	dv->sdev_attr->va_ctime = now;
2712621Sllai1 }
2722621Sllai1 
2732621Sllai1 /* alloc and initialize a sdev_node */
2742621Sllai1 int
2752621Sllai1 sdev_nodeinit(struct sdev_node *ddv, char *nm, struct sdev_node **newdv,
2762621Sllai1     vattr_t *vap)
2772621Sllai1 {
2782621Sllai1 	struct sdev_node *dv = NULL;
2792621Sllai1 	struct vnode *vp;
2802621Sllai1 	size_t nmlen, len;
2812621Sllai1 	devname_handle_t  *dhl;
2822621Sllai1 
2832621Sllai1 	nmlen = strlen(nm) + 1;
2842621Sllai1 	if (nmlen > MAXNAMELEN) {
2852621Sllai1 		sdcmn_err9(("sdev_nodeinit: node name %s"
2862621Sllai1 		    " too long\n", nm));
2872621Sllai1 		*newdv = NULL;
2882621Sllai1 		return (ENAMETOOLONG);
2892621Sllai1 	}
2902621Sllai1 
2912621Sllai1 	dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP);
2922621Sllai1 
2932621Sllai1 	dv->sdev_name = kmem_alloc(nmlen, KM_SLEEP);
2942621Sllai1 	bcopy(nm, dv->sdev_name, nmlen);
2952621Sllai1 	dv->sdev_namelen = nmlen - 1;	/* '\0' not included */
2962621Sllai1 	len = strlen(ddv->sdev_path) + strlen(nm) + 2;
2972621Sllai1 	dv->sdev_path = kmem_alloc(len, KM_SLEEP);
2982621Sllai1 	(void) snprintf(dv->sdev_path, len, "%s/%s", ddv->sdev_path, nm);
2992621Sllai1 	/* overwritten for VLNK nodes */
3002621Sllai1 	dv->sdev_symlink = NULL;
3012621Sllai1 
3022621Sllai1 	vp = SDEVTOV(dv);
3032621Sllai1 	vn_reinit(vp);
3042621Sllai1 	vp->v_vfsp = SDEVTOV(ddv)->v_vfsp;
3052621Sllai1 	if (vap)
3062621Sllai1 		vp->v_type = vap->va_type;
3072621Sllai1 
3082621Sllai1 	/*
3092621Sllai1 	 * initialized to the parent's vnodeops.
3102621Sllai1 	 * maybe overwriten for a VDIR
3112621Sllai1 	 */
3122621Sllai1 	vn_setops(vp, vn_getops(SDEVTOV(ddv)));
3132621Sllai1 	vn_exists(vp);
3142621Sllai1 
3152621Sllai1 	dv->sdev_dotdot = NULL;
3162621Sllai1 	dv->sdev_dot = NULL;
3172621Sllai1 	dv->sdev_next = NULL;
3182621Sllai1 	dv->sdev_attrvp = NULL;
3192621Sllai1 	if (vap) {
3202621Sllai1 		sdev_attrinit(dv, vap);
3212621Sllai1 	} else {
3222621Sllai1 		dv->sdev_attr = NULL;
3232621Sllai1 	}
3242621Sllai1 
3252621Sllai1 	dv->sdev_ino = sdev_mkino(dv);
3262621Sllai1 	dv->sdev_nlink = 0;		/* updated on insert */
3272621Sllai1 	dv->sdev_flags = ddv->sdev_flags; /* inherit from the parent first */
3282621Sllai1 	dv->sdev_flags |= SDEV_BUILD;
3292621Sllai1 	mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL);
3302621Sllai1 	cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL);
3312621Sllai1 	if (SDEV_IS_GLOBAL(ddv)) {
3322621Sllai1 		dv->sdev_flags |= SDEV_GLOBAL;
3332621Sllai1 		dv->sdev_mapinfo = NULL;
3342621Sllai1 		dhl = &(dv->sdev_handle);
3352621Sllai1 		dhl->dh_data = dv;
3362621Sllai1 		dhl->dh_spec = DEVNAME_NS_NONE;
3372621Sllai1 		dhl->dh_args = NULL;
3382621Sllai1 		sdev_set_no_nocache(dv);
3392621Sllai1 		dv->sdev_gdir_gen = 0;
3402621Sllai1 	} else {
3412621Sllai1 		dv->sdev_flags &= ~SDEV_GLOBAL;
3422621Sllai1 		dv->sdev_origin = NULL; /* set later */
3432621Sllai1 		bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
3442621Sllai1 		dv->sdev_ldir_gen = 0;
3452621Sllai1 		dv->sdev_devtree_gen = 0;
3462621Sllai1 	}
3472621Sllai1 
3482621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
3492621Sllai1 	sdev_set_nodestate(dv, SDEV_INIT);
3502621Sllai1 	rw_exit(&dv->sdev_contents);
3512621Sllai1 	*newdv = dv;
3522621Sllai1 
3532621Sllai1 	return (0);
3542621Sllai1 }
3552621Sllai1 
3562621Sllai1 /*
3572621Sllai1  * transition a sdev_node into SDEV_READY state
3582621Sllai1  */
3592621Sllai1 int
3602621Sllai1 sdev_nodeready(struct sdev_node *dv, struct vattr *vap, struct vnode *avp,
3612621Sllai1     void *args, struct cred *cred)
3622621Sllai1 {
3632621Sllai1 	int error = 0;
3642621Sllai1 	struct vnode *vp = SDEVTOV(dv);
3652621Sllai1 	vtype_t type;
3662621Sllai1 
3672621Sllai1 	ASSERT(dv && (dv->sdev_state != SDEV_READY) && vap);
3682621Sllai1 
3692621Sllai1 	type = vap->va_type;
3702621Sllai1 	vp->v_type = type;
3712621Sllai1 	vp->v_rdev = vap->va_rdev;
3722621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
3732621Sllai1 	if (type == VDIR) {
3742621Sllai1 		dv->sdev_nlink = 2;
3752621Sllai1 		dv->sdev_flags &= ~SDEV_PERSIST;
3762621Sllai1 		dv->sdev_flags &= ~SDEV_DYNAMIC;
3772621Sllai1 		vn_setops(vp, sdev_get_vop(dv)); /* from internal vtab */
3782621Sllai1 		error = sdev_get_moduleops(dv); /* from plug-in module */
3792621Sllai1 		ASSERT(dv->sdev_dotdot);
3802621Sllai1 		ASSERT(SDEVTOV(dv->sdev_dotdot)->v_type == VDIR);
3812621Sllai1 		vp->v_rdev = SDEVTOV(dv->sdev_dotdot)->v_rdev;
3822621Sllai1 	} else if (type == VLNK) {
3832621Sllai1 		ASSERT(args);
3842621Sllai1 		dv->sdev_nlink = 1;
3852621Sllai1 		dv->sdev_symlink = i_ddi_strdup((char *)args, KM_SLEEP);
3862621Sllai1 	} else {
3872621Sllai1 		dv->sdev_nlink = 1;
3882621Sllai1 	}
3892621Sllai1 
3902621Sllai1 	if (!(SDEV_IS_GLOBAL(dv))) {
3912621Sllai1 		dv->sdev_origin = (struct sdev_node *)args;
3922621Sllai1 		dv->sdev_flags &= ~SDEV_PERSIST;
3932621Sllai1 	}
3942621Sllai1 
3952621Sllai1 	/*
3962621Sllai1 	 * shadow node is created here OR
3972621Sllai1 	 * if failed (indicated by dv->sdev_attrvp == NULL),
3982621Sllai1 	 * created later in sdev_setattr
3992621Sllai1 	 */
4002621Sllai1 	if (avp) {
4012621Sllai1 		dv->sdev_attrvp = avp;
4022621Sllai1 	} else {
4032621Sllai1 		if (dv->sdev_attr == NULL)
4042621Sllai1 			sdev_attrinit(dv, vap);
4052621Sllai1 		else
4062621Sllai1 			*dv->sdev_attr = *vap;
4072621Sllai1 
4082621Sllai1 		if ((SDEV_IS_PERSIST(dv) && (dv->sdev_attrvp == NULL)) ||
4092621Sllai1 		    ((SDEVTOV(dv)->v_type == VDIR) &&
4102621Sllai1 		    (dv->sdev_attrvp == NULL)))
4112621Sllai1 			error = sdev_shadow_node(dv, cred);
4122621Sllai1 	}
4132621Sllai1 
4142621Sllai1 	/* transition to READY state */
4152621Sllai1 	sdev_set_nodestate(dv, SDEV_READY);
4162621Sllai1 	sdev_nc_node_exists(dv);
4172621Sllai1 	rw_exit(&dv->sdev_contents);
4182621Sllai1 	return (error);
4192621Sllai1 }
4202621Sllai1 
4212621Sllai1 /*
4222621Sllai1  * setting ZOMBIE state
4232621Sllai1  */
4242621Sllai1 static int
4252621Sllai1 sdev_nodezombied(struct sdev_node *dv)
4262621Sllai1 {
4272621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
4282621Sllai1 	sdev_set_nodestate(dv, SDEV_ZOMBIE);
4292621Sllai1 	rw_exit(&dv->sdev_contents);
4302621Sllai1 	return (0);
4312621Sllai1 }
4322621Sllai1 
4332621Sllai1 /*
4342621Sllai1  * Build the VROOT sdev_node.
4352621Sllai1  */
4362621Sllai1 /*ARGSUSED*/
4372621Sllai1 struct sdev_node *
4382621Sllai1 sdev_mkroot(struct vfs *vfsp, dev_t devdev, struct vnode *mvp,
4392621Sllai1     struct vnode *avp, struct cred *cred)
4402621Sllai1 {
4412621Sllai1 	struct sdev_node *dv;
4422621Sllai1 	struct vnode *vp;
4432621Sllai1 	char devdir[] = "/dev";
4442621Sllai1 
4452621Sllai1 	ASSERT(sdev_node_cache != NULL);
4462621Sllai1 	ASSERT(avp);
4472621Sllai1 	dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP);
4482621Sllai1 	vp = SDEVTOV(dv);
4492621Sllai1 	vn_reinit(vp);
4502621Sllai1 	vp->v_flag |= VROOT;
4512621Sllai1 	vp->v_vfsp = vfsp;
4522621Sllai1 	vp->v_type = VDIR;
4532621Sllai1 	vp->v_rdev = devdev;
4542621Sllai1 	vn_setops(vp, sdev_vnodeops); /* apply the default vnodeops at /dev */
4552621Sllai1 	vn_exists(vp);
4562621Sllai1 
4572621Sllai1 	if (vfsp->vfs_mntpt)
4582621Sllai1 		dv->sdev_name = i_ddi_strdup(
4592621Sllai1 		    (char *)refstr_value(vfsp->vfs_mntpt), KM_SLEEP);
4602621Sllai1 	else
4612621Sllai1 		/* vfs_mountdev1 set mount point later */
4622621Sllai1 		dv->sdev_name = i_ddi_strdup("/dev", KM_SLEEP);
4632621Sllai1 	dv->sdev_namelen = strlen(dv->sdev_name); /* '\0' not included */
4642621Sllai1 	dv->sdev_path = i_ddi_strdup(devdir, KM_SLEEP);
4652621Sllai1 	dv->sdev_ino = SDEV_ROOTINO;
4662621Sllai1 	dv->sdev_nlink = 2;		/* name + . (no sdev_insert) */
4672621Sllai1 	dv->sdev_dotdot = dv;		/* .. == self */
4682621Sllai1 	dv->sdev_attrvp = avp;
4692621Sllai1 	dv->sdev_attr = NULL;
4702621Sllai1 	mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL);
4712621Sllai1 	cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL);
4722621Sllai1 	if (strcmp(dv->sdev_name, "/dev") == 0) {
4732621Sllai1 		mutex_init(&devname_nsmaps_lock, NULL, MUTEX_DEFAULT, NULL);
4742621Sllai1 		dv->sdev_mapinfo = NULL;
4752621Sllai1 		dv->sdev_flags = SDEV_BUILD|SDEV_GLOBAL|SDEV_PERSIST;
4762621Sllai1 		bzero(&dv->sdev_handle, sizeof (dv->sdev_handle));
4772621Sllai1 		dv->sdev_gdir_gen = 0;
4782621Sllai1 	} else {
4792621Sllai1 		dv->sdev_flags = SDEV_BUILD;
4802621Sllai1 		dv->sdev_flags &= ~SDEV_PERSIST;
4812621Sllai1 		bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
4822621Sllai1 		dv->sdev_ldir_gen = 0;
4832621Sllai1 		dv->sdev_devtree_gen = 0;
4842621Sllai1 	}
4852621Sllai1 
4862621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
4872621Sllai1 	sdev_set_nodestate(dv, SDEV_READY);
4882621Sllai1 	rw_exit(&dv->sdev_contents);
4892621Sllai1 	sdev_nc_node_exists(dv);
4902621Sllai1 	return (dv);
4912621Sllai1 }
4922621Sllai1 
4932621Sllai1 /*
4942621Sllai1  *  1. load the module
4952621Sllai1  *  2. modload invokes sdev_module_register, which in turn sets
4962621Sllai1  *     the dv->sdev_mapinfo->dir_ops
4972621Sllai1  *
4982621Sllai1  * note: locking order:
4992621Sllai1  *	dv->sdev_contents -> map->dir_lock
5002621Sllai1  */
5012621Sllai1 static int
5022621Sllai1 sdev_get_moduleops(struct sdev_node *dv)
5032621Sllai1 {
5042621Sllai1 	int error = 0;
5052621Sllai1 	struct devname_nsmap *map = NULL;
5062621Sllai1 	char *module;
5072621Sllai1 	char *path;
5082621Sllai1 	int load = 1;
5092621Sllai1 
5102621Sllai1 	ASSERT(SDEVTOV(dv)->v_type == VDIR);
5112621Sllai1 
5122621Sllai1 	if (devname_nsmaps == NULL)
5132621Sllai1 		return (0);
5142621Sllai1 
5152621Sllai1 	if (!sdev_nsmaps_loaded() && !sdev_nsmaps_reloaded())
5162621Sllai1 		return (0);
5172621Sllai1 
5182621Sllai1 
5192621Sllai1 	path = dv->sdev_path;
5202621Sllai1 	if ((map = sdev_get_nsmap_by_dir(path, 0))) {
5212621Sllai1 		rw_enter(&map->dir_lock, RW_READER);
5222621Sllai1 		if (map->dir_invalid) {
5232621Sllai1 			if (map->dir_module && map->dir_newmodule &&
5242621Sllai1 			    (strcmp(map->dir_module,
5252621Sllai1 					map->dir_newmodule) == 0)) {
5262621Sllai1 				load = 0;
5272621Sllai1 			}
5282621Sllai1 			sdev_replace_nsmap(map, map->dir_newmodule,
5292621Sllai1 			    map->dir_newmap);
5302621Sllai1 		}
5312621Sllai1 
5322621Sllai1 		module = map->dir_module;
5332621Sllai1 		if (module && load) {
5342621Sllai1 			sdcmn_err6(("sdev_get_moduleops: "
5352621Sllai1 			    "load module %s", module));
5362621Sllai1 			rw_exit(&map->dir_lock);
5372621Sllai1 			error = modload("devname", module);
5382621Sllai1 			sdcmn_err6(("sdev_get_moduleops: error %d\n", error));
5392621Sllai1 			if (error < 0) {
5402621Sllai1 				return (-1);
5412621Sllai1 			}
5422621Sllai1 		} else if (module == NULL) {
5432621Sllai1 			/*
5442621Sllai1 			 * loading the module ops for name services
5452621Sllai1 			 */
5462621Sllai1 			if (devname_ns_ops == NULL) {
5472621Sllai1 				sdcmn_err6((
5482621Sllai1 				    "sdev_get_moduleops: modload default\n"));
5492621Sllai1 				error = modload("devname", DEVNAME_NSCONFIG);
5502621Sllai1 				sdcmn_err6((
5512621Sllai1 				    "sdev_get_moduleops: error %d\n", error));
5522621Sllai1 				if (error < 0) {
5532621Sllai1 					return (-1);
5542621Sllai1 				}
5552621Sllai1 			}
5562621Sllai1 
5572621Sllai1 			if (!rw_tryupgrade(&map->dir_lock)) {
5582621Sllai1 				rw_exit(&map->dir_lock);
5592621Sllai1 				rw_enter(&map->dir_lock, RW_WRITER);
5602621Sllai1 			}
5612621Sllai1 			ASSERT(devname_ns_ops);
5622621Sllai1 			map->dir_ops = devname_ns_ops;
5632621Sllai1 			rw_exit(&map->dir_lock);
5642621Sllai1 		}
5652621Sllai1 	}
5662621Sllai1 
5672621Sllai1 	dv->sdev_mapinfo = map;
5682621Sllai1 	return (0);
5692621Sllai1 }
5702621Sllai1 
5712621Sllai1 /* directory dependent vop table */
5722621Sllai1 struct sdev_vop_table {
5732621Sllai1 	char *vt_name;				/* subdirectory name */
5742621Sllai1 	const fs_operation_def_t *vt_service;	/* vnodeops table */
5752621Sllai1 	struct vnodeops *vt_vops;		/* constructed vop */
5762621Sllai1 	struct vnodeops **vt_global_vops;	/* global container for vop */
5772621Sllai1 	int (*vt_vtor)(struct sdev_node *);	/* validate sdev_node */
5782621Sllai1 	int vt_flags;
5792621Sllai1 };
5802621Sllai1 
5812621Sllai1 /*
5822621Sllai1  * A nice improvement would be to provide a plug-in mechanism
5832621Sllai1  * for this table instead of a const table.
5842621Sllai1  */
5852621Sllai1 static struct sdev_vop_table vtab[] =
5862621Sllai1 {
5872621Sllai1 	{ "pts", devpts_vnodeops_tbl, NULL, &devpts_vnodeops, devpts_validate,
5882621Sllai1 	SDEV_DYNAMIC | SDEV_VTOR },
5892621Sllai1 
5902621Sllai1 	{ "zcons", NULL, NULL, NULL, NULL, SDEV_NO_NCACHE },
5912621Sllai1 
5922621Sllai1 	{ NULL, NULL, NULL, NULL, NULL, 0}
5932621Sllai1 };
5942621Sllai1 
5952621Sllai1 
5962621Sllai1 /*
5972621Sllai1  *  sets a directory's vnodeops if the directory is in the vtab;
5982621Sllai1  */
5992621Sllai1 static struct vnodeops *
6002621Sllai1 sdev_get_vop(struct sdev_node *dv)
6012621Sllai1 {
6022621Sllai1 	int i;
6032621Sllai1 	char *path;
6042621Sllai1 
6052621Sllai1 	path = dv->sdev_path;
6062621Sllai1 	ASSERT(path);
6072621Sllai1 
6082621Sllai1 	/* gets the relative path to /dev/ */
6092621Sllai1 	path += 5;
6102621Sllai1 
6112621Sllai1 	/* gets the vtab entry if matches */
6122621Sllai1 	for (i = 0; vtab[i].vt_name; i++) {
6132621Sllai1 		if (strcmp(vtab[i].vt_name, path) != 0)
6142621Sllai1 			continue;
6152621Sllai1 		dv->sdev_flags |= vtab[i].vt_flags;
6162621Sllai1 
6172621Sllai1 		if (vtab[i].vt_vops) {
6182621Sllai1 			if (vtab[i].vt_global_vops)
6192621Sllai1 				*(vtab[i].vt_global_vops) = vtab[i].vt_vops;
6202621Sllai1 			return (vtab[i].vt_vops);
6212621Sllai1 		}
6222621Sllai1 
6232621Sllai1 		if (vtab[i].vt_service) {
6242621Sllai1 			fs_operation_def_t *templ;
6252621Sllai1 			templ = sdev_merge_vtab(vtab[i].vt_service);
6262621Sllai1 			if (vn_make_ops(vtab[i].vt_name,
6272621Sllai1 			    (const fs_operation_def_t *)templ,
6282621Sllai1 			    &vtab[i].vt_vops) != 0) {
6292621Sllai1 				cmn_err(CE_PANIC, "%s: malformed vnode ops\n",
6302621Sllai1 				    vtab[i].vt_name);
6312621Sllai1 				/*NOTREACHED*/
6322621Sllai1 			}
6332621Sllai1 			if (vtab[i].vt_global_vops) {
6342621Sllai1 				*(vtab[i].vt_global_vops) = vtab[i].vt_vops;
6352621Sllai1 			}
6362621Sllai1 			sdev_free_vtab(templ);
6372621Sllai1 			return (vtab[i].vt_vops);
6382621Sllai1 		}
6392621Sllai1 		return (sdev_vnodeops);
6402621Sllai1 	}
6412621Sllai1 
6422621Sllai1 	/* child inherits the persistence of the parent */
6432621Sllai1 	if (SDEV_IS_PERSIST(dv->sdev_dotdot))
6442621Sllai1 		dv->sdev_flags |= SDEV_PERSIST;
6452621Sllai1 
6462621Sllai1 	return (sdev_vnodeops);
6472621Sllai1 }
6482621Sllai1 
6492621Sllai1 static void
6502621Sllai1 sdev_set_no_nocache(struct sdev_node *dv)
6512621Sllai1 {
6522621Sllai1 	int i;
6532621Sllai1 	char *path;
6542621Sllai1 
6552621Sllai1 	ASSERT(dv->sdev_path);
6562621Sllai1 	path = dv->sdev_path + strlen("/dev/");
6572621Sllai1 
6582621Sllai1 	for (i = 0; vtab[i].vt_name; i++) {
6592621Sllai1 		if (strcmp(vtab[i].vt_name, path) == 0) {
6602621Sllai1 			if (vtab[i].vt_flags & SDEV_NO_NCACHE)
6612621Sllai1 				dv->sdev_flags |= SDEV_NO_NCACHE;
6622621Sllai1 			break;
6632621Sllai1 		}
6642621Sllai1 	}
6652621Sllai1 }
6662621Sllai1 
6672621Sllai1 void *
6682621Sllai1 sdev_get_vtor(struct sdev_node *dv)
6692621Sllai1 {
6702621Sllai1 	int i;
6712621Sllai1 
6722621Sllai1 	for (i = 0; vtab[i].vt_name; i++) {
6732621Sllai1 		if (strcmp(vtab[i].vt_name, dv->sdev_name) != 0)
6742621Sllai1 			continue;
6752621Sllai1 		return ((void *)vtab[i].vt_vtor);
6762621Sllai1 	}
6772621Sllai1 	return (NULL);
6782621Sllai1 }
6792621Sllai1 
6802621Sllai1 /*
6812621Sllai1  * Build the base root inode
6822621Sllai1  */
6832621Sllai1 ino_t
6842621Sllai1 sdev_mkino(struct sdev_node *dv)
6852621Sllai1 {
6862621Sllai1 	ino_t	ino;
6872621Sllai1 
6882621Sllai1 	/*
6892621Sllai1 	 * for now, follow the lead of tmpfs here
6902621Sllai1 	 * need to someday understand the requirements here
6912621Sllai1 	 */
6922621Sllai1 	ino = (ino_t)(uint32_t)((uintptr_t)dv >> 3);
6932621Sllai1 	ino += SDEV_ROOTINO + 1;
6942621Sllai1 
6952621Sllai1 	return (ino);
6962621Sllai1 }
6972621Sllai1 
6982621Sllai1 static int
6992621Sllai1 sdev_getlink(struct vnode *linkvp, char **link)
7002621Sllai1 {
7012621Sllai1 	int err;
7022621Sllai1 	char *buf;
7032621Sllai1 	struct uio uio = {0};
7042621Sllai1 	struct iovec iov = {0};
7052621Sllai1 
7062621Sllai1 	if (linkvp == NULL)
7072621Sllai1 		return (ENOENT);
7082621Sllai1 	ASSERT(linkvp->v_type == VLNK);
7092621Sllai1 
7102621Sllai1 	buf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
7112621Sllai1 	iov.iov_base = buf;
7122621Sllai1 	iov.iov_len = MAXPATHLEN;
7132621Sllai1 	uio.uio_iov = &iov;
7142621Sllai1 	uio.uio_iovcnt = 1;
7152621Sllai1 	uio.uio_resid = MAXPATHLEN;
7162621Sllai1 	uio.uio_segflg = UIO_SYSSPACE;
7172621Sllai1 	uio.uio_llimit = MAXOFFSET_T;
7182621Sllai1 
7192621Sllai1 	err = VOP_READLINK(linkvp, &uio, kcred);
7202621Sllai1 	if (err) {
7212621Sllai1 		cmn_err(CE_WARN, "readlink %s failed in dev\n", buf);
7222621Sllai1 		kmem_free(buf, MAXPATHLEN);
7232621Sllai1 		return (ENOENT);
7242621Sllai1 	}
7252621Sllai1 
7262621Sllai1 	/* mission complete */
7272621Sllai1 	*link = i_ddi_strdup(buf, KM_SLEEP);
7282621Sllai1 	kmem_free(buf, MAXPATHLEN);
7292621Sllai1 	return (0);
7302621Sllai1 }
7312621Sllai1 
7322621Sllai1 /*
7332621Sllai1  * A convenient wrapper to get the devfs node vnode for a device
7342621Sllai1  * minor functionality: readlink() of a /dev symlink
7352621Sllai1  * Place the link into dv->sdev_symlink
7362621Sllai1  */
7372621Sllai1 static int
7382621Sllai1 sdev_follow_link(struct sdev_node *dv)
7392621Sllai1 {
7402621Sllai1 	int err;
7412621Sllai1 	struct vnode *linkvp;
7422621Sllai1 	char *link = NULL;
7432621Sllai1 
7442621Sllai1 	linkvp = SDEVTOV(dv);
7452621Sllai1 	if (linkvp == NULL)
7462621Sllai1 		return (ENOENT);
7472621Sllai1 	ASSERT(linkvp->v_type == VLNK);
7482621Sllai1 	err = sdev_getlink(linkvp, &link);
7492621Sllai1 	if (err) {
7502621Sllai1 		(void) sdev_nodezombied(dv);
7512621Sllai1 		dv->sdev_symlink = NULL;
7522621Sllai1 		return (ENOENT);
7532621Sllai1 	}
7542621Sllai1 
7552621Sllai1 	ASSERT(link != NULL);
7562621Sllai1 	dv->sdev_symlink = link;
7572621Sllai1 	return (0);
7582621Sllai1 }
7592621Sllai1 
7602621Sllai1 static int
7612621Sllai1 sdev_node_check(struct sdev_node *dv, struct vattr *nvap, void *nargs)
7622621Sllai1 {
7632621Sllai1 	vtype_t otype = SDEVTOV(dv)->v_type;
7642621Sllai1 
7652621Sllai1 	/*
7662621Sllai1 	 * existing sdev_node has a different type.
7672621Sllai1 	 */
7682621Sllai1 	if (otype != nvap->va_type) {
7692621Sllai1 		sdcmn_err9(("sdev_node_check: existing node "
7702621Sllai1 		    "  %s type %d does not match new node type %d\n",
7712621Sllai1 		    dv->sdev_name, otype, nvap->va_type));
7722621Sllai1 		return (EEXIST);
7732621Sllai1 	}
7742621Sllai1 
7752621Sllai1 	/*
7762621Sllai1 	 * For a symlink, the target should be the same.
7772621Sllai1 	 */
7782621Sllai1 	if (otype == VLNK) {
7792621Sllai1 		ASSERT(nargs != NULL);
7802621Sllai1 		ASSERT(dv->sdev_symlink != NULL);
7812621Sllai1 		if (strcmp(dv->sdev_symlink, (char *)nargs) != 0) {
7822621Sllai1 			sdcmn_err9(("sdev_node_check: existing node "
7832621Sllai1 			    " %s has different symlink %s as new node "
7842621Sllai1 			    " %s\n", dv->sdev_name, dv->sdev_symlink,
7852621Sllai1 			    (char *)nargs));
7862621Sllai1 			return (EEXIST);
7872621Sllai1 		}
7882621Sllai1 	}
7892621Sllai1 
7902621Sllai1 	return (0);
7912621Sllai1 }
7922621Sllai1 
7932621Sllai1 /*
7942621Sllai1  * sdev_mknode - a wrapper for sdev_nodeinit(), sdev_nodeready()
7952621Sllai1  *
7962621Sllai1  * arguments:
7972621Sllai1  *	- ddv (parent)
7982621Sllai1  *	- nm (child name)
7992621Sllai1  *	- newdv (sdev_node for nm is returned here)
8002621Sllai1  *	- vap (vattr for the node to be created, va_type should be set.
8012621Sllai1  *	  the defaults should be used if unknown)
8022621Sllai1  *	- cred
8032621Sllai1  *	- args
8042621Sllai1  *	    . tnm (for VLNK)
8052621Sllai1  *	    . global sdev_node (for !SDEV_GLOBAL)
8062621Sllai1  * 	- state: SDEV_INIT, SDEV_READY
8072621Sllai1  *
8082621Sllai1  * only ddv, nm, newddv, vap, cred are required for sdev_mknode(SDEV_INIT)
8092621Sllai1  *
8102621Sllai1  * NOTE:  directory contents writers lock needs to be held before
8112621Sllai1  *	  calling this routine.
8122621Sllai1  */
8132621Sllai1 int
8142621Sllai1 sdev_mknode(struct sdev_node *ddv, char *nm, struct sdev_node **newdv,
8152621Sllai1     struct vattr *vap, struct vnode *avp, void *args, struct cred *cred,
8162621Sllai1     sdev_node_state_t state)
8172621Sllai1 {
8182621Sllai1 	int error = 0;
8192621Sllai1 	sdev_node_state_t node_state;
8202621Sllai1 	struct sdev_node *dv = NULL;
8212621Sllai1 
8222621Sllai1 	ASSERT(state != SDEV_ZOMBIE);
8232621Sllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
8242621Sllai1 
8252621Sllai1 	if (*newdv) {
8262621Sllai1 		dv = *newdv;
8272621Sllai1 	} else {
8282621Sllai1 		/* allocate and initialize a sdev_node */
8292621Sllai1 		if (ddv->sdev_state == SDEV_ZOMBIE) {
8302621Sllai1 			sdcmn_err9(("sdev_mknode: parent %s ZOMBIEd\n",
8312621Sllai1 			    ddv->sdev_path));
8322621Sllai1 			return (ENOENT);
8332621Sllai1 		}
8342621Sllai1 
8352621Sllai1 		error = sdev_nodeinit(ddv, nm, &dv, vap);
8362621Sllai1 		if (error != 0) {
8372621Sllai1 			sdcmn_err9(("sdev_mknode: error %d,"
8382621Sllai1 			    " name %s can not be initialized\n",
8392621Sllai1 			    error, nm));
8402621Sllai1 			return (ENOENT);
8412621Sllai1 		}
8422621Sllai1 		ASSERT(dv);
8432621Sllai1 
8442621Sllai1 		/* insert into the directory cache */
8452621Sllai1 		error = sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_ADD);
8462621Sllai1 		if (error) {
8472621Sllai1 			sdcmn_err9(("sdev_mknode: node %s can not"
8482621Sllai1 			    " be added into directory cache\n", nm));
8492621Sllai1 			return (ENOENT);
8502621Sllai1 		}
8512621Sllai1 	}
8522621Sllai1 
8532621Sllai1 	ASSERT(dv);
8542621Sllai1 	node_state = dv->sdev_state;
8552621Sllai1 	ASSERT(node_state != SDEV_ZOMBIE);
8562621Sllai1 
8572621Sllai1 	if (state == SDEV_READY) {
8582621Sllai1 		switch (node_state) {
8592621Sllai1 		case SDEV_INIT:
8602621Sllai1 			error = sdev_nodeready(dv, vap, avp, args, cred);
8612621Sllai1 			/*
8622621Sllai1 			 * masking the errors with ENOENT
8632621Sllai1 			 */
8642621Sllai1 			if (error) {
8652621Sllai1 				sdcmn_err9(("sdev_mknode: node %s can NOT"
8662621Sllai1 				    " be transitioned into READY state, "
8672621Sllai1 				    "error %d\n", nm, error));
8682621Sllai1 				error = ENOENT;
8692621Sllai1 			}
8702621Sllai1 			break;
8712621Sllai1 		case SDEV_READY:
8722621Sllai1 			/*
8732621Sllai1 			 * Do some sanity checking to make sure
8742621Sllai1 			 * the existing sdev_node is what has been
8752621Sllai1 			 * asked for.
8762621Sllai1 			 */
8772621Sllai1 			error = sdev_node_check(dv, vap, args);
8782621Sllai1 			break;
8792621Sllai1 		default:
8802621Sllai1 			break;
8812621Sllai1 		}
8822621Sllai1 	}
8832621Sllai1 
8842621Sllai1 	if (!error) {
8852621Sllai1 		*newdv = dv;
8862621Sllai1 		ASSERT((*newdv)->sdev_state != SDEV_ZOMBIE);
8872621Sllai1 	} else {
8882621Sllai1 		SDEV_SIMPLE_RELE(dv);
8892621Sllai1 		*newdv = NULL;
8902621Sllai1 	}
8912621Sllai1 
8922621Sllai1 	return (error);
8932621Sllai1 }
8942621Sllai1 
8952621Sllai1 /*
8962621Sllai1  * convenient wrapper to change vp's ATIME, CTIME and ATIME
8972621Sllai1  */
8982621Sllai1 void
8992621Sllai1 sdev_update_timestamps(struct vnode *vp, cred_t *cred, uint_t mask)
9002621Sllai1 {
9012621Sllai1 	struct vattr attr;
9022621Sllai1 	timestruc_t now;
9032621Sllai1 	int err;
9042621Sllai1 
9052621Sllai1 	ASSERT(vp);
9062621Sllai1 	gethrestime(&now);
9072621Sllai1 	if (mask & AT_CTIME)
9082621Sllai1 		attr.va_ctime = now;
9092621Sllai1 	if (mask & AT_MTIME)
9102621Sllai1 		attr.va_mtime = now;
9112621Sllai1 	if (mask & AT_ATIME)
9122621Sllai1 		attr.va_atime = now;
9132621Sllai1 
9142621Sllai1 	attr.va_mask = (mask & AT_TIMES);
9152621Sllai1 	err = VOP_SETATTR(vp, &attr, 0, cred, NULL);
9162621Sllai1 	if (err && (err != EROFS)) {
9172621Sllai1 		sdcmn_err(("update timestamps error %d\n", err));
9182621Sllai1 	}
9192621Sllai1 }
9202621Sllai1 
9212621Sllai1 /*
9222621Sllai1  * the backing store vnode is released here
9232621Sllai1  */
9242621Sllai1 /*ARGSUSED1*/
9252621Sllai1 void
9262621Sllai1 sdev_nodedestroy(struct sdev_node *dv, uint_t flags)
9272621Sllai1 {
9282621Sllai1 	/* no references */
9292621Sllai1 	ASSERT(dv->sdev_nlink == 0);
9302621Sllai1 
9312621Sllai1 	if (dv->sdev_attrvp != NULLVP) {
9322621Sllai1 		VN_RELE(dv->sdev_attrvp);
9332621Sllai1 		/*
9342621Sllai1 		 * reset the attrvp so that no more
9352621Sllai1 		 * references can be made on this already
9362621Sllai1 		 * vn_rele() vnode
9372621Sllai1 		 */
9382621Sllai1 		dv->sdev_attrvp = NULLVP;
9392621Sllai1 	}
9402621Sllai1 
9412621Sllai1 	if (dv->sdev_attr != NULL) {
9422621Sllai1 		kmem_free(dv->sdev_attr, sizeof (struct vattr));
9432621Sllai1 		dv->sdev_attr = NULL;
9442621Sllai1 	}
9452621Sllai1 
9462621Sllai1 	if (dv->sdev_name != NULL) {
9472621Sllai1 		kmem_free(dv->sdev_name, dv->sdev_namelen + 1);
9482621Sllai1 		dv->sdev_name = NULL;
9492621Sllai1 	}
9502621Sllai1 
9512621Sllai1 	if (dv->sdev_symlink != NULL) {
9522621Sllai1 		kmem_free(dv->sdev_symlink, strlen(dv->sdev_symlink) + 1);
9532621Sllai1 		dv->sdev_symlink = NULL;
9542621Sllai1 	}
9552621Sllai1 
9562621Sllai1 	if (dv->sdev_path) {
9572621Sllai1 		kmem_free(dv->sdev_path, strlen(dv->sdev_path) + 1);
9582621Sllai1 		dv->sdev_path = NULL;
9592621Sllai1 	}
9602621Sllai1 
9612621Sllai1 	if (!SDEV_IS_GLOBAL(dv))
9622621Sllai1 		sdev_prof_free(dv);
9632621Sllai1 
9642621Sllai1 	mutex_destroy(&dv->sdev_lookup_lock);
9652621Sllai1 	cv_destroy(&dv->sdev_lookup_cv);
9662621Sllai1 
9672621Sllai1 	/* return node to initial state as per constructor */
9682621Sllai1 	(void) memset((void *)&dv->sdev_instance_data, 0,
9692621Sllai1 	    sizeof (dv->sdev_instance_data));
9702621Sllai1 	vn_invalid(SDEVTOV(dv));
9712621Sllai1 	kmem_cache_free(sdev_node_cache, dv);
9722621Sllai1 }
9732621Sllai1 
9742621Sllai1 /*
9752621Sllai1  * DIRECTORY CACHE lookup
9762621Sllai1  */
9772621Sllai1 struct sdev_node *
9782621Sllai1 sdev_findbyname(struct sdev_node *ddv, char *nm)
9792621Sllai1 {
9802621Sllai1 	struct sdev_node *dv;
9812621Sllai1 	size_t	nmlen = strlen(nm);
9822621Sllai1 
9832621Sllai1 	ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
9842621Sllai1 	for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next) {
9852621Sllai1 		if (dv->sdev_namelen != nmlen) {
9862621Sllai1 			continue;
9872621Sllai1 		}
9882621Sllai1 
9892621Sllai1 		/*
9902621Sllai1 		 * Can't lookup stale nodes
9912621Sllai1 		 */
9922621Sllai1 		if (dv->sdev_flags & SDEV_STALE) {
9932621Sllai1 			sdcmn_err9((
9942621Sllai1 			    "sdev_findbyname: skipped stale node: %s\n",
9952621Sllai1 			    dv->sdev_name));
9962621Sllai1 			continue;
9972621Sllai1 		}
9982621Sllai1 
9992621Sllai1 		if (strcmp(dv->sdev_name, nm) == 0) {
10002621Sllai1 			SDEV_HOLD(dv);
10012621Sllai1 			return (dv);
10022621Sllai1 		}
10032621Sllai1 	}
10042621Sllai1 	return (NULL);
10052621Sllai1 }
10062621Sllai1 
10072621Sllai1 /*
10082621Sllai1  * Inserts a new sdev_node in a parent directory
10092621Sllai1  */
10102621Sllai1 void
10112621Sllai1 sdev_direnter(struct sdev_node *ddv, struct sdev_node *dv)
10122621Sllai1 {
10132621Sllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
10142621Sllai1 	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
10152621Sllai1 	ASSERT(ddv->sdev_nlink >= 2);
10162621Sllai1 	ASSERT(dv->sdev_nlink == 0);
10172621Sllai1 
10182621Sllai1 	dv->sdev_dotdot = ddv;
10192621Sllai1 	dv->sdev_next = ddv->sdev_dot;
10202621Sllai1 	ddv->sdev_dot = dv;
10212621Sllai1 	ddv->sdev_nlink++;
10222621Sllai1 }
10232621Sllai1 
10242621Sllai1 /*
10252621Sllai1  * The following check is needed because while sdev_nodes are linked
10262621Sllai1  * in SDEV_INIT state, they have their link counts incremented only
10272621Sllai1  * in SDEV_READY state.
10282621Sllai1  */
10292621Sllai1 static void
10302621Sllai1 decr_link(struct sdev_node *dv)
10312621Sllai1 {
10322621Sllai1 	if (dv->sdev_state != SDEV_INIT)
10332621Sllai1 		dv->sdev_nlink--;
10342621Sllai1 	else
10352621Sllai1 		ASSERT(dv->sdev_nlink == 0);
10362621Sllai1 }
10372621Sllai1 
10382621Sllai1 /*
10392621Sllai1  * Delete an existing dv from directory cache
10402621Sllai1  *
10412621Sllai1  * In the case of a node is still held by non-zero reference count,
10422621Sllai1  *     the node is put into ZOMBIE state. Once the reference count
10432621Sllai1  *     reaches "0", the node is unlinked and destroyed,
10442621Sllai1  *     in sdev_inactive().
10452621Sllai1  */
10462621Sllai1 static int
10472621Sllai1 sdev_dirdelete(struct sdev_node *ddv, struct sdev_node *dv)
10482621Sllai1 {
10492621Sllai1 	struct sdev_node *idv;
10502621Sllai1 	struct sdev_node *prev = NULL;
10512621Sllai1 	struct vnode *vp;
10522621Sllai1 
10532621Sllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
10542621Sllai1 
10552621Sllai1 	vp = SDEVTOV(dv);
10562621Sllai1 	mutex_enter(&vp->v_lock);
10572621Sllai1 
10582621Sllai1 	/* dv is held still */
10592621Sllai1 	if (vp->v_count > 1) {
10602621Sllai1 		rw_enter(&dv->sdev_contents, RW_WRITER);
10612621Sllai1 		if (dv->sdev_state == SDEV_READY) {
10622621Sllai1 			sdcmn_err9((
10632621Sllai1 			    "sdev_delete: node %s busy with count %d\n",
10642621Sllai1 			    dv->sdev_name, vp->v_count));
10652621Sllai1 			dv->sdev_state = SDEV_ZOMBIE;
10662621Sllai1 		}
10672621Sllai1 		rw_exit(&dv->sdev_contents);
10682621Sllai1 		--vp->v_count;
10692621Sllai1 		mutex_exit(&vp->v_lock);
10702621Sllai1 		return (EBUSY);
10712621Sllai1 	}
10722621Sllai1 	ASSERT(vp->v_count == 1);
10732621Sllai1 
10742621Sllai1 	/* unlink from the memory cache */
10752621Sllai1 	ddv->sdev_nlink--;	/* .. to above */
10762621Sllai1 	if (vp->v_type == VDIR) {
10772621Sllai1 		decr_link(dv);		/* . to self */
10782621Sllai1 	}
10792621Sllai1 
10802621Sllai1 	for (idv = ddv->sdev_dot; idv && idv != dv;
10812621Sllai1 	    prev = idv, idv = idv->sdev_next)
10822621Sllai1 		;
10832621Sllai1 	ASSERT(idv == dv);	/* node to be deleted must exist */
10842621Sllai1 	if (prev == NULL)
10852621Sllai1 		ddv->sdev_dot = dv->sdev_next;
10862621Sllai1 	else
10872621Sllai1 		prev->sdev_next = dv->sdev_next;
10882621Sllai1 	dv->sdev_next = NULL;
10892621Sllai1 	decr_link(dv);	/* name, back to zero */
10902621Sllai1 	vp->v_count--;
10912621Sllai1 	mutex_exit(&vp->v_lock);
10922621Sllai1 
10932621Sllai1 	/* destroy the node */
10942621Sllai1 	sdev_nodedestroy(dv, 0);
10952621Sllai1 	return (0);
10962621Sllai1 }
10972621Sllai1 
10982621Sllai1 /*
10992621Sllai1  * check if the source is in the path of the target
11002621Sllai1  *
11012621Sllai1  * source and target are different
11022621Sllai1  */
11032621Sllai1 /*ARGSUSED2*/
11042621Sllai1 static int
11052621Sllai1 sdev_checkpath(struct sdev_node *sdv, struct sdev_node *tdv, struct cred *cred)
11062621Sllai1 {
11072621Sllai1 	int error = 0;
11082621Sllai1 	struct sdev_node *dotdot, *dir;
11092621Sllai1 
11102621Sllai1 	dotdot = tdv->sdev_dotdot;
11112621Sllai1 	ASSERT(dotdot);
11122621Sllai1 
11132621Sllai1 	/* fs root */
11142621Sllai1 	if (dotdot == tdv) {
11152621Sllai1 		return (0);
11162621Sllai1 	}
11172621Sllai1 
11182621Sllai1 	for (;;) {
11192621Sllai1 		/*
11202621Sllai1 		 * avoid error cases like
11212621Sllai1 		 *	mv a a/b
11222621Sllai1 		 *	mv a a/b/c
11232621Sllai1 		 *	etc.
11242621Sllai1 		 */
11252621Sllai1 		if (dotdot == sdv) {
11262621Sllai1 			error = EINVAL;
11272621Sllai1 			break;
11282621Sllai1 		}
11292621Sllai1 
11302621Sllai1 		dir = dotdot;
11312621Sllai1 		dotdot = dir->sdev_dotdot;
11322621Sllai1 
11332621Sllai1 		/* done checking because root is reached */
11342621Sllai1 		if (dir == dotdot) {
11352621Sllai1 			break;
11362621Sllai1 		}
11372621Sllai1 	}
11382621Sllai1 	return (error);
11392621Sllai1 }
11402621Sllai1 
11412621Sllai1 int
11422621Sllai1 sdev_rnmnode(struct sdev_node *oddv, struct sdev_node *odv,
11432621Sllai1     struct sdev_node *nddv, struct sdev_node **ndvp, char *nnm,
11442621Sllai1     struct cred *cred)
11452621Sllai1 {
11462621Sllai1 	int error = 0;
11472621Sllai1 	struct vnode *ovp = SDEVTOV(odv);
11482621Sllai1 	struct vnode *nvp;
11492621Sllai1 	struct vattr vattr;
11502621Sllai1 	int doingdir = (ovp->v_type == VDIR);
11512621Sllai1 	char *link = NULL;
1152*2729Sllai1 	int samedir = (oddv == nddv) ? 1 : 0;
1153*2729Sllai1 	int bkstore = 0;
1154*2729Sllai1 	int bypass = 0;
1155*2729Sllai1 	struct sdev_node *idv = NULL;
1156*2729Sllai1 	struct sdev_node *ndv = NULL;
1157*2729Sllai1 	timestruc_t now;
1158*2729Sllai1 
1159*2729Sllai1 	vattr.va_mask = AT_MODE|AT_UID|AT_GID;
1160*2729Sllai1 	error = VOP_GETATTR(ovp, &vattr, 0, cred);
1161*2729Sllai1 	if (error)
1162*2729Sllai1 		return (error);
1163*2729Sllai1 
1164*2729Sllai1 	if (!samedir)
1165*2729Sllai1 		rw_enter(&oddv->sdev_contents, RW_WRITER);
1166*2729Sllai1 	rw_enter(&nddv->sdev_contents, RW_WRITER);
1167*2729Sllai1 
1168*2729Sllai1 	/*
1169*2729Sllai1 	 * the source may have been deleted by another thread before
1170*2729Sllai1 	 * we gets here.
1171*2729Sllai1 	 */
1172*2729Sllai1 	if (odv->sdev_state != SDEV_READY) {
1173*2729Sllai1 		error = ENOENT;
1174*2729Sllai1 		goto err_out;
1175*2729Sllai1 	}
1176*2729Sllai1 
1177*2729Sllai1 	if (doingdir && (odv == nddv)) {
1178*2729Sllai1 		error = EINVAL;
1179*2729Sllai1 		goto err_out;
1180*2729Sllai1 	}
11812621Sllai1 
11822621Sllai1 	/*
11832621Sllai1 	 * If renaming a directory, and the parents are different (".." must be
11842621Sllai1 	 * changed) then the source dir must not be in the dir hierarchy above
11852621Sllai1 	 * the target since it would orphan everything below the source dir.
11862621Sllai1 	 */
11872621Sllai1 	if (doingdir && (oddv != nddv)) {
11882621Sllai1 		error = sdev_checkpath(odv, nddv, cred);
11892621Sllai1 		if (error)
1190*2729Sllai1 			goto err_out;
11912621Sllai1 	}
11922621Sllai1 
1193*2729Sllai1 	/* destination existing */
11942621Sllai1 	if (*ndvp) {
11952621Sllai1 		nvp = SDEVTOV(*ndvp);
11962621Sllai1 		ASSERT(nvp);
11972621Sllai1 
11982621Sllai1 		/* handling renaming to itself */
1199*2729Sllai1 		if (odv == *ndvp) {
1200*2729Sllai1 			error = 0;
1201*2729Sllai1 			goto err_out;
1202*2729Sllai1 		}
1203*2729Sllai1 
1204*2729Sllai1 		if (nvp->v_type == VDIR) {
1205*2729Sllai1 			if (!doingdir) {
1206*2729Sllai1 				error = EISDIR;
1207*2729Sllai1 				goto err_out;
1208*2729Sllai1 			}
1209*2729Sllai1 
1210*2729Sllai1 			if (vn_vfswlock(nvp)) {
1211*2729Sllai1 				error = EBUSY;
1212*2729Sllai1 				goto err_out;
1213*2729Sllai1 			}
1214*2729Sllai1 
1215*2729Sllai1 			if (vn_mountedvfs(nvp) != NULL) {
1216*2729Sllai1 				vn_vfsunlock(nvp);
1217*2729Sllai1 				error = EBUSY;
1218*2729Sllai1 				goto err_out;
1219*2729Sllai1 			}
1220*2729Sllai1 
1221*2729Sllai1 			/* in case dir1 exists in dir2 and "mv dir1 dir2" */
1222*2729Sllai1 			if ((*ndvp)->sdev_nlink > 2) {
1223*2729Sllai1 				vn_vfsunlock(nvp);
1224*2729Sllai1 				error = EEXIST;
1225*2729Sllai1 				goto err_out;
1226*2729Sllai1 			}
1227*2729Sllai1 			vn_vfsunlock(nvp);
1228*2729Sllai1 
1229*2729Sllai1 			(void) sdev_dirdelete(nddv, *ndvp);
1230*2729Sllai1 			*ndvp = NULL;
1231*2729Sllai1 			error = VOP_RMDIR(nddv->sdev_attrvp, nnm,
1232*2729Sllai1 				    nddv->sdev_attrvp, cred);
1233*2729Sllai1 			if (error)
1234*2729Sllai1 				goto err_out;
1235*2729Sllai1 		} else {
1236*2729Sllai1 			if (doingdir) {
1237*2729Sllai1 				error = ENOTDIR;
1238*2729Sllai1 				goto err_out;
1239*2729Sllai1 			}
1240*2729Sllai1 
1241*2729Sllai1 			if (SDEV_IS_PERSIST((*ndvp))) {
1242*2729Sllai1 				bkstore = 1;
1243*2729Sllai1 			}
12442621Sllai1 
12452621Sllai1 			/*
1246*2729Sllai1 			 * get rid of the node from the directory cache
1247*2729Sllai1 			 * note, in case EBUSY is returned, the ZOMBIE
1248*2729Sllai1 			 * node is taken care in sdev_mknode.
12492621Sllai1 			 */
1250*2729Sllai1 			(void) sdev_dirdelete(nddv, *ndvp);
1251*2729Sllai1 			*ndvp = NULL;
1252*2729Sllai1 			if (bkstore) {
1253*2729Sllai1 				error = VOP_REMOVE(nddv->sdev_attrvp,
1254*2729Sllai1 				    nnm, cred);
1255*2729Sllai1 				if (error)
1256*2729Sllai1 				    goto err_out;
12572621Sllai1 			}
12582621Sllai1 		}
12592621Sllai1 	}
12602621Sllai1 
12612621Sllai1 	/* fix the source for a symlink */
12622621Sllai1 	if (vattr.va_type == VLNK) {
12632621Sllai1 		if (odv->sdev_symlink == NULL) {
12642621Sllai1 			error = sdev_follow_link(odv);
1265*2729Sllai1 			if (error) {
1266*2729Sllai1 				error = ENOENT;
1267*2729Sllai1 				goto err_out;
1268*2729Sllai1 			}
12692621Sllai1 		}
12702621Sllai1 		ASSERT(odv->sdev_symlink);
12712621Sllai1 		link = i_ddi_strdup(odv->sdev_symlink, KM_SLEEP);
12722621Sllai1 	}
12732621Sllai1 
1274*2729Sllai1 	/*
1275*2729Sllai1 	 * make a fresh node from the source attrs
1276*2729Sllai1 	 */
1277*2729Sllai1 	ASSERT(RW_WRITE_HELD(&nddv->sdev_contents));
1278*2729Sllai1 	error = sdev_mknode(nddv, nnm, ndvp, &vattr,
1279*2729Sllai1 	    NULL, (void *)link, cred, SDEV_READY);
12802621Sllai1 
12812621Sllai1 	if (link)
12822621Sllai1 		kmem_free(link, strlen(link) + 1);
12832621Sllai1 
1284*2729Sllai1 	if (error)
1285*2729Sllai1 		goto err_out;
1286*2729Sllai1 	ASSERT(*ndvp);
1287*2729Sllai1 	ASSERT((*ndvp)->sdev_state == SDEV_READY);
1288*2729Sllai1 
1289*2729Sllai1 	/* move dir contents */
1290*2729Sllai1 	if (doingdir) {
1291*2729Sllai1 		for (idv = odv->sdev_dot; idv; idv = idv->sdev_next) {
1292*2729Sllai1 			error = sdev_rnmnode(odv, idv,
1293*2729Sllai1 			    (struct sdev_node *)(*ndvp), &ndv,
1294*2729Sllai1 			    idv->sdev_name, cred);
1295*2729Sllai1 
1296*2729Sllai1 			if (error)
1297*2729Sllai1 				goto err_out;
1298*2729Sllai1 			ndv = NULL;
1299*2729Sllai1 		}
1300*2729Sllai1 
1301*2729Sllai1 	}
1302*2729Sllai1 
1303*2729Sllai1 	if ((*ndvp)->sdev_attrvp) {
1304*2729Sllai1 		sdev_update_timestamps((*ndvp)->sdev_attrvp, kcred,
1305*2729Sllai1 		    AT_CTIME|AT_ATIME);
1306*2729Sllai1 	} else {
1307*2729Sllai1 		ASSERT((*ndvp)->sdev_attr);
1308*2729Sllai1 		gethrestime(&now);
1309*2729Sllai1 		(*ndvp)->sdev_attr->va_ctime = now;
1310*2729Sllai1 		(*ndvp)->sdev_attr->va_atime = now;
1311*2729Sllai1 	}
1312*2729Sllai1 
1313*2729Sllai1 	if (nddv->sdev_attrvp) {
1314*2729Sllai1 		sdev_update_timestamps(nddv->sdev_attrvp, kcred,
1315*2729Sllai1 		    AT_MTIME|AT_ATIME);
1316*2729Sllai1 	} else {
1317*2729Sllai1 		ASSERT(nddv->sdev_attr);
1318*2729Sllai1 		gethrestime(&now);
1319*2729Sllai1 		nddv->sdev_attr->va_mtime = now;
1320*2729Sllai1 		nddv->sdev_attr->va_atime = now;
1321*2729Sllai1 	}
1322*2729Sllai1 	rw_exit(&nddv->sdev_contents);
1323*2729Sllai1 	if (!samedir)
1324*2729Sllai1 		rw_exit(&oddv->sdev_contents);
1325*2729Sllai1 
13262621Sllai1 	SDEV_RELE(*ndvp);
1327*2729Sllai1 	return (error);
1328*2729Sllai1 
1329*2729Sllai1 err_out:
1330*2729Sllai1 	rw_exit(&nddv->sdev_contents);
1331*2729Sllai1 	if (!samedir)
1332*2729Sllai1 		rw_exit(&oddv->sdev_contents);
1333*2729Sllai1 	return (error);
13342621Sllai1 }
13352621Sllai1 
13362621Sllai1 /*
13372621Sllai1  * Merge sdev_node specific information into an attribute structure.
13382621Sllai1  *
13392621Sllai1  * note: sdev_node is not locked here
13402621Sllai1  */
13412621Sllai1 void
13422621Sllai1 sdev_vattr_merge(struct sdev_node *dv, struct vattr *vap)
13432621Sllai1 {
13442621Sllai1 	struct vnode *vp = SDEVTOV(dv);
13452621Sllai1 
13462621Sllai1 	vap->va_nlink = dv->sdev_nlink;
13472621Sllai1 	vap->va_nodeid = dv->sdev_ino;
13482621Sllai1 	vap->va_fsid = SDEVTOV(dv->sdev_dotdot)->v_rdev;
13492621Sllai1 	vap->va_type = vp->v_type;
13502621Sllai1 
13512621Sllai1 	if (vp->v_type == VDIR) {
13522621Sllai1 		vap->va_rdev = 0;
13532621Sllai1 		vap->va_fsid = vp->v_rdev;
13542621Sllai1 	} else if (vp->v_type == VLNK) {
13552621Sllai1 		vap->va_rdev = 0;
13562621Sllai1 		vap->va_mode  &= ~S_IFMT;
13572621Sllai1 		vap->va_mode |= S_IFLNK;
13582621Sllai1 	} else if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
13592621Sllai1 		vap->va_rdev = vp->v_rdev;
13602621Sllai1 		vap->va_mode &= ~S_IFMT;
13612621Sllai1 		if (vap->va_type == VCHR)
13622621Sllai1 			vap->va_mode |= S_IFCHR;
13632621Sllai1 		else
13642621Sllai1 			vap->va_mode |= S_IFBLK;
13652621Sllai1 	} else {
13662621Sllai1 		vap->va_rdev = 0;
13672621Sllai1 	}
13682621Sllai1 }
13692621Sllai1 
13702621Sllai1 static struct vattr *
13712621Sllai1 sdev_getdefault_attr(enum vtype type)
13722621Sllai1 {
13732621Sllai1 	if (type == VDIR)
13742621Sllai1 		return (&sdev_vattr_dir);
13752621Sllai1 	else if (type == VCHR)
13762621Sllai1 		return (&sdev_vattr_chr);
13772621Sllai1 	else if (type == VBLK)
13782621Sllai1 		return (&sdev_vattr_blk);
13792621Sllai1 	else if (type == VLNK)
13802621Sllai1 		return (&sdev_vattr_lnk);
13812621Sllai1 	else
13822621Sllai1 		return (NULL);
13832621Sllai1 }
13842621Sllai1 int
13852621Sllai1 sdev_to_vp(struct sdev_node *dv, struct vnode **vpp)
13862621Sllai1 {
13872621Sllai1 	int rv = 0;
13882621Sllai1 	struct vnode *vp = SDEVTOV(dv);
13892621Sllai1 
13902621Sllai1 	switch (vp->v_type) {
13912621Sllai1 	case VCHR:
13922621Sllai1 	case VBLK:
13932621Sllai1 		/*
13942621Sllai1 		 * If vnode is a device, return special vnode instead
13952621Sllai1 		 * (though it knows all about -us- via sp->s_realvp)
13962621Sllai1 		 */
13972621Sllai1 		*vpp = specvp(vp, vp->v_rdev, vp->v_type, kcred);
13982621Sllai1 		VN_RELE(vp);
13992621Sllai1 		if (*vpp == NULLVP)
14002621Sllai1 			rv = ENOSYS;
14012621Sllai1 		break;
14022621Sllai1 	default:	/* most types are returned as is */
14032621Sllai1 		*vpp = vp;
14042621Sllai1 		break;
14052621Sllai1 	}
14062621Sllai1 	return (rv);
14072621Sllai1 }
14082621Sllai1 
14092621Sllai1 /*
14102621Sllai1  * loopback into sdev_lookup()
14112621Sllai1  */
14122621Sllai1 static struct vnode *
14132621Sllai1 devname_find_by_devpath(char *devpath, struct vattr *vattr)
14142621Sllai1 {
14152621Sllai1 	int error = 0;
14162621Sllai1 	struct vnode *vp;
14172621Sllai1 
14182621Sllai1 	error = lookupname(devpath, UIO_SYSSPACE, NO_FOLLOW, NULLVPP, &vp);
14192621Sllai1 	if (error) {
14202621Sllai1 		return (NULL);
14212621Sllai1 	}
14222621Sllai1 
14232621Sllai1 	if (vattr)
14242621Sllai1 		(void) VOP_GETATTR(vp, vattr, 0, kcred);
14252621Sllai1 	return (vp);
14262621Sllai1 }
14272621Sllai1 
14282621Sllai1 /*
14292621Sllai1  * the junction between devname and devfs
14302621Sllai1  */
14312621Sllai1 static struct vnode *
14322621Sllai1 devname_configure_by_path(char *physpath, struct vattr *vattr)
14332621Sllai1 {
14342621Sllai1 	int error = 0;
14352621Sllai1 	struct vnode *vp;
14362621Sllai1 
1437*2729Sllai1 	ASSERT(strncmp(physpath, "/devices/", sizeof ("/devices/") - 1)
14382621Sllai1 	    == 0);
14392621Sllai1 
14402621Sllai1 	error = devfs_lookupname(physpath + sizeof ("/devices/") - 1,
14412621Sllai1 	    NULLVPP, &vp);
14422621Sllai1 	if (error != 0) {
14432621Sllai1 		if (error == ENODEV) {
14442621Sllai1 			cmn_err(CE_CONT, "%s: not found (line %d)\n",
14452621Sllai1 			    physpath, __LINE__);
14462621Sllai1 		}
14472621Sllai1 
14482621Sllai1 		return (NULL);
14492621Sllai1 	}
14502621Sllai1 
14512621Sllai1 	if (vattr)
14522621Sllai1 		(void) VOP_GETATTR(vp, vattr, 0, kcred);
14532621Sllai1 	return (vp);
14542621Sllai1 }
14552621Sllai1 
14562621Sllai1 /*
14572621Sllai1  * junction between devname and root file system, e.g. ufs
14582621Sllai1  */
14592621Sllai1 int
14602621Sllai1 devname_backstore_lookup(struct sdev_node *ddv, char *nm, struct vnode **rvp)
14612621Sllai1 {
14622621Sllai1 	struct vnode *rdvp = ddv->sdev_attrvp;
14632621Sllai1 	int rval = 0;
14642621Sllai1 
14652621Sllai1 	ASSERT(rdvp);
14662621Sllai1 
14672621Sllai1 	rval = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, kcred);
14682621Sllai1 	return (rval);
14692621Sllai1 }
14702621Sllai1 
14712621Sllai1 static int
14722621Sllai1 sdev_filldir_from_store(struct sdev_node *ddv, int dlen, struct cred *cred)
14732621Sllai1 {
14742621Sllai1 	struct sdev_node *dv = NULL;
14752621Sllai1 	char	*nm;
14762621Sllai1 	struct vnode *dirvp;
14772621Sllai1 	int	error;
14782621Sllai1 	vnode_t	*vp;
14792621Sllai1 	int eof;
14802621Sllai1 	struct iovec iov;
14812621Sllai1 	struct uio uio;
14822621Sllai1 	struct dirent64 *dp;
14832621Sllai1 	dirent64_t *dbuf;
14842621Sllai1 	size_t dbuflen;
14852621Sllai1 	struct vattr vattr;
14862621Sllai1 	char *link = NULL;
14872621Sllai1 
14882621Sllai1 	if (ddv->sdev_attrvp == NULL)
14892621Sllai1 		return (0);
14902621Sllai1 	if (!(ddv->sdev_flags & SDEV_BUILD))
14912621Sllai1 		return (0);
14922621Sllai1 
14932621Sllai1 	dirvp = ddv->sdev_attrvp;
14942621Sllai1 	VN_HOLD(dirvp);
14952621Sllai1 	dbuf = kmem_zalloc(dlen, KM_SLEEP);
14962621Sllai1 
14972621Sllai1 	uio.uio_iov = &iov;
14982621Sllai1 	uio.uio_iovcnt = 1;
14992621Sllai1 	uio.uio_segflg = UIO_SYSSPACE;
15002621Sllai1 	uio.uio_fmode = 0;
15012621Sllai1 	uio.uio_extflg = UIO_COPY_CACHED;
15022621Sllai1 	uio.uio_loffset = 0;
15032621Sllai1 	uio.uio_llimit = MAXOFFSET_T;
15042621Sllai1 
15052621Sllai1 	eof = 0;
15062621Sllai1 	error = 0;
15072621Sllai1 	while (!error && !eof) {
15082621Sllai1 		uio.uio_resid = dlen;
15092621Sllai1 		iov.iov_base = (char *)dbuf;
15102621Sllai1 		iov.iov_len = dlen;
15112621Sllai1 		(void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL);
15122621Sllai1 		error = VOP_READDIR(dirvp, &uio, kcred, &eof);
15132621Sllai1 		VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL);
15142621Sllai1 
15152621Sllai1 		dbuflen = dlen - uio.uio_resid;
15162621Sllai1 		if (error || dbuflen == 0)
15172621Sllai1 			break;
15182621Sllai1 
15192621Sllai1 		if (!(ddv->sdev_flags & SDEV_BUILD)) {
15202621Sllai1 			error = 0;
15212621Sllai1 			break;
15222621Sllai1 		}
15232621Sllai1 
15242621Sllai1 		for (dp = dbuf; ((intptr_t)dp <
15252621Sllai1 		    (intptr_t)dbuf + dbuflen);
15262621Sllai1 		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
15272621Sllai1 			nm = dp->d_name;
15282621Sllai1 
15292621Sllai1 			if (strcmp(nm, ".") == 0 ||
15302621Sllai1 			    strcmp(nm, "..") == 0)
15312621Sllai1 				continue;
15322621Sllai1 
15332621Sllai1 			vp = NULLVP;
15342621Sllai1 			dv = sdev_cache_lookup(ddv, nm);
15352621Sllai1 			if (dv) {
15362621Sllai1 				if (dv->sdev_state != SDEV_ZOMBIE) {
15372621Sllai1 					SDEV_SIMPLE_RELE(dv);
15382621Sllai1 				} else {
15392621Sllai1 					/*
15402621Sllai1 					 * A ZOMBIE node may not have been
15412621Sllai1 					 * cleaned up from the backing store,
15422621Sllai1 					 * bypass this entry in this case,
15432621Sllai1 					 * and clean it up from the directory
15442621Sllai1 					 * cache if this is the last call.
15452621Sllai1 					 */
15462621Sllai1 					(void) sdev_dirdelete(ddv, dv);
15472621Sllai1 				}
15482621Sllai1 				continue;
15492621Sllai1 			}
15502621Sllai1 
15512621Sllai1 			/* refill the cache if not already */
15522621Sllai1 			error = devname_backstore_lookup(ddv, nm, &vp);
15532621Sllai1 			if (error)
15542621Sllai1 				continue;
15552621Sllai1 
15562621Sllai1 			vattr.va_mask = AT_MODE|AT_UID|AT_GID;
15572621Sllai1 			error = VOP_GETATTR(vp, &vattr, 0, cred);
15582621Sllai1 			if (error)
15592621Sllai1 				continue;
15602621Sllai1 
15612621Sllai1 			if (vattr.va_type == VLNK) {
15622621Sllai1 				error = sdev_getlink(vp, &link);
15632621Sllai1 				if (error) {
15642621Sllai1 					continue;
15652621Sllai1 				}
15662621Sllai1 				ASSERT(link != NULL);
15672621Sllai1 			}
15682621Sllai1 
15692621Sllai1 			if (!rw_tryupgrade(&ddv->sdev_contents)) {
15702621Sllai1 				rw_exit(&ddv->sdev_contents);
15712621Sllai1 				rw_enter(&ddv->sdev_contents, RW_WRITER);
15722621Sllai1 			}
15732621Sllai1 			error = sdev_mknode(ddv, nm, &dv, &vattr, vp, link,
15742621Sllai1 			    cred, SDEV_READY);
15752621Sllai1 			rw_downgrade(&ddv->sdev_contents);
15762621Sllai1 
15772621Sllai1 			if (link != NULL) {
15782621Sllai1 				kmem_free(link, strlen(link) + 1);
15792621Sllai1 				link = NULL;
15802621Sllai1 			}
15812621Sllai1 
15822621Sllai1 			if (!error) {
15832621Sllai1 				ASSERT(dv);
15842621Sllai1 				ASSERT(dv->sdev_state != SDEV_ZOMBIE);
15852621Sllai1 				SDEV_SIMPLE_RELE(dv);
15862621Sllai1 			}
15872621Sllai1 			vp = NULL;
15882621Sllai1 			dv = NULL;
15892621Sllai1 		}
15902621Sllai1 	}
15912621Sllai1 
15922621Sllai1 done:
15932621Sllai1 	VN_RELE(dirvp);
15942621Sllai1 	kmem_free(dbuf, dlen);
15952621Sllai1 
15962621Sllai1 	return (error);
15972621Sllai1 }
15982621Sllai1 
15992621Sllai1 static int
16002621Sllai1 sdev_filldir_dynamic(struct sdev_node *ddv)
16012621Sllai1 {
16022621Sllai1 	int error;
16032621Sllai1 	int i;
16042621Sllai1 	struct vattr *vap;
16052621Sllai1 	char *nm = NULL;
16062621Sllai1 	struct sdev_node *dv = NULL;
16072621Sllai1 
16082621Sllai1 	if (!(ddv->sdev_flags & SDEV_BUILD)) {
16092621Sllai1 		return (0);
16102621Sllai1 	}
16112621Sllai1 
16122621Sllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
16132621Sllai1 	if (!rw_tryupgrade(&ddv->sdev_contents)) {
16142621Sllai1 		rw_exit(&ddv->sdev_contents);
16152621Sllai1 		rw_enter(&ddv->sdev_contents, RW_WRITER);
16162621Sllai1 	}
16172621Sllai1 
16182621Sllai1 	vap = sdev_getdefault_attr(VDIR);
16192621Sllai1 	for (i = 0; vtab[i].vt_name != NULL; i++) {
16202621Sllai1 		nm = vtab[i].vt_name;
16212621Sllai1 		ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
16222621Sllai1 		error = sdev_mknode(ddv, nm, &dv, vap, NULL,
16232621Sllai1 		    NULL, kcred, SDEV_READY);
16242621Sllai1 		if (error)
16252621Sllai1 			continue;
16262621Sllai1 		ASSERT(dv);
16272621Sllai1 		ASSERT(dv->sdev_state != SDEV_ZOMBIE);
16282621Sllai1 		SDEV_SIMPLE_RELE(dv);
16292621Sllai1 		dv = NULL;
16302621Sllai1 	}
16312621Sllai1 	rw_downgrade(&ddv->sdev_contents);
16322621Sllai1 	return (0);
16332621Sllai1 }
16342621Sllai1 
16352621Sllai1 /*
16362621Sllai1  * Creating a backing store entry based on sdev_attr.
16372621Sllai1  * This is called either as part of node creation in a persistent directory
16382621Sllai1  * or from setattr/setsecattr to persist access attributes across reboot.
16392621Sllai1  */
16402621Sllai1 int
16412621Sllai1 sdev_shadow_node(struct sdev_node *dv, struct cred *cred)
16422621Sllai1 {
16432621Sllai1 	int error = 0;
16442621Sllai1 	struct vnode *dvp = SDEVTOV(dv->sdev_dotdot);
16452621Sllai1 	struct vnode *rdvp = VTOSDEV(dvp)->sdev_attrvp;
16462621Sllai1 	struct vattr *vap = dv->sdev_attr;
16472621Sllai1 	char *nm = dv->sdev_name;
16482621Sllai1 	struct vnode *tmpvp, **rvp = &tmpvp, *rrvp = NULL;
16492621Sllai1 
16502621Sllai1 	ASSERT(dv && dv->sdev_name && rdvp);
16512621Sllai1 	ASSERT(RW_WRITE_HELD(&dv->sdev_contents) && dv->sdev_attrvp == NULL);
16522621Sllai1 
16532621Sllai1 lookup:
16542621Sllai1 	/* try to find it in the backing store */
16552621Sllai1 	error = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, cred);
16562621Sllai1 	if (error == 0) {
16572621Sllai1 		if (VOP_REALVP(*rvp, &rrvp) == 0) {
16582621Sllai1 			VN_HOLD(rrvp);
16592621Sllai1 			VN_RELE(*rvp);
16602621Sllai1 			*rvp = rrvp;
16612621Sllai1 		}
16622621Sllai1 
16632621Sllai1 		kmem_free(dv->sdev_attr, sizeof (vattr_t));
16642621Sllai1 		dv->sdev_attr = NULL;
16652621Sllai1 		dv->sdev_attrvp = *rvp;
16662621Sllai1 		return (0);
16672621Sllai1 	}
16682621Sllai1 
16692621Sllai1 	/* let's try to persist the node */
16702621Sllai1 	gethrestime(&vap->va_atime);
16712621Sllai1 	vap->va_mtime = vap->va_atime;
16722621Sllai1 	vap->va_ctime = vap->va_atime;
16732621Sllai1 	vap->va_mask |= AT_TYPE|AT_MODE;
16742621Sllai1 	switch (vap->va_type) {
16752621Sllai1 	case VDIR:
16762621Sllai1 		error = VOP_MKDIR(rdvp, nm, vap, rvp, cred);
16772621Sllai1 		sdcmn_err9(("sdev_shadow_node: mkdir vp %p error %d\n",
16782621Sllai1 		    (void *)(*rvp), error));
16792621Sllai1 		break;
16802621Sllai1 	case VCHR:
16812621Sllai1 	case VBLK:
16822621Sllai1 	case VREG:
16832621Sllai1 	case VDOOR:
16842621Sllai1 		error = VOP_CREATE(rdvp, nm, vap, NONEXCL, VREAD|VWRITE,
16852621Sllai1 		    rvp, cred, 0);
16862621Sllai1 		sdcmn_err9(("sdev_shadow_node: create vp %p, error %d\n",
16872621Sllai1 		    (void *)(*rvp), error));
16882621Sllai1 		if (!error)
16892621Sllai1 			VN_RELE(*rvp);
16902621Sllai1 		break;
16912621Sllai1 	case VLNK:
16922621Sllai1 		ASSERT(dv->sdev_symlink);
16932621Sllai1 		error = VOP_SYMLINK(rdvp, nm, vap, dv->sdev_symlink, cred);
16942621Sllai1 		sdcmn_err9(("sdev_shadow_node: create symlink error %d\n",
16952621Sllai1 		    error));
16962621Sllai1 		break;
16972621Sllai1 	default:
16982621Sllai1 		cmn_err(CE_PANIC, "dev: %s: sdev_shadow_node "
16992621Sllai1 		    "create\n", nm);
17002621Sllai1 		/*NOTREACHED*/
17012621Sllai1 	}
17022621Sllai1 
17032621Sllai1 	/* go back to lookup to factor out spec node and set attrvp */
17042621Sllai1 	if (error == 0)
17052621Sllai1 		goto lookup;
17062621Sllai1 
17072621Sllai1 	return (error);
17082621Sllai1 }
17092621Sllai1 
17102621Sllai1 static int
17112621Sllai1 sdev_cache_add(struct sdev_node *ddv, struct sdev_node **dv, char *nm)
17122621Sllai1 {
17132621Sllai1 	int error = 0;
17142621Sllai1 	struct sdev_node *dup = NULL;
17152621Sllai1 
17162621Sllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
17172621Sllai1 	if ((dup = sdev_findbyname(ddv, nm)) == NULL) {
17182621Sllai1 		sdev_direnter(ddv, *dv);
17192621Sllai1 	} else {
17202621Sllai1 		if (dup->sdev_state == SDEV_ZOMBIE) {
17212621Sllai1 			error = sdev_dirdelete(ddv, dup);
17222621Sllai1 			/*
17232621Sllai1 			 * The ZOMBIE node is still hanging
17242621Sllai1 			 * around with more than one reference counts.
17252621Sllai1 			 * Fail the new node creation so that
17262621Sllai1 			 * the directory cache won't have
17272621Sllai1 			 * duplicate entries for the same named node
17282621Sllai1 			 */
17292621Sllai1 			if (error == EBUSY) {
17302621Sllai1 				SDEV_SIMPLE_RELE(*dv);
17312621Sllai1 				sdev_nodedestroy(*dv, 0);
17322621Sllai1 				*dv = NULL;
17332621Sllai1 				return (error);
17342621Sllai1 			}
17352621Sllai1 			sdev_direnter(ddv, *dv);
17362621Sllai1 		} else {
17372621Sllai1 			ASSERT((*dv)->sdev_state != SDEV_ZOMBIE);
17382621Sllai1 			SDEV_SIMPLE_RELE(*dv);
17392621Sllai1 			sdev_nodedestroy(*dv, 0);
17402621Sllai1 			*dv = dup;
17412621Sllai1 		}
17422621Sllai1 	}
17432621Sllai1 
17442621Sllai1 	return (0);
17452621Sllai1 }
17462621Sllai1 
17472621Sllai1 static int
17482621Sllai1 sdev_cache_delete(struct sdev_node *ddv, struct sdev_node **dv)
17492621Sllai1 {
17502621Sllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
17512621Sllai1 	return (sdev_dirdelete(ddv, *dv));
17522621Sllai1 }
17532621Sllai1 
17542621Sllai1 /*
17552621Sllai1  * update the in-core directory cache
17562621Sllai1  */
17572621Sllai1 int
17582621Sllai1 sdev_cache_update(struct sdev_node *ddv, struct sdev_node **dv, char *nm,
17592621Sllai1     sdev_cache_ops_t ops)
17602621Sllai1 {
17612621Sllai1 	int error = 0;
17622621Sllai1 
17632621Sllai1 	ASSERT((SDEV_HELD(*dv)));
17642621Sllai1 
17652621Sllai1 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
17662621Sllai1 	switch (ops) {
17672621Sllai1 	case SDEV_CACHE_ADD:
17682621Sllai1 		error = sdev_cache_add(ddv, dv, nm);
17692621Sllai1 		break;
17702621Sllai1 	case SDEV_CACHE_DELETE:
17712621Sllai1 		error = sdev_cache_delete(ddv, dv);
17722621Sllai1 		break;
17732621Sllai1 	default:
17742621Sllai1 		break;
17752621Sllai1 	}
17762621Sllai1 
17772621Sllai1 	return (error);
17782621Sllai1 }
17792621Sllai1 
17802621Sllai1 /*
17812621Sllai1  * retrive the named entry from the directory cache
17822621Sllai1  */
17832621Sllai1 struct sdev_node *
17842621Sllai1 sdev_cache_lookup(struct sdev_node *ddv, char *nm)
17852621Sllai1 {
17862621Sllai1 	struct sdev_node *dv = NULL;
17872621Sllai1 
17882621Sllai1 	ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
17892621Sllai1 	dv = sdev_findbyname(ddv, nm);
17902621Sllai1 
17912621Sllai1 	return (dv);
17922621Sllai1 }
17932621Sllai1 
17942621Sllai1 /*
17952621Sllai1  * Implicit reconfig for nodes constructed by a link generator
17962621Sllai1  * Start devfsadm if needed, or if devfsadm is in progress,
17972621Sllai1  * prepare to block on devfsadm either completing or
17982621Sllai1  * constructing the desired node.  As devfsadmd is global
17992621Sllai1  * in scope, constructing all necessary nodes, we only
18002621Sllai1  * need to initiate it once.
18012621Sllai1  */
18022621Sllai1 static int
18032621Sllai1 sdev_call_devfsadmd(struct sdev_node *ddv, struct sdev_node *dv, char *nm)
18042621Sllai1 {
18052621Sllai1 	int error = 0;
18062621Sllai1 
18072621Sllai1 	if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) {
18082621Sllai1 		sdcmn_err6(("lookup: waiting for %s/%s, 0x%x\n",
18092621Sllai1 		    ddv->sdev_name, nm, devfsadm_state));
18102621Sllai1 		mutex_enter(&dv->sdev_lookup_lock);
18112621Sllai1 		SDEV_BLOCK_OTHERS(dv, (SDEV_LOOKUP | SDEV_LGWAITING));
18122621Sllai1 		mutex_exit(&dv->sdev_lookup_lock);
18132621Sllai1 		error = 0;
18142621Sllai1 	} else if (!DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state)) {
18152621Sllai1 		sdcmn_err6(("lookup %s/%s starting devfsadm, 0x%x\n",
18162621Sllai1 			ddv->sdev_name, nm, devfsadm_state));
18172621Sllai1 
18182621Sllai1 		sdev_devfsadmd_thread(ddv, dv, kcred);
18192621Sllai1 		mutex_enter(&dv->sdev_lookup_lock);
18202621Sllai1 		SDEV_BLOCK_OTHERS(dv,
18212621Sllai1 		    (SDEV_LOOKUP | SDEV_LGWAITING));
18222621Sllai1 		mutex_exit(&dv->sdev_lookup_lock);
18232621Sllai1 		error = 0;
18242621Sllai1 	} else {
18252621Sllai1 		error = -1;
18262621Sllai1 	}
18272621Sllai1 
18282621Sllai1 	return (error);
18292621Sllai1 }
18302621Sllai1 
18312621Sllai1 static int
18322621Sllai1 sdev_call_modulelookup(struct sdev_node *ddv, struct sdev_node **dvp, char *nm,
18332621Sllai1     int (*fn)(char *, devname_handle_t *, struct cred *), struct cred *cred)
18342621Sllai1 {
18352621Sllai1 	struct vnode *rvp = NULL;
18362621Sllai1 	int error = 0;
18372621Sllai1 	struct vattr *vap;
18382621Sllai1 	devname_spec_t spec;
18392621Sllai1 	devname_handle_t *hdl;
18402621Sllai1 	void *args = NULL;
18412621Sllai1 	struct sdev_node *dv = *dvp;
18422621Sllai1 
18432621Sllai1 	ASSERT(dv && ddv);
18442621Sllai1 	hdl = &(dv->sdev_handle);
18452621Sllai1 	ASSERT(hdl->dh_data == dv);
18462621Sllai1 	mutex_enter(&dv->sdev_lookup_lock);
18472621Sllai1 	SDEV_BLOCK_OTHERS(dv, SDEV_LOOKUP);
18482621Sllai1 	mutex_exit(&dv->sdev_lookup_lock);
18492621Sllai1 	error = (*fn)(nm, hdl, cred);
18502621Sllai1 	if (error) {
18512621Sllai1 		return (error);
18522621Sllai1 	}
18532621Sllai1 
18542621Sllai1 	spec = hdl->dh_spec;
18552621Sllai1 	args = hdl->dh_args;
18562621Sllai1 	ASSERT(args);
18572621Sllai1 
18582621Sllai1 	switch (spec) {
18592621Sllai1 	case DEVNAME_NS_PATH:
18602621Sllai1 		/*
18612621Sllai1 		 * symlink of:
18622621Sllai1 		 *	/dev/dir/nm -> /device/...
18632621Sllai1 		 */
18642621Sllai1 		rvp = devname_configure_by_path((char *)args, NULL);
18652621Sllai1 		break;
18662621Sllai1 	case DEVNAME_NS_DEV:
18672621Sllai1 		/*
18682621Sllai1 		 * symlink of:
18692621Sllai1 		 *	/dev/dir/nm -> /dev/...
18702621Sllai1 		 */
18712621Sllai1 		rvp = devname_find_by_devpath((char *)args, NULL);
18722621Sllai1 		break;
18732621Sllai1 	default:
18742621Sllai1 		if (args)
18752621Sllai1 			kmem_free((char *)args, strlen(args) + 1);
18762621Sllai1 		return (ENOENT);
18772621Sllai1 
18782621Sllai1 	}
18792621Sllai1 
18802621Sllai1 	if (rvp == NULL) {
18812621Sllai1 		if (args)
18822621Sllai1 			kmem_free((char *)args, strlen(args) + 1);
18832621Sllai1 		return (ENOENT);
18842621Sllai1 	} else {
18852621Sllai1 		vap = sdev_getdefault_attr(VLNK);
18862621Sllai1 		ASSERT(RW_READ_HELD(&ddv->sdev_contents));
18872621Sllai1 		/*
18882621Sllai1 		 * Could sdev_mknode return a different dv_node
18892621Sllai1 		 * once the lock is dropped?
18902621Sllai1 		 */
18912621Sllai1 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
18922621Sllai1 			rw_exit(&ddv->sdev_contents);
18932621Sllai1 			rw_enter(&ddv->sdev_contents, RW_WRITER);
18942621Sllai1 		}
18952621Sllai1 		error = sdev_mknode(ddv, nm, &dv, vap, NULL, args, cred,
18962621Sllai1 		    SDEV_READY);
18972621Sllai1 		rw_downgrade(&ddv->sdev_contents);
18982621Sllai1 		if (error) {
18992621Sllai1 			if (args)
19002621Sllai1 				kmem_free((char *)args, strlen(args) + 1);
19012621Sllai1 			return (error);
19022621Sllai1 		} else {
19032621Sllai1 			mutex_enter(&dv->sdev_lookup_lock);
19042621Sllai1 			SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
19052621Sllai1 			mutex_exit(&dv->sdev_lookup_lock);
19062621Sllai1 			error = 0;
19072621Sllai1 		}
19082621Sllai1 	}
19092621Sllai1 
19102621Sllai1 	if (args)
19112621Sllai1 		kmem_free((char *)args, strlen(args) + 1);
19122621Sllai1 
19132621Sllai1 	*dvp = dv;
19142621Sllai1 	return (0);
19152621Sllai1 }
19162621Sllai1 
19172621Sllai1 /*
19182621Sllai1  *  Support for specialized device naming construction mechanisms
19192621Sllai1  */
19202621Sllai1 static int
19212621Sllai1 sdev_call_dircallback(struct sdev_node *ddv, struct sdev_node **dvp, char *nm,
19222621Sllai1     int (*callback)(struct sdev_node *, char *, void **, struct cred *,
19232621Sllai1     void *, char *), int flags, struct cred *cred)
19242621Sllai1 {
19252621Sllai1 	int rv = 0;
19262621Sllai1 	char *physpath = NULL;
19272621Sllai1 	struct vnode *rvp = NULL;
19282621Sllai1 	struct vattr vattr;
19292621Sllai1 	struct vattr *vap;
19302621Sllai1 	struct sdev_node *dv = *dvp;
19312621Sllai1 
19322621Sllai1 	mutex_enter(&dv->sdev_lookup_lock);
19332621Sllai1 	SDEV_BLOCK_OTHERS(dv, SDEV_LOOKUP);
19342621Sllai1 	mutex_exit(&dv->sdev_lookup_lock);
19352621Sllai1 
19362621Sllai1 	/* for non-devfsadm devices */
19372621Sllai1 	if (flags & SDEV_PATH) {
19382621Sllai1 		physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
19392621Sllai1 		rv = callback(ddv, nm, (void *)&physpath, kcred, NULL,
19402621Sllai1 		    NULL);
19412621Sllai1 		if (rv) {
19422621Sllai1 			kmem_free(physpath, MAXPATHLEN);
19432621Sllai1 			return (-1);
19442621Sllai1 		}
19452621Sllai1 
19462621Sllai1 		ASSERT(physpath);
19472621Sllai1 		rvp = devname_configure_by_path(physpath, NULL);
19482621Sllai1 		if (rvp == NULL) {
19492621Sllai1 			sdcmn_err3(("devname_configure_by_path: "
19502621Sllai1 			    "failed for /dev/%s/%s\n",
19512621Sllai1 			    ddv->sdev_name, nm));
19522621Sllai1 			kmem_free(physpath, MAXPATHLEN);
19532621Sllai1 			rv = -1;
19542621Sllai1 		} else {
19552621Sllai1 			vap = sdev_getdefault_attr(VLNK);
19562621Sllai1 			ASSERT(RW_READ_HELD(&ddv->sdev_contents));
19572621Sllai1 
19582621Sllai1 			/*
19592621Sllai1 			 * Sdev_mknode may return back a different sdev_node
19602621Sllai1 			 * that was created by another thread that
19612621Sllai1 			 * raced to the directroy cache before this thread.
19622621Sllai1 			 *
19632621Sllai1 			 * With current directory cache mechanism
19642621Sllai1 			 * (linked list with the sdev_node name as
19652621Sllai1 			 * the entity key), this is a way to make sure
19662621Sllai1 			 * only one entry exists for the same name
19672621Sllai1 			 * in the same directory. The outcome is
19682621Sllai1 			 * the winner wins.
19692621Sllai1 			 */
19702621Sllai1 			if (!rw_tryupgrade(&ddv->sdev_contents)) {
19712621Sllai1 				rw_exit(&ddv->sdev_contents);
19722621Sllai1 				rw_enter(&ddv->sdev_contents, RW_WRITER);
19732621Sllai1 			}
19742621Sllai1 			rv = sdev_mknode(ddv, nm, &dv, vap, NULL,
19752621Sllai1 			    (void *)physpath, cred, SDEV_READY);
19762621Sllai1 			rw_downgrade(&ddv->sdev_contents);
19772621Sllai1 			kmem_free(physpath, MAXPATHLEN);
19782621Sllai1 			if (rv) {
19792621Sllai1 				return (rv);
19802621Sllai1 			} else {
19812621Sllai1 				mutex_enter(&dv->sdev_lookup_lock);
19822621Sllai1 				SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
19832621Sllai1 				mutex_exit(&dv->sdev_lookup_lock);
19842621Sllai1 				return (0);
19852621Sllai1 			}
19862621Sllai1 		}
19872621Sllai1 	} else if (flags & SDEV_VNODE) {
19882621Sllai1 		/*
19892621Sllai1 		 * DBNR has its own way to create the device
19902621Sllai1 		 * and return a backing store vnode in rvp
19912621Sllai1 		 */
19922621Sllai1 		ASSERT(callback);
19932621Sllai1 		rv = callback(ddv, nm, (void *)&rvp, kcred, NULL, NULL);
19942621Sllai1 		if (rv || (rvp == NULL)) {
19952621Sllai1 			sdcmn_err3(("devname_lookup_func: SDEV_VNODE "
19962621Sllai1 			    "callback failed \n"));
19972621Sllai1 			return (-1);
19982621Sllai1 		}
19992621Sllai1 		vap = sdev_getdefault_attr(rvp->v_type);
20002621Sllai1 		if (vap == NULL)
20012621Sllai1 			return (-1);
20022621Sllai1 
20032621Sllai1 		ASSERT(RW_READ_HELD(&ddv->sdev_contents));
20042621Sllai1 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
20052621Sllai1 			rw_exit(&ddv->sdev_contents);
20062621Sllai1 			rw_enter(&ddv->sdev_contents, RW_WRITER);
20072621Sllai1 		}
20082621Sllai1 		rv = sdev_mknode(ddv, nm, &dv, vap, rvp, NULL,
20092621Sllai1 		    cred, SDEV_READY);
20102621Sllai1 		rw_downgrade(&ddv->sdev_contents);
20112621Sllai1 		if (rv)
20122621Sllai1 			return (rv);
20132621Sllai1 
20142621Sllai1 		mutex_enter(&dv->sdev_lookup_lock);
20152621Sllai1 		SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
20162621Sllai1 		mutex_exit(&dv->sdev_lookup_lock);
20172621Sllai1 		return (0);
20182621Sllai1 	} else if (flags & SDEV_VATTR) {
20192621Sllai1 		/*
20202621Sllai1 		 * /dev/pts
20212621Sllai1 		 *
20222621Sllai1 		 * DBNR has its own way to create the device
20232621Sllai1 		 * "0" is returned upon success.
20242621Sllai1 		 *
20252621Sllai1 		 * callback is responsible to set the basic attributes,
20262621Sllai1 		 * e.g. va_type/va_uid/va_gid/
20272621Sllai1 		 *    dev_t if VCHR or VBLK/
20282621Sllai1 		 */
20292621Sllai1 		ASSERT(callback);
20302621Sllai1 		rv = callback(ddv, nm, (void *)&vattr, kcred, NULL, NULL);
20312621Sllai1 		if (rv) {
20322621Sllai1 			sdcmn_err3(("devname_lookup_func: SDEV_NONE "
20332621Sllai1 			    "callback failed \n"));
20342621Sllai1 			return (-1);
20352621Sllai1 		}
20362621Sllai1 
20372621Sllai1 		ASSERT(RW_READ_HELD(&ddv->sdev_contents));
20382621Sllai1 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
20392621Sllai1 			rw_exit(&ddv->sdev_contents);
20402621Sllai1 			rw_enter(&ddv->sdev_contents, RW_WRITER);
20412621Sllai1 		}
20422621Sllai1 		rv = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL,
20432621Sllai1 		    cred, SDEV_READY);
20442621Sllai1 		rw_downgrade(&ddv->sdev_contents);
20452621Sllai1 
20462621Sllai1 		if (rv)
20472621Sllai1 			return (rv);
20482621Sllai1 
20492621Sllai1 		mutex_enter(&dv->sdev_lookup_lock);
20502621Sllai1 		SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
20512621Sllai1 		mutex_exit(&dv->sdev_lookup_lock);
20522621Sllai1 		return (0);
20532621Sllai1 	} else {
20542621Sllai1 		impossible(("lookup: %s/%s by %s not supported (%d)\n",
20552621Sllai1 		    SDEVTOV(ddv)->v_path, nm, curproc->p_user.u_comm,
20562621Sllai1 		    __LINE__));
20572621Sllai1 		rv = -1;
20582621Sllai1 	}
20592621Sllai1 
20602621Sllai1 	*dvp = dv;
20612621Sllai1 	return (rv);
20622621Sllai1 }
20632621Sllai1 
20642621Sllai1 static int
20652621Sllai1 is_devfsadm_thread(char *exec_name)
20662621Sllai1 {
20672621Sllai1 	/*
20682621Sllai1 	 * note: because devfsadmd -> /usr/sbin/devfsadm
20692621Sllai1 	 * it is safe to use "devfsadm" to capture the lookups
20702621Sllai1 	 * from devfsadm and its daemon version.
20712621Sllai1 	 */
20722621Sllai1 	if (strcmp(exec_name, "devfsadm") == 0)
20732621Sllai1 		return (1);
20742621Sllai1 	return (0);
20752621Sllai1 }
20762621Sllai1 
20772621Sllai1 
20782621Sllai1 /*
20792621Sllai1  * Lookup Order:
20802621Sllai1  *	sdev_node cache;
20812621Sllai1  *	backing store (SDEV_PERSIST);
20822621Sllai1  *	DBNR: a. dir_ops implemented in the loadable modules;
20832621Sllai1  *	      b. vnode ops in vtab.
20842621Sllai1  */
20852621Sllai1 int
20862621Sllai1 devname_lookup_func(struct sdev_node *ddv, char *nm, struct vnode **vpp,
20872621Sllai1     struct cred *cred, int (*callback)(struct sdev_node *, char *, void **,
20882621Sllai1     struct cred *, void *, char *), int flags)
20892621Sllai1 {
20902621Sllai1 	int rv = 0, nmlen;
20912621Sllai1 	struct vnode *rvp = NULL;
20922621Sllai1 	struct sdev_node *dv = NULL;
20932621Sllai1 	int	retried = 0;
20942621Sllai1 	int	error = 0;
20952621Sllai1 	struct devname_nsmap *map = NULL;
20962621Sllai1 	struct devname_ops *dirops = NULL;
20972621Sllai1 	int (*fn)(char *, devname_handle_t *, struct cred *) = NULL;
20982621Sllai1 	struct vattr vattr;
20992621Sllai1 	char *lookup_thread = curproc->p_user.u_comm;
21002621Sllai1 	int failed_flags = 0;
21012621Sllai1 	int (*vtor)(struct sdev_node *) = NULL;
21022621Sllai1 	int state;
21032621Sllai1 	int parent_state;
21042621Sllai1 	char *link = NULL;
21052621Sllai1 
21062621Sllai1 	if (SDEVTOV(ddv)->v_type != VDIR)
21072621Sllai1 		return (ENOTDIR);
21082621Sllai1 
21092621Sllai1 	/*
21102621Sllai1 	 * Empty name or ., return node itself.
21112621Sllai1 	 */
21122621Sllai1 	nmlen = strlen(nm);
21132621Sllai1 	if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
21142621Sllai1 		*vpp = SDEVTOV(ddv);
21152621Sllai1 		VN_HOLD(*vpp);
21162621Sllai1 		return (0);
21172621Sllai1 	}
21182621Sllai1 
21192621Sllai1 	/*
21202621Sllai1 	 * .., return the parent directory
21212621Sllai1 	 */
21222621Sllai1 	if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
21232621Sllai1 		*vpp = SDEVTOV(ddv->sdev_dotdot);
21242621Sllai1 		VN_HOLD(*vpp);
21252621Sllai1 		return (0);
21262621Sllai1 	}
21272621Sllai1 
21282621Sllai1 	rw_enter(&ddv->sdev_contents, RW_READER);
21292621Sllai1 	if (ddv->sdev_flags & SDEV_VTOR) {
21302621Sllai1 		vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
21312621Sllai1 		ASSERT(vtor);
21322621Sllai1 	}
21332621Sllai1 
21342621Sllai1 tryagain:
21352621Sllai1 	/*
21362621Sllai1 	 * (a) directory cache lookup:
21372621Sllai1 	 */
21382621Sllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
21392621Sllai1 	parent_state = ddv->sdev_state;
21402621Sllai1 	dv = sdev_cache_lookup(ddv, nm);
21412621Sllai1 	if (dv) {
21422621Sllai1 		state = dv->sdev_state;
21432621Sllai1 		switch (state) {
21442621Sllai1 		case SDEV_INIT:
21452621Sllai1 			if (is_devfsadm_thread(lookup_thread))
21462621Sllai1 				break;
21472621Sllai1 
21482621Sllai1 			/* ZOMBIED parent won't allow node creation */
21492621Sllai1 			if (parent_state == SDEV_ZOMBIE) {
21502621Sllai1 				SD_TRACE_FAILED_LOOKUP(ddv, nm,
21512621Sllai1 				    retried);
21522621Sllai1 				goto nolock_notfound;
21532621Sllai1 			}
21542621Sllai1 
21552621Sllai1 			mutex_enter(&dv->sdev_lookup_lock);
21562621Sllai1 			/* compensate the threads started after devfsadm */
21572621Sllai1 			if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
21582621Sllai1 			    !(SDEV_IS_LOOKUP(dv)))
21592621Sllai1 				SDEV_BLOCK_OTHERS(dv,
21602621Sllai1 				    (SDEV_LOOKUP | SDEV_LGWAITING));
21612621Sllai1 
21622621Sllai1 			if (SDEV_IS_LOOKUP(dv)) {
21632621Sllai1 				failed_flags |= SLF_REBUILT;
21642621Sllai1 				rw_exit(&ddv->sdev_contents);
21652621Sllai1 				error = sdev_wait4lookup(dv, SDEV_LOOKUP);
21662621Sllai1 				mutex_exit(&dv->sdev_lookup_lock);
21672621Sllai1 				rw_enter(&ddv->sdev_contents, RW_READER);
21682621Sllai1 
21692621Sllai1 				if (error != 0) {
21702621Sllai1 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
21712621Sllai1 					    retried);
21722621Sllai1 					goto nolock_notfound;
21732621Sllai1 				}
21742621Sllai1 
21752621Sllai1 				state = dv->sdev_state;
21762621Sllai1 				if (state == SDEV_INIT) {
21772621Sllai1 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
21782621Sllai1 					    retried);
21792621Sllai1 					goto nolock_notfound;
21802621Sllai1 				} else if (state == SDEV_READY) {
21812621Sllai1 					goto found;
21822621Sllai1 				} else if (state == SDEV_ZOMBIE) {
21832621Sllai1 					rw_exit(&ddv->sdev_contents);
21842621Sllai1 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
21852621Sllai1 					    retried);
21862621Sllai1 					SDEV_RELE(dv);
21872621Sllai1 					goto lookup_failed;
21882621Sllai1 				}
21892621Sllai1 			} else {
21902621Sllai1 				mutex_exit(&dv->sdev_lookup_lock);
21912621Sllai1 			}
21922621Sllai1 			break;
21932621Sllai1 		case SDEV_READY:
21942621Sllai1 			goto found;
21952621Sllai1 		case SDEV_ZOMBIE:
21962621Sllai1 			rw_exit(&ddv->sdev_contents);
21972621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
21982621Sllai1 			SDEV_RELE(dv);
21992621Sllai1 			goto lookup_failed;
22002621Sllai1 		default:
22012621Sllai1 			rw_exit(&ddv->sdev_contents);
22022621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
22032621Sllai1 			sdev_lookup_failed(ddv, nm, failed_flags);
22042621Sllai1 			*vpp = NULLVP;
22052621Sllai1 			return (ENOENT);
22062621Sllai1 		}
22072621Sllai1 	}
22082621Sllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
22092621Sllai1 
22102621Sllai1 	/*
22112621Sllai1 	 * ZOMBIED parent does not allow new node creation.
22122621Sllai1 	 * bail out early
22132621Sllai1 	 */
22142621Sllai1 	if (parent_state == SDEV_ZOMBIE) {
22152621Sllai1 		rw_exit(&ddv->sdev_contents);
22162621Sllai1 		*vpp = NULL;
22172621Sllai1 		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
22182621Sllai1 		return (ENOENT);
22192621Sllai1 	}
22202621Sllai1 
22212621Sllai1 	/*
22222621Sllai1 	 * (b0): backing store lookup
22232621Sllai1 	 *	SDEV_PERSIST is default except:
22242621Sllai1 	 *		1) pts nodes
22252621Sllai1 	 *		2) non-chmod'ed local nodes
22262621Sllai1 	 */
22272621Sllai1 	if (SDEV_IS_PERSIST(ddv)) {
22282621Sllai1 		error = devname_backstore_lookup(ddv, nm, &rvp);
22292621Sllai1 
22302621Sllai1 		if (!error) {
22312621Sllai1 			sdcmn_err3(("devname_backstore_lookup: "
22322621Sllai1 			    "found attrvp %p for %s\n", (void *)rvp, nm));
22332621Sllai1 
22342621Sllai1 			vattr.va_mask = AT_MODE|AT_UID|AT_GID;
22352621Sllai1 			error = VOP_GETATTR(rvp, &vattr, 0, cred);
22362621Sllai1 			if (error) {
22372621Sllai1 				rw_exit(&ddv->sdev_contents);
22382621Sllai1 				if (dv)
22392621Sllai1 					SDEV_RELE(dv);
22402621Sllai1 				SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
22412621Sllai1 				sdev_lookup_failed(ddv, nm, failed_flags);
22422621Sllai1 				*vpp = NULLVP;
22432621Sllai1 				return (ENOENT);
22442621Sllai1 			}
22452621Sllai1 
22462621Sllai1 			if (vattr.va_type == VLNK) {
22472621Sllai1 				error = sdev_getlink(rvp, &link);
22482621Sllai1 				if (error) {
22492621Sllai1 					rw_exit(&ddv->sdev_contents);
22502621Sllai1 					if (dv)
22512621Sllai1 						SDEV_RELE(dv);
22522621Sllai1 					SD_TRACE_FAILED_LOOKUP(ddv, nm,
22532621Sllai1 					    retried);
22542621Sllai1 					sdev_lookup_failed(ddv, nm,
22552621Sllai1 					    failed_flags);
22562621Sllai1 					*vpp = NULLVP;
22572621Sllai1 					return (ENOENT);
22582621Sllai1 				}
22592621Sllai1 				ASSERT(link != NULL);
22602621Sllai1 			}
22612621Sllai1 
22622621Sllai1 			if (!rw_tryupgrade(&ddv->sdev_contents)) {
22632621Sllai1 				rw_exit(&ddv->sdev_contents);
22642621Sllai1 				rw_enter(&ddv->sdev_contents, RW_WRITER);
22652621Sllai1 			}
22662621Sllai1 			error = sdev_mknode(ddv, nm, &dv, &vattr,
22672621Sllai1 			    rvp, link, cred, SDEV_READY);
22682621Sllai1 			rw_downgrade(&ddv->sdev_contents);
22692621Sllai1 
22702621Sllai1 			if (link != NULL) {
22712621Sllai1 				kmem_free(link, strlen(link) + 1);
22722621Sllai1 				link = NULL;
22732621Sllai1 			}
22742621Sllai1 
22752621Sllai1 			if (error) {
22762621Sllai1 				SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
22772621Sllai1 				rw_exit(&ddv->sdev_contents);
22782621Sllai1 				if (dv)
22792621Sllai1 					SDEV_RELE(dv);
22802621Sllai1 				goto lookup_failed;
22812621Sllai1 			} else {
22822621Sllai1 				goto found;
22832621Sllai1 			}
22842621Sllai1 		} else if (retried) {
22852621Sllai1 			rw_exit(&ddv->sdev_contents);
22862621Sllai1 			sdcmn_err3(("retry of lookup of %s/%s: failed\n",
22872621Sllai1 			    ddv->sdev_name, nm));
22882621Sllai1 			if (dv)
22892621Sllai1 				SDEV_RELE(dv);
22902621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
22912621Sllai1 			sdev_lookup_failed(ddv, nm, failed_flags);
22922621Sllai1 			*vpp = NULLVP;
22932621Sllai1 			return (ENOENT);
22942621Sllai1 		}
22952621Sllai1 	}
22962621Sllai1 
22972621Sllai1 
22982621Sllai1 	/* first thread that is doing the lookup on this node */
22992621Sllai1 	if (!dv) {
23002621Sllai1 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
23012621Sllai1 			rw_exit(&ddv->sdev_contents);
23022621Sllai1 			rw_enter(&ddv->sdev_contents, RW_WRITER);
23032621Sllai1 		}
23042621Sllai1 		error = sdev_mknode(ddv, nm, &dv, NULL, NULL, NULL,
23052621Sllai1 		    cred, SDEV_INIT);
23062621Sllai1 		if (!dv) {
23072621Sllai1 			rw_exit(&ddv->sdev_contents);
23082621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
23092621Sllai1 			sdev_lookup_failed(ddv, nm, failed_flags);
23102621Sllai1 			*vpp = NULLVP;
23112621Sllai1 			return (ENOENT);
23122621Sllai1 		}
23132621Sllai1 		rw_downgrade(&ddv->sdev_contents);
23142621Sllai1 	}
23152621Sllai1 	ASSERT(dv);
23162621Sllai1 	ASSERT(SDEV_HELD(dv));
23172621Sllai1 
23182621Sllai1 	if (SDEV_IS_NO_NCACHE(dv)) {
23192621Sllai1 		failed_flags |= SLF_NO_NCACHE;
23202621Sllai1 	}
23212621Sllai1 
23222621Sllai1 	if (SDEV_IS_GLOBAL(ddv)) {
23232621Sllai1 		map = sdev_get_map(ddv, 1);
23242621Sllai1 		dirops = map ? map->dir_ops : NULL;
23252621Sllai1 		fn = dirops ? dirops->devnops_lookup : NULL;
23262621Sllai1 	}
23272621Sllai1 
23282621Sllai1 	/*
23292621Sllai1 	 * (b1) invoking devfsadm once per life time for devfsadm nodes
23302621Sllai1 	 */
23312621Sllai1 	if ((fn == NULL) && !callback) {
23322621Sllai1 
23332621Sllai1 		if (sdev_reconfig_boot || !i_ddi_io_initialized() ||
23342621Sllai1 		    SDEV_IS_DYNAMIC(ddv) || SDEV_IS_NO_NCACHE(dv) ||
23352621Sllai1 		    ((moddebug & MODDEBUG_FINI_EBUSY) != 0)) {
23362621Sllai1 			ASSERT(SDEV_HELD(dv));
23372621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
23382621Sllai1 			goto nolock_notfound;
23392621Sllai1 		}
23402621Sllai1 
23412621Sllai1 		/*
23422621Sllai1 		 * filter out known non-existent devices recorded
23432621Sllai1 		 * during initial reconfiguration boot for which
23442621Sllai1 		 * reconfig should not be done and lookup may
23452621Sllai1 		 * be short-circuited now.
23462621Sllai1 		 */
23472621Sllai1 		if (sdev_lookup_filter(ddv, nm)) {
23482621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
23492621Sllai1 			goto nolock_notfound;
23502621Sllai1 		}
23512621Sllai1 
23522621Sllai1 		/* bypassing devfsadm internal nodes */
23532621Sllai1 		if (is_devfsadm_thread(lookup_thread)) {
23542621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
23552621Sllai1 			goto nolock_notfound;
23562621Sllai1 		}
23572621Sllai1 
23582621Sllai1 		if (sdev_reconfig_disable) {
23592621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
23602621Sllai1 			goto nolock_notfound;
23612621Sllai1 		}
23622621Sllai1 
23632621Sllai1 		error = sdev_call_devfsadmd(ddv, dv, nm);
23642621Sllai1 		if (error == 0) {
23652621Sllai1 			sdcmn_err8(("lookup of %s/%s by %s: reconfig\n",
23662621Sllai1 			    ddv->sdev_name, nm, curproc->p_user.u_comm));
23672621Sllai1 			if (sdev_reconfig_verbose) {
23682621Sllai1 				cmn_err(CE_CONT,
23692621Sllai1 				    "?lookup of %s/%s by %s: reconfig\n",
23702621Sllai1 				    ddv->sdev_name, nm, curproc->p_user.u_comm);
23712621Sllai1 			}
23722621Sllai1 			retried = 1;
23732621Sllai1 			failed_flags |= SLF_REBUILT;
23742621Sllai1 			ASSERT(dv->sdev_state != SDEV_ZOMBIE);
23752621Sllai1 			SDEV_SIMPLE_RELE(dv);
23762621Sllai1 			goto tryagain;
23772621Sllai1 		} else {
23782621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
23792621Sllai1 			goto nolock_notfound;
23802621Sllai1 		}
23812621Sllai1 	}
23822621Sllai1 
23832621Sllai1 	/*
23842621Sllai1 	 * (b2) Directory Based Name Resolution (DBNR):
23852621Sllai1 	 *	ddv	- parent
23862621Sllai1 	 *	nm	- /dev/(ddv->sdev_name)/nm
23872621Sllai1 	 *
23882621Sllai1 	 *	note: module vnode ops take precedence than the build-in ones
23892621Sllai1 	 */
23902621Sllai1 	if (fn) {
23912621Sllai1 		error = sdev_call_modulelookup(ddv, &dv, nm, fn, cred);
23922621Sllai1 		if (error) {
23932621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
23942621Sllai1 			goto notfound;
23952621Sllai1 		} else {
23962621Sllai1 			goto found;
23972621Sllai1 		}
23982621Sllai1 	} else if (callback) {
23992621Sllai1 		error = sdev_call_dircallback(ddv, &dv, nm, callback,
24002621Sllai1 		    flags, cred);
24012621Sllai1 		if (error == 0) {
24022621Sllai1 			goto found;
24032621Sllai1 		} else {
24042621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
24052621Sllai1 			goto notfound;
24062621Sllai1 		}
24072621Sllai1 	}
24082621Sllai1 	ASSERT(rvp);
24092621Sllai1 
24102621Sllai1 found:
24112621Sllai1 	ASSERT(!(dv->sdev_flags & SDEV_STALE));
24122621Sllai1 	ASSERT(dv->sdev_state == SDEV_READY);
24132621Sllai1 	if (vtor) {
24142621Sllai1 		/*
24152621Sllai1 		 * Check validity of returned node
24162621Sllai1 		 */
24172621Sllai1 		switch (vtor(dv)) {
24182621Sllai1 		case SDEV_VTOR_VALID:
24192621Sllai1 			break;
24202621Sllai1 		case SDEV_VTOR_INVALID:
24212621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
24222621Sllai1 			sdcmn_err7(("lookup: destroy invalid "
24232621Sllai1 			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
24242621Sllai1 			goto nolock_notfound;
24252621Sllai1 		case SDEV_VTOR_SKIP:
24262621Sllai1 			sdcmn_err7(("lookup: node not applicable - "
24272621Sllai1 			    "skipping: %s(%p)\n", dv->sdev_name, (void *)dv));
24282621Sllai1 			rw_exit(&ddv->sdev_contents);
24292621Sllai1 			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
24302621Sllai1 			SDEV_RELE(dv);
24312621Sllai1 			goto lookup_failed;
24322621Sllai1 		default:
24332621Sllai1 			cmn_err(CE_PANIC,
24342621Sllai1 			    "dev fs: validator failed: %s(%p)\n",
24352621Sllai1 			    dv->sdev_name, (void *)dv);
24362621Sllai1 			break;
24372621Sllai1 			/*NOTREACHED*/
24382621Sllai1 		}
24392621Sllai1 	}
24402621Sllai1 
24412621Sllai1 	if ((SDEVTOV(dv)->v_type == VDIR) && SDEV_IS_GLOBAL(dv)) {
24422621Sllai1 		rw_enter(&dv->sdev_contents, RW_READER);
24432621Sllai1 		(void) sdev_get_map(dv, 1);
24442621Sllai1 		rw_exit(&dv->sdev_contents);
24452621Sllai1 	}
24462621Sllai1 	rw_exit(&ddv->sdev_contents);
24472621Sllai1 	rv = sdev_to_vp(dv, vpp);
24482621Sllai1 	sdcmn_err3(("devname_lookup_func: returning vp %p v_count %d state %d "
24492621Sllai1 	    "for nm %s, error %d\n", (void *)*vpp, (*vpp)->v_count,
24502621Sllai1 	    dv->sdev_state, nm, rv));
24512621Sllai1 	return (rv);
24522621Sllai1 
24532621Sllai1 notfound:
24542621Sllai1 	mutex_enter(&dv->sdev_lookup_lock);
24552621Sllai1 	SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
24562621Sllai1 	mutex_exit(&dv->sdev_lookup_lock);
24572621Sllai1 nolock_notfound:
24582621Sllai1 	/*
24592621Sllai1 	 * Destroy the node that is created for synchronization purposes.
24602621Sllai1 	 */
24612621Sllai1 	sdcmn_err3(("devname_lookup_func: %s with state %d\n",
24622621Sllai1 	    nm, dv->sdev_state));
24632621Sllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
24642621Sllai1 	if (dv->sdev_state == SDEV_INIT) {
24652621Sllai1 		if (!rw_tryupgrade(&ddv->sdev_contents)) {
24662621Sllai1 			rw_exit(&ddv->sdev_contents);
24672621Sllai1 			rw_enter(&ddv->sdev_contents, RW_WRITER);
24682621Sllai1 		}
24692621Sllai1 
24702621Sllai1 		/*
24712621Sllai1 		 * Node state may have changed during the lock
24722621Sllai1 		 * changes. Re-check.
24732621Sllai1 		 */
24742621Sllai1 		if (dv->sdev_state == SDEV_INIT) {
24752621Sllai1 			(void) sdev_dirdelete(ddv, dv);
24762621Sllai1 			rw_exit(&ddv->sdev_contents);
24772621Sllai1 			sdev_lookup_failed(ddv, nm, failed_flags);
24782621Sllai1 			*vpp = NULL;
24792621Sllai1 			return (ENOENT);
24802621Sllai1 		}
24812621Sllai1 	}
24822621Sllai1 
24832621Sllai1 	rw_exit(&ddv->sdev_contents);
24842621Sllai1 	SDEV_RELE(dv);
24852621Sllai1 
24862621Sllai1 lookup_failed:
24872621Sllai1 	sdev_lookup_failed(ddv, nm, failed_flags);
24882621Sllai1 	*vpp = NULL;
24892621Sllai1 	return (ENOENT);
24902621Sllai1 }
24912621Sllai1 
24922621Sllai1 /*
24932621Sllai1  * Given a directory node, mark all nodes beneath as
24942621Sllai1  * STALE, i.e. nodes that don't exist as far as new
24952621Sllai1  * consumers are concerned
24962621Sllai1  */
24972621Sllai1 void
24982621Sllai1 sdev_stale(struct sdev_node *ddv)
24992621Sllai1 {
25002621Sllai1 	struct sdev_node *dv;
25012621Sllai1 	struct vnode *vp;
25022621Sllai1 
25032621Sllai1 	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
25042621Sllai1 
25052621Sllai1 	rw_enter(&ddv->sdev_contents, RW_WRITER);
25062621Sllai1 	for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next) {
25072621Sllai1 		vp = SDEVTOV(dv);
25082621Sllai1 		if (vp->v_type == VDIR)
25092621Sllai1 			sdev_stale(dv);
25102621Sllai1 
25112621Sllai1 		sdcmn_err9(("sdev_stale: setting stale %s\n",
25122621Sllai1 		    dv->sdev_name));
25132621Sllai1 		dv->sdev_flags |= SDEV_STALE;
25142621Sllai1 	}
25152621Sllai1 	ddv->sdev_flags |= SDEV_BUILD;
25162621Sllai1 	rw_exit(&ddv->sdev_contents);
25172621Sllai1 }
25182621Sllai1 
25192621Sllai1 /*
25202621Sllai1  * Given a directory node, clean out all the nodes beneath.
25212621Sllai1  * If expr is specified, clean node with names matching expr.
25222621Sllai1  * If SDEV_ENFORCE is specified in flags, busy nodes are made stale,
25232621Sllai1  *	so they are excluded from future lookups.
25242621Sllai1  */
25252621Sllai1 int
25262621Sllai1 sdev_cleandir(struct sdev_node *ddv, char *expr, uint_t flags)
25272621Sllai1 {
25282621Sllai1 	int error = 0;
25292621Sllai1 	int busy = 0;
25302621Sllai1 	struct vnode *vp;
25312621Sllai1 	struct sdev_node *dv, *next = NULL;
25322621Sllai1 	int bkstore = 0;
25332621Sllai1 	int len = 0;
25342621Sllai1 	char *bks_name = NULL;
25352621Sllai1 
25362621Sllai1 	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
25372621Sllai1 
25382621Sllai1 	/*
25392621Sllai1 	 * We try our best to destroy all unused sdev_node's
25402621Sllai1 	 */
25412621Sllai1 	rw_enter(&ddv->sdev_contents, RW_WRITER);
25422621Sllai1 	for (dv = ddv->sdev_dot; dv; dv = next) {
25432621Sllai1 		next = dv->sdev_next;
25442621Sllai1 		vp = SDEVTOV(dv);
25452621Sllai1 
25462621Sllai1 		if (expr && gmatch(dv->sdev_name, expr) == 0)
25472621Sllai1 			continue;
25482621Sllai1 
25492621Sllai1 		if (vp->v_type == VDIR &&
25502621Sllai1 		    sdev_cleandir(dv, NULL, flags) != 0) {
25512621Sllai1 			sdcmn_err9(("sdev_cleandir: dir %s busy\n",
25522621Sllai1 			    dv->sdev_name));
25532621Sllai1 			busy++;
25542621Sllai1 			continue;
25552621Sllai1 		}
25562621Sllai1 
25572621Sllai1 		if (vp->v_count > 0 && (flags & SDEV_ENFORCE) == 0) {
25582621Sllai1 			sdcmn_err9(("sdev_cleandir: dir %s busy\n",
25592621Sllai1 			    dv->sdev_name));
25602621Sllai1 			busy++;
25612621Sllai1 			continue;
25622621Sllai1 		}
25632621Sllai1 
25642621Sllai1 		/*
25652621Sllai1 		 * at this point, either dv is not held or SDEV_ENFORCE
25662621Sllai1 		 * is specified. In either case, dv needs to be deleted
25672621Sllai1 		 */
25682621Sllai1 		SDEV_HOLD(dv);
25692621Sllai1 
25702621Sllai1 		bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0;
25712621Sllai1 		if (bkstore && (vp->v_type == VDIR))
25722621Sllai1 			bkstore += 1;
25732621Sllai1 
25742621Sllai1 		if (bkstore) {
25752621Sllai1 			len = strlen(dv->sdev_name) + 1;
25762621Sllai1 			bks_name = kmem_alloc(len, KM_SLEEP);
25772621Sllai1 			bcopy(dv->sdev_name, bks_name, len);
25782621Sllai1 		}
25792621Sllai1 
25802621Sllai1 		error = sdev_dirdelete(ddv, dv);
25812621Sllai1 
25822621Sllai1 		if (error == EBUSY) {
25832621Sllai1 			sdcmn_err9(("sdev_cleandir: dir busy\n"));
25842621Sllai1 			busy++;
25852621Sllai1 		}
25862621Sllai1 
25872621Sllai1 		/* take care the backing store clean up */
25882621Sllai1 		if (bkstore && (error == 0)) {
25892621Sllai1 			ASSERT(bks_name);
25902621Sllai1 			ASSERT(ddv->sdev_attrvp);
25912621Sllai1 
25922621Sllai1 			if (bkstore == 1) {
25932621Sllai1 				error = VOP_REMOVE(ddv->sdev_attrvp,
25942621Sllai1 				    bks_name, kcred);
25952621Sllai1 			} else if (bkstore == 2) {
25962621Sllai1 				error = VOP_RMDIR(ddv->sdev_attrvp,
25972621Sllai1 				    bks_name, ddv->sdev_attrvp, kcred);
25982621Sllai1 			}
25992621Sllai1 
26002621Sllai1 			/* do not propagate the backing store errors */
26012621Sllai1 			if (error) {
26022621Sllai1 				sdcmn_err9(("sdev_cleandir: backing store"
26032621Sllai1 				    "not cleaned\n"));
26042621Sllai1 				error = 0;
26052621Sllai1 			}
26062621Sllai1 
26072621Sllai1 			bkstore = 0;
26082621Sllai1 			kmem_free(bks_name, len);
26092621Sllai1 			bks_name = NULL;
26102621Sllai1 			len = 0;
26112621Sllai1 		}
26122621Sllai1 	}
26132621Sllai1 
26142621Sllai1 	ddv->sdev_flags |= SDEV_BUILD;
26152621Sllai1 	rw_exit(&ddv->sdev_contents);
26162621Sllai1 
26172621Sllai1 	if (busy) {
26182621Sllai1 		error = EBUSY;
26192621Sllai1 	}
26202621Sllai1 
26212621Sllai1 	return (error);
26222621Sllai1 }
26232621Sllai1 
26242621Sllai1 /*
26252621Sllai1  * a convenient wrapper for readdir() funcs
26262621Sllai1  */
26272621Sllai1 size_t
26282621Sllai1 add_dir_entry(dirent64_t *de, char *nm, size_t size, ino_t ino, offset_t off)
26292621Sllai1 {
26302621Sllai1 	size_t reclen = DIRENT64_RECLEN(strlen(nm));
26312621Sllai1 	if (reclen > size)
26322621Sllai1 		return (0);
26332621Sllai1 
26342621Sllai1 	de->d_ino = (ino64_t)ino;
26352621Sllai1 	de->d_off = (off64_t)off + 1;
26362621Sllai1 	de->d_reclen = (ushort_t)reclen;
26372621Sllai1 	(void) strncpy(de->d_name, nm, DIRENT64_NAMELEN(reclen));
26382621Sllai1 	return (reclen);
26392621Sllai1 }
26402621Sllai1 
26412621Sllai1 /*
26422621Sllai1  * sdev_mount service routines
26432621Sllai1  */
26442621Sllai1 int
26452621Sllai1 sdev_copyin_mountargs(struct mounta *uap, struct sdev_mountargs *args)
26462621Sllai1 {
26472621Sllai1 	int	error;
26482621Sllai1 
26492621Sllai1 	if (uap->datalen != sizeof (*args))
26502621Sllai1 		return (EINVAL);
26512621Sllai1 
26522621Sllai1 	if (error = copyin(uap->dataptr, args, sizeof (*args))) {
26532621Sllai1 		cmn_err(CE_WARN, "sdev_copyin_mountargs: can not"
26542621Sllai1 		    "get user data. error %d\n", error);
26552621Sllai1 		return (EFAULT);
26562621Sllai1 	}
26572621Sllai1 
26582621Sllai1 	return (0);
26592621Sllai1 }
26602621Sllai1 
26612621Sllai1 #ifdef nextdp
26622621Sllai1 #undef nextdp
26632621Sllai1 #endif
26642621Sllai1 #define	nextdp(dp)	((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
26652621Sllai1 
26662621Sllai1 /*
26672621Sllai1  * readdir helper func
26682621Sllai1  */
26692621Sllai1 int
26702621Sllai1 devname_readdir_func(vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp,
26712621Sllai1     int flags)
26722621Sllai1 {
26732621Sllai1 	struct sdev_node *ddv = VTOSDEV(vp);
26742621Sllai1 	struct sdev_node *dv;
26752621Sllai1 	dirent64_t	*dp;
26762621Sllai1 	ulong_t		outcount = 0;
26772621Sllai1 	size_t		namelen;
26782621Sllai1 	ulong_t		alloc_count;
26792621Sllai1 	void		*outbuf;
26802621Sllai1 	struct iovec	*iovp;
26812621Sllai1 	int		error = 0;
26822621Sllai1 	size_t		reclen;
26832621Sllai1 	offset_t	diroff;
26842621Sllai1 	offset_t	soff;
26852621Sllai1 	int		this_reclen;
26862621Sllai1 	struct devname_nsmap	*map = NULL;
26872621Sllai1 	struct devname_ops	*dirops = NULL;
26882621Sllai1 	int (*fn)(devname_handle_t *, struct cred *) = NULL;
26892621Sllai1 	int (*vtor)(struct sdev_node *) = NULL;
26902621Sllai1 	struct vattr attr;
26912621Sllai1 	timestruc_t now;
26922621Sllai1 
26932621Sllai1 	ASSERT(ddv->sdev_attr || ddv->sdev_attrvp);
26942621Sllai1 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
26952621Sllai1 
26962621Sllai1 	if (uiop->uio_loffset >= MAXOFF_T) {
26972621Sllai1 		if (eofp)
26982621Sllai1 			*eofp = 1;
26992621Sllai1 		return (0);
27002621Sllai1 	}
27012621Sllai1 
27022621Sllai1 	if (uiop->uio_iovcnt != 1)
27032621Sllai1 		return (EINVAL);
27042621Sllai1 
27052621Sllai1 	if (vp->v_type != VDIR)
27062621Sllai1 		return (ENOTDIR);
27072621Sllai1 
27082621Sllai1 	if (ddv->sdev_flags & SDEV_VTOR) {
27092621Sllai1 		vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
27102621Sllai1 		ASSERT(vtor);
27112621Sllai1 	}
27122621Sllai1 
27132621Sllai1 	if (eofp != NULL)
27142621Sllai1 		*eofp = 0;
27152621Sllai1 
27162621Sllai1 	soff = uiop->uio_offset;
27172621Sllai1 	iovp = uiop->uio_iov;
27182621Sllai1 	alloc_count = iovp->iov_len;
27192621Sllai1 	dp = outbuf = kmem_alloc(alloc_count, KM_SLEEP);
27202621Sllai1 	outcount = 0;
27212621Sllai1 
27222621Sllai1 	if (ddv->sdev_state == SDEV_ZOMBIE)
27232621Sllai1 		goto get_cache;
27242621Sllai1 
27252679Sszhou 	if (SDEV_IS_GLOBAL(ddv)) {
27262621Sllai1 		map = sdev_get_map(ddv, 0);
27272621Sllai1 		dirops = map ? map->dir_ops : NULL;
27282621Sllai1 		fn = dirops ? dirops->devnops_readdir : NULL;
27292621Sllai1 
27302621Sllai1 		if (map && map->dir_map) {
27312621Sllai1 			/*
27322621Sllai1 			 * load the name mapping rule database
27332621Sllai1 			 * through invoking devfsadm and symlink
27342621Sllai1 			 * all the entries in the map
27352621Sllai1 			 */
27362621Sllai1 			devname_rdr_result_t rdr_result;
27372621Sllai1 			int do_thread = 0;
27382621Sllai1 
27392621Sllai1 			rw_enter(&map->dir_lock, RW_READER);
27402621Sllai1 			do_thread = map->dir_maploaded ? 0 : 1;
27412621Sllai1 			rw_exit(&map->dir_lock);
27422621Sllai1 
27432621Sllai1 			if (do_thread) {
27442621Sllai1 				mutex_enter(&ddv->sdev_lookup_lock);
27452621Sllai1 				SDEV_BLOCK_OTHERS(ddv, SDEV_READDIR);
27462621Sllai1 				mutex_exit(&ddv->sdev_lookup_lock);
27472621Sllai1 
27482621Sllai1 				sdev_dispatch_to_nsrdr_thread(ddv,
27492621Sllai1 				    map->dir_map, &rdr_result);
27502621Sllai1 			}
27512621Sllai1 		} else if ((sdev_boot_state == SDEV_BOOT_STATE_COMPLETE) &&
27522621Sllai1 		    !sdev_reconfig_boot && (flags & SDEV_BROWSE) &&
27532621Sllai1 		    !SDEV_IS_DYNAMIC(ddv) && !SDEV_IS_NO_NCACHE(ddv) &&
27542621Sllai1 		    ((moddebug & MODDEBUG_FINI_EBUSY) == 0) &&
27552621Sllai1 		    !DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state) &&
27562621Sllai1 		    !DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
27572621Sllai1 		    !sdev_reconfig_disable) {
27582621Sllai1 			/*
27592621Sllai1 			 * invoking "devfsadm" to do system device reconfig
27602621Sllai1 			 */
27612621Sllai1 			mutex_enter(&ddv->sdev_lookup_lock);
27622621Sllai1 			SDEV_BLOCK_OTHERS(ddv,
27632621Sllai1 			    (SDEV_READDIR|SDEV_LGWAITING));
27642621Sllai1 			mutex_exit(&ddv->sdev_lookup_lock);
27652621Sllai1 
27662621Sllai1 			sdcmn_err8(("readdir of %s by %s: reconfig\n",
27672621Sllai1 			    ddv->sdev_path, curproc->p_user.u_comm));
27682621Sllai1 			if (sdev_reconfig_verbose) {
27692621Sllai1 				cmn_err(CE_CONT,
27702621Sllai1 				    "?readdir of %s by %s: reconfig\n",
27712621Sllai1 				    ddv->sdev_path, curproc->p_user.u_comm);
27722621Sllai1 			}
27732621Sllai1 
27742621Sllai1 			sdev_devfsadmd_thread(ddv, NULL, kcred);
27752621Sllai1 		} else if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) {
27762621Sllai1 			/*
27772621Sllai1 			 * compensate the "ls" started later than "devfsadm"
27782621Sllai1 			 */
27792621Sllai1 			mutex_enter(&ddv->sdev_lookup_lock);
27802621Sllai1 			SDEV_BLOCK_OTHERS(ddv, (SDEV_READDIR|SDEV_LGWAITING));
27812621Sllai1 			mutex_exit(&ddv->sdev_lookup_lock);
27822621Sllai1 		}
27832621Sllai1 
27842621Sllai1 		/*
27852621Sllai1 		 * release the contents lock so that
27862621Sllai1 		 * the cache maybe updated by devfsadmd
27872621Sllai1 		 */
27882621Sllai1 		rw_exit(&ddv->sdev_contents);
27892621Sllai1 		mutex_enter(&ddv->sdev_lookup_lock);
27902621Sllai1 		if (SDEV_IS_READDIR(ddv))
27912621Sllai1 			(void) sdev_wait4lookup(ddv, SDEV_READDIR);
27922621Sllai1 		mutex_exit(&ddv->sdev_lookup_lock);
27932621Sllai1 		rw_enter(&ddv->sdev_contents, RW_READER);
27942621Sllai1 
27952621Sllai1 		sdcmn_err4(("readdir of directory %s by %s\n",
27962621Sllai1 		    ddv->sdev_name, curproc->p_user.u_comm));
27972621Sllai1 		while (ddv->sdev_flags & SDEV_BUILD) {
27982621Sllai1 			if (SDEV_IS_PERSIST(ddv)) {
27992621Sllai1 				error = sdev_filldir_from_store(ddv,
28002621Sllai1 				    alloc_count, cred);
28012621Sllai1 			}
28022621Sllai1 
28032621Sllai1 			/*
28042621Sllai1 			 * pre-creating the directories
28052621Sllai1 			 * defined in vtab
28062621Sllai1 			 */
28072621Sllai1 			if (SDEVTOV(ddv)->v_flag & VROOT) {
28082621Sllai1 				error = sdev_filldir_dynamic(ddv);
28092621Sllai1 			}
28102621Sllai1 
28112621Sllai1 			if (!error)
28122621Sllai1 				ddv->sdev_flags &= ~SDEV_BUILD;
28132621Sllai1 		}
28142621Sllai1 	}
28152621Sllai1 
28162621Sllai1 get_cache:
28172621Sllai1 	/* handle "." and ".." */
28182621Sllai1 	diroff = 0;
28192621Sllai1 	if (soff == 0) {
28202621Sllai1 		/* first time */
28212621Sllai1 		this_reclen = DIRENT64_RECLEN(1);
28222621Sllai1 		if (alloc_count < this_reclen) {
28232621Sllai1 			error = EINVAL;
28242621Sllai1 			goto done;
28252621Sllai1 		}
28262621Sllai1 
28272621Sllai1 		dp->d_ino = (ino64_t)ddv->sdev_ino;
28282621Sllai1 		dp->d_off = (off64_t)1;
28292621Sllai1 		dp->d_reclen = (ushort_t)this_reclen;
28302621Sllai1 
28312621Sllai1 		(void) strncpy(dp->d_name, ".",
28322621Sllai1 		    DIRENT64_NAMELEN(this_reclen));
28332621Sllai1 		outcount += dp->d_reclen;
28342621Sllai1 		dp = nextdp(dp);
28352621Sllai1 	}
28362621Sllai1 
28372621Sllai1 	diroff++;
28382621Sllai1 	if (soff <= 1) {
28392621Sllai1 		this_reclen = DIRENT64_RECLEN(2);
28402621Sllai1 		if (alloc_count < outcount + this_reclen) {
28412621Sllai1 			error = EINVAL;
28422621Sllai1 			goto done;
28432621Sllai1 		}
28442621Sllai1 
28452621Sllai1 		dp->d_reclen = (ushort_t)this_reclen;
28462621Sllai1 		dp->d_ino = (ino64_t)ddv->sdev_dotdot->sdev_ino;
28472621Sllai1 		dp->d_off = (off64_t)2;
28482621Sllai1 
28492621Sllai1 		(void) strncpy(dp->d_name, "..",
28502621Sllai1 		    DIRENT64_NAMELEN(this_reclen));
28512621Sllai1 		outcount += dp->d_reclen;
28522621Sllai1 
28532621Sllai1 		dp = nextdp(dp);
28542621Sllai1 	}
28552621Sllai1 
28562621Sllai1 
28572621Sllai1 	/* gets the cache */
28582621Sllai1 	diroff++;
28592621Sllai1 	for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next, diroff++) {
28602621Sllai1 		sdcmn_err3(("sdev_readdir: diroff %lld soff %lld for '%s' \n",
28612621Sllai1 		    diroff, soff, dv->sdev_name));
28622621Sllai1 
28632621Sllai1 		/* bypassing pre-matured nodes */
28642621Sllai1 		if (diroff < soff || (dv->sdev_state != SDEV_READY)) {
28652621Sllai1 			sdcmn_err3(("sdev_readdir: pre-mature node  "
28662621Sllai1 			    "%s\n", dv->sdev_name));
28672621Sllai1 			continue;
28682621Sllai1 		}
28692621Sllai1 
28702621Sllai1 		/* don't list stale nodes */
28712621Sllai1 		if (dv->sdev_flags & SDEV_STALE) {
28722621Sllai1 			sdcmn_err4(("sdev_readdir: STALE node  "
28732621Sllai1 			    "%s\n", dv->sdev_name));
28742621Sllai1 			continue;
28752621Sllai1 		}
28762621Sllai1 
28772621Sllai1 		/*
28782621Sllai1 		 * Check validity of node
28792621Sllai1 		 */
28802621Sllai1 		if (vtor) {
28812621Sllai1 			switch (vtor(dv)) {
28822621Sllai1 			case SDEV_VTOR_VALID:
28832621Sllai1 				break;
28842621Sllai1 			case SDEV_VTOR_INVALID:
28852621Sllai1 			case SDEV_VTOR_SKIP:
28862621Sllai1 				continue;
28872621Sllai1 			default:
28882621Sllai1 				cmn_err(CE_PANIC,
28892621Sllai1 				    "dev fs: validator failed: %s(%p)\n",
28902621Sllai1 				    dv->sdev_name, (void *)dv);
28912621Sllai1 				break;
28922621Sllai1 			/*NOTREACHED*/
28932621Sllai1 			}
28942621Sllai1 		}
28952621Sllai1 
28962621Sllai1 		/*
28972621Sllai1 		 * call back into the module for the validity/bookkeeping
28982621Sllai1 		 * of this entry
28992621Sllai1 		 */
29002621Sllai1 		if (fn) {
29012621Sllai1 			error = (*fn)(&(dv->sdev_handle), cred);
29022621Sllai1 			if (error) {
29032621Sllai1 				sdcmn_err4(("sdev_readdir: module did not "
29042621Sllai1 				    "validate %s\n", dv->sdev_name));
29052621Sllai1 				continue;
29062621Sllai1 			}
29072621Sllai1 		}
29082621Sllai1 
29092621Sllai1 		namelen = strlen(dv->sdev_name);
29102621Sllai1 		reclen = DIRENT64_RECLEN(namelen);
29112621Sllai1 		if (outcount + reclen > alloc_count) {
29122621Sllai1 			goto full;
29132621Sllai1 		}
29142621Sllai1 		dp->d_reclen = (ushort_t)reclen;
29152621Sllai1 		dp->d_ino = (ino64_t)dv->sdev_ino;
29162621Sllai1 		dp->d_off = (off64_t)diroff + 1;
29172621Sllai1 		(void) strncpy(dp->d_name, dv->sdev_name,
29182621Sllai1 		    DIRENT64_NAMELEN(reclen));
29192621Sllai1 		outcount += reclen;
29202621Sllai1 		dp = nextdp(dp);
29212621Sllai1 	}
29222621Sllai1 
29232621Sllai1 full:
29242621Sllai1 	sdcmn_err4(("sdev_readdir: moving %lu bytes: "
29252621Sllai1 	    "diroff %lld, soff %lld, dv %p\n", outcount, diroff, soff,
29262621Sllai1 	    (void *)dv));
29272621Sllai1 
29282621Sllai1 	if (outcount)
29292621Sllai1 		error = uiomove(outbuf, outcount, UIO_READ, uiop);
29302621Sllai1 
29312621Sllai1 	if (!error) {
29322621Sllai1 		uiop->uio_offset = diroff;
29332621Sllai1 		if (eofp)
29342621Sllai1 			*eofp = dv ? 0 : 1;
29352621Sllai1 	}
29362621Sllai1 
29372621Sllai1 
29382621Sllai1 	if (ddv->sdev_attrvp) {
29392621Sllai1 		gethrestime(&now);
29402621Sllai1 		attr.va_ctime = now;
29412621Sllai1 		attr.va_atime = now;
29422621Sllai1 		attr.va_mask = AT_CTIME|AT_ATIME;
29432621Sllai1 
29442621Sllai1 		(void) VOP_SETATTR(ddv->sdev_attrvp, &attr, 0, kcred, NULL);
29452621Sllai1 	}
29462621Sllai1 done:
29472621Sllai1 	kmem_free(outbuf, alloc_count);
29482621Sllai1 	return (error);
29492621Sllai1 }
29502621Sllai1 
29512621Sllai1 
29522621Sllai1 static int
29532621Sllai1 sdev_modctl_lookup(const char *path, vnode_t **r_vp)
29542621Sllai1 {
29552621Sllai1 	vnode_t *vp;
29562621Sllai1 	vnode_t *cvp;
29572621Sllai1 	struct sdev_node *svp;
29582621Sllai1 	char *nm;
29592621Sllai1 	struct pathname pn;
29602621Sllai1 	int error;
29612621Sllai1 	int persisted = 0;
29622621Sllai1 
29632621Sllai1 	if (error = pn_get((char *)path, UIO_SYSSPACE, &pn))
29642621Sllai1 		return (error);
29652621Sllai1 	nm = kmem_alloc(MAXNAMELEN, KM_SLEEP);
29662621Sllai1 
29672621Sllai1 	vp = rootdir;
29682621Sllai1 	VN_HOLD(vp);
29692621Sllai1 
29702621Sllai1 	while (pn_pathleft(&pn)) {
29712621Sllai1 		ASSERT(vp->v_type == VDIR);
29722621Sllai1 		(void) pn_getcomponent(&pn, nm);
29732621Sllai1 		error = VOP_LOOKUP(vp, nm, &cvp, NULL, 0, NULL, kcred);
29742621Sllai1 		VN_RELE(vp);
29752621Sllai1 
29762621Sllai1 		if (error)
29772621Sllai1 			break;
29782621Sllai1 
29792621Sllai1 		/* traverse mount points encountered on our journey */
29802621Sllai1 		if (vn_ismntpt(cvp) && (error = traverse(&cvp)) != 0) {
29812621Sllai1 			VN_RELE(cvp);
29822621Sllai1 			break;
29832621Sllai1 		}
29842621Sllai1 
29852621Sllai1 		/*
29862621Sllai1 		 * Direct the operation to the persisting filesystem
29872621Sllai1 		 * underlying /dev.  Bail if we encounter a
29882621Sllai1 		 * non-persistent dev entity here.
29892621Sllai1 		 */
29902621Sllai1 		if (cvp->v_vfsp->vfs_fstype == devtype) {
29912621Sllai1 
29922621Sllai1 			if ((VTOSDEV(cvp)->sdev_flags & SDEV_PERSIST) == 0) {
29932621Sllai1 				error = ENOENT;
29942621Sllai1 				VN_RELE(cvp);
29952621Sllai1 				break;
29962621Sllai1 			}
29972621Sllai1 
29982621Sllai1 			if (VTOSDEV(cvp) == NULL) {
29992621Sllai1 				error = ENOENT;
30002621Sllai1 				VN_RELE(cvp);
30012621Sllai1 				break;
30022621Sllai1 			}
30032621Sllai1 			svp = VTOSDEV(cvp);
30042621Sllai1 			if ((vp = svp->sdev_attrvp) == NULL) {
30052621Sllai1 				error = ENOENT;
30062621Sllai1 				VN_RELE(cvp);
30072621Sllai1 				break;
30082621Sllai1 			}
30092621Sllai1 			persisted = 1;
30102621Sllai1 			VN_HOLD(vp);
30112621Sllai1 			VN_RELE(cvp);
30122621Sllai1 			cvp = vp;
30132621Sllai1 		}
30142621Sllai1 
30152621Sllai1 		vp = cvp;
30162621Sllai1 		pn_skipslash(&pn);
30172621Sllai1 	}
30182621Sllai1 
30192621Sllai1 	kmem_free(nm, MAXNAMELEN);
30202621Sllai1 	pn_free(&pn);
30212621Sllai1 
30222621Sllai1 	if (error)
30232621Sllai1 		return (error);
30242621Sllai1 
30252621Sllai1 	/*
30262621Sllai1 	 * Only return persisted nodes in the filesystem underlying /dev.
30272621Sllai1 	 */
30282621Sllai1 	if (!persisted) {
30292621Sllai1 		VN_RELE(vp);
30302621Sllai1 		return (ENOENT);
30312621Sllai1 	}
30322621Sllai1 
30332621Sllai1 	*r_vp = vp;
30342621Sllai1 	return (0);
30352621Sllai1 }
30362621Sllai1 
30372621Sllai1 int
30382621Sllai1 sdev_modctl_readdir(const char *dir, char ***dirlistp,
30392621Sllai1 	int *npathsp, int *npathsp_alloc)
30402621Sllai1 {
30412621Sllai1 	char	**pathlist = NULL;
30422621Sllai1 	char	**newlist = NULL;
30432621Sllai1 	int	npaths = 0;
30442621Sllai1 	int	npaths_alloc = 0;
30452621Sllai1 	dirent64_t *dbuf = NULL;
30462621Sllai1 	int	n;
30472621Sllai1 	char	*s;
30482621Sllai1 	int error;
30492621Sllai1 	vnode_t *vp;
30502621Sllai1 	int eof;
30512621Sllai1 	struct iovec iov;
30522621Sllai1 	struct uio uio;
30532621Sllai1 	struct dirent64 *dp;
30542621Sllai1 	size_t dlen;
30552621Sllai1 	size_t dbuflen;
30562621Sllai1 	int ndirents = 64;
30572621Sllai1 	char *nm;
30582621Sllai1 
30592621Sllai1 	error = sdev_modctl_lookup(dir, &vp);
30602621Sllai1 	sdcmn_err11(("modctl readdir: %s by %s: %s\n",
30612621Sllai1 	    dir, curproc->p_user.u_comm,
30622621Sllai1 	    (error == 0) ? "ok" : "failed"));
30632621Sllai1 	if (error)
30642621Sllai1 		return (error);
30652621Sllai1 
30662621Sllai1 	dlen = ndirents * (sizeof (*dbuf));
30672621Sllai1 	dbuf = kmem_alloc(dlen, KM_SLEEP);
30682621Sllai1 
30692621Sllai1 	uio.uio_iov = &iov;
30702621Sllai1 	uio.uio_iovcnt = 1;
30712621Sllai1 	uio.uio_segflg = UIO_SYSSPACE;
30722621Sllai1 	uio.uio_fmode = 0;
30732621Sllai1 	uio.uio_extflg = UIO_COPY_CACHED;
30742621Sllai1 	uio.uio_loffset = 0;
30752621Sllai1 	uio.uio_llimit = MAXOFFSET_T;
30762621Sllai1 
30772621Sllai1 	eof = 0;
30782621Sllai1 	error = 0;
30792621Sllai1 	while (!error && !eof) {
30802621Sllai1 		uio.uio_resid = dlen;
30812621Sllai1 		iov.iov_base = (char *)dbuf;
30822621Sllai1 		iov.iov_len = dlen;
30832621Sllai1 
30842621Sllai1 		(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
30852621Sllai1 		error = VOP_READDIR(vp, &uio, kcred, &eof);
30862621Sllai1 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
30872621Sllai1 
30882621Sllai1 		dbuflen = dlen - uio.uio_resid;
30892621Sllai1 
30902621Sllai1 		if (error || dbuflen == 0)
30912621Sllai1 			break;
30922621Sllai1 
30932621Sllai1 		for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen);
30942621Sllai1 			dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
30952621Sllai1 
30962621Sllai1 			nm = dp->d_name;
30972621Sllai1 
30982621Sllai1 			if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0)
30992621Sllai1 				continue;
31002621Sllai1 
31012621Sllai1 			if (npaths == npaths_alloc) {
31022621Sllai1 				npaths_alloc += 64;
31032621Sllai1 				newlist = (char **)
31042621Sllai1 				    kmem_zalloc((npaths_alloc + 1) *
31052621Sllai1 					sizeof (char *), KM_SLEEP);
31062621Sllai1 				if (pathlist) {
31072621Sllai1 					bcopy(pathlist, newlist,
31082621Sllai1 					    npaths * sizeof (char *));
31092621Sllai1 					kmem_free(pathlist,
31102621Sllai1 					    (npaths + 1) * sizeof (char *));
31112621Sllai1 				}
31122621Sllai1 				pathlist = newlist;
31132621Sllai1 			}
31142621Sllai1 			n = strlen(nm) + 1;
31152621Sllai1 			s = kmem_alloc(n, KM_SLEEP);
31162621Sllai1 			bcopy(nm, s, n);
31172621Sllai1 			pathlist[npaths++] = s;
31182621Sllai1 			sdcmn_err11(("  %s/%s\n", dir, s));
31192621Sllai1 		}
31202621Sllai1 	}
31212621Sllai1 
31222621Sllai1 exit:
31232621Sllai1 	VN_RELE(vp);
31242621Sllai1 
31252621Sllai1 	if (dbuf)
31262621Sllai1 		kmem_free(dbuf, dlen);
31272621Sllai1 
31282621Sllai1 	if (error)
31292621Sllai1 		return (error);
31302621Sllai1 
31312621Sllai1 	*dirlistp = pathlist;
31322621Sllai1 	*npathsp = npaths;
31332621Sllai1 	*npathsp_alloc = npaths_alloc;
31342621Sllai1 
31352621Sllai1 	return (0);
31362621Sllai1 }
31372621Sllai1 
31382621Sllai1 void
31392621Sllai1 sdev_modctl_readdir_free(char **pathlist, int npaths, int npaths_alloc)
31402621Sllai1 {
31412621Sllai1 	int	i, n;
31422621Sllai1 
31432621Sllai1 	for (i = 0; i < npaths; i++) {
31442621Sllai1 		n = strlen(pathlist[i]) + 1;
31452621Sllai1 		kmem_free(pathlist[i], n);
31462621Sllai1 	}
31472621Sllai1 
31482621Sllai1 	kmem_free(pathlist, (npaths_alloc + 1) * sizeof (char *));
31492621Sllai1 }
31502621Sllai1 
31512621Sllai1 int
31522621Sllai1 sdev_modctl_devexists(const char *path)
31532621Sllai1 {
31542621Sllai1 	vnode_t *vp;
31552621Sllai1 	int error;
31562621Sllai1 
31572621Sllai1 	error = sdev_modctl_lookup(path, &vp);
31582621Sllai1 	sdcmn_err11(("modctl dev exists: %s by %s: %s\n",
31592621Sllai1 	    path, curproc->p_user.u_comm,
31602621Sllai1 	    (error == 0) ? "ok" : "failed"));
31612621Sllai1 	if (error == 0)
31622621Sllai1 		VN_RELE(vp);
31632621Sllai1 
31642621Sllai1 	return (error);
31652621Sllai1 }
31662621Sllai1 
31672621Sllai1 void
31682621Sllai1 sdev_update_newnsmap(struct devname_nsmap *map, char *module, char *mapname)
31692621Sllai1 {
31702621Sllai1 	rw_enter(&map->dir_lock, RW_WRITER);
31712621Sllai1 	if (module) {
31722621Sllai1 		ASSERT(map->dir_newmodule == NULL);
31732621Sllai1 		map->dir_newmodule = i_ddi_strdup(module, KM_SLEEP);
31742621Sllai1 	}
31752621Sllai1 	if (mapname) {
31762621Sllai1 		ASSERT(map->dir_newmap == NULL);
31772621Sllai1 		map->dir_newmap = i_ddi_strdup(mapname, KM_SLEEP);
31782621Sllai1 	}
31792621Sllai1 
31802621Sllai1 	map->dir_invalid = 1;
31812621Sllai1 	rw_exit(&map->dir_lock);
31822621Sllai1 }
31832621Sllai1 
31842621Sllai1 void
31852621Sllai1 sdev_replace_nsmap(struct devname_nsmap *map, char *module, char *mapname)
31862621Sllai1 {
31872621Sllai1 	char *old_module = NULL;
31882621Sllai1 	char *old_map = NULL;
31892621Sllai1 
31902621Sllai1 	ASSERT(RW_LOCK_HELD(&map->dir_lock));
31912621Sllai1 	if (!rw_tryupgrade(&map->dir_lock)) {
31922621Sllai1 		rw_exit(&map->dir_lock);
31932621Sllai1 		rw_enter(&map->dir_lock, RW_WRITER);
31942621Sllai1 	}
31952621Sllai1 
31962621Sllai1 	old_module = map->dir_module;
31972621Sllai1 	if (module) {
31982621Sllai1 		if (old_module && strcmp(old_module, module) != 0) {
31992621Sllai1 			kmem_free(old_module, strlen(old_module) + 1);
32002621Sllai1 		}
32012621Sllai1 		map->dir_module = module;
32022621Sllai1 		map->dir_newmodule = NULL;
32032621Sllai1 	}
32042621Sllai1 
32052621Sllai1 	old_map = map->dir_map;
32062621Sllai1 	if (mapname) {
32072621Sllai1 		if (old_map && strcmp(old_map, mapname) != 0) {
32082621Sllai1 			kmem_free(old_map, strlen(old_map) + 1);
32092621Sllai1 		}
32102621Sllai1 
32112621Sllai1 		map->dir_map = mapname;
32122621Sllai1 		map->dir_newmap = NULL;
32132621Sllai1 	}
32142621Sllai1 	map->dir_maploaded = 0;
32152621Sllai1 	map->dir_invalid = 0;
32162621Sllai1 	rw_downgrade(&map->dir_lock);
32172621Sllai1 }
32182621Sllai1 
32192621Sllai1 /*
32202621Sllai1  * dir_name should have at least one attribute,
32212621Sllai1  *	dir_module
32222621Sllai1  *	or dir_map
32232621Sllai1  *	or both
32242621Sllai1  * caller holds the devname_nsmaps_lock
32252621Sllai1  */
32262621Sllai1 void
32272621Sllai1 sdev_insert_nsmap(char *dir_name, char *dir_module, char *dir_map)
32282621Sllai1 {
32292621Sllai1 	struct devname_nsmap *map;
32302621Sllai1 	int len = 0;
32312621Sllai1 
32322621Sllai1 	ASSERT(dir_name);
32332621Sllai1 	ASSERT(dir_module || dir_map);
32342621Sllai1 	ASSERT(MUTEX_HELD(&devname_nsmaps_lock));
32352621Sllai1 
32362621Sllai1 	if (map = sdev_get_nsmap_by_dir(dir_name, 1)) {
32372621Sllai1 		sdev_update_newnsmap(map, dir_module, dir_map);
32382621Sllai1 		return;
32392621Sllai1 	}
32402621Sllai1 
32412621Sllai1 	map = (struct devname_nsmap *)kmem_zalloc(sizeof (*map), KM_SLEEP);
32422621Sllai1 	map->dir_name = i_ddi_strdup(dir_name, KM_SLEEP);
32432621Sllai1 	if (dir_module) {
32442621Sllai1 		map->dir_module = i_ddi_strdup(dir_module, KM_SLEEP);
32452621Sllai1 	}
32462621Sllai1 
32472621Sllai1 	if (dir_map) {
32482621Sllai1 		if (dir_map[0] != '/') {
32492621Sllai1 			len = strlen(ETC_DEV_DIR) + strlen(dir_map) + 2;
32502621Sllai1 			map->dir_map = kmem_zalloc(len, KM_SLEEP);
32512621Sllai1 			(void) snprintf(map->dir_map, len, "%s/%s", ETC_DEV_DIR,
32522621Sllai1 			    dir_map);
32532621Sllai1 		} else {
32542621Sllai1 			map->dir_map = i_ddi_strdup(dir_map, KM_SLEEP);
32552621Sllai1 		}
32562621Sllai1 	}
32572621Sllai1 
32582621Sllai1 	map->dir_ops = NULL;
32592621Sllai1 	map->dir_maploaded = 0;
32602621Sllai1 	map->dir_invalid = 0;
32612621Sllai1 	rw_init(&map->dir_lock, NULL, RW_DEFAULT, NULL);
32622621Sllai1 
32632621Sllai1 	map->next = devname_nsmaps;
32642621Sllai1 	map->prev = NULL;
32652621Sllai1 	if (devname_nsmaps) {
32662621Sllai1 		devname_nsmaps->prev = map;
32672621Sllai1 	}
32682621Sllai1 	devname_nsmaps = map;
32692621Sllai1 }
32702621Sllai1 
32712621Sllai1 struct devname_nsmap *
32722621Sllai1 sdev_get_nsmap_by_dir(char *dir_path, int locked)
32732621Sllai1 {
32742621Sllai1 	struct devname_nsmap *map = NULL;
32752621Sllai1 
32762621Sllai1 	if (!locked)
32772621Sllai1 		mutex_enter(&devname_nsmaps_lock);
32782621Sllai1 	for (map = devname_nsmaps; map; map = map->next) {
32792621Sllai1 		sdcmn_err6(("sdev_get_nsmap_by_dir: dir %s\n", map->dir_name));
32802621Sllai1 		if (strcmp(map->dir_name, dir_path) == 0) {
32812621Sllai1 			if (!locked)
32822621Sllai1 				mutex_exit(&devname_nsmaps_lock);
32832621Sllai1 			return (map);
32842621Sllai1 		}
32852621Sllai1 	}
32862621Sllai1 	if (!locked)
32872621Sllai1 		mutex_exit(&devname_nsmaps_lock);
32882621Sllai1 	return (NULL);
32892621Sllai1 }
32902621Sllai1 
32912621Sllai1 struct devname_nsmap *
32922621Sllai1 sdev_get_nsmap_by_module(char *mod_name)
32932621Sllai1 {
32942621Sllai1 	struct devname_nsmap *map = NULL;
32952621Sllai1 
32962621Sllai1 	mutex_enter(&devname_nsmaps_lock);
32972621Sllai1 	for (map = devname_nsmaps; map; map = map->next) {
32982621Sllai1 		sdcmn_err7(("sdev_get_nsmap_by_module: module %s\n",
32992621Sllai1 		    map->dir_module));
33002621Sllai1 		if (map->dir_module && strcmp(map->dir_module, mod_name) == 0) {
33012621Sllai1 			mutex_exit(&devname_nsmaps_lock);
33022621Sllai1 			return (map);
33032621Sllai1 		}
33042621Sllai1 	}
33052621Sllai1 	mutex_exit(&devname_nsmaps_lock);
33062621Sllai1 	return (NULL);
33072621Sllai1 }
33082621Sllai1 
33092621Sllai1 void
33102621Sllai1 sdev_invalidate_nsmaps()
33112621Sllai1 {
33122621Sllai1 	struct devname_nsmap *map = NULL;
33132621Sllai1 
33142621Sllai1 	ASSERT(MUTEX_HELD(&devname_nsmaps_lock));
33152621Sllai1 
33162621Sllai1 	if (devname_nsmaps == NULL)
33172621Sllai1 		return;
33182621Sllai1 
33192621Sllai1 	for (map = devname_nsmaps; map; map = map->next) {
33202621Sllai1 		rw_enter(&map->dir_lock, RW_WRITER);
33212621Sllai1 		map->dir_invalid = 1;
33222621Sllai1 		rw_exit(&map->dir_lock);
33232621Sllai1 	}
33242621Sllai1 	devname_nsmaps_invalidated = 1;
33252621Sllai1 }
33262621Sllai1 
33272621Sllai1 
33282621Sllai1 int
33292621Sllai1 sdev_nsmaps_loaded()
33302621Sllai1 {
33312621Sllai1 	int ret = 0;
33322621Sllai1 
33332621Sllai1 	mutex_enter(&devname_nsmaps_lock);
33342621Sllai1 	if (devname_nsmaps_loaded)
33352621Sllai1 		ret = 1;
33362621Sllai1 
33372621Sllai1 	mutex_exit(&devname_nsmaps_lock);
33382621Sllai1 	return (ret);
33392621Sllai1 }
33402621Sllai1 
33412621Sllai1 int
33422621Sllai1 sdev_nsmaps_reloaded()
33432621Sllai1 {
33442621Sllai1 	int ret = 0;
33452621Sllai1 
33462621Sllai1 	mutex_enter(&devname_nsmaps_lock);
33472621Sllai1 	if (devname_nsmaps_invalidated)
33482621Sllai1 		ret = 1;
33492621Sllai1 
33502621Sllai1 	mutex_exit(&devname_nsmaps_lock);
33512621Sllai1 	return (ret);
33522621Sllai1 }
33532621Sllai1 
33542621Sllai1 static void
33552621Sllai1 sdev_free_nsmap(struct devname_nsmap *map)
33562621Sllai1 {
33572621Sllai1 	ASSERT(map);
33582621Sllai1 	if (map->dir_name)
33592621Sllai1 		kmem_free(map->dir_name, strlen(map->dir_name) + 1);
33602621Sllai1 	if (map->dir_module)
33612621Sllai1 		kmem_free(map->dir_module, strlen(map->dir_module) + 1);
33622621Sllai1 	if (map->dir_map)
33632621Sllai1 		kmem_free(map->dir_map, strlen(map->dir_map) + 1);
33642621Sllai1 	rw_destroy(&map->dir_lock);
33652621Sllai1 	kmem_free(map, sizeof (*map));
33662621Sllai1 }
33672621Sllai1 
33682621Sllai1 void
33692621Sllai1 sdev_validate_nsmaps()
33702621Sllai1 {
33712621Sllai1 	struct devname_nsmap *map = NULL;
33722621Sllai1 	struct devname_nsmap *oldmap = NULL;
33732621Sllai1 
33742621Sllai1 	ASSERT(MUTEX_HELD(&devname_nsmaps_lock));
33752621Sllai1 	map = devname_nsmaps;
33762621Sllai1 	while (map) {
33772621Sllai1 		rw_enter(&map->dir_lock, RW_READER);
33782621Sllai1 		if ((map->dir_invalid == 1) && (map->dir_newmodule == NULL) &&
33792621Sllai1 		    (map->dir_newmap == NULL)) {
33802621Sllai1 			oldmap = map;
33812621Sllai1 			rw_exit(&map->dir_lock);
33822621Sllai1 			if (map->prev)
33832621Sllai1 				map->prev->next = oldmap->next;
33842621Sllai1 			if (map == devname_nsmaps)
33852621Sllai1 				devname_nsmaps = oldmap->next;
33862621Sllai1 
33872621Sllai1 			map = oldmap->next;
33882621Sllai1 			if (map)
33892621Sllai1 				map->prev = oldmap->prev;
33902621Sllai1 			sdev_free_nsmap(oldmap);
33912621Sllai1 			oldmap = NULL;
33922621Sllai1 		} else {
33932621Sllai1 			rw_exit(&map->dir_lock);
33942621Sllai1 			map = map->next;
33952621Sllai1 		}
33962621Sllai1 	}
33972621Sllai1 	devname_nsmaps_invalidated = 0;
33982621Sllai1 }
33992621Sllai1 
34002621Sllai1 static int
34012621Sllai1 sdev_map_is_invalid(struct devname_nsmap *map)
34022621Sllai1 {
34032621Sllai1 	int ret = 0;
34042621Sllai1 
34052621Sllai1 	ASSERT(map);
34062621Sllai1 	rw_enter(&map->dir_lock, RW_READER);
34072621Sllai1 	if (map->dir_invalid)
34082621Sllai1 		ret = 1;
34092621Sllai1 	rw_exit(&map->dir_lock);
34102621Sllai1 	return (ret);
34112621Sllai1 }
34122621Sllai1 
34132621Sllai1 static int
34142621Sllai1 sdev_check_map(struct devname_nsmap *map)
34152621Sllai1 {
34162621Sllai1 	struct devname_nsmap *mapp;
34172621Sllai1 
34182621Sllai1 	mutex_enter(&devname_nsmaps_lock);
34192621Sllai1 	if (devname_nsmaps == NULL) {
34202621Sllai1 		mutex_exit(&devname_nsmaps_lock);
34212621Sllai1 		return (1);
34222621Sllai1 	}
34232621Sllai1 
34242621Sllai1 	for (mapp = devname_nsmaps; mapp; mapp = mapp->next) {
34252621Sllai1 		if (mapp == map) {
34262621Sllai1 			mutex_exit(&devname_nsmaps_lock);
34272621Sllai1 			return (0);
34282621Sllai1 		}
34292621Sllai1 	}
34302621Sllai1 
34312621Sllai1 	mutex_exit(&devname_nsmaps_lock);
34322621Sllai1 	return (1);
34332621Sllai1 
34342621Sllai1 }
34352621Sllai1 
34362621Sllai1 struct devname_nsmap *
34372621Sllai1 sdev_get_map(struct sdev_node *dv, int validate)
34382621Sllai1 {
34392621Sllai1 	struct devname_nsmap *map;
34402621Sllai1 	int error;
34412621Sllai1 
34422621Sllai1 	ASSERT(RW_READ_HELD(&dv->sdev_contents));
34432621Sllai1 	map = dv->sdev_mapinfo;
34442621Sllai1 	if (map && sdev_check_map(map)) {
34452621Sllai1 		if (!rw_tryupgrade(&dv->sdev_contents)) {
34462621Sllai1 			rw_exit(&dv->sdev_contents);
34472621Sllai1 			rw_enter(&dv->sdev_contents, RW_WRITER);
34482621Sllai1 		}
34492621Sllai1 		dv->sdev_mapinfo = NULL;
34502621Sllai1 		rw_downgrade(&dv->sdev_contents);
34512621Sllai1 		return (NULL);
34522621Sllai1 	}
34532621Sllai1 
34542621Sllai1 	if (validate && (!map || (map && sdev_map_is_invalid(map)))) {
34552621Sllai1 		if (!rw_tryupgrade(&dv->sdev_contents)) {
34562621Sllai1 			rw_exit(&dv->sdev_contents);
34572621Sllai1 			rw_enter(&dv->sdev_contents, RW_WRITER);
34582621Sllai1 		}
34592621Sllai1 		error = sdev_get_moduleops(dv);
34602621Sllai1 		if (!error)
34612621Sllai1 			map = dv->sdev_mapinfo;
34622621Sllai1 		rw_downgrade(&dv->sdev_contents);
34632621Sllai1 	}
34642621Sllai1 	return (map);
34652621Sllai1 }
34662621Sllai1 
34672621Sllai1 void
34682621Sllai1 sdev_handle_alloc(struct sdev_node *dv)
34692621Sllai1 {
34702621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
34712621Sllai1 	dv->sdev_handle.dh_data = dv;
34722621Sllai1 	rw_exit(&dv->sdev_contents);
34732621Sllai1 }
34742621Sllai1 
34752621Sllai1 
34762621Sllai1 extern int sdev_vnodeops_tbl_size;
34772621Sllai1 
34782621Sllai1 /*
34792621Sllai1  * construct a new template with overrides from vtab
34802621Sllai1  */
34812621Sllai1 static fs_operation_def_t *
34822621Sllai1 sdev_merge_vtab(const fs_operation_def_t tab[])
34832621Sllai1 {
34842621Sllai1 	fs_operation_def_t *new;
34852621Sllai1 	const fs_operation_def_t *tab_entry;
34862621Sllai1 
34872621Sllai1 	/* make a copy of standard vnode ops table */
34882621Sllai1 	new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP);
34892621Sllai1 	bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size);
34902621Sllai1 
34912621Sllai1 	/* replace the overrides from tab */
34922621Sllai1 	for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) {
34932621Sllai1 		fs_operation_def_t *std_entry = new;
34942621Sllai1 		while (std_entry->name) {
34952621Sllai1 			if (strcmp(tab_entry->name, std_entry->name) == 0) {
34962621Sllai1 				std_entry->func = tab_entry->func;
34972621Sllai1 				break;
34982621Sllai1 			}
34992621Sllai1 			std_entry++;
35002621Sllai1 		}
35012621Sllai1 		if (std_entry->name == NULL)
35022621Sllai1 			cmn_err(CE_NOTE, "sdev_merge_vtab: entry %s unused.",
35032621Sllai1 			    tab_entry->name);
35042621Sllai1 	}
35052621Sllai1 
35062621Sllai1 	return (new);
35072621Sllai1 }
35082621Sllai1 
35092621Sllai1 /* free memory allocated by sdev_merge_vtab */
35102621Sllai1 static void
35112621Sllai1 sdev_free_vtab(fs_operation_def_t *new)
35122621Sllai1 {
35132621Sllai1 	kmem_free(new, sdev_vnodeops_tbl_size);
35142621Sllai1 }
35152621Sllai1 
35162621Sllai1 void
35172621Sllai1 devname_get_vnode(devname_handle_t *hdl, vnode_t **vpp)
35182621Sllai1 {
35192621Sllai1 	struct sdev_node *dv = hdl->dh_data;
35202621Sllai1 
35212621Sllai1 	ASSERT(dv);
35222621Sllai1 
35232621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
35242621Sllai1 	*vpp = SDEVTOV(dv);
35252621Sllai1 	rw_exit(&dv->sdev_contents);
35262621Sllai1 }
35272621Sllai1 
35282621Sllai1 int
35292621Sllai1 devname_get_path(devname_handle_t *hdl, char **path)
35302621Sllai1 {
35312621Sllai1 	struct sdev_node *dv = hdl->dh_data;
35322621Sllai1 
35332621Sllai1 	ASSERT(dv);
35342621Sllai1 
35352621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
35362621Sllai1 	*path = dv->sdev_path;
35372621Sllai1 	rw_exit(&dv->sdev_contents);
35382621Sllai1 	return (0);
35392621Sllai1 }
35402621Sllai1 
35412621Sllai1 int
35422621Sllai1 devname_get_name(devname_handle_t *hdl, char **entry)
35432621Sllai1 {
35442621Sllai1 	struct sdev_node *dv = hdl->dh_data;
35452621Sllai1 
35462621Sllai1 	ASSERT(dv);
35472621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
35482621Sllai1 	*entry = dv->sdev_name;
35492621Sllai1 	rw_exit(&dv->sdev_contents);
35502621Sllai1 	return (0);
35512621Sllai1 }
35522621Sllai1 
35532621Sllai1 void
35542621Sllai1 devname_get_dir_vnode(devname_handle_t *hdl, vnode_t **vpp)
35552621Sllai1 {
35562621Sllai1 	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
35572621Sllai1 
35582621Sllai1 	ASSERT(dv);
35592621Sllai1 
35602621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
35612621Sllai1 	*vpp = SDEVTOV(dv);
35622621Sllai1 	rw_exit(&dv->sdev_contents);
35632621Sllai1 }
35642621Sllai1 
35652621Sllai1 int
35662621Sllai1 devname_get_dir_path(devname_handle_t *hdl, char **path)
35672621Sllai1 {
35682621Sllai1 	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
35692621Sllai1 
35702621Sllai1 	ASSERT(dv);
35712621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
35722621Sllai1 	*path = dv->sdev_path;
35732621Sllai1 	rw_exit(&dv->sdev_contents);
35742621Sllai1 	return (0);
35752621Sllai1 }
35762621Sllai1 
35772621Sllai1 int
35782621Sllai1 devname_get_dir_name(devname_handle_t *hdl, char **entry)
35792621Sllai1 {
35802621Sllai1 	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
35812621Sllai1 
35822621Sllai1 	ASSERT(dv);
35832621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
35842621Sllai1 	*entry = dv->sdev_name;
35852621Sllai1 	rw_exit(&dv->sdev_contents);
35862621Sllai1 	return (0);
35872621Sllai1 }
35882621Sllai1 
35892621Sllai1 int
35902621Sllai1 devname_get_dir_nsmap(devname_handle_t *hdl, struct devname_nsmap **map)
35912621Sllai1 {
35922621Sllai1 	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
35932621Sllai1 
35942621Sllai1 	ASSERT(dv);
35952621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
35962621Sllai1 	*map = dv->sdev_mapinfo;
35972621Sllai1 	rw_exit(&dv->sdev_contents);
35982621Sllai1 	return (0);
35992621Sllai1 }
36002621Sllai1 
36012621Sllai1 int
36022621Sllai1 devname_get_dir_handle(devname_handle_t *hdl, devname_handle_t **dir_hdl)
36032621Sllai1 {
36042621Sllai1 	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
36052621Sllai1 
36062621Sllai1 	ASSERT(dv);
36072621Sllai1 	rw_enter(&dv->sdev_contents, RW_READER);
36082621Sllai1 	*dir_hdl = &(dv->sdev_handle);
36092621Sllai1 	rw_exit(&dv->sdev_contents);
36102621Sllai1 	return (0);
36112621Sllai1 }
36122621Sllai1 
36132621Sllai1 void
36142621Sllai1 devname_set_nodetype(devname_handle_t *hdl, void *args, int spec)
36152621Sllai1 {
36162621Sllai1 	struct sdev_node *dv = hdl->dh_data;
36172621Sllai1 
36182621Sllai1 	ASSERT(dv);
36192621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
36202621Sllai1 	hdl->dh_spec = (devname_spec_t)spec;
36212621Sllai1 	hdl->dh_args = (void *)i_ddi_strdup((char *)args, KM_SLEEP);
36222621Sllai1 	rw_exit(&dv->sdev_contents);
36232621Sllai1 }
36242621Sllai1 
36252621Sllai1 /*
36262621Sllai1  * a generic setattr() function
36272621Sllai1  *
36282621Sllai1  * note: flags only supports AT_UID and AT_GID.
36292621Sllai1  *	 Future enhancements can be done for other types, e.g. AT_MODE
36302621Sllai1  */
36312621Sllai1 int
36322621Sllai1 devname_setattr_func(struct vnode *vp, struct vattr *vap, int flags,
36332621Sllai1     struct cred *cred, int (*callback)(struct sdev_node *, struct vattr *,
36342621Sllai1     int), int protocol)
36352621Sllai1 {
36362621Sllai1 	struct sdev_node	*dv = VTOSDEV(vp);
36372621Sllai1 	struct sdev_node	*parent = dv->sdev_dotdot;
36382621Sllai1 	struct vattr		*get;
36392621Sllai1 	uint_t			mask = vap->va_mask;
36402621Sllai1 	int 			error;
36412621Sllai1 
36422621Sllai1 	/* some sanity checks */
36432621Sllai1 	if (vap->va_mask & AT_NOSET)
36442621Sllai1 		return (EINVAL);
36452621Sllai1 
36462621Sllai1 	if (vap->va_mask & AT_SIZE) {
36472621Sllai1 		if (vp->v_type == VDIR) {
36482621Sllai1 			return (EISDIR);
36492621Sllai1 		}
36502621Sllai1 	}
36512621Sllai1 
36522621Sllai1 	/* no need to set attribute, but do not fail either */
36532621Sllai1 	ASSERT(parent);
36542621Sllai1 	rw_enter(&parent->sdev_contents, RW_READER);
36552621Sllai1 	if (dv->sdev_state == SDEV_ZOMBIE) {
36562621Sllai1 		rw_exit(&parent->sdev_contents);
36572621Sllai1 		return (0);
36582621Sllai1 	}
36592621Sllai1 
36602621Sllai1 	/* If backing store exists, just set it. */
36612621Sllai1 	if (dv->sdev_attrvp) {
36622621Sllai1 		rw_exit(&parent->sdev_contents);
36632621Sllai1 		return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL));
36642621Sllai1 	}
36652621Sllai1 
36662621Sllai1 	/*
36672621Sllai1 	 * Otherwise, for nodes with the persistence attribute, create it.
36682621Sllai1 	 */
36692621Sllai1 	ASSERT(dv->sdev_attr);
36702621Sllai1 	if (SDEV_IS_PERSIST(dv) ||
36712621Sllai1 	    ((vap->va_mask & ~AT_TIMES) != 0 && !SDEV_IS_DYNAMIC(dv))) {
36722621Sllai1 		sdev_vattr_merge(dv, vap);
36732621Sllai1 		rw_enter(&dv->sdev_contents, RW_WRITER);
36742621Sllai1 		error = sdev_shadow_node(dv, cred);
36752621Sllai1 		rw_exit(&dv->sdev_contents);
36762621Sllai1 		rw_exit(&parent->sdev_contents);
36772621Sllai1 
36782621Sllai1 		if (error)
36792621Sllai1 			return (error);
36802621Sllai1 		return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL));
36812621Sllai1 	}
36822621Sllai1 
36832621Sllai1 
36842621Sllai1 	/*
36852621Sllai1 	 * sdev_attr was allocated in sdev_mknode
36862621Sllai1 	 */
36872621Sllai1 	rw_enter(&dv->sdev_contents, RW_WRITER);
36882621Sllai1 	error = secpolicy_vnode_setattr(cred, vp, vap, dv->sdev_attr,
36892621Sllai1 	    flags, sdev_unlocked_access, dv);
36902621Sllai1 	if (error) {
36912621Sllai1 		rw_exit(&dv->sdev_contents);
36922621Sllai1 		rw_exit(&parent->sdev_contents);
36932621Sllai1 		return (error);
36942621Sllai1 	}
36952621Sllai1 
36962621Sllai1 	get = dv->sdev_attr;
36972621Sllai1 	if (mask & AT_MODE) {
36982621Sllai1 		get->va_mode &= S_IFMT;
36992621Sllai1 		get->va_mode |= vap->va_mode & ~S_IFMT;
37002621Sllai1 	}
37012621Sllai1 
37022621Sllai1 	if ((mask & AT_UID) || (mask & AT_GID)) {
37032621Sllai1 		if (mask & AT_UID)
37042621Sllai1 			get->va_uid = vap->va_uid;
37052621Sllai1 		if (mask & AT_GID)
37062621Sllai1 			get->va_gid = vap->va_gid;
37072621Sllai1 		/*
37082621Sllai1 		 * a callback must be provided if the protocol is set
37092621Sllai1 		 */
37102621Sllai1 		if ((protocol & AT_UID) || (protocol & AT_GID)) {
37112621Sllai1 			ASSERT(callback);
37122621Sllai1 			error = callback(dv, get, protocol);
37132621Sllai1 			if (error) {
37142621Sllai1 				rw_exit(&dv->sdev_contents);
37152621Sllai1 				rw_exit(&parent->sdev_contents);
37162621Sllai1 				return (error);
37172621Sllai1 			}
37182621Sllai1 		}
37192621Sllai1 	}
37202621Sllai1 
37212621Sllai1 	if (mask & AT_ATIME)
37222621Sllai1 		get->va_atime = vap->va_atime;
37232621Sllai1 	if (mask & AT_MTIME)
37242621Sllai1 		get->va_mtime = vap->va_mtime;
37252621Sllai1 	if (mask & (AT_MODE | AT_UID | AT_GID | AT_CTIME)) {
37262621Sllai1 		gethrestime(&get->va_ctime);
37272621Sllai1 	}
37282621Sllai1 
37292621Sllai1 	sdev_vattr_merge(dv, get);
37302621Sllai1 	rw_exit(&dv->sdev_contents);
37312621Sllai1 	rw_exit(&parent->sdev_contents);
37322621Sllai1 	return (0);
37332621Sllai1 }
3734