diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c index f07b29ab48..f67ab40354 100644 --- a/src/libudev/libudev-util.c +++ b/src/libudev/libudev-util.c @@ -2,13 +2,12 @@ #include #include -#include -#include -#include -#include + +#include "sd-device.h" #include "device-nodes.h" #include "libudev-util.h" +#include "string-util.h" #include "strxcpyx.h" #include "utf8.h" @@ -20,16 +19,14 @@ */ /* handle "[/]" format */ -int util_resolve_subsys_kernel(const char *string, - char *result, size_t maxsize, int read_value) { - char temp[UTIL_PATH_SIZE]; - char *subsys; - char *sysname; - struct udev_device *dev; - char *attr; +int util_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value) { + char temp[UTIL_PATH_SIZE], *subsys, *sysname, *attr; + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + const char *val; + int r; if (string[0] != '[') - return -1; + return -EINVAL; strscpy(temp, sizeof(temp), string); @@ -37,13 +34,13 @@ int util_resolve_subsys_kernel(const char *string, sysname = strchr(subsys, '/'); if (!sysname) - return -1; + return -EINVAL; sysname[0] = '\0'; sysname = &sysname[1]; attr = strchr(sysname, ']'); if (!attr) - return -1; + return -EINVAL; attr[0] = '\0'; attr = &attr[1]; if (attr[0] == '/') @@ -52,38 +49,38 @@ int util_resolve_subsys_kernel(const char *string, attr = NULL; if (read_value && !attr) - return -1; + return -EINVAL; - dev = udev_device_new_from_subsystem_sysname(NULL, subsys, sysname); - if (!dev) - return -1; + r = sd_device_new_from_subsystem_sysname(&dev, subsys, sysname); + if (r < 0) + return r; if (read_value) { - const char *val; - - val = udev_device_get_sysattr_value(dev, attr); - if (val) - strscpy(result, maxsize, val); - else + r = sd_device_get_sysattr_value(dev, attr, &val); + if (r < 0 && r != -ENOENT) + return r; + if (r == -ENOENT) result[0] = '\0'; + else + strscpy(result, maxsize, val); log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result); } else { - size_t l; - char *s; + r = sd_device_get_syspath(dev, &val); + if (r < 0) + return r; - s = result; - l = strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL); - if (attr) - strpcpyl(&s, l, "/", attr, NULL); - log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, attr, result); + strscpyl(result, maxsize, val, attr ? "/" : NULL, attr ?: NULL, NULL); + log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, strempty(attr), result); } - udev_device_unref(dev); return 0; } size_t util_path_encode(const char *src, char *dest, size_t size) { size_t i, j; + assert(src); + assert(dest); + for (i = 0, j = 0; src[i] != '\0'; i++) { if (src[i] == '/') { if (j+4 >= size) { @@ -126,37 +123,41 @@ size_t util_path_encode(const char *src, char *dest, size_t size) { * Note this may be called with 'str' == 'to', i.e. to replace whitespace * in-place in a buffer. This function can handle that situation. */ -int util_replace_whitespace(const char *str, char *to, size_t len) { - size_t i, j; +size_t util_replace_whitespace(const char *str, char *to, size_t len) { + bool is_space = false; + const char *p = str; + size_t j; - /* strip trailing whitespace */ - len = strnlen(str, len); - while (len && isspace(str[len-1])) - len--; + assert(str); + assert(to); - /* strip leading whitespace */ - i = 0; - while ((i < len) && isspace(str[i])) - i++; + p += strspn(p, WHITESPACE); - j = 0; - while (i < len) { - /* substitute multiple whitespace with a single '_' */ - if (isspace(str[i])) { - while (isspace(str[i])) - i++; - to[j++] = '_'; + for (j = 0; j < len && *p != '\0'; p++) { + if (isspace(*p)) { + is_space = true; + continue; } - to[j++] = str[i++]; + + if (is_space) { + if (j + 1 >= len) + break; + + to[j++] = '_'; + is_space = false; + } + to[j++] = *p; } + to[j] = '\0'; return j; } /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */ -int util_replace_chars(char *str, const char *white) { - size_t i = 0; - int replaced = 0; +size_t util_replace_chars(char *str, const char *white) { + size_t i = 0, replaced = 0; + + assert(str); while (str[i] != '\0') { int len; diff --git a/src/libudev/libudev-util.h b/src/libudev/libudev-util.h index fb5558da88..32b626ebc9 100644 --- a/src/libudev/libudev-util.h +++ b/src/libudev/libudev-util.h @@ -11,9 +11,9 @@ #define UTIL_LINE_SIZE 16384 #define UDEV_ALLOWED_CHARS_INPUT "/ $%?," size_t util_path_encode(const char *src, char *dest, size_t size); -int util_replace_whitespace(const char *str, char *to, size_t len); -int util_replace_chars(char *str, const char *white); -int util_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, int read_value); +size_t util_replace_whitespace(const char *str, char *to, size_t len); +size_t util_replace_chars(char *str, const char *white); +int util_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value); /* Cleanup functions */ DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev*, udev_unref); diff --git a/src/test/test-libudev.c b/src/test/test-libudev.c index abf79874bd..c7cc453f7e 100644 --- a/src/test/test-libudev.c +++ b/src/test/test-libudev.c @@ -6,6 +6,7 @@ #include #include +#include "alloc-util.h" #include "fd-util.h" #include "libudev-util.h" #include "log.h" @@ -329,6 +330,80 @@ static void test_hwdb(struct udev *udev, const char *modalias) { assert_se(hwdb == NULL); } +static void test_util_replace_whitespace_one_len(const char *str, size_t len, const char *expected) { + _cleanup_free_ char *result = NULL; + int r; + + result = new(char, len + 1); + assert_se(result); + r = util_replace_whitespace(str, result, len); + assert_se((size_t) r == strlen(expected)); + assert_se(streq(result, expected)); +} + +static void test_util_replace_whitespace_one(const char *str, const char *expected) { + test_util_replace_whitespace_one_len(str, strlen(str), expected); +} + +static void test_util_replace_whitespace(void) { + test_util_replace_whitespace_one("hogehoge", "hogehoge"); + test_util_replace_whitespace_one("hoge hoge", "hoge_hoge"); + test_util_replace_whitespace_one(" hoge hoge ", "hoge_hoge"); + test_util_replace_whitespace_one(" ", ""); + test_util_replace_whitespace_one("hoge ", "hoge"); + + test_util_replace_whitespace_one_len("hoge hoge ", 9, "hoge_hoge"); + test_util_replace_whitespace_one_len("hoge hoge ", 8, "hoge_hog"); + test_util_replace_whitespace_one_len("hoge hoge ", 7, "hoge_ho"); + test_util_replace_whitespace_one_len("hoge hoge ", 6, "hoge_h"); + test_util_replace_whitespace_one_len("hoge hoge ", 5, "hoge"); + test_util_replace_whitespace_one_len("hoge hoge ", 4, "hoge"); + test_util_replace_whitespace_one_len("hoge hoge ", 3, "hog"); + test_util_replace_whitespace_one_len("hoge hoge ", 2, "ho"); + test_util_replace_whitespace_one_len("hoge hoge ", 1, "h"); + test_util_replace_whitespace_one_len("hoge hoge ", 0, ""); + + test_util_replace_whitespace_one_len(" hoge hoge ", 9, "hoge_hoge"); + test_util_replace_whitespace_one_len(" hoge hoge ", 8, "hoge_hog"); + test_util_replace_whitespace_one_len(" hoge hoge ", 7, "hoge_ho"); + test_util_replace_whitespace_one_len(" hoge hoge ", 6, "hoge_h"); + test_util_replace_whitespace_one_len(" hoge hoge ", 5, "hoge"); + test_util_replace_whitespace_one_len(" hoge hoge ", 4, "hoge"); + test_util_replace_whitespace_one_len(" hoge hoge ", 3, "hog"); + test_util_replace_whitespace_one_len(" hoge hoge ", 2, "ho"); + test_util_replace_whitespace_one_len(" hoge hoge ", 1, "h"); + test_util_replace_whitespace_one_len(" hoge hoge ", 0, ""); +} + +static void test_util_resolve_subsys_kernel_one(const char *str, bool read_value, int retval, const char *expected) { + char result[UTIL_PATH_SIZE]; + int r; + + r = util_resolve_subsys_kernel(str, result, sizeof(result), read_value); + assert_se(r == retval); + if (r >= 0) + assert_se(streq(result, expected)); +} + +static void test_util_resolve_subsys_kernel(void) { + test_util_resolve_subsys_kernel_one("hoge", false, -EINVAL, NULL); + test_util_resolve_subsys_kernel_one("[hoge", false, -EINVAL, NULL); + test_util_resolve_subsys_kernel_one("[hoge/foo", false, -EINVAL, NULL); + test_util_resolve_subsys_kernel_one("[hoge/]", false, -ENODEV, NULL); + + test_util_resolve_subsys_kernel_one("[net/lo]", false, 0, "/sys/devices/virtual/net/lo"); + test_util_resolve_subsys_kernel_one("[net/lo]/", false, 0, "/sys/devices/virtual/net/lo"); + test_util_resolve_subsys_kernel_one("[net/lo]hoge", false, 0, "/sys/devices/virtual/net/lo/hoge"); + test_util_resolve_subsys_kernel_one("[net/lo]/hoge", false, 0, "/sys/devices/virtual/net/lo/hoge"); + + test_util_resolve_subsys_kernel_one("[net/lo]", true, -EINVAL, NULL); + test_util_resolve_subsys_kernel_one("[net/lo]/", true, -EINVAL, NULL); + test_util_resolve_subsys_kernel_one("[net/lo]hoge", true, 0, ""); + test_util_resolve_subsys_kernel_one("[net/lo]/hoge", true, 0, ""); + test_util_resolve_subsys_kernel_one("[net/lo]address", true, 0, "00:00:00:00:00:00"); + test_util_resolve_subsys_kernel_one("[net/lo]/address", true, 0, "00:00:00:00:00:00"); +} + int main(int argc, char *argv[]) { _cleanup_(udev_unrefp) struct udev *udev = NULL; bool arg_monitor = false; @@ -397,7 +472,6 @@ int main(int argc, char *argv[]) { test_device_subsys_name(udev, "subsystem", "pci"); test_device_subsys_name(udev, "drivers", "scsi:sd"); test_device_subsys_name(udev, "module", "printk"); - test_device_parents(udev, syspath); test_enumerate(udev, subsystem); @@ -409,5 +483,8 @@ int main(int argc, char *argv[]) { if (arg_monitor) test_monitor(udev); + test_util_replace_whitespace(); + test_util_resolve_subsys_kernel(); + return EXIT_SUCCESS; } diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c index e50edb20b3..e94f2946f9 100644 --- a/src/udev/scsi_id/scsi_id.c +++ b/src/udev/scsi_id/scsi_id.c @@ -457,12 +457,12 @@ static int set_inq_values(struct scsi_id_device *dev_scsi, const char *path) { udev_util_encode_string(dev_scsi->vendor, vendor_enc_str, sizeof(vendor_enc_str)); udev_util_encode_string(dev_scsi->model, model_enc_str, sizeof(model_enc_str)); - util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str)); + util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str)-1); util_replace_chars(vendor_str, NULL); - util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str)); + util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str)-1); util_replace_chars(model_str, NULL); set_type(dev_scsi->type, type_str, sizeof(type_str)); - util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str)); + util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str)-1); util_replace_chars(revision_str, NULL); return 0; } @@ -503,10 +503,10 @@ static int scsi_id(char *maj_min_dev) { printf("ID_REVISION=%s\n", revision_str); printf("ID_TYPE=%s\n", type_str); if (dev_scsi.serial[0] != '\0') { - util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); + util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)-1); util_replace_chars(serial_str, NULL); printf("ID_SERIAL=%s\n", serial_str); - util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str)); + util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str)-1); util_replace_chars(serial_str, NULL); printf("ID_SERIAL_SHORT=%s\n", serial_str); } @@ -533,7 +533,7 @@ static int scsi_id(char *maj_min_dev) { if (reformat_serial) { char serial_str[MAX_SERIAL_LEN]; - util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); + util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)-1); util_replace_chars(serial_str, NULL); printf("%s\n", serial_str); goto out; diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index cc7efbb81e..f69458ef9b 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -234,7 +234,7 @@ static ssize_t subst_format_var(struct udev_event *event, return -EINVAL; /* try to read the value specified by "[dmi/id]product_name" */ - if (util_resolve_subsys_kernel(attr, vbuf, sizeof(vbuf), 1) == 0) + if (util_resolve_subsys_kernel(attr, vbuf, sizeof(vbuf), true) == 0) val = vbuf; /* try to read the attribute the device */ diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index c470338807..4f3dffad49 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -1688,7 +1688,7 @@ static int match_attr(struct udev_rules *rules, sd_device *dev, struct udev_even return -1; break; case SB_SUBSYS: - if (util_resolve_subsys_kernel(name, vbuf, sizeof(vbuf), 1) != 0) + if (util_resolve_subsys_kernel(name, vbuf, sizeof(vbuf), true) != 0) return -1; value = vbuf; break; @@ -1931,7 +1931,7 @@ int udev_rules_apply_to_event( int match; udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false); - if (util_resolve_subsys_kernel(filename, filename, sizeof(filename), 0) != 0) { + if (util_resolve_subsys_kernel(filename, filename, sizeof(filename), false) != 0) { if (filename[0] != '/') { char tmp[UTIL_PATH_SIZE]; @@ -2374,7 +2374,7 @@ int udev_rules_apply_to_event( const char *key_name; key_name = rules_str(rules, cur->key.attr_off); - if (util_resolve_subsys_kernel(key_name, attr, sizeof(attr), 0) != 0 && + if (util_resolve_subsys_kernel(key_name, attr, sizeof(attr), false) != 0 && sd_device_get_syspath(dev, &val) >= 0) strscpyl(attr, sizeof(attr), val, "/", key_name, NULL); attr_subst_subdir(attr, sizeof(attr));