int major, minor, type, len;
int mode = 0660;
#if ENABLE_FEATURE_MDEV_CONF
- uid_t uid = 0;
- gid_t gid = 0;
+ struct bb_uidgid_t ugid = { 0, 0 };
parser_t *parser;
char *tokens[5];
#endif
/* Try to read major/minor string. Note that the kernel puts \n after
* the data, so we don't need to worry about null terminating the string
- * because sscanf() will stop at the first nondigit, which \n is. We
- * also depend on path having writeable space after it.
+ * because sscanf() will stop at the first nondigit, which \n is.
+ * We also depend on path having writeable space after it.
*/
+ major = -1;
if (!delete) {
strcpy(dev_maj_min, "/dev");
len = open_read_close(path, dev_maj_min + 1, 64);
return;
/* no "dev" file, so just try to run script */
*dev_maj_min = '\0';
+ } else if (sscanf(dev_maj_min, "%u:%u", &major, &minor) != 2) {
+ major = -1;
}
}
* "/sys/block/..." is for block devices. "/sys/bus" etc is not.
* But since 2.6.25 block devices are also in /sys/class/block.
* We use strstr("/block/") to forestall future surprises. */
- type = S_IFCHR;
+ type = S_IFCHR;
if (strstr(path, "/block/"))
- type = S_IFBLK;
+ type = S_IFBLK;
#if ENABLE_FEATURE_MDEV_CONF
parser = config_open2("/etc/mdev.conf", fopen_for_read);
/* If we have config file, look up user settings */
- if (!parser)
- goto end_parse;
-
- while (config_read(parser, tokens, 4, 3, "# \t", PARSE_LAST_IS_GREEDY)) {
+ while (config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL)) {
regmatch_t off[1 + 9*ENABLE_FEATURE_MDEV_RENAME_REGEXP];
char *val;
/* Fields: regex uid:gid mode [alias] [cmd] */
- /* 1st field: regex to match this device */
- {
+ /* 1st field: @<numeric maj,min>... */
+ if (tokens[0][0] == '@') {
+ /* @major,minor[-last] */
+ /* (useful when name is ambiguous:
+ * "/sys/class/usb/lp0" and
+ * "/sys/class/printer/lp0") */
+ int cmaj, cmin0, cmin1, sc;
+ if (major < 0)
+ continue; /* no dev, no match */
+ sc = sscanf(tokens[0], "@%u,%u-%u", &cmaj, &cmin0, &cmin1);
+ if (sc < 1 || major != cmaj
+ || (sc == 2 && minor != cmin0)
+ || (sc == 3 && (minor < cmin0 || minor > cmin1))
+ ) {
+ continue; /* no match */
+ }
+ } else { /* ... or regex to match device name */
regex_t match;
int result;
* after parsing the rest of fields */
/* 2nd field: uid:gid - device ownership */
- {
- struct passwd *pass;
- struct group *grp;
- char *str_uid = tokens[1];
- char *str_gid = strchrnul(str_uid, ':');
-
- if (*str_gid)
- *str_gid++ = '\0';
- /* Parse UID */
- pass = getpwnam(str_uid);
- if (pass)
- uid = pass->pw_uid;
- else
- uid = strtoul(str_uid, NULL, 10);
- /* Parse GID */
- grp = getgrnam(str_gid);
- if (grp)
- gid = grp->gr_gid;
- else
- gid = strtoul(str_gid, NULL, 10);
- }
+ parse_chown_usergroup_or_die(&ugid, tokens[1]);
/* 3rd field: mode - device permissions */
mode = strtoul(tokens[2], NULL, 8);
unsigned i, n;
#endif
char *a = val;
- s = strchr(val, ' ');
- val = (s && s[1]) ? s+1 : NULL;
+ s = strchrnul(val, ' ');
+ val = (s[0] && s[1]) ? s+1 : NULL;
+ s[0] = '\0';
#if ENABLE_FEATURE_MDEV_RENAME_REGEXP
/* substitute %1..9 with off[1..9], if any */
n = 0;
} /* end of "while line is read from /etc/mdev.conf" */
config_close(parser);
- end_parse:
#endif /* ENABLE_FEATURE_MDEV_CONF */
- if (!delete && sscanf(dev_maj_min, "%u:%u", &major, &minor) == 2) {
+ if (!delete && major >= 0) {
if (ENABLE_FEATURE_MDEV_RENAME)
unlink(device_name);
symlink(device_name, "root");
#if ENABLE_FEATURE_MDEV_CONF
- chown(device_name, uid, gid);
+ chown(device_name, ugid.uid, ugid.gid);
#if ENABLE_FEATURE_MDEV_RENAME
if (alias) {
putenv(s);
if (system(command) == -1)
bb_perror_msg_and_die("can't run '%s'", command);
- s[4] = '\0';
- unsetenv(s);
+ unsetenv("MDEV");
free(s);
free(command);
}
* /sys/block/loop* (for example) are symlinks to dirs,
* not real directories.
* (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs,
- * but we can't enforce that on users) */
- recursive_action("/sys/block",
- ACTION_RECURSE | ACTION_FOLLOWLINKS,
- fileAction, dirAction, temp, 0);
+ * but we can't enforce that on users)
+ */
+ if (access("/sys/class/block", F_OK) != 0) {
+ /* Scan obsolete /sys/block only if /sys/class/block
+ * doesn't exist. Otherwise we'll have dupes.
+ */
+ recursive_action("/sys/block",
+ ACTION_RECURSE | ACTION_FOLLOWLINKS,
+ // not needed now? | ACTION_QUIET
+ fileAction, dirAction, temp, 0);
+ }
recursive_action("/sys/class",
ACTION_RECURSE | ACTION_FOLLOWLINKS,
fileAction, dirAction, temp, 0);