summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorllai1 <none@none>2006-08-25 17:24:25 -0700
committerllai1 <none@none>2006-08-25 17:24:25 -0700
commitfacf4a8d7b59fde89a8662b4f4c73a758e6c402c (patch)
tree4e0024c5508351006df1496ec4be6e7b564c3ce8 /usr/src
parentadcafb0fe4c49c4d46c0b393dfba36d4e1b55c0e (diff)
downloadillumos-joyent-facf4a8d7b59fde89a8662b4f4c73a758e6c402c.tar.gz
PSARC/2003/246 Filesystem Driven Device Naming
5050715 logical device names not created during early boot 6292952 devfsadm mishandles optarg 6362924 devfsadm secondary link generation is not zones aware 6413127 Integrate the Devname Project 6464196 bfu should remove pt_chmod, obsoleted by /dev filesystem --HG-- rename : usr/src/cmd/pt_chmod/Makefile => deleted_files/usr/src/cmd/pt_chmod/Makefile rename : usr/src/cmd/pt_chmod/pt_chmod.c => deleted_files/usr/src/cmd/pt_chmod/pt_chmod.c
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