#include "alloc-util.h"
#include "conf-files.h"
+#include "def.h"
#include "device-private.h"
#include "device-util.h"
#include "dirent-util.h"
#include "util.h"
#define PREALLOC_TOKEN 2048
+#define RULES_DIRS (const char* const*) CONF_PATHS_STRV("udev/rules.d")
struct uid_gid {
unsigned name_off;
};
};
-static const char* const rules_dirs[] = {
- "/etc/udev/rules.d",
- "/run/udev/rules.d",
- UDEVLIBEXECDIR "/rules.d",
- NULL
-};
-
struct UdevRules {
usec_t dirs_ts_usec;
ResolveNameTiming resolve_name_timing;
/* every key in the rules file becomes a token */
struct token *tokens;
- unsigned token_cur;
- unsigned token_max;
+ size_t token_cur;
+ size_t token_max;
/* all key strings are copied and de-duplicated in a single continuous string buffer */
struct strbuf *strbuf;
/* during rule parsing, uid/gid lookup results are cached */
struct uid_gid *uids;
- unsigned uids_cur;
- unsigned uids_max;
+ size_t uids_cur;
+ size_t uids_max;
struct uid_gid *gids;
- unsigned gids_cur;
- unsigned gids_max;
+ size_t gids_cur;
+ size_t gids_max;
};
static char *rules_str(UdevRules *rules, unsigned off) {
UdevRules *rules;
struct token rule;
struct token token[MAX_TK];
- unsigned token_cur;
+ size_t token_cur;
};
#if ENABLE_DEBUG_UDEV
}
static void dump_rules(UdevRules *rules) {
- unsigned i;
+ size_t i;
- log_debug("Dumping %u (%zu bytes) tokens, %zu (%zu bytes) strings",
+ log_debug("Dumping %zu (%zu bytes) tokens, %zu (%zu bytes) strings",
rules->token_cur,
rules->token_cur * sizeof(struct token),
rules->strbuf->nodes_count,
static int add_token(UdevRules *rules, struct token *token) {
/* grow buffer if needed */
- if (rules->token_cur+1 >= rules->token_max) {
- struct token *tokens;
- unsigned add;
-
- /* double the buffer size */
- add = rules->token_max;
- if (add < 8)
- add = 8;
-
- tokens = reallocarray(rules->tokens, rules->token_max + add, sizeof(struct token));
- if (!tokens)
- return -ENOMEM;
- rules->tokens = tokens;
- rules->token_max += add;
- }
+ if (!GREEDY_REALLOC(rules->tokens, rules->token_max, rules->token_cur + 1))
+ return -ENOMEM;
+
memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token));
rules->token_cur++;
return 0;
}
static uid_t add_uid(UdevRules *rules, const char *owner) {
- unsigned i;
uid_t uid = 0;
unsigned off;
+ size_t i;
int r;
/* lookup, if we know it already */
log_unknown_owner(NULL, r, "user", owner);
/* grow buffer if needed */
- if (rules->uids_cur+1 >= rules->uids_max) {
- struct uid_gid *uids;
- unsigned add;
-
- /* double the buffer size */
- add = rules->uids_max;
- if (add < 1)
- add = 8;
-
- uids = reallocarray(rules->uids, rules->uids_max + add, sizeof(struct uid_gid));
- if (!uids)
- return uid;
- rules->uids = uids;
- rules->uids_max += add;
- }
+ if (!GREEDY_REALLOC(rules->uids, rules->uids_max, rules->uids_cur + 1))
+ return -ENOMEM;
+
rules->uids[rules->uids_cur].uid = uid;
off = rules_add_string(rules, owner);
if (off <= 0)
}
static gid_t add_gid(UdevRules *rules, const char *group) {
- unsigned i;
gid_t gid = 0;
unsigned off;
+ size_t i;
int r;
/* lookup, if we know it already */
log_unknown_owner(NULL, r, "group", group);
/* grow buffer if needed */
- if (rules->gids_cur+1 >= rules->gids_max) {
- struct uid_gid *gids;
- unsigned add;
-
- /* double the buffer size */
- add = rules->gids_max;
- if (add < 1)
- add = 8;
-
- gids = reallocarray(rules->gids, rules->gids_max + add, sizeof(struct uid_gid));
- if (!gids)
- return gid;
- rules->gids = gids;
- rules->gids_max += add;
- }
+ if (!GREEDY_REALLOC(rules->gids, rules->gids_max, rules->gids_cur + 1))
+ return -ENOMEM;
+
rules->gids[rules->gids_cur].gid = gid;
off = rules_add_string(rules, group);
if (off <= 0)
static int get_key(char **line, char **key, enum operation_type *op, char **value) {
char *linepos;
char *temp;
- unsigned i, j;
+ size_t i, j;
linepos = *line;
if (!linepos || linepos[0] == '\0')
}
static int sort_token(UdevRules *rules, struct rule_tmp *rule_tmp) {
- unsigned i;
- unsigned start = 0;
- unsigned end = rule_tmp->token_cur;
+ size_t i;
+ size_t start = 0;
+ size_t end = rule_tmp->token_cur;
int r;
for (i = 0; i < rule_tmp->token_cur; i++) {
enum token_type next_val = TK_UNSET;
- unsigned next_idx = 0;
- unsigned j;
+ size_t next_idx = 0;
+ size_t j;
/* find smallest value */
for (j = start; j < end; j++) {
#define LOG_RULE_WARNING(fmt, ...) LOG_RULE_FULL(LOG_WARNING, fmt, ##__VA_ARGS__)
#define LOG_RULE_DEBUG(fmt, ...) LOG_RULE_FULL(LOG_DEBUG, fmt, ##__VA_ARGS__)
#define LOG_AND_RETURN(fmt, ...) { LOG_RULE_ERROR(fmt, __VA_ARGS__); return; }
-#define LOG_AND_RETURN_ADD_KEY LOG_AND_RETURN("Temporary rule array too small, aborting event processing with %u items", rule_tmp.token_cur);
+#define LOG_AND_RETURN_ADD_KEY LOG_AND_RETURN("Temporary rule array too small, aborting event processing with %zu items", rule_tmp.token_cur);
static void add_rule(UdevRules *rules, char *line,
const char *filename, unsigned filename_off, unsigned lineno) {
} else if (streq(key, "OWNER")) {
uid_t uid;
- char *endptr;
if (op == OP_REMOVE)
LOG_AND_RETURN("Invalid %s operation", key);
- uid = strtoul(value, &endptr, 10);
- if (endptr[0] == '\0')
+ if (parse_uid(value, &uid) >= 0)
r = rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid);
else if (rules->resolve_name_timing == RESOLVE_NAME_EARLY && !strchr("$%", value[0])) {
uid = add_uid(rules, value);
} else if (streq(key, "GROUP")) {
gid_t gid;
- char *endptr;
if (op == OP_REMOVE)
LOG_AND_RETURN("Invalid %s operation", key);
- gid = strtoul(value, &endptr, 10);
- if (endptr[0] == '\0')
+ if (parse_gid(value, &gid) >= 0)
r = rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid);
else if ((rules->resolve_name_timing == RESOLVE_NAME_EARLY) && !strchr("$%", value[0])) {
gid = add_gid(rules, value);
}
static int parse_file(UdevRules *rules, const char *filename) {
+ _cleanup_free_ char *continuation = NULL;
_cleanup_fclose_ FILE *f = NULL;
- unsigned first_token;
+ bool ignore_line = false;
+ size_t first_token, i;
unsigned filename_off;
- char line[UTIL_LINE_SIZE];
- int line_nr = 0;
- unsigned i;
+ int line_nr = 0, r;
f = fopen(filename, "re");
if (!f) {
first_token = rules->token_cur;
filename_off = rules_add_string(rules, filename);
- while (fgets(line, sizeof(line), f)) {
- char *key;
+ for (;;) {
+ _cleanup_free_ char *buf = NULL;
size_t len;
+ char *line;
+
+ r = read_line(f, UTIL_LINE_SIZE, &buf);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
- /* skip whitespace */
line_nr++;
- key = line;
- while (isspace(key[0]))
- key++;
+ line = buf + strspn(buf, WHITESPACE);
- /* comment */
- if (key[0] == '#')
+ if (line[0] == '#')
continue;
len = strlen(line);
- if (len < 3)
- continue;
- /* continue reading if backslash+newline is found */
- while (line[len-2] == '\\') {
- if (!fgets(&line[len-2], (sizeof(line)-len)+2, f))
- break;
- if (strlen(&line[len-2]) < 2)
- break;
- line_nr++;
- len = strlen(line);
+ if (continuation && !ignore_line) {
+ if (strlen(continuation) + len >= UTIL_LINE_SIZE)
+ ignore_line = true;
+
+ if (!strextend(&continuation, line, NULL))
+ return log_oom();
+
+ if (!ignore_line) {
+ line = continuation;
+ len = strlen(line);
+ }
}
- if (len+1 >= sizeof(line)) {
- log_error("Line too long '%s':%u, ignored", filename, line_nr);
+ if (len > 0 && line[len - 1] == '\\') {
+ if (ignore_line)
+ continue;
+
+ line[len - 1] = '\0';
+ if (!continuation) {
+ continuation = strdup(line);
+ if (!continuation)
+ return log_oom();
+ }
+
continue;
}
- add_rule(rules, key, filename, filename_off, line_nr);
+
+ if (ignore_line)
+ log_error("Line too long '%s':%u, ignored", filename, line_nr);
+ else if (len > 0)
+ add_rule(rules, line, filename, filename_off, line_nr);
+
+ continuation = mfree(continuation);
+ ignore_line = false;
}
/* link GOTOs to LABEL rules in this file to be able to fast-forward */
for (i = first_token+1; i < rules->token_cur; i++) {
if (rules->tokens[i].type == TK_A_GOTO) {
char *label = rules_str(rules, rules->tokens[i].key.value_off);
- unsigned j;
+ size_t j;
for (j = i+1; j < rules->token_cur; j++) {
if (rules->tokens[j].type != TK_RULE)
};
/* init token array and string buffer */
- rules->tokens = malloc_multiply(PREALLOC_TOKEN, sizeof(struct token));
+ rules->tokens = new(struct token, PREALLOC_TOKEN);
if (!rules->tokens)
return -ENOMEM;
rules->token_max = PREALLOC_TOKEN;
udev_rules_check_timestamp(rules);
- r = conf_files_list_strv(&files, ".rules", NULL, 0, rules_dirs);
+ r = conf_files_list_strv(&files, ".rules", NULL, 0, RULES_DIRS);
if (r < 0)
return log_error_errno(r, "Failed to enumerate rules files: %m");
struct token end_token = { .type = TK_END };
add_token(rules, &end_token);
- log_debug("Rules contain %zu bytes tokens (%u * %zu bytes), %zu bytes strings",
+ log_debug("Rules contain %zu bytes tokens (%zu * %zu bytes), %zu bytes strings",
rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->strbuf->len);
/* cleanup temporary strbuf data */
if (!rules)
return false;
- return paths_check_timestamp(rules_dirs, &rules->dirs_ts_usec, true);
+ return paths_check_timestamp(RULES_DIRS, &rules->dirs_ts_usec, true);
}
static bool match_key(UdevRules *rules, struct token *token, const char *val) {