Skip to content

Commit

Permalink
"always replace spaces in symlinks, unless string_escape=none" -- Thi…
Browse files Browse the repository at this point in the history
…s fix is backported from systemd which resolves an issue with whitespace characters in symbolic links created by [e]udev. See systemd/systemd issue # 4833 and systemd/systemd pull request # 4837 for details. This issue was originally observed with some NVMe devices when using eudev 3.2.x and this fix resolves eudev issue # 148.
  • Loading branch information
msmith626 committed Aug 25, 2017
1 parent 6b5db4f commit 5c39ec9
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 28 deletions.
2 changes: 1 addition & 1 deletion src/libudev/libudev-util.c
Expand Up @@ -241,7 +241,7 @@ int util_replace_whitespace(const char *str, char *to, size_t len)
to[j++] = str[i++];
}
to[j] = '\0';
return 0;
return j;
}

/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
Expand Down
41 changes: 36 additions & 5 deletions src/udev/udev-event.c
Expand Up @@ -62,7 +62,9 @@ void udev_event_unref(struct udev_event *event) {
free(event);
}

size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size) {
size_t udev_event_apply_format(struct udev_event *event,
const char *src, char *dest, size_t size,
bool replace_whitespace) {
struct udev_device *dev = event->dev;
enum subst_type {
SUBST_UNKNOWN,
Expand Down Expand Up @@ -117,8 +119,10 @@ size_t udev_event_apply_format(struct udev_event *event, const char *src, char *

for (;;) {
enum subst_type type = SUBST_UNKNOWN;
char attrbuf[UTIL_PATH_SIZE];
char *attr = NULL;
char attrbuf[UTIL_PATH_SIZE], sbuf[UTIL_PATH_SIZE];
char *attr = NULL, *_s;
size_t _l;
bool replws = replace_whitespace;

while (from[0] != '\0') {
if (from[0] == '$') {
Expand Down Expand Up @@ -187,6 +191,19 @@ size_t udev_event_apply_format(struct udev_event *event, const char *src, char *
attr = NULL;
}

/* result subst handles space as field separator */
if (type == SUBST_RESULT)
replws = false;

if (replws) {
/* store dest string ptr and remaining len */
_s = s;
_l = l;
/* temporarily use sbuf */
s = &sbuf;
l = UTIL_PATH_SIZE;
}

switch (type) {
case SUBST_DEVPATH:
l = strpcpy(&s, l, udev_device_get_devpath(dev));
Expand Down Expand Up @@ -367,6 +384,20 @@ size_t udev_event_apply_format(struct udev_event *event, const char *src, char *
log_error("unknown substitution type=%i", type);
break;
}

/* replace whitespace in sbuf and copy to dest */
if (replws) {
size_t tmplen = UTIL_PATH_SIZE - l;

/* restore s and l to dest string values */
s = _s;
l = _l;

/* copy ws-replaced value to s */
tmplen = util_replace_whitespace(sbuf, s, MIN(tmplen, l));
l -= tmplen;
s += tmplen;
}
}

out:
Expand Down Expand Up @@ -1026,7 +1057,7 @@ void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_
if (builtin_cmd < UDEV_BUILTIN_MAX) {
char command[UTIL_PATH_SIZE];

udev_event_apply_format(event, cmd, command, sizeof(command));
udev_event_apply_format(event, cmd, command, sizeof(command), false);
udev_builtin_run(event->dev, builtin_cmd, command, false);
} else {
char program[UTIL_PATH_SIZE];
Expand All @@ -1037,7 +1068,7 @@ void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_
sleep(event->exec_delay);
}

udev_event_apply_format(event, cmd, program, sizeof(program));
udev_event_apply_format(event, cmd, program, sizeof(program), false);
envp = udev_device_get_properties_envp(event->dev);
udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, sigmask, NULL, 0);
}
Expand Down
40 changes: 20 additions & 20 deletions src/udev/udev-rules.c
Expand Up @@ -1849,7 +1849,7 @@ static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct
name = rules_str(rules, cur->key.attr_off);
switch (cur->key.attrsubst) {
case SB_FORMAT:
udev_event_apply_format(event, name, nbuf, sizeof(nbuf));
udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false);
name = nbuf;
/* fall through */
case SB_NONE:
Expand Down Expand Up @@ -2006,7 +2006,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
char filename[UTIL_PATH_SIZE];
int found;

udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false);
found = (wait_for_file(event->dev, filename, 10) == 0);
if (!found && (cur->key.op != OP_NOMATCH))
goto nomatch;
Expand All @@ -2021,7 +2021,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
_cleanup_free_ char *value = NULL;
size_t len;

udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename));
udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false);
sysctl_normalize(filename);
if (sysctl_read(filename, &value) < 0)
goto nomatch;
Expand Down Expand Up @@ -2099,7 +2099,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
struct stat statbuf;
int match;

udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false);
if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) {
if (filename[0] != '/') {
char tmp[UTIL_PATH_SIZE];
Expand Down Expand Up @@ -2127,7 +2127,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,

free(event->program_result);
event->program_result = NULL;
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), program, sizeof(program));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), program, sizeof(program), false);
envp = udev_device_get_properties_envp(event->dev);
log_debug("PROGRAM '%s' %s:%u",
program,
Expand Down Expand Up @@ -2155,7 +2155,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
case TK_M_IMPORT_FILE: {
char import[UTIL_PATH_SIZE];

udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
if (import_file_into_properties(event->dev, import) != 0)
if (cur->key.op != OP_NOMATCH)
goto nomatch;
Expand All @@ -2164,7 +2164,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
case TK_M_IMPORT_PROG: {
char import[UTIL_PATH_SIZE];

udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
log_debug("IMPORT '%s' %s:%u",
import,
rules_str(rules, rule->rule.filename_off),
Expand Down Expand Up @@ -2195,7 +2195,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
event->builtin_run |= (1 << cur->key.builtin_cmd);
}

udev_event_apply_format(event, rules_str(rules, cur->key.value_off), command, sizeof(command));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), command, sizeof(command), false);
log_debug("IMPORT builtin '%s' %s:%u",
udev_builtin_name(cur->key.builtin_cmd),
rules_str(rules, rule->rule.filename_off),
Expand Down Expand Up @@ -2265,7 +2265,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
case TK_M_IMPORT_PARENT: {
char import[UTIL_PATH_SIZE];

udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
if (import_parent_into_properties(event->dev, import) != 0)
if (cur->key.op != OP_NOMATCH)
goto nomatch;
Expand Down Expand Up @@ -2303,7 +2303,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
break;
if (cur->key.op == OP_ASSIGN_FINAL)
event->owner_final = true;
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner), false);
event->owner_set = true;
r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL);
if (r < 0) {
Expand All @@ -2329,7 +2329,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
break;
if (cur->key.op == OP_ASSIGN_FINAL)
event->group_final = true;
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group), false);
event->group_set = true;
r = get_group_creds(&gr, &event->gid);
if (r < 0) {
Expand All @@ -2353,7 +2353,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,

if (event->mode_final)
break;
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), mode_str, sizeof(mode_str));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), mode_str, sizeof(mode_str), false);
mode = strtol(mode_str, &endptr, 8);
if (endptr[0] != '\0') {
log_error("ignoring invalid mode '%s'", mode_str);
Expand Down Expand Up @@ -2438,10 +2438,10 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
char temp[UTIL_NAME_SIZE];

/* append value separated by space */
udev_event_apply_format(event, value, temp, sizeof(temp));
udev_event_apply_format(event, value, temp, sizeof(temp), false);
strscpyl(value_new, sizeof(value_new), value_old, " ", temp, NULL);
} else
udev_event_apply_format(event, value, value_new, sizeof(value_new));
udev_event_apply_format(event, value, value_new, sizeof(value_new), false);

