mirror of
https://github.com/Dasharo/systemd.git
synced 2026-03-06 15:02:31 -08:00
udev: move several functions from udev-util.c to relevant udevd source files
The functions are only used by udevd (and relevant tests), hence it is not necessary to be in src/shared.
This commit is contained in:
@@ -10,7 +10,6 @@
|
||||
#include "device-util.h"
|
||||
#include "env-file.h"
|
||||
#include "errno-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "id128-util.h"
|
||||
#include "log.h"
|
||||
@@ -18,11 +17,9 @@
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "strxcpyx.h"
|
||||
#include "udev-util.h"
|
||||
#include "utf8.h"
|
||||
|
||||
@@ -337,62 +334,6 @@ void log_device_uevent(sd_device *device, const char *str) {
|
||||
sd_id128_is_null(event_id) ? "" : SD_ID128_TO_UUID_STRING(event_id));
|
||||
}
|
||||
|
||||
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) {
|
||||
char *i, *j;
|
||||
bool is_escaped;
|
||||
|
||||
/* value must be double quotated */
|
||||
is_escaped = str[0] == 'e';
|
||||
str += is_escaped;
|
||||
if (str[0] != '"')
|
||||
return -EINVAL;
|
||||
|
||||
if (!is_escaped) {
|
||||
/* unescape double quotation '\"'->'"' */
|
||||
for (j = str, i = str + 1; *i != '"'; i++, j++) {
|
||||
if (*i == '\0')
|
||||
return -EINVAL;
|
||||
if (i[0] == '\\' && i[1] == '"')
|
||||
i++;
|
||||
*j = *i;
|
||||
}
|
||||
j[0] = '\0';
|
||||
/*
|
||||
* The return value must be terminated by two subsequent NULs
|
||||
* so it could be safely interpreted as nulstr.
|
||||
*/
|
||||
j[1] = '\0';
|
||||
} else {
|
||||
_cleanup_free_ char *unescaped = NULL;
|
||||
ssize_t l;
|
||||
|
||||
/* find the end position of value */
|
||||
for (i = str + 1; *i != '"'; i++) {
|
||||
if (i[0] == '\\')
|
||||
i++;
|
||||
if (*i == '\0')
|
||||
return -EINVAL;
|
||||
}
|
||||
i[0] = '\0';
|
||||
|
||||
l = cunescape_length(str + 1, i - (str + 1), 0, &unescaped);
|
||||
if (l < 0)
|
||||
return l;
|
||||
|
||||
assert(l <= i - (str + 1));
|
||||
memcpy(str, unescaped, l + 1);
|
||||
/*
|
||||
* The return value must be terminated by two subsequent NULs
|
||||
* so it could be safely interpreted as nulstr.
|
||||
*/
|
||||
str[l + 1] = '\0';
|
||||
}
|
||||
|
||||
*ret_value = str;
|
||||
*ret_endpos = i + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t udev_replace_whitespace(const char *str, char *to, size_t len) {
|
||||
bool is_space = false;
|
||||
size_t i, j;
|
||||
@@ -435,22 +376,6 @@ size_t udev_replace_whitespace(const char *str, char *to, size_t len) {
|
||||
return j;
|
||||
}
|
||||
|
||||
size_t udev_replace_ifname(char *str) {
|
||||
size_t replaced = 0;
|
||||
|
||||
assert(str);
|
||||
|
||||
/* See ifname_valid_full(). */
|
||||
|
||||
for (char *p = str; *p != '\0'; p++)
|
||||
if (!ifname_valid_char(*p)) {
|
||||
*p = '_';
|
||||
replaced++;
|
||||
}
|
||||
|
||||
return replaced;
|
||||
}
|
||||
|
||||
size_t udev_replace_chars(char *str, const char *allow) {
|
||||
size_t i = 0, replaced = 0;
|
||||
|
||||
@@ -495,83 +420,6 @@ size_t udev_replace_chars(char *str, const char *allow) {
|
||||
return replaced;
|
||||
}
|
||||
|
||||
int udev_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
|
||||
_cleanup_free_ char *temp = NULL;
|
||||
char *subsys, *sysname, *attr;
|
||||
const char *val;
|
||||
int r;
|
||||
|
||||
assert(string);
|
||||
assert(result);
|
||||
|
||||
/* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
|
||||
|
||||
if (string[0] != '[')
|
||||
return -EINVAL;
|
||||
|
||||
temp = strdup(string);
|
||||
if (!temp)
|
||||
return -ENOMEM;
|
||||
|
||||
subsys = &temp[1];
|
||||
|
||||
sysname = strchr(subsys, '/');
|
||||
if (!sysname)
|
||||
return -EINVAL;
|
||||
sysname[0] = '\0';
|
||||
sysname = &sysname[1];
|
||||
|
||||
attr = strchr(sysname, ']');
|
||||
if (!attr)
|
||||
return -EINVAL;
|
||||
attr[0] = '\0';
|
||||
attr = &attr[1];
|
||||
if (attr[0] == '/')
|
||||
attr = &attr[1];
|
||||
if (attr[0] == '\0')
|
||||
attr = NULL;
|
||||
|
||||
if (read_value && !attr)
|
||||
return -EINVAL;
|
||||
|
||||
r = sd_device_new_from_subsystem_sysname(&dev, subsys, sysname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (read_value) {
|
||||
r = sd_device_get_sysattr_value(dev, attr, &val);
|
||||
if (r < 0 && !ERRNO_IS_PRIVILEGE(r) && r != -ENOENT)
|
||||
return r;
|
||||
if (r >= 0)
|
||||
strscpy(result, maxsize, val);
|
||||
else
|
||||
result[0] = '\0';
|
||||
log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
|
||||
} else {
|
||||
r = sd_device_get_syspath(dev, &val);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
strscpyl(result, maxsize, val, attr ? "/" : NULL, attr ?: NULL, NULL);
|
||||
log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, strempty(attr), result);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool devpath_conflict(const char *a, const char *b) {
|
||||
/* This returns true when two paths are equivalent, or one is a child of another. */
|
||||
|
||||
if (!a || !b)
|
||||
return false;
|
||||
|
||||
for (; *a != '\0' && *b != '\0'; a++, b++)
|
||||
if (*a != *b)
|
||||
return false;
|
||||
|
||||
return *a == '/' || *b == '/' || *a == *b;
|
||||
}
|
||||
|
||||
int udev_queue_is_empty(void) {
|
||||
return access("/run/udev/queue", F_OK) < 0 ?
|
||||
(errno == ENOENT ? true : -errno) : false;
|
||||
|
||||
@@ -39,13 +39,8 @@ bool device_for_action(sd_device *dev, sd_device_action_t action);
|
||||
|
||||
void log_device_uevent(sd_device *device, const char *str);
|
||||
|
||||
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos);
|
||||
size_t udev_replace_whitespace(const char *str, char *to, size_t len);
|
||||
size_t udev_replace_ifname(char *str);
|
||||
size_t udev_replace_chars(char *str, const char *allow);
|
||||
int udev_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value);
|
||||
|
||||
bool devpath_conflict(const char *a, const char *b);
|
||||
|
||||
int udev_queue_is_empty(void);
|
||||
|
||||
|
||||
@@ -8,76 +8,6 @@
|
||||
#include "tests.h"
|
||||
#include "udev-util.h"
|
||||
|
||||
static void test_udev_rule_parse_value_one(const char *in, const char *expected_value, int expected_retval) {
|
||||
_cleanup_free_ char *str = NULL;
|
||||
char *value = UINT_TO_PTR(0x12345678U);
|
||||
char *endpos = UINT_TO_PTR(0x87654321U);
|
||||
|
||||
log_info("/* %s (%s, %s, %d) */", __func__, in, strnull(expected_value), expected_retval);
|
||||
|
||||
assert_se(str = strdup(in));
|
||||
assert_se(udev_rule_parse_value(str, &value, &endpos) == expected_retval);
|
||||
if (expected_retval < 0) {
|
||||
/* not modified on failure */
|
||||
assert_se(value == UINT_TO_PTR(0x12345678U));
|
||||
assert_se(endpos == UINT_TO_PTR(0x87654321U));
|
||||
} else {
|
||||
assert_se(streq_ptr(value, expected_value));
|
||||
assert_se(endpos == str + strlen(in));
|
||||
/*
|
||||
* The return value must be terminated by two subsequent NULs
|
||||
* so it could be safely interpreted as nulstr.
|
||||
*/
|
||||
assert_se(value[strlen(value) + 1] == '\0');
|
||||
}
|
||||
}
|
||||
|
||||
TEST(udev_rule_parse_value) {
|
||||
/* input: "valid operand"
|
||||
* parsed: valid operand
|
||||
* use the following command to help generate textual C strings:
|
||||
* python3 -c 'import json; print(json.dumps(input()))' */
|
||||
test_udev_rule_parse_value_one("\"valid operand\"", "valid operand", 0);
|
||||
/* input: "va'l\'id\"op\"erand"
|
||||
* parsed: va'l\'id"op"erand */
|
||||
test_udev_rule_parse_value_one("\"va'l\\'id\\\"op\\\"erand\"", "va'l\\'id\"op\"erand", 0);
|
||||
test_udev_rule_parse_value_one("no quotes", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("\"\\\\a\\b\\x\\y\"", "\\\\a\\b\\x\\y", 0);
|
||||
test_udev_rule_parse_value_one("\"reject\0nul\"", NULL, -EINVAL);
|
||||
/* input: e"" */
|
||||
test_udev_rule_parse_value_one("e\"\"", "", 0);
|
||||
/* input: e"1234" */
|
||||
test_udev_rule_parse_value_one("e\"1234\"", "1234", 0);
|
||||
/* input: e"\"" */
|
||||
test_udev_rule_parse_value_one("e\"\\\"\"", "\"", 0);
|
||||
/* input: e"\ */
|
||||
test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
|
||||
/* input: e"\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\"", NULL, -EINVAL);
|
||||
/* input: e"\\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\"", "\\", 0);
|
||||
/* input: e"\\\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\"", NULL, -EINVAL);
|
||||
/* input: e"\\\"" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\"\"", "\\\"", 0);
|
||||
/* input: e"\\\\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\\\"", "\\\\", 0);
|
||||
/* input: e"operand with newline\n" */
|
||||
test_udev_rule_parse_value_one("e\"operand with newline\\n\"", "operand with newline\n", 0);
|
||||
/* input: e"single\rcharacter\t\aescape\bsequence" */
|
||||
test_udev_rule_parse_value_one(
|
||||
"e\"single\\rcharacter\\t\\aescape\\bsequence\"", "single\rcharacter\t\aescape\bsequence", 0);
|
||||
/* input: e"reject\invalid escape sequence" */
|
||||
test_udev_rule_parse_value_one("e\"reject\\invalid escape sequence", NULL, -EINVAL);
|
||||
/* input: e"\ */
|
||||
test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
|
||||
/* input: "s\u1d1c\u1d04\u029c \u1d1c\u0274\u026a\u1d04\u1d0f\u1d05\u1d07 \U0001d568\U0001d560\U0001d568" */
|
||||
test_udev_rule_parse_value_one(
|
||||
"e\"s\\u1d1c\\u1d04\\u029c \\u1d1c\\u0274\\u026a\\u1d04\\u1d0f\\u1d05\\u1d07 \\U0001d568\\U0001d560\\U0001d568\"",
|
||||
"s\xe1\xb4\x9c\xe1\xb4\x84\xca\x9c \xe1\xb4\x9c\xc9\xb4\xc9\xaa\xe1\xb4\x84\xe1\xb4\x8f\xe1\xb4\x85\xe1\xb4\x87 \xf0\x9d\x95\xa8\xf0\x9d\x95\xa0\xf0\x9d\x95\xa8",
|
||||
0);
|
||||
}
|
||||
|
||||
static void test_udev_replace_whitespace_one_len(const char *str, size_t len, const char *expected) {
|
||||
_cleanup_free_ char *result = NULL;
|
||||
int r;
|
||||
@@ -130,47 +60,4 @@ TEST(udev_replace_whitespace) {
|
||||
test_udev_replace_whitespace_one_len(" hoge hoge ", 0, "");
|
||||
}
|
||||
|
||||
static void test_udev_resolve_subsys_kernel_one(const char *str, bool read_value, int retval, const char *expected) {
|
||||
char result[PATH_MAX] = "";
|
||||
int r;
|
||||
|
||||
r = udev_resolve_subsys_kernel(str, result, sizeof(result), read_value);
|
||||
log_info("\"%s\" → expect: \"%s\", %d, actual: \"%s\", %d", str, strnull(expected), retval, result, r);
|
||||
assert_se(r == retval);
|
||||
if (r >= 0)
|
||||
assert_se(streq(result, expected));
|
||||
}
|
||||
|
||||
TEST(udev_resolve_subsys_kernel) {
|
||||
test_udev_resolve_subsys_kernel_one("hoge", false, -EINVAL, NULL);
|
||||
test_udev_resolve_subsys_kernel_one("[hoge", false, -EINVAL, NULL);
|
||||
test_udev_resolve_subsys_kernel_one("[hoge/foo", false, -EINVAL, NULL);
|
||||
test_udev_resolve_subsys_kernel_one("[hoge/]", false, -EINVAL, NULL);
|
||||
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]", false, 0, "/sys/devices/virtual/net/lo");
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]/", false, 0, "/sys/devices/virtual/net/lo");
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]hoge", false, 0, "/sys/devices/virtual/net/lo/hoge");
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]/hoge", false, 0, "/sys/devices/virtual/net/lo/hoge");
|
||||
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]", true, -EINVAL, NULL);
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]/", true, -EINVAL, NULL);
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]hoge", true, 0, "");
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]/hoge", true, 0, "");
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]address", true, 0, "00:00:00:00:00:00");
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]/address", true, 0, "00:00:00:00:00:00");
|
||||
}
|
||||
|
||||
TEST(devpath_conflict) {
|
||||
assert_se(!devpath_conflict(NULL, NULL));
|
||||
assert_se(!devpath_conflict(NULL, "/devices/pci0000:00/0000:00:1c.4"));
|
||||
assert_se(!devpath_conflict("/devices/pci0000:00/0000:00:1c.4", NULL));
|
||||
assert_se(!devpath_conflict("/devices/pci0000:00/0000:00:1c.4", "/devices/pci0000:00/0000:00:00.0"));
|
||||
assert_se(!devpath_conflict("/devices/virtual/net/veth99", "/devices/virtual/net/veth999"));
|
||||
|
||||
assert_se(devpath_conflict("/devices/pci0000:00/0000:00:1c.4", "/devices/pci0000:00/0000:00:1c.4"));
|
||||
assert_se(devpath_conflict("/devices/pci0000:00/0000:00:1c.4", "/devices/pci0000:00/0000:00:1c.4/0000:3c:00.0"));
|
||||
assert_se(devpath_conflict("/devices/pci0000:00/0000:00:1c.4/0000:3c:00.0/nvme/nvme0/nvme0n1",
|
||||
"/devices/pci0000:00/0000:00:1c.4/0000:3c:00.0/nvme/nvme0/nvme0n1/nvme0n1p1"));
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fuzz.h"
|
||||
#include "udev-util.h"
|
||||
#include "udev-rules.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
_cleanup_free_ char *str = NULL;
|
||||
@@ -17,7 +17,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
str[size] = '\0';
|
||||
|
||||
r = udev_rule_parse_value(str, &value, &endpos);
|
||||
|
||||
if (r < 0) {
|
||||
/* not modified on failure */
|
||||
assert_se(value == UINT_TO_PTR(0x12345678U));
|
||||
|
||||
@@ -14,7 +14,6 @@ udevadm_sources = files(
|
||||
'udevadm-verify.c',
|
||||
'udevadm-wait.c',
|
||||
'udevadm.c',
|
||||
'udevd.c',
|
||||
)
|
||||
|
||||
libudevd_core_sources = files(
|
||||
@@ -35,6 +34,7 @@ libudevd_core_sources = files(
|
||||
'udev-builtin-path_id.c',
|
||||
'udev-builtin-usb_id.c',
|
||||
'udev-builtin.c',
|
||||
'udevd.c',
|
||||
)
|
||||
|
||||
if conf.get('HAVE_KMOD') == 1
|
||||
@@ -169,10 +169,6 @@ if install_sysconfdir
|
||||
mkdir_p.format(sysconfdir / 'udev/rules.d'))
|
||||
endif
|
||||
|
||||
simple_fuzzers += files(
|
||||
'fuzz-udev-rule-parse-value.c',
|
||||
)
|
||||
|
||||
fuzzer_udev_base = {
|
||||
'link_with' : [libudevd_core, libshared],
|
||||
'dependencies' : [threads, libacl],
|
||||
@@ -184,6 +180,10 @@ fuzzers += [
|
||||
'includes' : udev_includes,
|
||||
'base' : fuzzer_udev_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('fuzz-udev-rule-parse-value.c'),
|
||||
'base' : fuzzer_udev_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('fuzz-udev-rules.c'),
|
||||
'base' : fuzzer_udev_base,
|
||||
@@ -212,12 +212,24 @@ tests += [
|
||||
'sources' : files('test-udev-builtin.c'),
|
||||
'base' : test_libudev_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-udev-format.c'),
|
||||
'base' : test_libudev_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-udev-node.c'),
|
||||
'base' : test_libudev_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-udev-rules.c'),
|
||||
'base' : test_libudev_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-udev-spawn.c'),
|
||||
'base' : test_libudev_base,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-udevd.c'),
|
||||
'base' : test_libudev_base,
|
||||
},
|
||||
]
|
||||
|
||||
37
src/udev/test-udev-format.c
Normal file
37
src/udev/test-udev-format.c
Normal file
@@ -0,0 +1,37 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "string-util.h"
|
||||
#include "tests.h"
|
||||
#include "udev-format.h"
|
||||
|
||||
static void test_udev_resolve_subsys_kernel_one(const char *str, bool read_value, int retval, const char *expected) {
|
||||
char result[PATH_MAX] = "";
|
||||
int r;
|
||||
|
||||
r = udev_resolve_subsys_kernel(str, result, sizeof(result), read_value);
|
||||
log_info("\"%s\" → expect: \"%s\", %d, actual: \"%s\", %d", str, strnull(expected), retval, result, r);
|
||||
assert_se(r == retval);
|
||||
if (r >= 0)
|
||||
assert_se(streq(result, expected));
|
||||
}
|
||||
|
||||
TEST(udev_resolve_subsys_kernel) {
|
||||
test_udev_resolve_subsys_kernel_one("hoge", false, -EINVAL, NULL);
|
||||
test_udev_resolve_subsys_kernel_one("[hoge", false, -EINVAL, NULL);
|
||||
test_udev_resolve_subsys_kernel_one("[hoge/foo", false, -EINVAL, NULL);
|
||||
test_udev_resolve_subsys_kernel_one("[hoge/]", false, -EINVAL, NULL);
|
||||
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]", false, 0, "/sys/devices/virtual/net/lo");
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]/", false, 0, "/sys/devices/virtual/net/lo");
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]hoge", false, 0, "/sys/devices/virtual/net/lo/hoge");
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]/hoge", false, 0, "/sys/devices/virtual/net/lo/hoge");
|
||||
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]", true, -EINVAL, NULL);
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]/", true, -EINVAL, NULL);
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]hoge", true, 0, "");
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]/hoge", true, 0, "");
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]address", true, 0, "00:00:00:00:00:00");
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]/address", true, 0, "00:00:00:00:00:00");
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
77
src/udev/test-udev-rules.c
Normal file
77
src/udev/test-udev-rules.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "string-util.h"
|
||||
#include "tests.h"
|
||||
#include "udev-rules.h"
|
||||
|
||||
static void test_udev_rule_parse_value_one(const char *in, const char *expected_value, int expected_retval) {
|
||||
_cleanup_free_ char *str = NULL;
|
||||
char *value = UINT_TO_PTR(0x12345678U);
|
||||
char *endpos = UINT_TO_PTR(0x87654321U);
|
||||
|
||||
log_info("/* %s (%s, %s, %d) */", __func__, in, strnull(expected_value), expected_retval);
|
||||
|
||||
assert_se(str = strdup(in));
|
||||
assert_se(udev_rule_parse_value(str, &value, &endpos) == expected_retval);
|
||||
if (expected_retval < 0) {
|
||||
/* not modified on failure */
|
||||
assert_se(value == UINT_TO_PTR(0x12345678U));
|
||||
assert_se(endpos == UINT_TO_PTR(0x87654321U));
|
||||
} else {
|
||||
assert_se(streq_ptr(value, expected_value));
|
||||
assert_se(endpos == str + strlen(in));
|
||||
/*
|
||||
* The return value must be terminated by two subsequent NULs
|
||||
* so it could be safely interpreted as nulstr.
|
||||
*/
|
||||
assert_se(value[strlen(value) + 1] == '\0');
|
||||
}
|
||||
}
|
||||
|
||||
TEST(udev_rule_parse_value) {
|
||||
/* input: "valid operand"
|
||||
* parsed: valid operand
|
||||
* use the following command to help generate textual C strings:
|
||||
* python3 -c 'import json; print(json.dumps(input()))' */
|
||||
test_udev_rule_parse_value_one("\"valid operand\"", "valid operand", 0);
|
||||
/* input: "va'l\'id\"op\"erand"
|
||||
* parsed: va'l\'id"op"erand */
|
||||
test_udev_rule_parse_value_one("\"va'l\\'id\\\"op\\\"erand\"", "va'l\\'id\"op\"erand", 0);
|
||||
test_udev_rule_parse_value_one("no quotes", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("\"\\\\a\\b\\x\\y\"", "\\\\a\\b\\x\\y", 0);
|
||||
test_udev_rule_parse_value_one("\"reject\0nul\"", NULL, -EINVAL);
|
||||
/* input: e"" */
|
||||
test_udev_rule_parse_value_one("e\"\"", "", 0);
|
||||
/* input: e"1234" */
|
||||
test_udev_rule_parse_value_one("e\"1234\"", "1234", 0);
|
||||
/* input: e"\"" */
|
||||
test_udev_rule_parse_value_one("e\"\\\"\"", "\"", 0);
|
||||
/* input: e"\ */
|
||||
test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
|
||||
/* input: e"\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\"", NULL, -EINVAL);
|
||||
/* input: e"\\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\"", "\\", 0);
|
||||
/* input: e"\\\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\"", NULL, -EINVAL);
|
||||
/* input: e"\\\"" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\"\"", "\\\"", 0);
|
||||
/* input: e"\\\\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\\\"", "\\\\", 0);
|
||||
/* input: e"operand with newline\n" */
|
||||
test_udev_rule_parse_value_one("e\"operand with newline\\n\"", "operand with newline\n", 0);
|
||||
/* input: e"single\rcharacter\t\aescape\bsequence" */
|
||||
test_udev_rule_parse_value_one(
|
||||
"e\"single\\rcharacter\\t\\aescape\\bsequence\"", "single\rcharacter\t\aescape\bsequence", 0);
|
||||
/* input: e"reject\invalid escape sequence" */
|
||||
test_udev_rule_parse_value_one("e\"reject\\invalid escape sequence", NULL, -EINVAL);
|
||||
/* input: e"\ */
|
||||
test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
|
||||
/* input: "s\u1d1c\u1d04\u029c \u1d1c\u0274\u026a\u1d04\u1d0f\u1d05\u1d07 \U0001d568\U0001d560\U0001d568" */
|
||||
test_udev_rule_parse_value_one(
|
||||
"e\"s\\u1d1c\\u1d04\\u029c \\u1d1c\\u0274\\u026a\\u1d04\\u1d0f\\u1d05\\u1d07 \\U0001d568\\U0001d560\\U0001d568\"",
|
||||
"s\xe1\xb4\x9c\xe1\xb4\x84\xca\x9c \xe1\xb4\x9c\xc9\xb4\xc9\xaa\xe1\xb4\x84\xe1\xb4\x8f\xe1\xb4\x85\xe1\xb4\x87 \xf0\x9d\x95\xa8\xf0\x9d\x95\xa0\xf0\x9d\x95\xa8",
|
||||
0);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
19
src/udev/test-udevd.c
Normal file
19
src/udev/test-udevd.c
Normal file
@@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "tests.h"
|
||||
#include "udevd.h"
|
||||
|
||||
TEST(devpath_conflict) {
|
||||
assert_se(!devpath_conflict(NULL, NULL));
|
||||
assert_se(!devpath_conflict(NULL, "/devices/pci0000:00/0000:00:1c.4"));
|
||||
assert_se(!devpath_conflict("/devices/pci0000:00/0000:00:1c.4", NULL));
|
||||
assert_se(!devpath_conflict("/devices/pci0000:00/0000:00:1c.4", "/devices/pci0000:00/0000:00:00.0"));
|
||||
assert_se(!devpath_conflict("/devices/virtual/net/veth99", "/devices/virtual/net/veth999"));
|
||||
|
||||
assert_se(devpath_conflict("/devices/pci0000:00/0000:00:1c.4", "/devices/pci0000:00/0000:00:1c.4"));
|
||||
assert_se(devpath_conflict("/devices/pci0000:00/0000:00:1c.4", "/devices/pci0000:00/0000:00:1c.4/0000:3c:00.0"));
|
||||
assert_se(devpath_conflict("/devices/pci0000:00/0000:00:1c.4/0000:3c:00.0/nvme/nvme0/nvme0n1",
|
||||
"/devices/pci0000:00/0000:00:1c.4/0000:3c:00.0/nvme/nvme0/nvme0n1/nvme0n1p1"));
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
@@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "device-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strxcpyx.h"
|
||||
@@ -481,3 +482,67 @@ int udev_check_format(const char *value, size_t *offset, const char **hint) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udev_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
|
||||
_cleanup_free_ char *temp = NULL;
|
||||
char *subsys, *sysname, *attr;
|
||||
const char *val;
|
||||
int r;
|
||||
|
||||
assert(string);
|
||||
assert(result);
|
||||
|
||||
/* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
|
||||
|
||||
if (string[0] != '[')
|
||||
return -EINVAL;
|
||||
|
||||
temp = strdup(string);
|
||||
if (!temp)
|
||||
return -ENOMEM;
|
||||
|
||||
subsys = &temp[1];
|
||||
|
||||
sysname = strchr(subsys, '/');
|
||||
if (!sysname)
|
||||
return -EINVAL;
|
||||
sysname[0] = '\0';
|
||||
sysname = &sysname[1];
|
||||
|
||||
attr = strchr(sysname, ']');
|
||||
if (!attr)
|
||||
return -EINVAL;
|
||||
attr[0] = '\0';
|
||||
attr = &attr[1];
|
||||
if (attr[0] == '/')
|
||||
attr = &attr[1];
|
||||
if (attr[0] == '\0')
|
||||
attr = NULL;
|
||||
|
||||
if (read_value && !attr)
|
||||
return -EINVAL;
|
||||
|
||||
r = sd_device_new_from_subsystem_sysname(&dev, subsys, sysname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (read_value) {
|
||||
r = sd_device_get_sysattr_value(dev, attr, &val);
|
||||
if (r < 0 && !ERRNO_IS_PRIVILEGE(r) && r != -ENOENT)
|
||||
return r;
|
||||
if (r >= 0)
|
||||
strscpy(result, maxsize, val);
|
||||
else
|
||||
result[0] = '\0';
|
||||
log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
|
||||
} else {
|
||||
r = sd_device_get_syspath(dev, &val);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
strscpyl(result, maxsize, val, attr ? "/" : NULL, attr ?: NULL, NULL);
|
||||
log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, strempty(attr), result);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -16,3 +16,5 @@ size_t udev_event_apply_format(
|
||||
bool replace_whitespace,
|
||||
bool *ret_truncated);
|
||||
int udev_check_format(const char *value, size_t *offset, const char **hint);
|
||||
|
||||
int udev_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "device-private.h"
|
||||
#include "device-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-util.h"
|
||||
@@ -23,6 +24,7 @@
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "socket-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "strv.h"
|
||||
#include "strxcpyx.h"
|
||||
@@ -1118,6 +1120,62 @@ static void check_token_delimiters(UdevRuleLine *rule_line, const char *line) {
|
||||
}
|
||||
}
|
||||
|
||||
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) {
|
||||
char *i, *j;
|
||||
bool is_escaped;
|
||||
|
||||
/* value must be double quotated */
|
||||
is_escaped = str[0] == 'e';
|
||||
str += is_escaped;
|
||||
if (str[0] != '"')
|
||||
return -EINVAL;
|
||||
|
||||
if (!is_escaped) {
|
||||
/* unescape double quotation '\"'->'"' */
|
||||
for (j = str, i = str + 1; *i != '"'; i++, j++) {
|
||||
if (*i == '\0')
|
||||
return -EINVAL;
|
||||
if (i[0] == '\\' && i[1] == '"')
|
||||
i++;
|
||||
*j = *i;
|
||||
}
|
||||
j[0] = '\0';
|
||||
/*
|
||||
* The return value must be terminated by two subsequent NULs
|
||||
* so it could be safely interpreted as nulstr.
|
||||
*/
|
||||
j[1] = '\0';
|
||||
} else {
|
||||
_cleanup_free_ char *unescaped = NULL;
|
||||
ssize_t l;
|
||||
|
||||
/* find the end position of value */
|
||||
for (i = str + 1; *i != '"'; i++) {
|
||||
if (i[0] == '\\')
|
||||
i++;
|
||||
if (*i == '\0')
|
||||
return -EINVAL;
|
||||
}
|
||||
i[0] = '\0';
|
||||
|
||||
l = cunescape_length(str + 1, i - (str + 1), 0, &unescaped);
|
||||
if (l < 0)
|
||||
return l;
|
||||
|
||||
assert(l <= i - (str + 1));
|
||||
memcpy(str, unescaped, l + 1);
|
||||
/*
|
||||
* The return value must be terminated by two subsequent NULs
|
||||
* so it could be safely interpreted as nulstr.
|
||||
*/
|
||||
str[l + 1] = '\0';
|
||||
}
|
||||
|
||||
*ret_value = str;
|
||||
*ret_endpos = i + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOperatorType *ret_op, char **ret_value) {
|
||||
char *key_begin, *key_end, *attr, *tmp;
|
||||
UdevRuleOperatorType op;
|
||||
@@ -1850,6 +1908,22 @@ static int attr_subst_subdir(char attr[static UDEV_PATH_SIZE]) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static size_t udev_replace_ifname(char *str) {
|
||||
size_t replaced = 0;
|
||||
|
||||
assert(str);
|
||||
|
||||
/* See ifname_valid_full(). */
|
||||
|
||||
for (char *p = str; *p != '\0'; p++)
|
||||
if (!ifname_valid_char(*p)) {
|
||||
*p = '_';
|
||||
replaced++;
|
||||
}
|
||||
|
||||
return replaced;
|
||||
}
|
||||
|
||||
static int udev_rule_apply_token_to_event(
|
||||
UdevRuleToken *token,
|
||||
sd_device *dev,
|
||||
|
||||
@@ -18,6 +18,7 @@ typedef enum {
|
||||
_ESCAPE_TYPE_INVALID = -EINVAL,
|
||||
} UdevRuleEscapeType;
|
||||
|
||||
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos);
|
||||
int udev_rules_parse_file(UdevRules *rules, const char *filename, bool extra_checks, UdevRuleFile **ret);
|
||||
unsigned udev_rule_file_get_issues(UdevRuleFile *rule_file);
|
||||
UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing);
|
||||
|
||||
@@ -889,6 +889,19 @@ static int event_run(Event *event) {
|
||||
return 1; /* event is now processing. */
|
||||
}
|
||||
|
||||
bool devpath_conflict(const char *a, const char *b) {
|
||||
/* This returns true when two paths are equivalent, or one is a child of another. */
|
||||
|
||||
if (!a || !b)
|
||||
return false;
|
||||
|
||||
for (; *a != '\0' && *b != '\0'; a++, b++)
|
||||
if (*a != *b)
|
||||
return false;
|
||||
|
||||
return *a == '/' || *b == '/' || *a == *b;
|
||||
}
|
||||
|
||||
static int event_is_blocked(Event *event) {
|
||||
Event *loop_event = NULL;
|
||||
int r;
|
||||
|
||||
@@ -2,3 +2,5 @@
|
||||
#pragma once
|
||||
|
||||
int run_udevd(int argc, char *argv[]);
|
||||
|
||||
bool devpath_conflict(const char *a, const char *b);
|
||||
|
||||
Reference in New Issue
Block a user