diff options
Diffstat (limited to 'usr/src')
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(®ex, 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(®ex, 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(®ex, 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(®ex, 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(®ex); - } + } else { + finddev_close(handle); + } + free(newpath); + free(match); + if (!alwaysmatch) { + regfree(®ex); } 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 |