From: Yu Watanabe Date: Sat, 11 Jan 2025 23:22:53 +0000 (+0900) Subject: udev-rules: use sd_device_set_sysattr_value() to write sysfs attribute X-Git-Tag: v258-rc1~1490^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a9559ebcbcf3098b2c14e5e10e99d05aaffe4ac3;p=thirdparty%2Fsystemd.git udev-rules: use sd_device_set_sysattr_value() to write sysfs attribute Then, we can avoid that files outside of sysfs are written by udev ATTR key. This also makes - logs failure in udev_resolve_subsys_kernel(), - failure in sd_device_get_syspath() critical, as that should not happen, - cache the value to be write when running on test mode, to make it shown by OPTIONS="dump" or obtained by ATTR match token. --- diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index f8afe923d84..3c0133d55e4 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -2960,50 +2960,85 @@ static int udev_rule_apply_token_to_event( } case TK_A_ATTR: { char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE]; - const char *val, *key_name = token->data; + const char *key = token->data; + + /* First, try to resolve "[/]" format. */ + r = udev_resolve_subsys_kernel(key, buf, sizeof(buf), false); + if (r == -ENOMEM) + return log_oom(); + if (ERRNO_IS_NEG_DEVICE_ABSENT(r)) { + log_event_warning_errno(event, token, r, "Failed to resolve sysfs attribute \"%s\", ignoring: %m", key); + return true; + } + if (r < 0) { + /* If not, make the path to sysfs attribute absolute, to make '*' resolvable by attr_subst_subdir(). */ + const char *syspath; + r = sd_device_get_syspath(dev, &syspath); + if (r < 0) + return log_event_error_errno(event, token, r, "Failed to get syspath: %m"); - if (udev_resolve_subsys_kernel(key_name, buf, sizeof(buf), false) < 0 && - sd_device_get_syspath(dev, &val) >= 0) { bool truncated; - strscpyl_full(buf, sizeof(buf), &truncated, val, "/", key_name, NULL); + strscpyl_full(buf, sizeof(buf), &truncated, syspath, "/", key, NULL); if (truncated) { log_event_warning(event, token, "The path to the attribute \"%s/%s\" is too long, refusing to set the attribute.", - val, key_name); + syspath, key); + return true; + } + + /* Resolve '*' in the path. */ + r = attr_subst_subdir(buf); + if (r < 0) { + log_event_error_errno(event, token, r, "Could not find file matches \"%s\", ignoring: %m", buf); return true; } } - r = attr_subst_subdir(buf); + /* Then, make the path relative again. This also checks if the path being inside of the sysfs. */ + _cleanup_free_ char *resolved = NULL; + _cleanup_close_ int fd = -EBADF; + r = device_chase(dev, buf, /* flags = */ 0, &resolved, &fd); if (r < 0) { - log_event_error_errno(event, token, r, "Could not find file matches \"%s\", ignoring: %m", buf); + log_event_error_errno(event, token, r, "Could not chase sysfs attribute \"%s\", ignoring: %m", buf); return true; } + /* Apply formatting to the value. */ if (!apply_format_value(event, token, value, sizeof(value), "attribute value")) return true; if (EVENT_MODE_DESTRUCTIVE(event)) { - log_event_debug(event, token, "Writing \"%s\" to sysfs attribute \"%s\".", value, buf); - r = write_string_file(buf, value, - WRITE_STRING_FILE_VERIFY_ON_FAILURE | - WRITE_STRING_FILE_DISABLE_BUFFER | - WRITE_STRING_FILE_AVOID_NEWLINE | - WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE); + log_event_debug(event, token, "Writing \"%s\" to sysfs attribute \"%s\".", value, resolved); + r = sd_device_set_sysattr_value(dev, resolved, value); if (r < 0) - log_event_error_errno(event, token, r, "Failed to write \"%s\" to sysfs attribute \"%s\", ignoring: %m", value, buf); + log_event_error_errno(event, token, r, "Failed to write \"%s\" to sysfs attribute \"%s\", ignoring: %m", value, resolved); else { - event_cache_written_sysattr(event, buf, value); + event_cache_written_sysattr(event, resolved, value); log_event_done(event, token); } } else { - log_event_debug(event, token, "Running in test mode, skipping writing \"%s\" to sysfs attribute \"%s\".", value, buf); + log_event_debug(event, token, "Running in test mode, skipping writing \"%s\" to sysfs attribute \"%s\".", value, resolved); - r = verify_regular_at(AT_FDCWD, buf, /* follow = */ false); + /* We assume the attribute is writable if the path points to a regular file, and cache + * the value to make it shown by OPTIONS="dump" or obtained by later ATTR match token. */ + r = fd_verify_regular(fd); if (r < 0 && !ERRNO_IS_NEG_PRIVILEGE(r)) - log_event_error_errno(event, token, r, "Failed to verify sysfs attribute \"%s\" is a regular file: %m", buf); - else - event_cache_written_sysattr(event, buf, value); + log_event_error_errno(event, token, r, "Failed to verify sysfs attribute \"%s\" is a regular file: %m", resolved); + else { + event_cache_written_sysattr(event, resolved, value); + + _cleanup_free_ char *copied = strdup(value); + if (!copied) + return log_oom(); + + r = device_cache_sysattr_value(dev, resolved, value, /* error = */ 0); + if (r < 0) + log_event_warning_errno(event, token, r, "Failed to cache sysfs attribute \"%s\", ignoring: %m", resolved); + else if (r > 0) { + TAKE_PTR(resolved); + TAKE_PTR(copied); + } + } } return true; }