X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udev%2Fudev-rules.c;h=6d32e7301702bd19083b6f7db5a665c04c0514e5;hp=f5d23b45b4a5cdbd503fda53ef2ebcf7a7d249ff;hb=3b529da42586aa430bb0d03800b19dd45552eae1;hpb=e9906fec711192761f27468244d3012041bcb0ee diff --git a/udev/udev-rules.c b/udev/udev-rules.c index f5d23b45b..6d32e7301 100644 --- a/udev/udev-rules.c +++ b/udev/udev-rules.c @@ -125,6 +125,7 @@ enum token_type { TK_M_DEVLINK, /* val */ TK_M_NAME, /* val */ TK_M_ENV, /* val, attr */ + TK_M_TAG, /* val */ TK_M_SUBSYSTEM, /* val */ TK_M_DRIVER, /* val */ TK_M_WAITFOR, /* val */ @@ -142,6 +143,7 @@ enum token_type { TK_M_IMPORT_FILE, /* val */ TK_M_IMPORT_PROG, /* val */ TK_M_IMPORT_DB, /* val */ + TK_M_IMPORT_CMDLINE, /* val */ TK_M_IMPORT_PARENT, /* val */ TK_M_RESULT, /* val */ TK_M_MAX, @@ -156,7 +158,9 @@ enum token_type { TK_A_OWNER_ID, /* uid_t */ TK_A_GROUP_ID, /* gid_t */ TK_A_MODE_ID, /* mode_t */ + TK_A_STATIC_NODE, /* val */ TK_A_ENV, /* val, attr */ + TK_A_TAG, /* val */ TK_A_NAME, /* val */ TK_A_DEVLINK, /* val */ TK_A_EVENT_TIMEOUT, /* int */ @@ -170,10 +174,12 @@ enum token_type { /* we try to pack stuff in a way that we take only 12 bytes per token */ struct token { union { - unsigned char type; /* same as in rule and key */ + unsigned char type; /* same in rule and key */ struct { enum token_type type:8; - unsigned int flags:8; + bool can_set_name:1; + bool has_static_node:1; + unsigned int unused:6; unsigned short token_count; unsigned int label_off; unsigned short filename_off; @@ -253,6 +259,7 @@ static const char *token_str(enum token_type type) [TK_M_DEVLINK] = "M DEVLINK", [TK_M_NAME] = "M NAME", [TK_M_ENV] = "M ENV", + [TK_M_TAG] = "M TAG", [TK_M_SUBSYSTEM] = "M SUBSYSTEM", [TK_M_DRIVER] = "M DRIVER", [TK_M_WAITFOR] = "M WAITFOR", @@ -270,6 +277,7 @@ static const char *token_str(enum token_type type) [TK_M_IMPORT_FILE] = "M IMPORT_FILE", [TK_M_IMPORT_PROG] = "M IMPORT_PROG", [TK_M_IMPORT_DB] = "M IMPORT_DB", + [TK_M_IMPORT_CMDLINE] = "M IMPORT_CMDLINE", [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT", [TK_M_RESULT] = "M RESULT", [TK_M_MAX] = "M MAX", @@ -283,8 +291,10 @@ static const char *token_str(enum token_type type) [TK_A_MODE] = "A MODE", [TK_A_OWNER_ID] = "A OWNER_ID", [TK_A_GROUP_ID] = "A GROUP_ID", + [TK_A_STATIC_NODE] = "A STATIC_NODE", [TK_A_MODE_ID] = "A MODE_ID", [TK_A_ENV] = "A ENV", + [TK_A_TAG] = "A ENV", [TK_A_NAME] = "A NAME", [TK_A_DEVLINK] = "A DEVLINK", [TK_A_EVENT_TIMEOUT] = "A EVENT_TIMEOUT", @@ -313,11 +323,10 @@ static void dump_token(struct udev_rules *rules, struct token *token) const char *tk_ptr = (char *)token; unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token); - dbg(rules->udev, "* RULE %s:%u, token: %u, count: %u, label: '%s', flags: 0x%02x\n", + dbg(rules->udev, "* RULE %s:%u, token: %u, count: %u, label: '%s'\n", &rules->buf[token->rule.filename_off], token->rule.filename_line, idx, token->rule.token_count, - &rules->buf[token->rule.label_off], - token->rule.flags); + &rules->buf[token->rule.label_off]); break; } case TK_M_ACTION: @@ -335,6 +344,7 @@ static void dump_token(struct udev_rules *rules, struct token *token) case TK_M_IMPORT_FILE: case TK_M_IMPORT_PROG: case TK_M_IMPORT_DB: + case TK_M_IMPORT_CMDLINE: case TK_M_IMPORT_PARENT: case TK_M_RESULT: case TK_A_NAME: @@ -354,8 +364,14 @@ static void dump_token(struct udev_rules *rules, struct token *token) dbg(rules->udev, "%s %s '%s' '%s'(%s)\n", token_str(type), operation_str(op), attr, value, string_glob_str(glob)); break; + case TK_M_TAG: + case TK_A_TAG: + dbg(rules->udev, "%s %s '%s'\n", token_str(type), operation_str(op), value); + break; case TK_A_STRING_ESCAPE_NONE: case TK_A_STRING_ESCAPE_REPLACE: + dbg(rules->udev, "%s\n", token_str(type)); + break; case TK_M_TEST: dbg(rules->udev, "%s %s '%s'(%s) %#o\n", token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode); @@ -364,7 +380,7 @@ static void dump_token(struct udev_rules *rules, struct token *token) dbg(rules->udev, "%s %u\n", token_str(type), token->key.watch); break; case TK_A_DEVLINK_PRIO: - dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.devlink_prio); + dbg(rules->udev, "%s %u\n", token_str(type), token->key.devlink_prio); break; case TK_A_OWNER_ID: dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.uid); @@ -375,8 +391,11 @@ static void dump_token(struct udev_rules *rules, struct token *token) case TK_A_MODE_ID: dbg(rules->udev, "%s %s %#o\n", token_str(type), operation_str(op), token->key.mode); break; + case TK_A_STATIC_NODE: + dbg(rules->udev, "%s '%s'\n", token_str(type), value); + break; case TK_A_EVENT_TIMEOUT: - dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.event_timeout); + dbg(rules->udev, "%s %u\n", token_str(type), token->key.event_timeout); break; case TK_A_GOTO: dbg(rules->udev, "%s '%s' %u\n", token_str(type), value, token->key.rule_goto); @@ -737,7 +756,7 @@ static int import_program_into_properties(struct udev_device *dev, const char *p char *line; envp = udev_device_get_properties_envp(dev); - if (util_run_program(udev, program, envp, result, sizeof(result), &reslen, NULL) != 0) + if (util_run_program(udev, program, envp, result, sizeof(result), &reslen, NULL, false) != 0) return -1; line = result; @@ -824,7 +843,7 @@ static int wait_for_file(struct udev_device *dev, const char *file, int timeout) static int attr_subst_subdir(char *attr, size_t len) { - int found = 0; + bool found = false; if (strstr(attr, "/*/")) { char *pos; @@ -849,7 +868,7 @@ static int attr_subst_subdir(char *attr, size_t len) continue; util_strscpyl(attr, len, dirname, "/", dent->d_name, tail, NULL); if (stat(attr, &stats) == 0) { - found = 1; + found = true; break; } } @@ -878,7 +897,7 @@ static int get_key(struct udev *udev, char **line, char **key, enum operation_ty return -1; *key = linepos; - while (1) { + for (;;) { linepos++; if (linepos[0] == '\0') return -1; @@ -994,6 +1013,7 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, case TK_M_IMPORT_FILE: case TK_M_IMPORT_PROG: case TK_M_IMPORT_DB: + case TK_M_IMPORT_CMDLINE: case TK_M_IMPORT_PARENT: case TK_M_RESULT: case TK_A_OWNER: @@ -1001,6 +1021,8 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, case TK_A_MODE: case TK_A_NAME: case TK_A_GOTO: + case TK_M_TAG: + case TK_A_TAG: token->key.value_off = add_string(rule_tmp->rules, value); break; case TK_M_ENV: @@ -1023,6 +1045,7 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, break; case TK_A_STRING_ESCAPE_NONE: case TK_A_STRING_ESCAPE_REPLACE: + break; case TK_A_RUN: token->key.value_off = add_string(rule_tmp->rules, value); token->key.fail_on_error = *(int *)data; @@ -1040,6 +1063,9 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, case TK_A_MODE_ID: token->key.mode = *(mode_t *)data; break; + case TK_A_STATIC_NODE: + token->key.value_off = add_string(rule_tmp->rules, value); + break; case TK_A_EVENT_TIMEOUT: token->key.event_timeout = *(int *)data; break; @@ -1158,7 +1184,7 @@ static int add_rule(struct udev_rules *rules, char *line, rule_tmp.rule.rule.filename_line = lineno; linepos = line; - while (1) { + for (;;) { char *key; char *value; enum operation_type op; @@ -1347,6 +1373,14 @@ static int add_rule(struct udev_rules *rules, char *line, continue; } + if (strcmp(key, "TAG") == 0) { + if (op < OP_MATCH_MAX) + rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL); + else + rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL); + continue; + } + if (strcmp(key, "PROGRAM") == 0) { rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL); continue; @@ -1372,6 +1406,9 @@ static int add_rule(struct udev_rules *rules, char *line, } else if (attr != NULL && strstr(attr, "db")) { dbg(rules->udev, "IMPORT will include db values\n"); rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL); + } else if (attr != NULL && strstr(attr, "cmdline")) { + dbg(rules->udev, "IMPORT will include db values\n"); + rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL); } else if (attr != NULL && strstr(attr, "parent")) { dbg(rules->udev, "IMPORT will include the parent values\n"); rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL); @@ -1447,14 +1484,19 @@ static int add_rule(struct udev_rules *rules, char *line, if (op < OP_MATCH_MAX) { rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL); } else { - if (value[0] == '\0') - info(rules->udev, "name empty, node creation suppressed\n"); - else if (strcmp(value, "%k") == 0) - err(rules->udev, "NAME=\"%%k\" is superfluous and breaks " - "kernel supplied names, please remove it from %s:%u\n", filename, lineno); + if (strcmp(value, "%k") == 0) { + err(rules->udev, "NAME=\"%%k\" is ignored, because it breaks kernel supplied names, " + "please remove it from %s:%u\n", filename, lineno); + continue; + } + if (value[0] == '\0') { + info(rules->udev, "NAME=\"\" is ignored, because udev will not delete any device nodes, " + "please remove it from %s:%u\n", filename, lineno); + continue; + } rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL); } - rule_tmp.rule.rule.flags = 1; + rule_tmp.rule.rule.can_set_name = true; continue; } @@ -1469,7 +1511,7 @@ static int add_rule(struct udev_rules *rules, char *line, flag = 1; rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, &flag); } - rule_tmp.rule.rule.flags = 1; + rule_tmp.rule.rule.can_set_name = true; continue; } @@ -1486,7 +1528,7 @@ static int add_rule(struct udev_rules *rules, char *line, } else if (rules->resolve_names >= 0) { rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL); } - rule_tmp.rule.rule.flags = 1; + rule_tmp.rule.rule.can_set_name = true; continue; } @@ -1503,7 +1545,7 @@ static int add_rule(struct udev_rules *rules, char *line, } else if (rules->resolve_names >= 0) { rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL); } - rule_tmp.rule.rule.flags = 1; + rule_tmp.rule.rule.can_set_name = true; continue; } @@ -1516,7 +1558,7 @@ static int add_rule(struct udev_rules *rules, char *line, rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode); else rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL); - rule_tmp.rule.rule.flags = 1; + rule_tmp.rule.rule.can_set_name = true; continue; } @@ -1527,39 +1569,44 @@ static int add_rule(struct udev_rules *rules, char *line, if (pos != NULL) { int prio = atoi(&pos[strlen("link_priority=")]); - rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, 0, NULL, &prio); + rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio); dbg(rules->udev, "link priority=%i\n", prio); } pos = strstr(value, "event_timeout="); if (pos != NULL) { int tout = atoi(&pos[strlen("event_timeout=")]); - rule_add_key(&rule_tmp, TK_A_EVENT_TIMEOUT, 0, NULL, &tout); + rule_add_key(&rule_tmp, TK_A_EVENT_TIMEOUT, op, NULL, &tout); dbg(rules->udev, "event timeout=%i\n", tout); } pos = strstr(value, "string_escape="); if (pos != NULL) { pos = &pos[strlen("string_escape=")]; if (strncmp(pos, "none", strlen("none")) == 0) - rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, 0, NULL, NULL); + rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL); else if (strncmp(pos, "replace", strlen("replace")) == 0) - rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, 0, NULL, NULL); + rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL); } pos = strstr(value, "nowatch"); if (pos != NULL) { const int off = 0; - rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, 0, NULL, &off); + rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &off); dbg(rules->udev, "inotify watch of device disabled\n"); } else { pos = strstr(value, "watch"); if (pos != NULL) { const int on = 1; - rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, 0, NULL, &on); + rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &on); dbg(rules->udev, "inotify watch of device requested\n"); } } + pos = strstr(value, "static_node="); + if (pos != NULL) { + rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, &pos[strlen("static_node=")], NULL); + rule_tmp.rule.rule.has_static_node = true; + } continue; } err(rules->udev, "unknown key '%s' in %s:%u\n", key, filename, lineno); @@ -1667,7 +1714,7 @@ static int add_matching_files(struct udev *udev, struct udev_list_node *file_lis return -1; } - while (1) { + for (;;) { struct dirent *dent; dent = readdir(dir); @@ -1881,7 +1928,7 @@ static int match_key(struct udev_rules *rules, struct token *token, const char * { char *key_value = &rules->buf[token->key.value_off]; char *pos; - int match = 0; + bool match = false; if (val == NULL) val = ""; @@ -1900,7 +1947,7 @@ static int match_key(struct udev_rules *rules, struct token *token, const char * split = &rules->buf[token->key.value_off]; len = strlen(val); - while (1) { + for (;;) { const char *next; next = strchr(split, '|'); @@ -2030,14 +2077,14 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event /* loop through token list, match, run actions or forward to next rule */ cur = &rules->tokens[0]; rule = cur; - while (1) { + for (;;) { dump_token(rules, cur); switch (cur->type) { case TK_RULE: /* current rule */ rule = cur; /* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */ - if (!can_set_name && rule->rule.flags) + if (!can_set_name && rule->rule.can_set_name) goto nomatch; esc = ESCAPE_UNSET; break; @@ -2057,14 +2104,14 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event { size_t devlen = strlen(udev_get_dev_path(event->udev))+1; struct udev_list_entry *list_entry; - int match = 0; + bool match = false; udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) { const char *devlink; devlink = &udev_list_entry_get_name(list_entry)[devlen]; if (match_key(rules, cur, devlink) == 0) { - match = 1; + match = true; break; } } @@ -2090,6 +2137,21 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event goto nomatch; break; } + case TK_M_TAG: + { + struct udev_list_entry *list_entry; + bool match = false; + + udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(event->dev)) { + if (strcmp(&rules->buf[cur->key.value_off], udev_list_entry_get_name(list_entry)) == 0) { + match = true; + break; + } + } + if (!match && (cur->key.op != OP_NOMATCH)) + goto nomatch; + break; + } case TK_M_SUBSYSTEM: if (match_key(rules, cur, udev_device_get_subsystem(event->dev)) != 0) goto nomatch; @@ -2127,7 +2189,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event /* loop over parents */ event->dev_parent = event->dev; - while (1) { + for (;;) { struct token *key; dbg(event->udev, "parent: '%s'\n", udev_device_get_syspath(event->dev_parent)); @@ -2214,7 +2276,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event program, &rules->buf[rule->rule.filename_off], rule->rule.filename_line); - if (util_run_program(event->udev, program, envp, result, sizeof(result), NULL, NULL) != 0) { + if (util_run_program(event->udev, program, envp, result, sizeof(result), NULL, NULL, false) != 0) { if (cur->key.op != OP_NOMATCH) goto nomatch; } else { @@ -2274,6 +2336,49 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event } break; } + case TK_M_IMPORT_CMDLINE: + { + FILE *f; + bool imported = false; + + f = fopen("/proc/cmdline", "r"); + if (f != NULL) { + char cmdline[4096]; + + if (fgets(cmdline, sizeof(cmdline), f) != NULL) { + const char *key = &rules->buf[cur->key.value_off]; + char *pos; + + pos = strstr(cmdline, key); + if (pos != NULL) { + struct udev_list_entry *entry; + + pos += strlen(key); + if (pos[0] == '\0' || isspace(pos[0])) { + /* we import simple flags as 'FLAG=1' */ + entry = udev_device_add_property(event->dev, key, "1"); + udev_list_entry_set_flags(entry, 1); + imported = true; + } else if (pos[0] == '=') { + const char *value; + + pos++; + value = pos; + while (pos[0] != '\0' && !isspace(pos[0])) + pos++; + pos[0] = '\0'; + entry = udev_device_add_property(event->dev, key, value); + udev_list_entry_set_flags(entry, 1); + imported = true; + } + } + } + fclose(f); + } + if (!imported && cur->key.op != OP_NOMATCH) + goto nomatch; + break; + } case TK_M_IMPORT_PARENT: { char import[UTIL_PATH_SIZE]; @@ -2295,6 +2400,10 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event esc = ESCAPE_REPLACE; break; case TK_A_INOTIFY_WATCH: + if (event->inotify_watch_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->inotify_watch_final = true; event->inotify_watch = cur->key.watch; break; case TK_A_DEVLINK_PRIO: @@ -2307,7 +2416,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event if (event->owner_final) break; if (cur->key.op == OP_ASSIGN_FINAL) - event->owner_final = 1; + event->owner_final = true; udev_event_apply_format(event, &rules->buf[cur->key.value_off], owner, sizeof(owner)); event->uid = util_lookup_user(event->udev, owner); info(event->udev, "OWNER %u %s:%u\n", @@ -2323,7 +2432,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event if (event->group_final) break; if (cur->key.op == OP_ASSIGN_FINAL) - event->group_final = 1; + event->group_final = true; udev_event_apply_format(event, &rules->buf[cur->key.value_off], group, sizeof(group)); event->gid = util_lookup_group(event->udev, group); info(event->udev, "GROUP %u %s:%u\n", @@ -2340,12 +2449,12 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event if (event->mode_final) break; if (cur->key.op == OP_ASSIGN_FINAL) - event->mode_final = 1; + event->mode_final = true; udev_event_apply_format(event, &rules->buf[cur->key.value_off], mode, sizeof(mode)); event->mode = strtol(mode, &endptr, 8); if (endptr[0] != '\0') { - err(event->udev, "invalide mode '%s' set default mode 0660\n", mode); - event->mode = 0660; + err(event->udev, "invalide mode '%s' set default mode 0600\n", mode); + event->mode = 0600; } info(event->udev, "MODE %#o %s:%u\n", event->mode, @@ -2357,7 +2466,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event if (event->owner_final) break; if (cur->key.op == OP_ASSIGN_FINAL) - event->owner_final = 1; + event->owner_final = true; event->uid = cur->key.uid; info(event->udev, "OWNER %u %s:%u\n", event->uid, @@ -2368,7 +2477,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event if (event->group_final) break; if (cur->key.op == OP_ASSIGN_FINAL) - event->group_final = 1; + event->group_final = true; event->gid = cur->key.gid; info(event->udev, "GROUP %u %s:%u\n", event->gid, @@ -2379,13 +2488,15 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event if (event->mode_final) break; if (cur->key.op == OP_ASSIGN_FINAL) - event->mode_final = 1; + event->mode_final = true; event->mode = cur->key.mode; info(event->udev, "MODE %#o %s:%u\n", event->mode, &rules->buf[rule->rule.filename_off], rule->rule.filename_line); break; + case TK_A_STATIC_NODE: + break; case TK_A_ENV: { const char *name = &rules->buf[cur->key.attr_off]; @@ -2405,6 +2516,11 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event } break; } + case TK_A_TAG: + if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) + udev_device_cleanup_tags_list(event->dev); + udev_device_add_tag(event->dev, &rules->buf[cur->key.value_off]); + break; case TK_A_NAME: { const char *name = &rules->buf[cur->key.value_off]; @@ -2414,7 +2530,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event if (event->name_final) break; if (cur->key.op == OP_ASSIGN_FINAL) - event->name_final = 1; + event->name_final = true; udev_event_apply_format(event, name, name_str, sizeof(name_str)); if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { count = udev_util_replace_chars(name_str, "/"); @@ -2441,7 +2557,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event if (major(udev_device_get_devnum(event->dev)) == 0) break; if (cur->key.op == OP_ASSIGN_FINAL) - event->devlink_final = 1; + event->devlink_final = true; if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) udev_device_cleanup_devlinks_list(event->dev); @@ -2546,3 +2662,75 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event (unsigned int) (cur - rules->tokens)); } } + +void udev_rules_apply_static_dev_perms(struct udev_rules *rules) +{ + struct token *cur; + struct token *rule; + uid_t uid = 0; + gid_t gid = 0; + mode_t mode = 0; + + if (rules->tokens == NULL) + return; + + cur = &rules->tokens[0]; + rule = cur; + for (;;) { + switch (cur->type) { + case TK_RULE: + /* current rule */ + rule = cur; + + /* skip rules without a static_node tag */ + if (!rule->rule.has_static_node) + goto next; + + uid = 0; + gid = 0; + mode = 0; + break; + case TK_A_OWNER_ID: + uid = cur->key.uid; + break; + case TK_A_GROUP_ID: + gid = cur->key.gid; + break; + case TK_A_MODE_ID: + mode = cur->key.mode; + break; + case TK_A_STATIC_NODE: { + char filename[UTIL_PATH_SIZE]; + struct stat stats; + /* we assure, that the permissions tokens are sorted before the static token */ + if (mode == 0 && uid == 0 && gid == 0) + goto next; + util_strscpyl(filename, sizeof(filename), udev_get_dev_path(rules->udev), "/", + &rules->buf[cur->key.value_off], NULL); + if (stat(filename, &stats) != 0) + goto next; + if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) + goto next; + if (mode != 0 && mode != (stats.st_mode & 0777)) { + chmod(filename, mode); + info(rules->udev, "chmod '%s' %#o\n", filename, mode); + } + if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) { + chown(filename, uid, gid); + info(rules->udev, "chown '%s' %u %u\n", filename, uid, gid); + } + utimensat(AT_FDCWD, filename, NULL, 0); + break; + } + case TK_END: + return; + } + + cur++; + continue; +next: + /* fast-forward to next rule */ + cur = rule + rule->rule.token_count; + continue; + } +}