summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/Makefile.lint1
-rw-r--r--usr/src/cmd/Makefile1
-rw-r--r--usr/src/cmd/allocate/Makefile6
-rw-r--r--usr/src/cmd/allocate/allocate.c14
-rw-r--r--usr/src/cmd/allocate/allocate3.c85
-rw-r--r--usr/src/cmd/allocate/mkdevalloc.c10
-rw-r--r--usr/src/cmd/devfsadm/devfsadm.c1183
-rw-r--r--usr/src/cmd/devfsadm/devfsadm.h5
-rw-r--r--usr/src/cmd/devfsadm/devfsadm_impl.h63
-rw-r--r--usr/src/cmd/devfsadm/message.h36
-rw-r--r--usr/src/cmd/devfsadm/misc_link.c2
-rw-r--r--usr/src/cmd/fs.d/Makefile2
-rw-r--r--usr/src/cmd/fs.d/dev/Makefile (renamed from usr/src/cmd/pt_chmod/Makefile)41
-rw-r--r--usr/src/cmd/fs.d/dev/mount.c363
-rw-r--r--usr/src/cmd/initpkg/rc2.sh5
-rw-r--r--usr/src/cmd/pt_chmod/pt_chmod.c105
-rw-r--r--usr/src/cmd/truss/print.c3
-rw-r--r--usr/src/cmd/zlogin/Makefile9
-rw-r--r--usr/src/cmd/zlogin/zlogin.c40
-rw-r--r--usr/src/cmd/zoneadmd/vplat.c488
-rw-r--r--usr/src/cmd/zoneadmd/zcons.c53
-rw-r--r--usr/src/head/libzonecfg.h7
-rw-r--r--usr/src/lib/libc/port/gen/pt.c128
-rw-r--r--usr/src/lib/libdevinfo/Makefile.com3
-rw-r--r--usr/src/lib/libdevinfo/devinfo_devlink.c103
-rw-r--r--usr/src/lib/libdevinfo/devinfo_devlink.h10
-rw-r--r--usr/src/lib/libdevinfo/devinfo_devname.c701
-rw-r--r--usr/src/lib/libdevinfo/devinfo_devperm.c152
-rw-r--r--usr/src/lib/libdevinfo/devinfo_finddev.c163
-rw-r--r--usr/src/lib/libdevinfo/libdevinfo.h45
-rw-r--r--usr/src/lib/libdevinfo/mapfile-vers15
-rw-r--r--usr/src/lib/libtsol/common/label.h8
-rw-r--r--usr/src/lib/libzonecfg/common/libzonecfg.c298
-rw-r--r--usr/src/lib/libzonecfg/common/mapfile-vers5
-rw-r--r--usr/src/lib/libzonecfg/dtd/zonecfg.dtd.120
-rw-r--r--usr/src/pkgdefs/SUNWckr/prototype_com1
-rw-r--r--usr/src/pkgdefs/SUNWckr/prototype_i3865
-rw-r--r--usr/src/pkgdefs/SUNWckr/prototype_sparc3
-rw-r--r--usr/src/pkgdefs/SUNWcsr/prototype_com2
-rw-r--r--usr/src/pkgdefs/SUNWcsu/prototype_com3
-rw-r--r--usr/src/pkgdefs/SUNWhea/prototype_com2
-rw-r--r--usr/src/tools/scripts/bfu.sh5
-rw-r--r--usr/src/ucbcmd/ucblinks/ucblinks.c11
-rw-r--r--usr/src/uts/Makefile.targ6
-rw-r--r--usr/src/uts/Makefile.uts17
-rw-r--r--usr/src/uts/common/Makefile.files9
-rw-r--r--usr/src/uts/common/Makefile.rules9
-rw-r--r--usr/src/uts/common/cpr/cpr_misc.c9
-rw-r--r--usr/src/uts/common/fs/dev/sdev_comm.c749
-rw-r--r--usr/src/uts/common/fs/dev/sdev_ncache.c740
-rw-r--r--usr/src/uts/common/fs/dev/sdev_nsconfig_mod.c198
-rw-r--r--usr/src/uts/common/fs/dev/sdev_profile.c983
-rw-r--r--usr/src/uts/common/fs/dev/sdev_ptsops.c398
-rw-r--r--usr/src/uts/common/fs/dev/sdev_subr.c3657
-rw-r--r--usr/src/uts/common/fs/dev/sdev_vfsops.c524
-rw-r--r--usr/src/uts/common/fs/dev/sdev_vnops.c1329
-rw-r--r--usr/src/uts/common/fs/devfs/devfs_subr.c82
-rw-r--r--usr/src/uts/common/fs/namefs/namevfs.c19
-rw-r--r--usr/src/uts/common/fs/vfs.c89
-rw-r--r--usr/src/uts/common/io/ptm.c48
-rw-r--r--usr/src/uts/common/io/ptms_conf.c250
-rw-r--r--usr/src/uts/common/io/pts.c19
-rw-r--r--usr/src/uts/common/os/devcfg.c68
-rw-r--r--usr/src/uts/common/os/devctl.c447
-rw-r--r--usr/src/uts/common/os/modconf.c47
-rw-r--r--usr/src/uts/common/os/modctl.c164
-rw-r--r--usr/src/uts/common/os/vfs_conf.c8
-rw-r--r--usr/src/uts/common/sys/Makefile2
-rw-r--r--usr/src/uts/common/sys/autoconf.h10
-rw-r--r--usr/src/uts/common/sys/devctl_impl.h79
-rw-r--r--usr/src/uts/common/sys/fs/dv_node.h10
-rw-r--r--usr/src/uts/common/sys/fs/sdev_impl.h699
-rw-r--r--usr/src/uts/common/sys/fs/sdev_node.h81
-rw-r--r--usr/src/uts/common/sys/mntent.h8
-rw-r--r--usr/src/uts/common/sys/modctl.h29
-rw-r--r--usr/src/uts/common/sys/ptms.h25
-rw-r--r--usr/src/uts/common/sys/sunddi.h1
-rw-r--r--usr/src/uts/common/syscall/uadmin.c4
-rw-r--r--usr/src/uts/i86pc/os/startup.c3
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared7
-rw-r--r--usr/src/uts/intel/dev/Makefile90
-rw-r--r--usr/src/uts/intel/ia32/ml/modstubs.s14
-rw-r--r--usr/src/uts/intel/sdev_nsconfig_mod/Makefile91
-rw-r--r--usr/src/uts/sparc/Makefile.sparc.shared7
-rw-r--r--usr/src/uts/sparc/dev/Makefile91
-rw-r--r--usr/src/uts/sparc/ml/modstubs.s17
-rw-r--r--usr/src/uts/sparc/sdev_nsconfig_mod/Makefile87
87 files changed, 13333 insertions, 2096 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index 857b0e0638..ddc364ac74 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -217,7 +217,6 @@ COMMON_SUBDIRS = \
cmd/psradm \
cmd/psrinfo \
cmd/psrset \
- cmd/pt_chmod \
cmd/ptools \
cmd/pwck \
cmd/pwconv \
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 36e80875a9..6868fa894a 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -297,7 +297,6 @@ COMMON_SUBDIRS= \
psradm \
psrinfo \
psrset \
- pt_chmod \
ptools \
pwck \
pwconv \
diff --git a/usr/src/cmd/allocate/Makefile b/usr/src/cmd/allocate/Makefile
index 53cf843d11..8fc40750cc 100644
--- a/usr/src/cmd/allocate/Makefile
+++ b/usr/src/cmd/allocate/Makefile
@@ -87,9 +87,9 @@ $(ROOTSECLIB)/% := FILEMODE = 0555
$(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
diff --git a/usr/src/cmd/allocate/allocate.c b/usr/src/cmd/allocate/allocate.c
index 36f0050f2b..496bea5274 100644
--- a/usr/src/cmd/allocate/allocate.c
+++ b/usr/src/cmd/allocate/allocate.c
@@ -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 @@ main(int argc, char *argv[], char *envp[])
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.
diff --git a/usr/src/cmd/allocate/allocate3.c b/usr/src/cmd/allocate/allocate3.c
index 7b4ef265f3..d4853a72f2 100644
--- a/usr/src/cmd/allocate/allocate3.c
+++ b/usr/src/cmd/allocate/allocate3.c
@@ -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 @@ struct zone_path {
char **path;
};
-struct devnames {
+struct dev_names {
char **dnames;
};
@@ -1227,7 +1228,7 @@ out:
}
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 @@ allocate(int optflag, uid_t uid, char *device, char *zonename)
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 @@ deallocate(int optflag, uid_t uid, char *device, char *zonename)
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 @@ _check_label(devalloc_t *da, char *zonename, uid_t uid, int flag)
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 @@ create_znode(char *zonename, struct zone_path *zpath, devmap_t *list)
}
(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 @@ create_znode(char *zonename, struct zone_path *zpath, devmap_t *list)
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 @@ remove_znode(char *zonename, devmap_t *dm)
char *zoneroot;
char **file;
char zonepath[MAXPATHLEN];
+ di_prof_t prof = NULL;
file = dm->dmap_devarray;
if (file == NULL)
@@ -1674,11 +1660,6 @@ remove_znode(char *zonename, devmap_t *dm)
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 @@ remove_znode(char *zonename, devmap_t *dm)
*/
(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 @@ remove_znode(char *zonename, devmap_t *dm)
*/
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);
}
diff --git a/usr/src/cmd/allocate/mkdevalloc.c b/usr/src/cmd/allocate/mkdevalloc.c
index e423e3727a..e4533f1673 100644
--- a/usr/src/cmd/allocate/mkdevalloc.c
+++ b/usr/src/cmd/allocate/mkdevalloc.c
@@ -180,6 +180,16 @@ main(int argc, char **argv)
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
diff --git a/usr/src/cmd/devfsadm/devfsadm.c b/usr/src/cmd/devfsadm/devfsadm.c
index 67a46fad15..b627bd5255 100644
--- a/usr/src/cmd/devfsadm/devfsadm.c
+++ b/usr/src/cmd/devfsadm/devfsadm.c
@@ -142,8 +142,14 @@ static char *devices_dir = DEVICES;
/* /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 @@ static char lphy_path[PATH_MAX + 1] = {""};
/* 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 @@ static int login_dev_enable = FALSE;
/* 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 mutex_t rcm_eventq_lock;
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 @@ main(int argc, char *argv[])
*/
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,58 +399,53 @@ load_dev_acl()
}
/*
- * 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)
+static void
+check_reconfig_state()
{
- char zpath[MAXPATHLEN];
- zone_dochandle_t hdl;
- void *dlhdl;
-
- assert(daemon_mode == FALSE);
+ struct stat sb;
- if (strcmp(zone_name, GLOBAL_ZONENAME) == 0) {
- err_print(INVALID_ZONE, zone_name);
- return (DEVFSADM_FAILURE);
+ if (s_stat("/reconfigure", &sb) == 0) {
+ (void) modctl(MODDEVNAME, MODDEVNAME_RECONFIG, 0);
}
+}
- if ((dlhdl = dlopen(LIBZONECFG_PATH, RTLD_LAZY)) == NULL) {
- err_print(ZONE_LIB_MISSING);
- return (DEVFSADM_FAILURE);
- }
+static void
+modctl_sysavail()
+{
+ /*
+ * Inform /dev that system is available, that
+ * implicit reconfig can now be performed.
+ */
+ (void) modctl(MODDEVNAME, MODDEVNAME_SYSAVAIL, 0);
+}
- 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);
+static void
+set_lock_root(void)
+{
+ struct stat sb;
+ char *lock_root;
+ size_t len;
- if ((hdl = zonecfg_init_handle()) == NULL) {
- err_print(ZONE_REP_FAILED, zone_name, strerror(errno));
- (void) dlclose(dlhdl);
- return (DEVFSADM_FAILURE);
- }
+ lock_root = attr_root ? attr_root : root_dir;
- 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);
+ len = strlen(lock_root) + strlen(ETCDEV) + 1;
+ etc_dev_dir = s_malloc(len);
+ (void) snprintf(etc_dev_dir, len, "%s%s", lock_root, ETCDEV);
- 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);
+ 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 @@ parse_args(int argc, char *argv[])
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 @@ parse_args(int argc, char *argv[])
} 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 @@ parse_args(int argc, char *argv[])
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 @@ parse_args(int argc, char *argv[])
/* 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 @@ parse_args(int argc, char *argv[])
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 @@ parse_args(int argc, char *argv[])
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 @@ parse_args(int argc, char *argv[])
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 @@ parse_args(int argc, char *argv[])
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 @@ parse_args(int argc, char *argv[])
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 @@ parse_args(int argc, char *argv[])
usage();
}
}
+ set_lock_root();
}
void
@@ -1089,13 +1084,30 @@ process_devinfo_tree()
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 @@ daemon_update(void)
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,61 +1159,74 @@ daemon_update(void)
(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));
+ if ((fd = open(door_file, O_RDWR | O_CREAT, SYNCH_DOOR_PERMS)) == -1) {
+ err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
devfsadm_exit(1);
}
(void) close(fd);
- if ((fd = door_create(zone_reg_handler, NULL,
+
+ if ((fd = door_create(sync_handler, NULL,
DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
- err_print(CANT_CREATE_ZONE_DOOR, door_file, strerror(errno));
+ err_print(CANT_CREATE_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));
+ err_print(CANT_CREATE_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);
+ /*
+ * devname_lookup_door
+ */
+ 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, SYNCH_DOOR_PERMS)) == -1) {
+ 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(sync_handler, NULL,
- DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
+ 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);
}
- devlink_door_fd = fd;
+ lookup_door_fd = fd;
- /*
- * Make sure devfsadm is managing any and all configured system zones.
- */
- if (register_all_zones() != DEVFSADM_SUCCESS) {
- err_print(ZONE_LIST_FAILED, strerror(errno));
- }
+ /* 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 @@ lock_dev(void)
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 @@ unlock_dev(int flag)
}
/*
- * 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 @@ zone_pathcheck(char *checkpath)
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 @@ zone_pathcheck(char *checkpath)
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 @@ event_handler(sysevent_t *ev)
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 @@ event_handler(sysevent_t *ev)
DI_NODE_NIL);
unlock_dev(CACHE_STATE);
startup_cache_sync_thread();
-
} else
err_print(UNKNOWN_EVENT, subclass);
@@ -2686,129 +2373,13 @@ call_minor_init(module_t *module)
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 @@ devfsadm_mklink_default(char *link, di_node_t node, di_minor_t minor, int flags)
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 @@ rm_parent_dir_if_empty(char *pathname)
{
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 @@ rm_parent_dir_if_empty(char *pathname)
*ptr = '\0';
- if (s_rmdir(path) == 0) {
- vprint(REMOVE_MID, "%sremoving empty dir %s\n",
- fcn, path);
- continue;
+ if ((pathlist = dev_readdir(path)) == NULL) {
+ err_print(OPENDIR_FAILED, path, strerror(errno));
+ return;
}
- if (errno == EEXIST) {
+
+ /*
+ * 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 @@ enter_dev_lock()
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 @@ enter_daemon_lock(void)
{
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 @@ recurse_dev_re(char *current_dir, char *path_re, recurse_dev_t *rd)
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 @@ recurse_dev_re(char *current_dir, char *path_re, recurse_dev_t *rd)
free(anchored_path_re);
- 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) {
- if (regexec(&re1, entp->d_name, 0, NULL, 0) == 0) {
+ 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 @@ recurse_dev_re(char *current_dir, char *path_re, recurse_dev_t *rd)
regfree(&re1);
out:
- s_closedir(dp);
+ free(pathlist);
}
/*
@@ -4767,7 +4312,7 @@ devfsadm_link_valid(char *link)
(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 @@ get_enum_cache(devfsadm_enumerate_t rules[], int nrules)
/*
* 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 @@ get_enum_cache(devfsadm_enumerate_t rules[], int nrules)
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 @@ get_stat_info(char *namebuf, struct stat *sb)
*/
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 @@ get_stat_info(char *namebuf, struct stat *sb)
* 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);
- (void) strcat(namebuf, "/");
- (void) strcat(namebuf, entp->d_name);
+ if ((strlcat(namebuf, "/", PATH_MAX) >= PATH_MAX) ||
+ (strlcat(namebuf, listp, PATH_MAX) >= PATH_MAX)) {
+ *cp = '\0';
+ return (DEVFSADM_FAILURE);
+ }
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 @@ enumerate_recurse(char *current_dir, char *path_left, numeral_set_t *setp,
char *slash;
char *new_path;
char *numeral_id;
- struct dirent *entp;
- DIR *dp;
+ char *pathlist;
+ char *listp;
+ int len;
- if ((dp = opendir(current_dir)) == NULL) {
+ if ((pathlist = dev_readdir(current_dir)) == NULL) {
return;
}
@@ -5617,29 +5160,24 @@ enumerate_recurse(char *current_dir, char *path_left, numeral_set_t *setp,
*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 @@ enumerate_recurse(char *current_dir, char *path_left, numeral_set_t *setp,
if (slash != NULL) {
*slash = '/';
}
- s_closedir(dp);
+ free(pathlist);
}
@@ -7007,8 +6545,6 @@ devfsadm_exit(int status)
(void) dlclose(librcm_hdl);
}
- zlist_deleteall_unlocked(); /* dispose of all zones */
-
exit_dev_lock();
exit_daemon_lock();
@@ -7020,14 +6556,10 @@ devfsadm_exit(int status)
}
/*
- * 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 @@ set_root_devices_dev_dir(char *dir, int zone_mode)
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 @@ alias(char *driver_name, char *alias_name)
/*
* 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 @@ build_and_log_event(char *class, char *subclass, char *node_path,
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);
+ }
+ }
+}
diff --git a/usr/src/cmd/devfsadm/devfsadm.h b/usr/src/cmd/devfsadm/devfsadm.h
index 43274c9612..4688e6043c 100644
--- a/usr/src/cmd/devfsadm/devfsadm.h
+++ b/usr/src/cmd/devfsadm/devfsadm.h
@@ -83,6 +83,10 @@ extern "C" {
/* 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 @@ typedef struct _devfsadm_remove_reg {
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
diff --git a/usr/src/cmd/devfsadm/devfsadm_impl.h b/usr/src/cmd/devfsadm/devfsadm_impl.h
index d73da6d5bd..3c22b42322 100644
--- a/usr/src/cmd/devfsadm/devfsadm_impl.h
+++ b/usr/src/cmd/devfsadm/devfsadm_impl.h
@@ -71,10 +71,13 @@ extern "C" {
#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 @@ extern "C" {
#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 @@ extern "C" {
#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 @@ struct zreg {
#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 @@ struct dca_impl {
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 @@ static int devfsadm_enumerate_int_start(char *devfs_path,
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 @@ static int lookup_enum_cache(numeral_set_t *set, char *cmp_str,
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 @@ static nvlist_t *build_event_attributes(char *, char *, char *,
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
}
diff --git a/usr/src/cmd/devfsadm/message.h b/usr/src/cmd/devfsadm/message.h
index d61918b87a..8d1436f38d 100644
--- a/usr/src/cmd/devfsadm/message.h
+++ b/usr/src/cmd/devfsadm/message.h
@@ -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 @@ gettext("ignoring devfsadm_create entry #%d in module %s\n")
#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 @@ major_number ]\n\t\t[ -n ]\n\t\t[ -r rootdir ]\n\t\t[ -v ]\n")
#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
diff --git a/usr/src/cmd/devfsadm/misc_link.c b/usr/src/cmd/devfsadm/misc_link.c
index a8f0ef5d71..0766c3dc28 100644
--- a/usr/src/cmd/devfsadm/misc_link.c
+++ b/usr/src/cmd/devfsadm/misc_link.c
@@ -60,7 +60,7 @@ static int ses_callback(di_minor_t minor, di_node_t node);
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",
diff --git a/usr/src/cmd/fs.d/Makefile b/usr/src/cmd/fs.d/Makefile
index 1fadbd2676..eb78c8d14e 100644
--- a/usr/src/cmd/fs.d/Makefile
+++ b/usr/src/cmd/fs.d/Makefile
@@ -45,7 +45,7 @@ DEFAULTFILES= fs.dfl
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)
diff --git a/usr/src/cmd/pt_chmod/Makefile b/usr/src/cmd/fs.d/dev/Makefile
index 39666fc628..8da5c545a1 100644
--- a/usr/src/cmd/pt_chmod/Makefile
+++ b/usr/src/cmd/fs.d/dev/Makefile
@@ -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,29 +18,23 @@
#
# CDDL HEADER END
#
-#
-#ident "%Z%%M% %I% %E% SMI"
-#
-# 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"
+#
-PROG= pt_chmod
-
-include ../Makefile.cmd
-
-FILEMODE= 04511
-
-LDLIBS += -ldevinfo -lsec
-
-.KEEP_STATE:
-
-all: $(PROG)
-
-install: all $(ROOTLIBPROG)
-
-clean:
+FSTYPE= dev
+PROG= mount
+LIBPROG= $(PROG)
+ROOTFS_PROG= $(PROG)
-lint: lint_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.targ
+include ../Makefile.fstype
+include ../Makefile.mount
+include ../Makefile.mount.targ
diff --git a/usr/src/cmd/fs.d/dev/mount.c b/usr/src/cmd/fs.d/dev/mount.c
new file mode 100644
index 0000000000..bf222c9db0
--- /dev/null
+++ b/usr/src/cmd/fs.d/dev/mount.c
@@ -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);
+}
diff --git a/usr/src/cmd/initpkg/rc2.sh b/usr/src/cmd/initpkg/rc2.sh
index d9628c977f..05dda3cd59 100644
--- a/usr/src/cmd/initpkg/rc2.sh
+++ b/usr/src/cmd/initpkg/rc2.sh
@@ -105,4 +105,9 @@ case $action in
exit 1
esac
+if smf_is_globalzone; then
+ # enable full implicit device reconfig
+ /usr/sbin/devfsadm -S
+fi
+
exit 0
diff --git a/usr/src/cmd/pt_chmod/pt_chmod.c b/usr/src/cmd/pt_chmod/pt_chmod.c
deleted file mode 100644
index 55aeb33d59..0000000000
--- a/usr/src/cmd/pt_chmod/pt_chmod.c
+++ /dev/null
@@ -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);
-}
diff --git a/usr/src/cmd/truss/print.c b/usr/src/cmd/truss/print.c
index d063081e5e..ccaaf61485 100644
--- a/usr/src/cmd/truss/print.c
+++ b/usr/src/cmd/truss/print.c
@@ -1099,6 +1099,9 @@ prt_mod(private_t *pri, int raw, long val) /* print modctl() code */
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;
}
}
diff --git a/usr/src/cmd/zlogin/Makefile b/usr/src/cmd/zlogin/Makefile
index 3bb8276831..cae4ae625f 100644
--- a/usr/src/cmd/zlogin/Makefile
+++ b/usr/src/cmd/zlogin/Makefile
@@ -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 @@ PROG = zlogin
include ../Makefile.cmd
-LDLIBS += -lsocket -ldevinfo -lzonecfg -lcontract
+LDLIBS += -lsocket -lzonecfg -lcontract
CFLAGS += $(CCVERBOSE)
FILEMODE = 0555
GROUP = bin
diff --git a/usr/src/cmd/zlogin/zlogin.c b/usr/src/cmd/zlogin/zlogin.c
index a48d10c310..890b055f83 100644
--- a/usr/src/cmd/zlogin/zlogin.c
+++ b/usr/src/cmd/zlogin/zlogin.c
@@ -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 @@ get_master_pty()
* 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 @@ init_slave_pty(zoneid_t zoneid, char *zonepath)
/*
* 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 @@ init_slave_pty(zoneid_t zoneid, char *zonepath)
}
(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 @@ main(int argc, char **argv)
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 @@ main(int argc, char **argv)
}
/*
- * 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 @@ main(int argc, char **argv)
(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);
/*
diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c
index 07a06eecf6..a997bb71a5 100644
--- a/usr/src/cmd/zoneadmd/vplat.c
+++ b/usr/src/cmd/zoneadmd/vplat.c
@@ -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
};
-/*
- * 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 },
+struct source_target {
+ const char *source;
+ const char *target;
};
/*
- * 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 @@ make_one_dir(zlog_t *zlogp, const char *prefix, const char *subdir, mode_t mode)
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 @@ mount_filesystems(zlog_t *zlogp, boolean_t mount_cmd)
}
/*
- * /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 @@ mount_filesystems(zlog_t *zlogp, boolean_t mount_cmd)
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 @@ mount_filesystems(zlog_t *zlogp, boolean_t mount_cmd)
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 @@ tcp_abort_connections(zlog_t *zlogp, zoneid_t zoneid)
}
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 @@ write_index_file(zoneid_t zoneid)
_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 @@ vplat_bringup(zlog_t *zlogp, boolean_t mount_cmd, zoneid_t zoneid)
return (-1);
}
- if (devcleanup(zlogp) != 0) {
- zerror(zlogp, B_TRUE, "device cleanup failed");
+ if (mount_filesystems(zlogp, mount_cmd) != 0) {
+ lofs_discard_mnttab();
return (-1);
}
- if (create_dev_files(zlogp) != 0 ||
- mount_filesystems(zlogp, mount_cmd) != 0) {
+ /* mount /dev for zone (both normal and scratch zone) */
+ if (vplat_mount_dev(zlogp) != 0) {
lofs_discard_mnttab();
return (-1);
}
- if (!mount_cmd && (devfsadm_register(zlogp) != 0 ||
- configure_network_interfaces(zlogp) != 0)) {
+
+ if (!mount_cmd && configure_network_interfaces(zlogp) != 0) {
lofs_discard_mnttab();
return (-1);
}
@@ -3846,9 +3652,6 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd)
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 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd)
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 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd)
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 @@ error:
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);
+}
diff --git a/usr/src/cmd/zoneadmd/zcons.c b/usr/src/cmd/zoneadmd/zcons.c
index bc92ae3f0e..4e775ccd57 100644
--- a/usr/src/cmd/zoneadmd/zcons.c
+++ b/usr/src/cmd/zoneadmd/zcons.c
@@ -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 @@ error:
* 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 @@ prep_console_slave(zlog_t *zlogp, char *zonepath)
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 @@ error:
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);
}
diff --git a/usr/src/head/libzonecfg.h b/usr/src/head/libzonecfg.h
index 94d493600e..51b8dc7005 100644
--- a/usr/src/head/libzonecfg.h
+++ b/usr/src/head/libzonecfg.h
@@ -279,12 +279,6 @@ extern int zonecfg_delete_dev(zone_dochandle_t, struct zone_devtab *);
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 @@ extern int zonecfg_set_limitpriv(zone_dochandle_t, char *);
* 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);
diff --git a/usr/src/lib/libc/port/gen/pt.c b/usr/src/lib/libc/port/gen/pt.c
index 2aa63e92e2..86bcd319c3 100644
--- a/usr/src/lib/libc/port/gen/pt.c
+++ b/usr/src/lib/libc/port/gen/pt.c
@@ -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 @@ ptsdev(int fd)
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 @@ ptsname(int fd)
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 @@ unlockpt(int fd)
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)
- return (-1);
-
- if (sigemptyset(&nset) == -1)
- return (-1);
- if (sigaddset(&nset, SIGCHLD) == -1)
- return (-1);
- if (sigprocmask(SIG_BLOCK, &nset, &oset) == -1)
+ if (ptsdev(fd) == NODEV)
return (-1);
- 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;
- return (-1);
- }
+ pto.pto_ruid = getuid();
- /*
- * 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;
+ gr_name = getgrnam(DEFAULT_TTY_GROUP);
+ if (gr_name)
+ pto.pto_rgid = gr_name->gr_gid;
+ else
+ pto.pto_rgid = getgid();
- /* Restore signal mask */
- (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+ istr.ic_cmd = PT_OWNER;
+ istr.ic_len = sizeof (pt_own_t);
+ istr.ic_timout = 0;
+ istr.ic_dp = (char *)&pto;
- /*
- * 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 (ioctl(fd, I_STR, &istr) != 0) {
+ errno = EACCES;
+ return (-1);
}
- /*
- * 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);
}
/*
diff --git a/usr/src/lib/libdevinfo/Makefile.com b/usr/src/lib/libdevinfo/Makefile.com
index 048c28653f..adcea87c26 100644
--- a/usr/src/lib/libdevinfo/Makefile.com
+++ b/usr/src/lib/libdevinfo/Makefile.com
@@ -29,7 +29,8 @@ LIBRARY= libdevinfo.a
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
diff --git a/usr/src/lib/libdevinfo/devinfo_devlink.c b/usr/src/lib/libdevinfo/devinfo_devlink.c
index 5417b88bc3..3bc5dadf6d 100644
--- a/usr/src/lib/libdevinfo/devinfo_devlink.c
+++ b/usr/src/lib/libdevinfo/devinfo_devlink.c
@@ -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 @@ get_db_path(
}
#endif
if (dir == NULL) {
- dir = hdp->dev_dir;
+ dir = hdp->db_dir;
}
(void) snprintf(buf, blen, "%s/%s", dir, fname);
@@ -246,6 +246,14 @@ open_db(struct di_devlink_handle *hdp, int flags)
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 @@ open_db(struct di_devlink_handle *hdp, int flags)
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 @@ handle_alloc(const char *root_dir, uint_t flags)
/*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 @@ handle_alloc(const char *root_dir, uint_t flags)
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 @@ link2minor(struct di_devlink_handle *hdp, cache_link_t *clp)
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 @@ link2minor(struct di_devlink_handle *hdp, cache_link_t *clp)
/*
* 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 @@ follow_link:
/*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 @@ do_recurse(
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 @@ do_recurse(
(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 @@ do_recurse(
cp = cur + len;
len = sizeof (cur) - len;
- while ((entp = readdir(dp)) != NULL) {
-
- if (strcmp(entp->d_name, ".") == 0 ||
- strcmp(entp->d_name, "..") == 0) {
- continue;
- }
+ for (;;) {
+ if ((d_name = (char *)finddev_next(handle)) == NULL)
+ break;
- (void) snprintf(cp, len, "%s", entp->d_name);
+ if (strlcpy(cp, d_name, len) >= len)
+ break;
/*
* Skip files we are not interested in.
@@ -2475,7 +2524,7 @@ next_entry:
break;
}
- (void) closedir(dp);
+ finddev_close(handle);
return (rv);
}
@@ -3011,7 +3060,7 @@ exit_update_lock(struct di_devlink_handle *hdp)
* 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 @@ daemon_call(const char *root, struct dca_off *dcp)
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;
diff --git a/usr/src/lib/libdevinfo/devinfo_devlink.h b/usr/src/lib/libdevinfo/devinfo_devlink.h
index 0bee7f4c39..d16bb7e52d 100644
--- a/usr/src/lib/libdevinfo/devinfo_devlink.h
+++ b/usr/src/lib/libdevinfo/devinfo_devlink.h
@@ -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 @@ extern "C" {
#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 db {
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 @@ typedef enum {
#define DB_NIL 0
#define DEV "/dev"
+#define ETCDEV "/etc/dev"
#define DEVICES_SUFFIX "ices"
#define HDR_LEN sizeof (struct db_hdr)
diff --git a/usr/src/lib/libdevinfo/devinfo_devname.c b/usr/src/lib/libdevinfo/devinfo_devname.c
new file mode 100644
index 0000000000..379c156627
--- /dev/null
+++ b/usr/src/lib/libdevinfo/devinfo_devname.c
@@ -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);
+}
diff --git a/usr/src/lib/libdevinfo/devinfo_devperm.c b/usr/src/lib/libdevinfo/devinfo_devperm.c
index b4fb3fb217..32af4dbe11 100644
--- a/usr/src/lib/libdevinfo/devinfo_devperm.c
+++ b/usr/src/lib/libdevinfo/devinfo_devperm.c
@@ -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 @@ setdevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode,
* 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_dev_acc(char *path, char *left_to_do, uid_t uid, gid_t gid, mode_t mode,
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 @@ dir_dev_acc(char *path, char *left_to_do, uid_t uid, gid_t gid, mode_t mode,
}
}
- 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;
-
- newpath = (char *)malloc(MAXPATHLEN);
- if (newpath == NULL) {
- return (-1);
- }
- match = (char *)calloc(MAXPATHLEN, 1);
- if (match == NULL) {
- free(newpath);
- return (-1);
- }
+ if (finddev_readdir(path, &handle) != 0)
+ return (0);
+ }
- if (p) {
- (void) strncpy(match, left_to_do, p - left_to_do);
- } else {
- (void) strcpy(match, left_to_do);
- }
+ p = strchr(left_to_do, '/');
+ alwaysmatch = 0;
- if (strcmp(match, "*") == 0) {
- alwaysmatch = 1;
- } else {
- if (regcomp(&regex, match, REG_EXTENDED) != 0) {
- free(newpath);
- free(match);
- return (-1);
- }
+ 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);
}
+ }
- 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;
+ } else {
+ if ((name = (char *)finddev_next(handle)) == NULL)
+ break;
+ }
- 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);
- }
+ 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;
- }
+ /*
+ * 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);
diff --git a/usr/src/lib/libdevinfo/devinfo_finddev.c b/usr/src/lib/libdevinfo/devinfo_finddev.c
new file mode 100644
index 0000000000..1b89afb7b3
--- /dev/null
+++ b/usr/src/lib/libdevinfo/devinfo_finddev.c
@@ -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);
+}
diff --git a/usr/src/lib/libdevinfo/libdevinfo.h b/usr/src/lib/libdevinfo/libdevinfo.h
index 62fa27bcb2..0fac182582 100644
--- a/usr/src/lib/libdevinfo/libdevinfo.h
+++ b/usr/src/lib/libdevinfo/libdevinfo.h
@@ -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_openr(char *);
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
diff --git a/usr/src/lib/libdevinfo/mapfile-vers b/usr/src/lib/libdevinfo/mapfile-vers
index 6e84e1bee1..5a6310b03d 100644
--- a/usr/src/lib/libdevinfo/mapfile-vers
+++ b/usr/src/lib/libdevinfo/mapfile-vers
@@ -145,6 +145,10 @@ SUNWprivate_1.1 {
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 @@ SUNWprivate_1.1 {
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 @@ SUNWprivate_1.1 {
di_dli_openr;
di_dli_openw;
di_dli_close;
+ device_exists;
+ finddev_readdir;
+ finddev_close;
+ finddev_next;
local:
*;
};
diff --git a/usr/src/lib/libtsol/common/label.h b/usr/src/lib/libtsol/common/label.h
index 6a5bfdc205..ed397f5fa3 100644
--- a/usr/src/lib/libtsol/common/label.h
+++ b/usr/src/lib/libtsol/common/label.h
@@ -235,6 +235,14 @@ extern int labelfields(struct name_fields *);
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
diff --git a/usr/src/lib/libzonecfg/common/libzonecfg.c b/usr/src/lib/libzonecfg/common/libzonecfg.c
index 6aef12bbed..569d0f6ba9 100644
--- a/usr/src/lib/libzonecfg/common/libzonecfg.c
+++ b/usr/src/lib/libzonecfg/common/libzonecfg.c
@@ -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 @@ zonecfg_add_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
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 @@ zonecfg_delete_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
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 @@ zonecfg_delete_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
}
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 @@ zonecfg_devperms_apply(zone_dochandle_t hdl, const char *inpath, uid_t owner,
}
/*
- * 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 @@ out:
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 @@ zone_get_rootpath(char *zone_name, char *rootpath, size_t rp_sz)
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)
{
diff --git a/usr/src/lib/libzonecfg/common/mapfile-vers b/usr/src/lib/libzonecfg/common/mapfile-vers
index 9654a93300..79d3de5d15 100644
--- a/usr/src/lib/libzonecfg/common/mapfile-vers
+++ b/usr/src/lib/libzonecfg/common/mapfile-vers
@@ -45,7 +45,6 @@ SUNWprivate_1.1 {
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 @@ SUNWprivate_1.1 {
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 @@ SUNWprivate_1.1 {
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 @@ SUNWprivate_1.1 {
zonecfg_setrctlent;
zonecfg_set_root;
zonecfg_set_zonepath;
- zonecfg_should_deldev;
zonecfg_strerror;
zonecfg_validate_zonename;
zonecfg_valid_fs_type;
@@ -167,6 +163,7 @@ SUNWprivate_1.1 {
zonecfg_valid_rctl;
zonecfg_valid_rctlblk;
zonecfg_valid_rctlname;
+ zone_get_devroot;
zone_get_id;
zone_get_rootpath;
zone_get_state;
diff --git a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1
index 6050fe6463..46e10f761b 100644
--- a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1
+++ b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1
@@ -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.
-
- 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
+ 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.
+
+ 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>
diff --git a/usr/src/pkgdefs/SUNWckr/prototype_com b/usr/src/pkgdefs/SUNWckr/prototype_com
index bb809aefe3..1706dec71c 100644
--- a/usr/src/pkgdefs/SUNWckr/prototype_com
+++ b/usr/src/pkgdefs/SUNWckr/prototype_com
@@ -139,3 +139,4 @@ f manifest var/svc/manifest/system/dumpadm.xml 0444 root sys
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
diff --git a/usr/src/pkgdefs/SUNWckr/prototype_i386 b/usr/src/pkgdefs/SUNWckr/prototype_i386
index 7602e51f64..16ec7a45fa 100644
--- a/usr/src/pkgdefs/SUNWckr/prototype_i386
+++ b/usr/src/pkgdefs/SUNWckr/prototype_i386
@@ -57,6 +57,7 @@ f none kernel/crypto/sha1 755 root sys
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/autofs 755 root sys
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/sha2 755 root sys
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 @@ d none kernel/fs/amd64 755 root sys
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
diff --git a/usr/src/pkgdefs/SUNWckr/prototype_sparc b/usr/src/pkgdefs/SUNWckr/prototype_sparc
index 722e4eb7d2..2a7d6875c3 100644
--- a/usr/src/pkgdefs/SUNWckr/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc
@@ -140,6 +140,7 @@ f none kernel/fs/sparcv9/autofs 755 root sys
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 @@ f none kernel/sys/sparcv9/pset 755 root sys
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
diff --git a/usr/src/pkgdefs/SUNWcsr/prototype_com b/usr/src/pkgdefs/SUNWcsr/prototype_com
index ef323d58d5..c19d3f3539 100644
--- a/usr/src/pkgdefs/SUNWcsr/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsr/prototype_com
@@ -146,6 +146,8 @@ d none etc/fs/hsfs 755 root sys
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
diff --git a/usr/src/pkgdefs/SUNWcsu/prototype_com b/usr/src/pkgdefs/SUNWcsu/prototype_com
index ec4abfeabf..af4c8cbcb1 100644
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsu/prototype_com
@@ -382,6 +382,8 @@ f none usr/lib/fs/cachefs/umount 555 root bin
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 @@ d none usr/lib/pci 755 root bin
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
diff --git a/usr/src/pkgdefs/SUNWhea/prototype_com b/usr/src/pkgdefs/SUNWhea/prototype_com
index 0097de394b..749e5ca0b4 100644
--- a/usr/src/pkgdefs/SUNWhea/prototype_com
+++ b/usr/src/pkgdefs/SUNWhea/prototype_com
@@ -696,6 +696,8 @@ f none usr/include/sys/fs/cachefs_log.h 644 root bin
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
diff --git a/usr/src/tools/scripts/bfu.sh b/usr/src/tools/scripts/bfu.sh
index 926af51ef2..2f43a355fd 100644
--- a/usr/src/tools/scripts/bfu.sh
+++ b/usr/src/tools/scripts/bfu.sh
@@ -5151,6 +5151,11 @@ mondo_loop() {
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";
diff --git a/usr/src/ucbcmd/ucblinks/ucblinks.c b/usr/src/ucbcmd/ucblinks/ucblinks.c
index cde4011bd2..1f96a85f2b 100644
--- a/usr/src/ucbcmd/ucblinks/ucblinks.c
+++ b/usr/src/ucbcmd/ucblinks/ucblinks.c
@@ -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 @@ call_device_rules(void)
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++) {
diff --git a/usr/src/uts/Makefile.targ b/usr/src/uts/Makefile.targ
index f0282e370f..ac467cb4ed 100644
--- a/usr/src/uts/Makefile.targ
+++ b/usr/src/uts/Makefile.targ
@@ -194,6 +194,9 @@ $(ROOT_FONT_DIR)/%: $(OBJS_DIR)/% $(ROOT_MOD_DIR) $(ROOT_FONT_DIR) FRC
$(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_DACF_DIR)/%: $(OBJS_DIR)/% $(USR_DACF_DIR) FRC
$(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
#
diff --git a/usr/src/uts/Makefile.uts b/usr/src/uts/Makefile.uts
index 0cc7ce9516..609c329580 100644
--- a/usr/src/uts/Makefile.uts
+++ b/usr/src/uts/Makefile.uts
@@ -456,6 +456,7 @@ ROOT_FONT_DIR_32 = $(ROOT_MOD_DIR)/fonts
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_FONT_DIR_64 = $(ROOT_MOD_DIR)/fonts/$(SUBDIR64)
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_FONT_DIR = $(ROOT_FONT_DIR_$(CLASS))
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_KGSS_DIR_32)
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_SYS_DIR_32 = $(USR_MOD_DIR)/sys
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_SYS_DIR_64 = $(USR_MOD_DIR)/sys/$(SUBDIR64)
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_SYS_DIR = $(USR_SYS_DIR_$(CLASS))
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 @@ KMODS = $(DRV_KMODS) $(EXEC_KMODS) $(FS_KMODS) $(SCHED_KMODS) $(TOD_KMODS) \
$(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 @@ $(CLOSED_BUILD)CLOSED_KMODS = $(CLOSED_DRV_KMODS) $(CLOSED_TOD_KMODS) \
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))
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index ef97e42257..12f2e58e0d 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -716,6 +716,8 @@ ZCONS_OBJS += zcons.o
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_OBJS += cachefs_cnode.o cachefs_cod.o \
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
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index 22e64fcf3b..fcfce9adb2 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -170,6 +170,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/cachefs/%.c
$(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 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/doorfs/%.c
$(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/doorfs/%.c
$(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))
diff --git a/usr/src/uts/common/cpr/cpr_misc.c b/usr/src/uts/common/cpr/cpr_misc.c
index 934e64cbbd..043219e77b 100644
--- a/usr/src/uts/common/cpr/cpr_misc.c
+++ b/usr/src/uts/common/cpr/cpr_misc.c
@@ -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 @@ cpr_reusable_mount_check(void)
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();
diff --git a/usr/src/uts/common/fs/dev/sdev_comm.c b/usr/src/uts/common/fs/dev/sdev_comm.c
new file mode 100644
index 0000000000..d82afffd07
--- /dev/null
+++ b/usr/src/uts/common/fs/dev/sdev_comm.c
@@ -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);
+}
diff --git a/usr/src/uts/common/fs/dev/sdev_ncache.c b/usr/src/uts/common/fs/dev/sdev_ncache.c
new file mode 100644
index 0000000000..f66532508d
--- /dev/null
+++ b/usr/src/uts/common/fs/dev/sdev_ncache.c
@@ -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);
+}
diff --git a/usr/src/uts/common/fs/dev/sdev_nsconfig_mod.c b/usr/src/uts/common/fs/dev/sdev_nsconfig_mod.c
new file mode 100644
index 0000000000..476eb2472d
--- /dev/null
+++ b/usr/src/uts/common/fs/dev/sdev_nsconfig_mod.c
@@ -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)
+{
+}
diff --git a/usr/src/uts/common/fs/dev/sdev_profile.c b/usr/src/uts/common/fs/dev/sdev_profile.c
new file mode 100644
index 0000000000..009dc4f8d5
--- /dev/null
+++ b/usr/src/uts/common/fs/dev/sdev_profile.c
@@ -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);
+}
diff --git a/usr/src/uts/common/fs/dev/sdev_ptsops.c b/usr/src/uts/common/fs/dev/sdev_ptsops.c
new file mode 100644
index 0000000000..7ec53cf417
--- /dev/null
+++ b/usr/src/uts/common/fs/dev/sdev_ptsops.c
@@ -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
+};
diff --git a/usr/src/uts/common/fs/dev/sdev_subr.c b/usr/src/uts/common/fs/dev/sdev_subr.c
new file mode 100644
index 0000000000..ddca87ac61
--- /dev/null
+++ b/usr/src/uts/common/fs/dev/sdev_subr.c
@@ -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);
+}
diff --git a/usr/src/uts/common/fs/dev/sdev_vfsops.c b/usr/src/uts/common/fs/dev/sdev_vfsops.c
new file mode 100644
index 0000000000..6ecea19f3f
--- /dev/null
+++ b/usr/src/uts/common/fs/dev/sdev_vfsops.c
@@ -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);
+}
diff --git a/usr/src/uts/common/fs/dev/sdev_vnops.c b/usr/src/uts/common/fs/dev/sdev_vnops.c
new file mode 100644
index 0000000000..da579439ee
--- /dev/null
+++ b/usr/src/uts/common/fs/dev/sdev_vnops.c
@@ -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);
diff --git a/usr/src/uts/common/fs/devfs/devfs_subr.c b/usr/src/uts/common/fs/devfs/devfs_subr.c
index 42dd03db8f..819ed9ba56 100644
--- a/usr/src/uts/common/fs/devfs/devfs_subr.c
+++ b/usr/src/uts/common/fs/devfs/devfs_subr.c
@@ -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 @@ dv_vattr_merge(struct dv_node *dv, struct vattr *vap)
}
/*
+ * 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 @@ dv_shadow_node(
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 @@ lookup:
/*
* 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);
diff --git a/usr/src/uts/common/fs/namefs/namevfs.c b/usr/src/uts/common/fs/namefs/namevfs.c
index 65cb102bc3..5d4b270661 100644
--- a/usr/src/uts/common/fs/namefs/namevfs.c
+++ b/usr/src/uts/common/fs/namefs/namevfs.c
@@ -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 @@ nm_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *crp)
}
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);
diff --git a/usr/src/uts/common/fs/vfs.c b/usr/src/uts/common/fs/vfs.c
index 2c426a0591..4cfe749214 100644
--- a/usr/src/uts/common/fs/vfs.c
+++ b/usr/src/uts/common/fs/vfs.c
@@ -123,12 +123,14 @@ static struct ipmnt *vfs_miplist_end = NULL;
*/
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 @@ vfs_mountdevices(void)
*/
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_mountdevices(void)
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 @@ vfs_mountroot(void)
}
/*
- * 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");
diff --git a/usr/src/uts/common/io/ptm.c b/usr/src/uts/common/io/ptm.c
index 82601c19ec..bd4dc10511 100644
--- a/usr/src/uts/common/io/ptm.c
+++ b/usr/src/uts/common/io/ptm.c
@@ -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 @@ ptmopen(
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 @@ ptmopen(
}
/*
- * 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 @@ ptmwput(queue_t *qp, mblk_t *mp)
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;
diff --git a/usr/src/uts/common/io/ptms_conf.c b/usr/src/uts/common/io/ptms_conf.c
index fa7f6366ce..b1b48f748b 100644
--- a/usr/src/uts/common/io/ptms_conf.c
+++ b/usr/src/uts/common/io/ptms_conf.c
@@ -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 struct pt_ttys **ptms_slots = NULL; /* Slots for actual pt structures */
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 @@ ptms_init(void)
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 @@ ptms_init(void)
mutex_exit(&ptms_lock);
}
-static void
-ptms_create_node(dev_info_t *devi, minor_t i)
-{
- char name[22]; /* For representing 64-bit minor + NUL */
-
- (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);
- }
-}
-
/*
- * Create nodes in /dev/pts directory.
- * Called from pts_attach.
+ * This routine attaches the pts dip.
*/
int
-ptms_create_pts_nodes(dev_info_t *devi)
+ptms_attach_slave(void)
{
- uint_t i;
-
- 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);
-
- mutex_exit(&ptms_lock);
+ if (pts_dip == NULL && i_ddi_attach_pseudo_node("pts") == NULL)
+ return (-1);
- return (DDI_SUCCESS);
+ ASSERT(pts_dip);
+ return (0);
}
/*
- * Destroy nodes in /dev/pts directory.
- * Called from pts_detach.
+ * Called from /dev fs. Checks if dip is attached,
+ * and if it is, returns its major number.
*/
-int
-ptms_destroy_pts_nodes(dev_info_t *devi)
+major_t
+ptms_slave_attached(void)
{
+ major_t maj = (major_t)-1;
+
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;
+ if (pts_dip)
+ maj = ddi_driver_major(pts_dip);
mutex_exit(&ptms_lock);
- return (DDI_SUCCESS);
+
+ return (maj);
}
/*
@@ -400,14 +379,6 @@ pt_ttys_alloc(void)
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_ttys_alloc(void)
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 @@ ptms_minor2ptty(minor_t dminor)
}
/*
+ * 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 @@ ptms_grow()
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 @@ ptms_grow()
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_grow()
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));
diff --git a/usr/src/uts/common/io/pts.c b/usr/src/uts/common/io/pts.c
index 8d7bbc5586..fa7997c146 100644
--- a/usr/src/uts/common/io/pts.c
+++ b/usr/src/uts/common/io/pts.c
@@ -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 @@ pts_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
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*/
diff --git a/usr/src/uts/common/os/devcfg.c b/usr/src/uts/common/os/devcfg.c
index d78dacc1fe..9a2b6b745f 100644
--- a/usr/src/uts/common/os/devcfg.c
+++ b/usr/src/uts/common/os/devcfg.c
@@ -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 *clone_dip;
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 @@ static ulong_t devinfo_attach_detach = 0;
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_ddi_forceattach_drivers()
* 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 @@ i_ddi_io_initialized()
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 @@ i_ddi_di_cache_invalidate(int kmflag)
}
/*
- * 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;
diff --git a/usr/src/uts/common/os/devctl.c b/usr/src/uts/common/os/devctl.c
index 57a7c05ac8..90354cde34 100644
--- a/usr/src/uts/common/os/devctl.c
+++ b/usr/src/uts/common/os/devctl.c
@@ -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_discovery_secs = 0;
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 kmutex_t devid_discovery_mutex;
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 @@ i_ddi_devices_init(void)
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 @@ e_fwrite_nvlist(nvfd_t *nvfd, nvlist_t *nvl)
}
static void
-nvp_free(nvp_list_t *np)
+devid_nvp_free(nvp_list_t *np)
+{
+ nvp_devid_t *dp = NVP2DEVID(np);
+
+ 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
+sdev_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_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(np, sizeof (nvp_list_t));
+ kmem_free(dp, sizeof (nvp_devname_t));
}
static void
-nvp_list_free(nvp_list_t *nvp)
+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 @@ nfd_devid_free_and_unlink(nvfd_t *nvf, nvp_list_t *np)
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 @@ nfd_devid_link(nvfd_t *nvf, nvp_list_t *np)
/*
* 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 @@ nvlist_to_nvp(nvlist_t *nvl, char *name)
}
}
- 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 @@ nvp_to_nvlist(nvfd_t *nvfd, nvlist_t **ret_nvl)
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 @@ nvp_to_nvlist(nvfd_t *nvfd, nvlist_t **ret_nvl)
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 @@ err:
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 @@ fread_nvp_list(nvfd_t *nvfd)
* 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 @@ fread_nvp_list(nvfd_t *nvfd)
error:
nvlist_free(nvl);
if (nvp_list)
- nvp_list_free(nvp_list);
+ nvp_list_free(nvfd, nvp_list);
return (rval);
}
@@ -810,7 +987,7 @@ i_ddi_read_one_nvfile(nvfd_t *nvfd)
{
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 @@ i_ddi_read_one_nvfile(nvfd_t *nvfd)
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 (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;
+ if (devid_cache_read_disable == 0) {
+ ASSERT(dcfd->nvf_list == NULL);
+ (void) i_ddi_read_one_nvfile(dcfd);
}
- dcfd->nvf_flags = nvfd.nvf_flags;
-
- rw_exit(&dcfd->nvf_lock);
+}
- rw_destroy(&nvfd.nvf_lock);
+/* 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);
+ }
}
static int
@@ -1003,8 +1167,8 @@ e_ddi_devid_discovery(ddi_devid_t devid)
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 @@ e_devid_cache_register(dev_info_t *dip, ddi_devid_t devid)
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 @@ e_devid_cache_register(dev_info_t *dip, ddi_devid_t devid)
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 @@ e_devid_cache_register(dev_info_t *dip, ddi_devid_t devid)
* 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 @@ e_devid_cache_register(dev_info_t *dip, ddi_devid_t devid)
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 @@ e_devid_cache_register(dev_info_t *dip, ddi_devid_t devid)
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 @@ exit:
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 @@ exit:
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 @@ e_devid_cache_unregister(dev_info_t *dip)
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 @@ static int
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 @@ e_devid_cache_devi_path_lists(ddi_devid_t devid, int retmax,
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 clock_t nvpticks;
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 @@ i_ddi_start_flush_daemon(void)
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_timeout(void *arg)
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 @@ nvpflush_timeout(void *arg)
}
}
-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 @@ nvpflush_one(nvfd_t *nvfd)
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 @@ nvpflush_one(nvfd_t *nvfd)
return (rval);
}
+
static void
nvpflush_daemon(void)
{
callb_cpr_t cprinfo;
clock_t clk;
int rval;
+ int i;
ASSERT(modrootloaded);
@@ -1701,21 +1887,40 @@ nvpflush_daemon(void)
* 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)
{
diff --git a/usr/src/uts/common/os/modconf.c b/usr/src/uts/common/os/modconf.c
index 35eb1c6825..2992567207 100644
--- a/usr/src/uts/common/os/modconf.c
+++ b/usr/src/uts/common/os/modconf.c
@@ -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 @@ struct mod_ops mod_dacfops = {
};
/*
+ * /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 @@ mod_removepcbe(struct modlpcbe *modl, struct modlinkage *modlp)
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
diff --git a/usr/src/uts/common/os/modctl.c b/usr/src/uts/common/os/modctl.c
index e776a36310..3509231ee2 100644
--- a/usr/src/uts/common/os/modctl.c
+++ b/usr/src/uts/common/os/modctl.c
@@ -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 @@ new_vfs_in_modpath()
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 @@ modctl_getmaj(char *uname, uint_t ulen, int *umajorp)
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 @@ modctl_allocpriv(const char *name)
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 @@ modctl(int cmd, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4,
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 @@ mod_in_autounload()
if (c == 0) \
return (0);
-static int
+int
gmatch(const char *s, const char *p)
{
int c, sc;
diff --git a/usr/src/uts/common/os/vfs_conf.c b/usr/src/uts/common/os/vfs_conf.c
index c43cd62c39..ae953b5030 100644
--- a/usr/src/uts/common/os/vfs_conf.c
+++ b/usr/src/uts/common/os/vfs_conf.c
@@ -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 @@ struct vfssw vfssw[] = {
{ "swapfs", swapinit }, /* SWAPFS */
{ "mntfs" }, /* MNTFS */
{ "devfs" }, /* DEVFS */
+ { "dev" }, /* DEV */
{ "ctfs" }, /* CONTRACTFS */
{ "objfs" }, /* OBJFS */
{ "" }, /* reserved for loadable fs */
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index a784680fe6..5d135f1489 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -682,6 +682,8 @@ FSHDRS= \
cachefs_ioctl.h \
cachefs_log.h \
dv_node.h \
+ sdev_impl.h \
+ sdev_node.h \
fifonode.h \
hsfs_isospec.h \
hsfs_node.h \
diff --git a/usr/src/uts/common/sys/autoconf.h b/usr/src/uts/common/sys/autoconf.h
index 9bc655f258..9083105553 100644
--- a/usr/src/uts/common/sys/autoconf.h
+++ b/usr/src/uts/common/sys/autoconf.h
@@ -164,6 +164,7 @@ struct di_cache {
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_add_devimap(dev_info_t *dip);
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
diff --git a/usr/src/uts/common/sys/devctl_impl.h b/usr/src/uts/common/sys/devctl_impl.h
index 7c39b18c37..09dab4cfe3 100644
--- a/usr/src/uts/common/sys/devctl_impl.h
+++ b/usr/src/uts/common/sys/devctl_impl.h
@@ -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 @@ typedef struct nvpacked_file_hdr {
* 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 kfile {
*/
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 @@ static void devid_log(char *, ddi_devid_t, char *);
#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 */
diff --git a/usr/src/uts/common/sys/fs/dv_node.h b/usr/src/uts/common/sys/fs/dv_node.h
index 94721eba50..52de93794a 100644
--- a/usr/src/uts/common/sys/fs/dv_node.h
+++ b/usr/src/uts/common/sys/fs/dv_node.h
@@ -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 @@ typedef struct dv_node {
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_clean(dev_info_t *, char *, uint_t);
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);
diff --git a/usr/src/uts/common/sys/fs/sdev_impl.h b/usr/src/uts/common/sys/fs/sdev_impl.h
new file mode 100644
index 0000000000..998f70ab67
--- /dev/null
+++ b/usr/src/uts/common/sys/fs/sdev_impl.h
@@ -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 */
diff --git a/usr/src/uts/common/sys/fs/sdev_node.h b/usr/src/uts/common/sys/fs/sdev_node.h
new file mode 100644
index 0000000000..96c4733545
--- /dev/null
+++ b/usr/src/uts/common/sys/fs/sdev_node.h
@@ -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 */
diff --git a/usr/src/uts/common/sys/mntent.h b/usr/src/uts/common/sys/mntent.h
index ef1e297430..1b1fd119aa 100644
--- a/usr/src/uts/common/sys/mntent.h
+++ b/usr/src/uts/common/sys/mntent.h
@@ -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 @@ extern "C" {
#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 */
diff --git a/usr/src/uts/common/sys/modctl.h b/usr/src/uts/common/sys/modctl.h
index d2f4cfd803..1093eddef6 100644
--- a/usr/src/uts/common/sys/modctl.h
+++ b/usr/src/uts/common/sys/modctl.h
@@ -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_syscallops32;
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 modlpcbe {
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 @@ struct modlinkage {
#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 @@ struct modlinkage {
#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 @@ typedef struct modctl {
#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 struct dev_ops *mod_hold_dev_by_devi(dev_info_t *);
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 **);
diff --git a/usr/src/uts/common/sys/ptms.h b/usr/src/uts/common/sys/ptms.h
index 47dbabf40c..9aa6493956 100644
--- a/usr/src/uts/common/sys/ptms.h
+++ b/usr/src/uts/common/sys/ptms.h
@@ -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 @@ struct pt_ttys {
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 void ptms_init(void);
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 @@ extern void ptms_logp(char *, uintptr_t);
* 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
}
diff --git a/usr/src/uts/common/sys/sunddi.h b/usr/src/uts/common/sys/sunddi.h
index 672bd7d444..6d97d3e3d8 100644
--- a/usr/src/uts/common/sys/sunddi.h
+++ b/usr/src/uts/common/sys/sunddi.h
@@ -241,6 +241,7 @@ extern "C" {
#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"
diff --git a/usr/src/uts/common/syscall/uadmin.c b/usr/src/uts/common/syscall/uadmin.c
index eba90cb1ab..3b9833088e 100644
--- a/usr/src/uts/common/syscall/uadmin.c
+++ b/usr/src/uts/common/syscall/uadmin.c
@@ -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 @@ kadmin(int cmd, int fcn, void *mdep, cred_t *credp)
curthread->t_cred = kcred;
}
+ /* indicate shutdown in progress */
+ sys_shutdown = 1;
+
/*
* Communcate that init shouldn't be restarted.
*/
diff --git a/usr/src/uts/i86pc/os/startup.c b/usr/src/uts/i86pc/os/startup.c
index 8ac9c6ffec..209ad07bca 100644
--- a/usr/src/uts/i86pc/os/startup.c
+++ b/usr/src/uts/i86pc/os/startup.c
@@ -1352,6 +1352,9 @@ startup_modules(void)
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();
diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared
index cc4c7b8c9c..7636718bd9 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -415,7 +415,7 @@ SCHED_KMODS += IA RT TS RT_DPTBL TS_DPTBL FSS FX FX_DPTBL
#
# 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 @@ PCBE_KMODS += p123_pcbe p4_pcbe opteron_pcbe
# MAC-Type Plugin Modules (/kernel/mac)
#
MAC_KMODS += mac_ether
+
+#
+# 'Devname' Modules (kernel/devname)
+#
+DEVNAME_KMODS += sdev_nsconfig_mod
diff --git a/usr/src/uts/intel/dev/Makefile b/usr/src/uts/intel/dev/Makefile
new file mode 100644
index 0000000000..4ec567573f
--- /dev/null
+++ b/usr/src/uts/intel/dev/Makefile
@@ -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
diff --git a/usr/src/uts/intel/ia32/ml/modstubs.s b/usr/src/uts/intel/ia32/ml/modstubs.s
index 480e4d2edf..1b84107a84 100644
--- a/usr/src/uts/intel/ia32/ml/modstubs.s
+++ b/usr/src/uts/intel/ia32/ml/modstubs.s
@@ -420,6 +420,20 @@ fcnname/**/_info: \
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.
*/
diff --git a/usr/src/uts/intel/sdev_nsconfig_mod/Makefile b/usr/src/uts/intel/sdev_nsconfig_mod/Makefile
new file mode 100644
index 0000000000..9f7d2215e2
--- /dev/null
+++ b/usr/src/uts/intel/sdev_nsconfig_mod/Makefile
@@ -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
+
diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared
index 64ccfaf037..c149117361 100644
--- a/usr/src/uts/sparc/Makefile.sparc.shared
+++ b/usr/src/uts/sparc/Makefile.sparc.shared
@@ -328,7 +328,7 @@ SCHED_KMODS += RT TS RT_DPTBL TS_DPTBL IA FSS FX FX_DPTBL
#
# 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 @@ DACF_KMODS += usb_ac_dacf
# MAC-Type Plugin Modules (/kernel/mac)
#
MAC_KMODS += mac_ether
+
+#
+# 'Devname' Modules (kernel/devname)
+#
+DEVNAME_KMODS += sdev_nsconfig_mod
diff --git a/usr/src/uts/sparc/dev/Makefile b/usr/src/uts/sparc/dev/Makefile
new file mode 100644
index 0000000000..dd1ad57edf
--- /dev/null
+++ b/usr/src/uts/sparc/dev/Makefile
@@ -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
diff --git a/usr/src/uts/sparc/ml/modstubs.s b/usr/src/uts/sparc/ml/modstubs.s
index 1cf7a4f293..c4e235909a 100644
--- a/usr/src/uts/sparc/ml/modstubs.s
+++ b/usr/src/uts/sparc/ml/modstubs.s
@@ -306,6 +306,23 @@ stubs_base:
#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.
*/
diff --git a/usr/src/uts/sparc/sdev_nsconfig_mod/Makefile b/usr/src/uts/sparc/sdev_nsconfig_mod/Makefile
new file mode 100644
index 0000000000..dcf823f89b
--- /dev/null
+++ b/usr/src/uts/sparc/sdev_nsconfig_mod/Makefile
@@ -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