#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));
* so it could be safely interpreted as nulstr.
*/
assert_se(value[strlen(value) + 1] == '\0');
+ assert_se(i == expected_case_insensitive);
}
}
* 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);
_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;
#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), \
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;
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;
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;
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);
"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);
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);
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);
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)
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)
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)
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;
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);
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);
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;
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")) {
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;
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;
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;
}
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;
}
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;
}
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))
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;
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")) {
}
}
-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;
*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;
assert(ret_key);
assert(ret_op);
assert(ret_value);
+ assert(ret_is_case_insensitive);
key_begin = skip_leading_chars(*line, WHITESPACE ",");
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;
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;
}
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);
str = strempty(str);
value = token->value;
+ case_insensitive = FLAGS_SET(token->match_type, MATCH_CASE_INSENSITIVE);
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)) {
_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;
}
_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;
}