mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
Merge pull request #34425 from yuwata/udev-rules-case-insensitive-match
udev-rules: support case insensitive match
This commit is contained in:
commit
590f430cac
@ -141,6 +141,12 @@
|
||||
For example, e"string\n" is parsed as 7 characters: 6 lowercase letters and a newline.
|
||||
This can be useful for writing special characters when a kernel driver requires them.</para>
|
||||
|
||||
<para>The string can be prefixed with a lowercase i (i"string") to mark that the string or pattern
|
||||
will match case-insensitively. For example, i"foo" will match
|
||||
<literal>foo</literal>, <literal>FOO</literal>, <literal>FoO</literal> and so on. The prefix can be
|
||||
used only for match (<literal>==</literal>) or unmatch (<literal>!=</literal>) rules, e.g.
|
||||
<varname>ATTR{foo}==i"abcd"</varname>.</para>
|
||||
|
||||
<para>Please note that <constant>NUL</constant> is not allowed in either string variant.</para>
|
||||
</refsect2>
|
||||
|
||||
|
@ -11,6 +11,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
int r;
|
||||
char *value = UINT_TO_PTR(0x12345678U);
|
||||
char *endpos = UINT_TO_PTR(0x87654321U);
|
||||
bool is_case_sensitive;
|
||||
|
||||
fuzz_setup_logging();
|
||||
|
||||
@ -18,7 +19,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
memcpy(str, data, size);
|
||||
str[size] = '\0';
|
||||
|
||||
r = udev_rule_parse_value(str, &value, &endpos);
|
||||
r = udev_rule_parse_value(str, &value, &endpos, &is_case_sensitive);
|
||||
if (r < 0) {
|
||||
/* not modified on failure */
|
||||
assert_se(value == UINT_TO_PTR(0x12345678U));
|
||||
|
@ -4,15 +4,16 @@
|
||||
#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) {
|
||||
static void test_udev_rule_parse_value_one(const char *in, const char *expected_value, bool expected_case_insensitive, int expected_retval) {
|
||||
_cleanup_free_ char *str = NULL;
|
||||
char *value = UINT_TO_PTR(0x12345678U);
|
||||
char *endpos = UINT_TO_PTR(0x87654321U);
|
||||
bool i;
|
||||
|
||||
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);
|
||||
assert_se(udev_rule_parse_value(str, &value, &endpos, &i) == expected_retval);
|
||||
if (expected_retval < 0) {
|
||||
/* not modified on failure */
|
||||
assert_se(value == UINT_TO_PTR(0x12345678U));
|
||||
@ -25,6 +26,7 @@ static void test_udev_rule_parse_value_one(const char *in, const char *expected_
|
||||
* so it could be safely interpreted as nulstr.
|
||||
*/
|
||||
assert_se(value[strlen(value) + 1] == '\0');
|
||||
assert_se(i == expected_case_insensitive);
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,45 +35,61 @@ TEST(udev_rule_parse_value) {
|
||||
* 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);
|
||||
test_udev_rule_parse_value_one("\"valid operand\"", "valid operand", /* case_insensitive = */ false, 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);
|
||||
test_udev_rule_parse_value_one("\"va'l\\'id\\\"op\\\"erand\"", "va'l\\'id\"op\"erand", /* case_insensitive = */ false, 0);
|
||||
test_udev_rule_parse_value_one("no quotes", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
test_udev_rule_parse_value_one("\"\\\\a\\b\\x\\y\"", "\\\\a\\b\\x\\y", /* case_insensitive = */ false, 0);
|
||||
test_udev_rule_parse_value_one("\"reject\0nul\"", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: e"" */
|
||||
test_udev_rule_parse_value_one("e\"\"", "", 0);
|
||||
test_udev_rule_parse_value_one("e\"\"", "", /* case_insensitive = */ false, 0);
|
||||
/* input: e"1234" */
|
||||
test_udev_rule_parse_value_one("e\"1234\"", "1234", 0);
|
||||
test_udev_rule_parse_value_one("e\"1234\"", "1234", /* case_insensitive = */ false, 0);
|
||||
/* input: e"\"" */
|
||||
test_udev_rule_parse_value_one("e\"\\\"\"", "\"", 0);
|
||||
test_udev_rule_parse_value_one("e\"\\\"\"", "\"", /* case_insensitive = */ false, 0);
|
||||
/* input: e"\ */
|
||||
test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("e\"\\", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: e"\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\"", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("e\"\\\"", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: e"\\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\"", "\\", 0);
|
||||
test_udev_rule_parse_value_one("e\"\\\\\"", "\\", /* case_insensitive = */ false, 0);
|
||||
/* input: e"\\\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\"", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\"", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: e"\\\"" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\"\"", "\\\"", 0);
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\"\"", "\\\"", /* case_insensitive = */ false, 0);
|
||||
/* input: e"\\\\" */
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\\\"", "\\\\", 0);
|
||||
test_udev_rule_parse_value_one("e\"\\\\\\\\\"", "\\\\", /* case_insensitive = */ false, 0);
|
||||
/* input: e"operand with newline\n" */
|
||||
test_udev_rule_parse_value_one("e\"operand with newline\\n\"", "operand with newline\n", 0);
|
||||
test_udev_rule_parse_value_one("e\"operand with newline\\n\"", "operand with newline\n", /* case_insensitive = */ false, 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);
|
||||
"e\"single\\rcharacter\\t\\aescape\\bsequence\"", "single\rcharacter\t\aescape\bsequence", /* case_insensitive = */ false, 0);
|
||||
/* input: e"reject\invalid escape sequence" */
|
||||
test_udev_rule_parse_value_one("e\"reject\\invalid escape sequence", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("e\"reject\\invalid escape sequence", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: e"\ */
|
||||
test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
|
||||
test_udev_rule_parse_value_one("e\"\\", NULL, /* case_insensitive = */ false, -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);
|
||||
/* case_insensitive = */ false, 0);
|
||||
/* input: i"ABCD1234" */
|
||||
test_udev_rule_parse_value_one("i\"ABCD1234\"", "ABCD1234", /* case_insensitive = */ true, 0);
|
||||
/* input: i"ABCD1234" */
|
||||
test_udev_rule_parse_value_one("e\"ABCD1234\"", "ABCD1234", /* case_insensitive = */ false, 0);
|
||||
/* input: ei"\\"ABCD1234 */
|
||||
test_udev_rule_parse_value_one("ei\"\\\\ABCD1234\"", "\\ABCD1234", /* case_insensitive = */ true, 0);
|
||||
/* input: ie"\\"ABCD1234 */
|
||||
test_udev_rule_parse_value_one("ie\"\\\\ABCD1234\"", "\\ABCD1234", /* case_insensitive = */ true, 0);
|
||||
/* input: i */
|
||||
test_udev_rule_parse_value_one("i", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: ee"" */
|
||||
test_udev_rule_parse_value_one("ee\"\"", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: iei"" */
|
||||
test_udev_rule_parse_value_one("iei\"\"", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
/* input: a"" */
|
||||
test_udev_rule_parse_value_one("a\"\"", NULL, /* case_insensitive = */ false, -EINVAL);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
@ -63,9 +63,15 @@ typedef enum {
|
||||
MATCH_TYPE_GLOB_WITH_EMPTY, /* shell globs ?,*,[] with empty string, e.g., "|foo*" */
|
||||
MATCH_TYPE_SUBSYSTEM, /* "subsystem", "bus", or "class" */
|
||||
_MATCH_TYPE_MAX,
|
||||
_MATCH_TYPE_INVALID = -EINVAL,
|
||||
|
||||
_MATCH_TYPE_MASK = (1 << 5) - 1,
|
||||
MATCH_REMOVE_TRAILING_WHITESPACE = 1 << 5, /* Remove trailing whitespaces in attribute */
|
||||
MATCH_CASE_INSENSITIVE = 1 << 6, /* string or pattern is matched case-insensitively */
|
||||
_MATCH_TYPE_INVALID = -EINVAL,
|
||||
} UdevRuleMatchType;
|
||||
|
||||
assert_cc(_MATCH_TYPE_MAX <= _MATCH_TYPE_MASK);
|
||||
|
||||
typedef enum {
|
||||
SUBST_TYPE_PLAIN, /* no substitution */
|
||||
SUBST_TYPE_FORMAT, /* % or $ */
|
||||
@ -155,8 +161,7 @@ struct UdevRuleToken {
|
||||
UdevRuleTokenType type:8;
|
||||
UdevRuleOperatorType op:8;
|
||||
UdevRuleMatchType match_type:8;
|
||||
UdevRuleSubstituteType attr_subst_type:7;
|
||||
bool attr_match_remove_trailing_whitespace:1;
|
||||
UdevRuleSubstituteType attr_subst_type:8;
|
||||
const char *value;
|
||||
void *data;
|
||||
|
||||
@ -295,6 +300,7 @@ struct UdevRules {
|
||||
|
||||
#define log_line_invalid_op(line, key) _log_line_invalid_token(line, key, "operator")
|
||||
#define log_line_invalid_attr(line, key) _log_line_invalid_token(line, key, "attribute")
|
||||
#define log_line_invalid_prefix(line, key) _log_line_invalid_token(line, key, "prefix 'i'")
|
||||
|
||||
#define log_line_invalid_attr_format(line, key, attr, offset, hint) \
|
||||
log_line_error_errno(line, SYNTHETIC_ERRNO(EINVAL), \
|
||||
@ -488,12 +494,10 @@ static bool type_has_nulstr_value(UdevRuleTokenType type) {
|
||||
return type < TK_M_TEST || type == TK_M_RESULT;
|
||||
}
|
||||
|
||||
static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type, UdevRuleOperatorType op, char *value, void *data) {
|
||||
static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type, UdevRuleOperatorType op, char *value, void *data, bool is_case_insensitive) {
|
||||
_cleanup_(udev_rule_token_freep) UdevRuleToken *token = NULL;
|
||||
UdevRuleMatchType match_type = _MATCH_TYPE_INVALID;
|
||||
UdevRuleSubstituteType subst_type = _SUBST_TYPE_INVALID;
|
||||
bool remove_trailing_whitespace = false;
|
||||
size_t len;
|
||||
|
||||
assert(rule_line);
|
||||
assert(type >= 0 && type < _TK_TYPE_MAX);
|
||||
@ -552,16 +556,21 @@ static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type,
|
||||
}
|
||||
|
||||
if (IN_SET(type, TK_M_ATTR, TK_M_PARENTS_ATTR)) {
|
||||
size_t len;
|
||||
|
||||
assert(value);
|
||||
assert(data);
|
||||
assert(match_type >= 0 && match_type < _MATCH_TYPE_MAX);
|
||||
|
||||
len = strlen(value);
|
||||
if (len > 0 && !isspace(value[len - 1]))
|
||||
remove_trailing_whitespace = true;
|
||||
match_type |= MATCH_REMOVE_TRAILING_WHITESPACE;
|
||||
|
||||
subst_type = rule_get_substitution_type(data);
|
||||
}
|
||||
|
||||
SET_FLAG(match_type, MATCH_CASE_INSENSITIVE, is_case_insensitive);
|
||||
|
||||
token = new(UdevRuleToken, 1);
|
||||
if (!token)
|
||||
return -ENOMEM;
|
||||
@ -573,7 +582,6 @@ static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type,
|
||||
.data = data,
|
||||
.match_type = match_type,
|
||||
.attr_subst_type = subst_type,
|
||||
.attr_match_remove_trailing_whitespace = remove_trailing_whitespace,
|
||||
.rule_line = rule_line,
|
||||
};
|
||||
|
||||
@ -621,7 +629,7 @@ static int check_attr_format_and_warn(UdevRuleLine *line, const char *key, const
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, UdevRuleOperatorType op, char *value) {
|
||||
static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, UdevRuleOperatorType op, char *value, bool is_case_insensitive) {
|
||||
ResolveNameTiming resolve_name_timing = LINE_GET_RULES(rule_line)->resolve_name_timing;
|
||||
bool is_match = IN_SET(op, OP_MATCH, OP_NOMATCH);
|
||||
int r;
|
||||
@ -629,35 +637,39 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
assert(key);
|
||||
assert(value);
|
||||
|
||||
if (!is_match && is_case_insensitive)
|
||||
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid prefix 'i' for '%s'. The 'i' prefix can be specified only for '==' or '!=' operator.", key);
|
||||
|
||||
if (streq(key, "ACTION")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_ACTION, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_ACTION, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "DEVPATH")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_DEVPATH, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_DEVPATH, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "KERNEL")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_KERNEL, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_KERNEL, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "SYMLINK")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match) {
|
||||
check_value_format_and_warn(rule_line, key, value, false);
|
||||
r = rule_line_add_token(rule_line, TK_A_DEVLINK, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_DEVLINK, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_DEVLINK, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_DEVLINK, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "NAME")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
@ -677,9 +689,9 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
"Ignoring NAME=\"\", as udev will not delete any network interfaces.");
|
||||
check_value_format_and_warn(rule_line, key, value, false);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_NAME, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_NAME, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_NAME, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_NAME, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "ENV")) {
|
||||
if (isempty(attr))
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
@ -697,15 +709,15 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
|
||||
check_value_format_and_warn(rule_line, key, value, false);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr, /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr, is_case_insensitive);
|
||||
} else if (streq(key, "CONST")) {
|
||||
if (isempty(attr) || !STR_IN_SET(attr, "arch", "virt"))
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
r = rule_line_add_token(rule_line, TK_M_CONST, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_M_CONST, op, value, attr, is_case_insensitive);
|
||||
} else if (streq(key, "TAG")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
@ -717,9 +729,9 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
if (!is_match) {
|
||||
check_value_format_and_warn(rule_line, key, value, true);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_TAG, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_TAG, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_TAG, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_TAG, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "SUBSYSTEM")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
@ -729,14 +741,14 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
if (STR_IN_SET(value, "bus", "class"))
|
||||
log_line_warning(rule_line, "\"%s\" must be specified as \"subsystem\".", value);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_SUBSYSTEM, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_SUBSYSTEM, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "DRIVER")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_DRIVER, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_DRIVER, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "ATTR")) {
|
||||
r = check_attr_format_and_warn(rule_line, key, attr);
|
||||
if (r < 0)
|
||||
@ -750,9 +762,9 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
|
||||
if (!is_match) {
|
||||
check_value_format_and_warn(rule_line, key, value, false);
|
||||
r = rule_line_add_token(rule_line, TK_A_ATTR, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_A_ATTR, op, value, attr, /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_ATTR, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_M_ATTR, op, value, attr, is_case_insensitive);
|
||||
} else if (streq(key, "SYSCTL")) {
|
||||
r = check_attr_format_and_warn(rule_line, key, attr);
|
||||
if (r < 0)
|
||||
@ -766,30 +778,30 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
|
||||
if (!is_match) {
|
||||
check_value_format_and_warn(rule_line, key, value, false);
|
||||
r = rule_line_add_token(rule_line, TK_A_SYSCTL, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_A_SYSCTL, op, value, attr, /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_SYSCTL, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_M_SYSCTL, op, value, attr, is_case_insensitive);
|
||||
} else if (streq(key, "KERNELS")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_KERNEL, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_KERNEL, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "SUBSYSTEMS")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_SUBSYSTEM, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_SUBSYSTEM, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "DRIVERS")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_DRIVER, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_DRIVER, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "ATTRS")) {
|
||||
r = check_attr_format_and_warn(rule_line, key, attr);
|
||||
if (r < 0)
|
||||
@ -802,14 +814,14 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
if (strstr(attr, "../"))
|
||||
log_line_warning(rule_line, "Direct reference to parent sysfs directory, may break in future kernels.");
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_ATTR, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_ATTR, op, value, attr, is_case_insensitive);
|
||||
} else if (streq(key, "TAGS")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_TAG, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_PARENTS_TAG, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "TEST")) {
|
||||
mode_t mode = MODE_INVALID;
|
||||
|
||||
@ -821,8 +833,10 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
check_value_format_and_warn(rule_line, key, value, true);
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
if (is_case_insensitive)
|
||||
return log_line_invalid_prefix(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_TEST, op, value, MODE_TO_PTR(mode));
|
||||
r = rule_line_add_token(rule_line, TK_M_TEST, op, value, MODE_TO_PTR(mode), is_case_insensitive);
|
||||
} else if (streq(key, "PROGRAM")) {
|
||||
if (attr)
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
@ -831,8 +845,10 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
if (!is_match)
|
||||
op = OP_MATCH;
|
||||
if (is_case_insensitive)
|
||||
return log_line_invalid_prefix(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_PROGRAM, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_PROGRAM, op, value, NULL, /* is_case_insensitive */ false);
|
||||
} else if (streq(key, "IMPORT")) {
|
||||
if (isempty(attr))
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
@ -841,18 +857,20 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
if (!is_match)
|
||||
op = OP_MATCH;
|
||||
if (is_case_insensitive)
|
||||
return log_line_invalid_prefix(rule_line, key);
|
||||
|
||||
if (streq(attr, "file"))
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_FILE, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_FILE, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(attr, "program")) {
|
||||
UdevBuiltinCommand cmd;
|
||||
|
||||
cmd = udev_builtin_lookup(value);
|
||||
if (cmd >= 0) {
|
||||
log_line_debug(rule_line, "Found builtin command '%s' for %s, replacing attribute.", value, key);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
|
||||
} else
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_PROGRAM, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_PROGRAM, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
} else if (streq(attr, "builtin")) {
|
||||
UdevBuiltinCommand cmd;
|
||||
|
||||
@ -860,13 +878,13 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
if (cmd < 0)
|
||||
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unknown builtin command: %s", value);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
|
||||
} else if (streq(attr, "db"))
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_DB, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_DB, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(attr, "cmdline"))
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_CMDLINE, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_CMDLINE, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(attr, "parent"))
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_PARENT, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_IMPORT_PARENT, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
else
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
} else if (streq(key, "RESULT")) {
|
||||
@ -875,7 +893,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
if (!is_match)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_M_RESULT, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_M_RESULT, op, value, NULL, is_case_insensitive);
|
||||
} else if (streq(key, "OPTIONS")) {
|
||||
char *tmp;
|
||||
|
||||
@ -887,24 +905,24 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
op = OP_ASSIGN;
|
||||
|
||||
if (streq(value, "string_escape=none"))
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_NONE, op, NULL, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_NONE, op, NULL, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(value, "string_escape=replace"))
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_REPLACE, op, NULL, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_REPLACE, op, NULL, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(value, "db_persist"))
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DB_PERSIST, op, NULL, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DB_PERSIST, op, NULL, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(value, "watch"))
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(1));
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(1), /* is_case_insensitive = */ false);
|
||||
else if (streq(value, "nowatch"))
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(0));
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(0), /* is_case_insensitive = */ false);
|
||||
else if ((tmp = startswith(value, "static_node=")))
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STATIC_NODE, op, tmp, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STATIC_NODE, op, tmp, NULL, /* is_case_insensitive = */ false);
|
||||
else if ((tmp = startswith(value, "link_priority="))) {
|
||||
int prio;
|
||||
|
||||
r = safe_atoi(tmp, &prio);
|
||||
if (r < 0)
|
||||
return log_line_error_errno(rule_line, r, "Failed to parse link priority '%s': %m", tmp);
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DEVLINK_PRIORITY, op, NULL, INT_TO_PTR(prio));
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DEVLINK_PRIORITY, op, NULL, INT_TO_PTR(prio), /* is_case_insensitive = */ false);
|
||||
} else if ((tmp = startswith(value, "log_level="))) {
|
||||
int level;
|
||||
|
||||
@ -915,7 +933,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
if (level < 0)
|
||||
return log_line_error_errno(rule_line, level, "Failed to parse log level '%s': %m", tmp);
|
||||
}
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_LOG_LEVEL, op, NULL, INT_TO_PTR(level));
|
||||
r = rule_line_add_token(rule_line, TK_A_OPTIONS_LOG_LEVEL, op, NULL, INT_TO_PTR(level), /* is_case_insensitive = */ false);
|
||||
} else {
|
||||
log_line_warning(rule_line, "Invalid value for OPTIONS key, ignoring: '%s'", value);
|
||||
return 0;
|
||||
@ -933,17 +951,17 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
}
|
||||
|
||||
if (parse_uid(value, &uid) >= 0)
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid));
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false);
|
||||
else if (resolve_name_timing == RESOLVE_NAME_EARLY &&
|
||||
rule_get_substitution_type(value) == SUBST_TYPE_PLAIN) {
|
||||
r = rule_resolve_user(rule_line, value, &uid);
|
||||
if (r < 0)
|
||||
return log_line_error_errno(rule_line, r, "Failed to resolve user name '%s': %m", value);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid));
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false);
|
||||
} else if (resolve_name_timing != RESOLVE_NAME_NEVER) {
|
||||
check_value_format_and_warn(rule_line, key, value, true);
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
} else {
|
||||
log_line_debug(rule_line, "User name resolution is disabled, ignoring %s=\"%s\".", key, value);
|
||||
return 0;
|
||||
@ -961,17 +979,17 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
}
|
||||
|
||||
if (parse_gid(value, &gid) >= 0)
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid));
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false);
|
||||
else if (resolve_name_timing == RESOLVE_NAME_EARLY &&
|
||||
rule_get_substitution_type(value) == SUBST_TYPE_PLAIN) {
|
||||
r = rule_resolve_group(rule_line, value, &gid);
|
||||
if (r < 0)
|
||||
return log_line_error_errno(rule_line, r, "Failed to resolve group name '%s': %m", value);
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid));
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false);
|
||||
} else if (resolve_name_timing != RESOLVE_NAME_NEVER) {
|
||||
check_value_format_and_warn(rule_line, key, value, true);
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_GROUP, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
} else {
|
||||
log_line_debug(rule_line, "Resolving group name is disabled, ignoring GROUP=\"%s\".", value);
|
||||
return 0;
|
||||
@ -989,10 +1007,10 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
}
|
||||
|
||||
if (parse_mode(value, &mode) >= 0)
|
||||
r = rule_line_add_token(rule_line, TK_A_MODE_ID, op, NULL, MODE_TO_PTR(mode));
|
||||
r = rule_line_add_token(rule_line, TK_A_MODE_ID, op, NULL, MODE_TO_PTR(mode), /* is_case_insensitive = */ false);
|
||||
else {
|
||||
check_value_format_and_warn(rule_line, key, value, true);
|
||||
r = rule_line_add_token(rule_line, TK_A_MODE, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_MODE, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
}
|
||||
} else if (streq(key, "SECLABEL")) {
|
||||
if (isempty(attr))
|
||||
@ -1005,13 +1023,13 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
op = OP_ASSIGN;
|
||||
}
|
||||
|
||||
r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, attr);
|
||||
r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, attr, /* is_case_insensitive = */ false);
|
||||
} else if (streq(key, "RUN")) {
|
||||
if (is_match || op == OP_REMOVE)
|
||||
return log_line_invalid_op(rule_line, key);
|
||||
check_value_format_and_warn(rule_line, key, value, true);
|
||||
if (!attr || streq(attr, "program"))
|
||||
r = rule_line_add_token(rule_line, TK_A_RUN_PROGRAM, op, value, NULL);
|
||||
r = rule_line_add_token(rule_line, TK_A_RUN_PROGRAM, op, value, NULL, /* is_case_insensitive = */ false);
|
||||
else if (streq(attr, "builtin")) {
|
||||
UdevBuiltinCommand cmd;
|
||||
|
||||
@ -1019,7 +1037,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
|
||||
if (cmd < 0)
|
||||
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unknown builtin command '%s', ignoring.", value);
|
||||
r = rule_line_add_token(rule_line, TK_A_RUN_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
|
||||
r = rule_line_add_token(rule_line, TK_A_RUN_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
|
||||
} else
|
||||
return log_line_invalid_attr(rule_line, key);
|
||||
} else if (streq(key, "GOTO")) {
|
||||
@ -1119,13 +1137,30 @@ static void check_token_delimiters(UdevRuleLine *rule_line, const char *line) {
|
||||
}
|
||||
}
|
||||
|
||||
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) {
|
||||
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos, bool *ret_is_case_insensitive) {
|
||||
char *i, *j;
|
||||
bool is_escaped;
|
||||
bool is_escaped = false, is_case_insensitive = false;
|
||||
|
||||
assert(str);
|
||||
assert(ret_value);
|
||||
assert(ret_endpos);
|
||||
assert(ret_is_case_insensitive);
|
||||
|
||||
/* check if string is prefixed with:
|
||||
* - "e" for escaped
|
||||
* - "i" for case insensitive match
|
||||
*
|
||||
* Note both e and i can be set but do not allow duplicates ("eei", "eii"). */
|
||||
for (const char *k = str; *k != '"' && k < str + 2; k++)
|
||||
if (*k == 'e' && !is_escaped)
|
||||
is_escaped = true;
|
||||
else if (*k == 'i' && !is_case_insensitive)
|
||||
is_case_insensitive = true;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
/* value must be double quotated */
|
||||
is_escaped = str[0] == 'e';
|
||||
str += is_escaped;
|
||||
str += is_escaped + is_case_insensitive;
|
||||
if (str[0] != '"')
|
||||
return -EINVAL;
|
||||
|
||||
@ -1172,10 +1207,11 @@ int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) {
|
||||
|
||||
*ret_value = str;
|
||||
*ret_endpos = i + 1;
|
||||
*ret_is_case_insensitive = is_case_insensitive;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOperatorType *ret_op, char **ret_value) {
|
||||
static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOperatorType *ret_op, char **ret_value, bool *ret_is_case_insensitive) {
|
||||
char *key_begin, *key_end, *attr, *tmp;
|
||||
UdevRuleOperatorType op;
|
||||
int r;
|
||||
@ -1185,6 +1221,7 @@ static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOper
|
||||
assert(ret_key);
|
||||
assert(ret_op);
|
||||
assert(ret_value);
|
||||
assert(ret_is_case_insensitive);
|
||||
|
||||
key_begin = skip_leading_chars(*line, WHITESPACE ",");
|
||||
|
||||
@ -1219,7 +1256,7 @@ static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOper
|
||||
|
||||
tmp += op == OP_ASSIGN ? 1 : 2;
|
||||
tmp = skip_leading_chars(tmp, NULL);
|
||||
r = udev_rule_parse_value(tmp, ret_value, line);
|
||||
r = udev_rule_parse_value(tmp, ret_value, line, ret_is_case_insensitive);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1291,17 +1328,18 @@ static int rule_add_line(UdevRuleFile *rule_file, const char *line_str, unsigned
|
||||
for (p = rule_line->line; !isempty(p); ) {
|
||||
char *key, *attr, *value;
|
||||
UdevRuleOperatorType op;
|
||||
bool is_case_insensitive;
|
||||
|
||||
if (extra_checks)
|
||||
check_token_delimiters(rule_line, p);
|
||||
|
||||
r = parse_line(&p, &key, &attr, &op, &value);
|
||||
r = parse_line(&p, &key, &attr, &op, &value, &is_case_insensitive);
|
||||
if (r < 0)
|
||||
return log_line_error_errno(rule_line, r, "Invalid key/value pair, ignoring.");
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
r = parse_token(rule_line, key, attr, op, value);
|
||||
r = parse_token(rule_line, key, attr, op, value, is_case_insensitive);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -1412,7 +1450,6 @@ static bool tokens_eq(const UdevRuleToken *a, const UdevRuleToken *b) {
|
||||
assert(b);
|
||||
|
||||
return a->attr_subst_type == b->attr_subst_type &&
|
||||
a->attr_match_remove_trailing_whitespace == b->attr_match_remove_trailing_whitespace &&
|
||||
token_type_and_value_eq(a, b) &&
|
||||
token_type_and_data_eq(a, b);
|
||||
}
|
||||
@ -1427,7 +1464,6 @@ static bool nulstr_tokens_conflict(const UdevRuleToken *a, const UdevRuleToken *
|
||||
a->op == OP_MATCH &&
|
||||
a->match_type == b->match_type &&
|
||||
a->attr_subst_type == b->attr_subst_type &&
|
||||
a->attr_match_remove_trailing_whitespace == b->attr_match_remove_trailing_whitespace &&
|
||||
token_type_and_data_eq(a, b)))
|
||||
return false;
|
||||
|
||||
@ -1696,7 +1732,7 @@ bool udev_rules_should_reload(UdevRules *rules) {
|
||||
|
||||
static bool token_match_string(UdevRuleToken *token, const char *str) {
|
||||
const char *value;
|
||||
bool match = false;
|
||||
bool match = false, case_insensitive;
|
||||
|
||||
assert(token);
|
||||
assert(token->value);
|
||||
@ -1704,13 +1740,17 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
|
||||
|
||||
str = strempty(str);
|
||||
value = token->value;
|
||||
case_insensitive = FLAGS_SET(token->match_type, MATCH_CASE_INSENSITIVE);
|
||||
|
||||
switch (token->match_type) {
|
||||
switch (token->match_type & _MATCH_TYPE_MASK) {
|
||||
case MATCH_TYPE_EMPTY:
|
||||
match = isempty(str);
|
||||
break;
|
||||
case MATCH_TYPE_SUBSYSTEM:
|
||||
match = STR_IN_SET(str, "subsystem", "class", "bus");
|
||||
if (case_insensitive)
|
||||
match = STRCASE_IN_SET(str, "subsystem", "class", "bus");
|
||||
else
|
||||
match = STR_IN_SET(str, "subsystem", "class", "bus");
|
||||
break;
|
||||
case MATCH_TYPE_PLAIN_WITH_EMPTY:
|
||||
if (isempty(str)) {
|
||||
@ -1720,7 +1760,7 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
|
||||
_fallthrough_;
|
||||
case MATCH_TYPE_PLAIN:
|
||||
NULSTR_FOREACH(i, value)
|
||||
if (streq(i, str)) {
|
||||
if (case_insensitive ? strcaseeq(i, str) : streq(i, str)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
@ -1733,7 +1773,7 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
|
||||
_fallthrough_;
|
||||
case MATCH_TYPE_GLOB:
|
||||
NULSTR_FOREACH(i, value)
|
||||
if ((fnmatch(i, str, 0) == 0)) {
|
||||
if ((fnmatch(i, str, case_insensitive ? FNM_CASEFOLD : 0) == 0)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
@ -1773,7 +1813,7 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
|
||||
return false;
|
||||
|
||||
/* remove trailing whitespace, if not asked to match for it */
|
||||
if (token->attr_match_remove_trailing_whitespace) {
|
||||
if (FLAGS_SET(token->match_type, MATCH_REMOVE_TRAILING_WHITESPACE)) {
|
||||
strscpy(vbuf, sizeof(vbuf), value);
|
||||
value = delete_trailing_chars(vbuf, NULL);
|
||||
}
|
||||
@ -1785,7 +1825,7 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
|
||||
return false;
|
||||
|
||||
/* remove trailing whitespace, if not asked to match for it */
|
||||
if (token->attr_match_remove_trailing_whitespace)
|
||||
if (FLAGS_SET(token->match_type, MATCH_REMOVE_TRAILING_WHITESPACE))
|
||||
delete_trailing_chars(vbuf, NULL);
|
||||
|
||||
return token_match_string(token, vbuf);
|
||||
|
@ -29,7 +29,7 @@ typedef enum ResolveNameTiming {
|
||||
_RESOLVE_NAME_TIMING_INVALID = -EINVAL,
|
||||
} ResolveNameTiming;
|
||||
|
||||
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos);
|
||||
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos, bool *ret_is_case_insensitive);
|
||||
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);
|
||||
|
@ -2313,6 +2313,17 @@ SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c \"printf %%s 'foo1 foo2' | grep 'foo1 f
|
||||
SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sd*", SYMLINK+="blockdev"
|
||||
KERNEL=="sda6", OPTIONS+="link_priority=10"
|
||||
"""),
|
||||
|
||||
Rules.new(
|
||||
"case insensitive match",
|
||||
Device(
|
||||
"/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
|
||||
exp_links = ["ok"],
|
||||
),
|
||||
|
||||
rules = r"""
|
||||
KERNEL==i"SDA1", SUBSYSTEMS==i"SCSI", ATTRS{vendor}==i"a?a", SYMLINK+="ok"
|
||||
"""),
|
||||
]
|
||||
|
||||
def fork_and_run_udev(action: str, rules: Rules) -> None:
|
||||
|
@ -237,6 +237,8 @@ test_syntax_error 'ENV=="b"' 'Invalid attribute for ENV.'
|
||||
test_syntax_error 'ENV{a}-="b"' 'Invalid operator for ENV.'
|
||||
test_syntax_error 'ENV{a}:="b"' "ENV key takes '==', '!=', '=', or '+=' operator, assuming '='."
|
||||
test_syntax_error 'ENV{ACTION}="b"' "Invalid ENV attribute. 'ACTION' cannot be set."
|
||||
test_syntax_error 'ENV{a}=i"b"' "Invalid prefix 'i' for 'ENV'. The 'i' prefix can be specified only for '==' or '!=' operator."
|
||||
test_syntax_error 'ENV{a}+=i"b"' "Invalid prefix 'i' for 'ENV'. The 'i' prefix can be specified only for '==' or '!=' operator."
|
||||
test_syntax_error 'CONST=="b"' 'Invalid attribute for CONST.'
|
||||
test_syntax_error 'CONST{a}=="b"' 'Invalid attribute for CONST.'
|
||||
test_syntax_error 'CONST{arch}="b"' 'Invalid operator for CONST.'
|
||||
@ -275,10 +277,12 @@ test_syntax_error 'TEST{0644}="b"' 'Invalid operator for TEST.'
|
||||
test_syntax_error 'PROGRAM{a}=="b"' 'Invalid attribute for PROGRAM.'
|
||||
test_syntax_error 'PROGRAM-="b"' 'Invalid operator for PROGRAM.'
|
||||
test_syntax_error 'PROGRAM=="%", NAME="b"' 'Invalid value "%" for PROGRAM (char 1: invalid substitution type), ignoring.'
|
||||
test_syntax_error 'PROGRAM==i"b"' "Invalid prefix 'i' for PROGRAM."
|
||||
test_syntax_error 'IMPORT="b"' 'Invalid attribute for IMPORT.'
|
||||
test_syntax_error 'IMPORT{a}="b"' 'Invalid attribute for IMPORT.'
|
||||
test_syntax_error 'IMPORT{a}-="b"' 'Invalid operator for IMPORT.'
|
||||
test_syntax_error 'IMPORT{file}=="%", NAME="b"' 'Invalid value "%" for IMPORT (char 1: invalid substitution type), ignoring.'
|
||||
test_syntax_error 'IMPORT{file}==i"a", NAME="b"' "Invalid prefix 'i' for IMPORT."
|
||||
test_syntax_error 'IMPORT{builtin}!="foo"' 'Unknown builtin command: foo'
|
||||
test_syntax_error 'RESULT{a}=="b"' 'Invalid attribute for RESULT.'
|
||||
test_syntax_error 'RESULT:="b"' 'Invalid operator for RESULT.'
|
||||
|
Loading…
Reference in New Issue
Block a user