changeset 2621:4ea88858d952

PSARC/2003/246 Filesystem Driven Device Naming 5050715 logical device names not created during early boot 6292952 devfsadm mishandles optarg 6362924 devfsadm secondary link generation is not zones aware 6413127 Integrate the Devname Project 6464196 bfu should remove pt_chmod, obsoleted by /dev filesystem
author llai1
date Fri, 25 Aug 2006 17:24:25 -0700
parents b7ba4a998a90
children ffef85cab3c3
files deleted_files/usr/src/cmd/pt_chmod/Makefile deleted_files/usr/src/cmd/pt_chmod/pt_chmod.c usr/src/Makefile.lint usr/src/cmd/Makefile usr/src/cmd/allocate/Makefile usr/src/cmd/allocate/allocate.c usr/src/cmd/allocate/allocate3.c usr/src/cmd/allocate/mkdevalloc.c usr/src/cmd/devfsadm/devfsadm.c usr/src/cmd/devfsadm/devfsadm.h usr/src/cmd/devfsadm/devfsadm_impl.h usr/src/cmd/devfsadm/message.h usr/src/cmd/devfsadm/misc_link.c usr/src/cmd/fs.d/Makefile usr/src/cmd/fs.d/dev/Makefile usr/src/cmd/fs.d/dev/mount.c usr/src/cmd/initpkg/rc2.sh usr/src/cmd/pt_chmod/Makefile usr/src/cmd/pt_chmod/pt_chmod.c usr/src/cmd/truss/print.c usr/src/cmd/zlogin/Makefile usr/src/cmd/zlogin/zlogin.c usr/src/cmd/zoneadmd/vplat.c usr/src/cmd/zoneadmd/zcons.c usr/src/head/libzonecfg.h usr/src/lib/libc/port/gen/pt.c usr/src/lib/libdevinfo/Makefile.com usr/src/lib/libdevinfo/devinfo_devlink.c usr/src/lib/libdevinfo/devinfo_devlink.h usr/src/lib/libdevinfo/devinfo_devname.c usr/src/lib/libdevinfo/devinfo_devperm.c usr/src/lib/libdevinfo/devinfo_finddev.c usr/src/lib/libdevinfo/libdevinfo.h usr/src/lib/libdevinfo/mapfile-vers usr/src/lib/libtsol/common/label.h usr/src/lib/libzonecfg/common/libzonecfg.c usr/src/lib/libzonecfg/common/mapfile-vers usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 usr/src/pkgdefs/SUNWckr/prototype_com usr/src/pkgdefs/SUNWckr/prototype_i386 usr/src/pkgdefs/SUNWckr/prototype_sparc usr/src/pkgdefs/SUNWcsr/prototype_com usr/src/pkgdefs/SUNWcsu/prototype_com usr/src/pkgdefs/SUNWhea/prototype_com usr/src/tools/scripts/bfu.sh usr/src/ucbcmd/ucblinks/ucblinks.c usr/src/uts/Makefile.targ usr/src/uts/Makefile.uts usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/cpr/cpr_misc.c usr/src/uts/common/fs/dev/sdev_comm.c usr/src/uts/common/fs/dev/sdev_ncache.c usr/src/uts/common/fs/dev/sdev_nsconfig_mod.c usr/src/uts/common/fs/dev/sdev_profile.c usr/src/uts/common/fs/dev/sdev_ptsops.c usr/src/uts/common/fs/dev/sdev_subr.c usr/src/uts/common/fs/dev/sdev_vfsops.c usr/src/uts/common/fs/dev/sdev_vnops.c usr/src/uts/common/fs/devfs/devfs_subr.c usr/src/uts/common/fs/namefs/namevfs.c usr/src/uts/common/fs/vfs.c usr/src/uts/common/io/ptm.c usr/src/uts/common/io/ptms_conf.c usr/src/uts/common/io/pts.c usr/src/uts/common/os/devcfg.c usr/src/uts/common/os/devctl.c usr/src/uts/common/os/modconf.c usr/src/uts/common/os/modctl.c usr/src/uts/common/os/vfs_conf.c usr/src/uts/common/sys/Makefile usr/src/uts/common/sys/autoconf.h usr/src/uts/common/sys/devctl_impl.h usr/src/uts/common/sys/fs/dv_node.h usr/src/uts/common/sys/fs/sdev_impl.h usr/src/uts/common/sys/fs/sdev_node.h usr/src/uts/common/sys/mntent.h usr/src/uts/common/sys/modctl.h usr/src/uts/common/sys/ptms.h usr/src/uts/common/sys/sunddi.h usr/src/uts/common/syscall/uadmin.c usr/src/uts/i86pc/os/startup.c usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/dev/Makefile usr/src/uts/intel/ia32/ml/modstubs.s usr/src/uts/intel/sdev_nsconfig_mod/Makefile usr/src/uts/sparc/Makefile.sparc.shared usr/src/uts/sparc/dev/Makefile usr/src/uts/sparc/ml/modstubs.s usr/src/uts/sparc/sdev_nsconfig_mod/Makefile
diffstat 90 files changed, 13536 insertions(+), 2147 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deleted_files/usr/src/cmd/pt_chmod/Makefile	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License").  You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG= pt_chmod
+
+include ../Makefile.cmd
+
+FILEMODE= 04511
+
+LDLIBS += -ldevinfo -lsec
+
+.KEEP_STATE:
+
+all: $(PROG) 
+
+install: all $(ROOTLIBPROG)
+
+clean:
+
+lint:	lint_PROG
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deleted_files/usr/src/cmd/pt_chmod/pt_chmod.c	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,105 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
+ *
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <stropts.h>
+#include <sys/acl.h>
+#include <libdevinfo.h>
+
+#define	DEFAULT_TTY_GROUP	"tty"
+
+/*
+ * 1) change the owner and mode of the pseudo terminal slave device.
+ * 2) (re)create nodes and devlinks for pseduo terminal slave device.
+ */
+int
+main(int argc, char **argv)
+{
+	int	fd;
+	gid_t	gid;
+	char	*tty;
+	di_devlink_handle_t pp;
+
+	struct	group	*gr_name_ptr;
+
+	if (argc > 2)
+		return (1);
+
+	if ((gr_name_ptr = getgrnam(DEFAULT_TTY_GROUP)) != NULL)
+		gid = gr_name_ptr->gr_gid;
+	else
+		gid = getgid();
+
+	/* create pts minor device nodes and symlinks */
+	if (argc == 1) {
+		pp = di_devlink_init("pts", DI_MAKE_LINK);
+		if (pp != NULL) {
+			(void) di_devlink_fini(&pp);
+			return (0);
+		}
+		return (1);
+	}
+
+	fd = atoi(argv[1]);
+
+	tty = ptsname(fd);
+
+	if (tty == NULL)
+		return (1);
+
+	/*
+	 * Detach all STREAMs.
+	 * We need to continue to try this until we have succeeded
+	 * in calling chown on the underlying node.  From that point
+	 * onwards, no-one but root can fattach() as fattach() requires
+	 * ownership of the node.
+	 */
+	do {
+		if (chown(tty, 0, 0) != 0)
+			exit(1);
+	} while (fdetach(tty) == 0);
+
+	/* Remove ACLs */
+
+	(void) acl_strip(tty, 0, gid, 0620);
+
+	if (chown(tty, getuid(), gid))
+		return (1);
+
+	if (chmod(tty, 00620))
+		return (1);
+
+	return (0);
+}
--- a/usr/src/Makefile.lint	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/Makefile.lint	Fri Aug 25 17:24:25 2006 -0700
@@ -217,7 +217,6 @@
 	cmd/psradm \
 	cmd/psrinfo \
 	cmd/psrset \
-	cmd/pt_chmod \
 	cmd/ptools \
 	cmd/pwck \
 	cmd/pwconv \
--- a/usr/src/cmd/Makefile	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/Makefile	Fri Aug 25 17:24:25 2006 -0700
@@ -297,7 +297,6 @@
 	psradm		\
 	psrinfo		\
 	psrset		\
-	pt_chmod	\
 	ptools		\
 	pwck		\
 	pwconv		\
--- a/usr/src/cmd/allocate/Makefile	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/allocate/Makefile	Fri Aug 25 17:24:25 2006 -0700
@@ -87,9 +87,9 @@
 $(ROOTSECLIB)/% :=	OWNER = root
 $(ROOTSECLIB)/% :=	GROUP = sys
 
-LAZYLIBS =	$(ZLAZYLOAD) -ltsol -lgen $(ZNOLAZYLOAD)
-lint :=		LDLIBS += -lbsm -lsec -lsecdb -ltsol -lgen
-$(PROGalloc) :=		LDLIBS += -lbsm -lsec -lsecdb $(LAZYLIBS)
+LAZYLIBS =	$(ZLAZYLOAD) -ltsol $(ZNOLAZYLOAD)
+lint :=		LDLIBS += -lbsm -lsec -lsecdb -ltsol -ldevinfo
+$(PROGalloc) :=		LDLIBS += -lbsm -lsec -lsecdb -ldevinfo $(LAZYLIBS)
 $(PROGmkdevalloc) :=	LDLIBS += -lbsm
 $(PROGdminfo) :=	LDLIBS += -lbsm
 $(PROGaudio) :=		LDLIBS += -lbsm
--- a/usr/src/cmd/allocate/allocate.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/allocate/allocate.c	Fri Aug 25 17:24:25 2006 -0700
@@ -34,8 +34,10 @@
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
+#include <fcntl.h>
 #include <nss_dbdefs.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/wait.h>
 #include <tsol/label.h>
 #include <zone.h>
@@ -269,12 +271,24 @@
 	char		pw_buf[NSS_BUFLEN_PASSWD];
 	struct passwd	pw_ent;
 	int 		env_num = 1;	/* PATH= is 0 entry */
+#ifdef DEBUG
+	struct stat	statbuf;
+#endif
 
 	(void) setlocale(LC_ALL, "");
 	(void) textdomain(TEXT_DOMAIN);
 
 	system_labeled = is_system_labeled();
 
+	/* test hook: see also mkdevalloc.c and devfsadm.c */
+	if (!system_labeled) {
+		system_labeled = is_system_labeled_debug(&statbuf);
+		if (system_labeled) {
+			fprintf(stderr, "/ALLOCATE_FORCE_LABEL is set,\n"
+			    "forcing system label on for testing...\n");
+		}
+	}
+
 	/*
 	 * get all enviroment variables
 	 * which affect on internationalization.
--- a/usr/src/cmd/allocate/allocate3.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/allocate/allocate3.c	Fri Aug 25 17:24:25 2006 -0700
@@ -62,6 +62,7 @@
 #include <zone.h>
 #include <nss_dbdefs.h>
 #include <bsm/devalloc.h>
+#include <libdevinfo.h>
 #include "allocate.h"
 
 extern void print_error(int, char *);
@@ -114,7 +115,7 @@
 	char	**path;
 };
 
-struct devnames {
+struct dev_names {
 	char **dnames;
 };
 
@@ -1227,7 +1228,7 @@
 }
 
 void
-_store_devnames(int *count, struct devnames *dnms, char *zonename,
+_store_devnames(int *count, struct dev_names *dnms, char *zonename,
     devalloc_t *da, int flag)
 {
 	int i;
@@ -1257,7 +1258,7 @@
 	int		count = 0;
 	int		error = 0;
 	devalloc_t	*da;
-	struct devnames	dnms;
+	struct dev_names dnms;
 
 	if (optflag & (FORCE | USERID | USERNAME)) {
 		if (!_is_authorized(DEVICE_REVOKE_AUTH, getuid()))
@@ -1329,7 +1330,7 @@
 	int		count = 0;
 	int		error = 0;
 	devalloc_t	*da;
-	struct devnames dnms;
+	struct dev_names dnms;
 
 	if (optflag & (FORCE | FORCE_ALL)) {
 		if (!_is_authorized(DEVICE_REVOKE_AUTH, getuid()))
@@ -1557,14 +1558,13 @@
 int
 create_znode(char *zonename, struct zone_path *zpath, devmap_t *list)
 {
-	int		i;
 	int		size;
 	int		len = 0;
 	int		fcount = 0;
 	char		*p, *tmpfile, *zoneroot;
 	char		**file;
 	char		zonepath[MAXPATHLEN];
-	struct stat	statb;
+	di_prof_t	prof = NULL;
 
 	file = list->dmap_devarray;
 	if (file == NULL)
@@ -1575,16 +1575,15 @@
 	}
 	(void) strcpy(zonepath, zoneroot);
 	free(zoneroot);
-	if ((p = strstr(zonepath, "/root")) == NULL)
-		return (1);
-	*p = '\0';
 	len = strlen(zonepath);
 	size = sizeof (zonepath);
+	(void) strlcat(zonepath, "/dev", size);
+	if (di_prof_init(zonepath, &prof)) {
+		dprintf("failed to initialize dev profile at %s\n", zonepath);
+		return (1);
+	}
+	zonepath[len] = '\0';
 	for (; *file != NULL; file++) {
-		if (stat(*file, &statb) == -1) {
-			dprintf("Couldn't stat the file %s\n", *file);
-			return (1);
-		}
 		/*
 		 * First time initialization
 		 */
@@ -1615,48 +1614,34 @@
 			free(tmpfile);
 			tmpfile = strdup(dstlinkdir);
 		}
-		if ((p = rindex(tmpfile, '/')) == NULL) {
-			dprintf("bad path in create_znode for %s\n",
-			    tmpfile);
+		if (di_prof_add_dev(prof, tmpfile)) {
+			dprintf("failed to add %s to profile\n", tmpfile);
+			di_prof_fini(prof);
 			return (1);
 		}
-		*p = '\0';
-		(void) strcat(zonepath, tmpfile);
-		*p = '/';
-		if ((mkdirp(zonepath, 0755) != 0) && (errno != EEXIST)) {
-			dprintf("Unable to create directory %s\n", zonepath);
-			return (1);
-		}
-		zonepath[len] = '\0';
 		if (strlcat(zonepath, tmpfile, size) >= size) {
 			dprintf("Buffer overflow in create_znode for %s\n",
 			    *file);
 			free(tmpfile);
+			di_prof_fini(prof);
 			return (1);
 		}
 		free(tmpfile);
 		fcount++;
 		if ((zpath->path = (char **)realloc(zpath->path,
-		    (fcount * sizeof (char *)))) == NULL)
+		    (fcount * sizeof (char *)))) == NULL) {
+			di_prof_fini(prof);
 			return (1);
+		}
 		zpath->path[zpath->count] = strdup(zonepath);
 		zpath->count = fcount;
-		if (mknod(zonepath, statb.st_mode, statb.st_rdev) == -1) {
-			switch (errno) {
-			case EEXIST:
-				break;
-			default:
-				dprintf("mknod error: %s\n",
-				    strerror(errno));
-				for (i = 0; i <= fcount; i++)
-					free(zpath->path[i]);
-				free(zpath->path);
-				return (1);
-			}
-		}
 		zonepath[len] = '\0';
 	}
 
+	if (di_prof_commit(prof))
+		dprintf("failed to add devices to zone %s\n", zonename);
+	di_prof_fini(prof);
+
 	return (0);
 }
 
@@ -1667,6 +1652,7 @@
 	char		*zoneroot;
 	char		**file;
 	char		zonepath[MAXPATHLEN];
+	di_prof_t	prof = NULL;
 
 	file = dm->dmap_devarray;
 	if (file == NULL)
@@ -1674,11 +1660,6 @@
 	if ((zoneroot = getzonerootbyname(zonename)) == NULL) {
 		(void) snprintf(zonepath, MAXPATHLEN, "/zone/%s", zonename);
 	} else {
-		char *p;
-
-		if ((p = strstr(zoneroot, "/root")) == NULL)
-			return (1);
-		*p = '\0';
 		(void)  strcpy(zonepath, zoneroot);
 		free(zoneroot);
 	}
@@ -1688,6 +1669,10 @@
 	 */
 	(void) strncat(zonepath, "/dev", MAXPATHLEN);
 	len = strlen(zonepath);
+	if (di_prof_init(zonepath, &prof)) {
+		dprintf("failed to initialize dev profile at %s\n", zonepath);
+		return (1);
+	}
 	for (; *file != NULL; file++) {
 		char *devrelpath;
 
@@ -1702,19 +1687,17 @@
 		 */
 		devrelpath = strchr(*file + 1, '/');
 
-		if (strlcat(zonepath, devrelpath, MAXPATHLEN) >= MAXPATHLEN) {
-			dprintf("Buffer overflow in remove_znode for %s\n",
-			    *file);
-			return (1);
-		}
-		errno = 0;
-		if ((unlink(zonepath) == -1) && (errno != ENOENT)) {
-			perror(zonepath);
+		if (di_prof_add_exclude(prof, devrelpath + 1)) {
+			dprintf("Failed exclude %s in dev profile\n", *file);
+			di_prof_fini(prof);
 			return (1);
 		}
 		zonepath[len] = '\0';
 	}
 
+	if (di_prof_commit(prof))
+		dprintf("failed to remove devices from zone %s\n", zonename);
+	di_prof_fini(prof);
 	return (0);
 }
 
--- a/usr/src/cmd/allocate/mkdevalloc.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/allocate/mkdevalloc.c	Fri Aug 25 17:24:25 2006 -0700
@@ -180,6 +180,16 @@
 		exit(1);
 
 	system_labeled = is_system_labeled();
+
+	/* test hook: see also devfsadm.c and allocate.c */
+	if (!system_labeled) {
+		system_labeled = is_system_labeled_debug(&tx_stat);
+		if (system_labeled) {
+			fprintf(stderr, "/ALLOCATE_FORCE_LABEL is set,\n"
+			    "forcing system label on for testing...\n");
+		}
+	}
+
 	if (system_labeled == 0) {
 		/*
 		 * is_system_labeled() will return false in case we are
--- a/usr/src/cmd/devfsadm/devfsadm.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/devfsadm/devfsadm.c	Fri Aug 25 17:24:25 2006 -0700
@@ -142,8 +142,14 @@
 /* /dev or <rootdir>/dev */
 static char *dev_dir = DEV;
 
-/* /dev for the global zone */
-static char *global_dev_dir = DEV;
+/* /etc/dev or <rootdir>/etc/dev */
+static char *etc_dev_dir = ETCDEV;
+
+/*
+ * writable root (for lock files and doors during install).
+ * This is also root dir for /dev attr dir during install.
+ */
+static char *attr_root = NULL;
 
 /* /etc/path_to_inst unless -p used */
 static char *inst_file = INSTANCE_FILE;
@@ -193,7 +199,6 @@
 /* Globals used by the link database */
 static di_devlink_handle_t devlink_cache;
 static int update_database = FALSE;
-static int devlink_door_fd = -1; /* fd of devlink handler door */
 
 /* Globals used to set logindev perms */
 static struct login_dev *login_dev_cache = NULL;
@@ -202,11 +207,6 @@
 /* Global to use devinfo snapshot cache */
 static int use_snapshot_cache = FALSE;
 
-/* Zone-related information */
-static int zone_cmd_mode = 0;
-static mutex_t zone_mutex; /* protects zone registration/unregistration ops */
-static struct zone_devinfo *zone_head;	/* linked list of zones */
-
 /*
  * Packaged directories - not removed even when empty.
  * The dirs must be listed in canonical form
@@ -225,9 +225,19 @@
 static cond_t rcm_eventq_cv;
 static volatile int need_to_exit_rcm_event_thread = 0;
 
+/* Devname globals */
+static int devname_debug_msg = 1;
+static nvlist_t *devname_maps = NULL;
+static int devname_first_call = 1;
+static int load_devname_nsmaps = FALSE;
+static int lookup_door_fd = -1;
+static char *lookup_door_path;
+
 static void load_dev_acl(void);
 static void update_drvconf(major_t);
-
+static void check_reconfig_state(void);
+static void devname_setup_nsmaps(void);
+static int s_stat(const char *, struct stat *);
 
 int
 main(int argc, char *argv[])
@@ -284,6 +294,10 @@
 		 */
 		if (stat(DA_LABEL_CHECK, &tx_stat) == 0)
 			system_labeled = TRUE;
+		else {
+			/* test hook: see also mkdevalloc.c and allocate.c */
+			system_labeled = is_system_labeled_debug(&tx_stat);
+		}
 	}
 
 	parse_args(argc, argv);
@@ -385,57 +399,52 @@
 }
 
 /*
- * set_zone_params sets us up to run against a specific zone.  It should
- * only be called from parse_args.
+ * As devfsadm is run early in boot to provide the kernel with
+ * minor_perm info, we might as well check for reconfig at the
+ * same time to avoid running devfsadm twice.  This gets invoked
+ * earlier than the env variable RECONFIG_BOOT is set up.
  */
-static int
-set_zone_params(char *zone_name)
-{
-	char zpath[MAXPATHLEN];
-	zone_dochandle_t hdl;
-	void *dlhdl;
-
-	assert(daemon_mode == FALSE);
-
-	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0) {
-		err_print(INVALID_ZONE, zone_name);
-		return (DEVFSADM_FAILURE);
-	}
-
-	if ((dlhdl = dlopen(LIBZONECFG_PATH, RTLD_LAZY)) == NULL) {
-		err_print(ZONE_LIB_MISSING);
-		return (DEVFSADM_FAILURE);
-	}
-
-	if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) {
-		err_print(ZONE_ROOTPATH_FAILED, zone_name, strerror(errno));
-		(void) dlclose(dlhdl);
-		return (DEVFSADM_FAILURE);
-	}
-	set_root_devices_dev_dir(zpath, 1);
-
-	if ((hdl = zonecfg_init_handle()) == NULL) {
-		err_print(ZONE_REP_FAILED, zone_name, strerror(errno));
-		(void) dlclose(dlhdl);
-		return (DEVFSADM_FAILURE);
-	}
-
-	if ((zonecfg_get_snapshot_handle(zone_name, hdl)) != Z_OK) {
-		err_print(ZONE_REP_FAILED, zone_name, strerror(errno));
-		zonecfg_fini_handle(hdl);
-		(void) dlclose(dlhdl);
-		return (DEVFSADM_FAILURE);
-	}
-	(void) dlclose(dlhdl);
-
-	zone_head = s_malloc(sizeof (struct zone_devinfo));
-	zone_head->zone_path = s_strdup(zpath);
-	zone_head->zone_name = s_strdup(zone_name);
-	zone_head->zone_dochdl = hdl;
-	zone_head->zone_next = NULL;
-	zone_cmd_mode = 1;
-	return (DEVFSADM_SUCCESS);
-}
+static void
+check_reconfig_state()
+{
+	struct stat sb;
+
+	if (s_stat("/reconfigure", &sb) == 0) {
+		(void) modctl(MODDEVNAME, MODDEVNAME_RECONFIG, 0);
+	}
+}
+
+static void
+modctl_sysavail()
+{
+	/*
+	 * Inform /dev that system is available, that
+	 * implicit reconfig can now be performed.
+	 */
+	(void) modctl(MODDEVNAME, MODDEVNAME_SYSAVAIL, 0);
+}
+
+static void
+set_lock_root(void)
+{
+	struct stat sb;
+	char *lock_root;
+	size_t len;
+
+	lock_root = attr_root ? attr_root : root_dir;
+
+	len = strlen(lock_root) + strlen(ETCDEV) + 1;
+	etc_dev_dir = s_malloc(len);
+	(void) snprintf(etc_dev_dir, len, "%s%s", lock_root, ETCDEV);
+
+	if (s_stat(etc_dev_dir, &sb) != 0) {
+		s_mkdirp(etc_dev_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
+	} else if (!S_ISDIR(sb.st_mode)) {
+		err_print(NOT_DIR, etc_dev_dir);
+		devfsadm_exit(1);
+	}
+}
+
 
 /*
  * Parse arguments for all 6 programs handled from devfsadm.
@@ -489,7 +498,7 @@
 				load_attach_drv = FALSE;
 				break;
 			case 'r':
-				set_root_devices_dev_dir(optarg, 0);
+				set_root_devices_dev_dir(optarg);
 				if (zone_pathcheck(root_dir) !=
 				    DEVFSADM_SUCCESS)
 					devfsadm_exit(1);
@@ -636,10 +645,10 @@
 	} else if ((strcmp(prog, DEVFSADM) == 0) ||
 	    (strcmp(prog, DEVFSADMD) == 0)) {
 		char *zonename = NULL;
-		enum zreg_op zoneop;
 		int init_drvconf = 0;
 		int init_perm = 0;
 		int public_mode = 0;
+		int init_sysavail = 0;
 
 		if (strcmp(prog, DEVFSADMD) == 0) {
 			daemon_mode = TRUE;
@@ -648,16 +657,19 @@
 		devlinktab_file = DEVLINKTAB_FILE;
 
 		while ((opt = getopt(argc, argv,
-		    "Cc:deIi:l:np:PR:r:st:vV:x:z:Z:")) != EOF) {
-			if (opt == 'I' || opt == 'P') {
+		    "a:Cc:deIi:l:mnp:PR:r:sSt:vV:x:")) != EOF) {
+			if (opt == 'I' || opt == 'P' || opt == 'S') {
 				if (public_mode)
 					usage();
 			} else {
-				if (init_perm || init_drvconf)
+				if (init_perm || init_drvconf || init_sysavail)
 					usage();
 				public_mode = 1;
 			}
 			switch (opt) {
+			case 'a':
+				attr_root = s_strdup(optarg);
+				break;
 			case 'C':
 				cleanup = TRUE;
 				break;
@@ -698,6 +710,9 @@
 				/* specify an alternate module load path */
 				module_dirs = s_strdup(optarg);
 				break;
+			case 'm':
+				load_devname_nsmaps = TRUE;
+				break;
 			case 'n':
 				/* prevent driver loading and deferred attach */
 				load_attach_drv = FALSE;
@@ -721,7 +736,7 @@
 				devfsadm_exit(devfsadm_copy());
 				break;
 			case 'r':
-				set_root_devices_dev_dir(optarg, 0);
+				set_root_devices_dev_dir(optarg);
 				break;
 			case 's':
 				/*
@@ -731,6 +746,11 @@
 				file_mods = FALSE;
 				flush_path_to_inst_enable = FALSE;
 				break;
+			case 'S':
+				if (daemon_mode == TRUE)
+					usage();
+				init_sysavail = 1;
+				break;
 			case 't':
 				devlinktab_file = optarg;
 				break;
@@ -765,19 +785,10 @@
 					usage();
 				}
 				break;
-			case 'z':
-				zonename = optarg;
-				zoneop = ZONE_REG;
-				break;
-			case 'Z':
-				zonename = optarg;
-				zoneop = ZONE_UNREG;
-				break;
 			default:
 				usage();
 				break;
 			}
-
 		}
 		if (optind < argc) {
 			usage();
@@ -792,44 +803,27 @@
 				devfsadm_exit(1);
 		}
 
-		if (zonename != NULL) {
-			/*
-			 * -z and -Z cannot be used if we're the daemon.  The
-			 * daemon always manages all zones.
-			 */
-			if (daemon_mode == TRUE)
-				usage();
-
-			/*
-			 * -z and -Z are private flags, but to be paranoid we
-			 * check whether they have been combined with -r.
-			 */
-			if (*root_dir != '\0')
-				usage();
-
-			if (set_zone_params(optarg) != DEVFSADM_SUCCESS)
-				devfsadm_exit(1);
-
-			call_zone_register(zonename, zoneop);
-			if (zoneop == ZONE_UNREG)
-				devfsadm_exit(0);
-			/*
-			 * If we are in ZONE_REG mode we plow on, laying out
-			 * devices for this zone.
-			 */
-		}
-		if (init_drvconf || init_perm) {
+		if (init_drvconf || init_perm || init_sysavail) {
 			/*
 			 * Load minor perm before force-loading drivers
 			 * so the correct permissions are picked up.
 			 */
-			if (init_perm)
+			if (init_perm) {
+				check_reconfig_state();
 				load_dev_acl();
+			}
 			if (init_drvconf)
 				update_drvconf((major_t)-1);
+			if (init_sysavail)
+				modctl_sysavail();
 			devfsadm_exit(0);
 			/* NOTREACHED */
 		}
+
+		if (load_devname_nsmaps == TRUE) {
+			devname_setup_nsmaps();
+			devfsadm_exit(0);
+		}
 	}
 
 
@@ -852,7 +846,7 @@
 				load_attach_drv = FALSE;
 				break;
 			case 'r':
-				set_root_devices_dev_dir(optarg, 0);
+				set_root_devices_dev_dir(optarg);
 				if (zone_pathcheck(root_dir) !=
 				    DEVFSADM_SUCCESS)
 					devfsadm_exit(1);
@@ -879,6 +873,7 @@
 			usage();
 		}
 	}
+	set_lock_root();
 }
 
 void
@@ -1089,13 +1084,30 @@
 static void
 print_cache_signal(int signo)
 {
-
 	if (signal(SIGUSR1, print_cache_signal) == SIG_ERR) {
 		err_print("signal SIGUSR1 failed: %s\n", strerror(errno));
 		devfsadm_exit(1);
 	}
 }
 
+static void
+revoke_lookup_door(void)
+{
+	if (lookup_door_fd != -1) {
+		if (door_revoke(lookup_door_fd) == -1) {
+			err_print("door_revoke of %s failed - %s\n",
+			    lookup_door_path, strerror(errno));
+		}
+	}
+}
+
+/*ARGSUSED*/
+static void
+catch_exit(int signo)
+{
+	revoke_lookup_door();
+}
+
 /*
  * Register with eventd for messages. Create doors for synchronous
  * link creation.
@@ -1114,9 +1126,14 @@
 		err_print("signal SIGUSR1 failed: %s\n", strerror(errno));
 		devfsadm_exit(1);
 	}
+	if (signal(SIGTERM, catch_exit) == SIG_ERR) {
+		err_print("signal SIGTERM failed: %s\n", strerror(errno));
+		devfsadm_exit(1);
+	}
 
 	if (snprintf(door_file, sizeof (door_file),
-	    "%s%s", root_dir, DEVFSADM_SERVICE_DOOR) >= sizeof (door_file)) {
+	    "%s%s", attr_root ? attr_root : root_dir, DEVFSADM_SERVICE_DOOR)
+	    >= sizeof (door_file)) {
 		err_print("update_daemon failed to open sysevent service "
 		    "door\n");
 		devfsadm_exit(1);
@@ -1142,33 +1159,12 @@
 		(void) sysevent_close_channel(sysevent_hp);
 		devfsadm_exit(1);
 	}
-
-	if (snprintf(door_file, sizeof (door_file),
-	    "%s/%s", dev_dir, ZONE_REG_DOOR) >= sizeof (door_file)) {
-		err_print(CANT_CREATE_ZONE_DOOR, door_file,
+	if (snprintf(door_file, sizeof (door_file), "%s/%s",
+	    etc_dev_dir, DEVFSADM_SYNCH_DOOR) >= sizeof (door_file)) {
+		err_print(CANT_CREATE_DOOR, DEVFSADM_SYNCH_DOOR,
 		    strerror(ENAMETOOLONG));
 		devfsadm_exit(1);
 	}
-	(void) s_unlink(door_file);
-	if ((fd = open(door_file, O_RDWR | O_CREAT, ZONE_DOOR_PERMS)) == -1) {
-		err_print(CANT_CREATE_ZONE_DOOR, door_file, strerror(errno));
-		devfsadm_exit(1);
-	}
-	(void) close(fd);
-	if ((fd = door_create(zone_reg_handler, NULL,
-	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
-		err_print(CANT_CREATE_ZONE_DOOR, door_file, strerror(errno));
-		(void) s_unlink(door_file);
-		devfsadm_exit(1);
-	}
-	if (fattach(fd, door_file) == -1) {
-		err_print(CANT_CREATE_ZONE_DOOR, door_file, strerror(errno));
-		(void) s_unlink(door_file);
-		devfsadm_exit(1);
-	}
-
-	(void) snprintf(door_file, sizeof (door_file), "%s/%s", dev_dir,
-	    DEVFSADM_SYNCH_DOOR);
 
 	(void) s_unlink(door_file);
 	if ((fd = open(door_file, O_RDWR | O_CREAT, SYNCH_DOOR_PERMS)) == -1) {
@@ -1189,14 +1185,48 @@
 		(void) s_unlink(door_file);
 		devfsadm_exit(1);
 	}
-	devlink_door_fd = fd;
 
 	/*
-	 * Make sure devfsadm is managing any and all configured system zones.
+	 * devname_lookup_door
 	 */
-	if (register_all_zones() != DEVFSADM_SUCCESS) {
-		err_print(ZONE_LIST_FAILED, strerror(errno));
-	}
+	if (snprintf(door_file, sizeof (door_file), "%s/%s",
+	    etc_dev_dir, DEVNAME_LOOKUP_DOOR) >= sizeof (door_file)) {
+		err_print(CANT_CREATE_DOOR, DEVNAME_LOOKUP_DOOR,
+		    strerror(ENAMETOOLONG));
+		devfsadm_exit(1);
+	}
+
+	(void) s_unlink(door_file);
+	if ((fd = open(door_file, O_RDWR | O_CREAT, S_IRUSR|S_IWUSR)) == -1) {
+		err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
+		devfsadm_exit(1);
+	}
+	(void) close(fd);
+
+	if ((fd = door_create(devname_lookup_handler, NULL,
+	    DOOR_REFUSE_DESC)) == -1) {
+		err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
+		(void) s_unlink(door_file);
+		devfsadm_exit(1);
+	}
+
+	(void) fdetach(door_file);
+	lookup_door_path = s_strdup(door_file);
+retry:
+	if (fattach(fd, door_file) == -1) {
+		if (errno == EBUSY)
+			goto retry;
+		err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
+		(void) s_unlink(door_file);
+		devfsadm_exit(1);
+	}
+	lookup_door_fd = fd;
+
+	/* pass down the door name to kernel for door_ki_open */
+	if (devname_kcall(MODDEVNAME_LOOKUPDOOR, (void *)door_file) != 0)
+		err_print(DEVNAME_CONTACT_FAILED, strerror(errno));
+	else
+		devname_setup_nsmaps();
 
 	vprint(CHATTY_MID, "%spausing\n", fcn);
 	for (;;) {
@@ -1291,7 +1321,6 @@
 	if (devlink_cache == NULL)
 		devlink_cache = di_devlink_open(root_dir, 0);
 
-
 	/*
 	 * If modules were unloaded, reload them.  Also use module status
 	 * as an indication that we should check to see if other binding
@@ -1349,351 +1378,6 @@
 }
 
 /*
- * Contact the daemon to register the identified zone.  We do everything with
- * zone names, for simplicity.
- */
-static void
-call_zone_register(char *zone_name, int regop)
-{
-	int doorfd, ret, retries = 0;
-	door_arg_t arg;
-	struct zreg z;
-	char path[MAXPATHLEN];
-
-	assert(regop == ZONE_REG || regop == ZONE_UNREG);
-
-	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0) {
-		err_print(INVALID_ZONE, zone_name);
-		return;
-	}
-
-	z.zreg_error = 0;
-	z.zreg_op = regop;
-	(void) strlcpy(z.zreg_zonename, zone_name, ZONENAME_MAX);
-
-	(void) snprintf(path, sizeof (path), "/dev/%s", ZONE_REG_DOOR);
-	if ((doorfd = open(path, O_RDWR)) == -1) {
-		return;
-	}
-
-	bzero(&arg, sizeof (arg));
-	arg.data_ptr = (char *)&z;
-	arg.data_size = sizeof (z);
-	arg.rbuf = (char *)&z;
-	arg.rsize = sizeof (z);
-
-	/*
-	 * If the daemon is running, tell it about the zone.  If not, it's
-	 * ok.  When it next gets run by the system (because there is
-	 * device-related work to do), it will load the list of zones from
-	 * the kernel.
-	 */
-	while (((ret = door_call(doorfd, &arg)) == -1) && retries++ < 3) {
-		(void) sleep(retries);
-	}
-	(void) close(doorfd);
-
-	if (ret != 0) {
-		return;
-	}
-
-	switch (z.zreg_error) {
-	case ZONE_SUCCESS:
-		break;
-	case ZONE_ERR_NOZONE:
-		err_print(ZONE_REG_FAILED, zone_name, strerror(z.zreg_errno));
-		break;
-	case ZONE_ERR_DOOR:
-		err_print(ZONE_DOOR_MKFAIL, zone_name, strerror(z.zreg_errno));
-		break;
-	case ZONE_ERR_REPOSITORY:
-		err_print(ZONE_REP_FAILED, zone_name, strerror(z.zreg_errno));
-		break;
-	case ZONE_ERR_NOLIB:
-		err_print(ZONE_LIB_MISSING);
-		break;
-	default:
-		err_print(ZONE_REG_FAILED, zone_name, strerror(z.zreg_errno));
-		break;
-	}
-}
-
-/*
- * The following routines are the daemon-side code for managing the set of
- * currently registered zones.
- *
- * TODO: improve brain-dead list performance--- use libuutil avl tree or hash?
- */
-static void
-zlist_insert(struct zone_devinfo *newzone)
-{
-	struct zone_devinfo *z;
-	assert(MUTEX_HELD(&zone_mutex));
-
-	if (zone_head == NULL) {
-		zone_head = newzone;
-		return;
-	}
-	z = zlist_remove(newzone->zone_name);
-	if (z != NULL)
-		delete_zone(z);
-	newzone->zone_next = zone_head;
-	zone_head = newzone;
-}
-
-static void
-delete_zone(struct zone_devinfo *z) {
-	char door_file[PATH_MAX];
-
-	/*
-	 * Tidy up by withdrawing our door from the zone.
-	 */
-	(void) snprintf(door_file, sizeof (door_file), "%s/dev/%s",
-	    z->zone_path, DEVFSADM_SYNCH_DOOR);
-	(void) s_unlink(door_file);
-
-	zonecfg_fini_handle(z->zone_dochdl);
-	free(z->zone_path);
-	free(z->zone_name);
-	free(z);
-}
-
-static struct zone_devinfo *
-zlist_remove(char *zone_name)
-{
-	struct zone_devinfo *z, *unlinked = NULL, **prevnextp;
-	assert(MUTEX_HELD(&zone_mutex));
-
-	prevnextp = &zone_head;
-	for (z = zone_head; z != NULL; z = z->zone_next) {
-		if (strcmp(zone_name, z->zone_name) == 0) {
-			unlinked = z;
-			*prevnextp = z->zone_next;
-			return (unlinked);
-		}
-		prevnextp = &(z->zone_next);
-	}
-	return (NULL);
-}
-
-/*
- * Delete all zones.  Note that this should *only* be called in the exit
- * path of the daemon, as it does not take the zone_mutex-- this is because
- * we could wind up calling devfsadm_exit() with that zone_mutex_held.
- */
-static void
-zlist_deleteall_unlocked(void)
-{
-	struct zone_devinfo *tofree;
-
-	while (zone_head != NULL) {
-		tofree = zone_head;
-		zone_head = zone_head->zone_next;
-		delete_zone(tofree);
-	}
-	assert(zone_head == NULL);
-}
-
-static int
-zone_register(char *zone_name)
-{
-	char		door_file[MAXPATHLEN], zpath[MAXPATHLEN];
-	int		fd;
-	int 		need_unlink = 0, error = ZONE_SUCCESS, myerrno = 0;
-	zone_dochandle_t hdl = NULL;
-	void		*dlhdl = NULL;
-	struct zone_devinfo *newzone = NULL;
-
-	assert(MUTEX_HELD(&zone_mutex));
-
-	if ((dlhdl = dlopen(LIBZONECFG_PATH, RTLD_LAZY)) == NULL) {
-		error = ZONE_ERR_NOLIB;
-		goto bad;
-	}
-
-	if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) {
-		error = ZONE_ERR_NOZONE;
-		myerrno = errno;
-		goto bad;
-	}
-
-	if (snprintf(door_file, sizeof (door_file), "%s/dev/%s",
-	    zpath, DEVFSADM_SYNCH_DOOR) >= sizeof (door_file)) {
-		myerrno = ENAMETOOLONG;	/* synthesize a reasonable errno */
-		error = ZONE_ERR_DOOR;
-		goto bad;
-	}
-
-	(void) s_unlink(door_file);
-	if ((fd = open(door_file, O_RDWR | O_CREAT, ZONE_DOOR_PERMS)) == -1) {
-		myerrno = errno;
-		error = ZONE_ERR_DOOR;
-		goto bad;
-	}
-	need_unlink = 1;
-	(void) close(fd);
-
-	if (fattach(devlink_door_fd, door_file) == -1) {
-		error = ZONE_ERR_DOOR;
-		myerrno = errno;
-		goto bad;
-	}
-
-	if ((hdl = zonecfg_init_handle()) == NULL) {
-		error = ZONE_ERR_REPOSITORY;
-		myerrno = errno;
-		goto bad;
-	}
-
-	if ((zonecfg_get_snapshot_handle(zone_name, hdl)) != Z_OK) {
-		error = ZONE_ERR_REPOSITORY;
-		myerrno = errno;
-		goto bad;
-	}
-
-	newzone = s_malloc(sizeof (struct zone_devinfo));
-	newzone->zone_path = s_strdup(zpath);
-	newzone->zone_name = s_strdup(zone_name);
-	newzone->zone_next = NULL;
-	newzone->zone_dochdl = hdl;
-	zlist_insert(newzone);
-	(void) dlclose(dlhdl);
-
-	return (ZONE_SUCCESS);
-
-bad:
-	(void) devfsadm_errprint("%s[%ld]: failed to register zone %s: %s",
-	    prog, getpid(), zone_name, strerror(myerrno));
-
-	assert(newzone == NULL);
-	if (need_unlink)
-		(void) s_unlink(door_file);
-	if (hdl)
-		zonecfg_fini_handle(hdl);
-	if (dlhdl)
-		(void) dlclose(dlhdl);
-	errno = myerrno;
-	return (error);
-}
-
-static int
-zone_unregister(char *zone_name)
-{
-	struct zone_devinfo *z;
-
-	assert(MUTEX_HELD(&zone_mutex));
-
-	if ((z = zlist_remove(zone_name)) == NULL)
-		return (ZONE_ERR_NOZONE);
-
-	delete_zone(z);
-	return (ZONE_SUCCESS);
-}
-
-/*
- *  Called by the daemon when it receives a door call to the zone registration
- *  door.
- */
-/*ARGSUSED*/
-static void
-zone_reg_handler(void *cookie, char *ap, size_t asize, door_desc_t *dp,
-    uint_t ndesc)
-{
-	door_cred_t	dcred;
-	struct zreg 	*zregp, rzreg;
-
-	/*
-	 * We coarsely lock the whole registration process.
-	 */
-	(void) mutex_lock(&zone_mutex);
-
-	/*
-	 * Must be root to make this call
-	 * If caller is not root, don't touch its data.
-	 */
-	if (door_cred(&dcred) != 0 || dcred.dc_euid != 0) {
-		zregp = &rzreg;
-		zregp->zreg_error = ZONE_ERR_REPOSITORY;
-		zregp->zreg_errno = EPERM;
-		goto out;
-	}
-
-	assert(ap);
-	assert(asize == sizeof (*zregp));
-
-	zregp = (struct zreg *)(void *)ap;
-
-	/*
-	 * Kernel must know about this zone; one way of discovering this
-	 * is by looking up the zone id.
-	 */
-	if (getzoneidbyname(zregp->zreg_zonename) == -1) {
-		zregp->zreg_error = ZONE_ERR_REPOSITORY;
-		zregp->zreg_errno = errno;
-		goto out;
-	}
-
-	if (zregp->zreg_op == ZONE_REG) {
-		zregp->zreg_error = zone_register(zregp->zreg_zonename);
-		zregp->zreg_errno = errno;
-	} else {
-		zregp->zreg_error = zone_unregister(zregp->zreg_zonename);
-		zregp->zreg_errno = errno;
-	}
-
-out:
-	(void) mutex_unlock(&zone_mutex);
-	(void) door_return((char *)zregp, sizeof (*zregp), NULL, 0);
-}
-
-static int
-register_all_zones(void)
-{
-	zoneid_t *zids = NULL;
-	uint_t nzents, nzents_saved;
-	int i;
-
-	(void) mutex_lock(&zone_mutex);
-	if (zone_list(NULL, &nzents) != 0)
-		return (DEVFSADM_FAILURE);
-
-again:
-	assert(zids == NULL);
-	assert(MUTEX_HELD(&zone_mutex));
-	if (nzents == 0) {
-		(void) mutex_unlock(&zone_mutex);
-		return (DEVFSADM_SUCCESS);
-	}
-	zids = s_zalloc(nzents * sizeof (zoneid_t));
-	nzents_saved = nzents;
-	if (zone_list(zids, &nzents) != 0) {
-		(void) mutex_unlock(&zone_mutex);
-		free(zids);
-		return (DEVFSADM_FAILURE);
-	}
-	if (nzents != nzents_saved) {
-		/* list changed, try again */
-		free(zids);
-		zids = NULL;
-		goto again;
-	}
-
-	assert(zids != NULL);
-	for (i = 0; i < nzents; i++) {
-		char name[ZONENAME_MAX];
-
-		if (zids[i] == GLOBAL_ZONEID)
-			continue;
-		if (getzonenamebyid(zids[i], name, sizeof (name)) >= 0)
-			(void) zone_register(name);
-	}
-
-	(void) mutex_unlock(&zone_mutex);
-	free(zids);
-	return (DEVFSADM_SUCCESS);
-}
-
-/*
  * Check that if -r is set, it is not any part of a zone--- that is, that
  * the zonepath is not a substring of the root path.
  */
@@ -1705,7 +1389,7 @@
 	char		root[MAXPATHLEN]; /* resolved devfsadm root path */
 	char		zroot[MAXPATHLEN]; /* zone root path */
 	char		rzroot[MAXPATHLEN]; /* resolved zone root path */
-	char 		tmp[MAXPATHLEN];
+	char		tmp[MAXPATHLEN];
 	FILE		*cookie;
 	int		err = DEVFSADM_SUCCESS;
 
@@ -1722,7 +1406,7 @@
 	bzero(root, sizeof (root));
 	if (resolvepath(checkpath, root, sizeof (root) - 1) == -1) {
 		/*
-		 * In this case the user has done 'devfsadm -r' on some path
+		 * In this case the user has done "devfsadm -r" on some path
 		 * which does not yet exist, or we got some other misc. error.
 		 * We punt and don't resolve the path in this case.
 		 */
@@ -1805,6 +1489,10 @@
 	vprint(EVENT_MID, "event_handler: %s id:0X%llx\n",
 	    subclass, sysevent_get_seq(ev));
 
+	if (strcmp(subclass, ESC_DEVFS_START) == 0) {
+		return;
+	}
+
 	/* Check if event is an instance modification */
 	if (strcmp(subclass, ESC_DEVFS_INSTANCE_MOD) == 0) {
 		devfs_instance_mod();
@@ -1891,7 +1579,6 @@
 		    DI_NODE_NIL);
 		unlock_dev(CACHE_STATE);
 		startup_cache_sync_thread();
-
 	} else
 		err_print(UNKNOWN_EVENT, subclass);
 
@@ -2686,129 +2373,13 @@
 	return (DEVFSADM_SUCCESS);
 }
 
-static int
-i_mknod(char *path, int stype, int mode, dev_t dev, uid_t uid, gid_t gid)
-{
-	struct stat sbuf;
-
-	assert((stype & (S_IFCHR|S_IFBLK)) != 0);
-	assert((mode & S_IFMT) == 0);
-
-	if (stat(path, &sbuf) == 0) {
-		/*
-		 * the node already exists, check if it's device
-		 * information is correct
-		 */
-		if (((sbuf.st_mode & S_IFMT) == stype) &&
-		    (sbuf.st_rdev == dev)) {
-			/* the device node is correct, continue */
-			return (DEVFSADM_SUCCESS);
-		}
-		/*
-		 * the device node already exists but has the wrong
-		 * mode/dev_t value.  we need to delete the current
-		 * node and re-create it with the correct mode/dev_t
-		 * value, but we also want to preserve the current
-		 * owner and permission information.
-		 */
-		uid = sbuf.st_uid;
-		gid = sbuf.st_gid;
-		mode = sbuf.st_mode & ~S_IFMT;
-		s_unlink(path);
-	}
-
-top:
-	if (mknod(path, stype | mode, dev) == -1) {
-		if (errno == ENOENT) {
-			/* dirpath to node doesn't exist, create it */
-			char *hide = strrchr(path, '/');
-			*hide = '\0';
-			s_mkdirp(path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
-			*hide = '/';
-			goto top;
-		}
-		err_print(MKNOD_FAILED, path, strerror(errno));
-		return (DEVFSADM_FAILURE);
-	} else {
-		/*
-		 * If we successfully made the node, then set its owner
-		 * and group.  Existing nodes will be unaffected.
-		 */
-		(void) chown(path, uid, gid);
-	}
-	return (DEVFSADM_SUCCESS);
-}
-
-/*ARGSUSED*/
-int
-devfsadm_mklink_zone(struct zone_devinfo *z, char *link, di_node_t node,
-    di_minor_t minor, int flags)
-{
-	char path[PATH_MAX];
-	char phy_path[PATH_MAX];
-	char *dev_pathp;
-	char *acontents, *aminor = NULL;
-	mode_t mode;
-	uid_t uid;
-	gid_t gid;
-	dev_t dev;
-	struct zone_devtab out_match;
-
-	if (zonecfg_match_dev(z->zone_dochdl, link, &out_match) != Z_OK) {
-		return (DEVFSADM_FAILURE);
-	}
-
-	vprint(ZONE_MID, "zone device match: <device match=\"%s\"> "
-	    "matches /dev/%s\n", out_match.zone_dev_match, link);
-
-	/*
-	 * In daemon mode, zone_path will be non-empty.  In non-daemon mode
-	 * it will be empty since we've already stuck the zone into dev_dir,
-	 * etc.
-	 */
-	(void) snprintf(path, sizeof (path), "%s/dev/%s", z->zone_path, link);
-	dev = di_minor_devt(minor);
-
-	/*
-	 * If this is an alias node (i.e. a clone node), we have to figure
-	 * out the minor name.
-	 */
-	if (di_minor_type(minor) == DDM_ALIAS) {
-		/* use /pseudo/clone@0:<driver> as the phys path */
-		(void) snprintf(phy_path, sizeof (phy_path),
-		    "/pseudo/clone@0:%s",
-		    di_driver_name(di_minor_devinfo(minor)));
-		aminor = di_minor_name(minor);
-		acontents = phy_path;
-	} else {
-		if ((dev_pathp = di_devfs_path(node)) == NULL) {
-			err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
-			devfsadm_exit(1);
-		}
-		(void) snprintf(phy_path, sizeof (phy_path), "%s:%s",
-		    dev_pathp, di_minor_name(minor));
-		di_devfs_path_free(dev_pathp);
-		acontents = phy_path;
-	}
-
-
-	getattr(acontents, aminor, di_minor_spectype(minor), dev,
-	    &mode, &uid, &gid);
-	vprint(ZONE_MID, "zone getattr(%s, %s, %d, %lu, 0%lo, %lu, %lu)\n",
-	    acontents, aminor ? aminor : "<NULL>", di_minor_spectype(minor),
-	    dev, mode, uid, gid);
-
-	/* Create the node */
-	return (i_mknod(path, di_minor_spectype(minor), mode, dev, uid, gid));
-}
-
 /*
  * Creates a symlink 'link' to the physical path of node:minor.
  * Construct link contents, then call create_link_common().
  */
 /*ARGSUSED*/
 int
-devfsadm_mklink_default(char *link, di_node_t node, di_minor_t minor, int flags)
+devfsadm_mklink(char *link, di_node_t node, di_minor_t minor, int flags)
 {
 	char rcontents[PATH_MAX];
 	char devlink[PATH_MAX];
@@ -2919,38 +2490,6 @@
 	return (rv);
 }
 
-int
-devfsadm_mklink(char *link, di_node_t node, di_minor_t minor, int flags)
-{
-	struct zone_devinfo *z;
-	int error;
-
-	/*
-	 * If we're in zone mode (also implies !daemon_mode), then the
-	 * zone devinfo list has only one element, the zone we're configuring,
-	 * and we can just use zone_head.
-	 */
-	if (zone_cmd_mode)
-		return (devfsadm_mklink_zone(zone_head, link, node,
-		    minor, flags));
-	else if (!daemon_mode)
-		return (devfsadm_mklink_default(link, node, minor, flags));
-
-	/*
-	 * We're in daemon mode, so we need to make the link in the global
-	 * zone; then, walk the list of zones, creating the corresponding
-	 * mknod'd nodes in each.
-	 */
-	error = devfsadm_mklink_default(link, node, minor, flags);
-
-	(void) mutex_lock(&zone_mutex);
-	for (z = zone_head; z != NULL; z = z->zone_next) {
-		(void) devfsadm_mklink_zone(z, link, node, minor, flags);
-	}
-	(void) mutex_unlock(&zone_mutex);
-	return (error);
-}
-
 /*
  * Creates a symlink link to primary_link.  Calculates relative
  * directory offsets, then calls link_common().
@@ -3575,6 +3114,8 @@
 {
 	char *ptr, path[PATH_MAX + 1];
 	char *fcn = "rm_parent_dir_if_empty: ";
+	char *pathlist;
+	int len;
 
 	vprint(REMOVE_MID, "%schecking %s if empty\n", fcn, pathname);
 
@@ -3592,18 +3133,30 @@
 
 		*ptr = '\0';
 
-		if (s_rmdir(path) == 0) {
-			vprint(REMOVE_MID, "%sremoving empty dir %s\n",
-			    fcn, path);
-			continue;
-		}
-		if (errno == EEXIST) {
+		if ((pathlist = dev_readdir(path)) == NULL) {
+			err_print(OPENDIR_FAILED, path, strerror(errno));
+			return;
+		}
+
+		/*
+		 * An empty pathlist implies an empty directory
+		 */
+		len = strlen(pathlist);
+		free(pathlist);
+		if (len == 0) {
+			if (s_rmdir(path) == 0) {
+				vprint(REMOVE_MID,
+				    "%sremoving empty dir %s\n", fcn, path);
+			} else if (errno == EEXIST) {
+				vprint(REMOVE_MID,
+				    "%sfailed to remove dir: %s\n", fcn, path);
+				return;
+			}
+		} else {
+			/* some other file is here, so return */
 			vprint(REMOVE_MID, "%sdir not empty: %s\n", fcn, path);
 			return;
 		}
-		vprint(REMOVE_MID, "%s can't remove %s: %s\n", fcn, path,
-		    strerror(errno));
-		return;
 	}
 }
 
@@ -4247,9 +3800,8 @@
 		return (0);
 	}
 
-	s_mkdirp(dev_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
 	(void) snprintf(dev_lockfile, sizeof (dev_lockfile),
-	    "%s/%s", dev_dir, DEV_LOCK_FILE);
+	    "%s/%s", etc_dev_dir, DEV_LOCK_FILE);
 
 	vprint(LOCK_MID, "enter_dev_lock: lock file %s\n", dev_lockfile);
 
@@ -4362,9 +3914,8 @@
 {
 	struct flock lock;
 
-	s_mkdirp(dev_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
 	(void) snprintf(daemon_lockfile, sizeof (daemon_lockfile),
-	    "%s/%s", dev_dir, DAEMON_LOCK_FILE);
+	    "%s/%s", etc_dev_dir, DAEMON_LOCK_FILE);
 
 	vprint(LOCK_MID, "enter_daemon_lock: lock file %s\n", daemon_lockfile);
 
@@ -4650,16 +4201,15 @@
 	char *slash;
 	char new_path[PATH_MAX + 1];
 	char *anchored_path_re;
-	struct dirent *entp;
-	DIR *dp;
 	size_t len;
+	char *pathlist;
+	char *listp;
 
 	vprint(RECURSEDEV_MID, "recurse_dev_re: curr = %s path=%s\n",
 		current_dir, path_re);
 
-	if ((dp = opendir(current_dir)) == NULL) {
+	if ((pathlist = dev_readdir(current_dir)) == NULL)
 		return;
-	}
 
 	len = strlen(path_re);
 	if ((slash = strchr(path_re, '/')) != NULL) {
@@ -4676,18 +4226,13 @@
 
 	free(anchored_path_re);
 
-	while ((entp = readdir(dp)) != NULL) {
-
-		if (strcmp(entp->d_name, ".") == 0 ||
-		    strcmp(entp->d_name, "..") == 0) {
-			continue;
-		}
-
-		if (regexec(&re1, entp->d_name, 0, NULL, 0) == 0) {
+	for (listp = pathlist; (len = strlen(listp)) > 0; listp += len+1) {
+
+		if (regexec(&re1, listp, 0, NULL, 0) == 0) {
 			/* match */
 			(void) strcpy(new_path, current_dir);
 			(void) strcat(new_path, "/");
-			(void) strcat(new_path, entp->d_name);
+			(void) strcat(new_path, listp);
 
 			vprint(RECURSEDEV_MID, "recurse_dev_re: match, new "
 				"path = %s\n", new_path);
@@ -4706,7 +4251,7 @@
 	regfree(&re1);
 
 out:
-	s_closedir(dp);
+	free(pathlist);
 }
 
 /*
@@ -4767,7 +4312,7 @@
 	(void) strcat(devlink, "/");
 	(void) strcat(devlink, link);
 
-	if (lstat(devlink, &sb) != 0) {
+	if (!device_exists(devlink) || lstat(devlink, &sb) != 0) {
 		return (DEVFSADM_FALSE);
 	}
 
@@ -5404,14 +4949,11 @@
 
 	/*
 	 * For each RE, search disk and cache any matches on the
-	 * numeral list.  We are careful to use global_dev_dir here since
-	 * for zones, we want to use the global zone's enumeration as the
-	 * source for enumeration within the zone.  Otherwise, for example,
-	 * controller numbering would be wrong within the zone.
+	 * numeral list.
 	 */
 	for (i = 0; i < nrules; i++) {
 		path_left = s_strdup(setp->re[i]);
-		enumerate_recurse(global_dev_dir, path_left, setp, rules, i);
+		enumerate_recurse(dev_dir, path_left, setp, rules, i);
 		free(path_left);
 	}
 
@@ -5434,9 +4976,10 @@
 int
 get_stat_info(char *namebuf, struct stat *sb)
 {
-	struct dirent *entp;
-	DIR *dp;
 	char *cp;
+	char *pathlist;
+	char *listp;
+	int len;
 
 	if (lstat(namebuf, sb) < 0) {
 		(void) err_print(LSTAT_FAILED, namebuf, strerror(errno));
@@ -5453,7 +4996,7 @@
 	 */
 	if ((sb->st_mode & S_IFMT) == S_IFDIR) {
 
-		if ((dp = opendir(namebuf)) == NULL) {
+		if ((pathlist = dev_readdir(namebuf)) == NULL) {
 			return (DEVFSADM_FAILURE);
 		}
 
@@ -5461,22 +5004,21 @@
 		 *  Search each dir entry looking for a symlink.  Return
 		 *  the first symlink found in namebuf.  Recurse dirs.
 		 */
-		while ((entp = readdir(dp)) != NULL) {
-			if (strcmp(entp->d_name, ".") == 0 ||
-			    strcmp(entp->d_name, "..") == 0) {
-				continue;
+		for (listp = pathlist;
+		    (len = strlen(listp)) > 0; listp += len+1) {
+			cp = namebuf + strlen(namebuf);
+			if ((strlcat(namebuf, "/", PATH_MAX) >= PATH_MAX) ||
+			    (strlcat(namebuf, listp, PATH_MAX) >= PATH_MAX)) {
+				*cp = '\0';
+				return (DEVFSADM_FAILURE);
 			}
-
-			cp = namebuf + strlen(namebuf);
-			(void) strcat(namebuf, "/");
-			(void) strcat(namebuf, entp->d_name);
 			if (get_stat_info(namebuf, sb) == DEVFSADM_SUCCESS) {
-				s_closedir(dp);
+				free(pathlist);
 				return (DEVFSADM_SUCCESS);
 			}
 			*cp = '\0';
 		}
-		s_closedir(dp);
+		free(pathlist);
 	}
 
 	/* no symlink found, so return error */
@@ -5601,10 +5143,11 @@
 	char *slash;
 	char *new_path;
 	char *numeral_id;
-	struct dirent *entp;
-	DIR *dp;
-
-	if ((dp = opendir(current_dir)) == NULL) {
+	char *pathlist;
+	char *listp;
+	int len;
+
+	if ((pathlist = dev_readdir(current_dir)) == NULL) {
 		return;
 	}
 
@@ -5617,29 +5160,24 @@
 		*slash = '\0';
 	}
 
-	while ((entp = readdir(dp)) != NULL) {
-
-		if (strcmp(entp->d_name, ".") == 0 ||
-		    strcmp(entp->d_name, "..") == 0) {
-			continue;
-		}
+	for (listp = pathlist; (len = strlen(listp)) > 0; listp += len+1) {
 
 		/*
-		 *  Returns true if path_left matches entp->d_name
+		 *  Returns true if path_left matches the list entry.
 		 *  If it is the last path component, pass subexp
 		 *  so that it will return the corresponding ID in
 		 *  numeral_id.
 		 */
 		numeral_id = NULL;
-		if (match_path_component(path_left, entp->d_name, &numeral_id,
+		if (match_path_component(path_left, listp, &numeral_id,
 				    slash ? 0 : rules[index].subexp)) {
 
 			new_path = s_malloc(strlen(current_dir) +
-					    strlen(entp->d_name) + 2);
+			    strlen(listp) + 2);
 
 			(void) strcpy(new_path, current_dir);
 			(void) strcat(new_path, "/");
-			(void) strcat(new_path, entp->d_name);
+			(void) strcat(new_path, listp);
 
 			if (slash != NULL) {
 				enumerate_recurse(new_path, slash + 1,
@@ -5658,7 +5196,7 @@
 	if (slash != NULL) {
 		*slash = '/';
 	}
-	s_closedir(dp);
+	free(pathlist);
 }
 
 
@@ -7007,8 +6545,6 @@
 		(void) dlclose(librcm_hdl);
 	}
 
-	zlist_deleteall_unlocked();		/* dispose of all zones */
-
 	exit_dev_lock();
 	exit_daemon_lock();
 
@@ -7020,14 +6556,10 @@
 }
 
 /*
- * set root_dir, devices_dir, dev_dir using optarg.  zone_mode determines
- * whether we're operating on behalf of a zone; in this case, we need to
- * reference some things from the global zone.  Note that zone mode and
- * -R don't get along, but that should be OK since zone mode is not
- * a public interface.
+ * set root_dir, devices_dir, dev_dir using optarg.
  */
 static void
-set_root_devices_dev_dir(char *dir, int zone_mode)
+set_root_devices_dev_dir(char *dir)
 {
 	size_t len;
 
@@ -7038,14 +6570,6 @@
 	len = strlen(root_dir) + strlen(DEV) + 1;
 	dev_dir = s_malloc(len);
 	(void) snprintf(dev_dir, len, "%s%s", root_dir, DEV);
-	if (zone_mode) {
-		len = strlen(DEV) + 1;
-		global_dev_dir = s_malloc(len);
-		(void) snprintf(global_dev_dir, len, "%s", DEV);
-	} else {
-		global_dev_dir = s_malloc(len);
-		(void) snprintf(global_dev_dir, len, "%s%s", root_dir, DEV);
-	}
 }
 
 /*
@@ -7625,6 +7149,18 @@
 /*
  * convenience functions
  */
+static int
+s_stat(const char *path, struct stat *sbufp)
+{
+	int rv;
+retry:
+	if ((rv = stat(path, sbufp)) == -1) {
+		if (errno == EINTR)
+			goto retry;
+	}
+	return (rv);
+}
+
 static void *
 s_malloc(const size_t size)
 {
@@ -8478,3 +8014,272 @@
 		nvlist_free(nvl);
 	}
 }
+
+static int
+devname_kcall(int subcmd, void *args)
+{
+	int error = 0;
+	char *nvlbuf = NULL;
+	size_t nvlsize;
+
+	switch (subcmd) {
+	case MODDEVNAME_NSMAPS:
+		error = nvlist_pack((nvlist_t *)args, &nvlbuf, &nvlsize, 0, 0);
+		if (error) {
+			err_print("packing MODDEVNAME_NSMAPS failed\n");
+			break;
+		}
+		error = modctl(MODDEVNAME, subcmd, nvlbuf, nvlsize);
+		if (error) {
+			vprint(INFO_MID, "modctl(MODDEVNAME, "
+			    "MODDEVNAME_NSMAPS) failed - %s\n",
+			    strerror(errno));
+		}
+		free(nvlbuf);
+		nvlist_free(args);
+		break;
+	case MODDEVNAME_LOOKUPDOOR:
+		error = modctl(MODDEVNAME, subcmd, (uintptr_t)args);
+		if (error) {
+			vprint(INFO_MID, "modctl(MODDEVNAME, "
+			    "MODDEVNAME_LOOKUPDOOR) failed - %s\n",
+			    strerror(errno));
+		}
+		break;
+	default:
+		error = EINVAL;
+		break;
+	}
+	return (error);
+}
+
+static void
+devname_setup_nsmaps(void)
+{
+	int error = 0;
+
+	if (devname_first_call) {
+		devname_first_call = 0;
+	}
+
+	error = di_devname_get_mapinfo(DEVNAME_MASTER_MAP, &devname_maps);
+
+	if (error) {
+		vprint(INFO_MID, "devname_setup_nsmaps: error %d\n", errno);
+	} else {
+#ifdef DEBUG
+		di_devname_print_mapinfo(devname_maps);
+#endif
+		/* pass down the existing map names to kernel */
+		(void) devname_kcall(MODDEVNAME_NSMAPS, (void *)devname_maps);
+	}
+}
+
+static void
+devname_ns_services(uint8_t cmd, char *key, char *map)
+{
+	nvlist_t *nvl = NULL;
+	int32_t	error = 0;
+	sdev_door_res_t res;
+
+	vprint(DEVNAME_MID, "devname_ns_services: cmd %d key %s map %s\n",
+	    cmd, key, map);
+
+	switch (cmd) {
+	case DEVFSADMD_NS_LOOKUP:
+		vprint(DEVNAME_MID, "calling di_devname_get_mapent\n");
+		error = di_devname_get_mapent(key, map, &nvl);
+		if (nvl == NULL) {
+			error = DEVFSADM_NS_FAILED;
+			goto done;
+		}
+
+		if (error) {
+			nvlist_free(nvl);
+			goto done;
+		}
+
+		if (devname_debug_msg)
+			di_devname_print_mapinfo(nvl);
+
+		vprint(DEVNAME_MID, "calling di_devname_action_on_key for %d\n",
+		    cmd);
+		error = di_devname_action_on_key(nvl, cmd, key, (void *)&res);
+		nvlist_free(nvl);
+		break;
+	case DEVFSADMD_NS_READDIR:
+		vprint(DEVNAME_MID, "calling di_devname_get_mapinfo for cmd %d"
+		    "\n", cmd);
+		error = di_devname_get_mapinfo(map, &nvl);
+		if (nvl == NULL) {
+			error = DEVFSADM_NS_FAILED;
+			goto done;
+		}
+
+		if (error) {
+			nvlist_free(nvl);
+			goto done;
+		}
+
+		if (devname_debug_msg)
+			di_devname_print_mapinfo(nvl);
+
+		vprint(DEVNAME_MID, "calling di_devname_action_on_key\n");
+		error = di_devname_action_on_key(nvl, cmd, key, (void *)&res);
+		nvlist_free(nvl);
+		break;
+	default:
+		error = DEVFSADM_RUN_NOTSUP;
+		break;
+	}
+
+done:
+	vprint(DEVNAME_MID, "error %d\n", error);
+	res.devfsadm_error = error;
+	(void) door_return((char *)&res, sizeof (struct sdev_door_res),
+	    NULL, 0);
+}
+
+/* ARGSUSED */
+static void
+devname_lookup_handler(void *cookie, char *argp, size_t arg_size,
+    door_desc_t *dp, uint_t n_desc)
+{
+	int32_t error = 0;
+	door_cred_t dcred;
+	struct dca_impl	dci;
+	uint8_t	cmd;
+	char *ns_map, *ns_name;
+	sdev_door_res_t res;
+	sdev_door_arg_t *args;
+
+	if (argp == NULL || arg_size == 0) {
+		vprint(DEVNAME_MID, "devname_lookup_handler: argp wrong\n");
+		error = DEVFSADM_RUN_INVALID;
+		goto done;
+	}
+	vprint(DEVNAME_MID, "devname_lookup_handler\n");
+
+	if (door_cred(&dcred) != 0 || dcred.dc_euid != 0) {
+		vprint(DEVNAME_MID, "devname_lookup_handler: cred wrong\n");
+		error = DEVFSADM_RUN_EPERM;
+		goto done;
+	}
+
+	args = (sdev_door_arg_t *)argp;
+	cmd = args->devfsadm_cmd;
+
+	vprint(DEVNAME_MID, "devname_lookup_handler: cmd %d\n", cmd);
+	switch (cmd) {
+	case DEVFSADMD_NS_LOOKUP:
+	case DEVFSADMD_NS_READDIR:
+		ns_name = s_strdup(args->ns_hdl.ns_name);
+		ns_map = s_strdup(args->ns_hdl.ns_map);
+
+		vprint(DEVNAME_MID, " ns_name %s ns_map %s\n", ns_name, ns_map);
+		if (ns_name == NULL || ns_map == NULL) {
+			error = DEVFSADM_RUN_INVALID;
+			goto done;
+		}
+
+		devname_ns_services(cmd, ns_name, ns_map);
+		return;
+	case DEVFSADMD_RUN_ALL:
+		/*
+		 * run "devfsadm"
+		 */
+		dci.dci_root = "/";
+		dci.dci_minor = NULL;
+		dci.dci_driver = NULL;
+		dci.dci_error = 0;
+		dci.dci_flags = 0;
+		dci.dci_arg = NULL;
+
+		lock_dev();
+		update_drvconf((major_t)-1);
+		dci.dci_flags |= DCA_FLUSH_PATHINST;
+
+		pre_and_post_cleanup(RM_PRE);
+		devi_tree_walk(&dci, DINFOFORCE|DI_CACHE_SNAPSHOT_FLAGS, NULL);
+		error = (int32_t)dci.dci_error;
+		if (!error) {
+			pre_and_post_cleanup(RM_POST);
+			update_database = TRUE;
+			unlock_dev(SYNC_STATE);
+			update_database = FALSE;
+		} else {
+			if (DEVFSADM_DEBUG_ON) {
+				vprint(INFO_MID, "devname_lookup_handler: "
+				    "DEVFSADMD_RUN_ALL failed\n");
+			}
+
+			unlock_dev(SYNC_STATE);
+		}
+		break;
+	default:
+		/* log an error here? */
+		error = DEVFSADM_RUN_NOTSUP;
+		break;
+	}
+
+done:
+	vprint(DEVNAME_MID, "devname_lookup_handler: error %d\n", error);
+	res.devfsadm_error = error;
+	(void) door_return((char *)&res, sizeof (struct sdev_door_res),
+	    NULL, 0);
+}
+
+/*
+ * Use of the dev filesystem's private readdir does not trigger
+ * the implicit device reconfiguration.
+ *
+ * Note: only useable with paths mounted on an instance of the
+ * dev filesystem.
+ *
+ * Does not return the . and .. entries.
+ * Empty directories are returned as an zero-length list.
+ * ENOENT is returned as a NULL list pointer.
+ */
+static char *
+dev_readdir(char *path)
+{
+	int	rv;
+	int64_t	bufsiz;
+	char	*pathlist;
+	char	*p;
+	int	len;
+
+	assert((strcmp(path, "/dev") == 0) ||
+		(strncmp(path, "/dev/", 4) == 0));
+
+	rv = modctl(MODDEVREADDIR, path, strlen(path), NULL, &bufsiz);
+	if (rv != 0) {
+		vprint(READDIR_MID, "%s: %s\n", path, strerror(errno));
+		return (NULL);
+	}
+
+	for (;;) {
+		assert(bufsiz != 0);
+		pathlist = s_malloc(bufsiz);
+
+		rv = modctl(MODDEVREADDIR, path, strlen(path),
+		    pathlist, &bufsiz);
+		if (rv == 0) {
+			vprint(READDIR_MID, "%s\n", path);
+			vprint(READDIR_ALL_MID, "%s:\n", path);
+			for (p = pathlist; (len = strlen(p)) > 0; p += len+1) {
+				vprint(READDIR_ALL_MID, "    %s\n", p);
+			}
+			return (pathlist);
+		}
+		free(pathlist);
+		switch (errno) {
+		case EAGAIN:
+			break;
+		case ENOENT:
+		default:
+			vprint(READDIR_MID, "%s: %s\n", path, strerror(errno));
+			return (NULL);
+		}
+	}
+}
--- a/usr/src/cmd/devfsadm/devfsadm.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/devfsadm/devfsadm.h	Fri Aug 25 17:24:25 2006 -0700
@@ -83,6 +83,10 @@
 
 /* devfsadm event service door */
 #define	DEVFSADM_SERVICE_DOOR	"/etc/sysevent/devfsadm_event_channel"
+#define	DEVNAME_LOOKUP_DOOR	".devname_lookup_door"
+
+/* /dev device name binding rule locations */
+#define	DEVNAME_MASTER_MAP	"/etc/dev/devname_master"
 
 /* flags for devfsadm_mklink */
 #define	DEV_SYNC 0x02	/* synchronous mklink */
@@ -122,7 +126,6 @@
 	devfsadm_remove_t *tblp;
 } _devfsadm_remove_reg_t;
 
-
 /*
  * "flags" in the devfs_enumerate structure can take the following values.
  * These values specify the substring of devfs path to be used for
--- a/usr/src/cmd/devfsadm/devfsadm_impl.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/devfsadm/devfsadm_impl.h	Fri Aug 25 17:24:25 2006 -0700
@@ -71,10 +71,13 @@
 #include <message.h>
 #include <sys/cladm.h>
 #include <librcm.h>
-#include <sys/sysevent/eventdefs.h>
 #include <sys/sysevent/dev.h>
 #include <libzonecfg.h>
 #include <device_info.h>
+#include <sys/fs/sdev_node.h>
+#include <sys/syscall.h>
+#include <rpcsvc/ypclnt.h>
+#include <sys/sysevent/eventdefs.h>
 
 #undef	DEBUG
 #ifndef DEBUG
@@ -90,6 +93,8 @@
 #define	DAEMON_LOCK_FILE ".devfsadm_daemon.lock"
 
 #define	DEV "/dev"
+#define	ETC "/etc"
+#define	ETCDEV "/etc/dev"
 #define	DEV_LEN 4
 #define	DEVICES "/devices"
 #define	DEVICES_LEN 8
@@ -107,32 +112,8 @@
 #define	MINOR_FINI_TIMEOUT_DEFAULT 2
 #define	FORCE_CALL_MINOR_FINI	10
 
-
 #define	SYNCH_DOOR_PERMS	(S_IRUSR | S_IWUSR)
 
-#define	ZONE_DOOR_PERMS		(S_IRUSR | S_IWUSR)
-#define	ZONE_REG_DOOR	".zone_reg_door"
-
-enum zreg_op {
-	ZONE_REG = 1,
-	ZONE_UNREG = 2
-};
-
-enum zreg_err {
-	ZONE_SUCCESS = 0,
-	ZONE_ERR_NOZONE = 1,
-	ZONE_ERR_DOOR = 2,
-	ZONE_ERR_REPOSITORY = 3,
-	ZONE_ERR_NOLIB = 4
-};
-
-struct zreg {
-	char zreg_zonename[ZONENAME_MAX];
-	enum zreg_op zreg_op;
-	enum zreg_err zreg_error;
-	int zreg_errno;
-};
-
 #define	DRVCONFIG "drvconfig"
 #define	DEVFSADM "devfsadm"
 #define	DEVFSADMD "devfsadmd"
@@ -223,7 +204,9 @@
 #define	LINKCACHE_MID		"devfsadm:linkcache"
 #define	ADDREMCACHE_MID		"devfsadm:addremcache"
 #define	MALLOC_MID		"devfsadm:malloc"
-#define	ZONE_MID		"devfsadm:zone"
+#define	READDIR_MID		"devfsadm:readdir"
+#define	READDIR_ALL_MID		"devfsadm:readdir_all"
+#define	DEVNAME_MID		"devfsadm:devname"
 #define	ALL_MID			"all"
 
 #define	DEVFSADM_DEBUG_ON	(verbose == NULL) ? FALSE : TRUE
@@ -376,13 +359,6 @@
 	int dci_flags;
 };
 
-struct zone_devinfo {
-	struct zone_devinfo *zone_next;
-	char *zone_path;
-	char *zone_name;
-	zone_dochandle_t zone_dochdl;
-};
-
 /* RCM related */
 struct rcm_eventq {
 	nvlist_t *nvl;
@@ -393,7 +369,7 @@
 	int index, char **buf, devfsadm_enumerate_t rules[],
 	int nrules, char *start);
 static void startup_cache_sync_thread(void);
-static void set_root_devices_dev_dir(char *dir, int zone_mode);
+static void set_root_devices_dev_dir(char *dir);
 static void pre_and_post_cleanup(int flags);
 static void hot_cleanup(char *, char *, char *, char *, int);
 static void devfsadm_exit(int status);
@@ -499,14 +475,6 @@
     devfsadm_enumerate_t rules[], int index, numeral_t **matchnpp);
 static void sync_handler(void *cookie, char *ap, size_t asize,
     door_desc_t *dp, uint_t ndesc);
-static void zlist_insert(struct zone_devinfo *newzone);
-static void delete_zone(struct zone_devinfo *z);
-static struct zone_devinfo *zlist_remove(char *zone_name);
-static void zlist_deleteall_unlocked(void);
-static void call_zone_register(char *zone_name, int regop);
-static int register_all_zones(void);
-static void zone_reg_handler(void *cookie, char *ap, size_t asize,
-    door_desc_t *dp, uint_t ndesc);
 static int zone_pathcheck(char *checkpath);
 static void process_deferred_links(struct dca_impl *dcip, int flag);
 static void event_handler(sysevent_t *ev);
@@ -530,18 +498,29 @@
     di_node_t, char *, int);
 static void log_event(char *, char *, nvlist_t *);
 static void build_and_log_event(char *, char *, char *, di_node_t);
+static char *dev_readdir(char *);
 
 static void read_logindevperm_file(void);
 static void set_logindev_perms(char *devlink);
 
 static void reset_node_permissions(di_node_t, di_minor_t);
 
+/*
+ * devname related
+ */
+static void devname_lookup_handler(void *, char *, size_t,
+    door_desc_t *, uint_t);		/* /dev name lookup server */
+static int devname_kcall(int, void *);	/* syscall into the devname fs */
 
 /* convenient short hands */
 #define	vprint		devfsadm_print
 #define	err_print	devfsadm_errprint
+#ifndef TRUE
 #define	TRUE	1
+#endif
+#ifndef FALSE
 #define	FALSE	0
+#endif
 
 #ifdef	__cplusplus
 }
--- a/usr/src/cmd/devfsadm/message.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/devfsadm/message.h	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -86,9 +85,6 @@
 
 #define	CANT_CREATE_DOOR gettext("can not create event door %s: %s\n")
 
-#define	CANT_CREATE_ZONE_DOOR \
-    gettext("can not create zone registration door %s: %s\n")
-
 #define	FAILED_FOR_MODULE gettext("%s failed for module %s\n")
 
 #define	REMOVING_LINK gettext("removing link %s -> %s invalid contents\n")
@@ -267,32 +263,16 @@
 
 #define	LOG_EVENT_FAILED gettext("failed to log event: %s\n")
 
-#define	INVALID_ZONE gettext("invalid zone: '%s'\n")
-
-#define	ZONE_ROOTPATH_FAILED \
-    gettext("could not determine root path for zone %s: %s\n")
-
-#define	ZONE_LIST_FAILED \
-    gettext("could not determine system zone configuration: %s\n")
-
-#define	ZONE_DOOR_MKFAIL \
-    gettext("failed to create door server for zone %s: %s\n")
-
-#define	ZONE_REG_FAILED \
-    gettext("failed registration operation for zone %s: %s\n")
-
-#define	ZONE_REP_FAILED \
-    gettext("repository or name service failure for zone %s: %s\n")
-
-#define	ZONE_LIB_MISSING \
-    gettext("unable to load libzonecfg, make sure zone packages " \
-	"are installed\n")
-
 #define	ZONE_PATHCHECK \
     gettext("cannot manage root path '%s': path is part of zone '%s'\n")
 
+#define	DEVNAME_CONTACT_FAILED \
+    gettext("cannot talk to devname fs %s: %s\n")
+
 #define	NVLIST_ERROR gettext("nvlist interface failed: %s\n")
 
+#define	NOT_DIR gettext("file is not a directory: %s\n")
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/cmd/devfsadm/misc_link.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/devfsadm/misc_link.c	Fri Aug 25 17:24:25 2006 -0700
@@ -60,7 +60,7 @@
 static int kmdrv_create(di_minor_t minor, di_node_t node);
 
 static devfsadm_create_t misc_cbt[] = {
-	{ "pseudo", "ddi_pseudo", "(^pts$)|(^sad$)",
+	{ "pseudo", "ddi_pseudo", "(^sad$)",
 	    TYPE_EXACT | DRV_RE, ILEVEL_0, node_slash_minor
 	},
 	{ "pseudo", "ddi_pseudo", "zsh",
--- a/usr/src/cmd/fs.d/Makefile	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/fs.d/Makefile	Fri Aug 25 17:24:25 2006 -0700
@@ -45,7 +45,7 @@
 include ../Makefile.cmd
 
 SUBDIR1= lofs zfs
-SUBDIR2= fd pcfs nfs hsfs proc ctfs udfs ufs tmpfs cachefs autofs mntfs objfs
+SUBDIR2= dev fd pcfs nfs hsfs proc ctfs udfs ufs tmpfs cachefs autofs mntfs objfs
 i386_SUBDIRS= xmemfs
 i386_I18NDIRS= xmemfs
 SUBDIRS= $(SUBDIR1) $(SUBDIR2) $($(MACH)_SUBDIRS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/dev/Makefile	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,40 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+FSTYPE=		dev
+PROG=		mount
+LIBPROG=	$(PROG)
+ROOTFS_PROG=	$(PROG)
+
+# duplicate ROOTLIBFSTYPE value needed for installation rule
+# we must define this before including Makefile.fstype
+ROOTLIBFSTYPE = $(ROOT)/usr/lib/fs/$(FSTYPE)
+$(ROOTLIBFSTYPE)/%:   $(ROOTLIBFSTYPE) %
+	$(RM) $@; $(SYMLINK) ../../../../etc/fs/$(FSTYPE)/$(PROG) $@
+
+include		../Makefile.fstype
+include		../Makefile.mount
+include		../Makefile.mount.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/dev/mount.c	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,363 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <locale.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/mntent.h>
+#include <sys/fs/sdev_node.h>
+
+
+#define	READFLAG_RO	1
+#define	READFLAG_RW	2
+
+
+extern int	optind;
+extern char	*optarg;
+
+static char	typename[64], *myname;
+static char	fstype[] = MNTTYPE_DEV;
+
+static int	readflag;
+static int	overlay;
+static int	remount;
+
+static char	*special;
+static char	*mountpt;
+static struct sdev_mountargs	mountargs;
+
+static char	*myopts[] = {
+#define	SUBOPT_READONLY		0
+	"ro",
+#define	SUBOPT_READWRITE	1
+	"rw",
+#define	SUBOPT_ATTRIBDIR	2
+	"attrdir",
+#define	SUBOPT_REMOUNT		3
+	"remount",
+	NULL
+};
+
+
+static void
+usage(void)
+{
+	(void) fprintf(stderr, gettext(
+	    "%s usage:\n%s [-F %s] [-r] [-o specific_options]"
+	    " {special | mount_point}\n%s [-F %s] [-r] [-o specific_options]"
+	    " special mount_point\n"), fstype, myname, fstype, myname, fstype);
+	exit(1);
+}
+
+
+static int
+do_mount(void)
+{
+	int	flags = MS_DATA;
+
+	if (readflag == READFLAG_RO)
+		flags |= MS_RDONLY;
+	if (overlay)
+		flags |= MS_OVERLAY;
+	if (remount)
+		flags |= MS_REMOUNT;
+
+	if (mount(special, mountpt, flags, fstype, &mountargs,
+	    sizeof (mountargs), NULL, 0)) {
+		switch (errno) {
+		case EPERM:
+			(void) fprintf(stderr, gettext("%s: not super user\n"),
+			    typename);
+			break;
+		case ENXIO:
+			(void) fprintf(stderr, gettext("%s: %s no such "
+			    "device\n"), typename, special);
+			break;
+		case ENOTDIR:
+			(void) fprintf(stderr, gettext("%s: %s "
+			    "not a directory\n"
+			    "\tor a component of %s is not a directory\n"),
+			    typename, mountpt, special);
+			break;
+		case ENOENT:
+			(void) fprintf(stderr, gettext("%s: %s or %s, no such "
+			    "file or directory\n"),
+			    typename, special, mountpt);
+			break;
+		case EINVAL:
+			(void) fprintf(stderr, gettext("%s: %s is not this "
+			    "filesystem type.\n"), typename, special);
+			break;
+		case EBUSY:
+			(void) fprintf(stderr, gettext("%s: %s "
+			    "is already mounted, %s is busy,\n"
+			    "\tor allowable number of mount points exceeded\n"),
+			    typename, special, mountpt);
+			break;
+		case ENOTBLK:
+			(void) fprintf(stderr, gettext("%s: %s not a block "
+			    "device\n"), typename, special);
+			break;
+		case EROFS:
+			(void) fprintf(stderr, gettext("%s: %s read-only "
+			    "filesystem\n"), typename, special);
+			break;
+		case ENOSPC:
+			(void) fprintf(stderr, gettext("%s: the state of %s "
+			    "is not okay\n"
+			    "\tand read/write mount was attempted\n"),
+			    typename, special);
+			break;
+		default:
+			(void) fprintf(stderr, gettext("%s: cannot mount %s: "
+			    "%s\n"), typename, special, strerror(errno));
+			break;
+		}
+		return (-1);
+	}
+	return (0);
+}
+
+
+/*
+ * Wrapper around strdup().
+ */
+static char *
+do_strdup(const char *s1)
+{
+	char	*str;
+
+	str = strdup(s1);
+	if (str == NULL) {
+		(void) fprintf(stderr, gettext("%s: strdup failed: %s\n"),
+		    typename, strerror(errno));
+	}
+	return (str);
+}
+
+
+/*
+ * Wrapper around stat().
+ */
+static int
+do_stat(const char *path, struct stat *buf)
+{
+	int	ret;
+
+	ret = stat(path, buf);
+	if (ret < 0) {
+		(void) fprintf(stderr, gettext("%s: can't stat %s: %s\n"),
+		    typename, path, strerror(errno));
+	}
+	return (ret);
+}
+
+
+/*
+ * Wraper around realpath()
+ */
+static char *
+do_realpath(const char *path, char *resolved_path)
+{
+	char	*ret;
+
+	ret = realpath(path, resolved_path);
+	if (ret == NULL) {
+		(void) fprintf(stderr, gettext("%s: realpath %s failed: %s\n"),
+		    typename, path, strerror(errno));
+	}
+	return (ret);
+}
+
+
+static int
+parse_subopts(char *subopts)
+{
+	char	*value;
+	char	path[PATH_MAX + 1];
+
+	while (*subopts != '\0') {
+		switch (getsubopt(&subopts, myopts, &value)) {
+		case SUBOPT_READONLY:
+			if (readflag == READFLAG_RW) {
+				(void) fprintf(stderr, gettext("%s: both "
+				    "read-only and read-write options "
+				    "specified\n"), typename);
+				return (-1);
+			}
+			readflag = READFLAG_RO;
+			break;
+
+		case SUBOPT_READWRITE:
+			if (readflag == READFLAG_RO) {
+				(void) fprintf(stderr, gettext("%s: both "
+				    "read-only and read-write options "
+				    "specified\n"), typename);
+				return (-1);
+			}
+			readflag = READFLAG_RW;
+			break;
+
+		case SUBOPT_ATTRIBDIR:
+			if (value == NULL) {
+				(void) fprintf(stderr, gettext("%s: no "
+				    "attribute directory\n"), typename);
+				return (-1);
+			} else {
+				if (do_realpath(value, path) == NULL)
+					return (-1);
+				mountargs.sdev_attrdir =
+				    (uint64_t)(uintptr_t)do_strdup(path);
+				if (mountargs.sdev_attrdir == NULL)
+					return (-1);
+			}
+			break;
+
+		case SUBOPT_REMOUNT:
+			remount = 1;
+			break;
+
+		default:
+			(void) fprintf(stderr, gettext("%s: illegal -o "
+			    "suboption: %s\n"), typename, value);
+			return (-1);
+		}
+	}
+	return (0);
+}
+
+
+int
+main(int argc, char **argv)
+{
+	struct stat	st;
+	char		mntpath[PATH_MAX + 1];
+	int		cc;
+
+	(void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	if (myname = strrchr(argv[0], '/'))
+		myname++;
+	else
+		myname = argv[0];
+	(void) snprintf(typename, sizeof (typename), "%s %s", fstype, myname);
+	argv[0] = typename;
+
+	while ((cc = getopt(argc, argv, "?o:rmO")) != -1) {
+		switch (cc) {
+		case 'r':
+			if (readflag == READFLAG_RW) {
+				(void) fprintf(stderr, gettext("%s: both "
+				    "read-only and read-write options "
+				    "specified\n"), typename);
+				return (1);
+			}
+			readflag = READFLAG_RO;
+			break;
+
+		case 'O':
+			overlay = 1;
+			break;
+
+		case 'o':
+			if (parse_subopts(optarg))
+				return (1);
+			break;
+
+		default:
+			usage();
+			break;
+		}
+	}
+
+	/*
+	 * There must be at least 2 more arguments, the
+	 * special file and the directory.
+	 */
+	if ((argc - optind) != 2)
+		usage();
+
+	special = argv[optind++];
+
+	if (do_realpath(argv[optind++], mntpath) == NULL)
+		return (1);
+	mountpt = mntpath;
+
+	if (mountpt) {
+		if (do_stat(mountpt, &st) < 0)
+			return (1);
+		if (! S_ISDIR(st.st_mode)) {
+			(void) fprintf(stderr, gettext("%s: %s is not a "
+			    "directory\n"), typename, mountpt);
+			return (1);
+		}
+	}
+
+	if (mountargs.sdev_attrdir) {
+		if (do_stat((const char *)(uintptr_t)mountargs.sdev_attrdir,
+		    &st) < 0)
+			return (1);
+		if (! S_ISDIR(st.st_mode)) {
+			(void) fprintf(stderr, gettext("%s: %s is not a "
+			    "directory\n"), typename, mountargs.sdev_attrdir);
+			return (1);
+		}
+	}
+
+	/* Special checks if /dev is the mount point */
+	/* Remount of /dev requires an attribute directory */
+	if (strcmp(mountpt, "/dev") == 0 && remount &&
+	    mountargs.sdev_attrdir == NULL) {
+		(void) fprintf(stderr, gettext("%s: missing attribute "
+		    "directory\n"), typename);
+		return (1);
+	}
+
+	(void) signal(SIGHUP,  SIG_IGN);
+	(void) signal(SIGQUIT, SIG_IGN);
+	(void) signal(SIGINT,  SIG_IGN);
+
+	/* Perform the mount  */
+	if (do_mount())
+		return (1);
+
+	return (0);
+}
--- a/usr/src/cmd/initpkg/rc2.sh	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/initpkg/rc2.sh	Fri Aug 25 17:24:25 2006 -0700
@@ -105,4 +105,9 @@
 		exit 1
 esac
 
+if smf_is_globalzone; then
+	# enable full implicit device reconfig
+	/usr/sbin/devfsadm -S
+fi
+
 exit 0
--- a/usr/src/cmd/pt_chmod/Makefile	Fri Aug 25 16:44:08 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-
-PROG= pt_chmod
-
-include ../Makefile.cmd
-
-FILEMODE= 04511
-
-LDLIBS += -ldevinfo -lsec
-
-.KEEP_STATE:
-
-all: $(PROG) 
-
-install: all $(ROOTLIBPROG)
-
-clean:
-
-lint:	lint_PROG
-
-include ../Makefile.targ
--- a/usr/src/cmd/pt_chmod/pt_chmod.c	Fri Aug 25 16:44:08 2006 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
- *
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <grp.h>
-#include <stdlib.h>
-#include <stropts.h>
-#include <sys/acl.h>
-#include <libdevinfo.h>
-
-#define	DEFAULT_TTY_GROUP	"tty"
-
-/*
- * 1) change the owner and mode of the pseudo terminal slave device.
- * 2) (re)create nodes and devlinks for pseduo terminal slave device.
- */
-int
-main(int argc, char **argv)
-{
-	int	fd;
-	gid_t	gid;
-	char	*tty;
-	di_devlink_handle_t pp;
-
-	struct	group	*gr_name_ptr;
-
-	if (argc > 2)
-		return (1);
-
-	if ((gr_name_ptr = getgrnam(DEFAULT_TTY_GROUP)) != NULL)
-		gid = gr_name_ptr->gr_gid;
-	else
-		gid = getgid();
-
-	/* create pts minor device nodes and symlinks */
-	if (argc == 1) {
-		pp = di_devlink_init("pts", DI_MAKE_LINK);
-		if (pp != NULL) {
-			(void) di_devlink_fini(&pp);
-			return (0);
-		}
-		return (1);
-	}
-
-	fd = atoi(argv[1]);
-
-	tty = ptsname(fd);
-
-	if (tty == NULL)
-		return (1);
-
-	/*
-	 * Detach all STREAMs.
-	 * We need to continue to try this until we have succeeded
-	 * in calling chown on the underlying node.  From that point
-	 * onwards, no-one but root can fattach() as fattach() requires
-	 * ownership of the node.
-	 */
-	do {
-		if (chown(tty, 0, 0) != 0)
-			exit(1);
-	} while (fdetach(tty) == 0);
-
-	/* Remove ACLs */
-
-	(void) acl_strip(tty, 0, gid, 0620);
-
-	if (chown(tty, getuid(), gid))
-		return (1);
-
-	if (chmod(tty, 00620))
-		return (1);
-
-	return (0);
-}
--- a/usr/src/cmd/truss/print.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/truss/print.c	Fri Aug 25 17:24:25 2006 -0700
@@ -1099,6 +1099,9 @@
 		case MODADDMINORPERM:	s = "MODADDMINORPERM"; break;
 		case MODREMMINORPERM:	s = "MODREMMINORPERM"; break;
 		case MODREMDRVCLEANUP:	s = "MODREMDRVCLEANUP"; break;
+		case MODDEVEXISTS:	s = "MODDEVEXISTS"; break;
+		case MODDEVREADDIR:	s = "MODDEVREADDIR"; break;
+		case MODDEVNAME:	s = "MODDEVNAME"; break;
 		}
 	}
 
--- a/usr/src/cmd/zlogin/Makefile	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/zlogin/Makefile	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
@@ -29,7 +28,7 @@
 
 include ../Makefile.cmd
 
-LDLIBS += -lsocket -ldevinfo -lzonecfg -lcontract
+LDLIBS += -lsocket -lzonecfg -lcontract
 CFLAGS += $(CCVERBOSE)
 FILEMODE = 0555
 GROUP = bin
--- a/usr/src/cmd/zlogin/zlogin.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/zlogin/zlogin.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -325,7 +324,7 @@
  * above, in get_master_pty()).
  */
 static int
-init_slave_pty(zoneid_t zoneid, char *zonepath)
+init_slave_pty(zoneid_t zoneid, char *devroot)
 {
 	int slavefd = -1;
 	char *slavename, zoneslavename[MAXPATHLEN];
@@ -346,11 +345,8 @@
 	/*
 	 * We must open the slave side before zoning this pty; otherwise
 	 * the kernel would refuse us the open-- zoning a pty makes it
-	 * inaccessible to the global zone.  Since we are trying to
-	 * open the device node via the $ZONEPATH/dev path, we may have to
-	 * give devfsadm a kick to get it to create the device node for
-	 * us.  Normally this would "just work" because pt_chmod inside
-	 * the zone would take care of it for us.
+	 * inaccessible to the global zone.  Note we are trying to open
+	 * the device node via the $ZONEROOT/dev path for this pty.
 	 *
 	 * Later we'll close the slave out when once we've opened it again
 	 * from within the target zone.  Blarg.
@@ -361,18 +357,12 @@
 	}
 
 	(void) snprintf(zoneslavename, sizeof (zoneslavename), "%s%s",
-	    zonepath, slavename);
+	    devroot, slavename);
 
 	if ((slavefd = open(zoneslavename, O_RDWR)) < 0) {
-		di_devlink_handle_t h = di_devlink_init("pts", DI_MAKE_LINK);
-		if (h != NULL) {
-			(void) di_devlink_fini(&h);
-		}
-		if ((slavefd = open(zoneslavename, O_RDWR)) < 0) {
-			zerror(gettext("failed to open %s: %s"), zoneslavename,
-			    strerror(errno));
-			return (-1);
-		}
+		zerror(gettext("failed to open %s: %s"), zoneslavename,
+		    strerror(errno));
+		return (-1);
 	}
 
 	/*
@@ -1338,7 +1328,7 @@
 	char **proc_args = NULL;
 	char **new_args, **new_env;
 	sigset_t block_cld;
-	char zonepath[MAXPATHLEN];
+	char devroot[MAXPATHLEN];
 	char *slavename, slaveshortname[MAXPATHLEN];
 	priv_set_t *privset;
 	int tmpl_fd;
@@ -1550,10 +1540,10 @@
 	}
 
 	/*
-	 * We need the zone path only if we are setting up a pty.
+	 * We need the zone root path only if we are setting up a pty.
 	 */
-	if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) == -1) {
-		zerror(gettext("could not get root path for zone %s"),
+	if (zone_get_devroot(zonename, devroot, sizeof (devroot)) == -1) {
+		zerror(gettext("could not get dev path for zone %s"),
 		    zonename);
 		return (1);
 	}
@@ -1652,7 +1642,7 @@
 
 		(void) sigprocmask(SIG_UNBLOCK, &block_cld, NULL);
 
-		if ((slavefd = init_slave_pty(zoneid, zonepath)) == -1)
+		if ((slavefd = init_slave_pty(zoneid, devroot)) == -1)
 			return (1);
 
 		/*
--- a/usr/src/cmd/zoneadmd/vplat.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/zoneadmd/vplat.c	Fri Aug 25 17:24:25 2006 -0700
@@ -91,6 +91,7 @@
 #include <limits.h>
 #include <libgen.h>
 #include <libzfs.h>
+#include <libdevinfo.h>
 #include <zone.h>
 #include <assert.h>
 #include <libcontract.h>
@@ -128,52 +129,79 @@
 #define	MAXTNZLEN	2048
 
 /*
- * A list of directories which should be created.
+ * This is the set of directories and devices (relative to <zone_root>/dev)
+ * which must be present in every zone.  Users can augment this list with
+ * additional device rules in their zone configuration, but at present cannot
+ * remove any of the this set of standard devices.
  */
-
-struct dir_info {
-	char *dir_name;
-	mode_t dir_mode;
+static const char *standard_devs[] = {
+	"arp",
+	"conslog",
+	"cpu/self/cpuid",
+	"crypto",
+	"cryptoadm",
+	"dsk",
+	"dtrace/helper",
+	"fd",
+	"kstat",
+	"lo0",
+	"lo1",
+	"lo2",
+	"lo3",
+	"log",
+	"logindmux",
+	"null",
+#ifdef __sparc
+	"openprom",
+#endif
+	"poll",
+	"pool",
+	"ptmx",
+	"pts/*",
+	"random",
+	"rdsk",
+	"rmt",
+	"sad/user",
+	"swap",
+	"sysevent",
+	"tcp",
+	"tcp6",
+	"term",
+	"ticlts",
+	"ticots",
+	"ticotsord",
+	"tty",
+	"udp",
+	"udp6",
+	"urandom",
+	"zero",
+	"zfs",
+	NULL
+};
+
+struct source_target {
+	const char *source;
+	const char *target;
 };
 
 /*
- * The pathnames below are relative to the zonepath
- */
-static struct dir_info dev_dirs[] = {
-	{ "/dev",	0755 },
-	{ "/dev/dsk",	0755 },
-	{ "/dev/fd",	0555 },
-	{ "/dev/pts",	0755 },
-	{ "/dev/rdsk",	0755 },
-	{ "/dev/rmt",	0755 },
-	{ "/dev/sad",	0755 },
-	{ "/dev/swap",	0755 },
-	{ "/dev/term",	0755 },
-};
-
-/*
- * A list of devices which should be symlinked to /dev/zconsole.
+ * Set of symlinks (relative to <zone_root>/dev) which must be present in
+ * every zone.
  */
-
-struct symlink_info {
-	char *sl_source;
-	char *sl_target;
+static struct source_target standard_devlinks[] = {
+	{ "stderr",	"./fd/2" },
+	{ "stdin",	"./fd/0" },
+	{ "stdout",	"./fd/1" },
+	{ "dtremote",	"/dev/null" },
+	{ "console",	"zconsole" },
+	{ "syscon",	"zconsole" },
+	{ "sysmsg",	"zconsole" },
+	{ "systty",	"zconsole" },
+	{ "msglog",	"zconsole" },
+	{ NULL, NULL }
 };
 
-/*
- * The "source" paths are relative to the zonepath
- */
-static struct symlink_info dev_symlinks[] = {
-	{ "/dev/stderr",	"./fd/2" },
-	{ "/dev/stdin",		"./fd/0" },
-	{ "/dev/stdout",	"./fd/1" },
-	{ "/dev/dtremote",	"/dev/null" },
-	{ "/dev/console",	"zconsole" },
-	{ "/dev/syscon",	"zconsole" },
-	{ "/dev/sysmsg",	"zconsole" },
-	{ "/dev/systty",	"zconsole" },
-	{ "/dev/msglog",	"zconsole" },
-};
+static int vplat_mount_dev(zlog_t *);
 
 /* for routing socket */
 static int rts_seqno = 0;
@@ -504,81 +532,6 @@
 	return (0);
 }
 
-/*
- * Make /dev and various directories underneath it.
- */
-static int
-make_dev_dirs(zlog_t *zlogp, const char *zonepath)
-{
-	int i;
-
-	for (i = 0; i < sizeof (dev_dirs) / sizeof (struct dir_info); i++) {
-		if (make_one_dir(zlogp, zonepath, dev_dirs[i].dir_name,
-		    dev_dirs[i].dir_mode) != 0)
-			return (-1);
-	}
-	return (0);
-}
-
-/*
- * Make various sym-links underneath /dev.
- */
-static int
-make_dev_links(zlog_t *zlogp, char *zonepath)
-{
-	int i;
-
-	for (i = 0; i < sizeof (dev_symlinks) / sizeof (struct symlink_info);
-	    i++) {
-		char dev[MAXPATHLEN];
-		struct stat st;
-
-		(void) snprintf(dev, sizeof (dev), "%s%s", zonepath,
-		    dev_symlinks[i].sl_source);
-		if (lstat(dev, &st) == 0) {
-			/*
-			 * Try not to call unlink(2) on directories, since that
-			 * makes UFS unhappy.
-			 */
-			if (S_ISDIR(st.st_mode)) {
-				zerror(zlogp, B_FALSE, "symlink path %s is a "
-				    "directory", dev_symlinks[i].sl_source);
-				return (-1);
-			}
-			(void) unlink(dev);
-		}
-		if (symlink(dev_symlinks[i].sl_target, dev) != 0) {
-			zerror(zlogp, B_TRUE, "could not setup %s->%s symlink",
-			    dev_symlinks[i].sl_source,
-			    dev_symlinks[i].sl_target);
-			return (-1);
-		}
-	}
-	return (0);
-}
-
-/*
- * Create various directories and sym-links under /dev.
- */
-static int
-create_dev_files(zlog_t *zlogp)
-{
-	char zonepath[MAXPATHLEN];
-
-	if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) {
-		zerror(zlogp, B_TRUE, "unable to determine zone root");
-		return (-1);
-	}
-	if (zonecfg_in_alt_root())
-		resolve_lofs(zlogp, zonepath, sizeof (zonepath));
-
-	if (make_dev_dirs(zlogp, zonepath) != 0)
-		return (-1);
-	if (make_dev_links(zlogp, zonepath) != 0)
-		return (-1);
-	return (0);
-}
-
 static void
 free_remote_fstypes(char **types)
 {
@@ -1405,40 +1358,6 @@
 	}
 
 	/*
-	 * /dev in the zone is loopback'd from the external /dev repository,
-	 * in order to provide a largely read-only semantic.  But because
-	 * processes in the zone need to be able to chown, chmod, etc. zone
-	 * /dev files, we can't use a 'ro' lofs mount.  Instead we use a
-	 * special mode just for zones, "zonedevfs".
-	 *
-	 * In the future we should front /dev with a full-fledged filesystem.
-	 */
-	num_fs++;
-	if ((tmp_ptr = realloc(fs_ptr, num_fs * sizeof (*tmp_ptr))) == NULL) {
-		zerror(zlogp, B_TRUE, "memory allocation failed");
-		num_fs--;
-		goto bad;
-	}
-	fs_ptr = tmp_ptr;
-	fsp = &fs_ptr[num_fs - 1];
-	/*
-	 * Note that mount_one will prepend the alternate root to
-	 * zone_fs_special and do the necessary resolution, so all that is
-	 * needed here is to strip the root added by zone_get_zonepath.
-	 */
-	(void) strlcpy(fsp->zone_fs_dir, "/dev", sizeof (fsp->zone_fs_dir));
-	(void) snprintf(fsp->zone_fs_special, sizeof (fsp->zone_fs_special),
-	    "%s/dev", zonepath + strlen(zonecfg_get_root()));
-	fsp->zone_fs_raw[0] = '\0';
-	(void) strlcpy(fsp->zone_fs_type, MNTTYPE_LOFS,
-	    sizeof (fsp->zone_fs_type));
-	fsp->zone_fs_options = NULL;
-	if (zonecfg_add_fs_option(fsp, MNTOPT_LOFS_ZONEDEVFS) != Z_OK) {
-		zerror(zlogp, B_FALSE, "error adding property");
-		goto bad;
-	}
-
-	/*
 	 * Iterate through the rest of the filesystems, first the IPDs, then
 	 * the general FSs.  Sort them all, then mount them in sorted order.
 	 * This is to make sure the higher level directories (e.g., /usr)
@@ -1510,10 +1429,14 @@
 	handle = NULL;
 
 	/*
-	 * If we're mounting a zone for administration, then we need to set up
-	 * the "/a" environment inside the zone so that the commands that run
-	 * in there have access to both the running system's utilities and the
-	 * to-be-modified zone's files.
+	 * When we're mounting a zone for administration, / is the
+	 * scratch zone and dev is mounted at /dev.  The to-be-upgraded
+	 * zone is mounted at /a, and we set up that environment so that
+	 * process can access both the running system's utilities
+	 * and the to-be-modified zone's files.  The only exception
+	 * is the zone's /dev which isn't mounted at all, which is
+	 * the same as global zone installation where /a/dev and
+	 * /a/devices are not mounted.
 	 */
 	if (mount_cmd &&
 	    !build_mounted(zlogp, rootpath, sizeof (rootpath), zonepath))
@@ -1521,16 +1444,6 @@
 
 	qsort(fs_ptr, num_fs, sizeof (*fs_ptr), fs_compare);
 	for (i = 0; i < num_fs; i++) {
-		if (mount_cmd && strcmp(fs_ptr[i].zone_fs_dir, "/dev") == 0) {
-			size_t slen = strlen(rootpath) - 2;
-
-			/* /dev is special and always goes at the top */
-			rootpath[slen] = '\0';
-			if (mount_one(zlogp, &fs_ptr[i], rootpath) != 0)
-				goto bad;
-			rootpath[slen] = '/';
-			continue;
-		}
 		if (mount_one(zlogp, &fs_ptr[i], rootpath) != 0)
 			goto bad;
 	}
@@ -2325,39 +2238,6 @@
 }
 
 static int
-devfsadm_call(zlog_t *zlogp, const char *arg)
-{
-	char *argv[4];
-	int status;
-
-	argv[0] = DEVFSADM;
-	argv[1] = (char *)arg;
-	argv[2] = zone_name;
-	argv[3] = NULL;
-	status = forkexec(zlogp, DEVFSADM_PATH, argv);
-	if (status == 0 || status == -1)
-		return (status);
-	zerror(zlogp, B_FALSE, "%s call (%s %s %s) unexpectedly returned %d",
-	    DEVFSADM, DEVFSADM_PATH, arg, zone_name, status);
-	return (-1);
-}
-
-static int
-devfsadm_register(zlog_t *zlogp)
-{
-	/*
-	 * Ready the zone's devices.
-	 */
-	return (devfsadm_call(zlogp, "-z"));
-}
-
-static int
-devfsadm_unregister(zlog_t *zlogp)
-{
-	return (devfsadm_call(zlogp, "-Z"));
-}
-
-static int
 get_privset(zlog_t *zlogp, priv_set_t *privs, boolean_t mount_cmd)
 {
 	int error = -1;
@@ -3639,80 +3519,6 @@
 	_exit(0);
 }
 
-/*ARGSUSED1*/
-static int
-devcleanup_cb(const char *path, uid_t u, gid_t g, mode_t m, const char *a,
-    void *data)
-{
-	zone_dochandle_t h = (zone_dochandle_t)data;
-	boolean_t del;
-	char fullpath[MAXPATHLEN];
-	char zonepath[MAXPATHLEN];
-
-	if (zonecfg_should_deldev(h, path, &del) == Z_OK) {
-		if (del) {
-			if (zonecfg_get_zonepath(h, zonepath,
-			    sizeof (zonepath)) != Z_OK)
-				return (Z_OK);
-			(void) snprintf(fullpath, sizeof (fullpath),
-			    "%s/dev/%s", zonepath, path);
-			(void) unlink(fullpath);
-		}
-	}
-	return (Z_OK);
-}
-
-/*
- * If needed, initiate a walk of the zone's /dev tree, looking for device
- * entries which need to be cleaned up: this is a wrapper around functionality
- * in libzonecfg which keeps track of newly-defunct device entries.
- */
-static int
-devcleanup(zlog_t *zlogp)
-{
-	zone_dochandle_t handle;
-
-	if ((handle = zonecfg_init_handle()) == NULL) {
-		zerror(zlogp, B_TRUE, "getting zone configuration handle");
-		return (-1);
-	}
-
-	/*
-	 * Note that this is a rare case when we consult the *real* zone
-	 * config handle, not a snapshot-- that's because we want to
-	 * drop the deleted-device markers out of the config once we've
-	 * purged them from the FS.
-	 */
-	if (zonecfg_get_handle(zone_name, handle) != Z_OK) {
-		zerror(zlogp, B_FALSE, "invalid configuration");
-		zonecfg_fini_handle(handle);
-		return (-1);
-	}
-
-	/*
-	 * Quickly check whether there is any work to do prior to scanning
-	 * all of /dev.
-	 */
-	if (zonecfg_has_deldevs(handle) != Z_OK) {
-		zonecfg_fini_handle(handle);
-		return (0);
-	}
-
-	if (zonecfg_devwalk(handle, devcleanup_cb, (void *)handle) != Z_OK) {
-		zerror(zlogp, B_FALSE, "failed to walk devices");
-		zonecfg_fini_handle(handle);
-		return (-1);
-	}
-
-	/*
-	 * We don't need to process the deleted devices more than the one time.
-	 */
-	(void) zonecfg_clear_deldevs(handle);
-	(void) zonecfg_save(handle);
-	zonecfg_fini_handle(handle);
-	return (0);
-}
-
 int
 vplat_bringup(zlog_t *zlogp, boolean_t mount_cmd, zoneid_t zoneid)
 {
@@ -3722,18 +3528,18 @@
 		return (-1);
 	}
 
-	if (devcleanup(zlogp) != 0) {
-		zerror(zlogp, B_TRUE, "device cleanup failed");
-		return (-1);
-	}
-
-	if (create_dev_files(zlogp) != 0 ||
-	    mount_filesystems(zlogp, mount_cmd) != 0) {
+	if (mount_filesystems(zlogp, mount_cmd) != 0) {
 		lofs_discard_mnttab();
 		return (-1);
 	}
-	if (!mount_cmd && (devfsadm_register(zlogp) != 0 ||
-	    configure_network_interfaces(zlogp) != 0)) {
+
+	/* mount /dev for zone (both normal and scratch zone) */
+	if (vplat_mount_dev(zlogp) != 0) {
+		lofs_discard_mnttab();
+		return (-1);
+	}
+
+	if (!mount_cmd && configure_network_interfaces(zlogp) != 0) {
 		lofs_discard_mnttab();
 		return (-1);
 	}
@@ -3846,9 +3652,6 @@
 		goto error;
 	}
 
-	if (!unmount_cmd && devfsadm_unregister(zlogp) != 0)
-		goto error;
-
 	if (!unmount_cmd &&
 	    unconfigure_network_interfaces(zlogp, zoneid) != 0) {
 		zerror(zlogp, B_FALSE,
@@ -3861,6 +3664,10 @@
 		goto error;
 	}
 
+	/* destroy zconsole before umount /dev */
+	if (!unmount_cmd)
+		destroy_console_slave();
+
 	if (unmount_filesystems(zlogp, zoneid, unmount_cmd) != 0) {
 		zerror(zlogp, B_FALSE,
 		    "unable to unmount file systems in zone");
@@ -3881,9 +3688,6 @@
 	if (unmount_cmd && lu_root_teardown(zlogp) != 0)
 		goto error;
 
-	if (!unmount_cmd)
-		destroy_console_slave();
-
 	lofs_discard_mnttab();
 	return (0);
 
@@ -3891,3 +3695,129 @@
 	lofs_discard_mnttab();
 	return (-1);
 }
+
+/*
+ * Apply the standard lists of devices/symlinks/mappings and the user-specified
+ * list of devices (via zonecfg) to the /dev filesystem.  The filesystem will
+ * use these as a profile/filter to determine what exists in /dev.
+ */
+static int
+vplat_mount_dev(zlog_t *zlogp)
+{
+	char			zonedevpath[MAXPATHLEN];
+	zone_dochandle_t	handle = NULL;
+	struct zone_devtab	ztab;
+	zone_fsopt_t		opt_attr;
+	di_prof_t		prof = NULL;
+	int			i, err, len;
+	int			retval = -1;
+
+	struct zone_fstab devtab = {
+		"/dev",
+		"/dev",
+		MNTTYPE_DEV,
+		NULL,
+		""
+	};
+
+	if (err = zone_get_devroot(zone_name, zonedevpath,
+	    sizeof (zonedevpath))) {
+		zerror(zlogp, B_FALSE, "can't get zone dev: %s",
+		    zonecfg_strerror(err));
+		return (-1);
+	}
+
+	/*
+	 * The old /dev was a lofs mount from <zonepath>/dev, with
+	 * dev fs, that becomes a mount on <zonepath>/root/dev.
+	 * However, we need to preserve device permission bits during
+	 * upgrade.  What we should do is migrate the attribute directory
+	 * on upgrade, but for now, preserve it at <zonepath>/dev.
+	 */
+	(void) strcpy(opt_attr.zone_fsopt_opt, "attrdir=");
+	len = strlen(opt_attr.zone_fsopt_opt);
+	if (err = zone_get_zonepath(zone_name,
+	    opt_attr.zone_fsopt_opt + len, MAX_MNTOPT_STR - len)) {
+		zerror(zlogp, B_FALSE, "can't get zone path: %s",
+		    zonecfg_strerror(err));
+		return (-1);
+	}
+
+	if (make_one_dir(zlogp, opt_attr.zone_fsopt_opt + len, "/dev",
+	    DEFAULT_DIR_MODE) != 0)
+		return (-1);
+
+	(void) strlcat(opt_attr.zone_fsopt_opt, "/dev", MAX_MNTOPT_STR);
+	devtab.zone_fs_options = &opt_attr;
+	opt_attr.zone_fsopt_next = NULL;
+
+	/* mount /dev inside the zone */
+	i = strlen(zonedevpath);
+	if (mount_one(zlogp, &devtab, zonedevpath))
+		return (-1);
+
+	(void) strlcat(zonedevpath, "/dev", sizeof (zonedevpath));
+	if (di_prof_init(zonedevpath, &prof)) {
+		zerror(zlogp, B_TRUE, "failed to initialize profile");
+		goto cleanup;
+	}
+
+	/* Add the standard devices and directories */
+	for (i = 0; standard_devs[i] != NULL; ++i) {
+		if (di_prof_add_dev(prof, standard_devs[i])) {
+			zerror(zlogp, B_TRUE, "failed to add "
+			    "standard device");
+			goto cleanup;
+		}
+	}
+
+	/* Add the standard symlinks */
+	for (i = 0; standard_devlinks[i].source != NULL; ++i) {
+		if (di_prof_add_symlink(prof,
+		    standard_devlinks[i].source,
+		    standard_devlinks[i].target)) {
+			zerror(zlogp, B_TRUE, "failed to add "
+			    "standard symlink");
+			goto cleanup;
+		}
+	}
+
+	/* Add user-specified devices and directories */
+	if ((handle = zonecfg_init_handle()) == NULL) {
+		zerror(zlogp, B_FALSE, "can't initialize zone handle");
+		goto cleanup;
+	}
+	if (err = zonecfg_get_handle(zone_name, handle)) {
+		zerror(zlogp, B_FALSE, "can't get handle for zone "
+		    "%s: %s", zone_name, zonecfg_strerror(err));
+		goto cleanup;
+	}
+	if (err = zonecfg_setdevent(handle)) {
+		zerror(zlogp, B_FALSE, "%s: %s", zone_name,
+		    zonecfg_strerror(err));
+		goto cleanup;
+	}
+	while (zonecfg_getdevent(handle, &ztab) == Z_OK) {
+		if (di_prof_add_dev(prof, ztab.zone_dev_match)) {
+			zerror(zlogp, B_TRUE, "failed to add "
+			    "user-specified device");
+			goto cleanup;
+		}
+	}
+	(void) zonecfg_enddevent(handle);
+
+	/* Send profile to kernel */
+	if (di_prof_commit(prof)) {
+		zerror(zlogp, B_TRUE, "failed to commit profile");
+		goto cleanup;
+	}
+
+	retval = 0;
+
+cleanup:
+	if (handle)
+		zonecfg_fini_handle(handle);
+	if (prof)
+		di_prof_fini(prof);
+	return (retval);
+}
--- a/usr/src/cmd/zoneadmd/zcons.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/cmd/zoneadmd/zcons.c	Fri Aug 25 17:24:25 2006 -0700
@@ -98,6 +98,7 @@
 #include <sys/stat.h>
 #include <sys/termios.h>
 #include <sys/zcons.h>
+#include <sys/mkdev.h>
 
 #include <assert.h>
 #include <ctype.h>
@@ -118,6 +119,8 @@
 #include <libzonecfg.h>
 
 #include <syslog.h>
+#include <sys/modctl.h>
+#include <sys/fs/sdev_node.h>
 
 #include "zoneadmd.h"
 
@@ -409,38 +412,49 @@
  * console to have terminal semantics.
  */
 static int
-prep_console_slave(zlog_t *zlogp, char *zonepath)
+prep_console_slave(zlog_t *zlogp, char *devroot)
 {
 	char slavename[MAXPATHLEN];
 	char zoneslavename[MAXPATHLEN];
-	struct stat st;
+	char zonedev[MAXPATHLEN];
+	di_prof_t prof = NULL;
 
 	assert(slavefd == -1);
 
 	(void) snprintf(slavename, sizeof (slavename),
-	    "/dev/zcons/%s/%s", zone_name, ZCONS_SLAVE_NAME);
+	    "zcons/%s/%s", zone_name, ZCONS_SLAVE_NAME);
 
 	(void) snprintf(zoneslavename, sizeof (zoneslavename),
-	    "%s/dev/zconsole", zonepath);
+	    "%s/dev/zconsole", devroot);
+
+	(void) snprintf(zonedev, sizeof (zonedev),
+	    "%s/dev", devroot);
 
 	/*
-	 * We mknod the zone console in $zonepath/dev/; someday it would
-	 * be nice to not have to manually mknod this stuff-- if possible,
-	 * we could move this into devfsadm.
+	 * Specify zconsole as a name map in the dev profile
 	 */
-	if (stat(slavename, &st) == -1) {
-		zerror(zlogp, B_TRUE, "failed to stat %s", slavename);
+	if (di_prof_init(zonedev, &prof)) {
+		zerror(zlogp, B_TRUE, "failed to initialize profile");
+		goto error;
+	}
+
+	if (di_prof_add_map(prof, slavename, "zconsole")) {
+		zerror(zlogp, B_TRUE, "failed to add zconsole map");
 		goto error;
 	}
-	(void) unlink(zoneslavename);
-	if (mknod(zoneslavename, st.st_mode, st.st_rdev) == -1) {
-		zerror(zlogp, B_TRUE, "failed to mknod %s", zoneslavename);
+
+	/* Send profile to kernel */
+	if (di_prof_commit(prof)) {
+		zerror(zlogp, B_TRUE, "failed to commit profile");
 		goto error;
 	}
-	(void) chown(zoneslavename, st.st_uid, st.st_gid);
+
+	di_prof_fini(prof);
+	prof = NULL;
+
 	if ((slavefd = open(zoneslavename, O_RDWR | O_NOCTTY)) < 0) {
 		zerror(zlogp, B_TRUE, "failed to open %s", zoneslavename);
-		return (-1);
+		goto error;
 	}
 
 	/*
@@ -488,8 +502,11 @@
 
 	return (0);
 error:
-	(void) close(slavefd);
+	if (slavefd != -1)
+		(void) close(slavefd);
 	slavefd = -1;
+	if (prof)
+		di_prof_fini(prof);
 	return (-1);
 }
 
@@ -503,17 +520,17 @@
 int
 init_console_slave(zlog_t *zlogp)
 {
-	char zonepath[MAXPATHLEN];
+	char devroot[MAXPATHLEN];
 
 	if (slavefd != -1)
 		return (0);
 
-	if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) {
+	if (zone_get_devroot(zone_name, devroot, sizeof (devroot)) != Z_OK) {
 		zerror(zlogp, B_TRUE, "unable to determine zone root");
 		return (-1);
 	}
 
-	if (prep_console_slave(zlogp, zonepath) == -1) {
+	if (prep_console_slave(zlogp, devroot) == -1) {
 		zerror(zlogp, B_FALSE, "could not prep console slave");
 		return (-1);
 	}
--- a/usr/src/head/libzonecfg.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/head/libzonecfg.h	Fri Aug 25 17:24:25 2006 -0700
@@ -279,12 +279,6 @@
 extern	int	zonecfg_modify_dev(zone_dochandle_t, struct zone_devtab *,
     struct zone_devtab *);
 extern	int	zonecfg_lookup_dev(zone_dochandle_t, struct zone_devtab *);
-extern	int	zonecfg_match_dev(zone_dochandle_t, const char *,
-    struct zone_devtab *);
-extern	int	zonecfg_should_deldev(zone_dochandle_t, const char *,
-    boolean_t *);
-extern	int	zonecfg_clear_deldevs(zone_dochandle_t);
-extern	int	zonecfg_has_deldevs(zone_dochandle_t);
 
 /*
  * Resource control configuration.
@@ -390,6 +384,7 @@
  * Higher-level routines.
  */
 extern	int	zone_get_rootpath(char *, char *, size_t);
+extern	int	zone_get_devroot(char *, char *, size_t);
 extern	int	zone_get_zonepath(char *, char *, size_t);
 extern	int	zone_get_state(char *, zone_state_t *);
 extern	int	zone_set_state(char *, zone_state_t);
--- a/usr/src/lib/libc/port/gen/pt.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/lib/libc/port/gen/pt.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -58,15 +57,14 @@
 #include <thread.h>
 #include <spawn.h>
 #include <libc.h>
+#include <grp.h>
 #include "tsd.h"
 
 #define	PTSNAME "/dev/pts/"		/* slave name */
 #define	PTLEN   32			/* slave name length */
-#define	PTPATH  "/usr/lib/pt_chmod"    	/* setuid root program */
-#define	PTPGM   "pt_chmod"		/* setuid root program */
+#define	DEFAULT_TTY_GROUP	"tty"	/* slave device group owner */
 
 static void itoa(int, char *);
-static int grantpt_u(int, int);
 
 /*
  *  Check that fd argument is a file descriptor of an opened master.
@@ -94,18 +92,6 @@
 	return (minor(status.st_rdev));
 }
 
-static int
-ptscreate(void)
-{
-	static mutex_t clk = DEFAULTMUTEX;
-	int ret;
-
-	lmutex_lock(&clk);
-	ret = grantpt_u(-1, 1);
-	lmutex_unlock(&clk);
-	return (ret);
-}
-
 char *
 ptsname(int fd)
 {
@@ -122,12 +108,12 @@
 	itoa(dev, sname + strlen(PTSNAME));
 
 	/*
-	 * devfsadm synchronization: if the node does not exist,
-	 * attempt to synchronize with slave device node creation.
+	 * This lookup will create the /dev/pts node (if the corresponding
+	 * pty exists.
 	 */
-	if (access(sname, F_OK) ==  0 ||
-	    (ptscreate() == 0 && access(sname, F_OK) == 0))
+	if (access(sname, F_OK) ==  0)
 		return (sname);
+
 	return (NULL);
 }
 
@@ -151,92 +137,36 @@
 	return (0);
 }
 
-
-/*
- * Execute a setuid root program to change the mode, ownership and
- * group of the slave device. The parent forks a child process that
- * executes the setuid program. It then waits for the child to return.
- *
- * When create is 1, execute the setuid root program without arguments,
- * to create minor nodes and symlinks for all slave devices.
- */
-static int
-grantpt_u(int fd, int create)
+int
+grantpt(int fd)
 {
-	extern char **environ;
-	char *argvec[3];
-	int	st_loc;
-	pid_t	pid;
-	int	w;
-	char	fds[24];
-	sigset_t oset, nset;
-	int	error;
+	struct strioctl istr;
+	pt_own_t pto;
+	struct group *gr_name;
 
 	/* validate the file descriptor before proceeding */
-	if (create != 1 && ptsdev(fd) == NODEV)
+	if (ptsdev(fd) == NODEV)
 		return (-1);
 
-	if (sigemptyset(&nset) == -1)
-		return (-1);
-	if (sigaddset(&nset, SIGCHLD) == -1)
-		return (-1);
-	if (sigprocmask(SIG_BLOCK, &nset, &oset) == -1)
-		return (-1);
+	pto.pto_ruid = getuid();
+
+	gr_name = getgrnam(DEFAULT_TTY_GROUP);
+	if (gr_name)
+		pto.pto_rgid = gr_name->gr_gid;
+	else
+		pto.pto_rgid = getgid();
 
-	itoa(fd, fds);
-	argvec[0] = PTPGM;
-	argvec[1] = create == 1 ? NULL : fds;
-	argvec[2] = NULL;
-	error = posix_spawn(&pid, PTPATH, NULL, NULL, argvec, environ);
-	if (error) {
-		(void) sigprocmask(SIG_SETMASK, &oset, NULL);
-		errno = error;
+	istr.ic_cmd = PT_OWNER;
+	istr.ic_len = sizeof (pt_own_t);
+	istr.ic_timout = 0;
+	istr.ic_dp = (char *)&pto;
+
+	if (ioctl(fd, I_STR, &istr) != 0) {
+		errno = EACCES;
 		return (-1);
 	}
 
-	/*
-	 * waitpid() returns the process id for the child process
-	 * on success or -1 on failure.
-	 */
-	while ((w = waitpid(pid, &st_loc, 0)) < 0 && errno == EINTR)
-		continue;
-
-	/* Restore signal mask */
-	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
-
-	/*
-	 * If SIGCHLD is currently ignored, waitpid() fails with
-	 * ECHILD after the child terminates.
-	 * This is not a failure; assume the child succeded.
-	 */
-	if (w == -1) {
-		if (errno != ECHILD)
-			return (-1);
-		st_loc = 0;
-	}
-
-	/*
-	 * If child terminated due to exit() and the exit status is zero
-	 *	return success
-	 * else it was an exit(-1) or it was struck by a signal
-	 *	return failure (EACCES)
-	 */
-	if (WIFEXITED(st_loc) && WEXITSTATUS(st_loc) == 0)
-		return (0);
-	errno = EACCES;
-	return (-1);
-}
-
-int
-grantpt(int fd)
-{
-	static mutex_t glk = DEFAULTMUTEX;
-	int ret;
-
-	lmutex_lock(&glk);
-	ret = grantpt_u(fd, 0);
-	lmutex_unlock(&glk);
-	return (ret);
+	return (0);
 }
 
 /*
--- a/usr/src/lib/libdevinfo/Makefile.com	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/lib/libdevinfo/Makefile.com	Fri Aug 25 17:24:25 2006 -0700
@@ -29,7 +29,8 @@
 VERS=		.1
 
 OBJECTS=	devfsinfo.o devinfo.o devinfo_prop_decode.o devinfo_devlink.o \
-		devinfo_devperm.o devfsmap.o devinfo_dli.o
+		devinfo_devperm.o devfsmap.o devinfo_devname.o \
+		devinfo_finddev.o devinfo_dli.o
 
 include ../../Makefile.lib
 include ../../Makefile.rootfs
--- a/usr/src/lib/libdevinfo/devinfo_devlink.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/lib/libdevinfo/devinfo_devlink.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,12 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #pragma ident	"%Z%%M%	%I%	%E% SMI"
 
+#include "libdevinfo.h"
 #include "devinfo_devlink.h"
 
 #undef DEBUG
@@ -205,7 +205,7 @@
 	}
 #endif
 	if (dir == NULL) {
-		dir = hdp->dev_dir;
+		dir = hdp->db_dir;
 	}
 
 	(void) snprintf(buf, blen, "%s/%s", dir, fname);
@@ -246,6 +246,14 @@
 		get_db_path(hdp, DB_TMP, path, sizeof (path));
 	}
 
+	/*
+	 * Avoid triggering /dev reconfigure for read when not present
+	 */
+	if (IS_RDONLY(flags) &&
+	    (strncmp(path, "/dev/", 5) == 0) && !device_exists(path)) {
+		return (-1);
+	}
+
 	if ((fd = open(path, flg, DB_PERMS)) == -1) {
 		return (-1);
 	}
@@ -298,12 +306,13 @@
 static struct di_devlink_handle *
 handle_alloc(const char *root_dir, uint_t flags)
 {
-	char dev_dir[PATH_MAX], path[PATH_MAX];
+	char dev_dir[PATH_MAX], path[PATH_MAX], db_dir[PATH_MAX];
 	struct di_devlink_handle *hdp, proto = {0};
 
 	assert(flags == OPEN_RDWR || flags == OPEN_RDONLY);
 
 	dev_dir[0] = '\0';
+	db_dir[0] = '\0';
 
 	/*
 	 * NULL and the empty string are equivalent to "/"
@@ -319,18 +328,24 @@
 		/*LINTED*/
 		assert(sizeof (dev_dir) >= PATH_MAX);
 #endif
-		if (realpath(root_dir, dev_dir) == NULL) {
+		if ((realpath(root_dir, dev_dir) == NULL) ||
+		    (realpath(root_dir, db_dir) == NULL)) {
 			return (NULL);
 		}
 	}
 
 	if (strcmp(dev_dir, "/") == 0) {
-		(void) strlcpy(dev_dir, DEV, sizeof (dev_dir));
+		dev_dir[0] = 0;
+		db_dir[0] = 0;
 	} else {
-		(void) strlcat(dev_dir, DEV, sizeof (dev_dir));
+		(void) strlcpy(db_dir, dev_dir, sizeof (db_dir));
 	}
 
+	(void) strlcat(dev_dir, DEV, sizeof (dev_dir));
+	(void) strlcat(db_dir, ETCDEV, sizeof (db_dir));
+
 	proto.dev_dir = dev_dir;
+	proto.db_dir = db_dir;
 	proto.flags = flags;
 	proto.lock_fd = -1;
 
@@ -366,6 +381,11 @@
 		goto error;
 	}
 
+	if ((hdp->db_dir = strdup(proto.db_dir)) == NULL) {
+		free(hdp->dev_dir);
+		free(hdp);
+		goto error;
+	}
 
 	return (hdp);
 
@@ -1110,6 +1130,8 @@
 	cache_link_t *plp;
 	const char *minor_path;
 	char *cp, buf[PATH_MAX], link[PATH_MAX];
+	char abspath[PATH_MAX];
+	struct stat st;
 
 	if (TYPE_PRI(attr2type(clp->attr))) {
 		/*
@@ -1127,7 +1149,7 @@
 	/*
 	 * If secondary, the primary link is derived from the secondary
 	 * link contents. Secondary link contents can have two formats:
-	 * 	audio -> /dev/sound/0
+	 *	audio -> /dev/sound/0
 	 *	fb0 -> fbs/afb0
 	 */
 
@@ -1162,6 +1184,35 @@
 	/*LINTED*/
 	assert(sizeof (buf) >= PATH_MAX);
 #endif
+
+	/*
+	 * A realpath attempt to lookup a dangling link can invoke implicit
+	 * reconfig so verify there's an actual device behind the link first.
+	 */
+	if (lstat(link, &st) == -1)
+		return (NULL);
+	if (S_ISLNK(st.st_mode)) {
+		if (s_readlink(link, buf, sizeof (buf)) < 0)
+			return (NULL);
+		if (buf[0] != '/') {
+			char *p;
+			size_t n = sizeof (abspath);
+			if (strlcpy(abspath, link, n) >= n)
+				return (NULL);
+			p = strrchr(abspath, '/') + 1;
+			*p = 0;
+			n = sizeof (abspath) - strlen(p);
+			if (strlcpy(p, buf, n) >= n)
+				return (NULL);
+		} else {
+			if (strlcpy(abspath, buf, sizeof (abspath)) >=
+			    sizeof (abspath))
+				return (NULL);
+		}
+		if (!device_exists(abspath))
+			return (NULL);
+	}
+
 	if (realpath(link, buf) == NULL || !is_minor_node(buf, &minor_path)) {
 		return (NULL);
 	}
@@ -2399,13 +2450,13 @@
 	recurse_t *rp,
 	int *retp)
 {
-	DIR *dp;
 	size_t len;
 	const char *rel;
 	struct stat sbuf;
 	char cur[PATH_MAX], *cp;
 	int i, rv = DI_WALK_CONTINUE;
-	struct dirent *entp;
+	finddevhdl_t handle;
+	char *d_name;
 
 
 	if ((rel = rel_path(hdp, dir)) == NULL)
@@ -2424,7 +2475,7 @@
 
 	(void) dprintf(DBG_STEP, "do_recurse: dir = %s\n", dir);
 
-	if ((dp = opendir(dir)) == NULL)
+	if (finddev_readdir(dir, &handle) != 0)
 		return (DI_WALK_CONTINUE);
 
 	(void) snprintf(cur, sizeof (cur), "%s/", dir);
@@ -2432,14 +2483,12 @@
 	cp = cur + len;
 	len = sizeof (cur) - len;
 
-	while ((entp = readdir(dp)) != NULL) {
-
-		if (strcmp(entp->d_name, ".") == 0 ||
-		    strcmp(entp->d_name, "..") == 0) {
-			continue;
-		}
-
-		(void) snprintf(cp, len, "%s", entp->d_name);
+	for (;;) {
+		if ((d_name = (char *)finddev_next(handle)) == NULL)
+			break;
+
+		if (strlcpy(cp, d_name, len) >= len)
+			break;
 
 		/*
 		 * Skip files we are not interested in.
@@ -2475,7 +2524,7 @@
 			break;
 	}
 
-	(void) closedir(dp);
+	finddev_close(handle);
 
 	return (rv);
 }
@@ -3011,7 +3060,7 @@
  * returns 1 if contents is a minor node in /devices.
  * If mn_root is not NULL, mn_root is set to:
  *	if contents is a /dev node, mn_root = contents
- * 			OR
+ *			OR
  *	if contents is a /devices node, mn_root set to the '/'
  *	following /devices.
  */
@@ -3203,9 +3252,17 @@
 	int		fd, door_error;
 	sigset_t	oset, nset;
 	char		synch_door[PATH_MAX];
-
+	struct statvfs	svf;
+	char		*prefix;
+
+	/*
+	 * If readonly root, assume we are in install
+	 */
+	prefix =
+	    (statvfs("/etc/dev", &svf) == 0 && (svf.f_flag & ST_RDONLY)) ?
+		"/tmp" : (char *)root;
 	(void) snprintf(synch_door, sizeof (synch_door),
-	    "%s/dev/%s", root, DEVFSADM_SYNCH_DOOR);
+	    "%s/etc/dev/%s", prefix, DEVFSADM_SYNCH_DOOR);
 
 	if ((fd = open(synch_door, O_RDONLY)) == -1) {
 		dcp->dca_error = errno;
--- a/usr/src/lib/libdevinfo/devinfo_devlink.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/lib/libdevinfo/devinfo_devlink.h	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -19,7 +18,7 @@
  *
  * CDDL HEADER END
  *
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -56,6 +55,7 @@
 #include <sys/wait.h>
 #include <door.h>
 #include <signal.h>
+#include <sys/statvfs.h>
 
 struct db_link {
 	uint32_t attr;		/* primary or secondary */
@@ -143,6 +143,7 @@
 
 struct di_devlink_handle {
 	char *dev_dir;			/* <root-dir>/dev */
+	char *db_dir;			/* <root-dir>/etc/dev */
 	uint_t	flags;			/* handle flags	*/
 	uint_t  error;			/* records errors encountered */
 	int lock_fd;			/* lock file for updates */
@@ -199,6 +200,7 @@
 #define	DB_NIL		0
 
 #define	DEV		"/dev"
+#define	ETCDEV		"/etc/dev"
 #define	DEVICES_SUFFIX	"ices"
 
 #define	HDR_LEN			sizeof (struct db_hdr)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libdevinfo/devinfo_devname.c	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,701 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <stdio.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <libnvpair.h>
+#include <libdevinfo.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/systeminfo.h>
+#include <sys/modctl.h>
+#include <sys/fs/sdev_node.h>
+
+
+#define	LINEMAX		1024
+#define	SPC		" \t\n"
+#define	QUOTES		"\'\""
+
+/*
+ * This is for local file supports of DBNR configurations.
+ */
+static int di_devname_getmapent_files(char *, char *, nvlist_t **);
+static int di_devname_get_mapinfo_files(char *, nvlist_t **);
+static int parse_mapinfo_file(FILE *, nvlist_t **);
+static FILE *open_local_map(char *);
+static void unquote(char *, char *);
+
+static int msglog = 1;
+typedef enum {
+	DBG_ERR = 1,
+	DBG_INFO,
+	DBG_STEP,
+	DBG_ALL
+} debug_level_t;
+static int devname_debug = 1;
+static void dprintf(debug_level_t, const char *, ...);
+
+extern int isspace(int);
+
+/* exported interfaces */
+void di_devname_print_mapinfo(nvlist_t *);
+int di_devname_get_mapinfo(char *, nvlist_t **);
+int di_devname_get_mapent(char *, char *, nvlist_t **);
+int di_devname_action_on_key(nvlist_t *, uint8_t, char *, void *);
+
+/*
+ * Returns 0 and the valid maplist, otherwise errno.
+ */
+int
+di_devname_get_mapinfo_files(char *mapname, nvlist_t **maplist)
+{
+	FILE *fp;
+	int rval = 0;
+	nvlist_t *nvl = NULL;
+
+	fp = open_local_map(mapname);
+	if (fp == NULL) {
+		dprintf(DBG_INFO, "di_devname_get_mapinfo_files: file %s does"
+		    "not exist\n", mapname);
+		return (ENOENT);
+	}
+
+	rval = parse_mapinfo_file(fp, &nvl);
+	if (rval == 0) {
+		*maplist = nvl;
+	}
+	(void) fclose(fp);
+
+	return (rval);
+}
+
+static FILE *
+open_local_map(char *mapname)
+{
+	char filename[LINEMAX];
+
+	if (*mapname != '/') {
+		(void) snprintf(filename, sizeof (filename), "/etc/dev/%s",
+		    mapname);
+	} else {
+		(void) snprintf(filename, sizeof (filename), "%s", mapname);
+	}
+
+	return (fopen(filename, "r"));
+}
+
+static void
+unquote(char *str, char *qbuf)
+{
+	register int escaped, inquote, quoted;
+	register char *ip, *bp, *qp;
+	char buf[LINEMAX];
+
+	escaped = inquote = quoted = 0;
+
+	for (ip = str, bp = buf, qp = qbuf; *ip; ip++) {
+		if (!escaped) {
+			if (*ip == '\\') {
+				escaped = 1;
+				quoted ++;
+				continue;
+			} else if (*ip == '"') {
+				inquote = !inquote;
+				quoted ++;
+				continue;
+			}
+		}
+
+		*bp++ = *ip;
+		*qp++ = (inquote || escaped) ? '^' : ' ';
+		escaped = 0;
+	}
+	*bp = '\0';
+	*qp = '\0';
+	if (quoted)
+		(void) strcpy(str, buf);
+}
+
+/*
+ * gets the qualified characters in *p into w, which has space allocated
+ * already
+ */
+static int
+getword(char *w, char *wq, char **p, char **pq, char delim, int wordsz)
+{
+	char *tmp = w;
+	char *tmpq = wq;
+	int count = wordsz;
+
+	if (wordsz <= 0) {
+		return (-1);
+	}
+
+	while ((delim == ' ' ? isspace(**p) : **p == delim) && **pq == ' ') {
+		(*p)++;
+		(*pq)++;
+	}
+
+	while (**p &&
+		!((delim == ' ' ? isspace(**p) : **p == delim) &&
+			**pq == ' ')) {
+		if (--count <= 0) {
+			*tmp = '\0';
+			*tmpq = '\0';
+			dprintf(DBG_INFO, "maximum word length %d exceeded\n",
+				wordsz);
+			return (-1);
+		}
+		*w++ = *(*p)++;
+		*wq++ = *(*pq)++;
+	}
+	*w = '\0';
+	*wq = '\0';
+	return (0);
+}
+
+static int
+parse_mapinfo_file(FILE *fp, nvlist_t **ret_nvlp)
+{
+	int error = 0;
+	nvlist_t *nvl = NULL, *attrs = NULL;
+	char line[LINEMAX], lineq[LINEMAX];
+	char word[MAXPATHLEN+1], wordq[MAXPATHLEN+1];
+	char *lp, *lq;
+
+	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
+		return (EFAULT);
+	}
+
+	while (fgets(line, sizeof (line), fp)) {
+		char *name, *key, *val;
+
+		lp = (char *)line;
+		lq = (char *)lineq;
+		unquote(lp, lq);
+		if ((getword(word, wordq, &lp, &lq, ' ',
+		    sizeof (word)) == -1) || (word[0] == '\0'))
+			continue;
+
+		if (word[0] == '#')
+			continue;
+
+		name = strtok(line, SPC);
+		if (name == NULL)
+			continue;
+
+		(void) dprintf(DBG_INFO, "get a line for %s\n", name);
+		key = strtok(NULL, "=");
+		if (key == NULL) {
+			(void) dprintf(DBG_INFO, "no attributes specified for "
+			    "%s\n", name);
+			continue;
+		}
+
+		attrs = NULL;
+		if (nvlist_alloc(&attrs, NV_UNIQUE_NAME, 0) != 0) {
+			error = EFAULT;
+			goto fail1;
+		}
+
+		while (key && *key) {
+			char *rest;
+			rest = strtok(NULL, "\n");
+			if (rest == NULL) {
+				(void) dprintf(DBG_INFO, "no value for key "
+				    "%s\n", key);
+				break;
+			}
+			if (rest[0] == ';') {
+				val = strdup("devname_null");
+				rest++;
+			} else {
+				val = strtok(rest, ";");
+				rest = strtok(NULL, "");
+			}
+			(void) dprintf(DBG_INFO, "parse_map_info: one entry "
+			    "key=%s val=%s\n", key, val);
+			if (nvlist_add_string(attrs, key, val) != 0) {
+				error = EFAULT;
+				goto fail;
+			}
+
+			key = strtok(rest, "=");
+		}
+		(void) dprintf(DBG_INFO, "parse_map_info: add entry name=%s\n",
+		    name);
+		if (nvlist_add_nvlist(nvl, name, attrs) != 0) {
+			error = EFAULT;
+			goto fail;
+		}
+	}
+
+done:
+	*ret_nvlp = nvl;
+	return (0);
+
+fail:
+	nvlist_free(attrs);
+fail1:
+	nvlist_free(nvl);
+	return (error);
+}
+
+void
+di_devname_print_mapinfo(nvlist_t *nvl)
+{
+	char *name, *key, *val;
+	nvlist_t *attrs;
+	nvpair_t *nvp, *kvp;
+
+	nvp = nvlist_next_nvpair(nvl, NULL);
+	while (nvp) {
+		name = nvpair_name(nvp);
+		(void) nvpair_value_nvlist(nvp, &attrs);
+		(void) printf("name = %s, binding attributes:\n", name);
+		kvp = nvlist_next_nvpair(attrs, NULL);
+		while (kvp) {
+			key = nvpair_name(kvp);
+			(void) nvpair_value_string(kvp, &val);
+			(void) printf("\t%s = %s\n", key, val);
+			kvp = nvlist_next_nvpair(attrs, kvp);
+		}
+		nvp = nvlist_next_nvpair(nvl, nvp);
+	}
+}
+
+static int
+action_mklink(char *target, char *source)
+{
+	(void) dprintf(DBG_INFO, "mklink for source %s target %s\n",
+	    source, target);
+	return (symlink(source, target));
+}
+
+static struct actions {
+	char *key;
+	devname_spec_t spec;
+	int (*action)(char *, char *);
+} actions[] = {
+	{"devices-path", DEVNAME_NS_PATH, action_mklink},
+	{"dev-path", DEVNAME_NS_DEV, action_mklink},
+	{NULL, DEVNAME_NS_NONE, NULL}
+};
+
+static int
+action_on_key(uint_t cmd, char *dir_name, char *devname, nvpair_t *attr,
+    uint32_t  *nsmapcount, char **devfsadm_link, devname_spec_t *devfsadm_spec)
+{
+	int i = 0;
+	int error = 0;
+	char *attrname, *attrval;
+	int len = 0;
+	char *path = NULL;
+
+	attrname = nvpair_name(attr);
+	(void) nvpair_value_string(attr, &attrval);
+	(void) dprintf(DBG_INFO, "key = %s; value = %s\n", attrname, attrval);
+
+	while (actions[i].key) {
+		if (strcmp(actions[i].key, attrname) == 0) {
+			switch (cmd) {
+			case DEVFSADMD_NS_READDIR:
+				len = strlen(dir_name) + strlen(devname) + 2;
+				path = malloc(len);
+				(void) snprintf(path, len, "%s/%s", dir_name,
+				    devname);
+				error = actions[i].action(path, attrval);
+				free(path);
+				if (error) {
+					(void) dprintf(DBG_INFO, "action "
+					    "failed %d\n", error);
+					return (error);
+				} else {
+					(*nsmapcount)++;
+					(void) dprintf(DBG_INFO,
+					    "mapcount %d\n", *nsmapcount);
+				}
+				break;
+			case DEVFSADMD_NS_LOOKUP:
+				*devfsadm_link = strdup(attrval);
+				*devfsadm_spec = actions[i].spec;
+				break;
+			default:
+				break;
+			}
+		}
+		i++;
+	}
+	return (0);
+}
+
+int
+di_devname_action_on_key(nvlist_t *map, uint8_t cmd, char *dir_name, void *hdl)
+{
+	char *name = NULL;
+	nvpair_t *entry;
+	nvlist_t *attrs;
+	int32_t error = 0;
+	uint32_t ns_mapcount = 0;
+	char *devfsadm_link = NULL;
+	devname_spec_t devfsadm_spec = DEVNAME_NS_NONE;
+	sdev_door_res_t *resp;
+
+	entry = nvlist_next_nvpair(map, NULL);
+	while (entry) {
+		nvpair_t *attr;
+		name = nvpair_name(entry);
+		(void) dprintf(DBG_INFO, "di_devname_action_on_key: name %s\n",
+		    name);
+		(void) nvpair_value_nvlist(entry, &attrs);
+
+		attr = nvlist_next_nvpair(attrs, NULL);
+		while (attr) {
+			error = action_on_key(cmd, dir_name, name, attr,
+			    &ns_mapcount, &devfsadm_link, &devfsadm_spec);
+
+			/* do not continue if encountered the first error */
+			if (error) {
+				(void) dprintf(DBG_INFO, "error %d\n", error);
+				return ((int32_t)error);
+			}
+			attr = nvlist_next_nvpair(attrs, attr);
+		}
+		entry = nvlist_next_nvpair(map, entry);
+	}
+
+	resp = (sdev_door_res_t *)hdl;
+	(void) dprintf(DBG_INFO, "cmd is %d\n", cmd);
+	switch (cmd) {
+	case DEVFSADMD_NS_READDIR:
+		resp->ns_rdr_hdl.ns_mapcount = (uint32_t)ns_mapcount;
+		(void) dprintf(DBG_INFO, "mapcount is %d\n", ns_mapcount);
+		break;
+	case DEVFSADMD_NS_LOOKUP:
+		if (devfsadm_link && devfsadm_spec != DEVNAME_NS_NONE) {
+			(void) dprintf(DBG_INFO, "devfsadm_link is %s\n",
+			    devfsadm_link);
+			(void) snprintf(resp->ns_lkp_hdl.devfsadm_link,
+			    strlen(devfsadm_link) + 1, "%s", devfsadm_link);
+			resp->ns_lkp_hdl.devfsadm_spec = devfsadm_spec;
+		} else {
+			(void) dprintf(DBG_INFO, "error out\n");
+			return (1);
+		}
+		break;
+	default:
+		(void) dprintf(DBG_INFO, "error NOTSUP out\n");
+		return (ENOTSUP);
+	}
+
+	return (0);
+}
+
+
+static nvlist_t *
+getent_mapinfo_file(FILE *fp, char *match)
+{
+	nvlist_t *nvl, *attrs;
+	char line[LINEMAX], lineq[LINEMAX];
+	char word[MAXPATHLEN+1], wordq[MAXPATHLEN+1];
+	int count = 0;
+	char *lp, *lq;
+
+	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
+		return (NULL);
+
+	while (fgets(line, sizeof (line), fp)) {
+		char *name, *key, *val;
+
+		if (line[0] == '#')
+			continue;
+
+		dprintf(DBG_INFO, "getent_mapinfo_file: get a line %s\n", line);
+		lp = (char *)line;
+		lq = (char *)lineq;
+		unquote(lp, lq);
+		if ((getword(word, wordq, &lp, &lq, ' ', sizeof (word))
+			== -1) || (word[0] == '\0'))
+			continue;
+
+		name = strtok(line, SPC);
+		if (name == NULL)
+			continue;
+
+		dprintf(DBG_INFO, "macthing with the key %s match %s\n",
+		    name, match);
+		/* bypass the non-related entries */
+		if (strcmp(name, match) != 0)
+			continue;
+
+		/* get a matched entry */
+		key = strtok(NULL, "=");
+		if (key == NULL) {
+			(void) dprintf(DBG_INFO, "no attributes specified "
+			    "for %s\n", name);
+			goto fail1;
+		}
+
+		attrs = NULL;
+		if (nvlist_alloc(&attrs, NV_UNIQUE_NAME, 0) != 0)
+			goto fail1;
+		while (key && *key) {
+			char *rest;
+			rest = strtok(NULL, "\n");
+			if (rest == NULL) {
+				(void) dprintf(DBG_INFO, "no value for key "
+				    "%s\n", key);
+				goto fail;
+			}
+			if (rest[0] == ';') {
+				val = strdup("devname_null");
+				rest++;
+			} else {
+				val = strtok(rest, ";");
+				rest = strtok(NULL, "");
+			}
+			(void) dprintf(DBG_INFO, "found entry %s %s for %s\n",
+			    key, val, name);
+			if (nvlist_add_string(attrs, key, val) != 0)
+				goto fail;
+
+			key = strtok(rest, "=");
+		}
+		(void) dprintf(DBG_INFO, "adding nvlist for %s\n", name);
+		if (nvlist_add_nvlist(nvl, name, attrs) != 0)
+			goto fail;
+		count++;
+		break;
+	}
+
+	if (count == 0)
+		goto fail1;
+
+	return (nvl);
+
+fail:
+	nvlist_free(attrs);
+fail1:
+	nvlist_free(nvl);
+	errno = EFAULT;
+	return (NULL);
+}
+
+static int
+di_devname_getmapent_files(char *key, char *mapname, nvlist_t **map)
+{
+	FILE *fp;
+	int rval = 0;
+	nvlist_t *nvl = NULL;
+
+	fp = open_local_map(mapname);
+	if (fp == NULL)
+		return (1);
+
+	nvl = getent_mapinfo_file(fp, key);
+	if (nvl != NULL) {
+		*map = nvl;
+	} else {
+		rval = errno;
+	}
+	(void) fclose(fp);
+
+	return (rval);
+}
+
+int
+di_devname_get_mapent(char *key, char *mapname, nvlist_t **map)
+{
+	dprintf(DBG_INFO, "di_devname_get_mapent: called for %s in %s\n",
+	    key, mapname);
+
+	return (di_devname_getmapent_files(key, mapname, map));
+
+}
+
+int
+di_devname_get_mapinfo(char *mapname, nvlist_t **maps)
+{
+	dprintf(DBG_INFO, "di_devname_get_mapinfo: called for %s\n", mapname);
+
+	return (di_devname_get_mapinfo_files(mapname, maps));
+}
+
+static void
+debug_print(debug_level_t msglevel, const char *fmt, va_list ap)
+{
+	if (devname_debug < msglevel)
+		return;
+
+	/* Print a distinctive label for error msgs */
+	if (msglevel == DBG_ERR) {
+		(void) fprintf(stderr, "[ERROR]: ");
+	}
+
+	if (msglog == TRUE) {
+		(void) vsyslog(LOG_NOTICE, fmt, ap);
+	} else {
+		(void) vfprintf(stderr, fmt, ap);
+	}
+}
+
+/* ARGSUSED */
+/* PRINTFLIKE2 */
+static void
+dprintf(debug_level_t msglevel, const char *fmt, ...)
+{
+	va_list ap;
+
+	assert(msglevel > 0);
+
+	if (!devname_debug)
+		return;
+
+	va_start(ap, fmt);
+	debug_print(msglevel, fmt, ap);
+	va_end(ap);
+}
+
+
+/*
+ * Private interfaces for non-global /dev profile
+ */
+
+/*
+ * Allocate opaque data structure for passing profile to the kernel for
+ * the given mount point.
+ *
+ * Note that this interface returns an empty, initialized, profile.
+ * It does not return what may have been previously committed.
+ */
+int
+di_prof_init(const char *mountpt, di_prof_t *profp)
+{
+	nvlist_t	*nvl;
+
+	if (nvlist_alloc(&nvl, 0, 0))
+		return (-1);
+
+	if (nvlist_add_string(nvl, SDEV_NVNAME_MOUNTPT, mountpt)) {
+		nvlist_free(nvl);
+		return (-1);
+	}
+
+	*profp = (di_prof_t)nvl;
+	return (0);
+}
+
+/*
+ * Free space allocated by di_prof_init().
+ */
+void
+di_prof_fini(di_prof_t prof)
+{
+	nvlist_free((nvlist_t *)prof);
+}
+
+/*
+ * Sends profile to the kernel.
+ */
+int
+di_prof_commit(di_prof_t prof)
+{
+	char	*buf = NULL;
+	size_t	buflen = 0;
+	int	rv;
+
+	if (nvlist_pack((nvlist_t *)prof, &buf, &buflen, NV_ENCODE_NATIVE, 0))
+		return (-1);
+	rv = modctl(MODDEVNAME, MODDEVNAME_PROFILE, buf, buflen);
+	free(buf);
+	return (rv);
+}
+
+/*
+ * Add a device or directory to profile's include list.
+ *
+ * Note that there is no arbitration between conflicting
+ * include and exclude profile entries, most recent
+ * is the winner.
+ */
+int
+di_prof_add_dev(di_prof_t prof, const char *dev)
+{
+	if (nvlist_add_string((nvlist_t *)prof, SDEV_NVNAME_INCLUDE, dev))
+		return (-1);
+	return (0);
+}
+
+/*
+ * Add a device or directory to profile's exclude list.
+ * This can effectively remove a previously committed device.
+ */
+int
+di_prof_add_exclude(di_prof_t prof, const char *dev)
+{
+	if (nvlist_add_string((nvlist_t *)prof, SDEV_NVNAME_EXCLUDE, dev))
+		return (-1);
+	return (0);
+}
+
+/*
+ * Add a symlink to profile.
+ */
+int
+di_prof_add_symlink(di_prof_t prof, const char *linkname, const char *target)
+{
+	nvlist_t	*nvl = (nvlist_t *)prof;
+	char		*syml[2];
+
+	syml[0] = (char *)linkname;	/* 1st entry must be the symlink */
+	syml[1] = (char *)target;	/* 2nd entry must be the target */
+	if (nvlist_add_string_array(nvl, SDEV_NVNAME_SYMLINK, syml, 2))
+		return (-1);
+	return (0);
+}
+
+/*
+ * Add a name mapping to profile.
+ */
+int
+di_prof_add_map(di_prof_t prof, const char *source, const char *target)
+{
+	nvlist_t	*nvl = (nvlist_t *)prof;
+	char		*map[2];
+
+	map[0] = (char *)source;	/* 1st entry must be the source */
+	map[1] = (char *)target;	/* 2nd entry must be the target */
+	if (nvlist_add_string_array(nvl, SDEV_NVNAME_MAP, map, 2))
+		return (-1);
+	return (0);
+}
--- a/usr/src/lib/libdevinfo/devinfo_devperm.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/lib/libdevinfo/devinfo_devperm.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -116,6 +115,7 @@
 		 * doesn't support ACLs, therefore, we must assume that
 		 * there were no ACLs to remove in the first place.
 		 */
+		err = 0;
 		if (errno != ENOSYS) {
 			err = -1;
 
@@ -377,8 +377,29 @@
 	DIR *dirp;
 	struct dirent *direntp;
 	char errstring[MAX_LINELEN];
+	char *p;
+	regex_t regex;
+	int alwaysmatch = 0;
+	char *match;
+	char *name, *newpath, *remainder_path;
+	finddevhdl_t handle;
+	int find_method;
+
+	/*
+	 * Determine to begin if the search needs to be performed via
+	 * finddev, which returns only persisted names in /dev, or readdir,
+	 * for paths other than /dev.  This use of finddev avoids triggering
+	 * potential implicit reconfig for names noted as managed by
+	 * logindevperm but not present on the system.
+	 */
+	find_method = ((strcmp(path, "/dev") == 0) ||
+	    (strncmp(path, "/dev/", 5) == 0)) ?
+		FLAG_USE_FINDDEV : FLAG_USE_READDIR;
 
 	/* path must be a valid name */
+	if (find_method == FLAG_USE_FINDDEV && !device_exists(path)) {
+		return (-1);
+	}
 	if (stat(path, &stat_buf) == -1) {
 		/*
 		 * ENOENT errors are expected errors when there are
@@ -411,76 +432,89 @@
 		}
 	}
 
-	dirp = opendir(path);
-	if (dirp == NULL) {
-		return (0);
+	if (find_method == FLAG_USE_READDIR) {
+		dirp = opendir(path);
+		if (dirp == NULL)
+			return (0);
 	} else {
-		char *p = strchr(left_to_do, '/');
-		regex_t regex;
-		int alwaysmatch = 0;
-		char *match;
-		char *name, *newpath, *remainder_path;
+		if (finddev_readdir(path, &handle) != 0)
+			return (0);
+	}
+
+	p = strchr(left_to_do, '/');
+	alwaysmatch = 0;
 
-		newpath = (char *)malloc(MAXPATHLEN);
-		if (newpath == NULL) {
-			return (-1);
-		}
-		match = (char *)calloc(MAXPATHLEN, 1);
-		if (match == NULL) {
+	newpath = (char *)malloc(MAXPATHLEN);
+	if (newpath == NULL) {
+		return (-1);
+	}
+	match = (char *)calloc(MAXPATHLEN, 1);
+	if (match == NULL) {
+		free(newpath);
+		return (-1);
+	}
+
+	if (p) {
+		(void) strncpy(match, left_to_do, p - left_to_do);
+	} else {
+		(void) strcpy(match, left_to_do);
+	}
+
+	if (strcmp(match, "*") == 0) {
+		alwaysmatch = 1;
+	} else {
+		if (regcomp(&regex, match, REG_EXTENDED) != 0) {
 			free(newpath);
+			free(match);
 			return (-1);
 		}
-
-		if (p) {
-			(void) strncpy(match, left_to_do, p - left_to_do);
-		} else {
-			(void) strcpy(match, left_to_do);
-		}
+	}
 
-		if (strcmp(match, "*") == 0) {
-			alwaysmatch = 1;
-		} else {
-			if (regcomp(&regex, match, REG_EXTENDED) != 0) {
-				free(newpath);
-				free(match);
-				return (-1);
-			}
-		}
-
-		while ((direntp = readdir(dirp)) != NULL) {
+	for (;;) {
+		if (find_method == FLAG_USE_READDIR) {
+			if ((direntp = readdir(dirp)) == NULL)
+				break;
 			name = direntp->d_name;
 			if ((strcmp(name, ".") == 0) ||
 			    (strcmp(name, "..") == 0))
 				continue;
-
-			if (alwaysmatch ||
-			    regexec(&regex, name, 0, NULL, 0) == 0) {
-				if (strcmp(path, "/") == 0) {
-					(void) snprintf(newpath,
-					    MAXPATHLEN, "%s%s", path, name);
-				} else {
-					(void) snprintf(newpath,
-					    MAXPATHLEN, "%s/%s", path, name);
-				}
+		} else {
+			if ((name = (char *)finddev_next(handle)) == NULL)
+				break;
+		}
 
-				/*
-				 * recurse but adjust what is still left to do
-				 */
-				remainder_path = (p ?
-				    left_to_do + (p - left_to_do) + 1 :
-				    &left_to_do[strlen(left_to_do)]);
-				if (dir_dev_acc(newpath, remainder_path,
-				    uid, gid, mode, line, errmsg)) {
-					err = -1;
-				}
+		if (alwaysmatch ||
+		    regexec(&regex, name, 0, NULL, 0) == 0) {
+			if (strcmp(path, "/") == 0) {
+				(void) snprintf(newpath,
+				    MAXPATHLEN, "%s%s", path, name);
+			} else {
+				(void) snprintf(newpath,
+				    MAXPATHLEN, "%s/%s", path, name);
+			}
+
+			/*
+			 * recurse but adjust what is still left to do
+			 */
+			remainder_path = (p ?
+			    left_to_do + (p - left_to_do) + 1 :
+			    &left_to_do[strlen(left_to_do)]);
+			if (dir_dev_acc(newpath, remainder_path,
+			    uid, gid, mode, line, errmsg)) {
+				err = -1;
 			}
 		}
+	}
+
+	if (find_method == FLAG_USE_READDIR) {
 		(void) closedir(dirp);
-		free(newpath);
-		free(match);
-		if (!alwaysmatch) {
-			regfree(&regex);
-		}
+	} else {
+		finddev_close(handle);
+	}
+	free(newpath);
+	free(match);
+	if (!alwaysmatch) {
+		regfree(&regex);
 	}
 
 	return (err);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libdevinfo/devinfo_finddev.c	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,163 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <thread.h>
+#include <synch.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <dirent.h>
+#include <regex.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <libdevinfo.h>
+#include <sys/modctl.h>
+#include <syslog.h>
+
+#include <assert.h>
+
+
+struct finddevhdl {
+	int	npaths;
+	int	curpath;
+	char	**paths;
+};
+
+
+int
+device_exists(const char *devname)
+{
+	int	rv;
+
+	rv = modctl(MODDEVEXISTS, devname, strlen(devname));
+	return ((rv == 0) ? 1 : 0);
+}
+
+int
+finddev_readdir(const char *dir, finddevhdl_t *handlep)
+{
+	struct finddevhdl	*handle;
+	int			n;
+	int			rv;
+	int64_t			bufsiz;
+	char			*pathlist;
+	char			*p;
+	int			len;
+
+	*handlep = NULL;
+	handle = calloc(1, sizeof (struct finddevhdl));
+	if (handle == NULL)
+		return (ENOMEM);
+
+	handle->npaths = 0;
+	handle->curpath = 0;
+	handle->paths = NULL;
+
+	rv = modctl(MODDEVREADDIR, dir, strlen(dir), NULL, &bufsiz);
+	if (rv != 0) {
+		free(handle);
+		return (rv);
+	}
+
+	for (;;) {
+		assert(bufsiz != 0);
+		if ((pathlist = malloc(bufsiz)) == NULL) {
+			free(handle);
+			return (ENOMEM);
+		}
+
+		rv = modctl(MODDEVREADDIR, dir, strlen(dir),
+		    pathlist, &bufsiz);
+		if (rv == 0) {
+			for (n = 0, p = pathlist;
+			    (len = strlen(p)) > 0; p += len+1) {
+				n++;
+			}
+			handle->npaths = n;
+			handle->paths = calloc(n, sizeof (char *));
+			if (handle->paths == NULL) {
+				free(handle);
+				free(pathlist);
+				return (ENOMEM);
+			}
+			for (n = 0, p = pathlist;
+			    (len = strlen(p)) > 0; p += len+1, n++) {
+				handle->paths[n] = strdup(p);
+				if (handle->paths[n] == NULL) {
+					finddev_close((finddevhdl_t)handle);
+					free(pathlist);
+					return (ENOMEM);
+				}
+			}
+			*handlep = (finddevhdl_t)handle;
+			free(pathlist);
+			return (0);
+		}
+		free(pathlist);
+		switch (errno) {
+		case EAGAIN:
+			break;
+		case ENOENT:
+		default:
+			free(handle);
+			return (errno);
+		}
+	}
+	/*NOTREACHED*/
+}
+
+void
+finddev_close(finddevhdl_t arg)
+{
+	struct finddevhdl *handle = (struct finddevhdl *)arg;
+	int i;
+
+	for (i = 0; i < handle->npaths; i++) {
+		if (handle->paths[i])
+			free(handle->paths[i]);
+	}
+	free(handle->paths);
+	free(handle);
+}
+
+const char *
+finddev_next(finddevhdl_t arg)
+{
+	struct finddevhdl *handle = (struct finddevhdl *)arg;
+	const char *path = NULL;
+
+	if (handle->curpath < handle->npaths) {
+		path = handle->paths[handle->curpath];
+		handle->curpath++;
+	}
+	return (path);
+}
--- a/usr/src/lib/libdevinfo/libdevinfo.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/lib/libdevinfo/libdevinfo.h	Fri Aug 25 17:24:25 2006 -0700
@@ -28,7 +28,12 @@
 
 #pragma ident	"%Z%%M%	%I%	%E% SMI"
 
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
 #include <errno.h>
+#include <libnvpair.h>
 #include <sys/param.h>
 #include <sys/sunddi.h>
 #include <sys/sunmdi.h>
@@ -37,10 +42,6 @@
 #include <sys/devinfo_impl.h>
 #include <limits.h>
 
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
 /*
  * flags for di_walk_node
  */
@@ -394,6 +395,42 @@
 extern int di_dli_openw(char *);
 extern void di_dli_close(int);
 
+/*
+ * Private interface for parsing devname binding info
+ */
+extern void di_devname_print_mapinfo(nvlist_t *);
+extern int di_devname_get_mapinfo(char *, nvlist_t **);
+extern int di_devname_get_mapent(char *, char *, nvlist_t **);
+extern int di_devname_action_on_key(nvlist_t *, uint8_t, char *, void *);
+
+/*
+ * finddev - alternate readdir to discover only /dev persisted device names
+ */
+typedef struct __finddevhdl *finddevhdl_t;
+
+extern int		device_exists(const char *);
+extern int		finddev_readdir(const char *, finddevhdl_t *);
+extern void		finddev_close(finddevhdl_t);
+extern const char	*finddev_next(finddevhdl_t);
+
+/* For interfaces implementing search either by readdir or finddev */
+#define	FLAG_USE_READDIR	0
+#define	FLAG_USE_FINDDEV	1
+
+
+/*
+ * Private interfaces for non-global /dev profile
+ */
+typedef struct __di_prof	*di_prof_t;
+
+extern int	di_prof_init(const char *mountpt, di_prof_t *);
+extern void	di_prof_fini(di_prof_t);
+extern int	di_prof_commit(di_prof_t);
+extern int	di_prof_add_dev(di_prof_t, const char *);
+extern int	di_prof_add_exclude(di_prof_t, const char *);
+extern int	di_prof_add_symlink(di_prof_t, const char *, const char *);
+extern int	di_prof_add_map(di_prof_t, const char *, const char *);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/lib/libdevinfo/mapfile-vers	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/lib/libdevinfo/mapfile-vers	Fri Aug 25 17:24:25 2006 -0700
@@ -145,6 +145,10 @@
 	di_devlink_walk;
 	di_devperm_login;
 	di_devperm_logout;
+	di_devname_get_mapinfo;
+	di_devname_get_mapent;
+	di_devname_action_on_key;
+	di_devname_print_mapinfo;
 	di_driver_private_data;
 	di_init_driver;
 	di_init_impl;
@@ -173,6 +177,13 @@
 	di_path_state;
 	di_phci_first_node;
 	di_phci_next_node;
+	di_prof_add_dev;
+	di_prof_add_exclude;
+	di_prof_add_map;
+	di_prof_add_symlink;
+	di_prof_commit;
+	di_prof_init;
+	di_prof_fini;
 	di_prop_drv_next;
 	di_prop_global_next;
 	di_prop_hw_next;
@@ -184,6 +195,10 @@
 	di_dli_openr;
 	di_dli_openw;
 	di_dli_close;
+	device_exists;
+	finddev_readdir;
+	finddev_close;
+	finddev_next;
     local:
 	*;
 };
--- a/usr/src/lib/libtsol/common/label.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/lib/libtsol/common/label.h	Fri Aug 25 17:24:25 2006 -0700
@@ -235,6 +235,14 @@
 extern int	userdefs(m_label_t *, m_label_t *);
 extern int	zonecopy(m_label_t *, char *, char *, char *, int);
 
+#ifdef DEBUG
+/* testing hook: see devfsadm.c, mkdevalloc.c and allocate.c */
+#define	is_system_labeled_debug(statbufp)	\
+	((stat("/ALLOCATE_FORCE_LABEL", (statbufp)) == 0) ? 1 : 0)
+#else	/* DEBUG */
+#define	is_system_labeled_debug(statbufp)	0
+#endif	/* DEBUG */
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/lib/libzonecfg/common/libzonecfg.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/lib/libzonecfg/common/libzonecfg.c	Fri Aug 25 17:24:25 2006 -0700
@@ -70,7 +70,6 @@
 #define	DTD_ELEM_ATTR		(const xmlChar *) "attr"
 #define	DTD_ELEM_COMMENT	(const xmlChar *) "comment"
 #define	DTD_ELEM_DEVICE		(const xmlChar *) "device"
-#define	DTD_ELEM_DELETEDDEVICE	(const xmlChar *) "deleted-device"
 #define	DTD_ELEM_FS		(const xmlChar *) "filesystem"
 #define	DTD_ELEM_FSOPTION	(const xmlChar *) "fsoption"
 #define	DTD_ELEM_IPD		(const xmlChar *) "inherited-pkg-dir"
@@ -2142,9 +2141,8 @@
 static int
 zonecfg_delete_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
 {
-	xmlNodePtr newnode, cur = handle->zone_dh_cur;
+	xmlNodePtr cur = handle->zone_dh_cur;
 	int match_match;
-	int err;
 
 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
 		if (xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
@@ -2154,19 +2152,6 @@
 		    tabptr->zone_dev_match);
 
 		if (match_match) {
-			/*
-			 * Since we're succesfully deleting (or modifying)
-			 * a device entry, we need to do device node cleanup
-			 * on the next zone bootup, so we leave behind a
-			 * historical record for zoneadmd to consume.
-			 */
-			newnode = xmlNewTextChild(handle->zone_dh_top, NULL,
-			    DTD_ELEM_DELETEDDEVICE, NULL);
-
-			if ((err = newprop(newnode, DTD_ATTR_MATCH,
-			    tabptr->zone_dev_match)) != Z_OK)
-				return (err);
-
 			xmlUnlinkNode(cur);
 			xmlFreeNode(cur);
 			return (Z_OK);
@@ -2193,43 +2178,6 @@
 }
 
 int
-zonecfg_clear_deldevs(zone_dochandle_t handle)
-{
-	xmlNodePtr cur;
-	int err;
-
-	if ((err = operation_prep(handle)) != Z_OK)
-		return (err);
-
-	cur = handle->zone_dh_cur;
-	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
-		if (xmlStrcmp(cur->name, DTD_ELEM_DELETEDDEVICE) != 0)
-			continue;
-
-		xmlUnlinkNode(cur);
-		xmlFreeNode(cur);
-	}
-	return (Z_OK);
-}
-
-int
-zonecfg_has_deldevs(zone_dochandle_t handle)
-{
-	xmlNodePtr cur;
-	int err;
-
-	if ((err = operation_prep(handle)) != Z_OK)
-		return (err);
-
-	cur = handle->zone_dh_cur;
-	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
-		if (xmlStrcmp(cur->name, DTD_ELEM_DELETEDDEVICE) == 0)
-			return (Z_OK);
-	}
-	return (Z_NO_ENTRY);
-}
-
-int
 zonecfg_modify_dev(
 	zone_dochandle_t handle,
 	struct zone_devtab *oldtabptr,
@@ -2388,54 +2336,6 @@
 }
 
 /*
- * This is the set of devices which must be present in every zone.  Users
- * can augment this list with additional device rules in their zone
- * configuration, but at present cannot remove any of the this set of
- * standard devices.  All matching is done by /dev pathname (the "/dev"
- * part is implicit.  Try to keep rules which match a large number of
- * devices (like the pts rule) first.
- */
-static const char *standard_devs[] = {
-	"pts/*",
-	"ptmx",
-	"random",
-	"urandom",
-	"poll",
-	"pool",
-	"kstat",
-	"zero",
-	"null",
-	"crypto",
-	"cryptoadm",
-	"ticots",
-	"ticotsord",
-	"ticlts",
-	"lo0",
-	"lo1",
-	"lo2",
-	"lo3",
-	"sad/user",
-	"tty",
-	"logindmux",
-	"log",
-	"conslog",
-	"arp",
-	"tcp",
-	"tcp6",
-	"udp",
-	"udp6",
-	"sysevent",
-#ifdef __sparc
-	"openprom",
-#endif
-	"cpu/self/cpuid",
-	"dtrace/*",
-	"dtrace/provider/*",
-	"zfs",
-	NULL
-};
-
-/*
  * This function finds everything mounted under a zone's rootpath.
  * This returns the number of mounts under rootpath, or -1 on error.
  * callback is called once per mount found with the first argument
@@ -2493,174 +2393,6 @@
 	return (rv);
 }
 
-/*
- * This routine is used to determine if a given device should appear in the
- * zone represented by 'handle'.  First it consults the list of "standard"
- * zone devices.  Then it scans the user-supplied device entries.
- */
-int
-zonecfg_match_dev(zone_dochandle_t handle, const char *devpath,
-    struct zone_devtab *out_match)
-{
-	int err;
-	boolean_t found = B_FALSE;
-	char match[MAXPATHLEN];
-	const char **stdmatch;
-	xmlNodePtr cur;
-
-	if (handle == NULL || devpath == NULL)
-		return (Z_INVAL);
-
-	/*
-	 * Check the "standard" devices which we require to be present.
-	 */
-	for (stdmatch = &standard_devs[0]; *stdmatch != NULL; stdmatch++) {
-		/*
-		 * fnmatch gives us simple but powerful shell-style matching.
-		 */
-		if (fnmatch(*stdmatch, devpath, FNM_PATHNAME) == 0) {
-			if (!out_match)
-				return (Z_OK);
-			(void) snprintf(out_match->zone_dev_match,
-			    sizeof (out_match->zone_dev_match),
-			    "/dev/%s", *stdmatch);
-			return (Z_OK);
-		}
-	}
-
-	/*
-	 * We got no hits in the set of standard devices.  On to the user
-	 * supplied ones.
-	 */
-	if ((err = operation_prep(handle)) != Z_OK) {
-		handle->zone_dh_cur = NULL;
-		return (err);
-	}
-
-	cur = handle->zone_dh_cur;
-	cur = cur->xmlChildrenNode;
-	if (cur == NULL)
-		return (Z_NO_ENTRY);
-	handle->zone_dh_cur = cur;
-
-	for (; cur != NULL; cur = cur->next) {
-		char *m;
-		if (xmlStrcmp(cur->name, DTD_ELEM_DEVICE) != 0)
-			continue;
-		if ((err = fetchprop(cur, DTD_ATTR_MATCH, match,
-		    sizeof (match))) != Z_OK) {
-			handle->zone_dh_cur = handle->zone_dh_top;
-			return (err);
-		}
-		m = match;
-		/*
-		 * fnmatch gives us simple but powerful shell-style matching;
-		 * but first, we need to strip out /dev/ from the matching rule.
-		 */
-		if (strncmp(m, "/dev/", 5) == 0)
-			m += 5;
-
-		if (fnmatch(m, devpath, FNM_PATHNAME) == 0) {
-			found = B_TRUE;
-			break;
-		}
-	}
-
-	if (!found)
-		return (Z_NO_ENTRY);
-
-	if (!out_match)
-		return (Z_OK);
-
-	(void) strlcpy(out_match->zone_dev_match, match,
-	    sizeof (out_match->zone_dev_match));
-	return (Z_OK);
-}
-
-/*
- * This routine answers the question: do we think <devpath> should be
- * deleted during this zone's bootup?
- *
- * The criteria are:
- * 	- Is there a matching rule for devpath?  If yes, then NO.
- * 	- Is this a CHR or BLK device node?  If no, then NO.
- *      - Is there a deleted device entry which matches devpath?  Then, YES
- *      - Else NO
- */
-int
-zonecfg_should_deldev(zone_dochandle_t handle, const char *devpath,
-    boolean_t *del)
-{
-	int err;
-	char match[MAXPATHLEN];
-	xmlNodePtr cur;
-	struct stat st;
-	char fullpath[MAXPATHLEN];
-	char zonepath[MAXPATHLEN];
-
-	if (handle == NULL || devpath == NULL || del == NULL)
-		return (Z_INVAL);
-
-	*del = B_FALSE;
-
-	/*
-	 * If a matching rule exists for this device, then leave it alone.
-	 */
-	if (zonecfg_match_dev(handle, devpath, NULL) == Z_OK)
-		return (Z_OK);
-
-	/*
-	 * lstat it.  If it's a regular file, a directory, a link or
-	 * something else miscellaneous, we'll be cautious and not
-	 * touch it.
-	 */
-	if ((err = zonecfg_get_zonepath(handle, zonepath,
-	    sizeof (zonepath))) != Z_OK)
-		return (err);
-
-	(void) snprintf(fullpath, sizeof (fullpath), "%s/dev/%s", zonepath,
-	    devpath);
-	if (lstat(fullpath, &st) == -1 ||
-	    (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)))
-		return (Z_OK);
-
-	if ((err = operation_prep(handle)) != Z_OK) {
-		handle->zone_dh_cur = NULL;
-		return (err);
-	}
-
-	cur = handle->zone_dh_cur;
-	cur = cur->xmlChildrenNode;
-	if (cur == NULL)
-		return (Z_NO_ENTRY);
-	handle->zone_dh_cur = cur;
-
-	for (; cur != NULL; cur = cur->next) {
-		char *m;
-		if (xmlStrcmp(cur->name, DTD_ELEM_DELETEDDEVICE) != 0)
-			continue;
-		if ((err = fetchprop(cur, DTD_ATTR_MATCH, match,
-		    sizeof (match))) != Z_OK) {
-			handle->zone_dh_cur = handle->zone_dh_top;
-			return (err);
-		}
-		m = match;
-		/*
-		 * fnmatch gives us simple but powerful shell-style matching;
-		 * but first, we need to strip out /dev/ from the matching rule.
-		 */
-		if (strncmp(m, "/dev/", 5) == 0)
-			m += 5;
-
-		if (fnmatch(m, devpath, FNM_PATHNAME) == 0) {
-			*del = B_TRUE;
-			break;
-		}
-	}
-
-	return (Z_OK);
-}
-
 int
 zonecfg_lookup_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
 {
@@ -3967,6 +3699,34 @@
 	return (Z_OK);
 }
 
+/*
+ * Return the appropriate root for the active /dev.
+ * For normal zone, the path is $ZONEPATH/root;
+ * for scratch zone, the dev path is $ZONEPATH/lu.
+ */
+int
+zone_get_devroot(char *zone_name, char *devroot, size_t rp_sz)
+{
+	int err;
+	char *suffix;
+	zone_state_t state;
+
+	/* This function makes sense for non-global zones only. */
+	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0)
+		return (Z_BOGUS_ZONE_NAME);
+	if ((err = zone_get_zonepath(zone_name, devroot, rp_sz)) != Z_OK)
+		return (err);
+
+	if (zone_get_state(zone_name, &state) == Z_OK &&
+	    state == ZONE_STATE_MOUNTED)
+		suffix = "/lu";
+	else
+		suffix = "/root";
+	if (strlcat(devroot, suffix, rp_sz) >= rp_sz)
+		return (Z_TOO_BIG);
+	return (Z_OK);
+}
+
 static zone_state_t
 kernel_state_to_user_state(zoneid_t zoneid, zone_status_t kernel_state)
 {
--- a/usr/src/lib/libzonecfg/common/mapfile-vers	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/lib/libzonecfg/common/mapfile-vers	Fri Aug 25 17:24:25 2006 -0700
@@ -45,7 +45,6 @@
 	zonecfg_add_scratch;
 	zonecfg_attach_manifest;
 	zonecfg_check_handle;
-	zonecfg_clear_deldevs;
 	zonecfg_close_scratch;
 	zonecfg_construct_rctlblk;
 	zonecfg_create_snapshot;
@@ -109,7 +108,6 @@
 	zonecfg_get_template_handle;
 	zonecfg_get_uuid;
 	zonecfg_get_zonepath;
-	zonecfg_has_deldevs;
 	zonecfg_in_alt_root;
 	zonecfg_init_handle;
 	zonecfg_is_rctl;
@@ -122,7 +120,6 @@
 	zonecfg_lookup_ipd;
 	zonecfg_lookup_nwif;
 	zonecfg_lookup_rctl;
-	zonecfg_match_dev;
 	zonecfg_modify_attr;
 	zonecfg_modify_dev;
 	zonecfg_modify_ds;
@@ -159,7 +156,6 @@
 	zonecfg_setrctlent;
 	zonecfg_set_root;
 	zonecfg_set_zonepath;
-	zonecfg_should_deldev;
 	zonecfg_strerror;
 	zonecfg_validate_zonename;
 	zonecfg_valid_fs_type;
@@ -167,6 +163,7 @@
 	zonecfg_valid_rctl;
 	zonecfg_valid_rctlblk;
 	zonecfg_valid_rctlname;
+	zone_get_devroot;
 	zone_get_id;
 	zone_get_rootpath;
 	zone_get_state;
--- a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1	Fri Aug 25 17:24:25 2006 -0700
@@ -52,16 +52,16 @@
 <!ATTLIST device	match		CDATA #REQUIRED>
 
 <!--
-	The deleted-device element denotes a used-to-be device element.
-	We keep track of device elements which the user has deleted or
-	modified, and make an attempt to cleanse /dev of associated entries
-	at next zone boot.
+	Historically, the deleted-device element denoted a used-to-be
+	device element.  This was used to keep track of device elements
+	deleted or modified by the user, and to cleanse /dev of such
+	entries at next zone boot.
 
-	The 'devnames' project will ultimately obsolete the need for this
-	functionality, but this element MUST remain in perpetuity, since
-	it is possible that zones crossing from pre-devnames to post-devnames
-	bits could carry a deleted-device element, and would therefore fail
-	XML validation if this were removed
+	With the ability to now configure devices dynamically, this
+	requirement no longer exists, but this element MUST remain in
+	perpetuity, since it is possible that an upgraded zone could
+	carry a deleted-device element, and would therefore fail XML
+	validation if removed
 -->
 <!ELEMENT deleted-device	EMPTY>
 
--- a/usr/src/pkgdefs/SUNWckr/prototype_com	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/pkgdefs/SUNWckr/prototype_com	Fri Aug 25 17:24:25 2006 -0700
@@ -139,3 +139,4 @@
 f manifest var/svc/manifest/system/fmd.xml 0444 root sys
 f manifest var/svc/manifest/system/intrd.xml 0444 root sys
 f manifest var/svc/manifest/system/scheduler.xml 0444 root sys
+d none kernel/devname 755 root sys
--- a/usr/src/pkgdefs/SUNWckr/prototype_i386	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/pkgdefs/SUNWckr/prototype_i386	Fri Aug 25 17:24:25 2006 -0700
@@ -57,6 +57,7 @@
 f none kernel/crypto/sha2 755 root sys
 f none kernel/crypto/swrand 755 root sys
 f none kernel/dacf/consconfig_dacf 755 root sys
+f none kernel/devname/sdev_nsconfig_mod 755 root sys
 f none kernel/drv/aggr 755 root sys
 f none kernel/drv/arp 755 root sys
 f none kernel/drv/bl 755 root sys
@@ -132,6 +133,7 @@
 f none kernel/fs/cachefs 755 root sys
 f none kernel/fs/ctfs 755 root sys
 f none kernel/fs/devfs 755 root sys
+f none kernel/fs/dev 755 root sys
 f none kernel/fs/fifofs 755 root sys
 f none kernel/fs/hsfs 755 root sys
 f none kernel/fs/lofs 755 root sys
@@ -234,6 +236,8 @@
 f none kernel/crypto/amd64/swrand 755 root sys
 d none kernel/dacf/amd64 755 root sys
 f none kernel/dacf/amd64/consconfig_dacf 755 root sys
+d none kernel/devname/amd64 755 root sys
+f none kernel/devname/amd64/sdev_nsconfig_mod 755 root sys
 d none kernel/drv/amd64 755 root sys
 f none kernel/drv/amd64/aggr 755 root sys
 f none kernel/drv/amd64/arp 755 root sys
@@ -303,6 +307,7 @@
 f none kernel/fs/amd64/autofs 755 root sys
 f none kernel/fs/amd64/cachefs 755 root sys
 f none kernel/fs/amd64/ctfs 755 root sys
+f none kernel/fs/amd64/dev 755 root sys
 f none kernel/fs/amd64/devfs 755 root sys
 f none kernel/fs/amd64/fifofs 755 root sys
 f none kernel/fs/amd64/hsfs 755 root sys
--- a/usr/src/pkgdefs/SUNWckr/prototype_sparc	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc	Fri Aug 25 17:24:25 2006 -0700
@@ -140,6 +140,7 @@
 f none kernel/fs/sparcv9/cachefs 755 root sys
 f none kernel/fs/sparcv9/ctfs 755 root sys
 f none kernel/fs/sparcv9/devfs 755 root sys
+f none kernel/fs/sparcv9/dev 755 root sys
 f none kernel/fs/sparcv9/fifofs 755 root sys
 f none kernel/fs/sparcv9/hsfs 755 root sys
 f none kernel/fs/sparcv9/lofs 755 root sys
@@ -233,3 +234,5 @@
 l none kernel/sys/sparcv9/rpcmod=../../../kernel/strmod/sparcv9/rpcmod
 f none kernel/sys/sparcv9/semsys 755 root sys
 f none kernel/sys/sparcv9/shmsys 755 root sys
+d none kernel/devname/sparcv9 755 root sys
+f none kernel/devname/sparcv9/sdev_nsconfig_mod 755 root sys
--- a/usr/src/pkgdefs/SUNWcsr/prototype_com	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/pkgdefs/SUNWcsr/prototype_com	Fri Aug 25 17:24:25 2006 -0700
@@ -146,6 +146,8 @@
 f none etc/fs/hsfs/mount 555 root bin
 d none etc/fs/ufs 755 root sys
 f none etc/fs/ufs/mount 555 root bin
+d none etc/fs/dev 755 root sys
+f none etc/fs/dev/mount 555 root bin
 s none etc/fsck=../usr/sbin/fsck
 s none etc/fsdb=../usr/sbin/fsdb
 s none etc/fstyp=../usr/sbin/fstyp
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/pkgdefs/SUNWcsu/prototype_com	Fri Aug 25 17:24:25 2006 -0700
@@ -382,6 +382,8 @@
 f none usr/lib/fs/cachefs/unshare 555 root bin
 d none usr/lib/fs/ctfs 755 root sys
 f none usr/lib/fs/ctfs/mount 555 root bin
+d none usr/lib/fs/dev 755 root sys
+s none usr/lib/fs/dev/mount=../../../../etc/fs/dev/mount
 d none usr/lib/fs/fd 755 root sys
 f none usr/lib/fs/fd/mount 555 root bin
 d none usr/lib/fs/hsfs 755 root sys
@@ -583,7 +585,6 @@
 f none usr/lib/pci/pcidr 555 root bin
 f none usr/lib/pci/pcidr_plugin.so 755 root bin
 f none usr/lib/platexec 555 root bin
-f none usr/lib/pt_chmod 4511 root bin
 d none usr/lib/rcm 755 root bin
 d none usr/lib/rcm/modules 755 root bin
 f none usr/lib/rcm/modules/SUNW_cluster_rcm.so 555 root bin
--- a/usr/src/pkgdefs/SUNWhea/prototype_com	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/pkgdefs/SUNWhea/prototype_com	Fri Aug 25 17:24:25 2006 -0700
@@ -696,6 +696,8 @@
 f none usr/include/sys/fs/cachefs_dlog.h 644 root bin
 f none usr/include/sys/fs/cachefs_ioctl.h 644 root bin
 f none usr/include/sys/fs/dv_node.h 644 root bin
+f none usr/include/sys/fs/sdev_node.h 644 root bin
+f none usr/include/sys/fs/sdev_impl.h 644 root bin
 f none usr/include/sys/fs/fifonode.h 644 root bin
 f none usr/include/sys/fs/hsfs_isospec.h 644 root bin
 f none usr/include/sys/fs/hsfs_node.h 644 root bin
--- a/usr/src/tools/scripts/bfu.sh	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/tools/scripts/bfu.sh	Fri Aug 25 17:24:25 2006 -0700
@@ -5151,6 +5151,11 @@
 	    done
 	fi;
 
+	# Remove pt_chmod - obsoleted by new /dev filesystem
+	if [ $zone = global ]; then
+	   rm -f $usr/lib/pt_chmod
+	fi
+
 	if [ $RM_32BIT_KERNEL -eq 1 -a $zone = global ];
 	then
 	    print "Removing 32-bit commands and kernel binaries ... \c";
--- a/usr/src/ucbcmd/ucblinks/ucblinks.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/ucbcmd/ucblinks/ucblinks.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -896,8 +895,10 @@
 	struct devices_ent **pdep;
 	struct devices_ent *dep;
 	int i;
+	char *root_etc;
 
-	link_handle = di_devlink_open(rootdir, 0);
+	root_etc = root_name("/etc");
+	link_handle = di_devlink_open(root_etc, 0);
 
 	pdep = devices_list;
 	for (i = 0; i < num_devices_ents; i++) {
--- a/usr/src/uts/Makefile.targ	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/Makefile.targ	Fri Aug 25 17:24:25 2006 -0700
@@ -194,6 +194,9 @@
 $(ROOT_MAC_DIR)/%:	$(OBJS_DIR)/% $(ROOT_MOD_DIR) $(ROOT_MAC_DIR) FRC
 	$(INS.file)
 
+$(ROOT_DEVNAME_DIR)/%:	$(OBJS_DIR)/% $(ROOT_DEVNAME_DIR) FRC
+	$(INS.file)
+
 $(USR_DRV_DIR)/%:	$(OBJS_DIR)/% $(USR_DRV_DIR) FRC
 	$(INS.file)
 
@@ -221,6 +224,9 @@
 $(USR_PCBE_DIR)/%:	$(OBJS_DIR)/% $(USR_PCBE_DIR) FRC
 	$(INS.file)
 
+$(USR_DEVNAME_DIR)/%:	$(OBJS_DIR)/% $(USR_DEVNAME_DIR) FRC
+	$(INS.file)
+
 include $(SRC)/Makefile.psm.targ
 
 #
--- a/usr/src/uts/Makefile.uts	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/Makefile.uts	Fri Aug 25 17:24:25 2006 -0700
@@ -456,6 +456,7 @@
 ROOT_DACF_DIR_32	= $(ROOT_MOD_DIR)/dacf
 ROOT_CRYPTO_DIR_32	= $(ROOT_MOD_DIR)/crypto
 ROOT_MAC_DIR_32		= $(ROOT_MOD_DIR)/mac
+ROOT_DEVNAME_DIR_32	= $(ROOT_MOD_DIR)/devname
 
 ROOT_KERN_DIR_64	= $(ROOT_MOD_DIR)/$(SUBDIR64)
 ROOT_DRV_DIR_64		= $(ROOT_MOD_DIR)/drv/$(SUBDIR64)
@@ -476,6 +477,7 @@
 ROOT_DACF_DIR_64	= $(ROOT_MOD_DIR)/dacf/$(SUBDIR64)
 ROOT_CRYPTO_DIR_64	= $(ROOT_MOD_DIR)/crypto/$(SUBDIR64)
 ROOT_MAC_DIR_64		= $(ROOT_MOD_DIR)/mac/$(SUBDIR64)
+ROOT_DEVNAME_DIR_64	= $(ROOT_MOD_DIR)/devname/$(SUBDIR64)
 
 ROOT_KERN_DIR		= $(ROOT_KERN_DIR_$(CLASS))
 ROOT_DRV_DIR		= $(ROOT_DRV_DIR_$(CLASS))
@@ -496,6 +498,7 @@
 ROOT_DACF_DIR		= $(ROOT_DACF_DIR_$(CLASS))
 ROOT_CRYPTO_DIR		= $(ROOT_CRYPTO_DIR_$(CLASS))
 ROOT_MAC_DIR		= $(ROOT_MAC_DIR_$(CLASS))
+ROOT_DEVNAME_DIR	= $(ROOT_DEVNAME_DIR_$(CLASS))
 
 ROOT_MOD_DIRS_32	= $(ROOT_DRV_DIR_32) $(ROOT_EXEC_DIR_32)
 ROOT_MOD_DIRS_32	+= $(ROOT_DTRACE_DIR_32)
@@ -507,6 +510,7 @@
 ROOT_MOD_DIRS_32	+= $(ROOT_CPU_DIR_32) $(ROOT_FONT_DIR_32)
 ROOT_MOD_DIRS_32	+= $(ROOT_TOD_DIR_32) $(ROOT_DACF_DIR_32)
 ROOT_MOD_DIRS_32	+= $(ROOT_CRYPTO_DIR_32) $(ROOT_MAC_DIR_32)
+ROOT_MOD_DIRS_32	+= $(ROOT_DEVNAME_DIR_32)
 
 USR_MOD_DIR		= $(ROOT)/usr/kernel
 
@@ -519,6 +523,7 @@
 USR_MISC_DIR_32		= $(USR_MOD_DIR)/misc
 USR_DACF_DIR_32		= $(USR_MOD_DIR)/dacf
 USR_PCBE_DIR_32		= $(USR_MOD_DIR)/pcbe
+USR_DEVNAME_DIR_32	= $(USR_MOD_DIR)/devname
 
 USR_DRV_DIR_64		= $(USR_MOD_DIR)/drv/$(SUBDIR64)
 USR_EXEC_DIR_64		= $(USR_MOD_DIR)/exec/$(SUBDIR64)
@@ -529,6 +534,7 @@
 USR_MISC_DIR_64		= $(USR_MOD_DIR)/misc/$(SUBDIR64)
 USR_DACF_DIR_64		= $(USR_MOD_DIR)/dacf/$(SUBDIR64)
 USR_PCBE_DIR_64		= $(USR_MOD_DIR)/pcbe/$(SUBDIR64)
+USR_DEVNAME_DIR_64	= $(USR_MOD_DIR)/devname/$(SUBDIR64)
 
 USR_DRV_DIR		= $(USR_DRV_DIR_$(CLASS))
 USR_EXEC_DIR		= $(USR_EXEC_DIR_$(CLASS))
@@ -539,12 +545,13 @@
 USR_MISC_DIR		= $(USR_MISC_DIR_$(CLASS))
 USR_DACF_DIR		= $(USR_DACF_DIR_$(CLASS))
 USR_PCBE_DIR		= $(USR_PCBE_DIR_$(CLASS))
+USR_DEVNAME_DIR		= $(USR_DEVNAME_DIR_$(CLASS))
 
 USR_MOD_DIRS_32		= $(USR_DRV_DIR_32) $(USR_EXEC_DIR_32)
 USR_MOD_DIRS_32		+= $(USR_FS_DIR_32) $(USR_SCHED_DIR_32)
 USR_MOD_DIRS_32		+= $(USR_STRMOD_DIR_32) $(USR_SYS_DIR_32)
 USR_MOD_DIRS_32		+= $(USR_MISC_DIR_32) $(USR_DACF_DIR_32)
-USR_MOD_DIRS_32		+= $(USR_PCBE_DIR_32)
+USR_MOD_DIRS_32		+= $(USR_PCBE_DIR_32) $(USR_DEVNAME_DIR_32)
 
 #
 #
@@ -584,7 +591,8 @@
 	  $(MACH_KMODS) $(CPU_KMODS) $(GENUNIX_KMODS) \
 	  $(GSS_KMODS) $(MMU_KMODS) $(DACF_KMODS) $(EXPORT_KMODS) \
 	  $(IPP_KMODS) $(CRYPTO_KMODS) $(CRYPTO_EK_KMODS) $(PCBE_KMODS) \
-	  $(DRV_KMODS_$(CLASS)) $(MISC_KMODS_$(CLASS)) $(MAC_KMODS)
+	  $(DRV_KMODS_$(CLASS)) $(MISC_KMODS_$(CLASS)) $(MAC_KMODS) \
+	  $(DEVNAME_KMODS)
 
 $(CLOSED_BUILD)CLOSED_KMODS = $(CLOSED_DRV_KMODS) $(CLOSED_TOD_KMODS) \
 	$(CLOSED_MISC_KMODS) \
@@ -593,8 +601,9 @@
 LINT_KMODS = $(DRV_KMODS) $(EXEC_KMODS) $(FS_KMODS) $(SCHED_KMODS) \
 	  $(TOD_KMODS) $(STRMOD_KMODS) $(SYS_KMODS) $(MISC_KMODS) \
 	  $(MACH_KMODS) $(GSS_KMODS) $(DACF_KMODS) $(IPP_KMODS) \
-	  $(CRYPTO_KMODS) $(PCBE_KMODS) \
-	  $(DRV_KMODS_$(CLASS)) $(MISC_KMODS_$(CLASS)) $(MAC_KMODS)
+	  $(CRYPTO_KMODS) $(PCBE_KMODS) $(DEVNAME_KMODS) \
+	  $(DRV_KMODS_$(CLASS)) $(MISC_KMODS_$(CLASS)) $(MAC_KMODS) \
+	  $(DEVNAME_KMODS)
 
 $(CLOSED_BUILD)CLOSED_LINT_KMODS = $(CLOSED_DRV_KMODS) $(CLOSED_TOD_KMODS) \
 	  $(CLOSED_MISC_KMODS) $(CLOSED_DRV_KMODS_$(CLASS))
--- a/usr/src/uts/common/Makefile.files	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/Makefile.files	Fri Aug 25 17:24:25 2006 -0700
@@ -716,6 +716,8 @@
 
 SI3124_OBJS += si3124.o
 
+NSCONFIG_DEVNAME_OBJS += sdev_nsconfig_mod.o
+
 PCIIDE_OBJS += pci-ide.o
 
 PCEPP_OBJS += pcepp.o
@@ -753,14 +755,17 @@
 		cachefs_subr.o		cachefs_vfsops.o \
 		cachefs_vnops.o
 
+DEVFS_OBJS +=	devfs_subr.o	devfs_vfsops.o	devfs_vnops.o
+
+DEV_OBJS  +=	sdev_subr.o	sdev_vfsops.o	sdev_vnops.o	\
+		sdev_ptsops.o	sdev_comm.o	sdev_profile.o	sdev_ncache.o
+
 CTFS_OBJS +=	ctfs_all.o ctfs_cdir.o ctfs_ctl.o ctfs_event.o \
 		ctfs_latest.o ctfs_root.o ctfs_sym.o ctfs_tdir.o ctfs_tmpl.o
 
 OBJFS_OBJS +=	objfs_vfs.o	objfs_root.o	objfs_common.o \
 		objfs_odir.o	objfs_data.o
 
-DEVFS_OBJS +=	devfs_subr.o	devfs_vfsops.o	devfs_vnops.o
-
 FDFS_OBJS +=	fdops.o
 
 FIFO_OBJS +=	fifosubr.o	fifovnops.o
--- a/usr/src/uts/common/Makefile.rules	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/Makefile.rules	Fri Aug 25 17:24:25 2006 -0700
@@ -170,6 +170,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/fs/devfs/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/fs/ctfs/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -178,7 +182,7 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
-$(OBJS_DIR)/%.o:		$(UTSBASE)/common/fs/devfs/%.c
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/fs/dev/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
@@ -987,6 +991,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/fs/devfs/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/fs/dev/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/fs/fd/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- a/usr/src/uts/common/cpr/cpr_misc.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/cpr/cpr_misc.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -1069,7 +1068,7 @@
 	char **cpp;
 	static char *cpr_writeok_fss[] = {
 		"autofs", "devfs", "fd", "lofs", "mntfs", "namefs", "nfs",
-		"proc", "tmpfs", "ctfs", "objfs", NULL
+		"proc", "tmpfs", "ctfs", "objfs", "dev", NULL
 	};
 
 	vfs_list_read_lock();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/fs/dev/sdev_comm.c	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,749 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * routines to invoke user level name lookup services
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/t_lock.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/user.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/flock.h>
+#include <sys/kmem.h>
+#include <sys/uio.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/cred.h>
+#include <sys/dirent.h>
+#include <sys/pathname.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/mode.h>
+#include <sys/policy.h>
+#include <sys/disp.h>
+#include <sys/door.h>
+#include <fs/fs_subr.h>
+#include <sys/mount.h>
+#include <sys/fs/snode.h>
+#include <sys/fs/dv_node.h>
+#include <sys/fs/sdev_impl.h>
+#include <sys/fs/sdev_node.h>
+#include <sys/sunndi.h>
+#include <sys/sunddi.h>
+#include <sys/sunmdi.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+
+/* default timeout to wait for devfsadm response in seconds */
+#define	DEV_DEVFSADM_STARTUP	(1 * 60)
+#define	DEV_NODE_WAIT_TIMEOUT	(5 * 60)
+
+/* atomic bitset for devfsadm status */
+volatile uint_t devfsadm_state;
+
+static kmutex_t devfsadm_lock;
+static kcondvar_t devfsadm_cv;
+
+int devname_nsmaps_loaded = 0;
+static int dev_node_wait_timeout = DEV_NODE_WAIT_TIMEOUT;
+static int dev_devfsadm_startup =  DEV_DEVFSADM_STARTUP;
+
+/*
+ * Door used to communicate with devfsadmd
+ */
+static door_handle_t	sdev_upcall_door = NULL;	/* Door for upcalls */
+static char		*sdev_door_upcall_filename = NULL;
+static int		sdev_upcall_door_revoked = 0;
+static int		sdev_door_upcall_filename_size;
+
+static void sdev_devfsadmd_nsrdr(sdev_nsrdr_work_t *);
+static int sdev_devfsadm_revoked(void);
+static int sdev_ki_call_devfsadmd(sdev_door_arg_t *, sdev_door_res_t *);
+
+/*
+ * nsmap_readdir processing thread
+ */
+static uint_t			sdev_nsrdr_thread_created = 0;
+static kmutex_t			sdev_nsrdr_thread_lock;
+static kcondvar_t		sdev_nsrdr_thread_cv;
+static sdev_nsrdr_work_t	*sdev_nsrdr_thread_workq = NULL;
+static sdev_nsrdr_work_t	*sdev_nsrdr_thread_tail = NULL;
+
+void
+sdev_devfsadm_lockinit(void)
+{
+	mutex_init(&devfsadm_lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&devfsadm_cv, NULL, CV_DEFAULT, NULL);
+}
+
+void
+sdev_devfsadm_lockdestroy(void)
+{
+	mutex_destroy(&devfsadm_lock);
+	cv_destroy(&devfsadm_cv);
+}
+
+/*
+ * Wait for node to be created
+ */
+int
+sdev_wait4lookup(struct sdev_node *dv, int cmd)
+{
+	clock_t	expire;
+	clock_t rv;
+	int rval = ENOENT;
+	int is_lookup = (cmd == SDEV_LOOKUP);
+
+	ASSERT(cmd == SDEV_LOOKUP || cmd == SDEV_READDIR);
+	ASSERT(MUTEX_HELD(&dv->sdev_lookup_lock));
+
+	/* tick value at which wait expires */
+	expire = ddi_get_lbolt() +
+	    drv_usectohz(dev_node_wait_timeout * 1000000);
+
+	sdcmn_err6(("wait4lookup %s %s, %ld %d\n",
+	    is_lookup ? "lookup" : "readdir",
+	    dv->sdev_name, expire - ddi_get_lbolt(), dv->sdev_state));
+
+	if (SDEV_IS_LGWAITING(dv)) {
+		/* devfsadm nodes */
+		while (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
+		    !sdev_devfsadm_revoked()) {
+			/* wait 2 sec and check devfsadm completion */
+			rv = cv_timedwait_sig(&dv->sdev_lookup_cv,
+			    &dv->sdev_lookup_lock, ddi_get_lbolt() +
+			    drv_usectohz(2 * 1000000));
+
+			if (is_lookup && (rv > 0)) {
+				/* was this node constructed ? */
+				if (dv->sdev_state == SDEV_READY) {
+					rval = 0;
+				}
+				sdcmn_err6(("%s: wait done, %screated %d\n",
+				    dv->sdev_name, rval ? "not " : "",
+				    dv->sdev_state));
+				break;
+			} else if (rv == 0) {
+				/* interrupted */
+				sdcmn_err6(("%s: wait interrupted\n",
+				    dv->sdev_name));
+				break;
+			} else if ((rv == -1) &&
+			    (ddi_get_lbolt() >= expire)) {
+				sdcmn_err6(("%s: wait time is up\n",
+					dv->sdev_name));
+				break;
+			}
+			sdcmn_err6(("%s: wait "
+			    "rv %ld state 0x%x expire %ld\n",
+			    dv->sdev_name, rv, devfsadm_state,
+			    expire - ddi_get_lbolt()));
+		}
+	} else {
+		/*
+		 * for the nodes created by
+		 * devname_lookup_func callback
+		 * or plug-in modules
+		 */
+		while (SDEV_IS_LOOKUP(dv) || SDEV_IS_READDIR(dv)) {
+			cv_wait(&dv->sdev_lookup_cv, &dv->sdev_lookup_lock);
+		}
+		rval = 0;
+	}
+
+	sdcmn_err6(("wait4lookup unblocking %s state 0x%x %d\n",
+	    dv->sdev_name, devfsadm_state, dv->sdev_state));
+
+	if (is_lookup) {
+		SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
+	} else {
+		SDEV_UNBLOCK_OTHERS(dv, SDEV_READDIR);
+	}
+
+	return (rval);
+}
+
+void
+sdev_unblock_others(struct sdev_node *dv, uint_t cmd)
+{
+	ASSERT(MUTEX_HELD(&dv->sdev_lookup_lock));
+
+	SDEV_CLEAR_LOOKUP_FLAGS(dv, cmd);
+	if (SDEV_IS_LGWAITING(dv)) {
+		SDEV_CLEAR_LOOKUP_FLAGS(dv, SDEV_LGWAITING);
+	}
+	cv_broadcast(&dv->sdev_lookup_cv);
+}
+
+/*
+ * In the case devfsadmd is down, it is re-started by syseventd
+ * upon receiving an event subscribed to by devfsadmd.
+ */
+static int
+sdev_start_devfsadmd()
+{
+	int		se_err = 0;
+	sysevent_t	*ev;
+	sysevent_id_t	eid;
+
+	ev = sysevent_alloc(EC_DEVFS, ESC_DEVFS_START, EP_DDI, SE_SLEEP);
+	ASSERT(ev);
+	if ((se_err = log_sysevent(ev, SE_SLEEP, &eid)) != 0) {
+		switch (se_err) {
+		case SE_NO_TRANSPORT:
+			cmn_err(CE_WARN, "unable to start devfsadm - "
+			    "syseventd may not be responding\n");
+			break;
+		default:
+			cmn_err(CE_WARN, "unable to start devfsadm - "
+			    "sysevent error %d\n", se_err);
+			break;
+		}
+	}
+
+	sysevent_free(ev);
+	return (se_err);
+}
+
+static int
+sdev_open_upcall_door()
+{
+	int error;
+	clock_t rv;
+	clock_t expire;
+
+	ASSERT(sdev_upcall_door == NULL);
+
+	/* tick value at which wait expires */
+	expire = ddi_get_lbolt() +
+	    drv_usectohz(dev_devfsadm_startup * 1000000);
+
+	if (sdev_door_upcall_filename == NULL) {
+		if ((error = sdev_start_devfsadmd()) != 0) {
+			return (error);
+		}
+
+		/* wait for devfsadmd start */
+		mutex_enter(&devfsadm_lock);
+		while (sdev_door_upcall_filename == NULL) {
+			sdcmn_err6(("waiting for dev_door creation, %ld\n",
+			    expire - ddi_get_lbolt()));
+			rv = cv_timedwait_sig(&devfsadm_cv, &devfsadm_lock,
+			    expire);
+			sdcmn_err6(("dev_door wait rv %ld\n", rv));
+			if (rv <= 0) {
+				sdcmn_err6(("devfsadmd startup error\n"));
+				mutex_exit(&devfsadm_lock);
+				return (EBADF);
+			}
+		}
+		sdcmn_err6(("devfsadmd is ready\n"));
+		mutex_exit(&devfsadm_lock);
+	}
+
+	if ((error = door_ki_open(sdev_door_upcall_filename,
+	    &sdev_upcall_door)) != 0) {
+		sdcmn_err6(("upcall_lookup: door open error %d\n",
+		    error));
+		return (error);
+	}
+
+	return (0);
+}
+
+static void
+sdev_release_door()
+{
+	if (sdev_upcall_door) {
+		door_ki_rele(sdev_upcall_door);
+		sdev_upcall_door = NULL;
+	}
+	if (sdev_door_upcall_filename) {
+		kmem_free(sdev_door_upcall_filename,
+		    sdev_door_upcall_filename_size);
+		sdev_door_upcall_filename = NULL;
+	}
+}
+
+static int
+sdev_ki_call_devfsadmd(sdev_door_arg_t *argp, sdev_door_res_t *resultp)
+{
+	door_arg_t	darg, save_arg;
+	int		error;
+	int		retry;
+
+	if (((sdev_upcall_door == NULL) &&
+	    ((error = sdev_open_upcall_door()) != 0)) ||
+	    sdev_devfsadm_revoked()) {
+		sdcmn_err6(("call_devfsadm: upcall lookup error\n"));
+		return (error);
+	}
+
+	ASSERT(argp);
+	darg.data_ptr = (char *)argp;
+	darg.data_size = sizeof (struct sdev_door_arg);
+	darg.desc_ptr = NULL;
+	darg.desc_num = 0;
+	darg.rbuf = (char *)(resultp);
+	darg.rsize = sizeof (struct sdev_door_res);
+
+	ASSERT(sdev_upcall_door);
+	save_arg = darg;
+	for (retry = 0; ; retry++) {
+		sdcmn_err6(("call devfsadm: upcall lookup, retry %d\n", retry));
+		if ((error = door_ki_upcall(sdev_upcall_door, &darg)) == 0) {
+			sdcmn_err6(("call devfsadm: upcall lookup ok\n"));
+			break;
+		}
+
+		/*
+		 * handle door call errors
+		 */
+		if (sdev_devfsadm_revoked()) {
+			sdcmn_err6(("upcall lookup door revoked, "
+			    "error %d\n", error));
+			return (error);
+		}
+
+		switch (error) {
+		case EINTR:
+			/* return error here? */
+			sdcmn_err6(("sdev_ki_call_devfsadm: EINTR\n"));
+			delay(hz);
+			break;
+		case EAGAIN:
+			sdcmn_err6(("sdev_ki_call_devfsadm: EAGAIN\n"));
+			delay(2 * hz);
+			break;
+		case EBADF:
+			if (retry > 4) {
+				sdcmn_err6(("sdev_ki_call_devfsadm: EBADF\n"));
+				return (EBADF);
+			}
+			sdcmn_err6((
+			    "sdev_ki_call_devfsadm: EBADF, re-binding\n"));
+			sdev_release_door();
+			delay(retry * hz);
+			error = sdev_open_upcall_door();
+			if (error != 0) {
+				sdcmn_err6(("sdev_ki_call_devfsadm: "
+				    "EBADF lookup error %d\n", error));
+				if (!sdev_devfsadm_revoked())
+					cmn_err(CE_NOTE,
+					    "?unable to invoke devfsadm - "
+					    "please run manually\n");
+				return (EBADF);
+			}
+			break;
+		case EINVAL:
+		default:
+			cmn_err(CE_CONT,
+			    "?sdev: door_ki_upcall unexpected result %d\n",
+			    error);
+			return (error);
+		}
+
+		darg = save_arg;
+	}
+
+	if (!error) {
+		ASSERT((struct sdev_door_res *)darg.rbuf == resultp);
+		if (resultp->devfsadm_error != 0) {
+			sdcmn_err6(("sdev_ki_call_devfsadmd: result %d\n",
+			    resultp->devfsadm_error));
+			error = resultp->devfsadm_error;
+		}
+	} else {
+		sdcmn_err6(("sdev_ki_call_devfsadmd with error %d\n", error));
+	}
+
+	return (error);
+}
+
+static int
+sdev_devfsadm_revoked(void)
+{
+	struct door_info info;
+	int rv;
+	extern int sys_shutdown;
+
+	if (sys_shutdown) {
+		sdcmn_err6(("dev: shutdown observed\n"));
+		return (1);
+	}
+
+	if (sdev_upcall_door && !sdev_upcall_door_revoked) {
+		rv = door_ki_info(sdev_upcall_door, &info);
+		if ((rv == 0) && info.di_attributes & DOOR_REVOKED) {
+			sdcmn_err6(("lookup door: revoked\n"));
+			sdev_upcall_door_revoked = 1;
+		}
+	}
+
+	return (sdev_upcall_door_revoked);
+}
+
+/*ARGSUSED*/
+static void
+sdev_config_all_thread(struct sdev_node *dv)
+{
+	int32_t error = 0;
+	sdev_door_arg_t	*argp;
+	sdev_door_res_t result;
+
+	argp = kmem_zalloc(sizeof (sdev_door_arg_t), KM_SLEEP);
+	argp->devfsadm_cmd = DEVFSADMD_RUN_ALL;
+
+	error = sdev_ki_call_devfsadmd(argp, &result);
+	if (!error) {
+		sdcmn_err6(("devfsadm result error: %d\n",
+		    result.devfsadm_error));
+		if (!result.devfsadm_error) {
+			DEVNAME_DEVFSADM_SET_RUN(devfsadm_state);
+		} else {
+			DEVNAME_DEVFSADM_SET_STOP(devfsadm_state);
+		}
+	} else {
+		DEVNAME_DEVFSADM_SET_STOP(devfsadm_state);
+	}
+
+	kmem_free(argp, sizeof (sdev_door_arg_t));
+done:
+	sdcmn_err6(("sdev_config_all_thread: stopping, devfsadm state 0x%x\n",
+	    devfsadm_state));
+	thread_exit();
+}
+
+/*
+ * launch an asynchronous thread to do the devfsadm dev_config_all
+ */
+/*ARGSUSED*/
+void
+sdev_devfsadmd_thread(struct sdev_node *ddv, struct sdev_node *dv,
+    struct cred *cred)
+{
+	ASSERT(i_ddi_io_initialized());
+	DEVNAME_DEVFSADM_SET_RUNNING(devfsadm_state);
+	(void) thread_create(NULL, 0, sdev_config_all_thread, dv, 0,
+	    &p0, TS_RUN, MINCLSYSPRI);
+}
+
+int
+devname_filename_register(int cmd, char *name)
+{
+	int error = 0;
+	char *strbuf;
+	char *namep;
+	int n;
+
+	ASSERT(cmd == MODDEVNAME_LOOKUPDOOR ||
+	    cmd == MODDEVNAME_DEVFSADMNODE);
+
+	strbuf = kmem_zalloc(MOD_MAXPATH, KM_SLEEP);
+
+	if (copyinstr(name, strbuf, MOD_MAXPATH, 0)) {
+		sdcmn_err6(("error copyin \n"));
+		error = EFAULT;
+	} else {
+		sdcmn_err6(("file %s is registering\n", strbuf));
+		switch (cmd) {
+		case MODDEVNAME_LOOKUPDOOR:
+			/* handling the daemon re-start situations */
+			n = strlen(strbuf) + 1;
+			namep = i_ddi_strdup(strbuf, KM_SLEEP);
+			mutex_enter(&devfsadm_lock);
+			sdev_release_door();
+			sdev_door_upcall_filename_size = n;
+			sdev_door_upcall_filename = namep;
+			sdcmn_err6(("size %d file name %s\n",
+			    sdev_door_upcall_filename_size,
+			    sdev_door_upcall_filename));
+			cv_broadcast(&devfsadm_cv);
+			mutex_exit(&devfsadm_lock);
+			break;
+		case MODDEVNAME_DEVFSADMNODE:
+			break;
+		}
+	}
+
+	kmem_free(strbuf, MOD_MAXPATH);
+	return (error);
+}
+static void
+sdev_nsrdr_thread(void)
+{
+	sdev_nsrdr_work_t *work;
+
+	for (;;) {
+		mutex_enter(&sdev_nsrdr_thread_lock);
+		if (sdev_nsrdr_thread_workq == NULL) {
+			cv_wait(&sdev_nsrdr_thread_cv, &sdev_nsrdr_thread_lock);
+		}
+		work = sdev_nsrdr_thread_workq;
+		sdev_nsrdr_thread_workq = work->next;
+		if (sdev_nsrdr_thread_tail == work)
+			sdev_nsrdr_thread_tail = work->next;
+		mutex_exit(&sdev_nsrdr_thread_lock);
+		sdev_devfsadmd_nsrdr(work);
+	}
+	/*NOTREACHED*/
+}
+
+int
+devname_nsmaps_register(char *nvlbuf, size_t nvlsize)
+{
+	int error = 0;
+	nvlist_t *nvl, *attrs;
+	nvpair_t *nvp = NULL;
+	nvpair_t *kvp = NULL;
+	char *buf;
+	char *key;
+	char *dirname = NULL;
+	char *dirmodule = NULL;
+	char *dirmap = NULL;
+	char *orig_module;
+	char *orig_map;
+	int len = 0;
+	char *tmpmap;
+	int mapcount = 0;
+
+	buf = kmem_zalloc(nvlsize, KM_SLEEP);
+	if ((error = ddi_copyin(nvlbuf, buf, nvlsize, 0)) != 0) {
+		kmem_free(buf, nvlsize);
+		return (error);
+	}
+
+	ASSERT(buf);
+	sdcmn_err6(("devname_nsmaps_register: nsmap buf %p\n", (void *)buf));
+	nvl = NULL;
+	error = nvlist_unpack(buf, nvlsize, &nvl, KM_SLEEP);
+	kmem_free(buf, nvlsize);
+	if (error || (nvl == NULL))
+		return (error);
+
+	/* invalidate all the nsmaps */
+	mutex_enter(&devname_nsmaps_lock);
+	sdev_invalidate_nsmaps();
+	for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
+	    nvp = nvlist_next_nvpair(nvl, nvp)) {
+		dirname = nvpair_name(nvp);
+		if (dirname == NULL) {
+			nvlist_free(nvl);
+			mutex_exit(&devname_nsmaps_lock);
+			return (-1);
+		}
+
+		sdcmn_err6(("dirname %s\n", dirname));
+		(void) nvpair_value_nvlist(nvp, &attrs);
+		for (kvp = nvlist_next_nvpair(attrs, NULL); kvp;
+		    kvp = nvlist_next_nvpair(attrs, kvp)) {
+			key = nvpair_name(kvp);
+			sdcmn_err6(("key %s\n", key));
+			if (strcmp(key, "module") == 0) {
+				(void) nvpair_value_string(kvp, &orig_module);
+				sdcmn_err6(("module %s\n", orig_module));
+				dirmodule = i_ddi_strdup(orig_module, KM_SLEEP);
+				if (strcmp(dirmodule, "devname_null") == 0)
+					dirmodule = NULL;
+			}
+
+			if (strcmp(key, "nsconfig") == 0) {
+				(void) nvpair_value_string(kvp, &orig_map);
+				sdcmn_err6(("dirmap %s\n", orig_map));
+				dirmap = i_ddi_strdup(orig_map, KM_SLEEP);
+				if (strcmp(dirmap, "devname_null") == 0)
+					dirmap = NULL;
+				else if (dirmap[0] != '/') {
+					len = strlen(dirmap) +
+					    strlen(ETC_DEV_DIR) + 2;
+					tmpmap = i_ddi_strdup(dirmap, KM_SLEEP);
+					(void) snprintf(dirmap, len, "%s/%s",
+					    ETC_DEV_DIR, tmpmap);
+					kmem_free(tmpmap, strlen(tmpmap) + 1);
+				}
+			}
+		}
+
+		if (dirmodule == NULL && dirmap == NULL) {
+			nvlist_free(nvl);
+			mutex_exit(&devname_nsmaps_lock);
+			return (-1);
+		}
+
+		sdcmn_err6(("sdev_nsmaps_register: dir %s module %s map %s\n",
+		    dirname, dirmodule, dirmap));
+		sdev_insert_nsmap(dirname, dirmodule, dirmap);
+		mapcount++;
+	}
+
+	if (mapcount > 0)
+		devname_nsmaps_loaded = 1;
+
+	/* clean up obsolete nsmaps */
+	sdev_validate_nsmaps();
+	mutex_exit(&devname_nsmaps_lock);
+	if (nvl)
+		nvlist_free(nvl);
+
+	if (sdev_nsrdr_thread_created) {
+		return (0);
+	}
+
+	mutex_init(&sdev_nsrdr_thread_lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&sdev_nsrdr_thread_cv, NULL, CV_DEFAULT, NULL);
+	(void) thread_create(NULL, 0, (void (*)())sdev_nsrdr_thread, NULL, 0,
+	    &p0, TS_RUN, minclsyspri);
+	sdev_nsrdr_thread_created = 1;
+
+	return (0);
+}
+
+void
+sdev_dispatch_to_nsrdr_thread(struct sdev_node *ddv, char *dir_map,
+    devname_rdr_result_t *result)
+{
+	sdev_nsrdr_work_t *new_work;
+
+	new_work = kmem_zalloc(sizeof (sdev_nsrdr_work_t), KM_SLEEP);
+	new_work->dir_name = i_ddi_strdup(ddv->sdev_name, KM_SLEEP);
+	new_work->dir_map = i_ddi_strdup(dir_map, KM_SLEEP);
+	new_work->dir_dv = ddv;
+	new_work->result = &result;
+	mutex_enter(&sdev_nsrdr_thread_lock);
+	if (sdev_nsrdr_thread_workq == NULL) {
+		sdev_nsrdr_thread_workq = new_work;
+		sdev_nsrdr_thread_tail = new_work;
+		new_work->next = NULL;
+	} else {
+		sdev_nsrdr_thread_tail->next = new_work;
+		sdev_nsrdr_thread_tail = new_work;
+		new_work->next = NULL;
+	}
+	cv_signal(&sdev_nsrdr_thread_cv);
+	mutex_exit(&sdev_nsrdr_thread_lock);
+}
+
+static void
+sdev_devfsadmd_nsrdr(sdev_nsrdr_work_t *work)
+{
+	int32_t error;
+	struct sdev_door_arg *argp;
+	struct sdev_door_res res;
+	struct sdev_node *ddv = work->dir_dv;
+	uint32_t mapcount;
+
+	argp = kmem_zalloc(sizeof (sdev_door_arg_t), KM_SLEEP);
+	argp->devfsadm_cmd = DEVFSADMD_NS_READDIR;
+
+	(void) snprintf(argp->ns_hdl.ns_name,
+	    strlen(work->dir_dv->sdev_path) + 1, "%s", work->dir_dv->sdev_path);
+	(void) snprintf(argp->ns_hdl.ns_map, strlen(work->dir_map) + 1, "%s",
+	    work->dir_map);
+
+	sdcmn_err6(("sdev_devfsadmd_nsrdr: ns_name %s, ns_map %s\n",
+	    argp->ns_hdl.ns_name, argp->ns_hdl.ns_map));
+	error = sdev_ki_call_devfsadmd(argp, &res);
+	sdcmn_err6(("sdev_devfsadmd_nsrdr error %d\n", error));
+	if (error == 0) {
+		error = res.devfsadm_error;
+		if (error) {
+			goto out;
+		}
+
+		mapcount = (uint32_t)res.ns_rdr_hdl.ns_mapcount;
+		sdcmn_err6(("nsmapcount %d\n", mapcount));
+		if (mapcount > 0) {
+			struct devname_nsmap *map =
+			    ddv->sdev_mapinfo;
+			ASSERT(map && map->dir_map);
+			rw_enter(&map->dir_lock, RW_WRITER);
+			map->dir_maploaded = 1;
+			rw_exit(&map->dir_lock);
+		}
+	}
+
+out:
+	mutex_enter(&ddv->sdev_lookup_lock);
+	SDEV_UNBLOCK_OTHERS(ddv, SDEV_READDIR);
+	mutex_exit(&ddv->sdev_lookup_lock);
+
+	kmem_free(argp, sizeof (sdev_door_arg_t));
+}
+
+
+int
+devname_nsmap_lookup(devname_lkp_arg_t *args, devname_lkp_result_t **result)
+{
+	int32_t error = 0;
+	struct sdev_door_arg *argp;
+	struct sdev_door_res resp;
+	char *link;
+	uint8_t spec;
+
+	argp = kmem_zalloc(sizeof (sdev_door_arg_t), KM_SLEEP);
+	argp->devfsadm_cmd = DEVFSADMD_NS_LOOKUP;
+
+	(void) snprintf(argp->ns_hdl.ns_name, strlen(args->devname_name) + 1,
+	    "%s", args->devname_name);
+	(void) snprintf(argp->ns_hdl.ns_map, strlen(args->devname_map) + 1,
+	    "%s", args->devname_map);
+
+	error = sdev_ki_call_devfsadmd(argp, &resp);
+	if (error == 0) {
+		error = resp.devfsadm_error;
+		sdcmn_err6(("devfsadm: error %d\n", error));
+		if (error) {
+			goto done;
+		}
+		link = resp.ns_lkp_hdl.devfsadm_link;
+		if (link == NULL) {
+			error = ENOENT;
+			goto done;
+		}
+		spec = resp.ns_lkp_hdl.devfsadm_spec;
+		sdcmn_err6(("devfsadm_link %s spec %d\n", link, spec));
+
+
+		(*result)->devname_spec = (devname_spec_t)spec;
+		(*result)->devname_link = i_ddi_strdup(link, KM_SLEEP);
+	} else {
+		(*result)->devname_spec = DEVNAME_NS_NONE;
+		(*result)->devname_link = NULL;
+	}
+done:
+	kmem_free(argp, sizeof (sdev_door_arg_t));
+	return (error);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/fs/dev/sdev_ncache.c	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,740 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * negative cache handling for the /dev fs
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/t_lock.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/user.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/flock.h>
+#include <sys/kmem.h>
+#include <sys/uio.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/cred.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/mode.h>
+#include <sys/policy.h>
+#include <fs/fs_subr.h>
+#include <sys/mount.h>
+#include <sys/fs/snode.h>
+#include <sys/fs/dv_node.h>
+#include <sys/fs/sdev_node.h>
+#include <sys/sunndi.h>
+#include <sys/sunmdi.h>
+#include <sys/ddi.h>
+#include <sys/modctl.h>
+#include <sys/devctl_impl.h>
+
+
+/*
+ * ncache is a negative cache of failed lookups.  An entry
+ * is added after an attempt to configure a device by that
+ * name failed.  An accumulation of these entries over time
+ * gives us a set of device name for which implicit reconfiguration
+ * does not need to be attempted.  If a name is created matching
+ * an entry in ncache, that entry is removed, with the
+ * persistent store updated.
+ *
+ * Implicit reconfig is initiated for any name during lookup that
+ * can't be resolved from the backing store and that isn't
+ * present in the negative cache.  This functionality is
+ * enabled during system startup once communication with devfsadm
+ * can be achieved.  Since readdir is more general, implicit
+ * reconfig initiated by reading a directory isn't enabled until
+ * the system is more fully booted, at the time of the multi-user
+ * milestone, corresponding to init state 2.
+ *
+ * A maximum is imposed on the number of entries in the cache
+ * to limit some script going wild and as a defense against attack.
+ * The default limit is 64 and can be adjusted via sdev_nc_max_entries.
+ *
+ * Each entry also has a expiration count.  When looked up a name in
+ * the cache is set to the default.  Subsequent boots will decrement
+ * the count if a name isn't referenced.  This permits a once-only
+ * entry to eventually be removed over time.
+ *
+ * sdev_reconfig_delay implements a "debounce" of the timing beyond
+ * system available indication, providing what the filesystem considers
+ * to be the system-is-fully-booted state.  This is provided to adjust
+ * the timing if some application startup is performing a readdir
+ * in /dev that initiates a troublesome implicit reconfig on every boot.
+ *
+ * sdev_nc_disable_reset can be used to disable clearing the negative cache
+ * on reconfig boot.  The default is to clear the cache on reconfig boot.
+ * sdev_nc_disable can be used to disable the negative cache itself.
+ *
+ * sdev_reconfig_disable can be used to disable implicit reconfig.
+ * The default is that implicit reconfig is enabled.
+ */
+
+/* tunables and defaults */
+#define	SDEV_NC_EXPIRECNT	4
+#define	SDEV_NC_MAX_ENTRIES	64
+#define	SEV_RECONFIG_DELAY	6	/* seconds */
+
+int			sdev_nc_expirecnt = SDEV_NC_EXPIRECNT;
+int			sdev_nc_max_entries = SDEV_NC_MAX_ENTRIES;
+int			sdev_reconfig_delay = SEV_RECONFIG_DELAY;
+int			sdev_reconfig_verbose = 0;
+int			sdev_reconfig_disable = 0;
+int			sdev_nc_disable = 0;
+int			sdev_nc_disable_reset = 0;
+int			sdev_nc_verbose = 0;
+
+/* globals */
+sdev_nc_list_t		*sdev_ncache;
+int			sdev_boot_state = SDEV_BOOT_STATE_INITIAL;
+int			sdev_reconfig_boot = 0;
+static timeout_id_t	sdev_timeout_id = 0;
+
+/* static prototypes */
+static void sdev_ncache_write_complete(nvfd_t *);
+static void sdev_ncache_write(void);
+static void sdev_ncache_process_store(void);
+static sdev_nc_list_t *sdev_nc_newlist(void);
+static void sdev_nc_free_unlinked_node(sdev_nc_node_t *);
+static void sdev_nc_free_all_nodes(sdev_nc_list_t *);
+static void sdev_nc_freelist(sdev_nc_list_t *);
+static sdev_nc_node_t *sdev_nc_findpath(sdev_nc_list_t *, char *);
+static void sdev_nc_insertnode(sdev_nc_list_t *, sdev_nc_node_t *);
+static void sdev_nc_free_bootonly(void);
+
+
+/*
+ * called once at filesystem initialization
+ */
+void
+sdev_ncache_init(void)
+{
+	sdev_ncache = sdev_nc_newlist();
+}
+
+/*
+ * called at mount of the global instance
+ * currently the global instance is never unmounted
+ */
+void
+sdev_ncache_setup(void)
+{
+	nvfd_t	*nvf = sdevfd;
+
+	nvf_register_write_complete(nvf, sdev_ncache_write_complete);
+
+	i_ddi_read_devname_file();
+	sdev_ncache_process_store();
+	sdev_devstate_change();
+}
+
+static void
+sdev_nvp_cache_free(nvfd_t *nvf)
+{
+	nvp_devname_t	*np;
+	nvp_devname_t	*next;
+
+	for (np = NVF_DEVNAME_LIST(nvf); np; np = next) {
+		next = NVP_DEVNAME_NEXT(np);
+		nfd_nvp_free_and_unlink(nvf, NVPLIST(np));
+	}
+}
+
+static void
+sdev_ncache_process_store(void)
+{
+	nvfd_t		*nvf = sdevfd;
+	sdev_nc_list_t	*ncl = sdev_ncache;
+	nvp_devname_t	*np;
+	sdev_nc_node_t	*lp;
+	char		*path;
+	int		i, n;
+
+	if (sdev_nc_disable)
+		return;
+
+	for (np = NVF_DEVNAME_LIST(nvf); np; np = NVP_DEVNAME_NEXT(np)) {
+		for (i = 0; i < np->nvp_npaths; i++) {
+			sdcmn_err5(("    %s %d\n",
+			    np->nvp_paths[i], np->nvp_expirecnts[i]));
+			if (ncl->ncl_nentries < sdev_nc_max_entries) {
+				path = np->nvp_paths[i];
+				n = strlen(path) + 1;
+				lp = kmem_alloc(sizeof (sdev_nc_node_t),
+				    KM_SLEEP);
+				lp->ncn_name = kmem_alloc(n, KM_SLEEP);
+				bcopy(path, lp->ncn_name, n);
+				lp->ncn_flags = NCN_SRC_STORE;
+				lp->ncn_expirecnt = np->nvp_expirecnts[i];
+				sdev_nc_insertnode(ncl, lp);
+			} else if (sdev_nc_verbose) {
+				cmn_err(CE_CONT,
+				    "?%s: truncating from ncache (max %d)\n",
+				    np->nvp_paths[i], sdev_nc_max_entries);
+			}
+		}
+	}
+}
+
+static void
+sdev_ncache_write_complete(nvfd_t *nvf)
+{
+	sdev_nc_list_t	*ncl = sdev_ncache;
+
+	mutex_enter(&ncl->ncl_mutex);
+
+	ASSERT(ncl->ncl_flags & NCL_LIST_WRITING);
+
+	if (ncl->ncl_flags & NCL_LIST_DIRTY) {
+		sdcmn_err5(("ncache write complete but dirty again\n"));
+		ncl->ncl_flags &= ~NCL_LIST_DIRTY;
+		mutex_exit(&ncl->ncl_mutex);
+		sdev_ncache_write();
+	} else {
+		sdcmn_err5(("ncache write complete\n"));
+		ncl->ncl_flags &= ~NCL_LIST_WRITING;
+		mutex_exit(&ncl->ncl_mutex);
+		rw_enter(&nvf->nvf_lock, RW_WRITER);
+		sdev_nvp_cache_free(nvf);
+		rw_exit(&nvf->nvf_lock);
+	}
+}
+
+static void
+sdev_ncache_write(void)
+{
+	nvfd_t		*nvf = sdevfd;
+	sdev_nc_list_t	*ncl = sdev_ncache;
+	nvp_devname_t	*np;
+	sdev_nc_node_t	*lp;
+	int		n, i;
+
+	if (sdev_cache_write_disable) {
+		mutex_enter(&ncl->ncl_mutex);
+		ncl->ncl_flags &= ~NCL_LIST_WRITING;
+		mutex_exit(&ncl->ncl_mutex);
+		return;
+	}
+
+	/* proper lock ordering here is essential */
+	rw_enter(&nvf->nvf_lock, RW_WRITER);
+	sdev_nvp_cache_free(nvf);
+
+	rw_enter(&ncl->ncl_lock, RW_READER);
+	n = ncl->ncl_nentries;
+	ASSERT(n <= sdev_nc_max_entries);
+
+	np = kmem_zalloc(sizeof (nvp_devname_t), KM_SLEEP);
+	np->nvp_npaths = n;
+	np->nvp_paths = kmem_zalloc(n * sizeof (char *), KM_SLEEP);
+	np->nvp_expirecnts = kmem_zalloc(n * sizeof (int), KM_SLEEP);
+
+	i = 0;
+	for (lp = list_head(&ncl->ncl_list); lp;
+	    lp = list_next(&ncl->ncl_list, lp)) {
+		np->nvp_paths[i] = i_ddi_strdup(lp->ncn_name, KM_SLEEP);
+		np->nvp_expirecnts[i] = lp->ncn_expirecnt;
+		sdcmn_err5(("    %s %d\n",
+		    np->nvp_paths[i], np->nvp_expirecnts[i]));
+		i++;
+	}
+
+	rw_exit(&ncl->ncl_lock);
+
+	NVF_MARK_DIRTY(nvf);
+	nfd_nvp_link(nvf, NVPLIST(np));
+	rw_exit(&nvf->nvf_lock);
+
+	wake_nvpflush_daemon();
+}
+
+static void
+sdev_nc_flush_updates(void)
+{
+	sdev_nc_list_t *ncl = sdev_ncache;
+
+	if (sdev_nc_disable || sdev_cache_write_disable)
+		return;
+
+	mutex_enter(&ncl->ncl_mutex);
+	if (((ncl->ncl_flags &
+	    (NCL_LIST_DIRTY | NCL_LIST_WENABLE | NCL_LIST_WRITING)) ==
+	    (NCL_LIST_DIRTY | NCL_LIST_WENABLE))) {
+		ncl->ncl_flags &= ~NCL_LIST_DIRTY;
+		ncl->ncl_flags |= NCL_LIST_WRITING;
+		mutex_exit(&ncl->ncl_mutex);
+		sdev_ncache_write();
+	} else {
+		mutex_exit(&ncl->ncl_mutex);
+	}
+}
+
+static void
+sdev_nc_flush_boot_update(void)
+{
+	sdev_nc_list_t *ncl = sdev_ncache;
+
+	if (sdev_nc_disable || sdev_cache_write_disable ||
+	    (sdev_boot_state == SDEV_BOOT_STATE_INITIAL)) {
+		return;
+	}
+	mutex_enter(&ncl->ncl_mutex);
+	if (ncl->ncl_flags & NCL_LIST_WENABLE) {
+		mutex_exit(&ncl->ncl_mutex);
+		sdev_nc_flush_updates();
+	} else {
+		mutex_exit(&ncl->ncl_mutex);
+	}
+
+}
+
+static void
+sdev_state_boot_complete()
+{
+	sdev_nc_list_t	*ncl = sdev_ncache;
+	sdev_nc_node_t	*lp, *next;
+
+	/*
+	 * Once boot is complete, decrement the expire count of each entry
+	 * in the cache not touched by a reference.  Remove any that
+	 * goes to zero.  This effectively removes random entries over
+	 * time.
+	 */
+	rw_enter(&ncl->ncl_lock, RW_WRITER);
+	mutex_enter(&ncl->ncl_mutex);
+
+	for (lp = list_head(&ncl->ncl_list); lp; lp = next) {
+		next = list_next(&ncl->ncl_list, lp);
+		if (sdev_nc_expirecnt > 0 && lp->ncn_expirecnt > 0) {
+			if (lp->ncn_flags & NCN_ACTIVE) {
+				if (lp->ncn_expirecnt != sdev_nc_expirecnt) {
+					lp->ncn_expirecnt = sdev_nc_expirecnt;
+					ncl->ncl_flags |= NCL_LIST_DIRTY;
+				}
+			} else {
+				if (--lp->ncn_expirecnt == 0) {
+					list_remove(&ncl->ncl_list, lp);
+					sdev_nc_free_unlinked_node(lp);
+					ncl->ncl_nentries--;
+				}
+				ncl->ncl_flags |= NCL_LIST_DIRTY;
+			}
+		}
+	}
+
+	mutex_exit(&ncl->ncl_mutex);
+	rw_exit(&ncl->ncl_lock);
+
+	sdev_nc_flush_boot_update();
+	sdev_boot_state = SDEV_BOOT_STATE_COMPLETE;
+}
+
+/*
+ * Upon transition to the login state on a reconfigure boot,
+ * a debounce timer is set up so that we cache all the nonsense
+ * lookups we're hit with by the windowing system startup.
+ */
+
+/*ARGSUSED*/
+static void
+sdev_state_timeout(void *arg)
+{
+	sdev_timeout_id = 0;
+	sdev_state_boot_complete();
+}
+
+static void
+sdev_state_sysavail()
+{
+	sdev_nc_list_t *ncl = sdev_ncache;
+	clock_t	nticks;
+	int nsecs;
+
+	mutex_enter(&ncl->ncl_mutex);
+	ncl->ncl_flags |= NCL_LIST_WENABLE;
+	mutex_exit(&ncl->ncl_mutex);
+
+	nsecs = sdev_reconfig_delay;
+	if (nsecs == 0) {
+		sdev_state_boot_complete();
+	} else {
+		nticks = drv_usectohz(1000000 * nsecs);
+		sdcmn_err5(("timeout initiated %ld\n", nticks));
+		sdev_timeout_id = timeout(sdev_state_timeout, NULL, nticks);
+		sdev_nc_flush_boot_update();
+	}
+}
+
+/*
+ * Called to inform the filesystem of progress during boot,
+ * either a notice of reconfiguration boot or an indication of
+ * system boot complete.  At system boot complete, set up a
+ * timer at the expiration of which no further failed lookups
+ * will be added to the negative cache.
+ *
+ * The dev filesystem infers from reconfig boot that implicit
+ * reconfig need not be invoked at all as all available devices
+ * will have already been named.
+ *
+ * The dev filesystem infers from "system available" that devfsadmd
+ * can now be run and hence implicit reconfiguration may be initiated.
+ * During early stages of system startup, implicit reconfig is
+ * not done to avoid impacting boot performance.
+ */
+void
+sdev_devstate_change(void)
+{
+	int new_state;
+
+	/*
+	 * Track system state and manage interesting transitions
+	 */
+	new_state = SDEV_BOOT_STATE_INITIAL;
+	if (i_ddi_reconfig())
+		new_state = SDEV_BOOT_STATE_RECONFIG;
+	if (i_ddi_sysavail())
+		new_state = SDEV_BOOT_STATE_SYSAVAIL;
+
+	if (sdev_boot_state < new_state) {
+		switch (new_state) {
+		case SDEV_BOOT_STATE_RECONFIG:
+			sdcmn_err5(("state change: reconfigure boot\n"));
+			sdev_boot_state = new_state;
+			sdev_reconfig_boot = 1;
+			if (!sdev_nc_disable_reset)
+				sdev_nc_free_bootonly();
+			break;
+		case SDEV_BOOT_STATE_SYSAVAIL:
+			sdcmn_err5(("system available\n"));
+			sdev_boot_state = new_state;
+			sdev_state_sysavail();
+			break;
+		}
+	}
+}
+
+/*
+ * Lookup: filter out entries in the negative cache
+ * Return 1 if the lookup should not cause a reconfig.
+ */
+int
+sdev_lookup_filter(sdev_node_t *dv, char *nm)
+{
+	int n;
+	sdev_nc_list_t *ncl = sdev_ncache;
+	sdev_nc_node_t *lp;
+	char *path;
+	int rval = 0;
+	int changed = 0;
+
+	ASSERT(i_ddi_io_initialized());
+	ASSERT(SDEVTOV(dv)->v_type == VDIR);
+
+	if (sdev_nc_disable)
+		return (0);
+
+	n = strlen(dv->sdev_path) + strlen(nm) + 2;
+	path = kmem_alloc(n, KM_SLEEP);
+	(void) sprintf(path, "%s/%s", dv->sdev_path, nm);
+
+	rw_enter(&ncl->ncl_lock, RW_READER);
+	if ((lp = sdev_nc_findpath(ncl, path)) != NULL) {
+		sdcmn_err5(("%s/%s: lookup by %s cached, no reconfig\n",
+		    dv->sdev_name, nm, curproc->p_user.u_comm));
+		if (sdev_nc_verbose) {
+			cmn_err(CE_CONT,
+			    "?%s/%s: lookup by %s cached, no reconfig\n",
+			    dv->sdev_name, nm, curproc->p_user.u_comm);
+		}
+		mutex_enter(&ncl->ncl_mutex);
+		lp->ncn_flags |= NCN_ACTIVE;
+		if (sdev_nc_expirecnt > 0 && lp->ncn_expirecnt > 0 &&
+		    lp->ncn_expirecnt < sdev_nc_expirecnt) {
+			lp->ncn_expirecnt = sdev_nc_expirecnt;
+			ncl->ncl_flags |= NCL_LIST_DIRTY;
+			changed = 1;
+		}
+		mutex_exit(&ncl->ncl_mutex);
+		rval = 1;
+	}
+	rw_exit(&ncl->ncl_lock);
+	kmem_free(path, n);
+	if (changed)
+		sdev_nc_flush_boot_update();
+	return (rval);
+}
+
+void
+sdev_lookup_failed(sdev_node_t *dv, char *nm, int failed_flags)
+{
+	if (sdev_nc_disable)
+		return;
+
+	/*
+	 * If we're still in the initial boot stage, always update
+	 * the cache - we may not have received notice of the
+	 * reconfig boot state yet.  On a reconfigure boot, entries
+	 * from the backing store are not re-persisted on update,
+	 * but new entries are marked as needing an update.
+	 * Never cache dynamic or non-global nodes.
+	 */
+	if (SDEV_IS_GLOBAL(dv) && !SDEV_IS_DYNAMIC(dv) &&
+	    !SDEV_IS_NO_NCACHE(dv) &&
+	    ((failed_flags & SLF_NO_NCACHE) == 0) &&
+	    ((sdev_reconfig_boot &&
+		(sdev_boot_state != SDEV_BOOT_STATE_COMPLETE)) ||
+	    (!sdev_reconfig_boot && ((failed_flags & SLF_REBUILT))))) {
+			sdev_nc_addname(sdev_ncache,
+			    dv, nm, NCN_SRC_CURRENT|NCN_ACTIVE);
+	}
+}
+
+static sdev_nc_list_t *
+sdev_nc_newlist(void)
+{
+	sdev_nc_list_t	*ncl;
+
+	ncl = kmem_zalloc(sizeof (sdev_nc_list_t), KM_SLEEP);
+
+	rw_init(&ncl->ncl_lock, NULL, RW_DEFAULT, NULL);
+	mutex_init(&ncl->ncl_mutex, NULL, MUTEX_DEFAULT, NULL);
+	list_create(&ncl->ncl_list, sizeof (sdev_nc_node_t),
+	    offsetof(sdev_nc_node_t, ncn_link));
+
+	return (ncl);
+}
+
+static void
+sdev_nc_free_unlinked_node(sdev_nc_node_t *lp)
+{
+	kmem_free(lp->ncn_name, strlen(lp->ncn_name) + 1);
+	kmem_free(lp, sizeof (sdev_nc_node_t));
+}
+
+static void
+sdev_nc_free_all_nodes(sdev_nc_list_t *ncl)
+{
+	sdev_nc_node_t *lp;
+
+	while ((lp = list_head(&ncl->ncl_list)) != NULL) {
+		list_remove(&ncl->ncl_list, lp);
+		sdev_nc_free_unlinked_node(lp);
+		ncl->ncl_nentries--;
+	}
+	ASSERT(ncl->ncl_nentries == 0);
+}
+
+static void
+sdev_nc_freelist(sdev_nc_list_t *ncl)
+{
+	if (!list_is_empty(&ncl->ncl_list))
+		sdev_nc_free_all_nodes(ncl);
+	ASSERT(list_is_empty(&ncl->ncl_list));
+	ASSERT(ncl->ncl_nentries == 0);
+
+	mutex_destroy(&ncl->ncl_mutex);
+	rw_destroy(&ncl->ncl_lock);
+	list_destroy(&ncl->ncl_list);
+	kmem_free(ncl, sizeof (sdev_nc_list_t));
+}
+
+static sdev_nc_node_t *
+sdev_nc_findpath(sdev_nc_list_t *ncl, char *path)
+{
+	sdev_nc_node_t *lp;
+
+	ASSERT(RW_LOCK_HELD(&ncl->ncl_lock));
+
+	for (lp = list_head(&ncl->ncl_list); lp;
+	    lp = list_next(&ncl->ncl_list, lp)) {
+		if (strcmp(path, lp->ncn_name) == 0)
+			return (lp);
+	}
+
+	return (NULL);
+}
+
+static void
+sdev_nc_insertnode(sdev_nc_list_t *ncl, sdev_nc_node_t *new)
+{
+	sdev_nc_node_t *lp;
+
+	rw_enter(&ncl->ncl_lock, RW_WRITER);
+
+	lp = sdev_nc_findpath(ncl, new->ncn_name);
+	if (lp == NULL) {
+		if (ncl->ncl_nentries == sdev_nc_max_entries) {
+			sdcmn_err5((
+			    "%s by %s: not adding to ncache (max %d)\n",
+			    new->ncn_name, curproc->p_user.u_comm,
+			    ncl->ncl_nentries));
+			if (sdev_nc_verbose) {
+				cmn_err(CE_CONT, "?%s by %s: "
+				    "not adding to ncache (max %d)\n",
+				    new->ncn_name, curproc->p_user.u_comm,
+				    ncl->ncl_nentries);
+			}
+			rw_exit(&ncl->ncl_lock);
+			sdev_nc_free_unlinked_node(new);
+		} else {
+
+			list_insert_tail(&ncl->ncl_list, new);
+			ncl->ncl_nentries++;
+
+			/* don't mark list dirty for nodes from store */
+			mutex_enter(&ncl->ncl_mutex);
+			if ((new->ncn_flags & NCN_SRC_STORE) == 0) {
+				sdcmn_err5(("%s by %s: add to ncache\n",
+				    new->ncn_name, curproc->p_user.u_comm));
+				if (sdev_nc_verbose) {
+					cmn_err(CE_CONT,
+					    "?%s by %s: add to ncache\n",
+					    new->ncn_name,
+					    curproc->p_user.u_comm);
+				}
+				ncl->ncl_flags |= NCL_LIST_DIRTY;
+			}
+			mutex_exit(&ncl->ncl_mutex);
+			rw_exit(&ncl->ncl_lock);
+			lp = new;
+			sdev_nc_flush_boot_update();
+		}
+	} else {
+		mutex_enter(&ncl->ncl_mutex);
+		lp->ncn_flags |= new->ncn_flags;
+		mutex_exit(&ncl->ncl_mutex);
+		rw_exit(&ncl->ncl_lock);
+		sdev_nc_free_unlinked_node(new);
+	}
+}
+
+void
+sdev_nc_addname(sdev_nc_list_t *ncl, sdev_node_t *dv, char *nm, int flags)
+{
+	int n;
+	sdev_nc_node_t *lp;
+
+	ASSERT(SDEVTOV(dv)->v_type == VDIR);
+
+	lp = kmem_zalloc(sizeof (sdev_nc_node_t), KM_SLEEP);
+
+	n = strlen(dv->sdev_path) + strlen(nm) + 2;
+	lp->ncn_name = kmem_alloc(n, KM_SLEEP);
+	(void) sprintf(lp->ncn_name, "%s/%s",
+		dv->sdev_path, nm);
+	lp->ncn_flags = flags;
+	lp->ncn_expirecnt = sdev_nc_expirecnt;
+	sdev_nc_insertnode(ncl, lp);
+}
+
+void
+sdev_nc_node_exists(sdev_node_t *dv)
+{
+	/* dynamic and non-global nodes are never cached */
+	if (SDEV_IS_GLOBAL(dv) && !SDEV_IS_DYNAMIC(dv) &&
+	    !SDEV_IS_NO_NCACHE(dv)) {
+		sdev_nc_path_exists(sdev_ncache, dv->sdev_path);
+	}
+}
+
+void
+sdev_nc_path_exists(sdev_nc_list_t *ncl, char *path)
+{
+	sdev_nc_node_t *lp;
+
+	if (sdev_nc_disable)
+		return;
+
+	rw_enter(&ncl->ncl_lock, RW_READER);
+	if ((lp = sdev_nc_findpath(ncl, path)) == NULL) {
+		rw_exit(&ncl->ncl_lock);
+		return;
+	}
+	if (rw_tryupgrade(&ncl->ncl_lock) == 0) {
+		rw_exit(&ncl->ncl_lock);
+		rw_enter(&ncl->ncl_lock, RW_WRITER);
+		lp = sdev_nc_findpath(ncl, path);
+	}
+	if (lp) {
+		list_remove(&ncl->ncl_list, lp);
+		ncl->ncl_nentries--;
+		mutex_enter(&ncl->ncl_mutex);
+		ncl->ncl_flags |= NCL_LIST_DIRTY;
+		if (ncl->ncl_flags & NCL_LIST_WENABLE) {
+			mutex_exit(&ncl->ncl_mutex);
+			rw_exit(&ncl->ncl_lock);
+			sdev_nc_flush_updates();
+		} else {
+			mutex_exit(&ncl->ncl_mutex);
+			rw_exit(&ncl->ncl_lock);
+		}
+		sdev_nc_free_unlinked_node(lp);
+		sdcmn_err5(("%s by %s: removed from ncache\n",
+		    path, curproc->p_user.u_comm));
+		if (sdev_nc_verbose) {
+			cmn_err(CE_CONT, "?%s by %s: removed from ncache\n",
+			    path, curproc->p_user.u_comm);
+		}
+	} else
+		rw_exit(&ncl->ncl_lock);
+}
+
+static void
+sdev_nc_free_bootonly(void)
+{
+	sdev_nc_list_t	*ncl = sdev_ncache;
+	sdev_nc_node_t *lp;
+	sdev_nc_node_t *next;
+
+	ASSERT(sdev_reconfig_boot);
+
+	rw_enter(&ncl->ncl_lock, RW_WRITER);
+
+	for (lp = list_head(&ncl->ncl_list); lp; lp = next) {
+		next = list_next(&ncl->ncl_list, lp);
+		if ((lp->ncn_flags & NCN_SRC_CURRENT) == 0) {
+			sdcmn_err5(("freeing %s\n", lp->ncn_name));
+			mutex_enter(&ncl->ncl_mutex);
+			ncl->ncl_flags |= NCL_LIST_DIRTY;
+			mutex_exit(&ncl->ncl_mutex);
+			list_remove(&ncl->ncl_list, lp);
+			sdev_nc_free_unlinked_node(lp);
+			ncl->ncl_nentries--;
+		}
+	}
+
+	rw_exit(&ncl->ncl_lock);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/fs/dev/sdev_nsconfig_mod.c	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,198 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * this module implements the devname_ops to fetch
+ * a specific entry from a /etc/dev/devname_map file or
+ * a name service map.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/cmn_err.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/modctl.h>
+#include <sys/debug.h>
+#include <sys/fs/sdev_impl.h>
+#include <sys/fs/sdev_node.h>
+
+static int devname_lookup(char *, devname_handle_t *, struct cred *);
+static int devname_remove(devname_handle_t *);
+static int devname_rename(devname_handle_t *, char *);
+static int devname_readdir(devname_handle_t *, struct cred *);
+static int devname_getattr(devname_handle_t *, struct vattr *,
+    struct cred *);
+static void devname_inactive(devname_handle_t *, struct cred *);
+
+static struct devname_ops devname_ops = {
+	DEVNOPS_REV,		/* devnops_rev, */
+	devname_lookup,		/* devnops_lookup */
+	devname_remove,		/* devnops_remove */
+	devname_rename,		/* devnops_rename */
+	devname_getattr,	/* devnops_getattr */
+	devname_readdir,	/* devname_readdir */
+	devname_inactive	/* devname_inactive */
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+static struct modldev modldev = {
+	&mod_devfsops,
+	"devname name service mod 1.0",
+	&devname_ops,
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, &modldev, NULL
+};
+
+int
+_init(void)
+{
+	return (mod_install(&modlinkage));
+}
+
+int
+_fini(void)
+{
+	return (mod_remove(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+/*ARGSUSED2*/
+static int
+devname_lookup(char *nm, devname_handle_t *dhl, struct cred *cred)
+{
+	int error = 0;
+	char *dir = NULL;
+	devname_lkp_arg_t *args = NULL;
+	devname_lkp_result_t *result = NULL;
+	struct devname_nsmap *map = NULL;
+
+	args = kmem_zalloc(sizeof (struct devname_lkp_arg), KM_SLEEP);
+	if (args == NULL) {
+		error = ENOENT;
+		goto errout;
+	}
+
+	args->devname_name = i_ddi_strdup(nm, KM_SLEEP);
+	error = devname_get_dir_path(dhl, &dir);
+	if (error) {
+		error = ENOENT;
+		goto errout;
+	}
+
+	args->devname_dir = i_ddi_strdup(dir, KM_SLEEP);
+	error = devname_get_dir_nsmap(dhl, &map);
+	if (map && map->dir_map)
+		args->devname_map = i_ddi_strdup(map->dir_map, KM_SLEEP);
+
+	result = kmem_zalloc(sizeof (struct devname_lkp_result), KM_SLEEP);
+	if (result == NULL) {
+		error = ENOENT;
+		goto errout;
+	}
+
+
+	error = devname_nsmap_lookup(args, &result);
+	if (error) {
+		error = ENOENT;
+		goto errout;
+	}
+
+	devname_set_nodetype(dhl, (void *)result->devname_link,
+	    (int)result->devname_spec);
+
+errout:
+	if (args->devname_name)
+		kmem_free(args->devname_name, strlen(args->devname_name) + 1);
+	if (args->devname_dir)
+		kmem_free(args->devname_dir, strlen(args->devname_dir) + 1);
+	if (args->devname_map)
+		kmem_free(args->devname_map, strlen(args->devname_map) + 1);
+	if (args)
+		kmem_free(args, sizeof (struct devname_lkp_arg));
+	if (result)
+		kmem_free(result, sizeof (struct devname_lkp_result));
+	return (error);
+}
+
+/*ARGSUSED*/
+static int
+devname_readdir(devname_handle_t *hdl, struct cred *cred)
+{
+	char *entry;
+	char *dir;
+
+	(void) devname_get_name(hdl, &entry);
+	(void) devname_get_dir_name(hdl, &dir);
+
+	/* do not waste to do the map check */
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+devname_remove(devname_handle_t *hdl)
+{
+	char *entry;
+
+	(void) devname_get_name(hdl, &entry);
+	return (EROFS);
+}
+
+/*ARGSUSED*/
+static int
+devname_rename(devname_handle_t *ohdl, char *new_name)
+{
+	char *oname;
+
+	(void) devname_get_name(ohdl, &oname);
+	return (ENOTSUP);
+}
+
+/*ARGSUSED*/
+static int
+devname_getattr(devname_handle_t *hdl, vattr_t *vap, struct cred *cred)
+{
+	return (0);
+}
+
+/*ARGSUSED*/
+static void
+devname_inactive(devname_handle_t *hdl, struct cred *cred)
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/fs/dev/sdev_profile.c	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,983 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * This file implements /dev filesystem operations for non-global
+ * instances. Three major entry points:
+ * devname_profile_update()
+ *   Update matching rules determining which names to export
+ * prof_readdir()
+ *   Return the list of exported names
+ * prof_lookup()
+ *   Implements lookup
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/vnode.h>
+#include <sys/uio.h>
+#include <sys/dirent.h>
+#include <sys/pathname.h>
+#include <sys/fs/dv_node.h>
+#include <sys/fs/sdev_impl.h>
+#include <sys/sunndi.h>
+#include <sys/modctl.h>
+
+enum {
+	PROFILE_TYPE_INCLUDE,
+	PROFILE_TYPE_EXCLUDE,
+	PROFILE_TYPE_MAP,
+	PROFILE_TYPE_SYMLINK
+};
+
+enum {
+	WALK_DIR_CONTINUE = 0,
+	WALK_DIR_TERMINATE
+};
+
+static const char *sdev_nvp_val_err = "nvpair_value error %d, %s\n";
+
+static void process_rule(struct sdev_node *, struct sdev_node *,
+    char *, char *, int);
+static void walk_dir(struct vnode *, void *, int (*)(char *, void *));
+
+static void
+prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv,
+    struct vattr *vap, struct vnode **avpp, int *no_fs_perm)
+{
+	struct vnode *advp;
+
+	/* get attribute from shadow, if present; else get default */
+	advp = dir->sdev_attrvp;
+	if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred) == 0) {
+		(void) VOP_GETATTR(*avpp, vap, 0, kcred);
+	} else if (gdv == NULL || gdv->v_type == VDIR) {
+		/* always create shadow directory */
+		*vap = sdev_vattr_dir;
+		if (advp && VOP_MKDIR(advp, name,
+		    &sdev_vattr_dir, avpp, kcred) != 0) {
+			*avpp = NULLVP;
+			sdcmn_err10(("prof_getattr: failed to create "
+			    "shadow directory %s/%s\n", dir->sdev_path, name));
+		}
+	} else {
+		/*
+		 * get default permission from devfs
+		 * Before calling devfs_get_defattr, we need to get
+		 * the realvp (the dv_node). If realvp is not a dv_node,
+		 * devfs_get_defattr() will return a system-wide default
+		 * attr for device nodes.
+		 */
+		struct vnode *rvp;
+		if (VOP_REALVP(gdv, &rvp) != 0)
+			rvp = gdv;
+		devfs_get_defattr(rvp, vap, no_fs_perm);
+		*avpp = NULLVP;
+	}
+
+	/* ignore dev_t and vtype from backing store */
+	if (gdv) {
+		vap->va_type = gdv->v_type;
+		vap->va_rdev = gdv->v_rdev;
+	}
+}
+
+static void
+apply_glob_pattern(struct sdev_node *pdir, struct sdev_node *cdir)
+{
+	char *name;
+	nvpair_t *nvp = NULL;
+	nvlist_t *nvl;
+	struct vnode *vp = SDEVTOV(cdir);
+	int rv = 0;
+
+	if (vp->v_type != VDIR)
+		return;
+	name = cdir->sdev_name;
+	nvl = pdir->sdev_prof.dev_glob_incdir;
+	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
+		char *pathleft;
+		char *expr = nvpair_name(nvp);
+		if (!gmatch(name, expr))
+			continue;
+		rv = nvpair_value_string(nvp, &pathleft);
+		if (rv != 0) {
+			cmn_err(CE_WARN, sdev_nvp_val_err,
+			    rv, nvpair_name(nvp));
+			break;
+		}
+		process_rule(cdir, cdir->sdev_origin,
+		    pathleft, NULL, PROFILE_TYPE_INCLUDE);
+	}
+}
+
+/*
+ * Some commonality here with sdev_mknode(), could be simplified.
+ * NOTE: prof_mknode returns with *newdv held once, if success.
+ */
+static int
+prof_mknode(struct sdev_node *dir, char *name, struct sdev_node **newdv,
+    vattr_t *vap, vnode_t *avp, void *arg, cred_t *cred)
+{
+	struct sdev_node *dv;
+	int rv;
+
+	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
+
+	/* check cache first */
+	if (dv = sdev_cache_lookup(dir, name)) {
+		*newdv = dv;
+		return (0);
+	}
+
+	/* allocate node and insert into cache */
+	rv = sdev_nodeinit(dir, name, &dv, NULL);
+	if (rv != 0) {
+		*newdv = NULL;
+		return (rv);
+	}
+
+	rv = sdev_cache_update(dir, &dv, name, SDEV_CACHE_ADD);
+	*newdv = dv;
+
+	/* put it in ready state */
+	rv = sdev_nodeready(*newdv, vap, avp, arg, cred);
+
+	/* handle glob pattern in the middle of a path */
+	if (rv == 0) {
+		if (SDEVTOV(*newdv)->v_type == VDIR)
+			sdcmn_err10(("sdev_origin for %s set to 0x%p\n",
+			    name, arg));
+		apply_glob_pattern(dir, *newdv);
+	}
+	return (rv);
+}
+
+/*
+ * Create a directory node in a non-global dev instance.
+ * Always create shadow vnode. Set sdev_origin to the corresponding
+ * global directory sdev_node if it exists. This facilitates the
+ * lookup operation.
+ */
+static int
+prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp)
+{
+	struct sdev_node *dir = *dirp;
+	struct sdev_node *gdir = *gdirp;
+	struct sdev_node *newdv;
+	struct vnode *avp, *gnewdir = NULL;
+	struct vattr vattr;
+	int error;
+
+	/* see if name already exists */
+	rw_enter(&dir->sdev_contents, RW_READER);
+	if (newdv = sdev_cache_lookup(dir, name)) {
+		*dirp = newdv;
+		*gdirp = newdv->sdev_origin;
+		SDEV_RELE(dir);
+		rw_exit(&dir->sdev_contents);
+		return (0);
+	}
+	rw_exit(&dir->sdev_contents);
+
+	/* find corresponding dir node in global dev */
+	if (gdir) {
+		error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir,
+		    NULL, 0, NULL, kcred);
+		if (error == 0) {
+			*gdirp = VTOSDEV(gnewdir);
+		} else { 	/* it's ok if there no global dir */
+			*gdirp = NULL;
+		}
+	}
+
+	/* get attribute from shadow, also create shadow dir */
+	prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL);
+
+	/* create dev directory vnode */
+	rw_enter(&dir->sdev_contents, RW_WRITER);
+	error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp,
+	    kcred);
+	rw_exit(&dir->sdev_contents);
+	if (error == 0) {
+		ASSERT(newdv);
+		*dirp = newdv;
+	}
+	SDEV_RELE(dir);
+	return (error);
+}
+
+/*
+ * Look up a logical name in the global zone.
+ * Provides the ability to map the global zone's device name
+ * to an alternate name within a zone.  The primary example
+ * is the virtual console device /dev/zcons/[zonename]/zconsole
+ * mapped to /[zonename]/root/dev/zconsole.
+ */
+static void
+prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir,
+    char *name, char *rename)
+{
+	/* global OS rootdir */
+	extern vnode_t *rootdir;
+
+	int error;
+	struct vnode *avp, *gdv, *gddv;
+	struct sdev_node *newdv;
+	struct vattr vattr = {0};
+	struct pathname pn;
+
+	/* check if node already exists */
+	newdv = sdev_cache_lookup(dir, rename);
+	if (newdv) {
+		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
+		SDEV_SIMPLE_RELE(newdv);
+		return;
+	}
+
+	/* sanity check arguments */
+	if (!gdir || pn_get(name, UIO_SYSSPACE, &pn))
+		return;
+
+	/* perform a relative lookup of the global /dev instance */
+	gddv = SDEVTOV(gdir);
+	VN_HOLD(gddv);
+	VN_HOLD(rootdir);
+	error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv,
+	    rootdir, gddv, kcred);
+	pn_free(&pn);
+	if (error) {
+		sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name));
+		return;
+	}
+	ASSERT(gdv && gdv->v_type != VLNK);
+
+	/*
+	 * Found the entry in global /dev, figure out attributes
+	 * by looking at backing store. Call into devfs for default.
+	 */
+	prof_getattr(dir, name, gdv, &vattr, &avp, NULL);
+
+	if (gdv->v_type != VDIR) {
+		VN_RELE(gdv);
+		gdir = NULL;
+	} else
+		gdir = VTOSDEV(gdv);
+
+	if (prof_mknode(dir, rename, &newdv, &vattr, avp,
+	    (void *)gdir, kcred) == 0) {
+		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
+		SDEV_SIMPLE_RELE(newdv);
+	}
+}
+
+static void
+prof_make_sym(struct sdev_node *dir, char *lnm, char *tgt)
+{
+	struct sdev_node *newdv;
+
+	if (prof_mknode(dir, lnm, &newdv, &sdev_vattr_lnk, NULL,
+	    (void *)tgt, kcred) == 0) {
+		ASSERT(newdv->sdev_state != SDEV_ZOMBIE);
+		SDEV_SIMPLE_RELE(newdv);
+	}
+}
+
+/*
+ * Create symlinks in the current directory based on profile
+ */
+static void
+prof_make_symlinks(struct sdev_node *dir)
+{
+	char *tgt, *lnm;
+	nvpair_t *nvp = NULL;
+	nvlist_t *nvl = dir->sdev_prof.dev_symlink;
+	int rv;
+
+	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
+
+	if (nvl == NULL)
+		return;
+
+	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
+		lnm = nvpair_name(nvp);
+		rv = nvpair_value_string(nvp, &tgt);
+		if (rv != 0) {
+			cmn_err(CE_WARN, sdev_nvp_val_err,
+			    rv, nvpair_name(nvp));
+			break;
+		}
+		prof_make_sym(dir, lnm, tgt);
+	}
+}
+
+static void
+prof_make_maps(struct sdev_node *dir)
+{
+	nvpair_t *nvp = NULL;
+	nvlist_t *nvl = dir->sdev_prof.dev_map;
+	int rv;
+
+	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
+
+	if (nvl == NULL)
+		return;
+
+	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
+		char *name;
+		char *rename = nvpair_name(nvp);
+		rv = nvpair_value_string(nvp, &name);
+		if (rv != 0) {
+			cmn_err(CE_WARN, sdev_nvp_val_err,
+			    rv, nvpair_name(nvp));
+			break;
+		}
+		sdcmn_err10(("map %s -> %s\n", name, rename));
+		(void) prof_lookup_globaldev(dir, sdev_origins->sdev_root,
+		    name, rename);
+	}
+}
+
+struct match_arg {
+	char *expr;
+	int match;
+};
+
+static int
+match_name(char *name, void *arg)
+{
+	struct match_arg *margp = (struct match_arg *)arg;
+
+	if (gmatch(name, margp->expr)) {
+		margp->match = 1;
+		return (WALK_DIR_TERMINATE);
+	}
+	return (WALK_DIR_CONTINUE);
+}
+
+static int
+is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir)
+{
+	struct match_arg marg;
+	struct pathname pn;
+	struct vnode *gvp;
+	struct sdev_node *gdir = dir->sdev_origin;
+
+	if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred) != 0)
+		return (0);
+
+	if (gvp->v_type != VDIR) {
+		VN_RELE(gvp);
+		return (0);
+	}
+
+	if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) {
+		VN_RELE(gvp);
+		return (0);
+	}
+
+	marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
+	(void) pn_getcomponent(&pn, marg.expr);
+	marg.match = 0;
+
+	walk_dir(gvp, &marg, match_name);
+	VN_RELE(gvp);
+	kmem_free(marg.expr, MAXNAMELEN);
+	pn_free(&pn);
+
+	return (marg.match);
+}
+
+
+/* Check if name passes matching rules */
+static int
+prof_name_matched(char *name, struct sdev_node *dir)
+{
+	int type, match = 0;
+	char *expr;
+	nvlist_t *nvl;
+	nvpair_t *nvp = NULL;
+	int rv;
+
+	/* check against nvlist for leaf include/exclude */
+	nvl = dir->sdev_prof.dev_name;
+	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
+		expr = nvpair_name(nvp);
+		rv = nvpair_value_int32(nvp, &type);
+		if (rv != 0) {
+			cmn_err(CE_WARN, sdev_nvp_val_err,
+			    rv, nvpair_name(nvp));
+			break;
+		}
+
+		if (type == PROFILE_TYPE_EXCLUDE) {
+			if (gmatch(name, expr))
+				return (0);	/* excluded */
+		} else if (!match) {
+			match = gmatch(name, expr);
+		}
+	}
+	if (match) {
+		sdcmn_err10(("prof_name_matched: %s\n", name));
+		return (match);
+	}
+
+	/* check for match against directory globbing pattern */
+	nvl = dir->sdev_prof.dev_glob_incdir;
+	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
+		char *pathleft;
+		expr = nvpair_name(nvp);
+		if (gmatch(name, expr) == 0)
+			continue;
+		rv = nvpair_value_string(nvp, &pathleft);
+		if (rv != 0) {
+			cmn_err(CE_WARN, sdev_nvp_val_err,
+			    rv, nvpair_name(nvp));
+			break;
+		}
+		if (is_nonempty_dir(name, pathleft, dir)) {
+			sdcmn_err10(("prof_name_matched: dir %s\n", name));
+			return (1);
+		}
+	}
+
+	return (0);
+}
+
+static void
+walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *))
+{
+	char    *nm;
+	int eof, error;
+	struct iovec iov;
+	struct uio uio;
+	struct dirent64 *dp;
+	dirent64_t *dbuf;
+	size_t dbuflen, dlen;
+
+	ASSERT(dvp);
+
+	dlen = 4096;
+	dbuf = kmem_zalloc(dlen, KM_SLEEP);
+
+	uio.uio_iov = &iov;
+	uio.uio_iovcnt = 1;
+	uio.uio_segflg = UIO_SYSSPACE;
+	uio.uio_fmode = 0;
+	uio.uio_extflg = UIO_COPY_CACHED;
+	uio.uio_loffset = 0;
+	uio.uio_llimit = MAXOFFSET_T;
+
+	eof = 0;
+	error = 0;
+	while (!error && !eof) {
+		uio.uio_resid = dlen;
+		iov.iov_base = (char *)dbuf;
+		iov.iov_len = dlen;
+		(void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
+		error = VOP_READDIR(dvp, &uio, kcred, &eof);
+		VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
+
+		dbuflen = dlen - uio.uio_resid;
+		if (error || dbuflen == 0)
+			break;
+		for (dp = dbuf; ((intptr_t)dp <
+		    (intptr_t)dbuf + dbuflen);
+		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
+			nm = dp->d_name;
+
+			if (strcmp(nm, ".") == 0 ||
+			    strcmp(nm, "..") == 0)
+				continue;
+
+			if (callback(nm, arg) == WALK_DIR_TERMINATE)
+				goto end;
+		}
+	}
+
+end:
+	kmem_free(dbuf, dlen);
+}
+
+static int
+prof_make_name(char *nm, void *arg)
+{
+	struct sdev_node *ddv = (struct sdev_node *)arg;
+
+	if (prof_name_matched(nm, ddv))
+		prof_lookup_globaldev(ddv, ddv->sdev_origin, nm, nm);
+	return (WALK_DIR_CONTINUE);
+}
+
+static void
+prof_make_names_glob(struct sdev_node *ddv)
+{
+	struct sdev_node *gdir;
+
+	gdir = ddv->sdev_origin;
+	if (gdir == NULL)
+		return;
+	walk_dir(SDEVTOV(gdir), (void *)ddv, prof_make_name);
+}
+
+static void
+prof_make_names(struct sdev_node *dir)
+{
+	char *name;
+	nvpair_t *nvp = NULL;
+	nvlist_t *nvl = dir->sdev_prof.dev_name;
+	int rv;
+
+	ASSERT(RW_WRITE_HELD(&dir->sdev_contents));
+
+	if (nvl == NULL)
+		return;
+
+	if (dir->sdev_prof.has_glob) {
+		prof_make_names_glob(dir);
+		return;
+	}
+
+	/* Walk nvlist and lookup corresponding device in global inst */
+	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
+		int type;
+		rv = nvpair_value_int32(nvp, &type);
+		if (rv != 0) {
+			cmn_err(CE_WARN, sdev_nvp_val_err,
+			    rv, nvpair_name(nvp));
+			break;
+		}
+		if (type == PROFILE_TYPE_EXCLUDE)
+			continue;
+		name = nvpair_name(nvp);
+		(void) prof_lookup_globaldev(dir, dir->sdev_origin,
+		    name, name);
+	}
+}
+
+/*
+ * Build directory vnodes based on the profile and the global
+ * dev instance.
+ */
+void
+prof_filldir(struct sdev_node *ddv)
+{
+	int firsttime = 1;
+	struct sdev_node *gdir = ddv->sdev_origin;
+
+	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
+
+	/*
+	 * We need to rebuild the directory content if
+	 * - SDEV_BUILD is set
+	 * - The device tree generation number has changed
+	 * - The corresponding /dev namespace has been updated
+	 */
+check_build:
+	if ((ddv->sdev_flags & SDEV_BUILD) == 0 &&
+	    ddv->sdev_devtree_gen == devtree_gen &&
+	    (gdir == NULL || ddv->sdev_ldir_gen
+	    == gdir->sdev_gdir_gen))
+		return;		/* already up to date */
+
+	if (firsttime && rw_tryupgrade(&ddv->sdev_contents) == 0) {
+		rw_exit(&ddv->sdev_contents);
+		firsttime = 0;
+		rw_enter(&ddv->sdev_contents, RW_WRITER);
+		goto check_build;
+	}
+	sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n",
+	    ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen));
+	if (gdir)
+		sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n",
+		    ddv->sdev_path, ddv->sdev_ldir_gen,
+		    gdir->sdev_gdir_gen));
+
+	/* update flags and generation number so next filldir is quick */
+	ddv->sdev_flags &= ~SDEV_BUILD;
+	ddv->sdev_devtree_gen = devtree_gen;
+	if (gdir)
+		ddv->sdev_ldir_gen = gdir->sdev_gdir_gen;
+
+	prof_make_symlinks(ddv);
+	prof_make_maps(ddv);
+	prof_make_names(ddv);
+	rw_downgrade(&ddv->sdev_contents);
+}
+
+/* apply include/exclude pattern to existing directory content */
+static void
+apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type)
+{
+	struct sdev_node *dv;
+
+	/* leaf pattern */
+	if (pathleft == NULL) {
+		if (type == PROFILE_TYPE_INCLUDE)
+			return;	/* nothing to do for include */
+		(void) sdev_cleandir(dir, expr, SDEV_ENFORCE);
+		return;
+	}
+
+	/* directory pattern */
+	rw_enter(&dir->sdev_contents, RW_WRITER);
+	for (dv = dir->sdev_dot; dv; dv = dv->sdev_next) {
+		if (gmatch(dv->sdev_name, expr) == 0 ||
+		    SDEVTOV(dv)->v_type != VDIR)
+			continue;
+		process_rule(dv, dv->sdev_origin,
+		    pathleft, NULL, type);
+	}
+	rw_exit(&dir->sdev_contents);
+}
+
+/*
+ * Add a profile rule.
+ * tgt represents a device name matching expression,
+ * matching device names are to be either included or excluded.
+ */
+static void
+prof_add_rule(char *name, char *tgt, struct sdev_node *dir, int type)
+{
+	int error;
+	nvlist_t **nvlp = NULL;
+	int rv;
+
+	ASSERT(SDEVTOV(dir)->v_type == VDIR);
+
+	rw_enter(&dir->sdev_contents, RW_WRITER);
+
+	switch (type) {
+	case PROFILE_TYPE_INCLUDE:
+		if (tgt)
+			nvlp = &(dir->sdev_prof.dev_glob_incdir);
+		else
+			nvlp = &(dir->sdev_prof.dev_name);
+		break;
+	case PROFILE_TYPE_EXCLUDE:
+		if (tgt)
+			nvlp = &(dir->sdev_prof.dev_glob_excdir);
+		else
+			nvlp = &(dir->sdev_prof.dev_name);
+		break;
+	case PROFILE_TYPE_MAP:
+		nvlp = &(dir->sdev_prof.dev_map);
+		break;
+	case PROFILE_TYPE_SYMLINK:
+		nvlp = &(dir->sdev_prof.dev_symlink);
+		break;
+	};
+
+	/* initialize nvlist */
+	if (*nvlp == NULL) {
+		error = nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP);
+		ASSERT(error == 0);
+	}
+
+	if (tgt) {
+		rv = nvlist_add_string(*nvlp, name, tgt);
+	} else {
+		rv = nvlist_add_int32(*nvlp, name, type);
+	}
+	ASSERT(rv == 0);
+	/* rebuild directory content */
+	dir->sdev_flags |= SDEV_BUILD;
+
+	if ((type == PROFILE_TYPE_INCLUDE) &&
+	    (strpbrk(name, "*?[]") != NULL)) {
+			dir->sdev_prof.has_glob = 1;
+	}
+
+	rw_exit(&dir->sdev_contents);
+
+	/* additional details for glob pattern and exclusion */
+	switch (type) {
+	case PROFILE_TYPE_INCLUDE:
+	case PROFILE_TYPE_EXCLUDE:
+		apply_dir_pattern(dir, name, tgt, type);
+		break;
+	};
+}
+
+/*
+ * Parse path components and apply requested matching rule at
+ * directory level.
+ */
+static void
+process_rule(struct sdev_node *dir, struct sdev_node *gdir,
+    char *path, char *tgt, int type)
+{
+	char *name;
+	struct pathname	pn;
+	int rv = 0;
+
+	if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) {
+		path += 5;
+	}
+
+	if (pn_get(path, UIO_SYSSPACE, &pn) != 0)
+		return;
+
+	name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+	(void) pn_getcomponent(&pn, name);
+	pn_skipslash(&pn);
+	SDEV_HOLD(dir);
+
+	while (pn_pathleft(&pn)) {
+		/* If this is pattern, just add the pattern */
+		if (strpbrk(name, "*?[]") != NULL &&
+		    (type == PROFILE_TYPE_INCLUDE ||
+		    type == PROFILE_TYPE_EXCLUDE)) {
+			ASSERT(tgt == NULL);
+			tgt = pn.pn_path;
+			break;
+		}
+		if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) {
+			cmn_err(CE_CONT, "process_rule: %s error %d\n",
+			    path, rv);
+			break;
+		}
+		(void) pn_getcomponent(&pn, name);
+		pn_skipslash(&pn);
+	}
+
+	/* process the leaf component */
+	if (rv == 0) {
+		prof_add_rule(name, tgt, dir, type);
+		SDEV_SIMPLE_RELE(dir);
+	}
+
+	kmem_free(name, MAXPATHLEN);
+	pn_free(&pn);
+}
+
+static int
+copyin_nvlist(char *packed_usr, size_t packed_sz, nvlist_t **nvlp)
+{
+	int err = 0;
+	char *packed;
+	nvlist_t *profile = NULL;
+
+	/* simple sanity check */
+	if (packed_usr == NULL || packed_sz == 0)
+		return (NULL);
+
+	/* copyin packed profile nvlist */
+	packed = kmem_alloc(packed_sz, KM_NOSLEEP);
+	if (packed == NULL)
+		return (ENOMEM);
+	err = copyin(packed_usr, packed, packed_sz);
+
+	/* unpack packed profile nvlist */
+	if (err)
+		cmn_err(CE_WARN, "copyin_nvlist: copyin failed with "
+		    "err %d\n", err);
+	else if (err = nvlist_unpack(packed, packed_sz, &profile, KM_NOSLEEP))
+		cmn_err(CE_WARN, "copyin_nvlist: nvlist_unpack "
+		    "failed with err %d\n", err);
+
+	kmem_free(packed, packed_sz);
+	if (err == 0)
+		*nvlp = profile;
+	return (err);
+}
+
+/*
+ * Process profile passed down from libdevinfo. There are four types
+ * of matching rules:
+ *  include: export a name or names matching a pattern
+ *  exclude: exclude a name or names matching a pattern
+ *  symlink: create a local symlink
+ *  map:     export a device with a name different from the global zone
+ * Note: We may consider supporting VOP_SYMLINK in non-global instances,
+ *	because it does not present any security risk. For now, the fs
+ *	instance is read only.
+ */
+static void
+sdev_process_profile(struct sdev_data *sdev_data, nvlist_t *profile)
+{
+	nvpair_t *nvpair;
+	char *nvname, *dname;
+	struct sdev_node *dir, *gdir;
+	char **pair;				/* for symlinks and maps */
+	uint_t nelem;
+	int rv;
+
+	gdir = sdev_origins->sdev_root;	/* root of global /dev */
+	dir = sdev_data->sdev_root;	/* root of current instance */
+
+	ASSERT(profile);
+
+	/* process nvpairs in the list */
+	nvpair = NULL;
+	while (nvpair = nvlist_next_nvpair(profile, nvpair)) {
+		nvname = nvpair_name(nvpair);
+		ASSERT(nvname != NULL);
+
+		if (strcmp(nvname, SDEV_NVNAME_INCLUDE) == 0) {
+			rv = nvpair_value_string(nvpair, &dname);
+			if (rv != 0) {
+				cmn_err(CE_WARN, sdev_nvp_val_err,
+				    rv, nvpair_name(nvpair));
+				break;
+			}
+			process_rule(dir, gdir, dname, NULL,
+			    PROFILE_TYPE_INCLUDE);
+		} else if (strcmp(nvname, SDEV_NVNAME_EXCLUDE) == 0) {
+			rv = nvpair_value_string(nvpair, &dname);
+			if (rv != 0) {
+				cmn_err(CE_WARN, sdev_nvp_val_err,
+				    rv, nvpair_name(nvpair));
+				break;
+			}
+			process_rule(dir, gdir, dname, NULL,
+			    PROFILE_TYPE_EXCLUDE);
+		} else if (strcmp(nvname, SDEV_NVNAME_SYMLINK) == 0) {
+			rv = nvpair_value_string_array(nvpair, &pair, &nelem);
+			if (rv != 0) {
+				cmn_err(CE_WARN, sdev_nvp_val_err,
+				    rv, nvpair_name(nvpair));
+				break;
+			}
+			ASSERT(nelem == 2);
+			process_rule(dir, gdir, pair[0], pair[1],
+			    PROFILE_TYPE_SYMLINK);
+		} else if (strcmp(nvname, SDEV_NVNAME_MAP) == 0) {
+			rv = nvpair_value_string_array(nvpair, &pair, &nelem);
+			if (rv != 0) {
+				cmn_err(CE_WARN, sdev_nvp_val_err,
+				    rv, nvpair_name(nvpair));
+				break;
+			}
+			process_rule(dir, gdir, pair[1], pair[0],
+			    PROFILE_TYPE_MAP);
+		} else if (strcmp(nvname, SDEV_NVNAME_MOUNTPT) != 0) {
+			cmn_err(CE_WARN, "sdev_process_profile: invalid "
+			    "nvpair %s\n", nvname);
+		}
+	}
+}
+
+/*ARGSUSED*/
+int
+prof_lookup(vnode_t *dvp, char *nm, struct vnode **vpp, struct cred *cred)
+{
+	struct sdev_node *ddv = VTOSDEV(dvp);
+	struct sdev_node *dv;
+	int nmlen;
+
+	/*
+	 * Empty name or ., return node itself.
+	 */
+	nmlen = strlen(nm);
+	if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
+		*vpp = SDEVTOV(ddv);
+		VN_HOLD(*vpp);
+		return (0);
+	}
+
+	/*
+	 * .., return the parent directory
+	 */
+	if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
+		*vpp = SDEVTOV(ddv->sdev_dotdot);
+		VN_HOLD(*vpp);
+		return (0);
+	}
+
+	rw_enter(&ddv->sdev_contents, RW_READER);
+	dv = sdev_cache_lookup(ddv, nm);
+	if (dv == NULL) {
+		prof_filldir(ddv);
+		dv = sdev_cache_lookup(ddv, nm);
+	}
+	rw_exit(&ddv->sdev_contents);
+	if (dv == NULL) {
+		sdcmn_err10(("prof_lookup: %s not found\n", nm));
+		return (ENOENT);
+	}
+
+	return (sdev_to_vp(dv, vpp));
+}
+
+/*
+ * This is invoked after a new filesystem is mounted to define the
+ * name space. It is also invoked during normal system operation
+ * to update the name space.
+ *
+ * Applications call di_prof_commit() in libdevinfo, which invokes
+ * modctl(). modctl calls this function. The input is a packed nvlist.
+ */
+int
+devname_profile_update(char *packed, size_t packed_sz)
+{
+	char *mntpt;
+	nvlist_t *nvl;
+	nvpair_t *nvp;
+	struct sdev_data *mntinfo;
+	int err;
+	int rv;
+
+	nvl = NULL;
+	if ((err = copyin_nvlist(packed, packed_sz, &nvl)) != 0)
+		return (err);
+	ASSERT(nvl);
+
+	/* The first nvpair must be the mount point */
+	nvp = nvlist_next_nvpair(nvl, NULL);
+	if (strcmp(nvpair_name(nvp), SDEV_NVNAME_MOUNTPT) != 0) {
+		cmn_err(CE_NOTE,
+		    "devname_profile_update: mount point not specified");
+		nvlist_free(nvl);
+		return (EINVAL);
+	}
+
+	/* find the matching filesystem instance */
+	rv = nvpair_value_string(nvp, &mntpt);
+	if (rv != 0) {
+		cmn_err(CE_WARN, sdev_nvp_val_err,
+		    rv, nvpair_name(nvp));
+	} else {
+		mntinfo = sdev_find_mntinfo(mntpt);
+		if (mntinfo == NULL) {
+			cmn_err(CE_NOTE, "devname_profile_update: "
+			    " mount point %s not found", mntpt);
+			nvlist_free(nvl);
+			return (EINVAL);
+		}
+
+		/* now do the hardwork to process the profile */
+		sdev_process_profile(mntinfo, nvl);
+
+		sdev_mntinfo_rele(mntinfo);
+	}
+
+	nvlist_free(nvl);
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/fs/dev/sdev_ptsops.c	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,398 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * vnode ops for the /dev/pts directory
+ *	The lookup is based on the internal pty table. We also
+ *	override readdir in order to delete pts nodes no longer
+ *	in use.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/sunndi.h>
+#include <fs/fs_subr.h>
+#include <sys/fs/dv_node.h>
+#include <sys/fs/sdev_impl.h>
+#include <sys/policy.h>
+#include <sys/ptms.h>
+#include <sys/stat.h>
+
+#define	DEVPTS_UID_DEFAULT	0
+#define	DEVPTS_GID_DEFAULT	3
+#define	DEVPTS_DEVMODE_DEFAULT	(0620)
+
+#define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
+
+static vattr_t devpts_vattr = {
+	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
+	VCHR,					/* va_type */
+	S_IFCHR | DEVPTS_DEVMODE_DEFAULT,	/* va_mode */
+	DEVPTS_UID_DEFAULT,			/* va_uid */
+	DEVPTS_GID_DEFAULT,			/* va_gid */
+	0					/* 0 hereafter */
+};
+
+struct vnodeops		*devpts_vnodeops;
+
+struct vnodeops *
+devpts_getvnodeops(void)
+{
+	return (devpts_vnodeops);
+}
+
+/*
+ * Convert string to minor number. Some care must be taken
+ * as we are processing user input. Catch cases like
+ * /dev/pts/4foo and /dev/pts/-1
+ */
+static int
+devpts_strtol(const char *nm, minor_t *mp)
+{
+	long uminor = 0;
+	char *endptr = NULL;
+
+	if (nm == NULL || !isdigit(*nm))
+		return (EINVAL);
+
+	*mp = 0;
+	if (ddi_strtol(nm, &endptr, 10, &uminor) != 0 ||
+	    *endptr != '\0' || uminor < 0) {
+		return (EINVAL);
+	}
+
+	*mp = uminor;
+	return (0);
+}
+
+/*
+ * Check if a pts sdev_node is still valid - i.e. it represents a current pty.
+ * This serves two purposes
+ *	- only valid pts nodes are returned during lookup() and readdir().
+ *	- since pts sdev_nodes are not actively destroyed when a pty goes
+ *	  away, we use the validator to do deferred cleanup i.e. when such
+ *	  nodes are encountered during subsequent lookup() and readdir().
+ */
+/*ARGSUSED*/
+int
+devpts_validate(struct sdev_node *dv)
+{
+	minor_t min;
+	uid_t uid;
+	gid_t gid;
+	timestruc_t now;
+	char *nm = dv->sdev_name;
+
+	ASSERT(!(dv->sdev_flags & SDEV_STALE));
+	ASSERT(dv->sdev_state == SDEV_READY);
+
+	/* validate only READY nodes */
+	if (dv->sdev_state != SDEV_READY) {
+		sdcmn_err(("dev fs: skipping: node not ready %s(%p)",
+		    nm, (void *)dv));
+		return (SDEV_VTOR_SKIP);
+	}
+
+	if (devpts_strtol(nm, &min) != 0) {
+		sdcmn_err7(("devpts_validate: not a valid minor: %s\n", nm));
+		return (SDEV_VTOR_INVALID);
+	}
+
+	/*
+	 * Check if pts driver is attached
+	 */
+	if (ptms_slave_attached() == (major_t)-1) {
+		sdcmn_err7(("devpts_validate: slave not attached\n"));
+		return (SDEV_VTOR_INVALID);
+	}
+
+	if (ptms_minor_valid(min, &uid, &gid) == 0) {
+		if (ptms_minor_exists(min)) {
+			sdcmn_err7(("devpts_validate: valid in different zone "
+			    "%s\n", nm));
+			return (SDEV_VTOR_SKIP);
+		} else {
+			sdcmn_err7(("devpts_validate: %s not valid pty\n",
+			    nm));
+			return (SDEV_VTOR_INVALID);
+		}
+	}
+
+	ASSERT(dv->sdev_attr);
+	if (dv->sdev_attr->va_uid != uid || dv->sdev_attr->va_gid != gid) {
+		ASSERT(uid >= 0);
+		ASSERT(gid >= 0);
+		dv->sdev_attr->va_uid = uid;
+		dv->sdev_attr->va_gid = gid;
+		gethrestime(&now);
+		dv->sdev_attr->va_atime = now;
+		dv->sdev_attr->va_mtime = now;
+		dv->sdev_attr->va_ctime = now;
+		sdcmn_err7(("devpts_validate: update uid/gid/times%s\n", nm));
+	}
+
+	return (SDEV_VTOR_VALID);
+}
+
+/*
+ * This callback is invoked from devname_lookup_func() to create
+ * a pts entry when the node is not found in the cache.
+ */
+/*ARGSUSED*/
+static int
+devpts_create_rvp(struct sdev_node *ddv, char *nm,
+    void **arg, cred_t *cred, void *whatever, char *whichever)
+{
+	minor_t min;
+	major_t maj;
+	uid_t uid;
+	gid_t gid;
+	timestruc_t now;
+	struct vattr *vap = (struct vattr *)arg;
+
+	if (devpts_strtol(nm, &min) != 0) {
+		sdcmn_err7(("devpts_create_rvp: not a valid minor: %s\n", nm));
+		return (-1);
+	}
+
+	/*
+	 * Check if pts driver is attached and if it is
+	 * get the major number.
+	 */
+	maj = ptms_slave_attached();
+	if (maj == (major_t)-1) {
+		sdcmn_err7(("devpts_create_rvp: slave not attached\n"));
+		return (-1);
+	}
+
+	/*
+	 * Only allow creation of ptys allocated to our zone
+	 */
+	if (!ptms_minor_valid(min, &uid, &gid)) {
+		sdcmn_err7(("devpts_create_rvp: %s not valid pty"
+		    "or not valid in this zone\n", nm));
+		return (-1);
+	}
+
+
+	/*
+	 * This is a valid pty (at least at this point in time).
+	 * Create the node by setting the attribute. The rest
+	 * is taken care of by devname_lookup_func().
+	 */
+	*vap = devpts_vattr;
+	vap->va_rdev = makedevice(maj, min);
+	ASSERT(uid >= 0);
+	ASSERT(gid >= 0);
+	vap->va_uid = uid;
+	vap->va_gid = gid;
+	gethrestime(&now);
+	vap->va_atime = now;
+	vap->va_mtime = now;
+	vap->va_ctime = now;
+
+	return (0);
+}
+
+/*
+ * Clean pts sdev_nodes that are no longer valid.
+ */
+static void
+devpts_prunedir(struct sdev_node *ddv)
+{
+	struct vnode *vp;
+	struct sdev_node *dv, *next = NULL;
+	int (*vtor)(struct sdev_node *) = NULL;
+
+	ASSERT(ddv->sdev_flags & SDEV_VTOR);
+
+	vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
+	ASSERT(vtor);
+
+	if (rw_tryupgrade(&ddv->sdev_contents) == NULL) {
+		rw_exit(&ddv->sdev_contents);
+		rw_enter(&ddv->sdev_contents, RW_WRITER);
+	}
+
+	for (dv = ddv->sdev_dot; dv; dv = next) {
+		next = dv->sdev_next;
+
+		/* skip stale nodes */
+		if (dv->sdev_flags & SDEV_STALE)
+			continue;
+
+		/* validate and prune only ready nodes */
+		if (dv->sdev_state != SDEV_READY)
+			continue;
+
+		switch (vtor(dv)) {
+		case SDEV_VTOR_VALID:
+		case SDEV_VTOR_SKIP:
+			continue;
+		case SDEV_VTOR_INVALID:
+			sdcmn_err7(("prunedir: destroy invalid "
+			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
+			break;
+		}
+		vp = SDEVTOV(dv);
+		if (vp->v_count > 0)
+			continue;
+		SDEV_HOLD(dv);
+		/* remove the cache node */
+		(void) sdev_cache_update(ddv, &dv, dv->sdev_name,
+		    SDEV_CACHE_DELETE);
+	}
+	rw_downgrade(&ddv->sdev_contents);
+}
+
+/*
+ * Lookup for /dev/pts directory
+ *	If the entry does not exist, the devpts_create_rvp() callback
+ *	is invoked to create it. Nodes do not persist across reboot.
+ */
+/*ARGSUSED3*/
+static int
+devpts_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
+    struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred)
+{
+	struct sdev_node *sdvp = VTOSDEV(dvp);
+	struct sdev_node *dv;
+	int error;
+
+	error = devname_lookup_func(sdvp, nm, vpp, cred, devpts_create_rvp,
+	    SDEV_VATTR);
+
+	if (error == 0) {
+		switch ((*vpp)->v_type) {
+		case VCHR:
+			dv = VTOSDEV(VTOS(*vpp)->s_realvp);
+			break;
+		case VDIR:
+			dv = VTOSDEV(*vpp);
+			break;
+		default:
+			cmn_err(CE_PANIC, "devpts_lookup: Unsupported node "
+			    "type: %p: %d", (void *)(*vpp), (*vpp)->v_type);
+			break;
+		}
+		ASSERT(SDEV_HELD(dv));
+	}
+
+	return (error);
+}
+
+/*
+ * We allow create to find existing nodes
+ *	- if the node doesn't exist - EROFS
+ *	- creating an existing dir read-only succeeds, otherwise EISDIR
+ *	- exclusive creates fail - EEXIST
+ */
+/*ARGSUSED2*/
+static int
+devpts_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
+    int mode, struct vnode **vpp, struct cred *cred, int flag)
+{
+	int error;
+	struct vnode *vp;
+
+	*vpp = NULL;
+
+	error = devpts_lookup(dvp, nm, &vp, NULL, 0, NULL, cred);
+	if (error == 0) {
+		if (excl == EXCL)
+			error = EEXIST;
+		else if (vp->v_type == VDIR && (mode & VWRITE))
+			error = EISDIR;
+		else
+			error = VOP_ACCESS(vp, mode, 0, cred);
+
+		if (error) {
+			VN_RELE(vp);
+		} else
+			*vpp = vp;
+	} else if (error == ENOENT) {
+		error = EROFS;
+	}
+
+	return (error);
+}
+
+/*
+ * Display all instantiated pts (slave) device nodes.
+ * A /dev/pts entry will be created only after the first lookup of the slave
+ * device succeeds.
+ */
+static int
+devpts_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
+    int *eofp)
+{
+	struct sdev_node *sdvp = VTOSDEV(dvp);
+	if (uiop->uio_offset == 0) {
+		devpts_prunedir(sdvp);
+	}
+
+	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
+}
+
+
+static int
+devpts_set_id(struct sdev_node *dv, struct vattr *vap, int protocol)
+{
+	ASSERT((protocol & AT_UID) || (protocol & AT_GID));
+	ptms_set_owner(getminor(SDEVTOV(dv)->v_rdev),
+	    vap->va_uid, vap->va_gid);
+	return (0);
+
+}
+
+static int
+devpts_setattr(struct vnode *vp, struct vattr *vap, int flags,
+    struct cred *cred)
+{
+	ASSERT((vp->v_type == VCHR) || (vp->v_type == VDIR));
+	return (devname_setattr_func(vp, vap, flags, cred,
+		    devpts_set_id, AT_UID|AT_GID));
+}
+
+/*
+ * We override lookup and readdir to build entries based on the
+ * in kernel pty table. Also override setattr/setsecattr to
+ * avoid persisting permissions.
+ */
+const fs_operation_def_t devpts_vnodeops_tbl[] = {
+	VOPNAME_READDIR, devpts_readdir,
+	VOPNAME_LOOKUP, devpts_lookup,
+	VOPNAME_CREATE, devpts_create,
+	VOPNAME_SETATTR, devpts_setattr,
+	VOPNAME_REMOVE, fs_nosys,
+	VOPNAME_MKDIR, fs_nosys,
+	VOPNAME_RMDIR, fs_nosys,
+	VOPNAME_SYMLINK, fs_nosys,
+	VOPNAME_SETSECATTR, fs_nosys,
+	NULL, NULL
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/fs/dev/sdev_subr.c	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,3657 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * utility routines for the /dev fs
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/t_lock.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/user.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/flock.h>
+#include <sys/kmem.h>
+#include <sys/uio.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/cred.h>
+#include <sys/dirent.h>
+#include <sys/pathname.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/mode.h>
+#include <sys/policy.h>
+#include <fs/fs_subr.h>
+#include <sys/mount.h>
+#include <sys/fs/snode.h>
+#include <sys/fs/dv_node.h>
+#include <sys/fs/sdev_impl.h>
+#include <sys/fs/sdev_node.h>
+#include <sys/sunndi.h>
+#include <sys/sunmdi.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/modctl.h>
+
+#ifdef DEBUG
+int sdev_debug = 0x00000001;
+int sdev_debug_cache_flags = 0;
+#endif
+
+/*
+ * globals
+ */
+/* prototype memory vattrs */
+vattr_t sdev_vattr_dir = {
+	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
+	VDIR,					/* va_type */
+	SDEV_DIRMODE_DEFAULT,			/* va_mode */
+	SDEV_UID_DEFAULT,			/* va_uid */
+	SDEV_GID_DEFAULT,			/* va_gid */
+	0,					/* va_fsid */
+	0,					/* va_nodeid */
+	0,					/* va_nlink */
+	0,					/* va_size */
+	0,					/* va_atime */
+	0,					/* va_mtime */
+	0,					/* va_ctime */
+	0,					/* va_rdev */
+	0,					/* va_blksize */
+	0,					/* va_nblocks */
+	0					/* va_vcode */
+};
+
+vattr_t sdev_vattr_lnk = {
+	AT_TYPE|AT_MODE,			/* va_mask */
+	VLNK,					/* va_type */
+	SDEV_LNKMODE_DEFAULT,			/* va_mode */
+	SDEV_UID_DEFAULT,			/* va_uid */
+	SDEV_GID_DEFAULT,			/* va_gid */
+	0,					/* va_fsid */
+	0,					/* va_nodeid */
+	0,					/* va_nlink */
+	0,					/* va_size */
+	0,					/* va_atime */
+	0,					/* va_mtime */
+	0,					/* va_ctime */
+	0,					/* va_rdev */
+	0,					/* va_blksize */
+	0,					/* va_nblocks */
+	0					/* va_vcode */
+};
+
+vattr_t sdev_vattr_blk = {
+	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
+	VBLK,					/* va_type */
+	S_IFBLK | SDEV_DEVMODE_DEFAULT,		/* va_mode */
+	SDEV_UID_DEFAULT,			/* va_uid */
+	SDEV_GID_DEFAULT,			/* va_gid */
+	0,					/* va_fsid */
+	0,					/* va_nodeid */
+	0,					/* va_nlink */
+	0,					/* va_size */
+	0,					/* va_atime */
+	0,					/* va_mtime */
+	0,					/* va_ctime */
+	0,					/* va_rdev */
+	0,					/* va_blksize */
+	0,					/* va_nblocks */
+	0					/* va_vcode */
+};
+
+vattr_t sdev_vattr_chr = {
+	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
+	VCHR,					/* va_type */
+	S_IFCHR | SDEV_DEVMODE_DEFAULT,		/* va_mode */
+	SDEV_UID_DEFAULT,			/* va_uid */
+	SDEV_GID_DEFAULT,			/* va_gid */
+	0,					/* va_fsid */
+	0,					/* va_nodeid */
+	0,					/* va_nlink */
+	0,					/* va_size */
+	0,					/* va_atime */
+	0,					/* va_mtime */
+	0,					/* va_ctime */
+	0,					/* va_rdev */
+	0,					/* va_blksize */
+	0,					/* va_nblocks */
+	0					/* va_vcode */
+};
+
+kmem_cache_t	*sdev_node_cache;	/* sdev_node cache */
+int		devtype;		/* fstype */
+
+struct devname_ops *devname_ns_ops;	/* default name service directory ops */
+kmutex_t devname_nsmaps_lock;	/* protect devname_nsmaps */
+
+/* static */
+static struct devname_nsmap *devname_nsmaps = NULL;
+				/* contents from /etc/dev/devname_master */
+static int devname_nsmaps_invalidated = 0; /* "devfsadm -m" has run */
+
+static struct vnodeops *sdev_get_vop(struct sdev_node *);
+static void sdev_set_no_nocache(struct sdev_node *);
+static int sdev_get_moduleops(struct sdev_node *);
+static void sdev_handle_alloc(struct sdev_node *);
+static fs_operation_def_t *sdev_merge_vtab(const fs_operation_def_t []);
+static void sdev_free_vtab(fs_operation_def_t *);
+
+static void
+sdev_prof_free(struct sdev_node *dv)
+{
+	ASSERT(!SDEV_IS_GLOBAL(dv));
+	if (dv->sdev_prof.dev_name)
+		nvlist_free(dv->sdev_prof.dev_name);
+	if (dv->sdev_prof.dev_map)
+		nvlist_free(dv->sdev_prof.dev_map);
+	if (dv->sdev_prof.dev_symlink)
+		nvlist_free(dv->sdev_prof.dev_symlink);
+	if (dv->sdev_prof.dev_glob_incdir)
+		nvlist_free(dv->sdev_prof.dev_glob_incdir);
+	if (dv->sdev_prof.dev_glob_excdir)
+		nvlist_free(dv->sdev_prof.dev_glob_excdir);
+	bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
+}
+
+/*
+ * sdev_node cache constructor
+ */
+/*ARGSUSED1*/
+static int
+i_sdev_node_ctor(void *buf, void *cfarg, int flag)
+{
+	struct sdev_node *dv = (struct sdev_node *)buf;
+	struct vnode *vp;
+
+	ASSERT(flag == KM_SLEEP);
+
+	bzero(buf, sizeof (struct sdev_node));
+	rw_init(&dv->sdev_contents, NULL, RW_DEFAULT, NULL);
+	dv->sdev_vnode = vn_alloc(KM_SLEEP);
+	vp = SDEVTOV(dv);
+	vp->v_data = (caddr_t)dv;
+	return (0);
+}
+
+/* sdev_node destructor for kmem cache */
+/*ARGSUSED1*/
+static void
+i_sdev_node_dtor(void *buf, void *arg)
+{
+	struct sdev_node *dv = (struct sdev_node *)buf;
+	struct vnode *vp = SDEVTOV(dv);
+
+	rw_destroy(&dv->sdev_contents);
+	vn_free(vp);
+}
+
+/* initialize sdev_node cache */
+void
+sdev_node_cache_init()
+{
+	int flags = 0;
+
+#ifdef	DEBUG
+	flags = sdev_debug_cache_flags;
+	if (flags)
+		sdcmn_err(("cache debug flags 0x%x\n", flags));
+#endif	/* DEBUG */
+
+	ASSERT(sdev_node_cache == NULL);
+	sdev_node_cache = kmem_cache_create("sdev_node_cache",
+	    sizeof (struct sdev_node), 0, i_sdev_node_ctor, i_sdev_node_dtor,
+	    NULL, NULL, NULL, flags);
+}
+
+/* destroy sdev_node cache */
+void
+sdev_node_cache_fini()
+{
+	ASSERT(sdev_node_cache != NULL);
+	kmem_cache_destroy(sdev_node_cache);
+	sdev_node_cache = NULL;
+}
+
+void
+sdev_set_nodestate(struct sdev_node *dv, sdev_node_state_t state)
+{
+	ASSERT(dv);
+	ASSERT(RW_WRITE_HELD(&dv->sdev_contents));
+	dv->sdev_state = state;
+}
+
+static void
+sdev_attrinit(struct sdev_node *dv, vattr_t *vap)
+{
+	timestruc_t now;
+
+	ASSERT(vap);
+
+	dv->sdev_attr = kmem_zalloc(sizeof (struct vattr), KM_SLEEP);
+	*dv->sdev_attr = *vap;
+
+	dv->sdev_attr->va_mode = MAKEIMODE(vap->va_type, vap->va_mode);
+
+	gethrestime(&now);
+	dv->sdev_attr->va_atime = now;
+	dv->sdev_attr->va_mtime = now;
+	dv->sdev_attr->va_ctime = now;
+}
+
+/* alloc and initialize a sdev_node */
+int
+sdev_nodeinit(struct sdev_node *ddv, char *nm, struct sdev_node **newdv,
+    vattr_t *vap)
+{
+	struct sdev_node *dv = NULL;
+	struct vnode *vp;
+	size_t nmlen, len;
+	devname_handle_t  *dhl;
+
+	nmlen = strlen(nm) + 1;
+	if (nmlen > MAXNAMELEN) {
+		sdcmn_err9(("sdev_nodeinit: node name %s"
+		    " too long\n", nm));
+		*newdv = NULL;
+		return (ENAMETOOLONG);
+	}
+
+	dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP);
+
+	dv->sdev_name = kmem_alloc(nmlen, KM_SLEEP);
+	bcopy(nm, dv->sdev_name, nmlen);
+	dv->sdev_namelen = nmlen - 1;	/* '\0' not included */
+	len = strlen(ddv->sdev_path) + strlen(nm) + 2;
+	dv->sdev_path = kmem_alloc(len, KM_SLEEP);
+	(void) snprintf(dv->sdev_path, len, "%s/%s", ddv->sdev_path, nm);
+	/* overwritten for VLNK nodes */
+	dv->sdev_symlink = NULL;
+
+	vp = SDEVTOV(dv);
+	vn_reinit(vp);
+	vp->v_vfsp = SDEVTOV(ddv)->v_vfsp;
+	if (vap)
+		vp->v_type = vap->va_type;
+
+	/*
+	 * initialized to the parent's vnodeops.
+	 * maybe overwriten for a VDIR
+	 */
+	vn_setops(vp, vn_getops(SDEVTOV(ddv)));
+	vn_exists(vp);
+
+	dv->sdev_dotdot = NULL;
+	dv->sdev_dot = NULL;
+	dv->sdev_next = NULL;
+	dv->sdev_attrvp = NULL;
+	if (vap) {
+		sdev_attrinit(dv, vap);
+	} else {
+		dv->sdev_attr = NULL;
+	}
+
+	dv->sdev_ino = sdev_mkino(dv);
+	dv->sdev_nlink = 0;		/* updated on insert */
+	dv->sdev_flags = ddv->sdev_flags; /* inherit from the parent first */
+	dv->sdev_flags |= SDEV_BUILD;
+	mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL);
+	if (SDEV_IS_GLOBAL(ddv)) {
+		dv->sdev_flags |= SDEV_GLOBAL;
+		dv->sdev_mapinfo = NULL;
+		dhl = &(dv->sdev_handle);
+		dhl->dh_data = dv;
+		dhl->dh_spec = DEVNAME_NS_NONE;
+		dhl->dh_args = NULL;
+		sdev_set_no_nocache(dv);
+		dv->sdev_gdir_gen = 0;
+	} else {
+		dv->sdev_flags &= ~SDEV_GLOBAL;
+		dv->sdev_origin = NULL; /* set later */
+		bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
+		dv->sdev_ldir_gen = 0;
+		dv->sdev_devtree_gen = 0;
+	}
+
+	rw_enter(&dv->sdev_contents, RW_WRITER);
+	sdev_set_nodestate(dv, SDEV_INIT);
+	rw_exit(&dv->sdev_contents);
+	*newdv = dv;
+
+	return (0);
+}
+
+/*
+ * transition a sdev_node into SDEV_READY state
+ */
+int
+sdev_nodeready(struct sdev_node *dv, struct vattr *vap, struct vnode *avp,
+    void *args, struct cred *cred)
+{
+	int error = 0;
+	struct vnode *vp = SDEVTOV(dv);
+	vtype_t type;
+
+	ASSERT(dv && (dv->sdev_state != SDEV_READY) && vap);
+
+	type = vap->va_type;
+	vp->v_type = type;
+	vp->v_rdev = vap->va_rdev;
+	rw_enter(&dv->sdev_contents, RW_WRITER);
+	if (type == VDIR) {
+		dv->sdev_nlink = 2;
+		dv->sdev_flags &= ~SDEV_PERSIST;
+		dv->sdev_flags &= ~SDEV_DYNAMIC;
+		vn_setops(vp, sdev_get_vop(dv)); /* from internal vtab */
+		error = sdev_get_moduleops(dv); /* from plug-in module */
+		ASSERT(dv->sdev_dotdot);
+		ASSERT(SDEVTOV(dv->sdev_dotdot)->v_type == VDIR);
+		vp->v_rdev = SDEVTOV(dv->sdev_dotdot)->v_rdev;
+	} else if (type == VLNK) {
+		ASSERT(args);
+		dv->sdev_nlink = 1;
+		dv->sdev_symlink = i_ddi_strdup((char *)args, KM_SLEEP);
+	} else {
+		dv->sdev_nlink = 1;
+	}
+
+	if (!(SDEV_IS_GLOBAL(dv))) {
+		dv->sdev_origin = (struct sdev_node *)args;
+		dv->sdev_flags &= ~SDEV_PERSIST;
+	}
+
+	/*
+	 * shadow node is created here OR
+	 * if failed (indicated by dv->sdev_attrvp == NULL),
+	 * created later in sdev_setattr
+	 */
+	if (avp) {
+		dv->sdev_attrvp = avp;
+	} else {
+		if (dv->sdev_attr == NULL)
+			sdev_attrinit(dv, vap);
+		else
+			*dv->sdev_attr = *vap;
+
+		if ((SDEV_IS_PERSIST(dv) && (dv->sdev_attrvp == NULL)) ||
+		    ((SDEVTOV(dv)->v_type == VDIR) &&
+		    (dv->sdev_attrvp == NULL)))
+			error = sdev_shadow_node(dv, cred);
+	}
+
+	/* transition to READY state */
+	sdev_set_nodestate(dv, SDEV_READY);
+	sdev_nc_node_exists(dv);
+	rw_exit(&dv->sdev_contents);
+	return (error);
+}
+
+/*
+ * setting ZOMBIE state
+ */
+static int
+sdev_nodezombied(struct sdev_node *dv)
+{
+	rw_enter(&dv->sdev_contents, RW_WRITER);
+	sdev_set_nodestate(dv, SDEV_ZOMBIE);
+	rw_exit(&dv->sdev_contents);
+	return (0);
+}
+
+/*
+ * Build the VROOT sdev_node.
+ */
+/*ARGSUSED*/
+struct sdev_node *
+sdev_mkroot(struct vfs *vfsp, dev_t devdev, struct vnode *mvp,
+    struct vnode *avp, struct cred *cred)
+{
+	struct sdev_node *dv;
+	struct vnode *vp;
+	char devdir[] = "/dev";
+
+	ASSERT(sdev_node_cache != NULL);
+	ASSERT(avp);
+	dv = kmem_cache_alloc(sdev_node_cache, KM_SLEEP);
+	vp = SDEVTOV(dv);
+	vn_reinit(vp);
+	vp->v_flag |= VROOT;
+	vp->v_vfsp = vfsp;
+	vp->v_type = VDIR;
+	vp->v_rdev = devdev;
+	vn_setops(vp, sdev_vnodeops); /* apply the default vnodeops at /dev */
+	vn_exists(vp);
+
+	if (vfsp->vfs_mntpt)
+		dv->sdev_name = i_ddi_strdup(
+		    (char *)refstr_value(vfsp->vfs_mntpt), KM_SLEEP);
+	else
+		/* vfs_mountdev1 set mount point later */
+		dv->sdev_name = i_ddi_strdup("/dev", KM_SLEEP);
+	dv->sdev_namelen = strlen(dv->sdev_name); /* '\0' not included */
+	dv->sdev_path = i_ddi_strdup(devdir, KM_SLEEP);
+	dv->sdev_ino = SDEV_ROOTINO;
+	dv->sdev_nlink = 2;		/* name + . (no sdev_insert) */
+	dv->sdev_dotdot = dv;		/* .. == self */
+	dv->sdev_attrvp = avp;
+	dv->sdev_attr = NULL;
+	mutex_init(&dv->sdev_lookup_lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&dv->sdev_lookup_cv, NULL, CV_DEFAULT, NULL);
+	if (strcmp(dv->sdev_name, "/dev") == 0) {
+		mutex_init(&devname_nsmaps_lock, NULL, MUTEX_DEFAULT, NULL);
+		dv->sdev_mapinfo = NULL;
+		dv->sdev_flags = SDEV_BUILD|SDEV_GLOBAL|SDEV_PERSIST;
+		bzero(&dv->sdev_handle, sizeof (dv->sdev_handle));
+		dv->sdev_gdir_gen = 0;
+	} else {
+		dv->sdev_flags = SDEV_BUILD;
+		dv->sdev_flags &= ~SDEV_PERSIST;
+		bzero(&dv->sdev_prof, sizeof (dv->sdev_prof));
+		dv->sdev_ldir_gen = 0;
+		dv->sdev_devtree_gen = 0;
+	}
+
+	rw_enter(&dv->sdev_contents, RW_WRITER);
+	sdev_set_nodestate(dv, SDEV_READY);
+	rw_exit(&dv->sdev_contents);
+	sdev_nc_node_exists(dv);
+	return (dv);
+}
+
+/*
+ *  1. load the module
+ *  2. modload invokes sdev_module_register, which in turn sets
+ *     the dv->sdev_mapinfo->dir_ops
+ *
+ * note: locking order:
+ *	dv->sdev_contents -> map->dir_lock
+ */
+static int
+sdev_get_moduleops(struct sdev_node *dv)
+{
+	int error = 0;
+	struct devname_nsmap *map = NULL;
+	char *module;
+	char *path;
+	int load = 1;
+
+	ASSERT(SDEVTOV(dv)->v_type == VDIR);
+
+	if (devname_nsmaps == NULL)
+		return (0);
+
+	if (!sdev_nsmaps_loaded() && !sdev_nsmaps_reloaded())
+		return (0);
+
+
+	path = dv->sdev_path;
+	if ((map = sdev_get_nsmap_by_dir(path, 0))) {
+		rw_enter(&map->dir_lock, RW_READER);
+		if (map->dir_invalid) {
+			if (map->dir_module && map->dir_newmodule &&
+			    (strcmp(map->dir_module,
+					map->dir_newmodule) == 0)) {
+				load = 0;
+			}
+			sdev_replace_nsmap(map, map->dir_newmodule,
+			    map->dir_newmap);
+		}
+
+		module = map->dir_module;
+		if (module && load) {
+			sdcmn_err6(("sdev_get_moduleops: "
+			    "load module %s", module));
+			rw_exit(&map->dir_lock);
+			error = modload("devname", module);
+			sdcmn_err6(("sdev_get_moduleops: error %d\n", error));
+			if (error < 0) {
+				return (-1);
+			}
+		} else if (module == NULL) {
+			/*
+			 * loading the module ops for name services
+			 */
+			if (devname_ns_ops == NULL) {
+				sdcmn_err6((
+				    "sdev_get_moduleops: modload default\n"));
+				error = modload("devname", DEVNAME_NSCONFIG);
+				sdcmn_err6((
+				    "sdev_get_moduleops: error %d\n", error));
+				if (error < 0) {
+					return (-1);
+				}
+			}
+
+			if (!rw_tryupgrade(&map->dir_lock)) {
+				rw_exit(&map->dir_lock);
+				rw_enter(&map->dir_lock, RW_WRITER);
+			}
+			ASSERT(devname_ns_ops);
+			map->dir_ops = devname_ns_ops;
+			rw_exit(&map->dir_lock);
+		}
+	}
+
+	dv->sdev_mapinfo = map;
+	return (0);
+}
+
+/* directory dependent vop table */
+struct sdev_vop_table {
+	char *vt_name;				/* subdirectory name */
+	const fs_operation_def_t *vt_service;	/* vnodeops table */
+	struct vnodeops *vt_vops;		/* constructed vop */
+	struct vnodeops **vt_global_vops;	/* global container for vop */
+	int (*vt_vtor)(struct sdev_node *);	/* validate sdev_node */
+	int vt_flags;
+};
+
+/*
+ * A nice improvement would be to provide a plug-in mechanism
+ * for this table instead of a const table.
+ */
+static struct sdev_vop_table vtab[] =
+{
+	{ "pts", devpts_vnodeops_tbl, NULL, &devpts_vnodeops, devpts_validate,
+	SDEV_DYNAMIC | SDEV_VTOR },
+
+	{ "zcons", NULL, NULL, NULL, NULL, SDEV_NO_NCACHE },
+
+	{ NULL, NULL, NULL, NULL, NULL, 0}
+};
+
+
+/*
+ *  sets a directory's vnodeops if the directory is in the vtab;
+ */
+static struct vnodeops *
+sdev_get_vop(struct sdev_node *dv)
+{
+	int i;
+	char *path;
+
+	path = dv->sdev_path;
+	ASSERT(path);
+
+	/* gets the relative path to /dev/ */
+	path += 5;
+
+	/* gets the vtab entry if matches */
+	for (i = 0; vtab[i].vt_name; i++) {
+		if (strcmp(vtab[i].vt_name, path) != 0)
+			continue;
+		dv->sdev_flags |= vtab[i].vt_flags;
+
+		if (vtab[i].vt_vops) {
+			if (vtab[i].vt_global_vops)
+				*(vtab[i].vt_global_vops) = vtab[i].vt_vops;
+			return (vtab[i].vt_vops);
+		}
+
+		if (vtab[i].vt_service) {
+			fs_operation_def_t *templ;
+			templ = sdev_merge_vtab(vtab[i].vt_service);
+			if (vn_make_ops(vtab[i].vt_name,
+			    (const fs_operation_def_t *)templ,
+			    &vtab[i].vt_vops) != 0) {
+				cmn_err(CE_PANIC, "%s: malformed vnode ops\n",
+				    vtab[i].vt_name);
+				/*NOTREACHED*/
+			}
+			if (vtab[i].vt_global_vops) {
+				*(vtab[i].vt_global_vops) = vtab[i].vt_vops;
+			}
+			sdev_free_vtab(templ);
+			return (vtab[i].vt_vops);
+		}
+		return (sdev_vnodeops);
+	}
+
+	/* child inherits the persistence of the parent */
+	if (SDEV_IS_PERSIST(dv->sdev_dotdot))
+		dv->sdev_flags |= SDEV_PERSIST;
+
+	return (sdev_vnodeops);
+}
+
+static void
+sdev_set_no_nocache(struct sdev_node *dv)
+{
+	int i;
+	char *path;
+
+	ASSERT(dv->sdev_path);
+	path = dv->sdev_path + strlen("/dev/");
+
+	for (i = 0; vtab[i].vt_name; i++) {
+		if (strcmp(vtab[i].vt_name, path) == 0) {
+			if (vtab[i].vt_flags & SDEV_NO_NCACHE)
+				dv->sdev_flags |= SDEV_NO_NCACHE;
+			break;
+		}
+	}
+}
+
+void *
+sdev_get_vtor(struct sdev_node *dv)
+{
+	int i;
+
+	for (i = 0; vtab[i].vt_name; i++) {
+		if (strcmp(vtab[i].vt_name, dv->sdev_name) != 0)
+			continue;
+		return ((void *)vtab[i].vt_vtor);
+	}
+	return (NULL);
+}
+
+/*
+ * Build the base root inode
+ */
+ino_t
+sdev_mkino(struct sdev_node *dv)
+{
+	ino_t	ino;
+
+	/*
+	 * for now, follow the lead of tmpfs here
+	 * need to someday understand the requirements here
+	 */
+	ino = (ino_t)(uint32_t)((uintptr_t)dv >> 3);
+	ino += SDEV_ROOTINO + 1;
+
+	return (ino);
+}
+
+static int
+sdev_getlink(struct vnode *linkvp, char **link)
+{
+	int err;
+	char *buf;
+	struct uio uio = {0};
+	struct iovec iov = {0};
+
+	if (linkvp == NULL)
+		return (ENOENT);
+	ASSERT(linkvp->v_type == VLNK);
+
+	buf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	iov.iov_base = buf;
+	iov.iov_len = MAXPATHLEN;
+	uio.uio_iov = &iov;
+	uio.uio_iovcnt = 1;
+	uio.uio_resid = MAXPATHLEN;
+	uio.uio_segflg = UIO_SYSSPACE;
+	uio.uio_llimit = MAXOFFSET_T;
+
+	err = VOP_READLINK(linkvp, &uio, kcred);
+	if (err) {
+		cmn_err(CE_WARN, "readlink %s failed in dev\n", buf);
+		kmem_free(buf, MAXPATHLEN);
+		return (ENOENT);
+	}
+
+	/* mission complete */
+	*link = i_ddi_strdup(buf, KM_SLEEP);
+	kmem_free(buf, MAXPATHLEN);
+	return (0);
+}
+
+/*
+ * A convenient wrapper to get the devfs node vnode for a device
+ * minor functionality: readlink() of a /dev symlink
+ * Place the link into dv->sdev_symlink
+ */
+static int
+sdev_follow_link(struct sdev_node *dv)
+{
+	int err;
+	struct vnode *linkvp;
+	char *link = NULL;
+
+	linkvp = SDEVTOV(dv);
+	if (linkvp == NULL)
+		return (ENOENT);
+	ASSERT(linkvp->v_type == VLNK);
+	err = sdev_getlink(linkvp, &link);
+	if (err) {
+		(void) sdev_nodezombied(dv);
+		dv->sdev_symlink = NULL;
+		return (ENOENT);
+	}
+
+	ASSERT(link != NULL);
+	dv->sdev_symlink = link;
+	return (0);
+}
+
+static int
+sdev_node_check(struct sdev_node *dv, struct vattr *nvap, void *nargs)
+{
+	vtype_t otype = SDEVTOV(dv)->v_type;
+
+	/*
+	 * existing sdev_node has a different type.
+	 */
+	if (otype != nvap->va_type) {
+		sdcmn_err9(("sdev_node_check: existing node "
+		    "  %s type %d does not match new node type %d\n",
+		    dv->sdev_name, otype, nvap->va_type));
+		return (EEXIST);
+	}
+
+	/*
+	 * For a symlink, the target should be the same.
+	 */
+	if (otype == VLNK) {
+		ASSERT(nargs != NULL);
+		ASSERT(dv->sdev_symlink != NULL);
+		if (strcmp(dv->sdev_symlink, (char *)nargs) != 0) {
+			sdcmn_err9(("sdev_node_check: existing node "
+			    " %s has different symlink %s as new node "
+			    " %s\n", dv->sdev_name, dv->sdev_symlink,
+			    (char *)nargs));
+			return (EEXIST);
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * sdev_mknode - a wrapper for sdev_nodeinit(), sdev_nodeready()
+ *
+ * arguments:
+ *	- ddv (parent)
+ *	- nm (child name)
+ *	- newdv (sdev_node for nm is returned here)
+ *	- vap (vattr for the node to be created, va_type should be set.
+ *	  the defaults should be used if unknown)
+ *	- cred
+ *	- args
+ *	    . tnm (for VLNK)
+ *	    . global sdev_node (for !SDEV_GLOBAL)
+ * 	- state: SDEV_INIT, SDEV_READY
+ *
+ * only ddv, nm, newddv, vap, cred are required for sdev_mknode(SDEV_INIT)
+ *
+ * NOTE:  directory contents writers lock needs to be held before
+ *	  calling this routine.
+ */
+int
+sdev_mknode(struct sdev_node *ddv, char *nm, struct sdev_node **newdv,
+    struct vattr *vap, struct vnode *avp, void *args, struct cred *cred,
+    sdev_node_state_t state)
+{
+	int error = 0;
+	sdev_node_state_t node_state;
+	struct sdev_node *dv = NULL;
+
+	ASSERT(state != SDEV_ZOMBIE);
+	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+
+	if (*newdv) {
+		dv = *newdv;
+	} else {
+		/* allocate and initialize a sdev_node */
+		if (ddv->sdev_state == SDEV_ZOMBIE) {
+			sdcmn_err9(("sdev_mknode: parent %s ZOMBIEd\n",
+			    ddv->sdev_path));
+			return (ENOENT);
+		}
+
+		error = sdev_nodeinit(ddv, nm, &dv, vap);
+		if (error != 0) {
+			sdcmn_err9(("sdev_mknode: error %d,"
+			    " name %s can not be initialized\n",
+			    error, nm));
+			return (ENOENT);
+		}
+		ASSERT(dv);
+
+		/* insert into the directory cache */
+		error = sdev_cache_update(ddv, &dv, nm, SDEV_CACHE_ADD);
+		if (error) {
+			sdcmn_err9(("sdev_mknode: node %s can not"
+			    " be added into directory cache\n", nm));
+			return (ENOENT);
+		}
+	}
+
+	ASSERT(dv);
+	node_state = dv->sdev_state;
+	ASSERT(node_state != SDEV_ZOMBIE);
+
+	if (state == SDEV_READY) {
+		switch (node_state) {
+		case SDEV_INIT:
+			error = sdev_nodeready(dv, vap, avp, args, cred);
+			/*
+			 * masking the errors with ENOENT
+			 */
+			if (error) {
+				sdcmn_err9(("sdev_mknode: node %s can NOT"
+				    " be transitioned into READY state, "
+				    "error %d\n", nm, error));
+				error = ENOENT;
+			}
+			break;
+		case SDEV_READY:
+			/*
+			 * Do some sanity checking to make sure
+			 * the existing sdev_node is what has been
+			 * asked for.
+			 */
+			error = sdev_node_check(dv, vap, args);
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!error) {
+		*newdv = dv;
+		ASSERT((*newdv)->sdev_state != SDEV_ZOMBIE);
+	} else {
+		SDEV_SIMPLE_RELE(dv);
+		*newdv = NULL;
+	}
+
+	return (error);
+}
+
+/*
+ * convenient wrapper to change vp's ATIME, CTIME and ATIME
+ */
+void
+sdev_update_timestamps(struct vnode *vp, cred_t *cred, uint_t mask)
+{
+	struct vattr attr;
+	timestruc_t now;
+	int err;
+
+	ASSERT(vp);
+	gethrestime(&now);
+	if (mask & AT_CTIME)
+		attr.va_ctime = now;
+	if (mask & AT_MTIME)
+		attr.va_mtime = now;
+	if (mask & AT_ATIME)
+		attr.va_atime = now;
+
+	attr.va_mask = (mask & AT_TIMES);
+	err = VOP_SETATTR(vp, &attr, 0, cred, NULL);
+	if (err && (err != EROFS)) {
+		sdcmn_err(("update timestamps error %d\n", err));
+	}
+}
+
+/*
+ * the backing store vnode is released here
+ */
+/*ARGSUSED1*/
+void
+sdev_nodedestroy(struct sdev_node *dv, uint_t flags)
+{
+	/* no references */
+	ASSERT(dv->sdev_nlink == 0);
+
+	if (dv->sdev_attrvp != NULLVP) {
+		VN_RELE(dv->sdev_attrvp);
+		/*
+		 * reset the attrvp so that no more
+		 * references can be made on this already
+		 * vn_rele() vnode
+		 */
+		dv->sdev_attrvp = NULLVP;
+	}
+
+	if (dv->sdev_attr != NULL) {
+		kmem_free(dv->sdev_attr, sizeof (struct vattr));
+		dv->sdev_attr = NULL;
+	}
+
+	if (dv->sdev_name != NULL) {
+		kmem_free(dv->sdev_name, dv->sdev_namelen + 1);
+		dv->sdev_name = NULL;
+	}
+
+	if (dv->sdev_symlink != NULL) {
+		kmem_free(dv->sdev_symlink, strlen(dv->sdev_symlink) + 1);
+		dv->sdev_symlink = NULL;
+	}
+
+	if (dv->sdev_path) {
+		kmem_free(dv->sdev_path, strlen(dv->sdev_path) + 1);
+		dv->sdev_path = NULL;
+	}
+
+	if (!SDEV_IS_GLOBAL(dv))
+		sdev_prof_free(dv);
+
+	mutex_destroy(&dv->sdev_lookup_lock);
+	cv_destroy(&dv->sdev_lookup_cv);
+
+	/* return node to initial state as per constructor */
+	(void) memset((void *)&dv->sdev_instance_data, 0,
+	    sizeof (dv->sdev_instance_data));
+
+	vn_invalid(SDEVTOV(dv));
+	kmem_cache_free(sdev_node_cache, dv);
+}
+
+/*
+ * DIRECTORY CACHE lookup
+ */
+struct sdev_node *
+sdev_findbyname(struct sdev_node *ddv, char *nm)
+{
+	struct sdev_node *dv;
+	size_t	nmlen = strlen(nm);
+
+	ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
+	for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next) {
+		if (dv->sdev_namelen != nmlen) {
+			continue;
+		}
+
+		/*
+		 * Can't lookup stale nodes
+		 */
+		if (dv->sdev_flags & SDEV_STALE) {
+			sdcmn_err9((
+			    "sdev_findbyname: skipped stale node: %s\n",
+			    dv->sdev_name));
+			continue;
+		}
+
+		if (strcmp(dv->sdev_name, nm) == 0) {
+			SDEV_HOLD(dv);
+			return (dv);
+		}
+	}
+	return (NULL);
+}
+
+/*
+ * Inserts a new sdev_node in a parent directory
+ */
+void
+sdev_direnter(struct sdev_node *ddv, struct sdev_node *dv)
+{
+	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
+	ASSERT(ddv->sdev_nlink >= 2);
+	ASSERT(dv->sdev_nlink == 0);
+
+	dv->sdev_dotdot = ddv;
+	dv->sdev_next = ddv->sdev_dot;
+	ddv->sdev_dot = dv;
+	ddv->sdev_nlink++;
+}
+
+/*
+ * The following check is needed because while sdev_nodes are linked
+ * in SDEV_INIT state, they have their link counts incremented only
+ * in SDEV_READY state.
+ */
+static void
+decr_link(struct sdev_node *dv)
+{
+	if (dv->sdev_state != SDEV_INIT)
+		dv->sdev_nlink--;
+	else
+		ASSERT(dv->sdev_nlink == 0);
+}
+
+/*
+ * Delete an existing dv from directory cache
+ *
+ * In the case of a node is still held by non-zero reference count,
+ *     the node is put into ZOMBIE state. Once the reference count
+ *     reaches "0", the node is unlinked and destroyed,
+ *     in sdev_inactive().
+ */
+static int
+sdev_dirdelete(struct sdev_node *ddv, struct sdev_node *dv)
+{
+	struct sdev_node *idv;
+	struct sdev_node *prev = NULL;
+	struct vnode *vp;
+
+	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+
+	vp = SDEVTOV(dv);
+	mutex_enter(&vp->v_lock);
+
+	/* dv is held still */
+	if (vp->v_count > 1) {
+		rw_enter(&dv->sdev_contents, RW_WRITER);
+		if (dv->sdev_state == SDEV_READY) {
+			sdcmn_err9((
+			    "sdev_delete: node %s busy with count %d\n",
+			    dv->sdev_name, vp->v_count));
+			dv->sdev_state = SDEV_ZOMBIE;
+		}
+		rw_exit(&dv->sdev_contents);
+		--vp->v_count;
+		mutex_exit(&vp->v_lock);
+		return (EBUSY);
+	}
+	ASSERT(vp->v_count == 1);
+
+	/* unlink from the memory cache */
+	ddv->sdev_nlink--;	/* .. to above */
+	if (vp->v_type == VDIR) {
+		decr_link(dv);		/* . to self */
+	}
+
+	for (idv = ddv->sdev_dot; idv && idv != dv;
+	    prev = idv, idv = idv->sdev_next)
+		;
+	ASSERT(idv == dv);	/* node to be deleted must exist */
+	if (prev == NULL)
+		ddv->sdev_dot = dv->sdev_next;
+	else
+		prev->sdev_next = dv->sdev_next;
+	dv->sdev_next = NULL;
+	decr_link(dv);	/* name, back to zero */
+	vp->v_count--;
+	mutex_exit(&vp->v_lock);
+
+	/* destroy the node */
+	sdev_nodedestroy(dv, 0);
+	return (0);
+}
+
+/*
+ * check if the source is in the path of the target
+ *
+ * source and target are different
+ */
+/*ARGSUSED2*/
+static int
+sdev_checkpath(struct sdev_node *sdv, struct sdev_node *tdv, struct cred *cred)
+{
+	int error = 0;
+	struct sdev_node *dotdot, *dir;
+
+	rw_enter(&tdv->sdev_contents, RW_READER);
+	dotdot = tdv->sdev_dotdot;
+	ASSERT(dotdot);
+
+	/* fs root */
+	if (dotdot == tdv) {
+		rw_exit(&tdv->sdev_contents);
+		return (0);
+	}
+
+	for (;;) {
+		/*
+		 * avoid error cases like
+		 *	mv a a/b
+		 *	mv a a/b/c
+		 *	etc.
+		 */
+		if (dotdot == sdv) {
+			error = EINVAL;
+			break;
+		}
+
+		dir = dotdot;
+		dotdot = dir->sdev_dotdot;
+
+		/* done checking because root is reached */
+		if (dir == dotdot) {
+			break;
+		}
+	}
+	rw_exit(&tdv->sdev_contents);
+	return (error);
+}
+
+/*
+ * Renaming a directory to a different parent
+ * requires modifying the ".." reference.
+ */
+static void
+sdev_fixdotdot(struct sdev_node *dv, struct sdev_node *oparent,
+    struct sdev_node *nparent)
+{
+	ASSERT(SDEVTOV(dv)->v_type == VDIR);
+	ASSERT(nparent);
+	ASSERT(oparent);
+
+	rw_enter(&nparent->sdev_contents, RW_WRITER);
+	nparent->sdev_nlink++;
+	ASSERT(dv->sdev_dotdot == oparent);
+	dv->sdev_dotdot = nparent;
+	rw_exit(&nparent->sdev_contents);
+
+	rw_enter(&oparent->sdev_contents, RW_WRITER);
+	oparent->sdev_nlink--;
+	rw_exit(&oparent->sdev_contents);
+}
+
+int
+sdev_rnmnode(struct sdev_node *oddv, struct sdev_node *odv,
+    struct sdev_node *nddv, struct sdev_node **ndvp, char *nnm,
+    struct cred *cred)
+{
+	int error = 0;
+	struct vnode *ovp = SDEVTOV(odv);
+	struct vnode *nvp;
+	struct vattr vattr;
+	int doingdir = (ovp->v_type == VDIR);
+	char *link = NULL;
+
+	/*
+	 * If renaming a directory, and the parents are different (".." must be
+	 * changed) then the source dir must not be in the dir hierarchy above
+	 * the target since it would orphan everything below the source dir.
+	 */
+	if (doingdir && (oddv != nddv)) {
+		error = sdev_checkpath(odv, nddv, cred);
+		if (error)
+			return (error);
+	}
+
+	vattr.va_mask = AT_MODE|AT_UID|AT_GID;
+	error = VOP_GETATTR(ovp, &vattr, 0, cred);
+	if (error)
+		return (error);
+
+	if (*ndvp) {
+		/* destination existing */
+		nvp = SDEVTOV(*ndvp);
+		ASSERT(nvp);
+
+		/* handling renaming to itself */
+		if (odv == *ndvp)
+			return (0);
+
+		/* special handling directory renaming */
+		if (doingdir) {
+			if (nvp->v_type != VDIR)
+				return (ENOTDIR);
+
+			/*
+			 * Renaming a directory with the parent different
+			 * requires that ".." be re-written.
+			 */
+			if (oddv != nddv) {
+				sdev_fixdotdot(*ndvp, oddv, nddv);
+			}
+		}
+	} else {
+		/* creating the destination node with the source attr */
+		rw_enter(&nddv->sdev_contents, RW_WRITER);
+		error = sdev_mknode(nddv, nnm, ndvp, &vattr, NULL, NULL,
+		    cred, SDEV_INIT);
+		rw_exit(&nddv->sdev_contents);
+		if (error)
+			return (error);
+
+		ASSERT(*ndvp);
+		nvp = SDEVTOV(*ndvp);
+	}
+
+	/* fix the source for a symlink */
+	if (vattr.va_type == VLNK) {
+		if (odv->sdev_symlink == NULL) {
+			error = sdev_follow_link(odv);
+			if (error)
+				return (ENOENT);
+		}
+		ASSERT(odv->sdev_symlink);
+		link = i_ddi_strdup(odv->sdev_symlink, KM_SLEEP);
+	}
+
+	rw_enter(&nddv->sdev_contents, RW_WRITER);
+	error = sdev_mknode(nddv, nnm, ndvp, &vattr, NULL, (void *)link,
+	    cred, SDEV_READY);
+	rw_exit(&nddv->sdev_contents);
+
+	if (link)
+		kmem_free(link, strlen(link) + 1);
+
+	/* update timestamps */
+	sdev_update_timestamps(nvp, kcred, AT_CTIME|AT_ATIME);
+	sdev_update_timestamps(SDEVTOV(nddv), kcred, AT_MTIME|AT_ATIME);
+	SDEV_RELE(*ndvp);
+	return (0);
+}
+
+/*
+ * Merge sdev_node specific information into an attribute structure.
+ *
+ * note: sdev_node is not locked here
+ */
+void
+sdev_vattr_merge(struct sdev_node *dv, struct vattr *vap)
+{
+	struct vnode *vp = SDEVTOV(dv);
+
+	vap->va_nlink = dv->sdev_nlink;
+	vap->va_nodeid = dv->sdev_ino;
+	vap->va_fsid = SDEVTOV(dv->sdev_dotdot)->v_rdev;
+	vap->va_type = vp->v_type;
+
+	if (vp->v_type == VDIR) {
+		vap->va_rdev = 0;
+		vap->va_fsid = vp->v_rdev;
+	} else if (vp->v_type == VLNK) {
+		vap->va_rdev = 0;
+		vap->va_mode  &= ~S_IFMT;
+		vap->va_mode |= S_IFLNK;
+	} else if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
+		vap->va_rdev = vp->v_rdev;
+		vap->va_mode &= ~S_IFMT;
+		if (vap->va_type == VCHR)
+			vap->va_mode |= S_IFCHR;
+		else
+			vap->va_mode |= S_IFBLK;
+	} else {
+		vap->va_rdev = 0;
+	}
+}
+
+static struct vattr *
+sdev_getdefault_attr(enum vtype type)
+{
+	if (type == VDIR)
+		return (&sdev_vattr_dir);
+	else if (type == VCHR)
+		return (&sdev_vattr_chr);
+	else if (type == VBLK)
+		return (&sdev_vattr_blk);
+	else if (type == VLNK)
+		return (&sdev_vattr_lnk);
+	else
+		return (NULL);
+}
+int
+sdev_to_vp(struct sdev_node *dv, struct vnode **vpp)
+{
+	int rv = 0;
+	struct vnode *vp = SDEVTOV(dv);
+
+	switch (vp->v_type) {
+	case VCHR:
+	case VBLK:
+		/*
+		 * If vnode is a device, return special vnode instead
+		 * (though it knows all about -us- via sp->s_realvp)
+		 */
+		*vpp = specvp(vp, vp->v_rdev, vp->v_type, kcred);
+		VN_RELE(vp);
+		if (*vpp == NULLVP)
+			rv = ENOSYS;
+		break;
+	default:	/* most types are returned as is */
+		*vpp = vp;
+		break;
+	}
+	return (rv);
+}
+
+/*
+ * loopback into sdev_lookup()
+ */
+static struct vnode *
+devname_find_by_devpath(char *devpath, struct vattr *vattr)
+{
+	int error = 0;
+	struct vnode *vp;
+
+	error = lookupname(devpath, UIO_SYSSPACE, NO_FOLLOW, NULLVPP, &vp);
+	if (error) {
+		return (NULL);
+	}
+
+	if (vattr)
+		(void) VOP_GETATTR(vp, vattr, 0, kcred);
+	return (vp);
+}
+
+/*
+ * the junction between devname and devfs
+ */
+static struct vnode *
+devname_configure_by_path(char *physpath, struct vattr *vattr)
+{
+	int error = 0;
+	struct vnode *vp;
+
+	ASSERT(strncmp(physpath, "/devices/", sizeof ("/devices/" - 1))
+	    == 0);
+
+	error = devfs_lookupname(physpath + sizeof ("/devices/") - 1,
+	    NULLVPP, &vp);
+	if (error != 0) {
+		if (error == ENODEV) {
+			cmn_err(CE_CONT, "%s: not found (line %d)\n",
+			    physpath, __LINE__);
+		}
+
+		return (NULL);
+	}
+
+	if (vattr)
+		(void) VOP_GETATTR(vp, vattr, 0, kcred);
+	return (vp);
+}
+
+/*
+ * junction between devname and root file system, e.g. ufs
+ */
+int
+devname_backstore_lookup(struct sdev_node *ddv, char *nm, struct vnode **rvp)
+{
+	struct vnode *rdvp = ddv->sdev_attrvp;
+	int rval = 0;
+
+	ASSERT(rdvp);
+
+	rval = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, kcred);
+	return (rval);
+}
+
+static int
+sdev_filldir_from_store(struct sdev_node *ddv, int dlen, struct cred *cred)
+{
+	struct sdev_node *dv = NULL;
+	char	*nm;
+	struct vnode *dirvp;
+	int	error;
+	vnode_t	*vp;
+	int eof;
+	struct iovec iov;
+	struct uio uio;
+	struct dirent64 *dp;
+	dirent64_t *dbuf;
+	size_t dbuflen;
+	struct vattr vattr;
+	char *link = NULL;
+
+	if (ddv->sdev_attrvp == NULL)
+		return (0);
+	if (!(ddv->sdev_flags & SDEV_BUILD))
+		return (0);
+
+	dirvp = ddv->sdev_attrvp;
+	VN_HOLD(dirvp);
+	dbuf = kmem_zalloc(dlen, KM_SLEEP);
+
+	uio.uio_iov = &iov;
+	uio.uio_iovcnt = 1;
+	uio.uio_segflg = UIO_SYSSPACE;
+	uio.uio_fmode = 0;
+	uio.uio_extflg = UIO_COPY_CACHED;
+	uio.uio_loffset = 0;
+	uio.uio_llimit = MAXOFFSET_T;
+
+	eof = 0;
+	error = 0;
+	while (!error && !eof) {
+		uio.uio_resid = dlen;
+		iov.iov_base = (char *)dbuf;
+		iov.iov_len = dlen;
+		(void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL);
+		error = VOP_READDIR(dirvp, &uio, kcred, &eof);
+		VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL);
+
+		dbuflen = dlen - uio.uio_resid;
+		if (error || dbuflen == 0)
+			break;
+
+		if (!(ddv->sdev_flags & SDEV_BUILD)) {
+			error = 0;
+			break;
+		}
+
+		for (dp = dbuf; ((intptr_t)dp <
+		    (intptr_t)dbuf + dbuflen);
+		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
+			nm = dp->d_name;
+
+			if (strcmp(nm, ".") == 0 ||
+			    strcmp(nm, "..") == 0)
+				continue;
+
+			vp = NULLVP;
+			dv = sdev_cache_lookup(ddv, nm);
+			if (dv) {
+				if (dv->sdev_state != SDEV_ZOMBIE) {
+					SDEV_SIMPLE_RELE(dv);
+				} else {
+					/*
+					 * A ZOMBIE node may not have been
+					 * cleaned up from the backing store,
+					 * bypass this entry in this case,
+					 * and clean it up from the directory
+					 * cache if this is the last call.
+					 */
+					(void) sdev_dirdelete(ddv, dv);
+				}
+				continue;
+			}
+
+			/* refill the cache if not already */
+			error = devname_backstore_lookup(ddv, nm, &vp);
+			if (error)
+				continue;
+
+			vattr.va_mask = AT_MODE|AT_UID|AT_GID;
+			error = VOP_GETATTR(vp, &vattr, 0, cred);
+			if (error)
+				continue;
+
+			if (vattr.va_type == VLNK) {
+				error = sdev_getlink(vp, &link);
+				if (error) {
+					continue;
+				}
+				ASSERT(link != NULL);
+			}
+
+			if (!rw_tryupgrade(&ddv->sdev_contents)) {
+				rw_exit(&ddv->sdev_contents);
+				rw_enter(&ddv->sdev_contents, RW_WRITER);
+			}
+			error = sdev_mknode(ddv, nm, &dv, &vattr, vp, link,
+			    cred, SDEV_READY);
+			rw_downgrade(&ddv->sdev_contents);
+
+			if (link != NULL) {
+				kmem_free(link, strlen(link) + 1);
+				link = NULL;
+			}
+
+			if (!error) {
+				ASSERT(dv);
+				ASSERT(dv->sdev_state != SDEV_ZOMBIE);
+				SDEV_SIMPLE_RELE(dv);
+			}
+			vp = NULL;
+			dv = NULL;
+		}
+	}
+
+done:
+	VN_RELE(dirvp);
+	kmem_free(dbuf, dlen);
+
+	return (error);
+}
+
+static int
+sdev_filldir_dynamic(struct sdev_node *ddv)
+{
+	int error;
+	int i;
+	struct vattr *vap;
+	char *nm = NULL;
+	struct sdev_node *dv = NULL;
+
+	if (!(ddv->sdev_flags & SDEV_BUILD)) {
+		return (0);
+	}
+
+	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
+	if (!rw_tryupgrade(&ddv->sdev_contents)) {
+		rw_exit(&ddv->sdev_contents);
+		rw_enter(&ddv->sdev_contents, RW_WRITER);
+	}
+
+	vap = sdev_getdefault_attr(VDIR);
+	for (i = 0; vtab[i].vt_name != NULL; i++) {
+		nm = vtab[i].vt_name;
+		ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+		error = sdev_mknode(ddv, nm, &dv, vap, NULL,
+		    NULL, kcred, SDEV_READY);
+		if (error)
+			continue;
+		ASSERT(dv);
+		ASSERT(dv->sdev_state != SDEV_ZOMBIE);
+		SDEV_SIMPLE_RELE(dv);
+		dv = NULL;
+	}
+	rw_downgrade(&ddv->sdev_contents);
+	return (0);
+}
+
+/*
+ * Creating a backing store entry based on sdev_attr.
+ * This is called either as part of node creation in a persistent directory
+ * or from setattr/setsecattr to persist access attributes across reboot.
+ */
+int
+sdev_shadow_node(struct sdev_node *dv, struct cred *cred)
+{
+	int error = 0;
+	struct vnode *dvp = SDEVTOV(dv->sdev_dotdot);
+	struct vnode *rdvp = VTOSDEV(dvp)->sdev_attrvp;
+	struct vattr *vap = dv->sdev_attr;
+	char *nm = dv->sdev_name;
+	struct vnode *tmpvp, **rvp = &tmpvp, *rrvp = NULL;
+
+	ASSERT(dv && dv->sdev_name && rdvp);
+	ASSERT(RW_WRITE_HELD(&dv->sdev_contents) && dv->sdev_attrvp == NULL);
+
+lookup:
+	/* try to find it in the backing store */
+	error = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, cred);
+	if (error == 0) {
+		if (VOP_REALVP(*rvp, &rrvp) == 0) {
+			VN_HOLD(rrvp);
+			VN_RELE(*rvp);
+			*rvp = rrvp;
+		}
+
+		kmem_free(dv->sdev_attr, sizeof (vattr_t));
+		dv->sdev_attr = NULL;
+		dv->sdev_attrvp = *rvp;
+		return (0);
+	}
+
+	/* let's try to persist the node */
+	gethrestime(&vap->va_atime);
+	vap->va_mtime = vap->va_atime;
+	vap->va_ctime = vap->va_atime;
+	vap->va_mask |= AT_TYPE|AT_MODE;
+	switch (vap->va_type) {
+	case VDIR:
+		error = VOP_MKDIR(rdvp, nm, vap, rvp, cred);
+		sdcmn_err9(("sdev_shadow_node: mkdir vp %p error %d\n",
+		    (void *)(*rvp), error));
+		break;
+	case VCHR:
+	case VBLK:
+	case VREG:
+	case VDOOR:
+		error = VOP_CREATE(rdvp, nm, vap, NONEXCL, VREAD|VWRITE,
+		    rvp, cred, 0);
+		sdcmn_err9(("sdev_shadow_node: create vp %p, error %d\n",
+		    (void *)(*rvp), error));
+		if (!error)
+			VN_RELE(*rvp);
+		break;
+	case VLNK:
+		ASSERT(dv->sdev_symlink);
+		error = VOP_SYMLINK(rdvp, nm, vap, dv->sdev_symlink, cred);
+		sdcmn_err9(("sdev_shadow_node: create symlink error %d\n",
+		    error));
+		break;
+	default:
+		cmn_err(CE_PANIC, "dev: %s: sdev_shadow_node "
+		    "create\n", nm);
+		/*NOTREACHED*/
+	}
+
+	/* go back to lookup to factor out spec node and set attrvp */
+	if (error == 0)
+		goto lookup;
+
+	return (error);
+}
+
+static int
+sdev_cache_add(struct sdev_node *ddv, struct sdev_node **dv, char *nm)
+{
+	int error = 0;
+	struct sdev_node *dup = NULL;
+
+	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+	if ((dup = sdev_findbyname(ddv, nm)) == NULL) {
+		sdev_direnter(ddv, *dv);
+	} else {
+		if (dup->sdev_state == SDEV_ZOMBIE) {
+			error = sdev_dirdelete(ddv, dup);
+			/*
+			 * The ZOMBIE node is still hanging
+			 * around with more than one reference counts.
+			 * Fail the new node creation so that
+			 * the directory cache won't have
+			 * duplicate entries for the same named node
+			 */
+			if (error == EBUSY) {
+				SDEV_SIMPLE_RELE(*dv);
+				sdev_nodedestroy(*dv, 0);
+				*dv = NULL;
+				return (error);
+			}
+			sdev_direnter(ddv, *dv);
+		} else {
+			ASSERT((*dv)->sdev_state != SDEV_ZOMBIE);
+			SDEV_SIMPLE_RELE(*dv);
+			sdev_nodedestroy(*dv, 0);
+			*dv = dup;
+		}
+	}
+
+	return (0);
+}
+
+static int
+sdev_cache_delete(struct sdev_node *ddv, struct sdev_node **dv)
+{
+	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+	return (sdev_dirdelete(ddv, *dv));
+}
+
+/*
+ * update the in-core directory cache
+ */
+int
+sdev_cache_update(struct sdev_node *ddv, struct sdev_node **dv, char *nm,
+    sdev_cache_ops_t ops)
+{
+	int error = 0;
+
+	ASSERT((SDEV_HELD(*dv)));
+
+	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+	switch (ops) {
+	case SDEV_CACHE_ADD:
+		error = sdev_cache_add(ddv, dv, nm);
+		break;
+	case SDEV_CACHE_DELETE:
+		error = sdev_cache_delete(ddv, dv);
+		break;
+	default:
+		break;
+	}
+
+	return (error);
+}
+
+/*
+ * retrive the named entry from the directory cache
+ */
+struct sdev_node *
+sdev_cache_lookup(struct sdev_node *ddv, char *nm)
+{
+	struct sdev_node *dv = NULL;
+
+	ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
+	dv = sdev_findbyname(ddv, nm);
+
+	return (dv);
+}
+
+/*
+ * Implicit reconfig for nodes constructed by a link generator
+ * Start devfsadm if needed, or if devfsadm is in progress,
+ * prepare to block on devfsadm either completing or
+ * constructing the desired node.  As devfsadmd is global
+ * in scope, constructing all necessary nodes, we only
+ * need to initiate it once.
+ */
+static int
+sdev_call_devfsadmd(struct sdev_node *ddv, struct sdev_node *dv, char *nm)
+{
+	int error = 0;
+
+	if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) {
+		sdcmn_err6(("lookup: waiting for %s/%s, 0x%x\n",
+		    ddv->sdev_name, nm, devfsadm_state));
+		mutex_enter(&dv->sdev_lookup_lock);
+		SDEV_BLOCK_OTHERS(dv, (SDEV_LOOKUP | SDEV_LGWAITING));
+		mutex_exit(&dv->sdev_lookup_lock);
+		error = 0;
+	} else if (!DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state)) {
+		sdcmn_err6(("lookup %s/%s starting devfsadm, 0x%x\n",
+			ddv->sdev_name, nm, devfsadm_state));
+
+		sdev_devfsadmd_thread(ddv, dv, kcred);
+		mutex_enter(&dv->sdev_lookup_lock);
+		SDEV_BLOCK_OTHERS(dv,
+		    (SDEV_LOOKUP | SDEV_LGWAITING));
+		mutex_exit(&dv->sdev_lookup_lock);
+		error = 0;
+	} else {
+		error = -1;
+	}
+
+	return (error);
+}
+
+static int
+sdev_call_modulelookup(struct sdev_node *ddv, struct sdev_node **dvp, char *nm,
+    int (*fn)(char *, devname_handle_t *, struct cred *), struct cred *cred)
+{
+	struct vnode *rvp = NULL;
+	int error = 0;
+	struct vattr *vap;
+	devname_spec_t spec;
+	devname_handle_t *hdl;
+	void *args = NULL;
+	struct sdev_node *dv = *dvp;
+
+	ASSERT(dv && ddv);
+	hdl = &(dv->sdev_handle);
+	ASSERT(hdl->dh_data == dv);
+	mutex_enter(&dv->sdev_lookup_lock);
+	SDEV_BLOCK_OTHERS(dv, SDEV_LOOKUP);
+	mutex_exit(&dv->sdev_lookup_lock);
+	error = (*fn)(nm, hdl, cred);
+	if (error) {
+		return (error);
+	}
+
+	spec = hdl->dh_spec;
+	args = hdl->dh_args;
+	ASSERT(args);
+
+	switch (spec) {
+	case DEVNAME_NS_PATH:
+		/*
+		 * symlink of:
+		 *	/dev/dir/nm -> /device/...
+		 */
+		rvp = devname_configure_by_path((char *)args, NULL);
+		break;
+	case DEVNAME_NS_DEV:
+		/*
+		 * symlink of:
+		 *	/dev/dir/nm -> /dev/...
+		 */
+		rvp = devname_find_by_devpath((char *)args, NULL);
+		break;
+	default:
+		if (args)
+			kmem_free((char *)args, strlen(args) + 1);
+		return (ENOENT);
+
+	}
+
+	if (rvp == NULL) {
+		if (args)
+			kmem_free((char *)args, strlen(args) + 1);
+		return (ENOENT);
+	} else {
+		vap = sdev_getdefault_attr(VLNK);
+		ASSERT(RW_READ_HELD(&ddv->sdev_contents));
+		/*
+		 * Could sdev_mknode return a different dv_node
+		 * once the lock is dropped?
+		 */
+		if (!rw_tryupgrade(&ddv->sdev_contents)) {
+			rw_exit(&ddv->sdev_contents);
+			rw_enter(&ddv->sdev_contents, RW_WRITER);
+		}
+		error = sdev_mknode(ddv, nm, &dv, vap, NULL, args, cred,
+		    SDEV_READY);
+		rw_downgrade(&ddv->sdev_contents);
+		if (error) {
+			if (args)
+				kmem_free((char *)args, strlen(args) + 1);
+			return (error);
+		} else {
+			mutex_enter(&dv->sdev_lookup_lock);
+			SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
+			mutex_exit(&dv->sdev_lookup_lock);
+			error = 0;
+		}
+	}
+
+	if (args)
+		kmem_free((char *)args, strlen(args) + 1);
+
+	*dvp = dv;
+	return (0);
+}
+
+/*
+ *  Support for specialized device naming construction mechanisms
+ */
+static int
+sdev_call_dircallback(struct sdev_node *ddv, struct sdev_node **dvp, char *nm,
+    int (*callback)(struct sdev_node *, char *, void **, struct cred *,
+    void *, char *), int flags, struct cred *cred)
+{
+	int rv = 0;
+	char *physpath = NULL;
+	struct vnode *rvp = NULL;
+	struct vattr vattr;
+	struct vattr *vap;
+	struct sdev_node *dv = *dvp;
+
+	mutex_enter(&dv->sdev_lookup_lock);
+	SDEV_BLOCK_OTHERS(dv, SDEV_LOOKUP);
+	mutex_exit(&dv->sdev_lookup_lock);
+
+	/* for non-devfsadm devices */
+	if (flags & SDEV_PATH) {
+		physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+		rv = callback(ddv, nm, (void *)&physpath, kcred, NULL,
+		    NULL);
+		if (rv) {
+			kmem_free(physpath, MAXPATHLEN);
+			return (-1);
+		}
+
+		ASSERT(physpath);
+		rvp = devname_configure_by_path(physpath, NULL);
+		if (rvp == NULL) {
+			sdcmn_err3(("devname_configure_by_path: "
+			    "failed for /dev/%s/%s\n",
+			    ddv->sdev_name, nm));
+			kmem_free(physpath, MAXPATHLEN);
+			rv = -1;
+		} else {
+			vap = sdev_getdefault_attr(VLNK);
+			ASSERT(RW_READ_HELD(&ddv->sdev_contents));
+
+			/*
+			 * Sdev_mknode may return back a different sdev_node
+			 * that was created by another thread that
+			 * raced to the directroy cache before this thread.
+			 *
+			 * With current directory cache mechanism
+			 * (linked list with the sdev_node name as
+			 * the entity key), this is a way to make sure
+			 * only one entry exists for the same name
+			 * in the same directory. The outcome is
+			 * the winner wins.
+			 */
+			if (!rw_tryupgrade(&ddv->sdev_contents)) {
+				rw_exit(&ddv->sdev_contents);
+				rw_enter(&ddv->sdev_contents, RW_WRITER);
+			}
+			rv = sdev_mknode(ddv, nm, &dv, vap, NULL,
+			    (void *)physpath, cred, SDEV_READY);
+			rw_downgrade(&ddv->sdev_contents);
+			kmem_free(physpath, MAXPATHLEN);
+			if (rv) {
+				return (rv);
+			} else {
+				mutex_enter(&dv->sdev_lookup_lock);
+				SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
+				mutex_exit(&dv->sdev_lookup_lock);
+				return (0);
+			}
+		}
+	} else if (flags & SDEV_VNODE) {
+		/*
+		 * DBNR has its own way to create the device
+		 * and return a backing store vnode in rvp
+		 */
+		ASSERT(callback);
+		rv = callback(ddv, nm, (void *)&rvp, kcred, NULL, NULL);
+		if (rv || (rvp == NULL)) {
+			sdcmn_err3(("devname_lookup_func: SDEV_VNODE "
+			    "callback failed \n"));
+			return (-1);
+		}
+		vap = sdev_getdefault_attr(rvp->v_type);
+		if (vap == NULL)
+			return (-1);
+
+		ASSERT(RW_READ_HELD(&ddv->sdev_contents));
+		if (!rw_tryupgrade(&ddv->sdev_contents)) {
+			rw_exit(&ddv->sdev_contents);
+			rw_enter(&ddv->sdev_contents, RW_WRITER);
+		}
+		rv = sdev_mknode(ddv, nm, &dv, vap, rvp, NULL,
+		    cred, SDEV_READY);
+		rw_downgrade(&ddv->sdev_contents);
+		if (rv)
+			return (rv);
+
+		mutex_enter(&dv->sdev_lookup_lock);
+		SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
+		mutex_exit(&dv->sdev_lookup_lock);
+		return (0);
+	} else if (flags & SDEV_VATTR) {
+		/*
+		 * /dev/pts
+		 *
+		 * DBNR has its own way to create the device
+		 * "0" is returned upon success.
+		 *
+		 * callback is responsible to set the basic attributes,
+		 * e.g. va_type/va_uid/va_gid/
+		 *    dev_t if VCHR or VBLK/
+		 */
+		ASSERT(callback);
+		rv = callback(ddv, nm, (void *)&vattr, kcred, NULL, NULL);
+		if (rv) {
+			sdcmn_err3(("devname_lookup_func: SDEV_NONE "
+			    "callback failed \n"));
+			return (-1);
+		}
+
+		ASSERT(RW_READ_HELD(&ddv->sdev_contents));
+		if (!rw_tryupgrade(&ddv->sdev_contents)) {
+			rw_exit(&ddv->sdev_contents);
+			rw_enter(&ddv->sdev_contents, RW_WRITER);
+		}
+		rv = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL,
+		    cred, SDEV_READY);
+		rw_downgrade(&ddv->sdev_contents);
+
+		if (rv)
+			return (rv);
+
+		mutex_enter(&dv->sdev_lookup_lock);
+		SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
+		mutex_exit(&dv->sdev_lookup_lock);
+		return (0);
+	} else {
+		impossible(("lookup: %s/%s by %s not supported (%d)\n",
+		    SDEVTOV(ddv)->v_path, nm, curproc->p_user.u_comm,
+		    __LINE__));
+		rv = -1;
+	}
+
+	*dvp = dv;
+	return (rv);
+}
+
+static int
+is_devfsadm_thread(char *exec_name)
+{
+	/*
+	 * note: because devfsadmd -> /usr/sbin/devfsadm
+	 * it is safe to use "devfsadm" to capture the lookups
+	 * from devfsadm and its daemon version.
+	 */
+	if (strcmp(exec_name, "devfsadm") == 0)
+		return (1);
+	return (0);
+}
+
+
+/*
+ * Lookup Order:
+ *	sdev_node cache;
+ *	backing store (SDEV_PERSIST);
+ *	DBNR: a. dir_ops implemented in the loadable modules;
+ *	      b. vnode ops in vtab.
+ */
+int
+devname_lookup_func(struct sdev_node *ddv, char *nm, struct vnode **vpp,
+    struct cred *cred, int (*callback)(struct sdev_node *, char *, void **,
+    struct cred *, void *, char *), int flags)
+{
+	int rv = 0, nmlen;
+	struct vnode *rvp = NULL;
+	struct sdev_node *dv = NULL;
+	int	retried = 0;
+	int	error = 0;
+	struct devname_nsmap *map = NULL;
+	struct devname_ops *dirops = NULL;
+	int (*fn)(char *, devname_handle_t *, struct cred *) = NULL;
+	struct vattr vattr;
+	char *lookup_thread = curproc->p_user.u_comm;
+	int failed_flags = 0;
+	int (*vtor)(struct sdev_node *) = NULL;
+	int state;
+	int parent_state;
+	char *link = NULL;
+
+	if (SDEVTOV(ddv)->v_type != VDIR)
+		return (ENOTDIR);
+
+	/*
+	 * Empty name or ., return node itself.
+	 */
+	nmlen = strlen(nm);
+	if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
+		*vpp = SDEVTOV(ddv);
+		VN_HOLD(*vpp);
+		return (0);
+	}
+
+	/*
+	 * .., return the parent directory
+	 */
+	if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
+		*vpp = SDEVTOV(ddv->sdev_dotdot);
+		VN_HOLD(*vpp);
+		return (0);
+	}
+
+	rw_enter(&ddv->sdev_contents, RW_READER);
+	if (ddv->sdev_flags & SDEV_VTOR) {
+		vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
+		ASSERT(vtor);
+	}
+
+tryagain:
+	/*
+	 * (a) directory cache lookup:
+	 */
+	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
+	parent_state = ddv->sdev_state;
+	dv = sdev_cache_lookup(ddv, nm);
+	if (dv) {
+		state = dv->sdev_state;
+		switch (state) {
+		case SDEV_INIT:
+			if (is_devfsadm_thread(lookup_thread))
+				break;
+
+			/* ZOMBIED parent won't allow node creation */
+			if (parent_state == SDEV_ZOMBIE) {
+				SD_TRACE_FAILED_LOOKUP(ddv, nm,
+				    retried);
+				goto nolock_notfound;
+			}
+
+			mutex_enter(&dv->sdev_lookup_lock);
+			/* compensate the threads started after devfsadm */
+			if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
+			    !(SDEV_IS_LOOKUP(dv)))
+				SDEV_BLOCK_OTHERS(dv,
+				    (SDEV_LOOKUP | SDEV_LGWAITING));
+
+			if (SDEV_IS_LOOKUP(dv)) {
+				failed_flags |= SLF_REBUILT;
+				rw_exit(&ddv->sdev_contents);
+				error = sdev_wait4lookup(dv, SDEV_LOOKUP);
+				mutex_exit(&dv->sdev_lookup_lock);
+				rw_enter(&ddv->sdev_contents, RW_READER);
+
+				if (error != 0) {
+					SD_TRACE_FAILED_LOOKUP(ddv, nm,
+					    retried);
+					goto nolock_notfound;
+				}
+
+				state = dv->sdev_state;
+				if (state == SDEV_INIT) {
+					SD_TRACE_FAILED_LOOKUP(ddv, nm,
+					    retried);
+					goto nolock_notfound;
+				} else if (state == SDEV_READY) {
+					goto found;
+				} else if (state == SDEV_ZOMBIE) {
+					rw_exit(&ddv->sdev_contents);
+					SD_TRACE_FAILED_LOOKUP(ddv, nm,
+					    retried);
+					SDEV_RELE(dv);
+					goto lookup_failed;
+				}
+			} else {
+				mutex_exit(&dv->sdev_lookup_lock);
+			}
+			break;
+		case SDEV_READY:
+			goto found;
+		case SDEV_ZOMBIE:
+			rw_exit(&ddv->sdev_contents);
+			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+			SDEV_RELE(dv);
+			goto lookup_failed;
+		default:
+			rw_exit(&ddv->sdev_contents);
+			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+			sdev_lookup_failed(ddv, nm, failed_flags);
+			*vpp = NULLVP;
+			return (ENOENT);
+		}
+	}
+	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
+
+	/*
+	 * ZOMBIED parent does not allow new node creation.
+	 * bail out early
+	 */
+	if (parent_state == SDEV_ZOMBIE) {
+		rw_exit(&ddv->sdev_contents);
+		*vpp = NULL;
+		SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+		return (ENOENT);
+	}
+
+	/*
+	 * (b0): backing store lookup
+	 *	SDEV_PERSIST is default except:
+	 *		1) pts nodes
+	 *		2) non-chmod'ed local nodes
+	 */
+	if (SDEV_IS_PERSIST(ddv)) {
+		error = devname_backstore_lookup(ddv, nm, &rvp);
+
+		if (!error) {
+			sdcmn_err3(("devname_backstore_lookup: "
+			    "found attrvp %p for %s\n", (void *)rvp, nm));
+
+			vattr.va_mask = AT_MODE|AT_UID|AT_GID;
+			error = VOP_GETATTR(rvp, &vattr, 0, cred);
+			if (error) {
+				rw_exit(&ddv->sdev_contents);
+				if (dv)
+					SDEV_RELE(dv);
+				SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+				sdev_lookup_failed(ddv, nm, failed_flags);
+				*vpp = NULLVP;
+				return (ENOENT);
+			}
+
+			if (vattr.va_type == VLNK) {
+				error = sdev_getlink(rvp, &link);
+				if (error) {
+					rw_exit(&ddv->sdev_contents);
+					if (dv)
+						SDEV_RELE(dv);
+					SD_TRACE_FAILED_LOOKUP(ddv, nm,
+					    retried);
+					sdev_lookup_failed(ddv, nm,
+					    failed_flags);
+					*vpp = NULLVP;
+					return (ENOENT);
+				}
+				ASSERT(link != NULL);
+			}
+
+			if (!rw_tryupgrade(&ddv->sdev_contents)) {
+				rw_exit(&ddv->sdev_contents);
+				rw_enter(&ddv->sdev_contents, RW_WRITER);
+			}
+			error = sdev_mknode(ddv, nm, &dv, &vattr,
+			    rvp, link, cred, SDEV_READY);
+			rw_downgrade(&ddv->sdev_contents);
+
+			if (link != NULL) {
+				kmem_free(link, strlen(link) + 1);
+				link = NULL;
+			}
+
+			if (error) {
+				SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+				rw_exit(&ddv->sdev_contents);
+				if (dv)
+					SDEV_RELE(dv);
+				goto lookup_failed;
+			} else {
+				goto found;
+			}
+		} else if (retried) {
+			rw_exit(&ddv->sdev_contents);
+			sdcmn_err3(("retry of lookup of %s/%s: failed\n",
+			    ddv->sdev_name, nm));
+			if (dv)
+				SDEV_RELE(dv);
+			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+			sdev_lookup_failed(ddv, nm, failed_flags);
+			*vpp = NULLVP;
+			return (ENOENT);
+		}
+	}
+
+
+	/* first thread that is doing the lookup on this node */
+	if (!dv) {
+		if (!rw_tryupgrade(&ddv->sdev_contents)) {
+			rw_exit(&ddv->sdev_contents);
+			rw_enter(&ddv->sdev_contents, RW_WRITER);
+		}
+		error = sdev_mknode(ddv, nm, &dv, NULL, NULL, NULL,
+		    cred, SDEV_INIT);
+		if (!dv) {
+			rw_exit(&ddv->sdev_contents);
+			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+			sdev_lookup_failed(ddv, nm, failed_flags);
+			*vpp = NULLVP;
+			return (ENOENT);
+		}
+		rw_downgrade(&ddv->sdev_contents);
+	}
+	ASSERT(dv);
+	ASSERT(SDEV_HELD(dv));
+
+	if (SDEV_IS_NO_NCACHE(dv)) {
+		failed_flags |= SLF_NO_NCACHE;
+	}
+
+	if (SDEV_IS_GLOBAL(ddv)) {
+		map = sdev_get_map(ddv, 1);
+		dirops = map ? map->dir_ops : NULL;
+		fn = dirops ? dirops->devnops_lookup : NULL;
+	}
+
+	/*
+	 * (b1) invoking devfsadm once per life time for devfsadm nodes
+	 */
+	if ((fn == NULL) && !callback) {
+
+		if (sdev_reconfig_boot || !i_ddi_io_initialized() ||
+		    SDEV_IS_DYNAMIC(ddv) || SDEV_IS_NO_NCACHE(dv) ||
+		    ((moddebug & MODDEBUG_FINI_EBUSY) != 0)) {
+			ASSERT(SDEV_HELD(dv));
+			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+			goto nolock_notfound;
+		}
+
+		/*
+		 * filter out known non-existent devices recorded
+		 * during initial reconfiguration boot for which
+		 * reconfig should not be done and lookup may
+		 * be short-circuited now.
+		 */
+		if (sdev_lookup_filter(ddv, nm)) {
+			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+			goto nolock_notfound;
+		}
+
+		/* bypassing devfsadm internal nodes */
+		if (is_devfsadm_thread(lookup_thread)) {
+			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+			goto nolock_notfound;
+		}
+
+		if (sdev_reconfig_disable) {
+			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+			goto nolock_notfound;
+		}
+
+		error = sdev_call_devfsadmd(ddv, dv, nm);
+		if (error == 0) {
+			sdcmn_err8(("lookup of %s/%s by %s: reconfig\n",
+			    ddv->sdev_name, nm, curproc->p_user.u_comm));
+			if (sdev_reconfig_verbose) {
+				cmn_err(CE_CONT,
+				    "?lookup of %s/%s by %s: reconfig\n",
+				    ddv->sdev_name, nm, curproc->p_user.u_comm);
+			}
+			retried = 1;
+			failed_flags |= SLF_REBUILT;
+			ASSERT(dv->sdev_state != SDEV_ZOMBIE);
+			SDEV_SIMPLE_RELE(dv);
+			goto tryagain;
+		} else {
+			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+			goto nolock_notfound;
+		}
+	}
+
+	/*
+	 * (b2) Directory Based Name Resolution (DBNR):
+	 *	ddv	- parent
+	 *	nm	- /dev/(ddv->sdev_name)/nm
+	 *
+	 *	note: module vnode ops take precedence than the build-in ones
+	 */
+	if (fn) {
+		error = sdev_call_modulelookup(ddv, &dv, nm, fn, cred);
+		if (error) {
+			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+			goto notfound;
+		} else {
+			goto found;
+		}
+	} else if (callback) {
+		error = sdev_call_dircallback(ddv, &dv, nm, callback,
+		    flags, cred);
+		if (error == 0) {
+			goto found;
+		} else {
+			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+			goto notfound;
+		}
+	}
+	ASSERT(rvp);
+
+found:
+	ASSERT(!(dv->sdev_flags & SDEV_STALE));
+	ASSERT(dv->sdev_state == SDEV_READY);
+	if (vtor) {
+		/*
+		 * Check validity of returned node
+		 */
+		switch (vtor(dv)) {
+		case SDEV_VTOR_VALID:
+			break;
+		case SDEV_VTOR_INVALID:
+			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+			sdcmn_err7(("lookup: destroy invalid "
+			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
+			goto nolock_notfound;
+		case SDEV_VTOR_SKIP:
+			sdcmn_err7(("lookup: node not applicable - "
+			    "skipping: %s(%p)\n", dv->sdev_name, (void *)dv));
+			rw_exit(&ddv->sdev_contents);
+			SD_TRACE_FAILED_LOOKUP(ddv, nm, retried);
+			SDEV_RELE(dv);
+			goto lookup_failed;
+		default:
+			cmn_err(CE_PANIC,
+			    "dev fs: validator failed: %s(%p)\n",
+			    dv->sdev_name, (void *)dv);
+			break;
+			/*NOTREACHED*/
+		}
+	}
+
+	if ((SDEVTOV(dv)->v_type == VDIR) && SDEV_IS_GLOBAL(dv)) {
+		rw_enter(&dv->sdev_contents, RW_READER);
+		(void) sdev_get_map(dv, 1);
+		rw_exit(&dv->sdev_contents);
+	}
+	rw_exit(&ddv->sdev_contents);
+	rv = sdev_to_vp(dv, vpp);
+	sdcmn_err3(("devname_lookup_func: returning vp %p v_count %d state %d "
+	    "for nm %s, error %d\n", (void *)*vpp, (*vpp)->v_count,
+	    dv->sdev_state, nm, rv));
+	return (rv);
+
+notfound:
+	mutex_enter(&dv->sdev_lookup_lock);
+	SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
+	mutex_exit(&dv->sdev_lookup_lock);
+nolock_notfound:
+	/*
+	 * Destroy the node that is created for synchronization purposes.
+	 */
+	sdcmn_err3(("devname_lookup_func: %s with state %d\n",
+	    nm, dv->sdev_state));
+	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
+	if (dv->sdev_state == SDEV_INIT) {
+		if (!rw_tryupgrade(&ddv->sdev_contents)) {
+			rw_exit(&ddv->sdev_contents);
+			rw_enter(&ddv->sdev_contents, RW_WRITER);
+		}
+
+		/*
+		 * Node state may have changed during the lock
+		 * changes. Re-check.
+		 */
+		if (dv->sdev_state == SDEV_INIT) {
+			(void) sdev_dirdelete(ddv, dv);
+			rw_exit(&ddv->sdev_contents);
+			sdev_lookup_failed(ddv, nm, failed_flags);
+			*vpp = NULL;
+			return (ENOENT);
+		}
+	}
+
+	rw_exit(&ddv->sdev_contents);
+	SDEV_RELE(dv);
+
+lookup_failed:
+	sdev_lookup_failed(ddv, nm, failed_flags);
+	*vpp = NULL;
+	return (ENOENT);
+}
+
+/*
+ * Given a directory node, mark all nodes beneath as
+ * STALE, i.e. nodes that don't exist as far as new
+ * consumers are concerned
+ */
+void
+sdev_stale(struct sdev_node *ddv)
+{
+	struct sdev_node *dv;
+	struct vnode *vp;
+
+	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
+
+	rw_enter(&ddv->sdev_contents, RW_WRITER);
+	for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next) {
+		vp = SDEVTOV(dv);
+		if (vp->v_type == VDIR)
+			sdev_stale(dv);
+
+		sdcmn_err9(("sdev_stale: setting stale %s\n",
+		    dv->sdev_name));
+		dv->sdev_flags |= SDEV_STALE;
+	}
+	ddv->sdev_flags |= SDEV_BUILD;
+	rw_exit(&ddv->sdev_contents);
+}
+
+/*
+ * Given a directory node, clean out all the nodes beneath.
+ * If expr is specified, clean node with names matching expr.
+ * If SDEV_ENFORCE is specified in flags, busy nodes are made stale,
+ *	so they are excluded from future lookups.
+ */
+int
+sdev_cleandir(struct sdev_node *ddv, char *expr, uint_t flags)
+{
+	int error = 0;
+	int busy = 0;
+	struct vnode *vp;
+	struct sdev_node *dv, *next = NULL;
+	int bkstore = 0;
+	int len = 0;
+	char *bks_name = NULL;
+
+	ASSERT(SDEVTOV(ddv)->v_type == VDIR);
+
+	/*
+	 * We try our best to destroy all unused sdev_node's
+	 */
+	rw_enter(&ddv->sdev_contents, RW_WRITER);
+	for (dv = ddv->sdev_dot; dv; dv = next) {
+		next = dv->sdev_next;
+		vp = SDEVTOV(dv);
+
+		if (expr && gmatch(dv->sdev_name, expr) == 0)
+			continue;
+
+		if (vp->v_type == VDIR &&
+		    sdev_cleandir(dv, NULL, flags) != 0) {
+			sdcmn_err9(("sdev_cleandir: dir %s busy\n",
+			    dv->sdev_name));
+			busy++;
+			continue;
+		}
+
+		if (vp->v_count > 0 && (flags & SDEV_ENFORCE) == 0) {
+			sdcmn_err9(("sdev_cleandir: dir %s busy\n",
+			    dv->sdev_name));
+			busy++;
+			continue;
+		}
+
+		/*
+		 * at this point, either dv is not held or SDEV_ENFORCE
+		 * is specified. In either case, dv needs to be deleted
+		 */
+		SDEV_HOLD(dv);
+
+		bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0;
+		if (bkstore && (vp->v_type == VDIR))
+			bkstore += 1;
+
+		if (bkstore) {
+			len = strlen(dv->sdev_name) + 1;
+			bks_name = kmem_alloc(len, KM_SLEEP);
+			bcopy(dv->sdev_name, bks_name, len);
+		}
+
+		error = sdev_dirdelete(ddv, dv);
+
+		if (error == EBUSY) {
+			sdcmn_err9(("sdev_cleandir: dir busy\n"));
+			busy++;
+		}
+
+		/* take care the backing store clean up */
+		if (bkstore && (error == 0)) {
+			ASSERT(bks_name);
+			ASSERT(ddv->sdev_attrvp);
+
+			if (bkstore == 1) {
+				error = VOP_REMOVE(ddv->sdev_attrvp,
+				    bks_name, kcred);
+			} else if (bkstore == 2) {
+				error = VOP_RMDIR(ddv->sdev_attrvp,
+				    bks_name, ddv->sdev_attrvp, kcred);
+			}
+
+			/* do not propagate the backing store errors */
+			if (error) {
+				sdcmn_err9(("sdev_cleandir: backing store"
+				    "not cleaned\n"));
+				error = 0;
+			}
+
+			bkstore = 0;
+			kmem_free(bks_name, len);
+			bks_name = NULL;
+			len = 0;
+		}
+	}
+
+	ddv->sdev_flags |= SDEV_BUILD;
+	rw_exit(&ddv->sdev_contents);
+
+	if (busy) {
+		error = EBUSY;
+	}
+
+	return (error);
+}
+
+/*
+ * a convenient wrapper for readdir() funcs
+ */
+size_t
+add_dir_entry(dirent64_t *de, char *nm, size_t size, ino_t ino, offset_t off)
+{
+	size_t reclen = DIRENT64_RECLEN(strlen(nm));
+	if (reclen > size)
+		return (0);
+
+	de->d_ino = (ino64_t)ino;
+	de->d_off = (off64_t)off + 1;
+	de->d_reclen = (ushort_t)reclen;
+	(void) strncpy(de->d_name, nm, DIRENT64_NAMELEN(reclen));
+	return (reclen);
+}
+
+/*
+ * sdev_mount service routines
+ */
+int
+sdev_copyin_mountargs(struct mounta *uap, struct sdev_mountargs *args)
+{
+	int	error;
+
+	if (uap->datalen != sizeof (*args))
+		return (EINVAL);
+
+	if (error = copyin(uap->dataptr, args, sizeof (*args))) {
+		cmn_err(CE_WARN, "sdev_copyin_mountargs: can not"
+		    "get user data. error %d\n", error);
+		return (EFAULT);
+	}
+
+	return (0);
+}
+
+#ifdef nextdp
+#undef nextdp
+#endif
+#define	nextdp(dp)	((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
+
+/*
+ * readdir helper func
+ */
+int
+devname_readdir_func(vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp,
+    int flags)
+{
+	struct sdev_node *ddv = VTOSDEV(vp);
+	struct sdev_node *dv;
+	dirent64_t	*dp;
+	ulong_t		outcount = 0;
+	size_t		namelen;
+	ulong_t		alloc_count;
+	void		*outbuf;
+	struct iovec	*iovp;
+	int		error = 0;
+	size_t		reclen;
+	offset_t	diroff;
+	offset_t	soff;
+	int		this_reclen;
+	struct devname_nsmap	*map = NULL;
+	struct devname_ops	*dirops = NULL;
+	int (*fn)(devname_handle_t *, struct cred *) = NULL;
+	int (*vtor)(struct sdev_node *) = NULL;
+	struct vattr attr;
+	timestruc_t now;
+
+	ASSERT(ddv->sdev_attr || ddv->sdev_attrvp);
+	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
+
+	if (uiop->uio_loffset >= MAXOFF_T) {
+		if (eofp)
+			*eofp = 1;
+		return (0);
+	}
+
+	if (uiop->uio_iovcnt != 1)
+		return (EINVAL);
+
+	if (vp->v_type != VDIR)
+		return (ENOTDIR);
+
+	if (ddv->sdev_flags & SDEV_VTOR) {
+		vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
+		ASSERT(vtor);
+	}
+
+	if (eofp != NULL)
+		*eofp = 0;
+
+	soff = uiop->uio_offset;
+	iovp = uiop->uio_iov;
+	alloc_count = iovp->iov_len;
+	dp = outbuf = kmem_alloc(alloc_count, KM_SLEEP);
+	outcount = 0;
+
+	if (ddv->sdev_state == SDEV_ZOMBIE)
+		goto get_cache;
+
+	if (!SDEV_IS_GLOBAL(ddv)) {
+		/* make sure directory content is up to date */
+		prof_filldir(ddv);
+	} else {
+		map = sdev_get_map(ddv, 0);
+		dirops = map ? map->dir_ops : NULL;
+		fn = dirops ? dirops->devnops_readdir : NULL;
+
+		if (map && map->dir_map) {
+			/*
+			 * load the name mapping rule database
+			 * through invoking devfsadm and symlink
+			 * all the entries in the map
+			 */
+			devname_rdr_result_t rdr_result;
+			int do_thread = 0;
+
+			rw_enter(&map->dir_lock, RW_READER);
+			do_thread = map->dir_maploaded ? 0 : 1;
+			rw_exit(&map->dir_lock);
+
+			if (do_thread) {
+				mutex_enter(&ddv->sdev_lookup_lock);
+				SDEV_BLOCK_OTHERS(ddv, SDEV_READDIR);
+				mutex_exit(&ddv->sdev_lookup_lock);
+
+				sdev_dispatch_to_nsrdr_thread(ddv,
+				    map->dir_map, &rdr_result);
+			}
+		} else if ((sdev_boot_state == SDEV_BOOT_STATE_COMPLETE) &&
+		    !sdev_reconfig_boot && (flags & SDEV_BROWSE) &&
+		    !SDEV_IS_DYNAMIC(ddv) && !SDEV_IS_NO_NCACHE(ddv) &&
+		    ((moddebug & MODDEBUG_FINI_EBUSY) == 0) &&
+		    !DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state) &&
+		    !DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
+		    !sdev_reconfig_disable) {
+			/*
+			 * invoking "devfsadm" to do system device reconfig
+			 */
+			mutex_enter(&ddv->sdev_lookup_lock);
+			SDEV_BLOCK_OTHERS(ddv,
+			    (SDEV_READDIR|SDEV_LGWAITING));
+			mutex_exit(&ddv->sdev_lookup_lock);
+
+			sdcmn_err8(("readdir of %s by %s: reconfig\n",
+			    ddv->sdev_path, curproc->p_user.u_comm));
+			if (sdev_reconfig_verbose) {
+				cmn_err(CE_CONT,
+				    "?readdir of %s by %s: reconfig\n",
+				    ddv->sdev_path, curproc->p_user.u_comm);
+			}
+
+			sdev_devfsadmd_thread(ddv, NULL, kcred);
+		} else if (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)) {
+			/*
+			 * compensate the "ls" started later than "devfsadm"
+			 */
+			mutex_enter(&ddv->sdev_lookup_lock);
+			SDEV_BLOCK_OTHERS(ddv, (SDEV_READDIR|SDEV_LGWAITING));
+			mutex_exit(&ddv->sdev_lookup_lock);
+		}
+
+		/*
+		 * release the contents lock so that
+		 * the cache maybe updated by devfsadmd
+		 */
+		rw_exit(&ddv->sdev_contents);
+		mutex_enter(&ddv->sdev_lookup_lock);
+		if (SDEV_IS_READDIR(ddv))
+			(void) sdev_wait4lookup(ddv, SDEV_READDIR);
+		mutex_exit(&ddv->sdev_lookup_lock);
+		rw_enter(&ddv->sdev_contents, RW_READER);
+
+		sdcmn_err4(("readdir of directory %s by %s\n",
+		    ddv->sdev_name, curproc->p_user.u_comm));
+		while (ddv->sdev_flags & SDEV_BUILD) {
+			if (SDEV_IS_PERSIST(ddv)) {
+				error = sdev_filldir_from_store(ddv,
+				    alloc_count, cred);
+			}
+
+			/*
+			 * pre-creating the directories
+			 * defined in vtab
+			 */
+			if (SDEVTOV(ddv)->v_flag & VROOT) {
+				error = sdev_filldir_dynamic(ddv);
+			}
+
+			if (!error)
+				ddv->sdev_flags &= ~SDEV_BUILD;
+		}
+	}
+
+get_cache:
+	/* handle "." and ".." */
+	diroff = 0;
+	if (soff == 0) {
+		/* first time */
+		this_reclen = DIRENT64_RECLEN(1);
+		if (alloc_count < this_reclen) {
+			error = EINVAL;
+			goto done;
+		}
+
+		dp->d_ino = (ino64_t)ddv->sdev_ino;
+		dp->d_off = (off64_t)1;
+		dp->d_reclen = (ushort_t)this_reclen;
+
+		(void) strncpy(dp->d_name, ".",
+		    DIRENT64_NAMELEN(this_reclen));
+		outcount += dp->d_reclen;
+		dp = nextdp(dp);
+	}
+
+	diroff++;
+	if (soff <= 1) {
+		this_reclen = DIRENT64_RECLEN(2);
+		if (alloc_count < outcount + this_reclen) {
+			error = EINVAL;
+			goto done;
+		}
+
+		dp->d_reclen = (ushort_t)this_reclen;
+		dp->d_ino = (ino64_t)ddv->sdev_dotdot->sdev_ino;
+		dp->d_off = (off64_t)2;
+
+		(void) strncpy(dp->d_name, "..",
+		    DIRENT64_NAMELEN(this_reclen));
+		outcount += dp->d_reclen;
+
+		dp = nextdp(dp);
+	}
+
+
+	/* gets the cache */
+	diroff++;
+	for (dv = ddv->sdev_dot; dv; dv = dv->sdev_next, diroff++) {
+		sdcmn_err3(("sdev_readdir: diroff %lld soff %lld for '%s' \n",
+		    diroff, soff, dv->sdev_name));
+
+		/* bypassing pre-matured nodes */
+		if (diroff < soff || (dv->sdev_state != SDEV_READY)) {
+			sdcmn_err3(("sdev_readdir: pre-mature node  "
+			    "%s\n", dv->sdev_name));
+			continue;
+		}
+
+		/* don't list stale nodes */
+		if (dv->sdev_flags & SDEV_STALE) {
+			sdcmn_err4(("sdev_readdir: STALE node  "
+			    "%s\n", dv->sdev_name));
+			continue;
+		}
+
+		/*
+		 * Check validity of node
+		 */
+		if (vtor) {
+			switch (vtor(dv)) {
+			case SDEV_VTOR_VALID:
+				break;
+			case SDEV_VTOR_INVALID:
+			case SDEV_VTOR_SKIP:
+				continue;
+			default:
+				cmn_err(CE_PANIC,
+				    "dev fs: validator failed: %s(%p)\n",
+				    dv->sdev_name, (void *)dv);
+				break;
+			/*NOTREACHED*/
+			}
+		}
+
+		/*
+		 * call back into the module for the validity/bookkeeping
+		 * of this entry
+		 */
+		if (fn) {
+			error = (*fn)(&(dv->sdev_handle), cred);
+			if (error) {
+				sdcmn_err4(("sdev_readdir: module did not "
+				    "validate %s\n", dv->sdev_name));
+				continue;
+			}
+		}
+
+		namelen = strlen(dv->sdev_name);
+		reclen = DIRENT64_RECLEN(namelen);
+		if (outcount + reclen > alloc_count) {
+			goto full;
+		}
+		dp->d_reclen = (ushort_t)reclen;
+		dp->d_ino = (ino64_t)dv->sdev_ino;
+		dp->d_off = (off64_t)diroff + 1;
+		(void) strncpy(dp->d_name, dv->sdev_name,
+		    DIRENT64_NAMELEN(reclen));
+		outcount += reclen;
+		dp = nextdp(dp);
+	}
+
+full:
+	sdcmn_err4(("sdev_readdir: moving %lu bytes: "
+	    "diroff %lld, soff %lld, dv %p\n", outcount, diroff, soff,
+	    (void *)dv));
+
+	if (outcount)
+		error = uiomove(outbuf, outcount, UIO_READ, uiop);
+
+	if (!error) {
+		uiop->uio_offset = diroff;
+		if (eofp)
+			*eofp = dv ? 0 : 1;
+	}
+
+
+	if (ddv->sdev_attrvp) {
+		gethrestime(&now);
+		attr.va_ctime = now;
+		attr.va_atime = now;
+		attr.va_mask = AT_CTIME|AT_ATIME;
+
+		(void) VOP_SETATTR(ddv->sdev_attrvp, &attr, 0, kcred, NULL);
+	}
+done:
+	kmem_free(outbuf, alloc_count);
+	return (error);
+}
+
+
+static int
+sdev_modctl_lookup(const char *path, vnode_t **r_vp)
+{
+	vnode_t *vp;
+	vnode_t *cvp;
+	struct sdev_node *svp;
+	char *nm;
+	struct pathname pn;
+	int error;
+	int persisted = 0;
+
+	if (error = pn_get((char *)path, UIO_SYSSPACE, &pn))
+		return (error);
+	nm = kmem_alloc(MAXNAMELEN, KM_SLEEP);
+
+	vp = rootdir;
+	VN_HOLD(vp);
+
+	while (pn_pathleft(&pn)) {
+		ASSERT(vp->v_type == VDIR);
+		(void) pn_getcomponent(&pn, nm);
+		error = VOP_LOOKUP(vp, nm, &cvp, NULL, 0, NULL, kcred);
+		VN_RELE(vp);
+
+		if (error)
+			break;
+
+		/* traverse mount points encountered on our journey */
+		if (vn_ismntpt(cvp) && (error = traverse(&cvp)) != 0) {
+			VN_RELE(cvp);
+			break;
+		}
+
+		/*
+		 * Direct the operation to the persisting filesystem
+		 * underlying /dev.  Bail if we encounter a
+		 * non-persistent dev entity here.
+		 */
+		if (cvp->v_vfsp->vfs_fstype == devtype) {
+
+			if ((VTOSDEV(cvp)->sdev_flags & SDEV_PERSIST) == 0) {
+				error = ENOENT;
+				VN_RELE(cvp);
+				break;
+			}
+
+			if (VTOSDEV(cvp) == NULL) {
+				error = ENOENT;
+				VN_RELE(cvp);
+				break;
+			}
+			svp = VTOSDEV(cvp);
+			if ((vp = svp->sdev_attrvp) == NULL) {
+				error = ENOENT;
+				VN_RELE(cvp);
+				break;
+			}
+			persisted = 1;
+			VN_HOLD(vp);
+			VN_RELE(cvp);
+			cvp = vp;
+		}
+
+		vp = cvp;
+		pn_skipslash(&pn);
+	}
+
+	kmem_free(nm, MAXNAMELEN);
+	pn_free(&pn);
+
+	if (error)
+		return (error);
+
+	/*
+	 * Only return persisted nodes in the filesystem underlying /dev.
+	 */
+	if (!persisted) {
+		VN_RELE(vp);
+		return (ENOENT);
+	}
+
+	*r_vp = vp;
+	return (0);
+}
+
+int
+sdev_modctl_readdir(const char *dir, char ***dirlistp,
+	int *npathsp, int *npathsp_alloc)
+{
+	char	**pathlist = NULL;
+	char	**newlist = NULL;
+	int	npaths = 0;
+	int	npaths_alloc = 0;
+	dirent64_t *dbuf = NULL;
+	int	n;
+	char	*s;
+	int error;
+	vnode_t *vp;
+	int eof;
+	struct iovec iov;
+	struct uio uio;
+	struct dirent64 *dp;
+	size_t dlen;
+	size_t dbuflen;
+	int ndirents = 64;
+	char *nm;
+
+	error = sdev_modctl_lookup(dir, &vp);
+	sdcmn_err11(("modctl readdir: %s by %s: %s\n",
+	    dir, curproc->p_user.u_comm,
+	    (error == 0) ? "ok" : "failed"));
+	if (error)
+		return (error);
+
+	dlen = ndirents * (sizeof (*dbuf));
+	dbuf = kmem_alloc(dlen, KM_SLEEP);
+
+	uio.uio_iov = &iov;
+	uio.uio_iovcnt = 1;
+	uio.uio_segflg = UIO_SYSSPACE;
+	uio.uio_fmode = 0;
+	uio.uio_extflg = UIO_COPY_CACHED;
+	uio.uio_loffset = 0;
+	uio.uio_llimit = MAXOFFSET_T;
+
+	eof = 0;
+	error = 0;
+	while (!error && !eof) {
+		uio.uio_resid = dlen;
+		iov.iov_base = (char *)dbuf;
+		iov.iov_len = dlen;
+
+		(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
+		error = VOP_READDIR(vp, &uio, kcred, &eof);
+		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
+
+		dbuflen = dlen - uio.uio_resid;
+
+		if (error || dbuflen == 0)
+			break;
+
+		for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen);
+			dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
+
+			nm = dp->d_name;
+
+			if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0)
+				continue;
+
+			if (npaths == npaths_alloc) {
+				npaths_alloc += 64;
+				newlist = (char **)
+				    kmem_zalloc((npaths_alloc + 1) *
+					sizeof (char *), KM_SLEEP);
+				if (pathlist) {
+					bcopy(pathlist, newlist,
+					    npaths * sizeof (char *));
+					kmem_free(pathlist,
+					    (npaths + 1) * sizeof (char *));
+				}
+				pathlist = newlist;
+			}
+			n = strlen(nm) + 1;
+			s = kmem_alloc(n, KM_SLEEP);
+			bcopy(nm, s, n);
+			pathlist[npaths++] = s;
+			sdcmn_err11(("  %s/%s\n", dir, s));
+		}
+	}
+
+exit:
+	VN_RELE(vp);
+
+	if (dbuf)
+		kmem_free(dbuf, dlen);
+
+	if (error)
+		return (error);
+
+	*dirlistp = pathlist;
+	*npathsp = npaths;
+	*npathsp_alloc = npaths_alloc;
+
+	return (0);
+}
+
+void
+sdev_modctl_readdir_free(char **pathlist, int npaths, int npaths_alloc)
+{
+	int	i, n;
+
+	for (i = 0; i < npaths; i++) {
+		n = strlen(pathlist[i]) + 1;
+		kmem_free(pathlist[i], n);
+	}
+
+	kmem_free(pathlist, (npaths_alloc + 1) * sizeof (char *));
+}
+
+int
+sdev_modctl_devexists(const char *path)
+{
+	vnode_t *vp;
+	int error;
+
+	error = sdev_modctl_lookup(path, &vp);
+	sdcmn_err11(("modctl dev exists: %s by %s: %s\n",
+	    path, curproc->p_user.u_comm,
+	    (error == 0) ? "ok" : "failed"));
+	if (error == 0)
+		VN_RELE(vp);
+
+	return (error);
+}
+
+void
+sdev_update_newnsmap(struct devname_nsmap *map, char *module, char *mapname)
+{
+	rw_enter(&map->dir_lock, RW_WRITER);
+	if (module) {
+		ASSERT(map->dir_newmodule == NULL);
+		map->dir_newmodule = i_ddi_strdup(module, KM_SLEEP);
+	}
+	if (mapname) {
+		ASSERT(map->dir_newmap == NULL);
+		map->dir_newmap = i_ddi_strdup(mapname, KM_SLEEP);
+	}
+
+	map->dir_invalid = 1;
+	rw_exit(&map->dir_lock);
+}
+
+void
+sdev_replace_nsmap(struct devname_nsmap *map, char *module, char *mapname)
+{
+	char *old_module = NULL;
+	char *old_map = NULL;
+
+	ASSERT(RW_LOCK_HELD(&map->dir_lock));
+	if (!rw_tryupgrade(&map->dir_lock)) {
+		rw_exit(&map->dir_lock);
+		rw_enter(&map->dir_lock, RW_WRITER);
+	}
+
+	old_module = map->dir_module;
+	if (module) {
+		if (old_module && strcmp(old_module, module) != 0) {
+			kmem_free(old_module, strlen(old_module) + 1);
+		}
+		map->dir_module = module;
+		map->dir_newmodule = NULL;
+	}
+
+	old_map = map->dir_map;
+	if (mapname) {
+		if (old_map && strcmp(old_map, mapname) != 0) {
+			kmem_free(old_map, strlen(old_map) + 1);
+		}
+
+		map->dir_map = mapname;
+		map->dir_newmap = NULL;
+	}
+	map->dir_maploaded = 0;
+	map->dir_invalid = 0;
+	rw_downgrade(&map->dir_lock);
+}
+
+/*
+ * dir_name should have at least one attribute,
+ *	dir_module
+ *	or dir_map
+ *	or both
+ * caller holds the devname_nsmaps_lock
+ */
+void
+sdev_insert_nsmap(char *dir_name, char *dir_module, char *dir_map)
+{
+	struct devname_nsmap *map;
+	int len = 0;
+
+	ASSERT(dir_name);
+	ASSERT(dir_module || dir_map);
+	ASSERT(MUTEX_HELD(&devname_nsmaps_lock));
+
+	if (map = sdev_get_nsmap_by_dir(dir_name, 1)) {
+		sdev_update_newnsmap(map, dir_module, dir_map);
+		return;
+	}
+
+	map = (struct devname_nsmap *)kmem_zalloc(sizeof (*map), KM_SLEEP);
+	map->dir_name = i_ddi_strdup(dir_name, KM_SLEEP);
+	if (dir_module) {
+		map->dir_module = i_ddi_strdup(dir_module, KM_SLEEP);
+	}
+
+	if (dir_map) {
+		if (dir_map[0] != '/') {
+			len = strlen(ETC_DEV_DIR) + strlen(dir_map) + 2;
+			map->dir_map = kmem_zalloc(len, KM_SLEEP);
+			(void) snprintf(map->dir_map, len, "%s/%s", ETC_DEV_DIR,
+			    dir_map);
+		} else {
+			map->dir_map = i_ddi_strdup(dir_map, KM_SLEEP);
+		}
+	}
+
+	map->dir_ops = NULL;
+	map->dir_maploaded = 0;
+	map->dir_invalid = 0;
+	rw_init(&map->dir_lock, NULL, RW_DEFAULT, NULL);
+
+	map->next = devname_nsmaps;
+	map->prev = NULL;
+	if (devname_nsmaps) {
+		devname_nsmaps->prev = map;
+	}
+	devname_nsmaps = map;
+}
+
+struct devname_nsmap *
+sdev_get_nsmap_by_dir(char *dir_path, int locked)
+{
+	struct devname_nsmap *map = NULL;
+
+	if (!locked)
+		mutex_enter(&devname_nsmaps_lock);
+	for (map = devname_nsmaps; map; map = map->next) {
+		sdcmn_err6(("sdev_get_nsmap_by_dir: dir %s\n", map->dir_name));
+		if (strcmp(map->dir_name, dir_path) == 0) {
+			if (!locked)
+				mutex_exit(&devname_nsmaps_lock);
+			return (map);
+		}
+	}
+	if (!locked)
+		mutex_exit(&devname_nsmaps_lock);
+	return (NULL);
+}
+
+struct devname_nsmap *
+sdev_get_nsmap_by_module(char *mod_name)
+{
+	struct devname_nsmap *map = NULL;
+
+	mutex_enter(&devname_nsmaps_lock);
+	for (map = devname_nsmaps; map; map = map->next) {
+		sdcmn_err7(("sdev_get_nsmap_by_module: module %s\n",
+		    map->dir_module));
+		if (map->dir_module && strcmp(map->dir_module, mod_name) == 0) {
+			mutex_exit(&devname_nsmaps_lock);
+			return (map);
+		}
+	}
+	mutex_exit(&devname_nsmaps_lock);
+	return (NULL);
+}
+
+void
+sdev_invalidate_nsmaps()
+{
+	struct devname_nsmap *map = NULL;
+
+	ASSERT(MUTEX_HELD(&devname_nsmaps_lock));
+
+	if (devname_nsmaps == NULL)
+		return;
+
+	for (map = devname_nsmaps; map; map = map->next) {
+		rw_enter(&map->dir_lock, RW_WRITER);
+		map->dir_invalid = 1;
+		rw_exit(&map->dir_lock);
+	}
+	devname_nsmaps_invalidated = 1;
+}
+
+
+int
+sdev_nsmaps_loaded()
+{
+	int ret = 0;
+
+	mutex_enter(&devname_nsmaps_lock);
+	if (devname_nsmaps_loaded)
+		ret = 1;
+
+	mutex_exit(&devname_nsmaps_lock);
+	return (ret);
+}
+
+int
+sdev_nsmaps_reloaded()
+{
+	int ret = 0;
+
+	mutex_enter(&devname_nsmaps_lock);
+	if (devname_nsmaps_invalidated)
+		ret = 1;
+
+	mutex_exit(&devname_nsmaps_lock);
+	return (ret);
+}
+
+static void
+sdev_free_nsmap(struct devname_nsmap *map)
+{
+	ASSERT(map);
+	if (map->dir_name)
+		kmem_free(map->dir_name, strlen(map->dir_name) + 1);
+	if (map->dir_module)
+		kmem_free(map->dir_module, strlen(map->dir_module) + 1);
+	if (map->dir_map)
+		kmem_free(map->dir_map, strlen(map->dir_map) + 1);
+	rw_destroy(&map->dir_lock);
+	kmem_free(map, sizeof (*map));
+}
+
+void
+sdev_validate_nsmaps()
+{
+	struct devname_nsmap *map = NULL;
+	struct devname_nsmap *oldmap = NULL;
+
+	ASSERT(MUTEX_HELD(&devname_nsmaps_lock));
+	map = devname_nsmaps;
+	while (map) {
+		rw_enter(&map->dir_lock, RW_READER);
+		if ((map->dir_invalid == 1) && (map->dir_newmodule == NULL) &&
+		    (map->dir_newmap == NULL)) {
+			oldmap = map;
+			rw_exit(&map->dir_lock);
+			if (map->prev)
+				map->prev->next = oldmap->next;
+			if (map == devname_nsmaps)
+				devname_nsmaps = oldmap->next;
+
+			map = oldmap->next;
+			if (map)
+				map->prev = oldmap->prev;
+			sdev_free_nsmap(oldmap);
+			oldmap = NULL;
+		} else {
+			rw_exit(&map->dir_lock);
+			map = map->next;
+		}
+	}
+	devname_nsmaps_invalidated = 0;
+}
+
+static int
+sdev_map_is_invalid(struct devname_nsmap *map)
+{
+	int ret = 0;
+
+	ASSERT(map);
+	rw_enter(&map->dir_lock, RW_READER);
+	if (map->dir_invalid)
+		ret = 1;
+	rw_exit(&map->dir_lock);
+	return (ret);
+}
+
+static int
+sdev_check_map(struct devname_nsmap *map)
+{
+	struct devname_nsmap *mapp;
+
+	mutex_enter(&devname_nsmaps_lock);
+	if (devname_nsmaps == NULL) {
+		mutex_exit(&devname_nsmaps_lock);
+		return (1);
+	}
+
+	for (mapp = devname_nsmaps; mapp; mapp = mapp->next) {
+		if (mapp == map) {
+			mutex_exit(&devname_nsmaps_lock);
+			return (0);
+		}
+	}
+
+	mutex_exit(&devname_nsmaps_lock);
+	return (1);
+
+}
+
+struct devname_nsmap *
+sdev_get_map(struct sdev_node *dv, int validate)
+{
+	struct devname_nsmap *map;
+	int error;
+
+	ASSERT(RW_READ_HELD(&dv->sdev_contents));
+	map = dv->sdev_mapinfo;
+	if (map && sdev_check_map(map)) {
+		if (!rw_tryupgrade(&dv->sdev_contents)) {
+			rw_exit(&dv->sdev_contents);
+			rw_enter(&dv->sdev_contents, RW_WRITER);
+		}
+		dv->sdev_mapinfo = NULL;
+		rw_downgrade(&dv->sdev_contents);
+		return (NULL);
+	}
+
+	if (validate && (!map || (map && sdev_map_is_invalid(map)))) {
+		if (!rw_tryupgrade(&dv->sdev_contents)) {
+			rw_exit(&dv->sdev_contents);
+			rw_enter(&dv->sdev_contents, RW_WRITER);
+		}
+		error = sdev_get_moduleops(dv);
+		if (!error)
+			map = dv->sdev_mapinfo;
+		rw_downgrade(&dv->sdev_contents);
+	}
+	return (map);
+}
+
+void
+sdev_handle_alloc(struct sdev_node *dv)
+{
+	rw_enter(&dv->sdev_contents, RW_WRITER);
+	dv->sdev_handle.dh_data = dv;
+	rw_exit(&dv->sdev_contents);
+}
+
+
+extern int sdev_vnodeops_tbl_size;
+
+/*
+ * construct a new template with overrides from vtab
+ */
+static fs_operation_def_t *
+sdev_merge_vtab(const fs_operation_def_t tab[])
+{
+	fs_operation_def_t *new;
+	const fs_operation_def_t *tab_entry;
+
+	/* make a copy of standard vnode ops table */
+	new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP);
+	bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size);
+
+	/* replace the overrides from tab */
+	for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) {
+		fs_operation_def_t *std_entry = new;
+		while (std_entry->name) {
+			if (strcmp(tab_entry->name, std_entry->name) == 0) {
+				std_entry->func = tab_entry->func;
+				break;
+			}
+			std_entry++;
+		}
+		if (std_entry->name == NULL)
+			cmn_err(CE_NOTE, "sdev_merge_vtab: entry %s unused.",
+			    tab_entry->name);
+	}
+
+	return (new);
+}
+
+/* free memory allocated by sdev_merge_vtab */
+static void
+sdev_free_vtab(fs_operation_def_t *new)
+{
+	kmem_free(new, sdev_vnodeops_tbl_size);
+}
+
+void
+devname_get_vnode(devname_handle_t *hdl, vnode_t **vpp)
+{
+	struct sdev_node *dv = hdl->dh_data;
+
+	ASSERT(dv);
+
+	rw_enter(&dv->sdev_contents, RW_READER);
+	*vpp = SDEVTOV(dv);
+	rw_exit(&dv->sdev_contents);
+}
+
+int
+devname_get_path(devname_handle_t *hdl, char **path)
+{
+	struct sdev_node *dv = hdl->dh_data;
+
+	ASSERT(dv);
+
+	rw_enter(&dv->sdev_contents, RW_READER);
+	*path = dv->sdev_path;
+	rw_exit(&dv->sdev_contents);
+	return (0);
+}
+
+int
+devname_get_name(devname_handle_t *hdl, char **entry)
+{
+	struct sdev_node *dv = hdl->dh_data;
+
+	ASSERT(dv);
+	rw_enter(&dv->sdev_contents, RW_READER);
+	*entry = dv->sdev_name;
+	rw_exit(&dv->sdev_contents);
+	return (0);
+}
+
+void
+devname_get_dir_vnode(devname_handle_t *hdl, vnode_t **vpp)
+{
+	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
+
+	ASSERT(dv);
+
+	rw_enter(&dv->sdev_contents, RW_READER);
+	*vpp = SDEVTOV(dv);
+	rw_exit(&dv->sdev_contents);
+}
+
+int
+devname_get_dir_path(devname_handle_t *hdl, char **path)
+{
+	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
+
+	ASSERT(dv);
+	rw_enter(&dv->sdev_contents, RW_READER);
+	*path = dv->sdev_path;
+	rw_exit(&dv->sdev_contents);
+	return (0);
+}
+
+int
+devname_get_dir_name(devname_handle_t *hdl, char **entry)
+{
+	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
+
+	ASSERT(dv);
+	rw_enter(&dv->sdev_contents, RW_READER);
+	*entry = dv->sdev_name;
+	rw_exit(&dv->sdev_contents);
+	return (0);
+}
+
+int
+devname_get_dir_nsmap(devname_handle_t *hdl, struct devname_nsmap **map)
+{
+	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
+
+	ASSERT(dv);
+	rw_enter(&dv->sdev_contents, RW_READER);
+	*map = dv->sdev_mapinfo;
+	rw_exit(&dv->sdev_contents);
+	return (0);
+}
+
+int
+devname_get_dir_handle(devname_handle_t *hdl, devname_handle_t **dir_hdl)
+{
+	struct sdev_node *dv = hdl->dh_data->sdev_dotdot;
+
+	ASSERT(dv);
+	rw_enter(&dv->sdev_contents, RW_READER);
+	*dir_hdl = &(dv->sdev_handle);
+	rw_exit(&dv->sdev_contents);
+	return (0);
+}
+
+void
+devname_set_nodetype(devname_handle_t *hdl, void *args, int spec)
+{
+	struct sdev_node *dv = hdl->dh_data;
+
+	ASSERT(dv);
+	rw_enter(&dv->sdev_contents, RW_WRITER);
+	hdl->dh_spec = (devname_spec_t)spec;
+	hdl->dh_args = (void *)i_ddi_strdup((char *)args, KM_SLEEP);
+	rw_exit(&dv->sdev_contents);
+}
+
+/*
+ * a generic setattr() function
+ *
+ * note: flags only supports AT_UID and AT_GID.
+ *	 Future enhancements can be done for other types, e.g. AT_MODE
+ */
+int
+devname_setattr_func(struct vnode *vp, struct vattr *vap, int flags,
+    struct cred *cred, int (*callback)(struct sdev_node *, struct vattr *,
+    int), int protocol)
+{
+	struct sdev_node	*dv = VTOSDEV(vp);
+	struct sdev_node	*parent = dv->sdev_dotdot;
+	struct vattr		*get;
+	uint_t			mask = vap->va_mask;
+	int 			error;
+
+	/* some sanity checks */
+	if (vap->va_mask & AT_NOSET)
+		return (EINVAL);
+
+	if (vap->va_mask & AT_SIZE) {
+		if (vp->v_type == VDIR) {
+			return (EISDIR);
+		}
+	}
+
+	/* no need to set attribute, but do not fail either */
+	ASSERT(parent);
+	rw_enter(&parent->sdev_contents, RW_READER);
+	if (dv->sdev_state == SDEV_ZOMBIE) {
+		rw_exit(&parent->sdev_contents);
+		return (0);
+	}
+
+	/* If backing store exists, just set it. */
+	if (dv->sdev_attrvp) {
+		rw_exit(&parent->sdev_contents);
+		return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL));
+	}
+
+	/*
+	 * Otherwise, for nodes with the persistence attribute, create it.
+	 */
+	ASSERT(dv->sdev_attr);
+	if (SDEV_IS_PERSIST(dv) ||
+	    ((vap->va_mask & ~AT_TIMES) != 0 && !SDEV_IS_DYNAMIC(dv))) {
+		sdev_vattr_merge(dv, vap);
+		rw_enter(&dv->sdev_contents, RW_WRITER);
+		error = sdev_shadow_node(dv, cred);
+		rw_exit(&dv->sdev_contents);
+		rw_exit(&parent->sdev_contents);
+
+		if (error)
+			return (error);
+		return (VOP_SETATTR(dv->sdev_attrvp, vap, flags, cred, NULL));
+	}
+
+
+	/*
+	 * sdev_attr was allocated in sdev_mknode
+	 */
+	rw_enter(&dv->sdev_contents, RW_WRITER);
+	error = secpolicy_vnode_setattr(cred, vp, vap, dv->sdev_attr,
+	    flags, sdev_unlocked_access, dv);
+	if (error) {
+		rw_exit(&dv->sdev_contents);
+		rw_exit(&parent->sdev_contents);
+		return (error);
+	}
+
+	get = dv->sdev_attr;
+	if (mask & AT_MODE) {
+		get->va_mode &= S_IFMT;
+		get->va_mode |= vap->va_mode & ~S_IFMT;
+	}
+
+	if ((mask & AT_UID) || (mask & AT_GID)) {
+		if (mask & AT_UID)
+			get->va_uid = vap->va_uid;
+		if (mask & AT_GID)
+			get->va_gid = vap->va_gid;
+		/*
+		 * a callback must be provided if the protocol is set
+		 */
+		if ((protocol & AT_UID) || (protocol & AT_GID)) {
+			ASSERT(callback);
+			error = callback(dv, get, protocol);
+			if (error) {
+				rw_exit(&dv->sdev_contents);
+				rw_exit(&parent->sdev_contents);
+				return (error);
+			}
+		}
+	}
+
+	if (mask & AT_ATIME)
+		get->va_atime = vap->va_atime;
+	if (mask & AT_MTIME)
+		get->va_mtime = vap->va_mtime;
+	if (mask & (AT_MODE | AT_UID | AT_GID | AT_CTIME)) {
+		gethrestime(&get->va_ctime);
+	}
+
+	sdev_vattr_merge(dv, get);
+	rw_exit(&dv->sdev_contents);
+	rw_exit(&parent->sdev_contents);
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/fs/dev/sdev_vfsops.c	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,524 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * This is the /dev (hence, the sdev_ prefix) filesystem.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/kmem.h>
+#include <sys/time.h>
+#include <sys/pathname.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/statvfs.h>
+#include <sys/policy.h>
+#include <sys/mount.h>
+#include <sys/debug.h>
+#include <sys/modctl.h>
+#include <sys/mkdev.h>
+#include <fs/fs_subr.h>
+#include <sys/fs/sdev_impl.h>
+#include <sys/fs/sdev_node.h>
+#include <sys/fs/snode.h>
+#include <sys/fs/dv_node.h>
+#include <sys/sunndi.h>
+#include <sys/mntent.h>
+
+/*
+ * /dev vfs operations.
+ */
+
+/*
+ * globals
+ */
+struct sdev_data *sdev_origins; /* mount info for origins under /dev */
+
+/*
+ * static
+ */
+static kmutex_t sdev_lock;	/* protects global data */
+static major_t devmajor;	/* the fictitious major we live on */
+static major_t devminor;	/* the fictitious minor of this instance */
+static struct sdev_data *sdev_mntinfo = NULL;	/* linked list of instances */
+static struct vnode *sdev_stale_attrvp; /* stale root attrvp after remount */
+static int sdev_mntinfo_cnt;	/* mntinfo reference count */
+
+static int sdev_mount(struct vfs *, struct vnode *, struct mounta *,
+    struct cred *);
+static int sdev_unmount(struct vfs *, int, struct cred *);
+static int sdev_root(struct vfs *, struct vnode **);
+static int sdev_statvfs(struct vfs *, struct statvfs64 *);
+static void sdev_insert_mntinfo(struct sdev_data *);
+static int devinit(int, char *);
+
+static vfsdef_t sdev_vfssw = {
+	VFSDEF_VERSION,
+	"dev",		/* type name string */
+	devinit,	/* init routine */
+	VSW_CANREMOUNT,	/* flags */
+	NULL		/* mount options table prototype */
+};
+
+
+/*
+ * Module linkage information
+ */
+static struct modlfs modlfs = {
+	&mod_fsops, "/dev filesystem %I%", &sdev_vfssw
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, (void *)&modlfs, NULL
+};
+
+int
+_init(void)
+{
+	int e;
+
+	mutex_init(&sdev_lock, NULL, MUTEX_DEFAULT, NULL);
+	sdev_node_cache_init();
+	sdev_devfsadm_lockinit();
+	if ((e = mod_install(&modlinkage)) != 0) {
+		sdev_devfsadm_lockdestroy();
+		sdev_node_cache_fini();
+		mutex_destroy(&sdev_lock);
+		return (e);
+	}
+	return (0);
+}
+
+/*
+ * dev module remained loaded for the global /dev instance
+ */
+int
+_fini(void)
+{
+	return (EBUSY);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+/*ARGSUSED*/
+static int
+devinit(int fstype, char *name)
+{
+	static const fs_operation_def_t dev_vfsops_tbl[] = {
+		VFSNAME_MOUNT, sdev_mount,	/* mount file system */
+		VFSNAME_UNMOUNT, sdev_unmount,	/* unmount file system */
+		VFSNAME_ROOT, sdev_root,	/* get root vnode */
+		VFSNAME_STATVFS, sdev_statvfs,	/* get file system statistics */
+		NULL, NULL
+	};
+
+	int	error;
+	extern major_t getudev(void);
+
+	devtype = fstype;
+
+	error = vfs_setfsops(fstype, dev_vfsops_tbl, NULL);
+	if (error != 0) {
+		cmn_err(CE_WARN, "devinit: bad vfs ops tbl");
+		return (error);
+	}
+
+	error = vn_make_ops("dev", sdev_vnodeops_tbl, &sdev_vnodeops);
+	if (error != 0) {
+		(void) vfs_freevfsops_by_type(fstype);
+		cmn_err(CE_WARN, "devinit: bad vnode ops tbl");
+		return (error);
+	}
+
+	if ((devmajor = getudev()) == (major_t)-1) {
+		cmn_err(CE_WARN, "%s: can't get unique dev", sdev_vfssw.name);
+		return (1);
+	}
+
+	/* initialize negative cache */
+	sdev_ncache_init();
+
+	return (0);
+}
+
+/*
+ * Both mount point and backing store directory name are
+ * passed in from userland
+ */
+static int
+sdev_mount(struct vfs *vfsp, struct vnode *mvp, struct mounta *uap,
+    struct cred *cr)
+{
+	struct sdev_data *sdev_data;
+	struct vnode *avp;
+	struct sdev_node *dv;
+	struct sdev_mountargs *args = NULL;
+	int	error = 0;
+	dev_t	devdev;
+
+	/*
+	 * security check
+	 */
+	if ((secpolicy_fs_mount(cr, mvp, vfsp) != 0) ||
+	    (secpolicy_sys_devices(cr) != 0))
+		return (EPERM);
+
+	/*
+	 * Sanity check the mount point
+	 */
+	if (mvp->v_type != VDIR)
+		return (ENOTDIR);
+
+	/*
+	 * Sanity Check for overlay mount.
+	 */
+	mutex_enter(&mvp->v_lock);
+	if ((uap->flags & MS_OVERLAY) == 0 &&
+	    (uap->flags & MS_REMOUNT) == 0 &&
+	    (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+		mutex_exit(&mvp->v_lock);
+		return (EBUSY);
+	}
+	mutex_exit(&mvp->v_lock);
+
+	args = kmem_zalloc(sizeof (*args), KM_SLEEP);
+
+	if ((uap->flags & MS_DATA) &&
+	    (uap->datalen != 0 && uap->dataptr != NULL)) {
+		/* copy in the arguments */
+		if (error = sdev_copyin_mountargs(uap, args))
+			goto cleanup;
+	}
+
+	/*
+	 * Sanity check the backing store
+	 */
+	if (args->sdev_attrdir) {
+		/* user supplied an attribute store */
+		if (error = lookupname((char *)(uintptr_t)args->sdev_attrdir,
+		    UIO_USERSPACE, FOLLOW, NULLVPP, &avp)) {
+			cmn_err(CE_NOTE, "/dev fs: lookup on attribute "
+			    "directory %s failed",
+			    (char *)(uintptr_t)args->sdev_attrdir);
+			goto cleanup;
+		}
+
+		if (avp->v_type != VDIR) {
+			VN_RELE(avp);
+			error = ENOTDIR;
+			goto cleanup;
+		}
+	} else {
+		/* use mountp as the attribute store */
+		avp = mvp;
+		VN_HOLD(avp);
+	}
+
+	mutex_enter(&sdev_lock);
+
+	/*
+	 * handling installation
+	 */
+	if (uap->flags & MS_REMOUNT) {
+		sdev_data = (struct sdev_data *)vfsp->vfs_data;
+		ASSERT(sdev_data);
+
+		dv = sdev_data->sdev_root;
+		ASSERT(dv == dv->sdev_dotdot);
+
+		/*
+		 * mark all existing sdev_nodes (except root node) stale
+		 */
+		sdev_stale(dv);
+
+		/* Reset previous mountargs */
+		if (sdev_data->sdev_mountargs) {
+			kmem_free(sdev_data->sdev_mountargs,
+			    sizeof (struct sdev_mountargs));
+		}
+		sdev_data->sdev_mountargs = args;
+		args = NULL;		/* so it won't be freed below */
+
+		sdev_stale_attrvp = dv->sdev_attrvp;
+		dv->sdev_attrvp = avp;
+		vfsp->vfs_mtime = ddi_get_time();
+
+		mutex_exit(&sdev_lock);
+		goto cleanup;				/* we're done */
+	}
+
+	/*
+	 * Create and initialize the vfs-private data.
+	 */
+	devdev = makedevice(devmajor, devminor);
+	while (vfs_devismounted(devdev)) {
+		devminor = (devminor + 1) & MAXMIN32;
+
+		/*
+		 * All the minor numbers are used up.
+		 */
+		if (devminor == 0) {
+			mutex_exit(&sdev_lock);
+			VN_RELE(avp);
+			error = ENODEV;
+			goto cleanup;
+		}
+
+		devdev = makedevice(devmajor, devminor);
+	}
+
+	dv = sdev_mkroot(vfsp, devdev, mvp, avp, cr);
+	sdev_data = kmem_zalloc(sizeof (struct sdev_data), KM_SLEEP);
+	vfsp->vfs_dev = devdev;
+	vfsp->vfs_data = (caddr_t)sdev_data;
+	vfsp->vfs_fstype = devtype;
+	vfsp->vfs_bsize = DEV_BSIZE;
+	vfsp->vfs_mtime = ddi_get_time();
+	vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, devtype);
+
+	ASSERT(dv == dv->sdev_dotdot);
+
+	sdev_data->sdev_vfsp = vfsp;
+	sdev_data->sdev_root = dv;
+	sdev_data->sdev_mountargs = args;
+
+	/* get acl flavor from attribute dir */
+	if (VOP_PATHCONF(avp, _PC_ACL_ENABLED, &sdev_data->sdev_acl_flavor,
+	    kcred) != 0 || sdev_data->sdev_acl_flavor == 0)
+		sdev_data->sdev_acl_flavor = _ACL_ACLENT_ENABLED;
+
+	args = NULL;			/* so it won't be freed below */
+	sdev_insert_mntinfo(sdev_data);
+	mutex_exit(&sdev_lock);
+
+	if (!SDEV_IS_GLOBAL(dv)) {
+		ASSERT(sdev_origins);
+		dv->sdev_flags &= ~SDEV_GLOBAL;
+		dv->sdev_origin = sdev_origins->sdev_root;
+	} else {
+		sdev_ncache_setup();
+	}
+
+	sdev_update_timestamps(dv->sdev_attrvp,
+		cr, AT_CTIME|AT_MTIME|AT_ATIME);
+
+cleanup:
+	if (args)
+		kmem_free(args, sizeof (*args));
+	return (error);
+}
+
+/*
+ * unmounting the non-global /dev instances, e.g. when deleting a Kevlar zone.
+ */
+static int
+sdev_unmount(struct vfs *vfsp, int flag, struct cred *cr)
+{
+	struct sdev_node *dv;
+	int error;
+	struct sdev_data *sdev_data, *prev, *next;
+
+	/*
+	 * enforce the security policies
+	 */
+	if ((secpolicy_fs_unmount(cr, vfsp) != 0) ||
+	    (secpolicy_sys_devices(cr) != 0))
+		return (EPERM);
+
+	if (flag & MS_FORCE)
+		return (ENOTSUP);
+
+	mutex_enter(&sdev_lock);
+	dv = VFSTOSDEVFS(vfsp)->sdev_root;
+	ASSERT(dv == dv->sdev_dotdot);
+	if (SDEVTOV(dv)->v_count > 1) {
+		mutex_exit(&sdev_lock);
+		return (EBUSY);
+	}
+
+	/*
+	 * global instance remains mounted
+	 */
+	if (SDEV_IS_GLOBAL(dv)) {
+		mutex_exit(&sdev_lock);
+		return (EBUSY);
+	}
+	mutex_exit(&sdev_lock);
+
+	/* verify the v_count */
+	if ((error = sdev_cleandir(dv, NULL, 0)) != 0) {
+		return (error);
+	}
+	ASSERT(SDEVTOV(dv)->v_count == 1);
+
+	/* release hold on root node and destroy it */
+	SDEV_RELE(dv);
+	dv->sdev_nlink -= 2;
+	sdev_nodedestroy(dv, 0);
+
+	sdev_data = (struct sdev_data *)vfsp->vfs_data;
+	vfsp->vfs_data = (caddr_t)0;
+
+	/*
+	 * XXX separate it into sdev_delete_mntinfo() if useful
+	 */
+	mutex_enter(&sdev_lock);
+	prev = sdev_data->sdev_prev;
+	next = sdev_data->sdev_next;
+	if (prev)
+		prev->sdev_next = next;
+	else
+		sdev_mntinfo = next;
+	if (next)
+		next->sdev_prev = prev;
+	mutex_exit(&sdev_lock);
+
+	if (sdev_data->sdev_mountargs) {
+		kmem_free(sdev_data->sdev_mountargs,
+		    sizeof (struct sdev_mountargs));
+	}
+	kmem_free(sdev_data, sizeof (struct sdev_data));
+	return (0);
+}
+
+/*
+ * return root vnode for given vfs
+ */
+static int
+sdev_root(struct vfs *vfsp, struct vnode **vpp)
+{
+	*vpp = SDEVTOV(VFSTOSDEVFS(vfsp)->sdev_root);
+	VN_HOLD(*vpp);
+	return (0);
+}
+
+/*
+ * return 'generic superblock' information to userland.
+ *
+ * not much that we can usefully admit to here
+ */
+static int
+sdev_statvfs(struct vfs *vfsp, struct statvfs64 *sbp)
+{
+	dev32_t d32;
+
+	bzero(sbp, sizeof (*sbp));
+	sbp->f_frsize = sbp->f_bsize = vfsp->vfs_bsize;
+	sbp->f_files = kmem_cache_stat(sdev_node_cache, "alloc");
+
+	/* no illusions that free/avail files is relevant to dev */
+	sbp->f_ffree = 0;
+	sbp->f_favail = 0;
+
+	/* no illusions that blocks are relevant to devfs */
+	sbp->f_bfree = 0;
+	sbp->f_bavail = 0;
+	sbp->f_blocks = 0;
+
+	(void) cmpldev(&d32, vfsp->vfs_dev);
+	sbp->f_fsid = d32;
+	(void) strcpy(sbp->f_basetype, vfssw[devtype].vsw_name);
+	sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
+	sbp->f_namemax = MAXNAMELEN - 1;
+	(void) strcpy(sbp->f_fstr, "dev");
+
+	return (0);
+}
+
+int
+sdev_module_register(char *mod_name, struct devname_ops *dev_ops)
+{
+	struct devname_nsmap *map = NULL;
+
+	if (strcmp(mod_name, DEVNAME_NSCONFIG) == 0) {
+		devname_ns_ops = dev_ops;
+		return (0);
+	}
+
+	map = sdev_get_nsmap_by_module(mod_name);
+	if (map == NULL)
+		return (EFAULT);
+
+	rw_enter(&map->dir_lock, RW_WRITER);
+	map->dir_ops = dev_ops;
+	rw_exit(&map->dir_lock);
+	return (0);
+}
+
+static void
+sdev_insert_mntinfo(struct sdev_data *data)
+{
+	ASSERT(mutex_owned(&sdev_lock));
+	data->sdev_next = sdev_mntinfo;
+	data->sdev_prev = NULL;
+	if (sdev_mntinfo) {
+		sdev_mntinfo->sdev_prev = data;
+	} else {
+		sdev_origins = data;
+	}
+	sdev_mntinfo = data;
+}
+
+struct sdev_data *
+sdev_find_mntinfo(char *mntpt)
+{
+	struct sdev_data *mntinfo;
+
+	mutex_enter(&sdev_lock);
+	mntinfo = sdev_mntinfo;
+	while (mntinfo) {
+		if (strcmp(mntpt, mntinfo->sdev_root->sdev_name) == 0) {
+			SDEVTOV(mntinfo->sdev_root)->v_count++;
+			break;
+		}
+		mntinfo = mntinfo->sdev_next;
+	}
+	mutex_exit(&sdev_lock);
+	return (mntinfo);
+}
+
+void
+sdev_mntinfo_rele(struct sdev_data *mntinfo)
+{
+	mutex_enter(&sdev_lock);
+	SDEVTOV(mntinfo->sdev_root)->v_count--;
+	mutex_exit(&sdev_lock);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/fs/dev/sdev_vnops.c	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,1329 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * vnode ops for the /dev filesystem
+ *
+ * - VDIR, VCHR, CBLK, and VLNK are considered must supported files
+ * - VREG and VDOOR are used for some internal implementations in
+ *    the global zone, e.g. devname and devfsadm communication
+ * - other file types are unusual in this namespace and
+ *    not supported for now
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/t_lock.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/user.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/flock.h>
+#include <sys/kmem.h>
+#include <sys/uio.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/cred.h>
+#include <sys/cred_impl.h>
+#include <sys/dirent.h>
+#include <sys/pathname.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/policy.h>
+#include <vm/hat.h>
+#include <vm/seg_vn.h>
+#include <vm/seg_map.h>
+#include <vm/seg.h>
+#include <vm/as.h>
+#include <vm/page.h>
+#include <sys/proc.h>
+#include <sys/mode.h>
+#include <sys/sunndi.h>
+#include <sys/ptms.h>
+#include <fs/fs_subr.h>
+#include <sys/fs/dv_node.h>
+#include <sys/fs/sdev_impl.h>
+#include <sys/fs/sdev_node.h>
+
+/*ARGSUSED*/
+static int
+sdev_open(struct vnode **vpp, int flag, struct cred *cred)
+{
+	struct sdev_node *dv = VTOSDEV(*vpp);
+	struct sdev_node *ddv = dv->sdev_dotdot;
+	int error = 0;
+
+	if ((*vpp)->v_type == VDIR)
+		return (0);
+
+	if (!SDEV_IS_GLOBAL(dv))
+		return (ENOTSUP);
+
+	ASSERT((*vpp)->v_type == VREG);
+	if ((*vpp)->v_type != VREG)
+		return (ENOTSUP);
+
+	ASSERT(ddv);
+	rw_enter(&ddv->sdev_contents, RW_READER);
+	if (dv->sdev_attrvp == NULL) {
+		rw_exit(&ddv->sdev_contents);
+		return (ENOENT);
+	}
+	error = VOP_OPEN(&(dv->sdev_attrvp), flag, cred);
+	rw_exit(&ddv->sdev_contents);
+	return (error);
+}
+
+/*ARGSUSED1*/
+static int
+sdev_close(struct vnode *vp, int flag, int count,
+    offset_t offset, struct cred *cred)
+{
+	struct sdev_node *dv = VTOSDEV(vp);
+
+	if (vp->v_type == VDIR) {
+		cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
+		cleanshares(vp, ttoproc(curthread)->p_pid);
+		return (0);
+	}
+
+	if (!SDEV_IS_GLOBAL(dv))
+		return (ENOTSUP);
+
+	ASSERT(vp->v_type == VREG);
+	if (vp->v_type != VREG)
+		return (ENOTSUP);
+
+	ASSERT(dv->sdev_attrvp);
+	return (VOP_CLOSE(dv->sdev_attrvp, flag, count, offset, cred));
+}
+
+/*ARGSUSED*/
+static int
+sdev_read(struct vnode *vp, struct uio *uio, int ioflag, struct cred *cred,
+	struct caller_context *ct)
+{
+	struct sdev_node *dv = (struct sdev_node *)VTOSDEV(vp);
+	int	error;
+
+	if (!SDEV_IS_GLOBAL(dv))
+		return (EINVAL);
+
+	if (vp->v_type == VDIR)
+		return (EISDIR);
+
+	/* only supporting regular files in /dev */
+	ASSERT(vp->v_type == VREG);
+	if (vp->v_type != VREG)
+		return (EINVAL);
+
+	ASSERT(RW_READ_HELD(&VTOSDEV(vp)->sdev_contents));
+	ASSERT(dv->sdev_attrvp);
+	(void) VOP_RWLOCK(dv->sdev_attrvp, 0, NULL);
+	error = VOP_READ(dv->sdev_attrvp, uio, ioflag, cred, ct);
+	VOP_RWUNLOCK(dv->sdev_attrvp, 0, NULL);
+	return (error);
+}
+
+/*ARGSUSED*/
+static int
+sdev_write(struct vnode *vp, struct uio *uio, int ioflag, struct cred *cred,
+	struct caller_context *ct)
+{
+	struct sdev_node *dv = VTOSDEV(vp);
+	int	error = 0;
+
+	if (!SDEV_IS_GLOBAL(dv))
+		return (EINVAL);
+
+	if (vp->v_type == VDIR)
+		return (EISDIR);
+
+	/* only supporting regular files in /dev */
+	ASSERT(vp->v_type == VREG);
+	if (vp->v_type != VREG)
+		return (EINVAL);
+
+	ASSERT(dv->sdev_attrvp);
+
+	(void) VOP_RWLOCK(dv->sdev_attrvp, 1, NULL);
+	error = VOP_WRITE(dv->sdev_attrvp, uio, ioflag, cred, ct);
+	VOP_RWUNLOCK(dv->sdev_attrvp, 1, NULL);
+	if (error == 0) {
+		sdev_update_timestamps(dv->sdev_attrvp, kcred,
+		    AT_MTIME);
+	}
+	return (error);
+}
+
+/*ARGSUSED*/
+static int
+sdev_ioctl(struct vnode *vp, int cmd, intptr_t arg, int flag,
+    struct cred *cred, int *rvalp)
+{
+	struct sdev_node *dv = VTOSDEV(vp);
+
+	if (!SDEV_IS_GLOBAL(dv) || (vp->v_type == VDIR))
+		return (ENOTTY);
+
+	ASSERT(vp->v_type == VREG);
+	if (vp->v_type != VREG)
+		return (EINVAL);
+
+	ASSERT(dv->sdev_attrvp);
+	return (VOP_IOCTL(dv->sdev_attrvp, cmd, arg, flag, cred, rvalp));
+}
+
+static int
+sdev_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr)
+{
+	int			error = 0;
+	struct sdev_node	*dv = VTOSDEV(vp);
+	struct sdev_node	*parent = dv->sdev_dotdot;
+	struct devname_nsmap *map = NULL;
+	struct devname_ops	*dirops = NULL;
+	int (*fn)(devname_handle_t *, struct vattr *, struct cred *);
+
+	ASSERT(parent);
+
+	rw_enter(&parent->sdev_contents, RW_READER);
+	ASSERT(dv->sdev_attr || dv->sdev_attrvp);
+	if (SDEV_IS_GLOBAL(dv) && (dv->sdev_state != SDEV_ZOMBIE)) {
+		map = sdev_get_map(parent, 0);
+		dirops = map ? map->dir_ops : NULL;
+	}
+
+	/*
+	 * search order:
+	 * 	- for persistent nodes (SDEV_PERSIST): backstore
+	 *	- for non-persistent nodes: module ops if global, then memory
+	 */
+	if (dv->sdev_attrvp) {
+		rw_exit(&parent->sdev_contents);
+		error = VOP_GETATTR(dv->sdev_attrvp, vap, flags, cr);
+		sdev_vattr_merge(dv, vap);
+	} else if (dirops && (fn = dirops->devnops_getattr)) {
+		sdev_vattr_merge(dv, vap);
+		rw_exit(&parent->sdev_contents);
+		error = (*fn)(&(dv->sdev_handle), vap, cr);
+	} else {
+		ASSERT(dv->sdev_attr);
+		*vap = *dv->sdev_attr;
+		sdev_vattr_merge(dv, vap);
+		rw_exit(&parent->sdev_contents);
+	}
+
+	return (error);
+}
+
+static int
+sdev_setattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cred)
+{
+	return (devname_setattr_func(vp, vap, flags, cred, NULL, 0));
+}
+
+static int
+sdev_getsecattr(struct vnode *vp, struct vsecattr *vsap, int flags,
+    struct cred *cr)
+{
+	int	error;
+	struct sdev_node *dv = VTOSDEV(vp);
+	struct vnode *avp = dv->sdev_attrvp;
+
+	if (avp == NULL) {
+		/* return fs_fab_acl() if flavor matches, else do nothing */
+		if ((SDEV_ACL_FLAVOR(vp) == _ACL_ACLENT_ENABLED &&
+		    (vsap->vsa_mask & (VSA_ACLCNT | VSA_DFACLCNT))) ||
+		    (SDEV_ACL_FLAVOR(vp) == _ACL_ACE_ENABLED &&
+		    (vsap->vsa_mask & (VSA_ACECNT | VSA_ACE))))
+			return (fs_fab_acl(vp, vsap, flags, cr));
+
+		return (ENOSYS);
+	}
+
+	(void) VOP_RWLOCK(avp, 1, NULL);
+	error = VOP_GETSECATTR(avp, vsap, flags, cr);
+	VOP_RWUNLOCK(avp, 1, NULL);
+	return (error);
+}
+
+static int
+sdev_setsecattr(struct vnode *vp, struct vsecattr *vsap, int flags,
+    struct cred *cr)
+{
+	int	error;
+	struct sdev_node *dv = VTOSDEV(vp);
+	struct vnode *avp = dv->sdev_attrvp;
+
+	if (dv->sdev_state == SDEV_ZOMBIE)
+		return (0);
+
+	if (avp == NULL) {
+		if (SDEV_IS_GLOBAL(dv) && !SDEV_IS_PERSIST(dv))
+			return (fs_nosys());
+
+		ASSERT(dv->sdev_attr);
+		/*
+		 * if coming in directly, the acl system call will
+		 * have held the read-write lock via VOP_RWLOCK()
+		 * If coming in via specfs, specfs will have
+		 * held the rw lock on the realvp i.e. us.
+		 */
+		ASSERT(RW_WRITE_HELD(&dv->sdev_contents));
+		sdev_vattr_merge(dv, dv->sdev_attr);
+		error =  sdev_shadow_node(dv, cr);
+		if (error) {
+			return (fs_nosys());
+		}
+
+		ASSERT(dv->sdev_attrvp);
+		/* clean out the memory copy if any */
+		if (dv->sdev_attr) {
+			kmem_free(dv->sdev_attr, sizeof (struct vattr));
+			dv->sdev_attr = NULL;
+		}
+		avp = dv->sdev_attrvp;
+	}
+	ASSERT(avp);
+
+	(void) VOP_RWLOCK(avp, V_WRITELOCK_TRUE, NULL);
+	error = VOP_SETSECATTR(avp, vsap, flags, cr);
+	VOP_RWUNLOCK(avp, V_WRITELOCK_TRUE, NULL);
+	return (error);
+}
+
+int
+sdev_unlocked_access(void *vdv, int mode, struct cred *cr)
+{
+	struct sdev_node	*dv = vdv;
+	int			shift = 0;
+	uid_t			owner = dv->sdev_attr->va_uid;
+
+	if (crgetuid(cr) != owner) {
+		shift += 3;
+		if (groupmember(dv->sdev_attr->va_gid, cr) == 0)
+			shift += 3;
+	}
+
+	mode &= ~(dv->sdev_attr->va_mode << shift);
+	if (mode == 0)
+		return (0);
+
+	return (secpolicy_vnode_access(cr, SDEVTOV(dv), owner, mode));
+}
+
+static int
+sdev_access(struct vnode *vp, int mode, int flags, struct cred *cr)
+{
+	struct sdev_node	*dv = VTOSDEV(vp);
+	int ret = 0;
+
+	ASSERT(dv->sdev_attr || dv->sdev_attrvp);
+
+	if (dv->sdev_attrvp) {
+		ret = VOP_ACCESS(dv->sdev_attrvp, mode, flags, cr);
+	} else if (dv->sdev_attr) {
+		rw_enter(&dv->sdev_contents, RW_READER);
+		ret = sdev_unlocked_access(dv, mode, cr);
+		if (ret)
+			ret = EACCES;
+		rw_exit(&dv->sdev_contents);
+	}
+
+	return (ret);
+}
+
+/*
+ * Lookup
+ */
+/*ARGSUSED3*/
+static int
+sdev_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
+    struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred)
+{
+	struct sdev_node	*parent;
+
+	parent = VTOSDEV(dvp);
+	ASSERT(parent);
+
+	if (!SDEV_IS_GLOBAL(parent))
+		return (prof_lookup(dvp, nm, vpp, cred));
+	return (devname_lookup_func(parent, nm, vpp, cred, NULL, 0));
+}
+
+/*ARGSUSED2*/
+static int
+sdev_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
+    int mode, struct vnode **vpp, struct cred *cred, int flag)
+{
+	struct vnode		*vp = NULL;
+	struct vnode		*avp;
+	struct sdev_node	*parent;
+	struct sdev_node	*self = NULL;
+	int			error = 0;
+	vtype_t			type = vap->va_type;
+
+	ASSERT(vap->va_type != VNON &&
+	    vap->va_type != VBAD);
+
+	if ((type == VFIFO) || (type == VSOCK) ||
+	    (type == VPROC) || (type == VPORT))
+		return (ENOTSUP);
+
+	parent = VTOSDEV(dvp);
+	ASSERT(parent);
+
+	rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER);
+	if (parent->sdev_state == SDEV_ZOMBIE) {
+		rw_exit(&parent->sdev_dotdot->sdev_contents);
+		return (ENOENT);
+	}
+
+	/* non-global do not allow pure node creation */
+	if (!SDEV_IS_GLOBAL(parent)) {
+		rw_exit(&parent->sdev_dotdot->sdev_contents);
+		return (prof_lookup(dvp, nm, vpp, cred));
+	}
+	rw_exit(&parent->sdev_dotdot->sdev_contents);
+
+again:
+	/* check existing name */
+	error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred);
+
+	/* name found */
+	if (error == 0) {
+		ASSERT(vp);
+		if (excl == EXCL) {
+			error = EEXIST;
+		} else if ((vp->v_type == VDIR) && (mode & VWRITE)) {
+			/* allowing create/read-only an existing directory */
+			error = EISDIR;
+		} else {
+			error = VOP_ACCESS(vp, mode, flag, cred);
+		}
+
+		if (error) {
+			VN_RELE(vp);
+			return (error);
+		}
+
+		/* truncation first */
+		if ((vp->v_type == VREG) && (vap->va_mask & AT_SIZE) &&
+		    (vap->va_size == 0)) {
+			ASSERT(parent->sdev_attrvp);
+			ASSERT(VTOSDEV(vp)->sdev_attrvp);
+			error = VOP_CREATE(parent->sdev_attrvp,
+			    nm, vap, excl, mode, &avp, cred, flag);
+
+			if (error) {
+				VN_RELE(vp);
+				return (error);
+			}
+		}
+
+		sdev_update_timestamps(vp, kcred,
+		    AT_CTIME|AT_MTIME|AT_ATIME);
+		*vpp = vp;
+		return (0);
+	}
+
+	/* bail out early */
+	if (error != ENOENT)
+		return (error);
+
+	/*
+	 * For memory-based (ROFS) directory:
+	 * 	- either disallow node creation;
+	 *	- or implement VOP_CREATE of its own
+	 */
+	rw_enter(&parent->sdev_contents, RW_WRITER);
+	if (!SDEV_IS_PERSIST(parent)) {
+		rw_exit(&parent->sdev_contents);
+		return (ENOTSUP);
+	}
+	ASSERT(parent->sdev_attrvp);
+	error = sdev_mknode(parent, nm, &self, vap, NULL, NULL,
+	    cred, SDEV_READY);
+	if (error) {
+		rw_exit(&parent->sdev_contents);
+		if (self)
+			SDEV_RELE(self);
+		return (error);
+	}
+	rw_exit(&parent->sdev_contents);
+
+	ASSERT(self);
+	/* take care the timestamps for the node and its parent */
+	sdev_update_timestamps(SDEVTOV(self), kcred,
+	    AT_CTIME|AT_MTIME|AT_ATIME);
+	sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME);
+	if (SDEV_IS_GLOBAL(parent))
+		atomic_inc_ulong(&parent->sdev_gdir_gen);
+
+	/* wake up other threads blocked on looking up this node */
+	mutex_enter(&self->sdev_lookup_lock);
+	SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP);
+	mutex_exit(&self->sdev_lookup_lock);
+	error = sdev_to_vp(self, vpp);
+	return (error);
+}
+
+static int
+sdev_remove(struct vnode *dvp, char *nm, struct cred *cred)
+{
+	int	error;
+	struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp);
+	struct vnode *vp = NULL;
+	struct sdev_node *dv = NULL;
+	struct devname_nsmap *map = NULL;
+	struct devname_ops *dirops = NULL;
+	int (*fn)(devname_handle_t *);
+	int len;
+	int bkstore = 0;
+
+	/* bail out early */
+	len = strlen(nm);
+	if (nm[0] == '.') {
+		if (len == 1) {
+			return (EINVAL);
+		} else if (len == 2 && nm[1] == '.') {
+			return (EEXIST);
+		}
+	}
+
+	ASSERT(parent);
+	rw_enter(&parent->sdev_contents, RW_READER);
+	if (!SDEV_IS_GLOBAL(parent)) {
+		rw_exit(&parent->sdev_contents);
+		return (ENOTSUP);
+	}
+
+	/* check existence first */
+	dv = sdev_cache_lookup(parent, nm);
+	if (dv == NULL) {
+		rw_exit(&parent->sdev_contents);
+		return (ENOENT);
+	}
+
+	vp = SDEVTOV(dv);
+	if ((dv->sdev_state == SDEV_INIT) ||
+	    (dv->sdev_state == SDEV_ZOMBIE)) {
+		rw_exit(&parent->sdev_contents);
+		VN_RELE(vp);
+		return (ENOENT);
+	}
+
+	/* the module may record/reject removing a device node */
+	map = sdev_get_map(parent, 0);
+	dirops = map ? map->dir_ops : NULL;
+	if (dirops && ((fn = dirops->devnops_remove) != NULL)) {
+		error = (*fn)(&(dv->sdev_handle));
+		if (error) {
+			rw_exit(&parent->sdev_contents);
+			VN_RELE(vp);
+			return (error);
+		}
+	}
+
+	/*
+	 * sdev_dirdelete does the real job of:
+	 *  - make sure no open ref count
+	 *  - destroying the sdev_node
+	 *  - releasing the hold on attrvp
+	 */
+	bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0;
+	if (!rw_tryupgrade(&parent->sdev_contents)) {
+		rw_exit(&parent->sdev_contents);
+		rw_enter(&parent->sdev_contents, RW_WRITER);
+	}
+	error = sdev_cache_update(parent, &dv, nm, SDEV_CACHE_DELETE);
+	rw_exit(&parent->sdev_contents);
+
+	sdcmn_err2(("sdev_remove: cache_update error %d\n", error));
+	if (error && (error != EBUSY)) {
+		/* report errors other than EBUSY */
+		VN_RELE(vp);
+	} else {
+		sdcmn_err2(("sdev_remove: cleaning node %s from cache "
+		    " with error %d\n", nm, error));
+
+		/*
+		 * best efforts clean up the backing store
+		 */
+		if (bkstore) {
+			ASSERT(parent->sdev_attrvp);
+			error = VOP_REMOVE(parent->sdev_attrvp, nm, cred);
+			/*
+			 * do not report BUSY error
+			 * because the backing store ref count is released
+			 * when the last ref count on the sdev_node is
+			 * released.
+			 */
+			if (error == EBUSY) {
+				sdcmn_err2(("sdev_remove: device %s is still on"
+				    "disk %s\n", nm, parent->sdev_path));
+				error = 0;
+			}
+		}
+
+		if (error == EBUSY)
+			error = 0;
+	}
+
+	return (error);
+}
+
+/*
+ * Some restrictions for this file system:
+ *  - both oldnm and newnm are in the scope of /dev file system,
+ *    to simply the namespace management model.
+ */
+static int
+sdev_rename(struct vnode *odvp, char *onm, struct vnode *ndvp, char *nnm,
+    struct cred *cred)
+{
+	struct sdev_node	*fromparent = NULL;
+	struct vattr		vattr;
+	struct sdev_node	*toparent;
+	struct sdev_node	*fromdv = NULL;	/* source node */
+	struct vnode 		*ovp;	/* source vnode */
+	struct sdev_node	*todv = NULL;	/* destination node */
+	struct vnode 		*nvp;		/* destination vnode */
+	int			samedir = 0;	/* set if odvp == ndvp */
+	struct vnode		*realvp;
+	int			len;
+	char			nnm_path[MAXPATHLEN];
+	struct devname_nsmap 	*omap = NULL;
+	struct devname_ops	*odirops = NULL;
+	int (*fn)(devname_handle_t *, char *);
+	int (*rmfn)(devname_handle_t *);
+	int error = 0;
+	dev_t fsid;
+	int bkstore = 0;
+
+	/* prevent modifying "." and ".." */
+	if ((onm[0] == '.' &&
+	    (onm[1] == '\0' || (onm[1] == '.' && onm[2] == '\0')))) {
+		return (EINVAL);
+	}
+
+	fromparent = VTOSDEV(odvp);
+	toparent = VTOSDEV(ndvp);
+
+	/* ZOMBIE parent doesn't allow new node creation */
+	rw_enter(&fromparent->sdev_dotdot->sdev_contents, RW_READER);
+	if (fromparent->sdev_state == SDEV_ZOMBIE) {
+		rw_exit(&fromparent->sdev_dotdot->sdev_contents);
+		return (ENOENT);
+	}
+
+	/* renaming only supported for global device nodes */
+	if (!SDEV_IS_GLOBAL(fromparent)) {
+		rw_exit(&fromparent->sdev_dotdot->sdev_contents);
+		return (ENOTSUP);
+	}
+	rw_exit(&fromparent->sdev_dotdot->sdev_contents);
+
+	rw_enter(&toparent->sdev_dotdot->sdev_contents, RW_READER);
+	if (toparent->sdev_state == SDEV_ZOMBIE) {
+		rw_exit(&toparent->sdev_dotdot->sdev_contents);
+		return (ENOENT);
+	}
+	rw_exit(&toparent->sdev_dotdot->sdev_contents);
+
+	/* check existence of the source node */
+	error = VOP_LOOKUP(odvp, onm, &ovp, NULL, 0, NULL, cred);
+	if (error) {
+		sdcmn_err2(("sdev_rename: the source node %s exists\n",
+		    onm));
+		return (error);
+	}
+
+	if (VOP_REALVP(ovp, &realvp) == 0) {
+		VN_HOLD(realvp);
+		VN_RELE(ovp);
+		ovp = realvp;
+	}
+
+	/* check existence of destination */
+	error = VOP_LOOKUP(ndvp, nnm, &nvp, NULL, 0, NULL, cred);
+	if (error && (error != ENOENT)) {
+		VN_RELE(ovp);
+		return (error);
+	}
+
+	if (nvp && (VOP_REALVP(nvp, &realvp) == 0)) {
+		VN_HOLD(realvp);
+		VN_RELE(nvp);
+		nvp = realvp;
+	}
+
+	/*
+	 * For now, if both exist, they must be the same type.
+	 * Changing the type of a node probably needs some special
+	 * handling.
+	 */
+	if (ovp && nvp) {
+		if (ovp->v_type != nvp->v_type) {
+			VN_RELE(ovp);
+			VN_RELE(nvp);
+			return (EINVAL);
+		}
+	}
+
+	/* make sure the source and the destination are in /dev */
+	if (odvp != ndvp) {
+		vattr.va_mask = AT_FSID;
+		if (error = VOP_GETATTR(odvp, &vattr, 0, cred)) {
+			VN_RELE(ovp);
+			return (error);
+		}
+		fsid = vattr.va_fsid;
+		vattr.va_mask = AT_FSID;
+		if (error = VOP_GETATTR(ndvp, &vattr, 0, cred)) {
+			VN_RELE(ovp);
+			return (error);
+		}
+		if (fsid != vattr.va_fsid) {
+			VN_RELE(ovp);
+			return (EXDEV);
+		}
+	}
+
+	/* make sure the old entry can be deleted */
+	error = VOP_ACCESS(odvp, VWRITE, 0, cred);
+	if (error) {
+		VN_RELE(ovp);
+		return (error);
+	}
+
+	/* make sure the destination allows creation */
+	samedir = (fromparent == toparent);
+	if (!samedir) {
+		error = VOP_ACCESS(ndvp, VEXEC|VWRITE, 0, cred);
+		if (error) {
+			VN_RELE(ovp);
+			return (error);
+		}
+	}
+
+	fromdv = VTOSDEV(ovp);
+	ASSERT(fromdv);
+
+	/* check with the plug-in modules for the source directory */
+	rw_enter(&fromparent->sdev_contents, RW_READER);
+	omap = sdev_get_map(fromparent, 0);
+	rw_exit(&fromparent->sdev_contents);
+	odirops = omap ? omap->dir_ops : NULL;
+	if (odirops && ((fn = odirops->devnops_rename) != NULL)) {
+		if (samedir) {
+			error = (*fn)(&(fromdv->sdev_handle), nnm);
+		} else {
+			len = strlen(nnm) + strlen(toparent->sdev_name) + 2;
+			(void) snprintf(nnm_path, len, "%s/%s",
+			    toparent->sdev_name, nnm);
+			error = (*fn)(&(fromdv->sdev_handle), nnm);
+		}
+
+		if (error) {
+			VN_RELE(ovp);
+			return (error);
+		}
+	}
+
+	/*
+	 * Remove the destination if exist
+	 * On failure, we should attempt to restore the current state
+	 * before returning error.
+	 */
+	if (nvp) {
+		switch (nvp->v_type) {
+		case VDIR:
+			error = VOP_RMDIR(ndvp, nnm, ndvp, cred);
+			break;
+		default:
+			error = VOP_REMOVE(ndvp, nnm, cred);
+			break;
+		}
+
+		if (error) {
+			sdcmn_err2(("sdev_rename: removing existing destination"
+			    " %s failed, error %d\n", nnm, error));
+			VN_RELE(ovp);
+			VN_RELE(nvp);
+			return (error);
+		}
+	}
+
+	/*
+	 * link source to new target in the memory
+	 */
+	error = VOP_LOOKUP(ndvp, nnm, &nvp, NULL, 0, NULL, cred);
+	if (error && (error != ENOENT)) {
+		VN_RELE(ovp);
+		return (error);
+	} else if (error == ENOENT) {
+		/* make a new node from the old node */
+		error = sdev_rnmnode(fromparent, fromdv, toparent, &todv,
+		    nnm, cred);
+	} else {
+		ASSERT(nvp);
+		if (VOP_REALVP(nvp, &realvp) == 0) {
+			VN_HOLD(realvp);
+			VN_RELE(nvp);
+			nvp = realvp;
+		}
+
+		/* destination file exists */
+		todv = VTOSDEV(nvp);
+		ASSERT(todv);
+		error = sdev_rnmnode(fromparent, fromdv, toparent, &todv,
+		    nnm, cred);
+		if (error) {
+			sdcmn_err2(("sdev_rename: renaming %s to %s failed "
+			    " with existing destination error %d\n",
+			    onm, nnm, error));
+			VN_RELE(nvp);
+			VN_RELE(ovp);
+			return (error);
+		}
+	}
+
+	/* unlink from source */
+	if (error == 0) {
+		/*
+		 * check with the plug-in module whether source can be
+		 * re-used or not
+		 */
+		if (odirops && ((rmfn = odirops->devnops_remove) != NULL)) {
+			error = (*rmfn)(&(fromdv->sdev_handle));
+		}
+
+		if (error == 0) {
+			bkstore = SDEV_IS_PERSIST(fromdv) ? 1 : 0;
+			rw_enter(&fromparent->sdev_contents, RW_WRITER);
+			error = sdev_cache_update(fromparent, &fromdv, onm,
+			    SDEV_CACHE_DELETE);
+			rw_exit(&fromparent->sdev_contents);
+
+			/* best effforts clean up the backing store */
+			if (bkstore) {
+				ASSERT(fromparent->sdev_attrvp);
+				error = VOP_REMOVE(fromparent->sdev_attrvp,
+				    onm, kcred);
+				if (error) {
+					sdcmn_err2(("sdev_rename: device %s is "
+					    "still on disk %s\n", onm,
+					    fromparent->sdev_path));
+					error = 0;
+				}
+			}
+
+			if (error == EBUSY) {
+				error = 0;
+			}
+		}
+	}
+
+	/* book keeping the ovp v_count */
+	if (error) {
+		sdcmn_err2(("sdev_rename: renaming %s to %s failed "
+		    " with error %d\n", onm, nnm, error));
+		VN_RELE(ovp);
+	}
+
+	return (error);
+}
+
+/*
+ * dev-fs version of "ln -s path dev-name"
+ *	tnm - path, e.g. /devices/... or /dev/...
+ *	lnm - dev_name
+ */
+static int
+sdev_symlink(struct vnode *dvp, char *lnm, struct vattr *tva,
+    char *tnm, struct cred *cred)
+{
+	int error;
+	struct vnode *vp = NULL;
+	struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp);
+	struct sdev_node *self = (struct sdev_node *)NULL;
+
+	ASSERT(parent);
+	rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER);
+	if (parent->sdev_state == SDEV_ZOMBIE) {
+		rw_exit(&parent->sdev_dotdot->sdev_contents);
+		sdcmn_err2(("sdev_symlink: parent %s is ZOMBIED \n",
+		    parent->sdev_name));
+		return (ENOENT);
+	}
+
+	if (!SDEV_IS_GLOBAL(parent)) {
+		rw_exit(&parent->sdev_dotdot->sdev_contents);
+		return (ENOTSUP);
+	}
+	rw_exit(&parent->sdev_dotdot->sdev_contents);
+
+	/* find existing name */
+	error = VOP_LOOKUP(dvp, lnm, &vp, NULL, 0, NULL, cred);
+	if (error == 0) {
+		ASSERT(vp);
+		VN_RELE(vp);
+		sdcmn_err2(("sdev_symlink: node %s already exists\n", lnm));
+		return (EEXIST);
+	}
+
+	if (error != ENOENT) {
+		return (error);
+	}
+
+	/* put it into memory cache */
+	rw_enter(&parent->sdev_contents, RW_WRITER);
+	error = sdev_mknode(parent, lnm, &self, tva, NULL, (void *)tnm,
+	    cred, SDEV_READY);
+	if (error) {
+		rw_exit(&parent->sdev_contents);
+		sdcmn_err2(("sdev_symlink: node %s creation failed\n", lnm));
+		if (self)
+			SDEV_RELE(self);
+
+		return (error);
+	}
+	ASSERT(self && (self->sdev_state == SDEV_READY));
+	rw_exit(&parent->sdev_contents);
+
+	/* take care the timestamps for the node and its parent */
+	sdev_update_timestamps(SDEVTOV(self), kcred,
+	    AT_CTIME|AT_MTIME|AT_ATIME);
+	sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME);
+	if (SDEV_IS_GLOBAL(parent))
+		atomic_inc_ulong(&parent->sdev_gdir_gen);
+
+	/* wake up other threads blocked on looking up this node */
+	mutex_enter(&self->sdev_lookup_lock);
+	SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP);
+	mutex_exit(&self->sdev_lookup_lock);
+	SDEV_RELE(self);	/* don't return with vnode held */
+	return (0);
+}
+
+static int
+sdev_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp,
+    struct cred *cred)
+{
+	int error;
+	struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp);
+	struct sdev_node *self = NULL;
+	struct vnode	*vp = NULL;
+
+	ASSERT(parent && parent->sdev_dotdot);
+	rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER);
+	if (parent->sdev_state == SDEV_ZOMBIE) {
+		rw_exit(&parent->sdev_dotdot->sdev_contents);
+		return (ENOENT);
+	}
+
+	/* non-global do not allow pure directory creation */
+	if (!SDEV_IS_GLOBAL(parent)) {
+		rw_exit(&parent->sdev_dotdot->sdev_contents);
+		return (prof_lookup(dvp, nm, vpp, cred));
+	}
+	rw_exit(&parent->sdev_dotdot->sdev_contents);
+
+	/* find existing name */
+	error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred);
+	if (error == 0) {
+		VN_RELE(vp);
+		return (EEXIST);
+	}
+
+	if (error != ENOENT)
+		return (error);
+
+	/* put it into memory */
+	rw_enter(&parent->sdev_contents, RW_WRITER);
+	error = sdev_mknode(parent, nm, &self,
+	    va, NULL, NULL, cred, SDEV_READY);
+	if (error) {
+		rw_exit(&parent->sdev_contents);
+		if (self)
+			SDEV_RELE(self);
+		return (error);
+	}
+	ASSERT(self && (self->sdev_state == SDEV_READY));
+	rw_exit(&parent->sdev_contents);
+
+	/* take care the timestamps for the node and its parent */
+	sdev_update_timestamps(SDEVTOV(self), kcred,
+	    AT_CTIME|AT_MTIME|AT_ATIME);
+	sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME);
+	if (SDEV_IS_GLOBAL(parent))
+		atomic_inc_ulong(&parent->sdev_gdir_gen);
+
+	/* wake up other threads blocked on looking up this node */
+	mutex_enter(&self->sdev_lookup_lock);
+	SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP);
+	mutex_exit(&self->sdev_lookup_lock);
+	*vpp = SDEVTOV(self);
+	return (0);
+}
+
+/*
+ * allowing removing an empty directory under /dev
+ */
+/*ARGSUSED*/
+static int
+sdev_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred)
+{
+	int error = 0;
+	struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp);
+	struct sdev_node *self = NULL;
+	struct vnode *vp = NULL;
+
+	/* bail out early */
+	if (strcmp(nm, ".") == 0)
+		return (EINVAL);
+	if (strcmp(nm, "..") == 0)
+		return (EEXIST); /* should be ENOTEMPTY */
+
+	/* no destruction of non-global node */
+	ASSERT(parent && parent->sdev_dotdot);
+	rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER);
+	if (!SDEV_IS_GLOBAL(parent)) {
+		rw_exit(&parent->sdev_dotdot->sdev_contents);
+		return (ENOTSUP);
+	}
+	rw_exit(&parent->sdev_dotdot->sdev_contents);
+
+	/* check existing name */
+	rw_enter(&parent->sdev_contents, RW_WRITER);
+	self = sdev_cache_lookup(parent, nm);
+	if (self == NULL) {
+		rw_exit(&parent->sdev_contents);
+		return (ENOENT);
+	}
+
+	vp = SDEVTOV(self);
+	if ((self->sdev_state == SDEV_INIT) ||
+	    (self->sdev_state == SDEV_ZOMBIE)) {
+		rw_exit(&parent->sdev_contents);
+		VN_RELE(vp);
+		return (ENOENT);
+	}
+
+	/* some sanity checks */
+	if (vp == dvp || vp == cdir) {
+		rw_exit(&parent->sdev_contents);
+		VN_RELE(vp);
+		return (EINVAL);
+	}
+
+	if (vp->v_type != VDIR) {
+		rw_exit(&parent->sdev_contents);
+		VN_RELE(vp);
+		return (ENOTDIR);
+	}
+
+	if (vn_vfswlock(vp)) {
+		rw_exit(&parent->sdev_contents);
+		VN_RELE(vp);
+		return (EBUSY);
+	}
+
+	if (vn_mountedvfs(vp) != NULL) {
+		rw_exit(&parent->sdev_contents);
+		vn_vfsunlock(vp);
+		VN_RELE(vp);
+		return (EBUSY);
+	}
+
+	self = VTOSDEV(vp);
+	/* bail out on a non-empty directory */
+	rw_enter(&self->sdev_contents, RW_READER);
+	if (self->sdev_nlink > 2) {
+		rw_exit(&self->sdev_contents);
+		rw_exit(&parent->sdev_contents);
+		vn_vfsunlock(vp);
+		VN_RELE(vp);
+		return (ENOTEMPTY);
+	}
+	rw_exit(&self->sdev_contents);
+
+	/* unlink it from the directory cache */
+	error = sdev_cache_update(parent, &self, nm, SDEV_CACHE_DELETE);
+	rw_exit(&parent->sdev_contents);
+	vn_vfsunlock(vp);
+
+	if (error && (error != EBUSY)) {
+		VN_RELE(vp);
+	} else {
+		sdcmn_err2(("sdev_rmdir: cleaning node %s from directory "
+		    " cache with error %d\n", nm, error));
+
+		/* best effort to clean up the backing store */
+		if (SDEV_IS_PERSIST(parent)) {
+			ASSERT(parent->sdev_attrvp);
+			error = VOP_RMDIR(parent->sdev_attrvp, nm,
+			    parent->sdev_attrvp, kcred);
+			sdcmn_err2(("sdev_rmdir: cleaning device %s is on"
+			    " disk error %d\n", parent->sdev_path, error));
+		}
+
+		if (error == EBUSY)
+			error = 0;
+	}
+
+	return (error);
+}
+
+/*
+ * read the contents of a symbolic link
+ */
+static int
+sdev_readlink(struct vnode *vp, struct uio *uiop, struct cred *cred)
+{
+	struct sdev_node *dv;
+	int	error = 0;
+
+	ASSERT(vp->v_type == VLNK);
+
+	dv = VTOSDEV(vp);
+
+	if (dv->sdev_attrvp) {
+		/* non-NULL attrvp implys a persisted node at READY state */
+		return (VOP_READLINK(dv->sdev_attrvp, uiop, cred));
+	} else if (dv->sdev_symlink != NULL) {
+		/* memory nodes, e.g. local nodes */
+		rw_enter(&dv->sdev_contents, RW_READER);
+		sdcmn_err2(("sdev_readlink link is %s\n", dv->sdev_symlink));
+		error = uiomove(dv->sdev_symlink, strlen(dv->sdev_symlink),
+		    UIO_READ, uiop);
+		rw_exit(&dv->sdev_contents);
+		return (error);
+	}
+
+	return (ENOENT);
+}
+
+static int
+sdev_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp)
+{
+	return (devname_readdir_func(dvp, uiop, cred, eofp, SDEV_BROWSE));
+}
+
+/*ARGSUSED1*/
+static void
+sdev_inactive(struct vnode *vp, struct cred *cred)
+{
+	int clean;
+	struct sdev_node *dv = VTOSDEV(vp);
+	struct sdev_node *ddv = dv->sdev_dotdot;
+	struct sdev_node *idv;
+	struct sdev_node *prev = NULL;
+	int state;
+	struct devname_nsmap *map = NULL;
+	struct devname_ops	*dirops = NULL;
+	void (*fn)(devname_handle_t *, struct cred *) = NULL;
+
+	rw_enter(&ddv->sdev_contents, RW_WRITER);
+	state = dv->sdev_state;
+
+	mutex_enter(&vp->v_lock);
+	ASSERT(vp->v_count >= 1);
+
+	clean = (vp->v_count == 1) && (state == SDEV_ZOMBIE);
+
+	/*
+	 * last ref count on the ZOMBIE node is released.
+	 * clean up the sdev_node, and
+	 * release the hold on the backing store node so that
+	 * the ZOMBIE backing stores also cleaned out.
+	 */
+	if (clean) {
+		ASSERT(ddv);
+		if (SDEV_IS_GLOBAL(dv)) {
+			map = ddv->sdev_mapinfo;
+			dirops = map ? map->dir_ops : NULL;
+			if (dirops && (fn = dirops->devnops_inactive))
+				(*fn)(&(dv->sdev_handle), cred);
+		}
+
+		ddv->sdev_nlink--;
+		if (vp->v_type == VDIR) {
+			dv->sdev_nlink--;
+		}
+		for (idv = ddv->sdev_dot; idv && idv != dv;
+		    prev = idv, idv = idv->sdev_next);
+		ASSERT(idv == dv);
+		if (prev == NULL)
+			ddv->sdev_dot = dv->sdev_next;
+		else
+			prev->sdev_next = dv->sdev_next;
+		dv->sdev_next = NULL;
+		dv->sdev_nlink--;
+		--vp->v_count;
+		mutex_exit(&vp->v_lock);
+		sdev_nodedestroy(dv, 0);
+	} else {
+		--vp->v_count;
+		mutex_exit(&vp->v_lock);
+	}
+	rw_exit(&ddv->sdev_contents);
+}
+
+static int
+sdev_fid(struct vnode *vp, struct fid *fidp)
+{
+	struct sdev_node	*dv = VTOSDEV(vp);
+	struct sdev_fid	*sdev_fid;
+
+	if (fidp->fid_len < (sizeof (struct sdev_fid) - sizeof (ushort_t))) {
+		fidp->fid_len = sizeof (struct sdev_fid) - sizeof (ushort_t);
+		return (ENOSPC);
+	}
+
+	sdev_fid = (struct sdev_fid *)fidp;
+	bzero(sdev_fid, sizeof (struct sdev_fid));
+	sdev_fid->sdevfid_len =
+	    (int)sizeof (struct sdev_fid) - sizeof (ushort_t);
+	sdev_fid->sdevfid_ino = dv->sdev_ino;
+
+	return (0);
+}
+
+/*
+ * This pair of routines bracket all VOP_READ, VOP_WRITE
+ * and VOP_READDIR requests.  The contents lock stops things
+ * moving around while we're looking at them.
+ */
+static void
+sdev_rwlock(struct vnode *vp, int write_flag)
+{
+	rw_enter(&VTOSDEV(vp)->sdev_contents, write_flag ? RW_WRITER :
+		RW_READER);
+}
+
+/*ARGSUSED1*/
+static void
+sdev_rwunlock(struct vnode *vp, int write_flag)
+{
+	rw_exit(&VTOSDEV(vp)->sdev_contents);
+}
+
+/*ARGSUSED1*/
+static int
+sdev_seek(struct vnode *vp, offset_t ooff, offset_t *noffp)
+{
+	struct vnode *attrvp = VTOSDEV(vp)->sdev_attrvp;
+
+	ASSERT(vp->v_type != VCHR &&
+	    vp->v_type != VBLK && vp->v_type != VLNK);
+
+	if (vp->v_type == VDIR)
+		return (fs_seek(vp, ooff, noffp));
+
+	ASSERT(attrvp);
+	return (VOP_SEEK(attrvp, ooff, noffp));
+}
+
+/*ARGSUSED1*/
+static int
+sdev_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
+    offset_t offset, struct flk_callback *flk_cbp, struct cred *cr)
+{
+	int error;
+	struct sdev_node *dv = VTOSDEV(vp);
+
+	ASSERT(dv);
+	ASSERT(dv->sdev_attrvp);
+	error = VOP_FRLOCK(dv->sdev_attrvp, cmd, bfp, flag, offset,
+	    flk_cbp, cr);
+
+	return (error);
+}
+
+static int
+sdev_setfl(struct vnode *vp, int oflags, int nflags, cred_t *cr)
+{
+	struct sdev_node *dv = VTOSDEV(vp);
+	ASSERT(dv);
+	ASSERT(dv->sdev_attrvp);
+
+	return (VOP_SETFL(dv->sdev_attrvp, oflags, nflags, cr));
+}
+
+static int
+sdev_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr)
+{
+	switch (cmd) {
+	case _PC_ACL_ENABLED:
+		*valp = SDEV_ACL_FLAVOR(vp);
+		return (0);
+	}
+
+	return (fs_pathconf(vp, cmd, valp, cr));
+}
+
+vnodeops_t *sdev_vnodeops;
+
+const fs_operation_def_t sdev_vnodeops_tbl[] = {
+	VOPNAME_OPEN, sdev_open,
+	VOPNAME_CLOSE, sdev_close,
+	VOPNAME_READ, sdev_read,
+	VOPNAME_WRITE, sdev_write,
+	VOPNAME_IOCTL, sdev_ioctl,
+	VOPNAME_GETATTR, sdev_getattr,
+	VOPNAME_SETATTR, sdev_setattr,
+	VOPNAME_ACCESS, sdev_access,
+	VOPNAME_LOOKUP, sdev_lookup,
+	VOPNAME_CREATE, sdev_create,
+	VOPNAME_RENAME, sdev_rename,
+	VOPNAME_REMOVE, sdev_remove,
+	VOPNAME_MKDIR, sdev_mkdir,
+	VOPNAME_RMDIR, sdev_rmdir,
+	VOPNAME_READDIR, sdev_readdir,
+	VOPNAME_SYMLINK, sdev_symlink,
+	VOPNAME_READLINK, sdev_readlink, /* readlink */
+	VOPNAME_FSYNC, (fs_generic_func_p) fs_sync,
+	VOPNAME_INACTIVE, (fs_generic_func_p)sdev_inactive,
+	VOPNAME_FID, sdev_fid,
+	VOPNAME_RWLOCK, (fs_generic_func_p)sdev_rwlock,
+	VOPNAME_RWUNLOCK, (fs_generic_func_p)sdev_rwunlock,
+	VOPNAME_SEEK, sdev_seek,
+	VOPNAME_FRLOCK, sdev_frlock,
+	VOPNAME_PATHCONF, sdev_pathconf,
+	VOPNAME_SETFL, sdev_setfl,
+	VOPNAME_SETSECATTR, sdev_setsecattr,	/* setsecattr */
+	VOPNAME_GETSECATTR, sdev_getsecattr,	/* getsecattr */
+	NULL, NULL
+};
+
+int sdev_vnodeops_tbl_size = sizeof (sdev_vnodeops_tbl);
--- a/usr/src/uts/common/fs/devfs/devfs_subr.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/fs/devfs/devfs_subr.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -569,6 +568,57 @@
 }
 
 /*
+ * Get default device permission by consulting rules in
+ * privilege specification in minor node and /etc/minor_perm.
+ *
+ * This function is called from the devname filesystem to get default
+ * permissions for a device exported to a non-global zone.
+ */
+void
+devfs_get_defattr(struct vnode *vp, struct vattr *vap, int *no_fs_perm)
+{
+	mperm_t	mp;
+	struct dv_node *dv;
+
+	/* If vp isn't a dv_node, return something sensible */
+	if (!vn_matchops(vp, dv_vnodeops)) {
+		if (no_fs_perm)
+			*no_fs_perm = 0;
+		*vap = dv_vattr_file;
+		return;
+	}
+
+	/*
+	 * For minors not created by ddi_create_priv_minor_node(),
+	 * use devfs defaults.
+	 */
+	dv = VTODV(vp);
+	if (vp->v_type == VDIR) {
+		*vap = dv_vattr_dir;
+	} else if (dv->dv_flags & DV_NO_FSPERM) {
+		if (no_fs_perm)
+			*no_fs_perm = 1;
+		*vap = dv_vattr_priv;
+	} else {
+		/*
+		 * look up perm bits from minor_perm
+		 */
+		*vap = dv_vattr_file;
+		if (dev_minorperm(dv->dv_devi, dv->dv_name, &mp) == 0) {
+			VATTR_MP_MERGE((*vap), mp);
+			dcmn_err5(("%s: minor perm mode 0%o\n",
+			    dv->dv_name, vap->va_mode));
+		} else if (dv->dv_flags & DV_DFLT_MODE) {
+			ASSERT((dv->dv_dflt_mode & ~S_IAMB) == 0);
+			vap->va_mode &= ~S_IAMB;
+			vap->va_mode |= dv->dv_dflt_mode;
+			dcmn_err5(("%s: priv mode 0%o\n",
+			    dv->dv_name, vap->va_mode));
+		}
+	}
+}
+
+/*
  * dv_shadow_node
  *
  * Given a VDIR dv_node, find/create the associated VDIR
@@ -608,7 +658,6 @@
 	struct vattr	vattr;
 	int		create_tried;
 	int		error;
-	mperm_t		mp;
 
 	ASSERT(vp->v_type == VDIR || vp->v_type == VCHR || vp->v_type == VBLK);
 	dv = VTODV(vp);
@@ -688,30 +737,9 @@
 
 	/*
 	 * Failed to find attribute in persistent backing store,
-	 * get default permission bits.  For minors not created by
-	 * ddi_create_priv_minor_node(), use devfs defaults.
+	 * get default permission bits.
 	 */
-	if (vp->v_type == VDIR) {
-		vattr = dv_vattr_dir;
-	} else if (dv->dv_flags & DV_NO_FSPERM) {
-		vattr = dv_vattr_priv;
-	} else {
-		/*
-		 * look up perm bits from minor_perm
-		 */
-		vattr = dv_vattr_file;
-		if (dev_minorperm(dv->dv_devi, dv->dv_name, &mp) == 0) {
-			VATTR_MP_MERGE(vattr, mp);
-			dcmn_err5(("%s: minor perm mode 0%o\n",
-			    dv->dv_name, vattr.va_mode));
-		} else if (dv->dv_flags & DV_DFLT_MODE) {
-			ASSERT((dv->dv_dflt_mode & ~S_IAMB) == 0);
-			vattr.va_mode &= ~S_IAMB;
-			vattr.va_mode |= dv->dv_dflt_mode;
-			dcmn_err5(("%s: priv mode 0%o\n",
-			    dv->dv_name, vattr.va_mode));
-		}
-	}
+	devfs_get_defattr(vp, &vattr, NULL);
 
 	dv_vattr_merge(dv, &vattr);
 	gethrestime(&vattr.va_atime);
--- a/usr/src/uts/common/fs/namefs/namevfs.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/fs/namefs/namevfs.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -64,6 +63,7 @@
 #include <fs/fs_subr.h>
 #include <sys/policy.h>
 #include <sys/vmem.h>
+#include <sys/fs/sdev_impl.h>
 
 #define	NM_INOQUANT		(64 * 1024)
 
@@ -347,6 +347,17 @@
 	}
 	mutex_exit(&mvp->v_lock);
 
+	/*
+	 * Cannot allow users to fattach() in /dev/pts.
+	 * First, there is no need for doing so and secondly
+	 * we cannot allow arbitrary users to park on a
+	 * /dev/pts node.
+	 */
+	if (vn_matchops(mvp, devpts_getvnodeops())) {
+		releasef(namefdp.fd);
+		return (ENOTSUP);
+	}
+
 	filevp = fp->f_vnode;
 	if (filevp->v_type == VDIR || filevp->v_type == VPORT) {
 		releasef(namefdp.fd);
--- a/usr/src/uts/common/fs/vfs.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/fs/vfs.c	Fri Aug 25 17:24:25 2006 -0700
@@ -123,12 +123,14 @@
  */
 vnode_t *rootdir;		/* pointer to root inode vnode. */
 vnode_t *devicesdir;		/* pointer to inode of devices root */
+vnode_t	*devdir;		/* pointer to inode of dev root */
 
 char *server_rootpath;		/* root path for diskless clients */
 char *server_hostname;		/* hostname of diskless server */
 
 static struct vfs root;
 static struct vfs devices;
+static struct vfs dev;
 struct vfs *rootvfs = &root;	/* pointer to root vfs; head of VFS list. */
 rvfs_t *rvfs_list;		/* array of vfs ptrs for vfs hash list */
 int vfshsz = 512;		/* # of heads/locks in vfs hash arrays */
@@ -651,15 +653,16 @@
 	 */
 	if (VFS_ROOT(&devices, &devicesdir))
 		cmn_err(CE_PANIC, "vfs_mountdevices: not devices root");
-	VN_HOLD(devicesdir);
 
 	if (vfs_lock(&devices) != 0) {
+		VN_RELE(devicesdir);
 		cmn_err(CE_NOTE, "Cannot acquire vfs_lock of /devices");
 		return;
 	}
 
 	if (vn_vfswlock(mvp) != 0) {
 		vfs_unlock(&devices);
+		VN_RELE(devicesdir);
 		cmn_err(CE_NOTE, "Cannot acquire vfswlock of /devices");
 		return;
 	}
@@ -667,6 +670,85 @@
 	vfs_add(mvp, &devices, 0);
 	vn_vfsunlock(mvp);
 	vfs_unlock(&devices);
+	VN_RELE(devicesdir);
+}
+
+/*
+ * mount the first instance of /dev  to root and remain mounted
+ */
+static void
+vfs_mountdev1(void)
+{
+	struct vfssw *vsw;
+	struct vnode *mvp;
+	struct mounta mounta = {	/* fake mounta for sdev_mount() */
+		NULL,
+		NULL,
+		MS_SYSSPACE | MS_OVERLAY,
+		NULL,
+		NULL,
+		0,
+		NULL,
+		0
+	};
+
+	/*
+	 * _init dev module to fill in the vfssw
+	 */
+	if (modload("fs", "dev") == -1)
+		cmn_err(CE_PANIC, "Cannot _init dev module\n");
+
+	/*
+	 * Hold vfs
+	 */
+	RLOCK_VFSSW();
+	vsw = vfs_getvfsswbyname("dev");
+	VFS_INIT(&dev, &vsw->vsw_vfsops, NULL);
+	VFS_HOLD(&dev);
+
+	/*
+	 * Locate mount point
+	 */
+	if (lookupname("/dev", UIO_SYSSPACE, FOLLOW, NULLVPP, &mvp))
+		cmn_err(CE_PANIC, "Cannot find /dev\n");
+
+	/*
+	 * Perform the mount of /dev
+	 */
+	if (VFS_MOUNT(&dev, mvp, &mounta, CRED()))
+		cmn_err(CE_PANIC, "Cannot mount /dev 1\n");
+
+	RUNLOCK_VFSSW();
+
+	/*
+	 * Set appropriate members and add to vfs list for mnttab display
+	 */
+	vfs_setresource(&dev, "/dev");
+	vfs_setmntpoint(&dev, "/dev");
+
+	/*
+	 * Hold the root of /dev so it won't go away
+	 */
+	if (VFS_ROOT(&dev, &devdir))
+		cmn_err(CE_PANIC, "vfs_mountdev1: not dev root");
+
+	if (vfs_lock(&dev) != 0) {
+		VN_RELE(devdir);
+		cmn_err(CE_NOTE, "Cannot acquire vfs_lock of /dev");
+		return;
+	}
+
+	if (vn_vfswlock(mvp) != 0) {
+		vfs_unlock(&dev);
+		VN_RELE(devdir);
+		cmn_err(CE_NOTE, "Cannot acquire vfswlock of /dev");
+		return;
+	}
+
+	vfs_add(mvp, &dev, 0);
+	vn_vfsunlock(mvp);
+	vfs_unlock(&dev);
+	VN_RELE(devdir);
 }
 
 /*
@@ -766,10 +848,11 @@
 	}
 
 	/*
-	 * Mount /devices, /system/contract, /etc/mnttab, /etc/svc/volatile,
-	 * /system/object, and /proc.
+	 * Mount /devices, /dev instance 1, /system/contract, /etc/mnttab,
+	 * /etc/svc/volatile, /system/object, and /proc.
 	 */
 	vfs_mountdevices();
+	vfs_mountdev1();
 
 	vfs_mountfs("ctfs", "ctfs", CTFS_ROOT);
 	vfs_mountfs("proc", "/proc", "/proc");
--- a/usr/src/uts/common/io/ptm.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/io/ptm.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -80,7 +79,9 @@
  *	    failure, the errno is set to EINVAL indicating that the master
  *	    device is not open.
  *
- *  ZONEPT: sets the zone membership of ths associated pts device.
+ *  ZONEPT: sets the zone membership of the associated pts device.
+ *
+ *  GRPPT:  sets the group owner of the associated pts device.
  *
  * Synchronization:
  *
@@ -314,8 +315,6 @@
 	int	sflag,		/* open state flag */
 	cred_t  *credp)		/* credentials */
 {
-	extern dev_info_t *pts_dip;
-
 	struct pt_ttys	*ptmp;
 	mblk_t		*mop;		/* ptr to a setopts message block */
 	struct stroptions *sop;
@@ -338,12 +337,13 @@
 	}
 
 	/*
-	 * pts dependency: pt_ttys_alloc(), used below, really needs the pts
-	 * driver (and pts_dip variable) to be initialized to successfully
-	 * create device nodes.
+	 * The master open requires that the slave be attached
+	 * before it returns so that attempts to open the slave will
+	 * succeeed
 	 */
-	if (pts_dip == NULL)
-		(void) i_ddi_attach_pseudo_node("pts");
+	if (ptms_attach_slave() != 0) {
+		return (ENXIO);
+	}
 
 	mop = allocb(sizeof (struct stroptions), BPRI_MED);
 	if (mop == NULL) {
@@ -548,6 +548,30 @@
 			miocack(qp, mp, 0, 0);
 			break;
 		}
+		case PT_OWNER:
+		{
+			pt_own_t *ptop;
+			int error;
+
+			if ((error = miocpullup(mp, sizeof (pt_own_t))) != 0) {
+				miocnak(qp, mp, 0, error);
+				break;
+			}
+
+			ptop = (pt_own_t *)mp->b_cont->b_rptr;
+
+			if (ptop->pto_ruid < 0 || ptop->pto_rgid < 0) {
+				miocnak(qp, mp, 0, EINVAL);
+				break;
+			}
+
+			mutex_enter(&ptmp->pt_lock);
+			ptmp->pt_ruid = ptop->pto_ruid;
+			ptmp->pt_rgid = ptop->pto_rgid;
+			mutex_exit(&ptmp->pt_lock);
+			miocack(qp, mp, 0, 0);
+			break;
+		}
 		}
 		break;
 
--- a/usr/src/uts/common/io/ptms_conf.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/io/ptms_conf.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -49,16 +48,14 @@
  *   pseudo-terminal subsystem, minor 0 should not be used. (Potential future
  *   development).
  *
- *   Device entries in /dev/pts directory are created dynamically via
- *   ddi_create_minor_node(). It enqueues requests to suer-mode event daemon
- *   which actually creates entries asynchronously, so they may not be available
- *   immediately. For this reason we create devices before they are actually
- *   needed, so for each slot table extension we already have node creation
- *   requests queued. To avoid overflowing of the event daemon event queue we
- *   limit the maximum extension of the slot table by the pt_maxdelta tuneable.
  *   After the table slot size reaches pt_maxdelta, we stop 2^N extension
  *   algorithm and start extending the slot table size by pt_maxdelta.
  *
+ *   Device entries /dev/pts directory are created dynamically by the
+ *   /dev filesystem. We no longer call ddi_create_minor_node() on
+ *   behalf of the slave driver. The /dev filesystem creates /dev/pts
+ *   nodes based on the pt_ttys array.
+ *
  * Synchronization:
  *
  *   All global data synchronization between ptm/pts is done via global
@@ -113,6 +110,24 @@
  *	Find pt_ttys structure by minor number.
  *	Returns NULL when minor is out of range.
  *
+ * int ptms_minor_valid(minor_t minor, uid_t *ruid, gid_t *rgid)
+ *
+ *	Check if minor refers to an allocated pty in the current zone.
+ *	Returns
+ *		 0 if not allocated or not for this zone.
+ *		 1 if an allocated pty in the current zone.
+ *	Also returns owner of pty.
+ *
+ * int ptms_minor_exists(minor_t minor)
+ *	Check if minor refers to an allocated pty (in any zone)
+ *	Returns
+ *		0 if not an allocated pty
+ *		1 if an allocated pty
+ *
+ * void ptms_set_owner(minor_t minor, uid_t ruid, gid_t rgid)
+ *
+ *	Sets the owner associated with a pty.
+ *
  * void ptms_close(struct pt_ttys *pt, uint_t flags_to_clear);
  *
  *	Clear flags_to_clear in pt and if no one owns it (PTMOPEN/PTSOPEN not
@@ -196,17 +211,13 @@
 static size_t ptms_nslots = 0;		/* Size of slot array */
 static size_t ptms_ptymax = 0;		/* Maximum number of ptys */
 static size_t ptms_inuse = 0;		/* # of ptys currently allocated */
-static size_t ptms_bt_words = 0;	/* Size of minor bitmap in words */
-static size_t ptms_bt_len = 0;		/* Size of minor bitmap in bits */
 
-dev_info_t 	*pts_dip = NULL;	/* private copy of slave devinfo ptr */
+dev_info_t 	*pts_dip = NULL;	/* set if slave is attached */
 
 static struct kmem_cache *ptms_cache = NULL;	/* pty cache */
 
 static vmem_t *ptms_minor_arena = NULL; /* Arena for device minors */
 
-static ulong_t *ptms_bt = NULL;		/* pty created minor node bitmap */
-
 static uint_t ptms_roundup(uint_t);
 static int ptms_constructor(void *, void *, int);
 static void ptms_destructor(void *, void *);
@@ -264,12 +275,6 @@
 		    sizeof (struct pt_ttys), 0, ptms_constructor,
 		    ptms_destructor, NULL, NULL, NULL, 0);
 
-		/* Allocate bit map for created minor nodes */
-		ptms_bt_len = pt_init_cnt * 2 + 1;
-		ptms_bt_words = howmany(ptms_bt_len, BT_NBIPUL);
-		ptms_bt = kmem_zalloc(sizeof (ulong_t) * ptms_bt_words,
-			KM_SLEEP);
-
 		ptms_nslots = pt_init_cnt;
 
 		/* Allocate integer space for minor numbers */
@@ -288,60 +293,34 @@
 	mutex_exit(&ptms_lock);
 }
 
-static void
-ptms_create_node(dev_info_t *devi, minor_t i)
+/*
+ * This routine attaches the pts dip.
+ */
+int
+ptms_attach_slave(void)
 {
-	char name[22];		/* For representing 64-bit minor + NUL */
+	if (pts_dip == NULL && i_ddi_attach_pseudo_node("pts") == NULL)
+		return (-1);
 
-	(void) snprintf(name, sizeof (name), "%d", i);
-	if (ddi_create_minor_node(devi, name, S_IFCHR,
-	    i, DDI_PSEUDO, NULL) == DDI_SUCCESS) {
-		BT_SET(ptms_bt, i);
-	}
+	ASSERT(pts_dip);
+	return (0);
 }
 
 /*
- * Create nodes in /dev/pts directory.
- * Called from pts_attach.
+ * Called from /dev fs. Checks if dip is attached,
+ * and if it is, returns its major number.
  */
-int
-ptms_create_pts_nodes(dev_info_t *devi)
+major_t
+ptms_slave_attached(void)
 {
-	uint_t i;
+	major_t maj = (major_t)-1;
 
 	mutex_enter(&ptms_lock);
-	pts_dip = devi;
-
-	/*
-	 * /dev/pts/0 is not used, but some applications may check it, so create
-	 * it also.
-	 *
-	 * Create all minor nodes that have been pre-allocated in ptms_init().
-	 */
-	for (i = 0; i <= pt_init_cnt * 2; i++)
-		ptms_create_node(devi, i);
-
+	if (pts_dip)
+		maj = ddi_driver_major(pts_dip);
 	mutex_exit(&ptms_lock);
 
-	return (DDI_SUCCESS);
-}
-
-/*
- * Destroy nodes in /dev/pts directory.
- * Called from pts_detach.
- */
-int
-ptms_destroy_pts_nodes(dev_info_t *devi)
-{
-	mutex_enter(&ptms_lock);
-	ddi_remove_minor_node(devi, NULL);
-	if (ptms_bt != NULL && ptms_bt_words > 0) {
-		/* Clear bitmap since all minor nodes have been removed */
-		bzero(ptms_bt, sizeof (ulong_t) * ptms_bt_words);
-	}
-	pts_dip = NULL;
-	mutex_exit(&ptms_lock);
-	return (DDI_SUCCESS);
+	return (maj);
 }
 
 /*
@@ -400,14 +379,6 @@
 		return (NULL);
 	}
 
-	if (BT_TEST(ptms_bt, dminor) == 0) {
-		/*
-		 * Retry failed node creation.
-		 */
-		if (pts_dip != NULL)
-			ptms_create_node(pts_dip, dminor);
-	}
-
 	pt = kmem_cache_alloc(ptms_cache, KM_NOSLEEP);
 	if (pt == NULL) {
 		/* Not enough memory - this entry can't be used now. */
@@ -418,6 +389,8 @@
 		pt->pt_pid = curproc->p_pid;	/* For debugging */
 		pt->pt_state = (PTMOPEN | PTLOCK);
 		pt->pt_zoneid = getzoneid();
+		pt->pt_ruid = 0; /* we don't know uid/gid yet. Report as root */
+		pt->pt_rgid = 0;
 		ASSERT(ptms_slots[dminor - 1] == NULL);
 		ptms_slots[dminor - 1] = pt;
 	}
@@ -443,6 +416,102 @@
 }
 
 /*
+ * Invoked in response to chown on /dev/pts nodes to change the
+ * permission on a pty
+ */
+void
+ptms_set_owner(minor_t dminor, uid_t ruid, gid_t rgid)
+{
+	struct pt_ttys *pt;
+
+	ASSERT(ruid >= 0);
+	ASSERT(rgid >= 0);
+
+	if (ruid < 0 || rgid < 0)
+		return;
+
+	/*
+	 * /dev/pts/0 is not used, but some applications may check it. There
+	 * is no pty backing it - so we have nothing to do.
+	 */
+	if (dminor == 0)
+		return;
+
+	mutex_enter(&ptms_lock);
+	pt = ptms_minor2ptty(dminor);
+	if (pt != NULL && pt->pt_zoneid == getzoneid()) {
+		pt->pt_ruid = ruid;
+		pt->pt_rgid = rgid;
+	}
+	mutex_exit(&ptms_lock);
+}
+
+/*
+ * Given a ptm/pts minor number
+ * returns:
+ *	1 if the pty is allocated to the current zone.
+ *	0 otherwise
+ *
+ * If the pty is allocated to the current zone, it also returns the owner.
+ */
+int
+ptms_minor_valid(minor_t dminor, uid_t *ruid, gid_t *rgid)
+{
+	struct pt_ttys *pt;
+	int ret;
+
+	ASSERT(ruid);
+	ASSERT(rgid);
+
+	*ruid = -1;
+	*rgid = -1;
+
+	/*
+	 * /dev/pts/0 is not used, but some applications may check it, so create
+	 * it also. Report the owner as root. It belongs to all zones.
+	 */
+	if (dminor == 0) {
+		*ruid = 0;
+		*rgid = 0;
+		return (1);
+	}
+
+	ret = 0;
+	mutex_enter(&ptms_lock);
+	pt = ptms_minor2ptty(dminor);
+	if (pt != NULL) {
+		ASSERT(pt->pt_ruid >= 0);
+		ASSERT(pt->pt_rgid >= 0);
+		if (pt->pt_zoneid == getzoneid()) {
+			ret = 1;
+			*ruid = pt->pt_ruid;
+			*rgid = pt->pt_rgid;
+		}
+	}
+	mutex_exit(&ptms_lock);
+
+	return (ret);
+}
+
+/*
+ * Given a ptm/pts minor number
+ * returns:
+ *	0 if the pty is not allocated
+ *	1 if the pty is allocated
+ */
+int
+ptms_minor_exists(minor_t dminor)
+{
+	int ret;
+
+	mutex_enter(&ptms_lock);
+	ret = ptms_minor2ptty(dminor) ? 1 : 0;
+	mutex_exit(&ptms_lock);
+
+	return (ret);
+}
+
+/*
  * Close the pt and clear flags_to_clear.
  * If pt device is not opened by someone else, free it and clear its slot.
  */
@@ -496,14 +565,9 @@
 	minor_t old_size = ptms_nslots;
 	minor_t delta = MIN(pt_maxdelta, old_size);
 	minor_t new_size = old_size + delta;
-	minor_t	new_delta = MIN(pt_maxdelta, new_size);
 	struct pt_ttys **ptms_old = ptms_slots;
 	struct pt_ttys **ptms_new;
-	ulong_t	*new_bt;
-	size_t	new_bt_words;
-	size_t	new_bt_len;
 	void  *vaddr;			/* vmem_add return value */
-	minor_t i;
 
 	ASSERT(MUTEX_HELD(&ptms_lock));
 
@@ -515,22 +579,12 @@
 	if (ptms_new == NULL)
 		return ((minor_t)0);
 
-	/* Allocate new ptms bitmap */
-	new_bt_len = ptms_bt_len + new_delta;
-	new_bt_words = howmany(new_bt_len, BT_NBIPUL);
-	new_bt = kmem_zalloc(sizeof (ulong_t) * new_bt_words, KM_NOSLEEP);
-	if (new_bt == NULL) {
-		kmem_free(ptms_new, new_size * sizeof (struct pt_ttys *));
-		return ((minor_t)0);
-	}
-
 	/* Increase clone index space */
 	vaddr = vmem_add(ptms_minor_arena, (void *)(uintptr_t)(old_size + 1),
 	    new_size - old_size, VM_NOSLEEP);
 
 	if (vaddr == NULL) {
 		kmem_free(ptms_new, new_size * sizeof (struct pt_ttys *));
-		kmem_free(new_bt, sizeof (ulong_t) * new_bt_words);
 		return ((minor_t)0);
 	}
 
@@ -540,28 +594,6 @@
 	ptms_slots = ptms_new;
 	kmem_free(ptms_old, old_size * sizeof (struct pt_ttys *));
 
-	/* Migrate bitmap entries to a new location */
-	bt_copy(ptms_bt, new_bt, ptms_bt_words);
-	kmem_free(ptms_bt, sizeof (ulong_t) * ptms_bt_words);
-	ptms_bt = new_bt;
-	ptms_bt_words = new_bt_words;
-	ptms_bt_len = new_bt_len;
-
-	/*
-	 * Add new or previously failed /devices entries.
-	 * Devices are created asynchronously via event daemon requests, so we
-	 * pre-create devices before they are actually needed.
-	 * Faster performance could be obtained by keeping track of
-	 * the last uncreated node, rather than searching.
-	 */
-	if (pts_dip != NULL) {
-		for (i = bt_availbit(ptms_bt, ptms_bt_len); i < ptms_bt_len;
-			i++) {
-			if (BT_TEST(ptms_bt, i) == 0)
-				ptms_create_node(pts_dip, i);
-		}
-	}
-
 	/* Allocate minor number and return it */
 	return ((minor_t)(uintptr_t)
 	    vmem_alloc(ptms_minor_arena, 1, VM_NOSLEEP));
--- a/usr/src/uts/common/io/pts.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/io/pts.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -231,16 +230,24 @@
 	if (cmd != DDI_ATTACH)
 		return (DDI_FAILURE);
 
-	return (ptms_create_pts_nodes(devi));
+	mutex_enter(&ptms_lock);
+	pts_dip = devi;
+	mutex_exit(&ptms_lock);
+
+	return (DDI_SUCCESS);
 }
 
+/*ARGSUSED*/
 static int
 pts_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 {
 	if (cmd != DDI_DETACH)
 		return (DDI_FAILURE);
 
-	return (ptms_destroy_pts_nodes(devi));
+	/*
+	 * For now, pts cannot be detached.
+	 */
+	return (DDI_FAILURE);
 }
 
 /*ARGSUSED*/
--- a/usr/src/uts/common/os/devcfg.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/os/devcfg.c	Fri Aug 25 17:24:25 2006 -0700
@@ -49,6 +49,7 @@
 #include <sys/strsubr.h>
 #include <sys/fs/snode.h>
 #include <sys/fs/dv_node.h>
+#include <sys/reboot.h>
 
 #ifdef DEBUG
 int ddidebug = DDI_AUDIT;
@@ -111,6 +112,13 @@
 dev_info_t *scsi_vhci_dip;		/* MPXIO dip */
 major_t clone_major;
 
+/*
+ * A non-global zone's /dev is derived from the device tree.
+ * This generation number serves to indicate when a zone's
+ * /dev may need to be updated.
+ */
+volatile ulong_t devtree_gen;		/* generation number */
+
 /* block all future dev_info state changes */
 static hrtime_t volatile devinfo_freeze = 0;
 
@@ -119,6 +127,9 @@
 
 extern kmutex_t global_vhci_lock;
 
+/* bitset of DS_SYSAVAIL & DS_RECONFIG - no races, no lock */
+static int devname_state = 0;
+
 /*
  * The devinfo snapshot cache and related variables.
  * The only field in the di_cache structure that needs initialization
@@ -3000,8 +3011,8 @@
  * I/O subsystem initialization is considered complete when devfsadm
  * is executed.
  *
- * NOTE: The start of syseventd in S60devfsadm happen to be convenient
- *	indicator for the completion of I/O initialization during boot.
+ * NOTE: The start of syseventd happens to be a convenient indicator
+ *	of the completion of I/O initialization during boot.
  *	The implementation should be replaced by something more robust.
  */
 int
@@ -3011,6 +3022,55 @@
 	return (sysevent_daemon_init);
 }
 
+/*
+ * May be used to determine system boot state
+ * "Available" means the system is for the most part up
+ * and initialized, with all system services either up or
+ * capable of being started.  This state is set by devfsadm
+ * during the boot process.  The /dev filesystem infers
+ * from this when implicit reconfig can be performed,
+ * ie, devfsadm can be invoked.  Please avoid making
+ * further use of this unless it's really necessary.
+ */
+int
+i_ddi_sysavail()
+{
+	return (devname_state & DS_SYSAVAIL);
+}
+
+/*
+ * May be used to determine if boot is a reconfigure boot.
+ */
+int
+i_ddi_reconfig()
+{
+	return (devname_state & DS_RECONFIG);
+}
+
+/*
+ * Note system services are up, inform /dev.
+ */
+void
+i_ddi_set_sysavail()
+{
+	if ((devname_state & DS_SYSAVAIL) == 0) {
+		devname_state |= DS_SYSAVAIL;
+		sdev_devstate_change();
+	}
+}
+
+/*
+ * Note reconfiguration boot, inform /dev.
+ */
+void
+i_ddi_set_reconfig()
+{
+	if ((devname_state & DS_RECONFIG) == 0) {
+		devname_state |= DS_RECONFIG;
+		sdev_devstate_change();
+	}
+}
+
 
 /*
  * device tree walking
@@ -6757,9 +6817,11 @@
 	}
 
 	/*
-	 * Invalidate the in-core cache
+	 * Invalidate the in-core cache and
+	 * increment devtree generation number
 	 */
 	atomic_and_32(&di_cache.cache_valid, 0);
+	atomic_inc_ulong(&devtree_gen);
 
 	flag = (kmflag == KM_SLEEP) ? TQ_SLEEP : TQ_NOSLEEP;
 
--- a/usr/src/uts/common/os/devctl.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/os/devctl.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -51,7 +50,6 @@
 #include <sys/fs/snode.h>
 #include <sys/fs/dv_node.h>
 #include <sys/kobj.h>
-
 #include <sys/devctl_impl.h>
 
 
@@ -65,6 +63,8 @@
 
 int devid_cache_read_disable = 0;
 int devid_cache_write_disable = 0;
+int sdev_cache_read_disable = 0;
+int sdev_cache_write_disable = 0;
 
 int kfio_report_error = 0;		/* kernel file i/o operations */
 int devid_report_error = 0;		/* devid cache operations */
@@ -78,13 +78,46 @@
 static kcondvar_t	devid_discovery_cv;
 static clock_t		devid_last_discovery = 0;
 
+
+static int		devid_nvp2nvl(nvfd_t *, nvlist_t **);
+static nvp_list_t	*devid_nvl2nvp(nvlist_t *, char *);
+static void		devid_nvp_free(nvp_list_t *);
+
+static int		sdev_nvp2nvl(nvfd_t *, nvlist_t **);
+static nvp_list_t	*sdev_nvl2nvp(nvlist_t *, char *);
+static void		sdev_nvp_free(nvp_list_t *);
+
 /*
- * Descriptor for /etc/devices/devid_cache
+ * Descriptors for the /etc/devices cache files
  */
-nvfd_t devid_cache_fd = {
+static nvfd_t devid_cache_fd = {
 	"/etc/devices/devid_cache",
+	devid_nvp2nvl,			/* nvf_nvp2nvl */
+	devid_nvl2nvp,			/* nvf_nvl2nvp */
+	devid_nvp_free,			/* nvf_nvp_free */
+	NULL,				/* nvf_write_complete */
+	0, NULL, NULL, 0
+
 };
-static nvfd_t *dcfd = &devid_cache_fd;
+static nvfd_t sdev_cache_fd = {
+	"/etc/devices/devname_cache",
+	sdev_nvp2nvl,			/* nvf_nvp2nvl */
+	sdev_nvl2nvp,			/* nvf_nvl2nvp */
+	sdev_nvp_free,			/* nvf_nvp_free */
+	NULL,				/* nvf_write_complete */
+	0, NULL, NULL, 0
+};
+
+static nvfd_t	*dcfd	= &devid_cache_fd;
+nvfd_t		*sdevfd	= &sdev_cache_fd;
+
+static nvfd_t *cachefds[] = {
+	&devid_cache_fd,
+	&sdev_cache_fd
+};
+
+#define	NCACHEFDS	((sizeof (cachefds)) / (sizeof (nvfd_t *)))
+
 
 
 extern int modrootloaded;
@@ -122,6 +155,11 @@
 	dcfd->nvf_tail = NULL;
 	rw_init(&dcfd->nvf_lock, NULL, RW_DRIVER, NULL);
 
+	sdevfd->nvf_flags = 0;
+	sdevfd->nvf_list = NULL;
+	sdevfd->nvf_tail = NULL;
+	rw_init(&sdevfd->nvf_lock, NULL, RW_DRIVER, NULL);
+
 	mutex_init(&devid_discovery_mutex, NULL, MUTEX_DEFAULT, NULL);
 	cv_init(&devid_discovery_cv, NULL, CV_DRIVER, NULL);
 }
@@ -546,44 +584,63 @@
 }
 
 static void
-nvp_free(nvp_list_t *np)
+devid_nvp_free(nvp_list_t *np)
 {
-	if (np->nvp_devpath)
-		kmem_free(np->nvp_devpath, strlen(np->nvp_devpath)+1);
-	if (np->nvp_devid)
-		kmem_free(np->nvp_devid, ddi_devid_sizeof(np->nvp_devid));
+	nvp_devid_t *dp = NVP2DEVID(np);
 
-	kmem_free(np, sizeof (nvp_list_t));
+	if (dp->nvp_devpath)
+		kmem_free(dp->nvp_devpath, strlen(dp->nvp_devpath)+1);
+	if (dp->nvp_devid)
+		kmem_free(dp->nvp_devid, ddi_devid_sizeof(dp->nvp_devid));
+
+	kmem_free(dp, sizeof (nvp_devid_t));
 }
 
 static void
-nvp_list_free(nvp_list_t *nvp)
+sdev_nvp_free(nvp_list_t *np)
+{
+	nvp_devname_t *dp = NVP2DEVNAME(np);
+	int	i;
+	char	**p;
+
+	if (dp->nvp_npaths > 0) {
+		p = dp->nvp_paths;
+		for (i = 0; i < dp->nvp_npaths; i++, p++) {
+			kmem_free(*p, strlen(*p)+1);
+		}
+		kmem_free(dp->nvp_paths,
+			dp->nvp_npaths * sizeof (char *));
+		kmem_free(dp->nvp_expirecnts,
+			dp->nvp_npaths * sizeof (int));
+	}
+
+	kmem_free(dp, sizeof (nvp_devname_t));
+}
+
+static void
+nvp_list_free(nvfd_t *nvf, nvp_list_t *nvp)
 {
 	nvp_list_t	*np;
 	nvp_list_t	*next;
 
 	for (np = nvp; np; np = next) {
 		next = np->nvp_next;
-		nvp_free(np);
+		(nvf->nvf_nvp_free)(np);
 	}
 }
 
+
 /*
- * Free the devid-related information in an nvp element
- * If no more data is stored in the nvp element, free
- * it and unlink it from the list
- *
- * Since at present there is no further use of nvp's,
- * there's nothing to check.
+ * Free an nvp element in a list
  */
-static nvp_list_t *
-nfd_devid_free_and_unlink(nvfd_t *nvf, nvp_list_t *np)
+void
+nfd_nvp_free_and_unlink(nvfd_t *nvf, nvp_list_t *np)
 {
 	nvp_list_t *pv, *next;
 
 	pv = np->nvp_prev;
 	next = np->nvp_next;
-	nvp_free(np);
+	(nvf->nvf_nvp_free)(np);
 
 	/* remove element at head */
 	if (pv == NULL) {
@@ -602,12 +659,10 @@
 		pv->nvp_next = next;
 		next->nvp_prev = pv;
 	}
-
-	return (next);
 }
 
-static void
-nfd_devid_link(nvfd_t *nvf, nvp_list_t *np)
+void
+nfd_nvp_link(nvfd_t *nvf, nvp_list_t *np)
 {
 	if (nvf->nvf_list == NULL) {
 		nvf->nvf_list = np;
@@ -622,16 +677,17 @@
 /*
  * Convert a device path/nvlist pair to an nvp_list_t
  * Used to parse the nvlist format when reading
+ * /etc/devices/devid_cache
  */
 static nvp_list_t *
-nvlist_to_nvp(nvlist_t *nvl, char *name)
+devid_nvl2nvp(nvlist_t *nvl, char *name)
 {
-	nvp_list_t *np;
+	nvp_devid_t *np;
 	ddi_devid_t devidp;
 	int rval;
 	uint_t n;
 
-	np = kmem_zalloc(sizeof (nvp_list_t), KM_SLEEP);
+	np = kmem_zalloc(sizeof (nvp_devid_t), KM_SLEEP);
 	np->nvp_devpath = i_ddi_strdup(name, KM_SLEEP);
 
 	NVP_DEVID_DEBUG_PATH((np->nvp_devpath));
@@ -654,18 +710,70 @@
 		}
 	}
 
-	return (np);
+	return (NVPLIST(np));
 }
 
 /*
+ * Convert a device path/nvlist pair to an nvp_list_t
+ * Used to parse the nvlist format when reading
+ * /etc/devices/devname_cache
+ */
+static nvp_list_t *
+sdev_nvl2nvp(nvlist_t *nvl, char *name)
+{
+	nvp_devname_t *np;
+	char	**strs;
+	int	*cnts;
+	uint_t	nstrs, ncnts;
+	int	rval, i;
+
+	/* name of the sublist must match what we created */
+	if (strcmp(name, DP_DEVNAME_ID) != 0) {
+		return (NULL);
+	}
+
+	np = kmem_zalloc(sizeof (nvp_devname_t), KM_SLEEP);
+
+	rval = nvlist_lookup_string_array(nvl,
+	    DP_DEVNAME_NCACHE_ID, &strs, &nstrs);
+	if (rval) {
+		kmem_free(np, sizeof (nvp_devname_t));
+		return (NULL);
+	}
+
+	np->nvp_npaths = nstrs;
+	np->nvp_paths = kmem_zalloc(nstrs * sizeof (char *), KM_SLEEP);
+	for (i = 0; i < nstrs; i++) {
+		np->nvp_paths[i] = i_ddi_strdup(strs[i], KM_SLEEP);
+	}
+	np->nvp_expirecnts = kmem_zalloc(nstrs * sizeof (int), KM_SLEEP);
+	for (i = 0; i < nstrs; i++) {
+		np->nvp_expirecnts[i] = 4; /* XXX sdev_nc_expirecnt */
+	}
+
+	rval = nvlist_lookup_int32_array(nvl,
+	    DP_DEVNAME_NC_EXPIRECNT_ID, &cnts, &ncnts);
+	if (rval == 0) {
+		ASSERT(ncnts == nstrs);
+		ncnts = max(ncnts, nstrs);
+		for (i = 0; i < nstrs; i++) {
+			np->nvp_expirecnts[i] = cnts[i];
+		}
+	}
+
+	return (NVPLIST(np));
+}
+
+
+/*
  * Convert a list of nvp_list_t's to a single nvlist
- * Used when writing the nvlist file
+ * Used when writing the nvlist file.
  */
 static int
-nvp_to_nvlist(nvfd_t *nvfd, nvlist_t **ret_nvl)
+devid_nvp2nvl(nvfd_t *nvfd, nvlist_t **ret_nvl)
 {
 	nvlist_t	*nvl, *sub_nvl;
-	nvp_list_t	*np;
+	nvp_devid_t	*np;
 	int		rval;
 
 	ASSERT(modrootloaded);
@@ -677,7 +785,7 @@
 		return (DDI_FAILURE);
 	}
 
-	for (np = nvfd->nvf_list; np; np = np->nvp_next) {
+	for (np = NVF_DEVID_LIST(nvfd); np; np = NVP_DEVID_NEXT(np)) {
 		if (np->nvp_devid == NULL)
 		    continue;
 		NVP_DEVID_DEBUG_PATH(np->nvp_devpath);
@@ -689,18 +797,16 @@
 			goto err;
 		}
 
-		if (np->nvp_devid) {
-			rval = nvlist_add_byte_array(sub_nvl, DP_DEVID_ID,
-				(uchar_t *)np->nvp_devid,
-				ddi_devid_sizeof(np->nvp_devid));
-			if (rval == 0) {
-				NVP_DEVID_DEBUG_DEVID(np->nvp_devid);
-			} else {
-				KFIOERR((CE_CONT,
-				    "%s: nvlist add error %d (devid)\n",
-				    nvfd->nvf_name, rval));
-				goto err;
-			}
+		rval = nvlist_add_byte_array(sub_nvl, DP_DEVID_ID,
+			(uchar_t *)np->nvp_devid,
+			ddi_devid_sizeof(np->nvp_devid));
+		if (rval == 0) {
+			NVP_DEVID_DEBUG_DEVID(np->nvp_devid);
+		} else {
+			KFIOERR((CE_CONT,
+			    "%s: nvlist add error %d (devid)\n",
+			    nvfd->nvf_name, rval));
+			goto err;
 		}
 
 		rval = nvlist_add_nvlist(nvl, np->nvp_devpath, sub_nvl);
@@ -723,6 +829,76 @@
 	return (DDI_FAILURE);
 }
 
+/*
+ * Convert a list of nvp_list_t's to a single nvlist
+ * Used when writing the nvlist file.
+ */
+static int
+sdev_nvp2nvl(nvfd_t *nvfd, nvlist_t **ret_nvl)
+{
+	nvlist_t	*nvl, *sub_nvl;
+	nvp_devname_t	*np;
+	int		rval;
+
+	ASSERT(modrootloaded);
+
+	rval = nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
+	if (rval != 0) {
+		KFIOERR((CE_CONT, "%s: nvlist alloc error %d\n",
+			nvfd->nvf_name, rval));
+		return (DDI_FAILURE);
+	}
+
+	if ((np = NVF_DEVNAME_LIST(nvfd)) != NULL) {
+		ASSERT(NVP_DEVNAME_NEXT(np) == NULL);
+
+		rval = nvlist_alloc(&sub_nvl, NV_UNIQUE_NAME, KM_SLEEP);
+		if (rval != 0) {
+			KFIOERR((CE_CONT, "%s: nvlist alloc error %d\n",
+				nvfd->nvf_name, rval));
+			sub_nvl = NULL;
+			goto err;
+		}
+
+		rval = nvlist_add_string_array(sub_nvl,
+		    DP_DEVNAME_NCACHE_ID, np->nvp_paths, np->nvp_npaths);
+		if (rval != 0) {
+			KFIOERR((CE_CONT,
+			    "%s: nvlist add error %d (sdev)\n",
+			    nvfd->nvf_name, rval));
+			goto err;
+		}
+
+		rval = nvlist_add_int32_array(sub_nvl,
+		    DP_DEVNAME_NC_EXPIRECNT_ID,
+		    np->nvp_expirecnts, np->nvp_npaths);
+		if (rval != 0) {
+			KFIOERR((CE_CONT,
+			    "%s: nvlist add error %d (sdev)\n",
+			    nvfd->nvf_name, rval));
+			goto err;
+		}
+
+		rval = nvlist_add_nvlist(nvl, DP_DEVNAME_ID, sub_nvl);
+		if (rval != 0) {
+			KFIOERR((CE_CONT, "%s: nvlist add error %d (sublist)\n",
+			    nvfd->nvf_name, rval));
+			goto err;
+		}
+		nvlist_free(sub_nvl);
+	}
+
+	*ret_nvl = nvl;
+	return (DDI_SUCCESS);
+
+err:
+	if (sub_nvl)
+		nvlist_free(sub_nvl);
+	nvlist_free(nvl);
+	*ret_nvl = NULL;
+	return (DDI_FAILURE);
+}
+
 
 /*
  * Read a file in the nvlist format
@@ -769,17 +945,18 @@
 			 * convert nvlist for this device to
 			 * an nvp_list_t struct
 			 */
-			np = nvlist_to_nvp(sublist, name);
-			np->nvp_next = NULL;
-			np->nvp_prev = nvp_tail;
+			np = (nvfd->nvf_nvl2nvp)(sublist, name);
+			if (np) {
+				np->nvp_next = NULL;
+				np->nvp_prev = nvp_tail;
 
-			if (nvp_list == NULL) {
-				nvp_list = np;
-			} else {
-				nvp_tail->nvp_next = np;
+				if (nvp_list == NULL) {
+					nvp_list = np;
+				} else {
+					nvp_tail->nvp_next = np;
+				}
+				nvp_tail = np;
 			}
-			nvp_tail = np;
-
 			break;
 
 		default:
@@ -800,7 +977,7 @@
 error:
 	nvlist_free(nvl);
 	if (nvp_list)
-		nvp_list_free(nvp_list);
+		nvp_list_free(nvfd, nvp_list);
 	return (rval);
 }
 
@@ -810,7 +987,7 @@
 {
 	int rval;
 
-	KFDEBUG((CE_CONT, "Reading %s\n", nvfd->nvf_name));
+	KFDEBUG((CE_CONT, "reading %s\n", nvfd->nvf_name));
 
 	rval = fread_nvp_list(nvfd);
 	if (rval) {
@@ -836,39 +1013,26 @@
 	return (rval);
 }
 
+/* for information possibly required to mount root */
 void
 i_ddi_read_devices_files(void)
 {
-	nvfd_t nvfd;
-	int rval;
-
 	mdi_read_devices_files();
 
-	if (devid_cache_read_disable)
-		return;
-
-	nvfd.nvf_name = dcfd->nvf_name;
-	nvfd.nvf_flags = 0;
-	nvfd.nvf_list = NULL;
-	nvfd.nvf_tail = NULL;
-	rw_init(&nvfd.nvf_lock, NULL, RW_DRIVER, NULL);
-
-	rval = i_ddi_read_one_nvfile(&nvfd);
-
-	rw_enter(&dcfd->nvf_lock, RW_WRITER);
+	if (devid_cache_read_disable == 0) {
+		ASSERT(dcfd->nvf_list == NULL);
+		(void) i_ddi_read_one_nvfile(dcfd);
+	}
+}
 
-	if (rval == 0) {
-		if (dcfd->nvf_list != NULL) {
-			nvp_list_free(dcfd->nvf_list);
-		}
-		dcfd->nvf_list = nvfd.nvf_list;
-		dcfd->nvf_tail = nvfd.nvf_tail;
+/* may be done after root is mounted */
+void
+i_ddi_read_devname_file(void)
+{
+	if (sdev_cache_read_disable == 0) {
+		ASSERT(sdevfd->nvf_list == NULL);
+		(void) i_ddi_read_one_nvfile(sdevfd);
 	}
-	dcfd->nvf_flags = nvfd.nvf_flags;
-
-	rw_exit(&dcfd->nvf_lock);
-
-	rw_destroy(&nvfd.nvf_lock);
 }
 
 static int
@@ -1003,8 +1167,8 @@
 int
 e_devid_cache_register(dev_info_t *dip, ddi_devid_t devid)
 {
-	nvp_list_t *np;
-	nvp_list_t *new_nvp;
+	nvp_devid_t *np;
+	nvp_devid_t *new_nvp;
 	ddi_devid_t new_devid;
 	int new_devid_size;
 	char *path, *fullpath;
@@ -1022,14 +1186,14 @@
 
 	DEVID_LOG_REG(("register", devid, path));
 
-	new_nvp = kmem_zalloc(sizeof (nvp_list_t), KM_SLEEP);
+	new_nvp = kmem_zalloc(sizeof (nvp_devid_t), KM_SLEEP);
 	new_devid_size = ddi_devid_sizeof(devid);
 	new_devid = kmem_alloc(new_devid_size, KM_SLEEP);
 	(void) bcopy(devid, new_devid, new_devid_size);
 
 	rw_enter(&dcfd->nvf_lock, RW_WRITER);
 
-	for (np = dcfd->nvf_list; np != NULL; np = np->nvp_next) {
+	for (np = NVF_DEVID_LIST(dcfd); np; np = NVP_DEVID_NEXT(np)) {
 		if (strcmp(path, np->nvp_devpath) == 0) {
 			DEVID_DEBUG2((CE_CONT,
 			    "register: %s path match\n", path));
@@ -1041,7 +1205,7 @@
 				np->nvp_dip = dip;
 				NVF_MARK_DIRTY(dcfd);
 				rw_exit(&dcfd->nvf_lock);
-				kmem_free(new_nvp, sizeof (nvp_list_t));
+				kmem_free(new_nvp, sizeof (nvp_devid_t));
 				kmem_free(path, pathlen);
 				goto exit;
 			}
@@ -1063,8 +1227,7 @@
 				 * may map to multiple paths but one path
 				 * should only map to one devid.
 				 */
-				(void) nfd_devid_free_and_unlink(
-					dcfd, np);
+				nfd_nvp_free_and_unlink(dcfd, NVPLIST(np));
 				np = NULL;
 				break;
 			} else {
@@ -1074,7 +1237,7 @@
 					NVP_DEVID_DIP | NVP_DEVID_REGISTERED;
 				np->nvp_dip = dip;
 				rw_exit(&dcfd->nvf_lock);
-				kmem_free(new_nvp, sizeof (nvp_list_t));
+				kmem_free(new_nvp, sizeof (nvp_devid_t));
 				kmem_free(path, pathlen);
 				kmem_free(new_devid, new_devid_size);
 				return (DDI_SUCCESS);
@@ -1093,7 +1256,7 @@
 	new_nvp->nvp_devid = new_devid;
 
 	NVF_MARK_DIRTY(dcfd);
-	nfd_devid_link(dcfd, new_nvp);
+	nfd_nvp_link(dcfd, NVPLIST(new_nvp));
 
 	rw_exit(&dcfd->nvf_lock);
 
@@ -1101,7 +1264,8 @@
 	if (free_devid)
 		kmem_free(free_devid, ddi_devid_sizeof(free_devid));
 
-	wake_nvpflush_daemon(dcfd);
+	if (!devid_cache_write_disable)
+		wake_nvpflush_daemon();
 
 	return (DDI_SUCCESS);
 }
@@ -1115,11 +1279,11 @@
 void
 e_devid_cache_unregister(dev_info_t *dip)
 {
-	nvp_list_t *np;
+	nvp_devid_t *np;
 
 	rw_enter(&dcfd->nvf_lock, RW_WRITER);
 
-	for (np = dcfd->nvf_list; np != NULL; np = np->nvp_next) {
+	for (np = NVF_DEVID_LIST(dcfd); np; np = NVP_DEVID_NEXT(np)) {
 		if (np->nvp_devid == NULL)
 			continue;
 		if ((np->nvp_flags & NVP_DEVID_DIP) && np->nvp_dip == dip) {
@@ -1138,26 +1302,26 @@
 void
 e_devid_cache_cleanup(void)
 {
-	nvp_list_t *np, *next;
+	nvp_devid_t *np, *next;
 
 	rw_enter(&dcfd->nvf_lock, RW_WRITER);
 
-	for (np = dcfd->nvf_list; np != NULL; np = next) {
-		next = np->nvp_next;
+	for (np = NVF_DEVID_LIST(dcfd); np; np = next) {
+		next = NVP_DEVID_NEXT(np);
 		if (np->nvp_devid == NULL)
 			continue;
 		if ((np->nvp_flags & NVP_DEVID_REGISTERED) == 0) {
 			DEVID_LOG_REMOVE((CE_CONT,
 				    "cleanup: %s\n", np->nvp_devpath));
 			NVF_MARK_DIRTY(dcfd);
-			next = nfd_devid_free_and_unlink(dcfd, np);
+			nfd_nvp_free_and_unlink(dcfd, NVPLIST(np));
 		}
 	}
 
 	rw_exit(&dcfd->nvf_lock);
 
 	if (NVF_IS_DIRTY(dcfd))
-		wake_nvpflush_daemon(dcfd);
+		wake_nvpflush_daemon();
 }
 
 
@@ -1229,7 +1393,7 @@
 e_devid_cache_devi_path_lists(ddi_devid_t devid, int retmax,
 	int *retndevis, dev_info_t **retdevis, int *retnpaths, char **retpaths)
 {
-	nvp_list_t *np;
+	nvp_devid_t *np;
 	int ndevis, npaths;
 	dev_info_t *dip, *pdip;
 	int circ;
@@ -1238,7 +1402,7 @@
 
 	ndevis = 0;
 	npaths = 0;
-	for (np = dcfd->nvf_list; np != NULL; np = np->nvp_next) {
+	for (np = NVF_DEVID_LIST(dcfd); np; np = NVP_DEVID_NEXT(np)) {
 		if (np->nvp_devid == NULL)
 			continue;
 		if (ddi_devid_valid(np->nvp_devid) != DDI_SUCCESS) {
@@ -1514,6 +1678,25 @@
 
 static void nvpflush_daemon(void);
 
+void
+nvf_register_write_complete(nvfd_t *fd, void (*f)(nvfd_t *))
+{
+	fd->nvf_write_complete = f;
+}
+
+void
+nvf_unregister_write_complete(nvfd_t *fd)
+{
+	fd->nvf_write_complete = NULL;
+}
+
+static void
+nvf_write_complete(nvfd_t *fd)
+{
+	if (fd->nvf_write_complete) {
+		(*(fd->nvf_write_complete))(fd);
+	}
+}
 
 void
 i_ddi_start_flush_daemon(void)
@@ -1523,8 +1706,9 @@
 	mutex_init(&nvpflush_lock, NULL, MUTEX_DRIVER, NULL);
 	cv_init(&nvpflush_cv, NULL, CV_DRIVER, NULL);
 
-	if (NVF_IS_DIRTY(dcfd)) {
-		wake_nvpflush_daemon(dcfd);
+	if ((NVF_IS_DIRTY(dcfd) && !devid_cache_write_disable) ||
+	    (NVF_IS_DIRTY(sdevfd) && !sdevfd && sdev_cache_write_disable)) {
+		wake_nvpflush_daemon();
 	}
 }
 
@@ -1542,6 +1726,7 @@
 		nvpflush_id = timeout(nvpflush_timeout, NULL, nticks);
 	} else {
 		do_nvpflush = 1;
+		NVPDAEMON_DEBUG((CE_CONT, "signal nvpdaemon\n"));
 		cv_signal(&nvpflush_cv);
 		nvpflush_id = 0;
 		nvpflush_timer_busy = 0;
@@ -1549,17 +1734,16 @@
 	}
 }
 
-static void
-wake_nvpflush_daemon(nvfd_t *nvfp)
+void
+wake_nvpflush_daemon()
 {
 	clock_t nticks;
 
 	/*
-	 * If root is readonly or the system isn't up yet
+	 * If the system isn't up yet
 	 * don't even think about starting a flush.
 	 */
-	if (devid_cache_write_disable ||
-	    !i_ddi_io_initialized() || NVF_IS_READONLY(nvfp))
+	if (!i_ddi_io_initialized())
 		return;
 
 	mutex_enter(&nvpflush_lock);
@@ -1603,7 +1787,7 @@
 		rw_exit(&nvfd->nvf_lock);
 		return (DDI_FAILURE);
 	}
-	if (nvp_to_nvlist(nvfd, &nvl) != DDI_SUCCESS) {
+	if (((nvfd->nvf_nvp2nvl)(nvfd, &nvl)) != DDI_SUCCESS) {
 		KFIOERR((CE_CONT, "nvpflush: "
 		    "%s nvlist construction failed\n", nvfd->nvf_name));
 		rw_exit(&nvfd->nvf_lock);
@@ -1654,12 +1838,14 @@
 	return (rval);
 }
 
+
 static void
 nvpflush_daemon(void)
 {
 	callb_cpr_t cprinfo;
 	clock_t clk;
 	int rval;
+	int i;
 
 	ASSERT(modrootloaded);
 
@@ -1701,21 +1887,40 @@
 		 * Try flushing what's dirty, reschedule if there's
 		 * a failure or data gets marked as dirty again.
 		 */
-		NVPDAEMON_DEBUG((CE_CONT, "nvpdaemon: flush\n"));
-		rval = nvpflush_one(dcfd);
+		for (i = 0; i < NCACHEFDS; i++) {
+			rw_enter(&cachefds[i]->nvf_lock, RW_READER);
+			if (NVF_IS_DIRTY(cachefds[i])) {
+				NVPDAEMON_DEBUG((CE_CONT,
+				    "nvpdaemon: flush %s\n",
+				    cachefds[i]->nvf_name));
+				rw_exit(&cachefds[i]->nvf_lock);
+				rval = nvpflush_one(cachefds[i]);
+				rw_enter(&cachefds[i]->nvf_lock, RW_READER);
+				if (rval != DDI_SUCCESS ||
+				    NVF_IS_DIRTY(cachefds[i])) {
+					rw_exit(&cachefds[i]->nvf_lock);
+					NVPDAEMON_DEBUG((CE_CONT,
+					    "nvpdaemon: %s dirty again\n",
+					    cachefds[i]->nvf_name));
+					wake_nvpflush_daemon();
+				} else {
+					rw_exit(&cachefds[i]->nvf_lock);
+					nvf_write_complete(cachefds[i]);
+				}
+			} else {
+				NVPDAEMON_DEBUG((CE_CONT,
+				    "nvpdaemon: not dirty %s\n",
+				    cachefds[i]->nvf_name));
+				rw_exit(&cachefds[i]->nvf_lock);
+			}
+		}
 
-		rw_enter(&dcfd->nvf_lock, RW_READER);
-		if (rval != DDI_SUCCESS || NVF_IS_DIRTY(dcfd)) {
-			rw_exit(&dcfd->nvf_lock);
-			NVPDAEMON_DEBUG((CE_CONT, "nvpdaemon: dirty again\n"));
-			wake_nvpflush_daemon(dcfd);
-		} else
-			rw_exit(&dcfd->nvf_lock);
 		mutex_enter(&nvpflush_lock);
 		nvpbusy = 0;
 	}
 }
 
+
 void
 i_ddi_clean_devices_files(void)
 {
--- a/usr/src/uts/common/os/modconf.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/os/modconf.c	Fri Aug 25 17:24:25 2006 -0700
@@ -57,6 +57,7 @@
 #include <sys/kcpc.h>
 #include <sys/cpc_pcbe.h>
 #include <sys/kstat.h>
+#include <sys/fs/sdev_node.h>
 
 extern int moddebug;
 
@@ -216,6 +217,17 @@
 };
 
 /*
+ * /dev fs modules
+ */
+static int mod_infodev(struct modldev *, struct modlinkage *, int *);
+static int mod_installdev(struct modldev *, struct modlinkage *);
+static int mod_removedev(struct modldev *, struct modlinkage *);
+
+struct mod_ops mod_devfsops = {
+	mod_installdev, mod_removedev, mod_infodev
+};
+
+/*
  * PCBE (Performance Counter BackEnd) modules.
  */
 static int mod_installpcbe(struct modlpcbe *, struct modlinkage *);
@@ -483,6 +495,41 @@
 	return (EBUSY);
 }
 
+/*
+ * manage /dev fs modules
+ */
+/*ARGSUSED*/
+static int
+mod_infodev(struct modldev *modl, struct modlinkage *modlp, int *p0)
+{
+	if (mod_getctl(modlp) == NULL) {
+		*p0 = -1;
+		return (0);	/* module is not yet installed */
+	}
+
+	*p0 = 0;
+	return (0);
+}
+
+static int
+mod_installdev(struct modldev *modl, struct modlinkage *modlp)
+{
+	struct modctl	*mcp;
+
+	if ((mcp = mod_getctl(modlp)) == NULL)
+		return (EINVAL);
+	return (sdev_module_register(mcp->mod_modname, modl->dev_ops));
+}
+
+/*
+ * /dev modules are not unloadable.
+ */
+/*ARGSUSED*/
+static int
+mod_removedev(struct modldev *modl, struct modlinkage *modlp)
+{
+	return (EBUSY);
+}
 
 /*
  * Install a new driver
--- a/usr/src/uts/common/os/modctl.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/os/modctl.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -79,6 +78,7 @@
 #include <ipp/ipp_impl.h>
 #include <sys/fs/dv_node.h>
 #include <sys/strsubr.h>
+#include <sys/fs/sdev_node.h>
 
 static int		mod_circdep(struct modctl *);
 static int		modinfo(modid_t, struct modinfo *);
@@ -679,7 +679,8 @@
 	return (1);	/* always reread driver.conf the first time */
 }
 
-static int modctl_load_drvconf(major_t major)
+static int
+modctl_load_drvconf(major_t major)
 {
 	int ret;
 
@@ -785,6 +786,8 @@
 	int retval;
 	major_t major;
 
+	if (ulen == 0)
+		return (EINVAL);
 	if ((retval = copyinstr(uname, name,
 	    (ulen < 256) ? ulen : 256, 0)) != 0)
 		return (retval);
@@ -1640,6 +1643,144 @@
 	return (error);
 }
 
+static int
+modctl_devexists(const char *upath, int pathlen)
+{
+	char	*path;
+	int	ret;
+
+	/*
+	 * copy in the path, including the terminating null
+	 */
+	pathlen++;
+	if (pathlen <= 1 || pathlen > MAXPATHLEN)
+		return (EINVAL);
+	path = kmem_zalloc(pathlen + 1, KM_SLEEP);
+	if ((ret = copyinstr(upath, path, pathlen, NULL)) == 0) {
+		ret = sdev_modctl_devexists(path);
+	}
+
+	kmem_free(path, pathlen + 1);
+	return (ret);
+}
+
+static int
+modctl_devreaddir(const char *udir, int udirlen,
+    char *upaths, int64_t *ulensp)
+{
+	char	*paths = NULL;
+	char	**dirlist = NULL;
+	char	*dir;
+	int64_t	ulens;
+	int64_t	lens;
+	int	i, n;
+	int	ret = 0;
+	char	*p;
+	int	npaths;
+	int	npaths_alloc;
+
+	/*
+	 * If upaths is NULL then we are only computing the amount of space
+	 * needed to return the paths, with the value returned in *ulensp. If we
+	 * are copying out paths then we get the amount of space allocated by
+	 * the caller. If the actual space needed for paths is larger, or
+	 * things are changing out from under us, then we return EAGAIN.
+	 */
+	if (upaths) {
+		if (ulensp == NULL)
+			return (EINVAL);
+		if (copyin(ulensp, &ulens, sizeof (ulens)) != 0)
+			return (EFAULT);
+	}
+
+	/*
+	 * copyin the /dev path including terminating null
+	 */
+	udirlen++;
+	if (udirlen <= 1 || udirlen > MAXPATHLEN)
+		return (EINVAL);
+	dir = kmem_zalloc(udirlen + 1, KM_SLEEP);
+	if ((ret = copyinstr(udir, dir, udirlen, NULL)) != 0)
+		goto err;
+
+	if ((ret = sdev_modctl_readdir(dir, &dirlist,
+	    &npaths, &npaths_alloc)) != 0) {
+		ASSERT(dirlist == NULL);
+		goto err;
+	}
+
+	lens = 0;
+	for (i = 0; i < npaths; i++) {
+		lens += strlen(dirlist[i]) + 1;
+	}
+	lens++;		/* add one for double termination */
+
+	if (upaths) {
+		if (lens > ulens) {
+			ret = EAGAIN;
+			goto out;
+		}
+
+		paths = kmem_alloc(lens, KM_SLEEP);
+
+		p = paths;
+		for (i = 0; i < npaths; i++) {
+			n = strlen(dirlist[i]) + 1;
+			bcopy(dirlist[i], p, n);
+			p += n;
+		}
+		*p = 0;
+
+		if (copyout(paths, upaths, lens)) {
+			ret = EFAULT;
+			goto err;
+		}
+	}
+
+out:
+	/* copy out the amount of space needed to hold the paths */
+	if (copyout(&lens, ulensp, sizeof (lens)))
+		ret = EFAULT;
+
+err:
+	if (dirlist)
+		sdev_modctl_readdir_free(dirlist, npaths, npaths_alloc);
+	if (paths)
+		kmem_free(paths, lens);
+	kmem_free(dir, udirlen + 1);
+	return (ret);
+}
+
+int
+modctl_moddevname(int subcmd, uintptr_t a1, uintptr_t a2)
+{
+	int error = 0;
+
+	switch (subcmd) {
+	case MODDEVNAME_LOOKUPDOOR:
+	case MODDEVNAME_DEVFSADMNODE:
+		error = devname_filename_register(subcmd, (char *)a1);
+		break;
+	case MODDEVNAME_NSMAPS:
+		error = devname_nsmaps_register((char *)a1, (size_t)a2);
+		break;
+	case MODDEVNAME_PROFILE:
+		error = devname_profile_update((char *)a1, (size_t)a2);
+		break;
+	case MODDEVNAME_RECONFIG:
+		i_ddi_set_reconfig();
+		break;
+	case MODDEVNAME_SYSAVAIL:
+		i_ddi_set_sysavail();
+		break;
+	default:
+		error = EINVAL;
+		break;
+	}
+
+	return (error);
+}
+
 /*ARGSUSED5*/
 int
 modctl(int cmd, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4,
@@ -1845,6 +1986,19 @@
 		error = modctl_remdrv_cleanup((const char *)a1);
 		break;
 
+	case MODDEVEXISTS:	/* non-reconfiguring /dev lookup */
+		error = modctl_devexists((const char *)a1, (size_t)a2);
+		break;
+
+	case MODDEVREADDIR:	/* non-reconfiguring /dev readdir */
+		error = modctl_devreaddir((const char *)a1, (size_t)a2,
+		    (char *)a3, (int64_t *)a4);
+		break;
+
+	case MODDEVNAME:
+		error = modctl_moddevname((int)a1, a2, a3);
+		break;
+
 	default:
 		error = EINVAL;
 		break;
@@ -3626,7 +3780,7 @@
 	if (c == 0) \
 		return (0);
 
-static int
+int
 gmatch(const char *s, const char *p)
 {
 	int c, sc;
--- a/usr/src/uts/common/os/vfs_conf.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/os/vfs_conf.c	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -79,6 +78,7 @@
 	{ "swapfs", swapinit },			/* SWAPFS */
 	{ "mntfs" },				/* MNTFS */
 	{ "devfs" },				/* DEVFS */
+	{ "dev" },				/* DEV */
 	{ "ctfs" },				/* CONTRACTFS */
 	{ "objfs" },				/* OBJFS */
 	{ "" },					/* reserved for loadable fs */
--- a/usr/src/uts/common/sys/Makefile	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/sys/Makefile	Fri Aug 25 17:24:25 2006 -0700
@@ -682,6 +682,8 @@
 	cachefs_ioctl.h		\
 	cachefs_log.h		\
 	dv_node.h		\
+	sdev_impl.h		\
+	sdev_node.h		\
 	fifonode.h		\
 	hsfs_isospec.h		\
 	hsfs_node.h		\
--- a/usr/src/uts/common/sys/autoconf.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/sys/autoconf.h	Fri Aug 25 17:24:25 2006 -0700
@@ -164,6 +164,7 @@
 
 extern struct di_cache di_cache;
 extern int di_cache_debug;
+extern volatile ulong_t devtree_gen;
 
 /*
  * Special dev_info nodes
@@ -245,6 +246,15 @@
 extern void i_ddi_di_cache_invalidate(int kmflag);
 extern void i_ddi_di_cache_free(struct di_cache *cache);
 
+/* devname_state - for /dev to denote reconfig and system available */
+#define	DS_RECONFIG	0x01		/* reconfig boot */
+#define	DS_SYSAVAIL	0x02		/* implicit reconfig enabled */
+
+extern int i_ddi_sysavail(void);
+extern int i_ddi_reconfig(void);
+extern void i_ddi_set_sysavail(void);
+extern void i_ddi_set_reconfig(void);
+
 #endif /* _KERNEL */
 
 #ifdef	__cplusplus
--- a/usr/src/uts/common/sys/devctl_impl.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/sys/devctl_impl.h	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -65,26 +64,57 @@
  * The top-level nvpair identifiers in the
  * /etc/devices/devid_cache nvlist format
  */
-#define	DP_DEVID_ID		"devid"
+#define	DP_DEVID_ID			"devid"
+#define	DP_DEVNAME_ID			"devname"
+#define	DP_DEVNAME_NCACHE_ID		"ncache"
+#define	DP_DEVNAME_NC_EXPIRECNT_ID	"expire-counts"
 
 
+#ifdef	_KERNEL
+
+/* common header for all formats */
 typedef struct nvp_list {
-	char		*nvp_devpath;
-	int		nvp_flags;
-	dev_info_t	*nvp_dip;
-	ddi_devid_t	nvp_devid;
 	struct nvp_list	*nvp_next;
 	struct nvp_list	*nvp_prev;
 } nvp_list_t;
 
+#define	NVPLIST(p)	(((nvp_list_t *)(p)))
+
+
+/* devid-specific list */
+typedef struct nvp_devid {
+	nvp_list_t	nvp_list;	/* must be first element */
+	int		nvp_flags;
+	char		*nvp_devpath;
+	dev_info_t	*nvp_dip;
+	ddi_devid_t	nvp_devid;
+} nvp_devid_t;
+
+#define	NVP2DEVID(p)		(((nvp_devid_t *)(p)))
+#define	NVP_DEVID_NEXT(p)	(NVP2DEVID((NVPLIST(p))->nvp_next))
+
 /*
- * nvp_flags
+ * nvp_flags - devid
  */
 #define	NVP_DEVID_REGISTERED	0x01	/* devid registered on this boot */
 #define	NVP_DEVID_DIP		0x02	/* devinfo valid for this devid */
 
 
-#ifdef	_KERNEL
+/* devname-specific list */
+typedef struct nvp_devname {
+	nvp_list_t	nvp_list;	/* must be first element */
+	char		**nvp_paths;
+	int		*nvp_expirecnts;
+	int		nvp_npaths;
+} nvp_devname_t;
+
+#define	NVP2DEVNAME(p)		(((nvp_devname_t *)(p)))
+#define	NVP_DEVNAME_NEXT(p)	(NVP2DEVNAME((NVPLIST(p))->nvp_next))
+
+/*
+ * nvp_flags - devname
+ */
+
 
 /*
  * Descriptor used for kernel-level file i/o
@@ -102,12 +132,20 @@
  */
 typedef struct nvfiledesc {
 	char		*nvf_name;
+	int		(*nvf_nvp2nvl)(struct nvfiledesc *, nvlist_t **);
+	nvp_list_t	*(*nvf_nvl2nvp)(nvlist_t *, char *name);
+	void		(*nvf_nvp_free)(nvp_list_t *);
+	void		(*nvf_write_complete)(struct nvfiledesc *);
 	int		nvf_flags;
 	nvp_list_t	*nvf_list;
 	nvp_list_t	*nvf_tail;
 	krwlock_t	nvf_lock;
 } nvfd_t;
 
+#define	NVF_DEVID_LIST(p)	((nvp_devid_t *)((p)->nvf_list))
+#define	NVF_DEVID_TAIL(p)	((nvp_devid_t *)((p)->nvf_tail))
+#define	NVF_DEVNAME_LIST(p)	((nvp_devname_t *)((p)->nvf_list))
+#define	NVF_DEVNAME_TAIL(p)	((nvp_devname_t *)((p)->nvf_tail))
 
 /*
  * Discovery refers to the heroic effort made to discover a device which
@@ -305,7 +343,24 @@
 #define	DEVIDERR(args)		{ if (devid_report_error) cmn_err args; }
 
 
-static void wake_nvpflush_daemon(nvfd_t *);
+/* extern data */
+
+extern nvfd_t	*sdevfd;
+
+extern int devid_cache_read_disable;
+extern int devid_cache_write_disable;
+extern int sdev_cache_read_disable;
+extern int sdev_cache_write_disable;
+
+/* extern prototypes */
+
+void i_ddi_read_devname_file(void);
+void nvf_register_write_complete(nvfd_t *, void (*f)(nvfd_t *));
+void nvf_unregister_write_complete(nvfd_t *);
+void nfd_nvp_link(nvfd_t *, nvp_list_t *);
+void nfd_nvp_free_and_unlink(nvfd_t *, nvp_list_t *);
+void wake_nvpflush_daemon(void);
+
 
 #endif	/* _KERNEL */
 
--- a/usr/src/uts/common/sys/fs/dv_node.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/sys/fs/dv_node.h	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -43,6 +42,7 @@
 #include <sys/sunddi.h>
 #include <sys/devops.h>
 #include <sys/ddi_impldefs.h>
+#include <sys/fs/sdev_node.h>
 #include <sys/devpolicy.h>
 
 #ifdef __cplusplus
@@ -82,6 +82,7 @@
 	uint_t		dv_busy;	/* directory busy count */
 	devplcy_t	*dv_priv;	/* access privilege */
 	mode_t		dv_dflt_mode;	/* create_priv_minor_node mode */
+	struct sdev_dv	*dv_sdev;	/* sdev node[s] if exists */
 } dvnode_t;
 
 #define	DV_BUILD	0x1		/* directory out-of-date */
@@ -176,6 +177,7 @@
 extern int devfs_lookupname(char *, vnode_t **, vnode_t **);
 extern int devfs_walk(char *, void (*f)(struct dv_node *, void *), void *);
 extern int devfs_devpolicy(vnode_t *, devplcy_t **);
+extern void devfs_get_defattr(vnode_t *, struct vattr *, int *);
 
 extern struct dv_node *devfs_dip_to_dvnode(dev_info_t *);
 extern int devfs_reset_perm(uint_t);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/fs/sdev_impl.h	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,699 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_SDEV_IMPL_H
+#define	_SYS_SDEV_IMPL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rpc/rpc.h>
+#include <sys/dirent.h>
+#include <sys/vfs.h>
+#include <sys/list.h>
+#include <sys/nvpair.h>
+
+/*
+ * sdev_nodes are the file-system specific part of the
+ * vnodes for the device filesystem.
+ *
+ * The device filesystem exports two node types:
+ *
+ * VDIR nodes		to represent directories
+ * VCHR & VBLK nodes	to represent devices
+ */
+
+/*
+ * /dev mount arguments
+ */
+struct sdev_mountargs {
+	uint64_t sdev_attrdir;
+};
+
+
+/*
+ * Nvpair names of profile information (list of device files available) of
+ * non-global /dev mounts.  These strings must be unique among them.
+ */
+#define	SDEV_NVNAME_MOUNTPT	"prof_mountpt"
+#define	SDEV_NVNAME_INCLUDE	"prof_include"
+#define	SDEV_NVNAME_EXCLUDE	"prof_exclude"
+#define	SDEV_NVNAME_SYMLINK	"prof_symlink"
+#define	SDEV_NVNAME_MAP		"prof_map"
+
+/*
+ * supported devfsadm_cmd
+ */
+#define	DEVFSADMD_RUN_ALL	1
+#define	DEVFSADMD_NS_LOOKUP	2
+#define	DEVFSADMD_NS_READDIR	3
+
+/*
+ * supported protocols
+ */
+typedef enum devname_spec {
+	DEVNAME_NS_NONE = 0,
+	DEVNAME_NS_PATH,	/* physical /devices path */
+	DEVNAME_NS_DEV		/* /dev path */
+} devname_spec_t;
+
+/*
+ * devfsadm_error codes
+ */
+#define	DEVFSADM_RUN_INVALID		1
+#define	DEVFSADM_RUN_EPERM		2
+#define	DEVFSADM_RUN_NOTSUP		3
+#define	DEVFSADM_NS_FAILED		4
+
+typedef struct sdev_ns_handle {
+	char	ns_name[MAXPATHLEN];	/* device to be looked up */
+	char	ns_map[MAXPATHLEN];
+} sdev_ns_handle_t;
+
+typedef struct sdev_lkp_handle {
+	devname_spec_t	devfsadm_spec;	/* returned path property */
+	char	devfsadm_link[MAXPATHLEN]; /* symlink destination */
+} sdev_lkp_handle_t;
+
+typedef struct sdev_rdr_handle {
+	uint32_t ns_mapcount;	/* number of entries in the map */
+	uint32_t maplist_size;
+} sdev_rdr_handle_t;
+
+/*
+ * devfsadm/devname door data structures
+ */
+typedef struct sdev_door_arg {
+	uint8_t devfsadm_cmd;	/* what to do for devfsadm[d] */
+	sdev_ns_handle_t ns_hdl;
+} sdev_door_arg_t;
+
+typedef struct sdev_door_res {
+	int32_t devfsadm_error;
+	sdev_lkp_handle_t ns_lkp_hdl;
+	sdev_rdr_handle_t ns_rdr_hdl;
+} sdev_door_res_t;
+
+#ifdef _KERNEL
+
+struct sdev_dprof {
+	int has_glob;
+	nvlist_t *dev_name;
+	nvlist_t *dev_map;
+	nvlist_t *dev_symlink;
+	nvlist_t *dev_glob_incdir;
+	nvlist_t *dev_glob_excdir;
+};
+
+/*
+ * devname_handle_t
+ */
+struct devname_handle {
+	struct sdev_node *dh_data;	/* the sdev_node */
+	devname_spec_t dh_spec;
+	void    *dh_args;
+};
+typedef struct devname_handle devname_handle_t;
+
+/*
+ * Per-instance node data for the global zone instance
+ * Only one mount of /dev in the global zone
+ */
+typedef struct sdev_global_data {
+	struct devname_handle sdev_ghandle;
+	struct devname_nsmap *sdev_gmapinfo;	/* VDIR name service info */
+	ulong_t		sdev_dir_ggen;		/* name space generation # */
+} sdev_global_data_t;
+
+/*
+ * Per-instance node data - profile data per non-global zone mount instance
+ */
+typedef struct sdev_local_data {
+	ulong_t sdev_dir_lgen;		/* cached generation # of /dev dir */
+	ulong_t sdev_devtree_lgen;	/* cached generation # of devtree */
+	struct sdev_node *sdev_lorigin;	/* corresponding global sdev_node */
+	struct sdev_dprof sdev_lprof;	/* profile for multi-inst */
+} sdev_local_data_t;
+
+/*
+ * /dev filesystem sdev_node defines
+ */
+typedef struct sdev_node {
+	char		*sdev_name;	/* node name */
+	size_t		sdev_namelen;	/* strlen(sdev_name) */
+	char		*sdev_path;	/* absolute path */
+	char		*sdev_symlink;	/* source for a symlink */
+	struct vnode	*sdev_vnode;	/* vnode */
+
+	krwlock_t	sdev_contents;	/* rw lock for this data structure */
+	struct sdev_node *sdev_dotdot;	/* parent */
+	struct sdev_node *sdev_dot;	/* child: VDIR: head of children */
+	struct sdev_node *sdev_next;	/* sibling: next in this directory */
+
+	struct vnode	*sdev_attrvp;	/* backing store vnode if persisted */
+	struct vattr	*sdev_attr;	/* memory copy of the vattr */
+
+	ino64_t		sdev_ino;	/* inode */
+	uint_t		sdev_nlink;	/* link count */
+	int		sdev_state;	/* state of this node */
+	int		sdev_flags;	/* flags bit */
+
+	kmutex_t	sdev_lookup_lock; /* node creation synch lock */
+	kcondvar_t	sdev_lookup_cv;	/* node creation sync cv */
+	int		sdev_lookup_flags; /* node creation flags */
+
+	/* per-instance data, either global or non-global zone */
+	union {
+		struct sdev_global_data	sdev_globaldata;
+		struct sdev_local_data	sdev_localdata;
+	} sdev_instance_data;
+} sdev_node_t;
+
+#define	sdev_ldata sdev_instance_data.sdev_localdata
+#define	sdev_gdata sdev_instance_data.sdev_globaldata
+
+#define	sdev_handle		sdev_gdata.sdev_ghandle
+#define	sdev_mapinfo		sdev_gdata.sdev_gmapinfo
+#define	sdev_gdir_gen		sdev_gdata.sdev_dir_ggen
+
+#define	sdev_ldir_gen		sdev_ldata.sdev_dir_lgen
+#define	sdev_devtree_gen	sdev_ldata.sdev_devtree_lgen
+#define	sdev_origin		sdev_ldata.sdev_lorigin
+#define	sdev_prof		sdev_ldata.sdev_lprof
+
+/*
+ * sdev_state
+ *
+ * A sdev_node may go through 3 states:
+ *	SDEV_INIT: When a new /dev file is first looked up, a sdev_node
+ *		   is allocated, initialized and added to the directory's
+ *		   sdev_node cache. A node at this state will also
+ *		   have the SDEV_LOOKUP flag set.
+ *
+ *		   Other threads that are trying to look up a node at
+ *		   this state will be blocked until the SDEV_LOOKUP flag
+ *		   is cleared.
+ *
+ *		   When the SDEV_LOOKUP flag is cleared, the node may
+ *		   transition into the SDEV_READY state for a successful
+ *		   lookup or the node is removed from the directory cache
+ *		   and destroyed if the named node can not be found.
+ *		   An ENOENT error is returned for the second case.
+ *
+ *	SDEV_READY: A /dev file has been successfully looked up and
+ *		    associated with a vnode. The /dev file is available
+ *		    for the supported /dev filesystem operations.
+ *
+ *	SDEV_ZOMBIE: Deletion of a /dev file has been explicitely issued
+ *		    to an SDEV_READY node. The node is transitioned into
+ *		    the SDEV_ZOMBIE state if the vnode reference count
+ *		    is still held. A SDEV_ZOMBIE node does not support
+ *		    any of the /dev filesystem operations. A SDEV_ZOMBIE
+ *		    node is removed from the directory cache and destroyed
+ *		    once the reference count reaches "zero".
+ */
+typedef enum {
+	SDEV_ZOMBIE = -1,
+	SDEV_INIT = 0,
+	SDEV_READY
+} sdev_node_state_t;
+
+/* sdev_flags */
+#define	SDEV_BUILD	0x0001	/* directory cache out-of-date */
+#define	SDEV_STALE	0x0002	/* stale sdev nodes */
+#define	SDEV_GLOBAL	0x0004	/* global /dev nodes */
+#define	SDEV_PERSIST	0x0008	/* backing store persisted node */
+#define	SDEV_NO_NCACHE	0x0010	/* do not include in neg. cache */
+#define	SDEV_DYNAMIC	0x0020	/* special-purpose vnode ops (ex: pts) */
+#define	SDEV_VTOR	0x0040	/* validate sdev_nodes during search */
+
+/* sdev_lookup_flags */
+#define	SDEV_LOOKUP	0x0001	/* node creation in progress */
+#define	SDEV_READDIR	0x0002	/* VDIR readdir in progress */
+#define	SDEV_LGWAITING	0x0004	/* waiting for devfsadm completion */
+
+#define	SDEV_VTOR_INVALID	-1
+#define	SDEV_VTOR_SKIP		0
+#define	SDEV_VTOR_VALID		1
+
+/* convenient macros */
+#define	SDEV_IS_GLOBAL(dv)	\
+	(dv->sdev_flags & SDEV_GLOBAL)
+#define	SDEV_IS_PERSIST(dv)	\
+	(dv->sdev_flags & SDEV_PERSIST)
+#define	SDEV_IS_DYNAMIC(dv)	\
+	(dv->sdev_flags & SDEV_DYNAMIC)
+#define	SDEV_IS_NO_NCACHE(dv)	\
+	(dv->sdev_flags & SDEV_NO_NCACHE)
+
+#define	SDEV_IS_LOOKUP(dv)	\
+	(dv->sdev_lookup_flags & SDEV_LOOKUP)
+#define	SDEV_IS_READDIR(dv)	\
+	(dv->sdev_lookup_flags & SDEV_READDIR)
+#define	SDEV_IS_LGWAITING(dv)	\
+	(dv->sdev_lookup_flags  & SDEV_LGWAITING)
+
+#define	SDEVTOV(n)	((struct vnode *)(n)->sdev_vnode)
+#define	VTOSDEV(vp)	((struct sdev_node *)(vp)->v_data)
+#define	VN_HELD(v)	((v)->v_count != 0)
+#define	SDEV_HELD(dv)	(VN_HELD(SDEVTOV(dv)))
+#define	SDEV_HOLD(dv)	VN_HOLD(SDEVTOV(dv))
+#define	SDEV_RELE(dv)	VN_RELE(SDEVTOV(dv))
+#define	SDEV_SIMPLE_RELE(dv)	{	\
+	mutex_enter(&SDEVTOV(dv)->v_lock);	\
+	SDEVTOV(dv)->v_count--;	\
+	mutex_exit(&SDEVTOV(dv)->v_lock);	\
+}
+
+#define	SDEV_ACL_FLAVOR(vp)	(VFSTOSDEVFS(vp->v_vfsp)->sdev_acl_flavor)
+
+/*
+ * some defaults
+ */
+#define	SDEV_ROOTINO		((ino_t)2)
+#define	SDEV_UID_DEFAULT	(0)
+#define	SDEV_GID_DEFAULT	(3)
+#define	SDEV_DIRMODE_DEFAULT	(S_IFDIR |0755)
+#define	SDEV_DEVMODE_DEFAULT	(0600)
+#define	SDEV_LNKMODE_DEFAULT	(S_IFLNK | 0777)
+
+extern struct vattr sdev_vattr_dir;
+extern struct vattr sdev_vattr_lnk;
+extern struct vattr sdev_vattr_blk;
+extern struct vattr sdev_vattr_chr;
+
+/*
+ * devname_lookup_func()
+ */
+extern int devname_lookup_func(struct sdev_node *, char *, struct vnode **,
+    struct cred *, int (*)(struct sdev_node *, char *, void **, struct cred *,
+    void *, char *), int);
+
+/*
+ * flags used by devname_lookup_func callbacks
+ */
+#define	SDEV_PATH	0x1	/* callback returning /devices physical path */
+#define	SDEV_VNODE	0x2	/* callback returning backing store vnode */
+#define	SDEV_VATTR	0x4	/* callback returning node vattr */
+
+/*
+ * devname_readdir_func()
+ */
+extern int devname_readdir_func(vnode_t *, uio_t *, cred_t *, int *, int);
+
+/*
+ * flags for devname_readdir_func
+ */
+#define	SDEV_BROWSE	0x1	/* fetch all entries from backing store */
+
+/*
+ * devname_setattr_func()
+ */
+extern int devname_setattr_func(struct vnode *, struct vattr *, int,
+    struct cred *, int (*)(struct sdev_node *, struct vattr *, int), int);
+
+/*
+ * /dev file system instance defines
+ */
+/*
+ * /dev version of vfs_data
+ */
+struct sdev_data {
+	struct sdev_data	*sdev_prev;
+	struct sdev_data	*sdev_next;
+	struct sdev_node	*sdev_root;
+	struct vfs		*sdev_vfsp;
+	struct sdev_mountargs	*sdev_mountargs;
+	ulong_t			sdev_acl_flavor;
+};
+
+#define	VFSTOSDEVFS(vfsp)	((struct sdev_data *)((vfsp)->vfs_data))
+
+/*
+ * sdev_fid overlays the fid structure (for VFS_VGET)
+ */
+struct sdev_fid {
+	uint16_t	sdevfid_len;
+	ino32_t		sdevfid_ino;
+	int32_t		sdevfid_gen;
+};
+
+/*
+ * devfsadm and devname communication defines
+ */
+typedef enum {
+	DEVNAME_DEVFSADM_STOPPED = 0,	/* devfsadm has never run */
+	DEVNAME_DEVFSADM_RUNNING,	/* devfsadm is running */
+	DEVNAME_DEVFSADM_RUN		/* devfsadm ran once */
+} devname_devfsadm_state_t;
+
+extern volatile uint_t  devfsadm_state; /* atomic mask for devfsadm status */
+
+#define	DEVNAME_DEVFSADM_SET_RUNNING(devfsadm_state)	\
+	devfsadm_state = DEVNAME_DEVFSADM_RUNNING
+#define	DEVNAME_DEVFSADM_SET_STOP(devfsadm_state)	\
+	devfsadm_state = DEVNAME_DEVFSADM_STOPPED
+#define	DEVNAME_DEVFSADM_SET_RUN(devfsadm_state)	\
+	devfsadm_state = DEVNAME_DEVFSADM_RUN
+#define	DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state)	\
+	devfsadm_state == DEVNAME_DEVFSADM_RUNNING
+#define	DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state)	\
+	(devfsadm_state == DEVNAME_DEVFSADM_RUN)
+
+#define	SDEV_BLOCK_OTHERS(dv, cmd)	{	\
+	ASSERT(MUTEX_HELD(&dv->sdev_lookup_lock));	\
+	dv->sdev_lookup_flags |= cmd;			\
+}
+extern void sdev_unblock_others(struct sdev_node *, uint_t);
+#define	SDEV_UNBLOCK_OTHERS(dv, cmd)	{	\
+	sdev_unblock_others(dv, cmd);		\
+}
+
+#define	SDEV_CLEAR_LOOKUP_FLAGS(dv, cmd)	{	\
+	dv->sdev_lookup_flags &= ~cmd;	\
+}
+
+extern int sdev_wait4lookup(struct sdev_node *, int);
+extern int devname_filename_register(int, char *);
+extern int devname_nsmaps_register(char *, size_t);
+extern void sdev_devfsadm_lockinit(void);
+extern void sdev_devfsadm_lockdestroy(void);
+extern void devname_add_devfsadm_node(char *);
+extern void sdev_devfsadmd_thread(struct sdev_node *, struct sdev_node *,
+    struct cred *);
+extern int devname_profile_update(char *, size_t);
+extern struct sdev_data *sdev_find_mntinfo(char *);
+void sdev_mntinfo_rele(struct sdev_data *);
+extern struct vnodeops *devpts_getvnodeops(void);
+
+/*
+ * Directory Based Device Naming (DBNR) defines
+ */
+
+#define	ETC_DEV_DIR		"/etc/dev"
+#define	DEVNAME_NSCONFIG	"sdev_nsconfig_mod"
+
+/*
+ * directory name rule
+ */
+struct devname_nsmap {
+	struct devname_nsmap	*prev;	/* previous entry */
+	struct devname_nsmap	*next;	/* next entry */
+	char	*dir_name;	/* /dev subdir name, e.g. /dev/disk */
+	char	*dir_module;	/* devname module impl the operations */
+	char	*dir_map;	/* dev naming rules, e.g. /etc/dev/disks */
+	struct devname_ops *dir_ops;	/* directory specific vnode ops */
+	char    *dir_newmodule; /* to be reloaded  */
+	char    *dir_newmap;    /* to be reloaded */
+	int	dir_invalid;    /* map entry obsolete */
+	int	dir_maploaded;	/* map contents read */
+	krwlock_t dir_lock;	/* protects the data structure */
+};
+
+/*
+ * name-property pairs to be looked up
+ */
+typedef struct devname_lkp_arg {
+	char *devname_dir;	/* the directory to look */
+	char *devname_name;	/* the device name to be looked up */
+	char *devname_map;	/* the directory device naming map */
+	int reserved;
+} devname_lkp_arg_t;
+
+/*
+ * name-value-property restured
+ */
+typedef struct devname_lkp_result {
+	devname_spec_t	devname_spec;	/* link to /devices or /dev */
+	char	*devname_link;		/* the source path */
+	int	reserved;
+} devname_lkp_result_t;
+
+/*
+ * directory name-value populating results
+ */
+typedef struct devname_rdr_result {
+	uint32_t	ns_mapcount;
+} devname_rdr_result_t;
+
+/*
+ * sdev_nsrdr work
+ */
+typedef struct sdev_nsrdr_work {
+	char *dir_name;
+	char *dir_map;
+	struct sdev_node *dir_dv;
+	devname_rdr_result_t **result;
+	struct sdev_nsrdr_work *next;
+} sdev_nsrdr_work_t;
+
+
+/*
+ * boot states - warning, the ordering here is significant
+ *
+ * the difference between "system available" and "boot complete"
+ * is a debounce timeout to catch some daemon issuing a readdir
+ * triggering a nuisance implict reconfig on each boot.
+ */
+#define	SDEV_BOOT_STATE_INITIAL		0
+#define	SDEV_BOOT_STATE_RECONFIG	1	/* reconfig */
+#define	SDEV_BOOT_STATE_SYSAVAIL	2	/* system available */
+#define	SDEV_BOOT_STATE_COMPLETE	3	/* boot complete */
+
+/*
+ * Negative cache list and list element
+ * The mutex protects the flags against multiple accesses and
+ * must only be acquired when already holding the r/w lock.
+ */
+typedef struct sdev_nc_list {
+	list_t		ncl_list;	/* the list itself */
+	kmutex_t	ncl_mutex;	/* protects ncl_flags */
+	krwlock_t	ncl_lock;	/* protects ncl_list */
+	int		ncl_flags;
+	int		ncl_nentries;
+} sdev_nc_list_t;
+
+typedef struct sdev_nc_node {
+	char		*ncn_name;	/* name of the node */
+	int		ncn_flags;	/* state information */
+	int		ncn_expirecnt;	/* remove once expired */
+	list_node_t	ncn_link;	/* link to next in list */
+} sdev_nc_node_t;
+
+/* ncl_flags */
+#define	NCL_LIST_DIRTY		0x01	/* needs to be flushed */
+#define	NCL_LIST_WRITING	0x02	/* write in progress */
+#define	NCL_LIST_WENABLE	0x04	/* write-enabled post boot */
+
+/* ncn_flags */
+#define	NCN_ACTIVE	0x01	/* a lookup has occurred */
+#define	NCN_SRC_STORE	0x02	/* src: persistent store */
+#define	NCN_SRC_CURRENT	0x04	/* src: current boot */
+
+/* sdev_lookup_failed flags */
+#define	SLF_NO_NCACHE	0x01	/* node should not be added to ncache */
+#define	SLF_REBUILT	0x02	/* reconfig performed during lookup attempt */
+
+/*
+ * name service globals and prototypes
+ */
+
+extern struct devname_ops *devname_ns_ops;
+extern int devname_nsmaps_loaded;
+extern kmutex_t devname_nsmaps_lock;
+
+extern void sdev_invalidate_nsmaps(void);
+extern void sdev_validate_nsmaps(void);
+extern int sdev_module_register(char *, struct devname_ops *);
+extern struct devname_nsmap *sdev_get_nsmap_by_dir(char *, int);
+extern struct devname_nsmap *sdev_get_nsmap_by_module(char *);
+extern void sdev_dispatch_to_nsrdr_thread(struct sdev_node *, char *,
+    devname_rdr_result_t *);
+extern void sdev_insert_nsmap(char *, char *, char *);
+extern int devname_nsmap_lookup(devname_lkp_arg_t *, devname_lkp_result_t **);
+extern struct devname_nsmap *sdev_get_map(struct sdev_node *, int);
+extern int sdev_nsmaps_loaded(void);
+extern void sdev_replace_nsmap(struct devname_nsmap *, char *, char *);
+extern int sdev_nsmaps_reloaded(void);
+extern int devname_get_dir_nsmap(devname_handle_t *, struct devname_nsmap **);
+
+/*
+ * vnodeops and vfsops helpers
+ */
+
+typedef enum {
+	SDEV_CACHE_ADD = 0,
+	SDEV_CACHE_DELETE
+} sdev_cache_ops_t;
+
+extern struct sdev_node *sdev_cache_lookup(struct sdev_node *, char *);
+extern int sdev_cache_update(struct sdev_node *, struct sdev_node **, char *,
+    sdev_cache_ops_t);
+extern void sdev_node_cache_init(void);
+extern void sdev_node_cache_fini(void);
+extern struct sdev_node *sdev_mkroot(struct vfs *, dev_t, struct vnode *,
+    struct vnode *, struct cred *);
+extern int sdev_mknode(struct sdev_node *, char *, struct sdev_node **,
+    struct vattr *, struct vnode *, void *, struct cred *, sdev_node_state_t);
+extern int sdev_nodeinit(struct sdev_node *, char *, struct sdev_node **,
+    vattr_t *);
+extern int sdev_nodeready(struct sdev_node *, vattr_t *, vnode_t *, void *,
+    cred_t *);
+extern int sdev_shadow_node(struct sdev_node *, struct cred *);
+extern void sdev_nodedestroy(struct sdev_node *, uint_t);
+extern void sdev_update_timestamps(struct vnode *, cred_t *, uint_t);
+extern void sdev_vattr_merge(struct sdev_node *, struct vattr *);
+extern void sdev_devstate_change(void);
+extern int sdev_lookup_filter(sdev_node_t *, char *);
+extern void sdev_lookup_failed(sdev_node_t *, char *, int);
+extern int sdev_unlocked_access(void *, int, struct cred *);
+
+#define	SDEV_ENFORCE	0x1
+extern void sdev_stale(struct sdev_node *);
+extern int sdev_cleandir(struct sdev_node *, char *, uint_t);
+extern int sdev_rnmnode(struct sdev_node *, struct sdev_node *,
+    struct sdev_node *, struct sdev_node **, char *, struct cred *);
+extern size_t add_dir_entry(dirent64_t *, char *, size_t, ino_t, offset_t);
+extern int sdev_to_vp(struct sdev_node *, struct vnode **);
+extern ino_t sdev_mkino(struct sdev_node *);
+extern int devname_backstore_lookup(struct sdev_node *, char *,
+    struct vnode **);
+extern int sdev_is_devfs_node(char *);
+extern int sdev_copyin_mountargs(struct mounta *, struct sdev_mountargs *);
+extern int sdev_reserve_subdirs(struct sdev_node *);
+extern int prof_lookup();
+extern void prof_filldir(struct sdev_node *);
+extern int devpts_validate(struct sdev_node *dv);
+extern void *sdev_get_vtor(struct sdev_node *dv);
+
+/*
+ * devinfo helpers
+ */
+extern int sdev_modctl_readdir(const char *, char ***, int *, int *);
+extern void sdev_modctl_readdir_free(char **, int, int);
+extern int sdev_modctl_devexists(const char *);
+
+
+/*
+ * ncache handlers
+ */
+
+extern void sdev_ncache_init(void);
+extern void sdev_ncache_setup(void);
+extern void sdev_ncache_teardown(void);
+extern void sdev_nc_addname(sdev_nc_list_t *, sdev_node_t *, char *, int);
+extern void sdev_nc_node_exists(sdev_node_t *);
+extern void sdev_nc_path_exists(sdev_nc_list_t *, char *);
+extern void sdev_modctl_dump_files(void);
+
+/*
+ * globals
+ */
+extern int devtype;
+extern kmem_cache_t *sdev_node_cache;
+extern struct vnodeops		*sdev_vnodeops;
+extern struct vnodeops		*devpts_vnodeops;
+extern struct sdev_data *sdev_origins; /* mount info for global /dev instance */
+extern const fs_operation_def_t	sdev_vnodeops_tbl[];
+extern const fs_operation_def_t	devpts_vnodeops_tbl[];
+extern const fs_operation_def_t	devsys_vnodeops_tbl[];
+extern const fs_operation_def_t	devpseudo_vnodeops_tbl[];
+
+extern sdev_nc_list_t	*sdev_ncache;
+extern int		sdev_reconfig_boot;
+extern int		sdev_boot_state;
+extern int		sdev_reconfig_verbose;
+extern int		sdev_reconfig_disable;
+extern int		sdev_nc_disable;
+extern int		sdev_nc_disable_reset;
+extern int		sdev_nc_verbose;
+
+/*
+ * misc. defines
+ */
+#ifdef DEBUG
+extern int sdev_debug;
+#define	SDEV_DEBUG		0x01	/* error messages to console/log */
+#define	SDEV_DEBUG_VOPS 	0x02	/* vnode ops errors */
+#define	SDEV_DEBUG_DLF		0x04	/* trace devname_lookup_func */
+#define	SDEV_DEBUG_DRF		0x08	/* trace devname_readdir_func */
+#define	SDEV_DEBUG_NCACHE	0x10	/* negative cache tracing */
+#define	SDEV_DEBUG_DEVFSADMD	0x20	/* comm. of devnamefs & devfsadm */
+#define	SDEV_DEBUG_PTS		0x40	/* /dev/pts tracing */
+#define	SDEV_DEBUG_RECONFIG	0x80	/* events triggering reconfig */
+#define	SDEV_DEBUG_SDEV_NODE	0x100	/* trace sdev_node activities */
+#define	SDEV_DEBUG_PROFILE	0x200	/* trace sdev_profile */
+#define	SDEV_DEBUG_MODCTL	0x400	/* trace modctl activity */
+#define	SDEV_DEBUG_FLK		0x800	/* trace failed lookups */
+
+#define	sdcmn_err(args)  if (sdev_debug & SDEV_DEBUG) printf args
+#define	sdcmn_err2(args) if (sdev_debug & SDEV_DEBUG_VOPS) printf args
+#define	sdcmn_err3(args) if (sdev_debug & SDEV_DEBUG_DLF) printf args
+#define	sdcmn_err4(args) if (sdev_debug & SDEV_DEBUG_DRF) printf args
+#define	sdcmn_err5(args) if (sdev_debug & SDEV_DEBUG_NCACHE) printf args
+#define	sdcmn_err6(args) if (sdev_debug & SDEV_DEBUG_DEVFSADMD) printf args
+#define	sdcmn_err7(args) if (sdev_debug & SDEV_DEBUG_PTS) printf args
+#define	sdcmn_err8(args) if (sdev_debug & SDEV_DEBUG_RECONFIG) printf args
+#define	sdcmn_err9(args) if (sdev_debug & SDEV_DEBUG_SDEV_NODE) printf args
+#define	sdcmn_err10(args) if (sdev_debug & SDEV_DEBUG_PROFILE) printf args
+#define	sdcmn_err11(args) if (sdev_debug & SDEV_DEBUG_MODCTL) printf args
+#define	impossible(args) printf args
+#else
+#define	sdcmn_err(args)		/* does nothing */
+#define	sdcmn_err2(args)	/* does nothing */
+#define	sdcmn_err3(args)	/* does nothing */
+#define	sdcmn_err4(args)	/* does nothing */
+#define	sdcmn_err5(args)	/* does nothing */
+#define	sdcmn_err6(args)	/* does nothing */
+#define	sdcmn_err7(args)	/* does nothing */
+#define	sdcmn_err8(args)	/* does nothing */
+#define	sdcmn_err9(args)	/* does nothing */
+#define	sdcmn_err10(args)	/* does nothing */
+#define	sdcmn_err11(args)	/* does nothing */
+#define	impossible(args)	/* does nothing */
+#endif
+
+#ifdef DEBUG
+#define	SD_TRACE_FAILED_LOOKUP(ddv, nm, retried)			\
+	if ((sdev_debug & SDEV_DEBUG_FLK) ||				\
+	    ((retried) && (sdev_debug & SDEV_DEBUG_RECONFIG))) {	\
+		printf("lookup of %s/%s by %s failed, line %d\n",	\
+		    (ddv)->sdev_name, (nm), curproc->p_user.u_comm,	\
+		    __LINE__);						\
+	}
+#else
+#define	SD_TRACE_FAILED_LOOKUP(ddv, nm, retried)
+#endif
+
+#endif	/* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _SYS_SDEV_IMPL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/fs/sdev_node.h	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,81 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_SDEV_NODE_H
+#define	_SYS_SDEV_NODE_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+
+#include <sys/fs/sdev_impl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _KERNEL
+
+#define	DEVNOPS_REV	1
+
+/*
+ * directory vnode ops implemented in a loadable module
+ */
+struct devname_ops {
+	int	devnops_rev;	/* module build version */
+	int	(*devnops_lookup)(char *, devname_handle_t *, struct cred *);
+	int	(*devnops_remove)(devname_handle_t *);
+	int	(*devnops_rename)(devname_handle_t *, char *);
+	int 	(*devnops_getattr)(devname_handle_t *, struct vattr *,
+		    struct cred *);
+	int	(*devnops_readdir)(devname_handle_t *, struct cred *);
+	void	(*devnops_inactive)(devname_handle_t *, struct cred *);
+};
+
+/*
+ * supported protocols
+ */
+#define	DEVNAME_NS_PATH	1
+#define	DEVNAME_NS_DEV	2
+
+/*
+ * default devname_ops for a /dev directory
+ * that has a device name binding rule map
+ */
+extern void devname_set_nodetype(devname_handle_t *, void *, int);
+extern void devname_get_vnode(devname_handle_t *, vnode_t **);
+extern int devname_get_path(devname_handle_t *, char **);
+extern int devname_get_name(devname_handle_t *, char **);
+extern int devname_get_dir_handle(devname_handle_t *, devname_handle_t **);
+extern void devname_get_dir_vnode(devname_handle_t *, vnode_t **);
+extern int devname_get_dir_path(devname_handle_t *, char **);
+extern int devname_get_dir_name(devname_handle_t *, char **);
+
+#endif	/* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _SYS_SDEV_NODE_H */
--- a/usr/src/uts/common/sys/mntent.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/sys/mntent.h	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
  *	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
@@ -56,6 +55,7 @@
 #define	MNTTYPE_AUTOFS	"autofs"	/* Automounter ``file'' system */
 #define	MNTTYPE_MNTFS	"mntfs"		/* In-kernel mnttab */
 #define	MNTTYPE_XMEMFS	"xmemfs"	/* Extended memory FS, IA32 only */
+#define	MNTTYPE_DEV	"dev"		/* /dev file system */
 #define	MNTTYPE_CTFS	"ctfs"		/* Contract file system */
 #define	MNTTYPE_OBJFS	"objfs"		/* Kernel object file system */
 
--- a/usr/src/uts/common/sys/modctl.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/sys/modctl.h	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -79,6 +78,7 @@
 extern struct mod_ops mod_dacfops;
 extern struct mod_ops mod_ippops;
 extern struct mod_ops mod_pcbeops;
+extern struct mod_ops mod_devfsops;
 
 #endif /* _KERNEL */
 
@@ -175,6 +175,13 @@
 	struct __pcbe_ops	*pcbe_ops;
 };
 
+/* for devname fs */
+struct modldev {
+	struct mod_ops		*dev_modops;
+	char			*dev_linkinfo;
+	struct devname_ops	*dev_ops;
+};
+
 /*
  * Revision number of loadable modules support.  This is the value
  * that must be used in the modlinkage structure.
@@ -238,6 +245,9 @@
 #define	MODADDMINORPERM		32
 #define	MODREMMINORPERM		33
 #define	MODREMDRVCLEANUP	34
+#define	MODDEVEXISTS		35
+#define	MODDEVREADDIR		36
+#define	MODDEVNAME		37
 
 /*
  * sub cmds for MODEVENTS
@@ -251,6 +261,17 @@
 #define	MODEVENTS_REGISTER_EVENT		6
 
 /*
+ * devname subcmds for MODDEVNAME
+ */
+#define	MODDEVNAME_LOOKUPDOOR	0
+#define	MODDEVNAME_DEVFSADMNODE	1
+#define	MODDEVNAME_NSMAPS	2
+#define	MODDEVNAME_PROFILE	3
+#define	MODDEVNAME_RECONFIG	4
+#define	MODDEVNAME_SYSAVAIL	5
+
+
+/*
  * Data structure passed to modconfig command in kernel to build devfs tree
  */
 
@@ -473,6 +494,7 @@
 #define	MOD_NONOTIFY		0x2	/* No krtld notifications on (un)load */
 #define	MOD_NOUNLOAD		0x4	/* Assume EBUSY for all _fini's */
 
+
 #ifdef _KERNEL
 
 #define	MOD_BIND_HASHSIZE	64
@@ -540,6 +562,7 @@
 extern void	mod_rele_dev_by_devi(dev_info_t *);
 
 extern int make_devname(char *, major_t);
+extern int gmatch(const char *, const char *);
 
 struct bind;
 extern void make_aliases(struct bind **);
--- a/usr/src/uts/common/sys/ptms.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/sys/ptms.h	Fri Aug 25 17:24:25 2006 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -52,6 +51,8 @@
 	kcondvar_t pt_cv;	/* condition variable for exclusive access */
 	kmutex_t pt_lock;	/* Per-element lock */
 	zoneid_t pt_zoneid;	/* Zone membership for this pty */
+	uid_t	 pt_ruid;	/* Real owner of pty */
+	gid_t	 pt_rgid;	/* Real group owner of pty */
 };
 
 /*
@@ -112,8 +113,11 @@
 extern struct pt_ttys *pt_ttys_alloc(void);
 extern void ptms_close(struct pt_ttys *, uint_t);
 extern struct pt_ttys *ptms_minor2ptty(minor_t);
-extern int ptms_create_pts_nodes(dev_info_t *);
-extern int ptms_destroy_pts_nodes(dev_info_t *);
+extern int ptms_attach_slave(void);
+extern int ptms_minor_valid(minor_t ptmin, uid_t *uid, gid_t *gid);
+extern int ptms_minor_exists(minor_t ptmin);
+extern void ptms_set_owner(minor_t ptmin, uid_t uid, gid_t gid);
+extern major_t ptms_slave_attached(void);
 
 #ifdef DEBUG
 extern void ptms_log(char *, uint_t);
@@ -141,11 +145,20 @@
  *  ZONEPT: Sets the zoneid of the pair of master and slave devices.  It
  *	    returns 0 upon success.  Used to force a pty 'into' a zone upon
  *	    zone entry.
+ *
+ * PT_OWNER: Sets uid and gid for slave device.  It returns 0 on success.
+ *
  */
 #define	ISPTM	(('P'<<8)|1)	/* query for master */
 #define	UNLKPT	(('P'<<8)|2)	/* unlock master/slave pair */
 #define	PTSSTTY	(('P'<<8)|3)	/* set tty flag */
 #define	ZONEPT	(('P'<<8)|4)	/* set zone of master/slave pair */
+#define	PT_OWNER (('P'<<8)|5)	/* set owner and group for slave device */
+
+typedef struct pt_own {
+	uid_t	pto_ruid;
+	gid_t	pto_rgid;
+} pt_own_t;
 
 #ifdef	__cplusplus
 }
--- a/usr/src/uts/common/sys/sunddi.h	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/sys/sunddi.h	Fri Aug 25 17:24:25 2006 -0700
@@ -241,6 +241,7 @@
 #define	ESC_DEVFS_INSTANCE_MOD	"ESC_devfs_instance_mod"
 #define	ESC_DEVFS_BRANCH_ADD	"ESC_devfs_branch_add"
 #define	ESC_DEVFS_BRANCH_REMOVE	"ESC_devfs_branch_remove"
+#define	ESC_DEVFS_START		"ESC_devfs_start"
 
 /* Class ddi subclasses */
 #define	ESC_DDI_INITIATOR_REGISTER	"ESC_ddi_initiator_register"
--- a/usr/src/uts/common/syscall/uadmin.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/common/syscall/uadmin.c	Fri Aug 25 17:24:25 2006 -0700
@@ -63,6 +63,7 @@
 extern ksema_t fsflush_sema;
 kmutex_t ualock;
 
+int sys_shutdown = 0;
 
 /*
  * Kill all user processes in said zone.  A special argument of ALL_ZONES is
@@ -202,6 +203,9 @@
 				curthread->t_cred = kcred;
 		}
 
+		/* indicate shutdown in progress */
+		sys_shutdown = 1;
+
 		/*
 		 * Communcate that init shouldn't be restarted.
 		 */
--- a/usr/src/uts/i86pc/os/startup.c	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/i86pc/os/startup.c	Fri Aug 25 17:24:25 2006 -0700
@@ -1352,6 +1352,9 @@
 	if (modload("fs", "devfs") == -1)
 		halt("Can't load devfs");
 
+	if (modload("fs", "dev") == -1)
+		halt("Can't load dev");
+
 	(void) modloadonly("sys", "lbl_edition");
 
 	dispinit();
--- a/usr/src/uts/intel/Makefile.intel.shared	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/intel/Makefile.intel.shared	Fri Aug 25 17:24:25 2006 -0700
@@ -415,7 +415,7 @@
 #
 #	File System Modules (/kernel/fs):
 #
-FS_KMODS	+= autofs cachefs ctfs devfs fdfs fifofs hsfs lofs
+FS_KMODS	+= autofs cachefs ctfs dev devfs fdfs fifofs hsfs lofs
 FS_KMODS	+= mntfs namefs nfs objfs zfs
 FS_KMODS	+= pcfs procfs sockfs specfs tmpfs udfs ufs xmemfs
 
@@ -579,3 +579,8 @@
 #	MAC-Type Plugin Modules (/kernel/mac)
 #
 MAC_KMODS	+= mac_ether
+
+#
+# 'Devname' Modules (kernel/devname)
+#
+DEVNAME_KMODS	+= sdev_nsconfig_mod
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/dev/Makefile	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,90 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# uts/intel/dev/Makefile
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives the production of the dev file system
+#	kernel module.
+#
+#	intel architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= dev
+OBJECTS		= $(DEV_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(DEV_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_FS_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Override defaults to build a unique, local modstubs.o.
+#
+MODSTUBS_DIR	 = $(OBJS_DIR)
+CFLAGS		+= -v
+LDFLAGS		+= -dy -Nfs/devfs
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
--- a/usr/src/uts/intel/ia32/ml/modstubs.s	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/intel/ia32/ml/modstubs.s	Fri Aug 25 17:24:25 2006 -0700
@@ -420,6 +420,20 @@
 	END_MODULE(devfs);
 #endif
 
+#ifndef	DEV_MODULE
+	MODULE(dev,fs);
+	NO_UNLOAD_STUB(dev, sdev_modctl_readdir,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, sdev_modctl_readdir_free,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, devname_filename_register,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, sdev_modctl_devexists,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, devname_nsmaps_register,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, devname_profile_update,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, sdev_module_register,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, sdev_devstate_change,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, devpts_getvnodeops,		nomod_zero);
+	END_MODULE(dev);
+#endif
+
 /*
  * Stubs for specfs. A non-unloadable module.
  */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/sdev_nsconfig_mod/Makefile	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,91 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# uts/intel/sdev_nsconfig_mod/Makefile
+#
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= sdev_nsconfig_mod
+OBJECTS		= $(NSCONFIG_DEVNAME_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(NSCONFIG_DEVNAME_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DEVNAME_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Override defaults to build a unique, local modstubs.o.
+#
+MODSTUBS_DIR	 = $(OBJS_DIR)
+CLEANFILES	+= $(MODSTUBS_O)
+
+#
+# depends on fs/dev module
+#
+LDFLAGS += -dy -Nfs/dev
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
--- a/usr/src/uts/sparc/Makefile.sparc.shared	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/sparc/Makefile.sparc.shared	Fri Aug 25 17:24:25 2006 -0700
@@ -328,7 +328,7 @@
 #
 #	File System Modules (/kernel/fs):
 #
-FS_KMODS	+= devfs fdfs fifofs hsfs lofs namefs nfs pcfs tmpfs zfs
+FS_KMODS	+= dev devfs fdfs fifofs hsfs lofs namefs nfs pcfs tmpfs zfs
 FS_KMODS	+= specfs udfs ufs autofs cachefs procfs sockfs mntfs
 FS_KMODS	+= ctfs objfs
 
@@ -443,3 +443,8 @@
 #	MAC-Type Plugin Modules (/kernel/mac)
 #
 MAC_KMODS	+= mac_ether
+
+#
+# 'Devname' Modules (kernel/devname)
+#
+DEVNAME_KMODS	+= sdev_nsconfig_mod
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/dev/Makefile	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,91 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+# uts/sparc/dev/Makefile
+#	This makefile drives the production of the /dev file system
+#	kernel module.
+#
+#	sparc implementation architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= dev
+OBJECTS		= $(DEV_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(DEV_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_FS_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+MODSTUBS_DIR	 = $(OBJS_DIR)
+# $(MODSTUBS_O)	:= AS_CPPFLAGS += -DDEVFS_MODULE
+# CLEANFILES	+= $(MODSTUBS_O)
+CFLAGS		+= -v
+LDFLAGS		+= -dy -Nfs/devfs
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
--- a/usr/src/uts/sparc/ml/modstubs.s	Fri Aug 25 16:44:08 2006 -0700
+++ b/usr/src/uts/sparc/ml/modstubs.s	Fri Aug 25 17:24:25 2006 -0700
@@ -306,6 +306,23 @@
 #endif
 
 /*
+ * Stubs for /dev fs.
+ */
+#ifndef DEV_MODULE
+	MODULE(dev, fs);
+	NO_UNLOAD_STUB(dev, sdev_modctl_readdir,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, sdev_modctl_readdir_free,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, devname_filename_register,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, sdev_modctl_devexists,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, devname_nsmaps_register,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, devname_profile_update,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, sdev_module_register,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, sdev_devstate_change,	nomod_minus_one);
+	NO_UNLOAD_STUB(dev, devpts_getvnodeops,		nomod_zero);
+	END_MODULE(dev);
+#endif
+
+/*
  * Stubs for specfs. A non-unloadable module.
  */
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/sdev_nsconfig_mod/Makefile	Fri Aug 25 17:24:25 2006 -0700
@@ -0,0 +1,87 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# uts/sparc/sdev_nsconfig_mod/Makefile
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives production of devname/sdev_nsconfig_mod module.
+#
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= sdev_nsconfig_mod
+OBJECTS		= $(NSCONFIG_DEVNAME_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(NSCONFIG_DEVNAME_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DEVNAME_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+# 
+# lint pass one enforcement 
+# 
+CFLAGS += $(CCVERBOSE)
+
+# 
+# Dependencies
+#
+LDFLAGS += -dy -Nfs/dev
+
+#
+#       Define targets
+#
+ALL_TARGET      = $(BINARY)
+LINT_TARGET     = $(MODULE).lint
+INSTALL_TARGET  = $(BINARY) $(ROOTMODULE)
+
+
+.KEEP_STATE:
+
+def:            $(DEF_DEPS)
+
+all:            $(ALL_DEPS)
+
+clean:          $(CLEAN_DEPS)
+
+clobber:        $(CLOBBER_DEPS)
+
+lint:           $(LINT_DEPS)
+
+modlintlib:     $(MODLINTLIB_DEPS)
+
+clean.lint:     $(CLEAN_LINT_DEPS)
+
+install:        $(INSTALL_DEPS)
+
+#
+#       Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