udev_device_add_property(event->dev, name, value_new);
break;
Expand All @@ -2450,7 +2450,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
char tag[UTIL_PATH_SIZE];
const char *p;

udev_event_apply_format(event, rules_str(rules, cur->key.value_off), tag, sizeof(tag));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), tag, sizeof(tag), false);
if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL)
udev_device_cleanup_tags_list(event->dev);
for (p = tag; *p != '\0'; p++) {
Expand Down Expand Up @@ -2478,7 +2478,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
break;
if (cur->key.op == OP_ASSIGN_FINAL)
event->name_final = true;
udev_event_apply_format(event, name, name_str, sizeof(name_str));
udev_event_apply_format(event, name, name_str, sizeof(name_str), false);
if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) {
count = util_replace_chars(name_str, "/");
if (count > 0)
Expand Down Expand Up @@ -2515,7 +2515,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
udev_device_cleanup_devlinks_list(event->dev);

/* allow multiple symlinks separated by spaces */
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), temp, sizeof(temp));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), temp, sizeof(temp), esc != ESCAPE_NONE);
if (esc == ESCAPE_UNSET)
count = util_replace_chars(temp, "/ ");
else if (esc == ESCAPE_REPLACE)
Expand Down Expand Up @@ -2555,7 +2555,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL);
attr_subst_subdir(attr, sizeof(attr));

udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
log_debug("ATTR '%s' writing '%s' %s:%u", attr, value,
rules_str(rules, rule->rule.filename_off),
rule->rule.filename_line);
Expand All @@ -2574,9 +2574,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules,
char value[UTIL_NAME_SIZE];
int r;

udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename));
udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false);
sysctl_normalize(filename);
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value));
udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
log_debug("SYSCTL '%s' writing '%s' %s:%u", filename, value,
rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
r = sysctl_write(filename, value);
Expand Down
4 changes: 3 additions & 1 deletion src/udev/udev.h
Expand Up @@ -81,7 +81,9 @@ int udev_rules_apply_static_dev_perms(struct udev_rules *rules);
/* udev-event.c */
struct udev_event *udev_event_new(struct udev_device *dev);
void udev_event_unref(struct udev_event *event);
size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size);
size_t udev_event_apply_format(struct udev_event *event,
const char *src, char *dest, size_t size,
bool replace_whitespace);
int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string,
char *result, size_t maxsize, int read_value);
int udev_event_spawn(struct udev_event *event,
Expand Down
2 changes: 1 addition & 1 deletion src/udev/udevadm-test.c
Expand Up @@ -153,7 +153,7 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) {
udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) {
char program[UTIL_PATH_SIZE];

udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program));
udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program), false);
printf("run: '%s'\n", program);
}
out:
Expand Down

0 comments on commit 5c39ec9

Please sign in to comment.