]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udev-rules.c
environment-d-generator: do not include /lib/environment.d in the list
[thirdparty/systemd.git] / src / udev / udev-rules.c
CommitLineData
e7145211 1/* SPDX-License-Identifier: GPL-2.0+ */
2232cac8 2
2232cac8 3#include <ctype.h>
4f5dd394
LP
4#include <errno.h>
5#include <fcntl.h>
cea61f5c 6#include <fnmatch.h>
4f5dd394
LP
7#include <limits.h>
8#include <stdbool.h>
9#include <stddef.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
959e8b5d 13#include <time.h>
4f5dd394 14#include <unistd.h>
2232cac8 15
b5efdb8a 16#include "alloc-util.h"
2c21044f 17#include "conf-files.h"
29b5eb5a 18#include "device-private.h"
480ecb7d 19#include "device-util.h"
8fb3f009 20#include "dirent-util.h"
4f5dd394 21#include "escape.h"
3ffd4af2 22#include "fd-util.h"
fae0f8a0 23#include "fileio.h"
fdd21be6 24#include "fs-util.h"
7d50b32a 25#include "glob-util.h"
5ea78a39
YW
26#include "libudev-util.h"
27#include "mkdir.h"
b4ba2fe3 28#include "parse-util.h"
4f5dd394 29#include "path-util.h"
88b013b2 30#include "proc-cmdline.h"
8fcde012 31#include "stat-util.h"
f4850a1d 32#include "stdio-util.h"
915bf0f6 33#include "strbuf.h"
07630cea 34#include "string-util.h"
84b6ad70 35#include "strv.h"
5ea78a39 36#include "strxcpyx.h"
f4cf2e5b 37#include "sysctl-util.h"
07a26e42 38#include "udev-builtin.h"
4f5dd394 39#include "udev.h"
b1d4f8e1 40#include "user-util.h"
07630cea 41#include "util.h"
2232cac8 42
a1525d17 43#define PREALLOC_TOKEN 2048
c7521974 44
4052400f 45struct uid_gid {
14cb109d 46 unsigned name_off;
912541b0 47 union {
a1525d17
KS
48 uid_t uid;
49 gid_t gid;
912541b0 50 };
4052400f
KS
51};
52
2ad8416d
ZJS
53static const char* const rules_dirs[] = {
54 "/etc/udev/rules.d",
55 "/run/udev/rules.d",
56 UDEVLIBEXECDIR "/rules.d",
4f5dd394
LP
57 NULL
58};
2ad8416d 59
9a07157d 60struct UdevRules {
97f2d76d 61 usec_t dirs_ts_usec;
c4d44cba 62 ResolveNameTiming resolve_name_timing;
912541b0
KS
63
64 /* every key in the rules file becomes a token */
65 struct token *tokens;
14cb109d
YW
66 unsigned token_cur;
67 unsigned token_max;
912541b0 68
ab06eef8 69 /* all key strings are copied and de-duplicated in a single continuous string buffer */
915bf0f6 70 struct strbuf *strbuf;
912541b0
KS
71
72 /* during rule parsing, uid/gid lookup results are cached */
73 struct uid_gid *uids;
14cb109d
YW
74 unsigned uids_cur;
75 unsigned uids_max;
912541b0 76 struct uid_gid *gids;
14cb109d
YW
77 unsigned gids_cur;
78 unsigned gids_max;
4052400f
KS
79};
80
9a07157d 81static char *rules_str(UdevRules *rules, unsigned off) {
915bf0f6
KS
82 return rules->strbuf->buf + off;
83}
84
9a07157d 85static unsigned rules_add_string(UdevRules *rules, const char *s) {
915bf0f6
KS
86 return strbuf_add_string(rules->strbuf, s, strlen(s));
87}
88
8e3ba377 89/* KEY=="", KEY!="", KEY+="", KEY-="", KEY="", KEY:="" */
b0f7409f 90enum operation_type {
912541b0 91 OP_UNSET,
b0f7409f 92
912541b0
KS
93 OP_MATCH,
94 OP_NOMATCH,
95 OP_MATCH_MAX,
b0f7409f 96
912541b0 97 OP_ADD,
8e3ba377 98 OP_REMOVE,
912541b0
KS
99 OP_ASSIGN,
100 OP_ASSIGN_FINAL,
c7521974
KS
101};
102
ac218d9c 103enum string_glob_type {
912541b0 104 GL_UNSET,
a1525d17 105 GL_PLAIN, /* no special chars */
912541b0 106 GL_GLOB, /* shell globs ?,*,[] */
a1525d17
KS
107 GL_SPLIT, /* multi-value A|B */
108 GL_SPLIT_GLOB, /* multi-value with glob A*|B* */
109 GL_SOMETHING, /* commonly used "?*" */
ac218d9c
KS
110};
111
32028733 112enum string_subst_type {
912541b0
KS
113 SB_UNSET,
114 SB_NONE,
115 SB_FORMAT,
116 SB_SUBSYS,
32028733
KS
117};
118
154a7b84 119/* tokens of a rule are sorted/handled in this order */
6880b25d 120enum token_type {
912541b0
KS
121 TK_UNSET,
122 TK_RULE,
123
a1525d17
KS
124 TK_M_ACTION, /* val */
125 TK_M_DEVPATH, /* val */
126 TK_M_KERNEL, /* val */
127 TK_M_DEVLINK, /* val */
128 TK_M_NAME, /* val */
129 TK_M_ENV, /* val, attr */
130 TK_M_TAG, /* val */
131 TK_M_SUBSYSTEM, /* val */
132 TK_M_DRIVER, /* val */
133 TK_M_WAITFOR, /* val */
134 TK_M_ATTR, /* val, attr */
f4cf2e5b 135 TK_M_SYSCTL, /* val, attr */
912541b0
KS
136
137 TK_M_PARENTS_MIN,
a1525d17 138 TK_M_KERNELS, /* val */
912541b0 139 TK_M_SUBSYSTEMS, /* val */
a1525d17
KS
140 TK_M_DRIVERS, /* val */
141 TK_M_ATTRS, /* val, attr */
142 TK_M_TAGS, /* val */
912541b0
KS
143 TK_M_PARENTS_MAX,
144
a1525d17 145 TK_M_TEST, /* val, mode_t */
a1525d17
KS
146 TK_M_PROGRAM, /* val */
147 TK_M_IMPORT_FILE, /* val */
148 TK_M_IMPORT_PROG, /* val */
149 TK_M_IMPORT_BUILTIN, /* val */
150 TK_M_IMPORT_DB, /* val */
151 TK_M_IMPORT_CMDLINE, /* val */
152 TK_M_IMPORT_PARENT, /* val */
153 TK_M_RESULT, /* val */
912541b0
KS
154 TK_M_MAX,
155
156 TK_A_STRING_ESCAPE_NONE,
157 TK_A_STRING_ESCAPE_REPLACE,
158 TK_A_DB_PERSIST,
a1525d17
KS
159 TK_A_INOTIFY_WATCH, /* int */
160 TK_A_DEVLINK_PRIO, /* int */
161 TK_A_OWNER, /* val */
162 TK_A_GROUP, /* val */
163 TK_A_MODE, /* val */
164 TK_A_OWNER_ID, /* uid_t */
165 TK_A_GROUP_ID, /* gid_t */
166 TK_A_MODE_ID, /* mode_t */
84b6ad70 167 TK_A_TAG, /* val */
a1525d17 168 TK_A_STATIC_NODE, /* val */
c26547d6 169 TK_A_SECLABEL, /* val, attr */
a1525d17 170 TK_A_ENV, /* val, attr */
a1525d17
KS
171 TK_A_NAME, /* val */
172 TK_A_DEVLINK, /* val */
173 TK_A_ATTR, /* val, attr */
f4cf2e5b 174 TK_A_SYSCTL, /* val, attr */
83cd6b75
KS
175 TK_A_RUN_BUILTIN, /* val, bool */
176 TK_A_RUN_PROGRAM, /* val, bool */
a1525d17 177 TK_A_GOTO, /* size_t */
912541b0
KS
178
179 TK_END,
c7521974
KS
180};
181
3f3aa9f5 182/* we try to pack stuff in a way that we take only 12 bytes per token */
4052400f 183struct token {
912541b0
KS
184 union {
185 unsigned char type; /* same in rule and key */
186 struct {
187 enum token_type type:8;
188 bool can_set_name:1;
189 bool has_static_node:1;
14cb109d 190 unsigned unused:6;
912541b0 191 unsigned short token_count;
14cb109d 192 unsigned label_off;
912541b0
KS
193 unsigned short filename_off;
194 unsigned short filename_line;
195 } rule;
196 struct {
197 enum token_type type:8;
198 enum operation_type op:8;
199 enum string_glob_type glob:8;
200 enum string_subst_type subst:4;
201 enum string_subst_type attrsubst:4;
14cb109d 202 unsigned value_off;
912541b0 203 union {
14cb109d
YW
204 unsigned attr_off;
205 unsigned rule_goto;
49c603bd 206 mode_t mode;
912541b0
KS
207 uid_t uid;
208 gid_t gid;
209 int devlink_prio;
912541b0
KS
210 int watch;
211 enum udev_builtin_cmd builtin_cmd;
212 };
213 } key;
214 };
4052400f
KS
215};
216
912541b0 217#define MAX_TK 64
4052400f 218struct rule_tmp {
9a07157d 219 UdevRules *rules;
912541b0
KS
220 struct token rule;
221 struct token token[MAX_TK];
14cb109d 222 unsigned token_cur;
4052400f
KS
223};
224
20e97dd3 225#if ENABLE_DEBUG_UDEV
9ec6e95b 226static const char *operation_str(enum operation_type type) {
912541b0 227 static const char *operation_strs[] = {
a1525d17
KS
228 [OP_UNSET] = "UNSET",
229 [OP_MATCH] = "match",
230 [OP_NOMATCH] = "nomatch",
912541b0
KS
231 [OP_MATCH_MAX] = "MATCH_MAX",
232
a1525d17 233 [OP_ADD] = "add",
8e3ba377 234 [OP_REMOVE] = "remove",
a1525d17
KS
235 [OP_ASSIGN] = "assign",
236 [OP_ASSIGN_FINAL] = "assign-final",
49c603bd 237 };
912541b0
KS
238
239 return operation_strs[type];
5d6a1fa6 240}
4052400f 241
9ec6e95b 242static const char *string_glob_str(enum string_glob_type type) {
912541b0 243 static const char *string_glob_strs[] = {
a1525d17
KS
244 [GL_UNSET] = "UNSET",
245 [GL_PLAIN] = "plain",
246 [GL_GLOB] = "glob",
247 [GL_SPLIT] = "split",
248 [GL_SPLIT_GLOB] = "split-glob",
249 [GL_SOMETHING] = "split-glob",
912541b0
KS
250 };
251
252 return string_glob_strs[type];
5d6a1fa6
KS
253}
254
9ec6e95b 255static const char *token_str(enum token_type type) {
912541b0 256 static const char *token_strs[] = {
a1525d17
KS
257 [TK_UNSET] = "UNSET",
258 [TK_RULE] = "RULE",
912541b0 259
a1525d17 260 [TK_M_ACTION] = "M ACTION",
912541b0 261 [TK_M_DEVPATH] = "M DEVPATH",
a1525d17 262 [TK_M_KERNEL] = "M KERNEL",
912541b0 263 [TK_M_DEVLINK] = "M DEVLINK",
a1525d17
KS
264 [TK_M_NAME] = "M NAME",
265 [TK_M_ENV] = "M ENV",
266 [TK_M_TAG] = "M TAG",
267 [TK_M_SUBSYSTEM] = "M SUBSYSTEM",
268 [TK_M_DRIVER] = "M DRIVER",
912541b0 269 [TK_M_WAITFOR] = "M WAITFOR",
a1525d17 270 [TK_M_ATTR] = "M ATTR",
f4cf2e5b 271 [TK_M_SYSCTL] = "M SYSCTL",
912541b0 272
a1525d17 273 [TK_M_PARENTS_MIN] = "M PARENTS_MIN",
912541b0 274 [TK_M_KERNELS] = "M KERNELS",
a1525d17 275 [TK_M_SUBSYSTEMS] = "M SUBSYSTEMS",
912541b0 276 [TK_M_DRIVERS] = "M DRIVERS",
a1525d17
KS
277 [TK_M_ATTRS] = "M ATTRS",
278 [TK_M_TAGS] = "M TAGS",
279 [TK_M_PARENTS_MAX] = "M PARENTS_MAX",
912541b0 280
a1525d17 281 [TK_M_TEST] = "M TEST",
912541b0 282 [TK_M_PROGRAM] = "M PROGRAM",
a1525d17
KS
283 [TK_M_IMPORT_FILE] = "M IMPORT_FILE",
284 [TK_M_IMPORT_PROG] = "M IMPORT_PROG",
285 [TK_M_IMPORT_BUILTIN] = "M IMPORT_BUILTIN",
286 [TK_M_IMPORT_DB] = "M IMPORT_DB",
287 [TK_M_IMPORT_CMDLINE] = "M IMPORT_CMDLINE",
288 [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT",
289 [TK_M_RESULT] = "M RESULT",
290 [TK_M_MAX] = "M MAX",
291
292 [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE",
293 [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE",
294 [TK_A_DB_PERSIST] = "A DB_PERSIST",
295 [TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH",
296 [TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO",
297 [TK_A_OWNER] = "A OWNER",
298 [TK_A_GROUP] = "A GROUP",
299 [TK_A_MODE] = "A MODE",
300 [TK_A_OWNER_ID] = "A OWNER_ID",
301 [TK_A_GROUP_ID] = "A GROUP_ID",
302 [TK_A_STATIC_NODE] = "A STATIC_NODE",
c26547d6 303 [TK_A_SECLABEL] = "A SECLABEL",
912541b0 304 [TK_A_MODE_ID] = "A MODE_ID",
a1525d17
KS
305 [TK_A_ENV] = "A ENV",
306 [TK_A_TAG] = "A ENV",
307 [TK_A_NAME] = "A NAME",
912541b0 308 [TK_A_DEVLINK] = "A DEVLINK",
a1525d17 309 [TK_A_ATTR] = "A ATTR",
f4cf2e5b 310 [TK_A_SYSCTL] = "A SYSCTL",
83cd6b75
KS
311 [TK_A_RUN_BUILTIN] = "A RUN_BUILTIN",
312 [TK_A_RUN_PROGRAM] = "A RUN_PROGRAM",
a1525d17 313 [TK_A_GOTO] = "A GOTO",
912541b0 314
a1525d17 315 [TK_END] = "END",
912541b0
KS
316 };
317
318 return token_strs[type];
5d6a1fa6 319}
c7521974 320
9a07157d 321static void dump_token(UdevRules *rules, struct token *token) {
912541b0
KS
322 enum token_type type = token->type;
323 enum operation_type op = token->key.op;
324 enum string_glob_type glob = token->key.glob;
fa394301
LR
325 const char *value = rules_str(rules, token->key.value_off);
326 const char *attr = &rules->strbuf->buf[token->key.attr_off];
912541b0
KS
327
328 switch (type) {
329 case TK_RULE:
330 {
331 const char *tks_ptr = (char *)rules->tokens;
332 const char *tk_ptr = (char *)token;
14cb109d 333 unsigned idx = (tk_ptr - tks_ptr) / sizeof(struct token);
912541b0 334
9f6445e3 335 log_debug("* RULE %s:%u, token: %u, count: %u, label: '%s'",
fa394301 336 &rules->strbuf->buf[token->rule.filename_off], token->rule.filename_line,
baa30fbc 337 idx, token->rule.token_count,
fa394301 338 &rules->strbuf->buf[token->rule.label_off]);
912541b0
KS
339 break;
340 }
341 case TK_M_ACTION:
342 case TK_M_DEVPATH:
343 case TK_M_KERNEL:
344 case TK_M_SUBSYSTEM:
345 case TK_M_DRIVER:
346 case TK_M_WAITFOR:
347 case TK_M_DEVLINK:
348 case TK_M_NAME:
349 case TK_M_KERNELS:
350 case TK_M_SUBSYSTEMS:
351 case TK_M_DRIVERS:
352 case TK_M_TAGS:
353 case TK_M_PROGRAM:
354 case TK_M_IMPORT_FILE:
355 case TK_M_IMPORT_PROG:
356 case TK_M_IMPORT_DB:
357 case TK_M_IMPORT_CMDLINE:
358 case TK_M_IMPORT_PARENT:
359 case TK_M_RESULT:
360 case TK_A_NAME:
361 case TK_A_DEVLINK:
362 case TK_A_OWNER:
363 case TK_A_GROUP:
364 case TK_A_MODE:
83cd6b75
KS
365 case TK_A_RUN_BUILTIN:
366 case TK_A_RUN_PROGRAM:
9f6445e3 367 log_debug("%s %s '%s'(%s)",
baa30fbc 368 token_str(type), operation_str(op), value, string_glob_str(glob));
912541b0
KS
369 break;
370 case TK_M_IMPORT_BUILTIN:
9f6445e3 371 log_debug("%s %i '%s'", token_str(type), token->key.builtin_cmd, value);
912541b0
KS
372 break;
373 case TK_M_ATTR:
f4cf2e5b 374 case TK_M_SYSCTL:
912541b0
KS
375 case TK_M_ATTRS:
376 case TK_M_ENV:
377 case TK_A_ATTR:
f4cf2e5b 378 case TK_A_SYSCTL:
912541b0 379 case TK_A_ENV:
9f6445e3 380 log_debug("%s %s '%s' '%s'(%s)",
baa30fbc 381 token_str(type), operation_str(op), attr, value, string_glob_str(glob));
912541b0
KS
382 break;
383 case TK_M_TAG:
384 case TK_A_TAG:
9f6445e3 385 log_debug("%s %s '%s'", token_str(type), operation_str(op), value);
912541b0
KS
386 break;
387 case TK_A_STRING_ESCAPE_NONE:
388 case TK_A_STRING_ESCAPE_REPLACE:
389 case TK_A_DB_PERSIST:
9f6445e3 390 log_debug("%s", token_str(type));
912541b0
KS
391 break;
392 case TK_M_TEST:
9f6445e3 393 log_debug("%s %s '%s'(%s) %#o",
baa30fbc 394 token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode);
912541b0
KS
395 break;
396 case TK_A_INOTIFY_WATCH:
9f6445e3 397 log_debug("%s %u", token_str(type), token->key.watch);
912541b0
KS
398 break;
399 case TK_A_DEVLINK_PRIO:
9f6445e3 400 log_debug("%s %u", token_str(type), token->key.devlink_prio);
912541b0
KS
401 break;
402 case TK_A_OWNER_ID:
9f6445e3 403 log_debug("%s %s %u", token_str(type), operation_str(op), token->key.uid);
912541b0
KS
404 break;
405 case TK_A_GROUP_ID:
9f6445e3 406 log_debug("%s %s %u", token_str(type), operation_str(op), token->key.gid);
912541b0
KS
407 break;
408 case TK_A_MODE_ID:
9f6445e3 409 log_debug("%s %s %#o", token_str(type), operation_str(op), token->key.mode);
912541b0
KS
410 break;
411 case TK_A_STATIC_NODE:
9f6445e3 412 log_debug("%s '%s'", token_str(type), value);
912541b0 413 break;
c26547d6 414 case TK_A_SECLABEL:
9f6445e3 415 log_debug("%s %s '%s' '%s'", token_str(type), operation_str(op), attr, value);
c26547d6 416 break;
912541b0 417 case TK_A_GOTO:
9f6445e3 418 log_debug("%s '%s' %u", token_str(type), value, token->key.rule_goto);
912541b0
KS
419 break;
420 case TK_END:
9f6445e3 421 log_debug("* %s", token_str(type));
912541b0
KS
422 break;
423 case TK_M_PARENTS_MIN:
424 case TK_M_PARENTS_MAX:
425 case TK_M_MAX:
426 case TK_UNSET:
8c19dc54 427 log_debug("Unknown token type %u", type);
912541b0
KS
428 break;
429 }
4052400f 430}
154a7b84 431
9a07157d 432static void dump_rules(UdevRules *rules) {
14cb109d 433 unsigned i;
912541b0 434
8c19dc54 435 log_debug("Dumping %u (%zu bytes) tokens, %zu (%zu bytes) strings",
baa30fbc
KS
436 rules->token_cur,
437 rules->token_cur * sizeof(struct token),
fa394301
LR
438 rules->strbuf->nodes_count,
439 rules->strbuf->len);
f168c273 440 for (i = 0; i < rules->token_cur; i++)
912541b0 441 dump_token(rules, &rules->tokens[i]);
4052400f
KS
442}
443#else
a1e92eee
TM
444static void dump_token(UdevRules *rules, struct token *token) {}
445static void dump_rules(UdevRules *rules) {}
20e97dd3 446#endif /* ENABLE_DEBUG_UDEV */
c7521974 447
9a07157d 448static int add_token(UdevRules *rules, struct token *token) {
912541b0
KS
449 /* grow buffer if needed */
450 if (rules->token_cur+1 >= rules->token_max) {
451 struct token *tokens;
14cb109d 452 unsigned add;
912541b0
KS
453
454 /* double the buffer size */
455 add = rules->token_max;
456 if (add < 8)
457 add = 8;
458
62d74c78 459 tokens = reallocarray(rules->tokens, rules->token_max + add, sizeof(struct token));
67e4b385 460 if (!tokens)
ef660d07 461 return -ENOMEM;
912541b0
KS
462 rules->tokens = tokens;
463 rules->token_max += add;
464 }
465 memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token));
466 rules->token_cur++;
467 return 0;
c7521974 468}
a27cd06c 469
b4ba2fe3 470static void log_unknown_owner(sd_device *dev, int error, const char *entity, const char *owner) {
2da03cbf 471 if (IN_SET(abs(error), ENOENT, ESRCH))
b4ba2fe3 472 log_device_error(dev, "Specified %s '%s' unknown", entity, owner);
2da03cbf 473 else
b4ba2fe3 474 log_device_error_errno(dev, error, "Failed to resolve %s '%s': %m", entity, owner);
2da03cbf
ZJS
475}
476
9a07157d 477static uid_t add_uid(UdevRules *rules, const char *owner) {
14cb109d 478 unsigned i;
23bf8dd7 479 uid_t uid = 0;
14cb109d 480 unsigned off;
23bf8dd7 481 int r;
912541b0
KS
482
483 /* lookup, if we know it already */
484 for (i = 0; i < rules->uids_cur; i++) {
485 off = rules->uids[i].name_off;
1f362ff1
YW
486 if (streq(rules_str(rules, off), owner))
487 return rules->uids[i].uid;
912541b0 488 }
fafff8f1 489 r = get_user_creds(&owner, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
2da03cbf 490 if (r < 0)
b4ba2fe3 491 log_unknown_owner(NULL, r, "user", owner);
912541b0
KS
492
493 /* grow buffer if needed */
494 if (rules->uids_cur+1 >= rules->uids_max) {
495 struct uid_gid *uids;
14cb109d 496 unsigned add;
912541b0
KS
497
498 /* double the buffer size */
499 add = rules->uids_max;
500 if (add < 1)
501 add = 8;
502
62d74c78 503 uids = reallocarray(rules->uids, rules->uids_max + add, sizeof(struct uid_gid));
67e4b385 504 if (!uids)
912541b0 505 return uid;
912541b0
KS
506 rules->uids = uids;
507 rules->uids_max += add;
508 }
509 rules->uids[rules->uids_cur].uid = uid;
915bf0f6 510 off = rules_add_string(rules, owner);
912541b0
KS
511 if (off <= 0)
512 return uid;
513 rules->uids[rules->uids_cur].name_off = off;
514 rules->uids_cur++;
515 return uid;
154a7b84
KS
516}
517
9a07157d 518static gid_t add_gid(UdevRules *rules, const char *group) {
14cb109d 519 unsigned i;
23bf8dd7 520 gid_t gid = 0;
14cb109d 521 unsigned off;
23bf8dd7 522 int r;
912541b0
KS
523
524 /* lookup, if we know it already */
525 for (i = 0; i < rules->gids_cur; i++) {
526 off = rules->gids[i].name_off;
1f362ff1
YW
527 if (streq(rules_str(rules, off), group))
528 return rules->gids[i].gid;
912541b0 529 }
fafff8f1 530 r = get_group_creds(&group, &gid, USER_CREDS_ALLOW_MISSING);
2da03cbf 531 if (r < 0)
b4ba2fe3 532 log_unknown_owner(NULL, r, "group", group);
912541b0
KS
533
534 /* grow buffer if needed */
535 if (rules->gids_cur+1 >= rules->gids_max) {
536 struct uid_gid *gids;
14cb109d 537 unsigned add;
912541b0
KS
538
539 /* double the buffer size */
540 add = rules->gids_max;
541 if (add < 1)
542 add = 8;
543
62d74c78 544 gids = reallocarray(rules->gids, rules->gids_max + add, sizeof(struct uid_gid));
67e4b385 545 if (!gids)
912541b0 546 return gid;
912541b0
KS
547 rules->gids = gids;
548 rules->gids_max += add;
549 }
550 rules->gids[rules->gids_cur].gid = gid;
915bf0f6 551 off = rules_add_string(rules, group);
912541b0
KS
552 if (off <= 0)
553 return gid;
554 rules->gids[rules->gids_cur].name_off = off;
555 rules->gids_cur++;
556 return gid;
154a7b84
KS
557}
558
29b5eb5a 559static int import_property_from_string(sd_device *dev, char *line) {
912541b0
KS
560 char *key;
561 char *val;
562 size_t len;
563
564 /* find key */
565 key = line;
566 while (isspace(key[0]))
567 key++;
568
569 /* comment or empty line */
4c701096 570 if (IN_SET(key[0], '#', '\0'))
29b5eb5a 571 return 0;
912541b0
KS
572
573 /* split key/value */
574 val = strchr(key, '=');
29b5eb5a
YW
575 if (!val)
576 return -EINVAL;
912541b0
KS
577 val[0] = '\0';
578 val++;
579
580 /* find value */
581 while (isspace(val[0]))
582 val++;
583
584 /* terminate key */
585 len = strlen(key);
586 if (len == 0)
29b5eb5a 587 return -EINVAL;
912541b0
KS
588 while (isspace(key[len-1]))
589 len--;
590 key[len] = '\0';
591
592 /* terminate value */
593 len = strlen(val);
594 if (len == 0)
29b5eb5a 595 return -EINVAL;
912541b0
KS
596 while (isspace(val[len-1]))
597 len--;
598 val[len] = '\0';
599
600 if (len == 0)
29b5eb5a 601 return -EINVAL;
912541b0
KS
602
603 /* unquote */
4c701096 604 if (IN_SET(val[0], '"', '\'')) {
baaa35ad
ZJS
605 if (len == 1 || val[len-1] != val[0])
606 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
8c19dc54 607 "Inconsistent quoting: '%s', skip",
baaa35ad 608 line);
912541b0
KS
609 val[len-1] = '\0';
610 val++;
611 }
612
29b5eb5a 613 return device_add_property(dev, key, val);
be4bedd1
KS
614}
615
13c7b75f 616static int import_file_into_properties(sd_device *dev, const char *filename) {
fae0f8a0
LP
617 _cleanup_fclose_ FILE *f = NULL;
618 int r;
912541b0 619
47ef94ac 620 f = fopen(filename, "re");
fae0f8a0
LP
621 if (!f)
622 return -errno;
623
624 for (;;) {
625 _cleanup_free_ char *line = NULL;
626
627 r = read_line(f, LONG_LINE_MAX, &line);
628 if (r < 0)
629 return r;
630 if (r == 0)
631 break;
632
13c7b75f 633 (void) import_property_from_string(dev, line);
fae0f8a0
LP
634 }
635
912541b0 636 return 0;
bd0ed2ff
KS
637}
638
2e088715 639static int import_program_into_properties(UdevEvent *event,
dd5eddd2 640 usec_t timeout_usec,
8314de1d 641 const char *program) {
912541b0
KS
642 char result[UTIL_LINE_SIZE];
643 char *line;
a7521142 644 int r;
912541b0 645
a7521142
ZJS
646 r = udev_event_spawn(event, timeout_usec, false, program, result, sizeof result);
647 if (r < 0)
648 return r;
649 if (r > 0)
650 return -EIO;
912541b0
KS
651
652 line = result;
67e4b385 653 while (line) {
912541b0
KS
654 char *pos;
655
656 pos = strchr(line, '\n');
67e4b385 657 if (pos) {
912541b0
KS
658 pos[0] = '\0';
659 pos = &pos[1];
660 }
cf28ad46 661 (void) import_property_from_string(event->dev, line);
912541b0
KS
662 line = pos;
663 }
664 return 0;
319c6700
KS
665}
666
1ce7fecb
YW
667static int import_parent_into_properties(sd_device *dev, const char *filter) {
668 const char *key, *val;
669 sd_device *parent;
670 int r;
912541b0 671
3b64e4d4
TG
672 assert(dev);
673 assert(filter);
674
1ce7fecb
YW
675 r = sd_device_get_parent(dev, &parent);
676 if (r < 0)
677 return r;
912541b0 678
1ce7fecb 679 FOREACH_DEVICE_PROPERTY(parent, key, val)
ece174c5 680 if (fnmatch(filter, key, 0) == 0)
1ce7fecb 681 device_add_property(dev, key, val);
912541b0 682 return 0;
e2ecb34f
KS
683}
684
f4850a1d
ZJS
685static void attr_subst_subdir(char *attr, size_t len) {
686 const char *pos, *tail, *path;
687 _cleanup_closedir_ DIR *dir = NULL;
688 struct dirent *dent;
689
690 pos = strstr(attr, "/*/");
691 if (!pos)
692 return;
693
694 tail = pos + 2;
695 path = strndupa(attr, pos - attr + 1); /* include slash at end */
696 dir = opendir(path);
67e4b385 697 if (!dir)
f4850a1d
ZJS
698 return;
699
8fb3f009 700 FOREACH_DIRENT_ALL(dent, dir, break)
f4850a1d 701 if (dent->d_name[0] != '.') {
916a8d43 702 char n[strlen(dent->d_name) + strlen(tail) + 1];
f4850a1d 703
916a8d43
ZJS
704 strscpyl(n, sizeof n, dent->d_name, tail, NULL);
705 if (faccessat(dirfd(dir), n, F_OK, 0) == 0) {
f4850a1d
ZJS
706 strscpyl(attr, len, path, n, NULL);
707 break;
912541b0 708 }
912541b0 709 }
0ea5e96e
KS
710}
711
2024ed61 712static int get_key(char **line, char **key, enum operation_type *op, char **value) {
912541b0
KS
713 char *linepos;
714 char *temp;
7e760b79 715 unsigned i, j;
912541b0
KS
716
717 linepos = *line;
67e4b385 718 if (!linepos || linepos[0] == '\0')
704dbfb2 719 return -EINVAL;
912541b0
KS
720
721 /* skip whitespace */
722 while (isspace(linepos[0]) || linepos[0] == ',')
723 linepos++;
724
725 /* get the key */
726 if (linepos[0] == '\0')
704dbfb2 727 return -EINVAL;
912541b0
KS
728 *key = linepos;
729
730 for (;;) {
731 linepos++;
732 if (linepos[0] == '\0')
704dbfb2 733 return -EINVAL;
912541b0
KS
734 if (isspace(linepos[0]))
735 break;
736 if (linepos[0] == '=')
737 break;
4c701096 738 if (IN_SET(linepos[0], '+', '-', '!', ':'))
912541b0
KS
739 if (linepos[1] == '=')
740 break;
741 }
742
743 /* remember end of key */
744 temp = linepos;
745
746 /* skip whitespace after key */
747 while (isspace(linepos[0]))
748 linepos++;
749 if (linepos[0] == '\0')
704dbfb2 750 return -EINVAL;
912541b0
KS
751
752 /* get operation type */
753 if (linepos[0] == '=' && linepos[1] == '=') {
754 *op = OP_MATCH;
755 linepos += 2;
756 } else if (linepos[0] == '!' && linepos[1] == '=') {
757 *op = OP_NOMATCH;
758 linepos += 2;
759 } else if (linepos[0] == '+' && linepos[1] == '=') {
760 *op = OP_ADD;
761 linepos += 2;
8e3ba377
DH
762 } else if (linepos[0] == '-' && linepos[1] == '=') {
763 *op = OP_REMOVE;
764 linepos += 2;
912541b0
KS
765 } else if (linepos[0] == '=') {
766 *op = OP_ASSIGN;
767 linepos++;
768 } else if (linepos[0] == ':' && linepos[1] == '=') {
769 *op = OP_ASSIGN_FINAL;
770 linepos += 2;
771 } else
704dbfb2 772 return -EINVAL;
912541b0
KS
773
774 /* terminate key */
775 temp[0] = '\0';
776
777 /* skip whitespace after operator */
778 while (isspace(linepos[0]))
779 linepos++;
780 if (linepos[0] == '\0')
704dbfb2 781 return -EINVAL;
912541b0
KS
782
783 /* get the value */
784 if (linepos[0] == '"')
785 linepos++;
786 else
704dbfb2 787 return -EINVAL;
912541b0
KS
788 *value = linepos;
789
790 /* terminate */
7e760b79
FB
791 for (i = 0, j = 0; ; i++, j++) {
792
793 if (linepos[i] == '"')
794 break;
795
796 if (linepos[i] == '\0')
704dbfb2 797 return -EINVAL;
7e760b79
FB
798
799 /* double quotes can be escaped */
800 if (linepos[i] == '\\')
801 if (linepos[i+1] == '"')
802 i++;
803
804 linepos[j] = linepos[i];
805 }
806 linepos[j] = '\0';
912541b0
KS
807
808 /* move line to next key */
7e760b79 809 *line = linepos + i + 1;
912541b0 810 return 0;
6880b25d 811}
95776dc6 812
6880b25d 813/* extract possible KEY{attr} */
2024ed61 814static const char *get_key_attribute(char *str) {
912541b0
KS
815 char *pos;
816 char *attr;
817
818 attr = strchr(str, '{');
67e4b385 819 if (attr) {
912541b0
KS
820 attr++;
821 pos = strchr(attr, '}');
67e4b385 822 if (!pos) {
25f027c5 823 log_error("Missing closing brace for format");
912541b0
KS
824 return NULL;
825 }
826 pos[0] = '\0';
912541b0
KS
827 return attr;
828 }
829 return NULL;
6880b25d 830}
95776dc6 831
6e2efb6c
YW
832static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
833 enum operation_type op,
834 const char *value, const void *data) {
19a8e656 835 struct token *token = rule_tmp->token + rule_tmp->token_cur;
912541b0
KS
836 const char *attr = NULL;
837
6e2efb6c
YW
838 if (rule_tmp->token_cur >= ELEMENTSOF(rule_tmp->token))
839 return -E2BIG;
840
29804cc1 841 memzero(token, sizeof(struct token));
912541b0
KS
842
843 switch (type) {
844 case TK_M_ACTION:
845 case TK_M_DEVPATH:
846 case TK_M_KERNEL:
847 case TK_M_SUBSYSTEM:
848 case TK_M_DRIVER:
849 case TK_M_WAITFOR:
850 case TK_M_DEVLINK:
851 case TK_M_NAME:
852 case TK_M_KERNELS:
853 case TK_M_SUBSYSTEMS:
854 case TK_M_DRIVERS:
855 case TK_M_TAGS:
856 case TK_M_PROGRAM:
857 case TK_M_IMPORT_FILE:
858 case TK_M_IMPORT_PROG:
859 case TK_M_IMPORT_DB:
860 case TK_M_IMPORT_CMDLINE:
861 case TK_M_IMPORT_PARENT:
862 case TK_M_RESULT:
863 case TK_A_OWNER:
864 case TK_A_GROUP:
865 case TK_A_MODE:
8a173387 866 case TK_A_DEVLINK:
912541b0
KS
867 case TK_A_NAME:
868 case TK_A_GOTO:
869 case TK_M_TAG:
870 case TK_A_TAG:
d6f116a7 871 case TK_A_STATIC_NODE:
915bf0f6 872 token->key.value_off = rules_add_string(rule_tmp->rules, value);
912541b0
KS
873 break;
874 case TK_M_IMPORT_BUILTIN:
915bf0f6 875 token->key.value_off = rules_add_string(rule_tmp->rules, value);
912541b0
KS
876 token->key.builtin_cmd = *(enum udev_builtin_cmd *)data;
877 break;
878 case TK_M_ENV:
879 case TK_M_ATTR:
f4cf2e5b 880 case TK_M_SYSCTL:
912541b0
KS
881 case TK_M_ATTRS:
882 case TK_A_ATTR:
f4cf2e5b 883 case TK_A_SYSCTL:
912541b0 884 case TK_A_ENV:
c26547d6 885 case TK_A_SECLABEL:
912541b0 886 attr = data;
915bf0f6
KS
887 token->key.value_off = rules_add_string(rule_tmp->rules, value);
888 token->key.attr_off = rules_add_string(rule_tmp->rules, attr);
912541b0 889 break;
912541b0 890 case TK_M_TEST:
915bf0f6 891 token->key.value_off = rules_add_string(rule_tmp->rules, value);
67e4b385 892 if (data)
912541b0
KS
893 token->key.mode = *(mode_t *)data;
894 break;
895 case TK_A_STRING_ESCAPE_NONE:
896 case TK_A_STRING_ESCAPE_REPLACE:
897 case TK_A_DB_PERSIST:
898 break;
83cd6b75 899 case TK_A_RUN_BUILTIN:
83cd6b75 900 case TK_A_RUN_PROGRAM:
4590cfe4 901 token->key.builtin_cmd = *(enum udev_builtin_cmd *)data;
915bf0f6 902 token->key.value_off = rules_add_string(rule_tmp->rules, value);
912541b0
KS
903 break;
904 case TK_A_INOTIFY_WATCH:
905 case TK_A_DEVLINK_PRIO:
906 token->key.devlink_prio = *(int *)data;
907 break;
908 case TK_A_OWNER_ID:
909 token->key.uid = *(uid_t *)data;
910 break;
911 case TK_A_GROUP_ID:
912 token->key.gid = *(gid_t *)data;
913 break;
914 case TK_A_MODE_ID:
915 token->key.mode = *(mode_t *)data;
916 break;
912541b0
KS
917 case TK_RULE:
918 case TK_M_PARENTS_MIN:
919 case TK_M_PARENTS_MAX:
920 case TK_M_MAX:
921 case TK_END:
922 case TK_UNSET:
19a8e656 923 assert_not_reached("wrong type");
912541b0
KS
924 }
925
67e4b385 926 if (value && type < TK_M_MAX) {
912541b0
KS
927 /* check if we need to split or call fnmatch() while matching rules */
928 enum string_glob_type glob;
67e4b385 929 bool has_split, has_glob;
912541b0 930
67e4b385 931 has_split = strchr(value, '|');
e3e0314b 932 has_glob = string_is_glob(value);
67e4b385 933 if (has_split && has_glob)
912541b0 934 glob = GL_SPLIT_GLOB;
67e4b385 935 else if (has_split)
912541b0 936 glob = GL_SPLIT;
67e4b385 937 else if (has_glob) {
33502ffe 938 if (streq(value, "?*"))
912541b0
KS
939 glob = GL_SOMETHING;
940 else
941 glob = GL_GLOB;
67e4b385 942 } else
912541b0 943 glob = GL_PLAIN;
67e4b385 944
912541b0
KS
945 token->key.glob = glob;
946 }
947
67e4b385 948 if (value && type > TK_M_MAX) {
912541b0
KS
949 /* check if assigned value has substitution chars */
950 if (value[0] == '[')
951 token->key.subst = SB_SUBSYS;
67e4b385 952 else if (strchr(value, '%') || strchr(value, '$'))
912541b0
KS
953 token->key.subst = SB_FORMAT;
954 else
955 token->key.subst = SB_NONE;
956 }
957
67e4b385 958 if (attr) {
d6f116a7 959 /* check if property/attribute name has substitution chars */
912541b0
KS
960 if (attr[0] == '[')
961 token->key.attrsubst = SB_SUBSYS;
67e4b385 962 else if (strchr(attr, '%') || strchr(attr, '$'))
912541b0
KS
963 token->key.attrsubst = SB_FORMAT;
964 else
965 token->key.attrsubst = SB_NONE;
966 }
967
968 token->key.type = type;
969 token->key.op = op;
970 rule_tmp->token_cur++;
6e2efb6c
YW
971
972 return 0;
6880b25d 973}
e57e7bc1 974
9a07157d 975static int sort_token(UdevRules *rules, struct rule_tmp *rule_tmp) {
14cb109d
YW
976 unsigned i;
977 unsigned start = 0;
978 unsigned end = rule_tmp->token_cur;
ef660d07 979 int r;
912541b0
KS
980
981 for (i = 0; i < rule_tmp->token_cur; i++) {
982 enum token_type next_val = TK_UNSET;
14cb109d
YW
983 unsigned next_idx = 0;
984 unsigned j;
912541b0
KS
985
986 /* find smallest value */
987 for (j = start; j < end; j++) {
988 if (rule_tmp->token[j].type == TK_UNSET)
989 continue;
990 if (next_val == TK_UNSET || rule_tmp->token[j].type < next_val) {
991 next_val = rule_tmp->token[j].type;
992 next_idx = j;
993 }
994 }
995
996 /* add token and mark done */
ef660d07
YW
997 r = add_token(rules, &rule_tmp->token[next_idx]);
998 if (r < 0)
999 return r;
912541b0
KS
1000 rule_tmp->token[next_idx].type = TK_UNSET;
1001
1002 /* shrink range */
1003 if (next_idx == start)
1004 start++;
1005 if (next_idx+1 == end)
1006 end--;
1007 }
1008 return 0;
724257d9
GKH
1009}
1010
8c19dc54
YW
1011#define LOG_RULE_FULL(level, fmt, ...) log_full(level, "%s:%u: " fmt, filename, lineno, ##__VA_ARGS__)
1012#define LOG_RULE_ERROR(fmt, ...) LOG_RULE_FULL(LOG_ERR, fmt, ##__VA_ARGS__)
1013#define LOG_RULE_WARNING(fmt, ...) LOG_RULE_FULL(LOG_WARNING, fmt, ##__VA_ARGS__)
1014#define LOG_RULE_DEBUG(fmt, ...) LOG_RULE_FULL(LOG_DEBUG, fmt, ##__VA_ARGS__)
19a8e656 1015#define LOG_AND_RETURN(fmt, ...) { LOG_RULE_ERROR(fmt, __VA_ARGS__); return; }
6e2efb6c 1016#define LOG_AND_RETURN_ADD_KEY LOG_AND_RETURN("Temporary rule array too small, aborting event processing with %u items", rule_tmp.token_cur);
19a8e656 1017
9a07157d 1018static void add_rule(UdevRules *rules, char *line,
14cb109d 1019 const char *filename, unsigned filename_off, unsigned lineno) {
912541b0 1020 char *linepos;
04a9d3a0 1021 const char *attr;
84198c18
TG
1022 struct rule_tmp rule_tmp = {
1023 .rules = rules,
1024 .rule.type = TK_RULE,
1025 };
6e2efb6c 1026 int r;
912541b0 1027
775f8b3c
KS
1028 /* the offset in the rule is limited to unsigned short */
1029 if (filename_off < USHRT_MAX)
1030 rule_tmp.rule.rule.filename_off = filename_off;
912541b0
KS
1031 rule_tmp.rule.rule.filename_line = lineno;
1032
1033 linepos = line;
1034 for (;;) {
1035 char *key;
1036 char *value;
1037 enum operation_type op;
1038
704dbfb2 1039 if (get_key(&linepos, &key, &op, &value) < 0) {
3cf0f8f7
DR
1040 /* Avoid erroring on trailing whitespace. This is probably rare
1041 * so save the work for the error case instead of always trying
1042 * to strip the trailing whitespace with strstrip(). */
1043 while (isblank(*linepos))
1044 linepos++;
1045
e736cf35
DR
1046 /* If we aren't at the end of the line, this is a parsing error.
1047 * Make a best effort to describe where the problem is. */
2783fe06 1048 if (!strchr(NEWLINE, *linepos)) {
6501b52d 1049 char buf[2] = {*linepos};
1291bc89
ZJS
1050 _cleanup_free_ char *tmp;
1051
1052 tmp = cescape(buf);
8c19dc54 1053 LOG_RULE_ERROR("Invalid key/value pair, starting at character %tu ('%s')", linepos - line + 1, tmp);
6501b52d 1054 if (*linepos == '#')
8c19dc54 1055 LOG_RULE_ERROR("Hint: comments can only start at beginning of line");
1291bc89 1056 }
912541b0 1057 break;
e736cf35 1058 }
912541b0 1059
33502ffe 1060 if (streq(key, "ACTION")) {
19a8e656 1061 if (op > OP_MATCH_MAX)
8c19dc54 1062 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1063
6e2efb6c
YW
1064 if (rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL) < 0)
1065 LOG_AND_RETURN_ADD_KEY;
912541b0 1066
99f16bb8 1067 } else if (streq(key, "DEVPATH")) {
19a8e656 1068 if (op > OP_MATCH_MAX)
8c19dc54 1069 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1070
6e2efb6c
YW
1071 if (rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL) < 0)
1072 LOG_AND_RETURN_ADD_KEY;
912541b0 1073
99f16bb8 1074 } else if (streq(key, "KERNEL")) {
19a8e656 1075 if (op > OP_MATCH_MAX)
8c19dc54 1076 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1077
6e2efb6c
YW
1078 if (rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL) < 0)
1079 LOG_AND_RETURN_ADD_KEY;
912541b0 1080
99f16bb8 1081 } else if (streq(key, "SUBSYSTEM")) {
19a8e656 1082 if (op > OP_MATCH_MAX)
8c19dc54 1083 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1084
912541b0 1085 /* bus, class, subsystem events should all be the same */
99f16bb8
ZJS
1086 if (STR_IN_SET(value, "subsystem", "bus", "class")) {
1087 if (!streq(value, "subsystem"))
19a8e656
ZJS
1088 LOG_RULE_WARNING("'%s' must be specified as 'subsystem'; please fix", value);
1089
6e2efb6c 1090 r = rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL);
912541b0 1091 } else
6e2efb6c
YW
1092 r = rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL);
1093 if (r < 0)
1094 LOG_AND_RETURN_ADD_KEY;
912541b0 1095
99f16bb8 1096 } else if (streq(key, "DRIVER")) {
19a8e656 1097 if (op > OP_MATCH_MAX)
8c19dc54 1098 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1099
6e2efb6c
YW
1100 if (rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL) < 0)
1101 LOG_AND_RETURN_ADD_KEY;
912541b0 1102
99f16bb8 1103 } else if (startswith(key, "ATTR{")) {
2024ed61 1104 attr = get_key_attribute(key + STRLEN("ATTR"));
67e4b385 1105 if (!attr)
8c19dc54 1106 LOG_AND_RETURN("Failed to parse %s attribute", "ATTR");
19a8e656
ZJS
1107
1108 if (op == OP_REMOVE)
8c19dc54 1109 LOG_AND_RETURN("Invalid %s operation", "ATTR");
19a8e656 1110
f4cf2e5b 1111 if (op < OP_MATCH_MAX)
6e2efb6c 1112 r = rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr);
f4cf2e5b 1113 else
6e2efb6c
YW
1114 r = rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr);
1115 if (r < 0)
1116 LOG_AND_RETURN_ADD_KEY;
f4cf2e5b 1117
99f16bb8 1118 } else if (startswith(key, "SYSCTL{")) {
2024ed61 1119 attr = get_key_attribute(key + STRLEN("SYSCTL"));
67e4b385 1120 if (!attr)
8c19dc54 1121 LOG_AND_RETURN("Failed to parse %s attribute", "ATTR");
19a8e656
ZJS
1122
1123 if (op == OP_REMOVE)
8c19dc54 1124 LOG_AND_RETURN("Invalid %s operation", "ATTR");
19a8e656 1125
f4cf2e5b 1126 if (op < OP_MATCH_MAX)
6e2efb6c 1127 r = rule_add_key(&rule_tmp, TK_M_SYSCTL, op, value, attr);
f4cf2e5b 1128 else
6e2efb6c
YW
1129 r = rule_add_key(&rule_tmp, TK_A_SYSCTL, op, value, attr);
1130 if (r < 0)
1131 LOG_AND_RETURN_ADD_KEY;
912541b0 1132
99f16bb8 1133 } else if (startswith(key, "SECLABEL{")) {
2024ed61 1134 attr = get_key_attribute(key + STRLEN("SECLABEL"));
67e4b385 1135 if (!attr)
8c19dc54 1136 LOG_AND_RETURN("Failed to parse %s attribute", "SECLABEL");
19a8e656
ZJS
1137
1138 if (op == OP_REMOVE)
8c19dc54 1139 LOG_AND_RETURN("Invalid %s operation", "SECLABEL");
c26547d6 1140
6e2efb6c
YW
1141 if (rule_add_key(&rule_tmp, TK_A_SECLABEL, op, value, attr) < 0)
1142 LOG_AND_RETURN_ADD_KEY;
c26547d6 1143
99f16bb8 1144 } else if (streq(key, "KERNELS")) {
19a8e656 1145 if (op > OP_MATCH_MAX)
8c19dc54 1146 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1147
6e2efb6c
YW
1148 if (rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL) < 0)
1149 LOG_AND_RETURN_ADD_KEY;
912541b0 1150
99f16bb8 1151 } else if (streq(key, "SUBSYSTEMS")) {
19a8e656 1152 if (op > OP_MATCH_MAX)
8c19dc54 1153 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1154
6e2efb6c
YW
1155 if (rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL) < 0)
1156 LOG_AND_RETURN_ADD_KEY;
912541b0 1157
99f16bb8 1158 } else if (streq(key, "DRIVERS")) {
19a8e656 1159 if (op > OP_MATCH_MAX)
8c19dc54 1160 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1161
6e2efb6c
YW
1162 if (rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL) < 0)
1163 LOG_AND_RETURN_ADD_KEY;
912541b0 1164
99f16bb8 1165 } else if (startswith(key, "ATTRS{")) {
19a8e656 1166 if (op > OP_MATCH_MAX)
8c19dc54 1167 LOG_AND_RETURN("Invalid %s operation", "ATTRS");
19a8e656 1168
2024ed61 1169 attr = get_key_attribute(key + STRLEN("ATTRS"));
67e4b385 1170 if (!attr)
8c19dc54 1171 LOG_AND_RETURN("Failed to parse %s attribute", "ATTRS");
19a8e656 1172
33502ffe 1173 if (startswith(attr, "device/"))
19a8e656 1174 LOG_RULE_WARNING("'device' link may not be available in future kernels; please fix");
67e4b385 1175 if (strstr(attr, "../"))
8c19dc54 1176 LOG_RULE_WARNING("Direct reference to parent sysfs directory, may break in future kernels; please fix");
6e2efb6c
YW
1177 if (rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr) < 0)
1178 LOG_AND_RETURN_ADD_KEY;
912541b0 1179
99f16bb8 1180 } else if (streq(key, "TAGS")) {
19a8e656 1181 if (op > OP_MATCH_MAX)
8c19dc54 1182 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1183
6e2efb6c
YW
1184 if (rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL) < 0)
1185 LOG_AND_RETURN_ADD_KEY;
912541b0 1186
99f16bb8 1187 } else if (startswith(key, "ENV{")) {
2024ed61 1188 attr = get_key_attribute(key + STRLEN("ENV"));
67e4b385 1189 if (!attr)
8c19dc54 1190 LOG_AND_RETURN("Failed to parse %s attribute", "ENV");
19a8e656
ZJS
1191
1192 if (op == OP_REMOVE)
8c19dc54 1193 LOG_AND_RETURN("Invalid %s operation", "ENV");
19a8e656
ZJS
1194
1195 if (op < OP_MATCH_MAX)
6e2efb6c 1196 r = rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr);
19a8e656 1197 else {
99f16bb8
ZJS
1198 if (STR_IN_SET(attr,
1199 "ACTION",
1200 "SUBSYSTEM",
1201 "DEVTYPE",
1202 "MAJOR",
1203 "MINOR",
1204 "DRIVER",
1205 "IFINDEX",
1206 "DEVNAME",
1207 "DEVLINKS",
1208 "DEVPATH",
19a8e656 1209 "TAGS"))
8c19dc54 1210 LOG_AND_RETURN("Invalid ENV attribute, '%s' cannot be set", attr);
19a8e656 1211
6e2efb6c 1212 r = rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr);
912541b0 1213 }
6e2efb6c
YW
1214 if (r < 0)
1215 LOG_AND_RETURN_ADD_KEY;
912541b0 1216
99f16bb8 1217 } else if (streq(key, "TAG")) {
912541b0 1218 if (op < OP_MATCH_MAX)
6e2efb6c 1219 r = rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL);
912541b0 1220 else
6e2efb6c
YW
1221 r = rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL);
1222 if (r < 0)
1223 LOG_AND_RETURN_ADD_KEY;
912541b0 1224
99f16bb8 1225 } else if (streq(key, "PROGRAM")) {
19a8e656 1226 if (op == OP_REMOVE)
8c19dc54 1227 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1228
6e2efb6c
YW
1229 if (rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL) < 0)
1230 LOG_AND_RETURN_ADD_KEY;
912541b0 1231
99f16bb8 1232 } else if (streq(key, "RESULT")) {
19a8e656 1233 if (op > OP_MATCH_MAX)
8c19dc54 1234 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1235
6e2efb6c
YW
1236 if (rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL) < 0)
1237 LOG_AND_RETURN_ADD_KEY;
912541b0 1238
99f16bb8 1239 } else if (startswith(key, "IMPORT")) {
2024ed61 1240 attr = get_key_attribute(key + STRLEN("IMPORT"));
67e4b385 1241 if (!attr) {
8c19dc54 1242 LOG_RULE_WARNING("Ignoring IMPORT{} with missing type");
912541b0
KS
1243 continue;
1244 }
19a8e656 1245 if (op == OP_REMOVE)
8c19dc54 1246 LOG_AND_RETURN("Invalid %s operation", "IMPORT");
19a8e656 1247
33502ffe 1248 if (streq(attr, "program")) {
912541b0
KS
1249 /* find known built-in command */
1250 if (value[0] != '/') {
19a8e656 1251 const enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
912541b0 1252
c45b369d 1253 if (cmd >= 0) {
19a8e656 1254 LOG_RULE_DEBUG("IMPORT found builtin '%s', replacing", value);
6e2efb6c
YW
1255 if (rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd) < 0)
1256 LOG_AND_RETURN_ADD_KEY;
912541b0
KS
1257 continue;
1258 }
1259 }
6e2efb6c 1260 r = rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL);
33502ffe 1261 } else if (streq(attr, "builtin")) {
19a8e656 1262 const enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
912541b0 1263
6e2efb6c
YW
1264 if (cmd < 0) {
1265 LOG_RULE_WARNING("IMPORT{builtin} '%s' unknown, ignoring", value);
1266 continue;
1267 } else
1268 r = rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd);
19a8e656 1269 } else if (streq(attr, "file"))
6e2efb6c 1270 r = rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL);
19a8e656 1271 else if (streq(attr, "db"))
6e2efb6c 1272 r = rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL);
19a8e656 1273 else if (streq(attr, "cmdline"))
6e2efb6c 1274 r = rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL);
19a8e656 1275 else if (streq(attr, "parent"))
6e2efb6c
YW
1276 r = rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL);
1277 else {
8c19dc54 1278 LOG_RULE_ERROR("Ignoring unknown %s{} type '%s'", "IMPORT", attr);
6e2efb6c
YW
1279 continue;
1280 }
1281 if (r < 0)
1282 LOG_AND_RETURN_ADD_KEY;
912541b0 1283
99f16bb8 1284 } else if (startswith(key, "TEST")) {
912541b0
KS
1285 mode_t mode = 0;
1286
19a8e656 1287 if (op > OP_MATCH_MAX)
8c19dc54 1288 LOG_AND_RETURN("Invalid %s operation", "TEST");
19a8e656 1289
2024ed61 1290 attr = get_key_attribute(key + STRLEN("TEST"));
67e4b385 1291 if (attr) {
912541b0 1292 mode = strtol(attr, NULL, 8);
6e2efb6c 1293 r = rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode);
99f16bb8 1294 } else
6e2efb6c
YW
1295 r = rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL);
1296 if (r < 0)
1297 LOG_AND_RETURN_ADD_KEY;
912541b0 1298
99f16bb8 1299 } else if (startswith(key, "RUN")) {
2024ed61 1300 attr = get_key_attribute(key + STRLEN("RUN"));
67e4b385 1301 if (!attr)
83cd6b75 1302 attr = "program";
19a8e656 1303 if (op == OP_REMOVE)
8c19dc54 1304 LOG_AND_RETURN("Invalid %s operation", "RUN");
83cd6b75 1305
33502ffe 1306 if (streq(attr, "builtin")) {
19a8e656 1307 const enum udev_builtin_cmd cmd = udev_builtin_lookup(value);
83cd6b75 1308
6e2efb6c
YW
1309 if (cmd < 0) {
1310 LOG_RULE_ERROR("RUN{builtin}: '%s' unknown, ignoring", value);
1311 continue;
1312 } else
1313 r = rule_add_key(&rule_tmp, TK_A_RUN_BUILTIN, op, value, &cmd);
33502ffe 1314 } else if (streq(attr, "program")) {
c45b369d 1315 const enum udev_builtin_cmd cmd = _UDEV_BUILTIN_MAX;
83cd6b75 1316
6e2efb6c
YW
1317 r = rule_add_key(&rule_tmp, TK_A_RUN_PROGRAM, op, value, &cmd);
1318 } else {
8c19dc54 1319 LOG_RULE_ERROR("Ignoring unknown %s{} type '%s'", "RUN", attr);
6e2efb6c
YW
1320 continue;
1321 }
1322 if (r < 0)
1323 LOG_AND_RETURN_ADD_KEY;
912541b0 1324
99f16bb8 1325 } else if (streq(key, "LABEL")) {
19a8e656 1326 if (op == OP_REMOVE)
8c19dc54 1327 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1328
915bf0f6 1329 rule_tmp.rule.rule.label_off = rules_add_string(rules, value);
912541b0 1330
99f16bb8 1331 } else if (streq(key, "GOTO")) {
19a8e656 1332 if (op == OP_REMOVE)
8c19dc54 1333 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1334
6e2efb6c
YW
1335 if (rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL) < 0)
1336 LOG_AND_RETURN_ADD_KEY;
912541b0 1337
99f16bb8 1338 } else if (startswith(key, "NAME")) {
19a8e656 1339 if (op == OP_REMOVE)
8c19dc54 1340 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656
ZJS
1341
1342 if (op < OP_MATCH_MAX)
6e2efb6c 1343 r = rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL);
19a8e656 1344 else {
33502ffe 1345 if (streq(value, "%k")) {
19a8e656 1346 LOG_RULE_WARNING("NAME=\"%%k\" is ignored, because it breaks kernel supplied names; please remove");
912541b0
KS
1347 continue;
1348 }
19a8e656
ZJS
1349 if (isempty(value)) {
1350 LOG_RULE_DEBUG("NAME=\"\" is ignored, because udev will not delete any device nodes; please remove");
912541b0
KS
1351 continue;
1352 }
6e2efb6c 1353 r = rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL);
912541b0 1354 }
6e2efb6c
YW
1355 if (r < 0)
1356 LOG_AND_RETURN_ADD_KEY;
912541b0 1357 rule_tmp.rule.rule.can_set_name = true;
912541b0 1358
99f16bb8 1359 } else if (streq(key, "SYMLINK")) {
19a8e656 1360 if (op == OP_REMOVE)
8c19dc54 1361 LOG_AND_RETURN("Invalid %s operation", key);
19a8e656 1362
8a173387 1363 if (op < OP_MATCH_MAX)
6e2efb6c 1364 r = rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL);
8a173387 1365 else
6e2efb6c
YW
1366 r = rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, NULL);
1367 if (r < 0)
1368 LOG_AND_RETURN_ADD_KEY;
912541b0 1369 rule_tmp.rule.rule.can_set_name = true;
912541b0 1370
99f16bb8 1371 } else if (streq(key, "OWNER")) {
912541b0
KS
1372 uid_t uid;
1373 char *endptr;
1374
19a8e656 1375 if (op == OP_REMOVE)
8c19dc54 1376 LOG_AND_RETURN("Invalid %s operation", key);
8e3ba377 1377
912541b0 1378 uid = strtoul(value, &endptr, 10);
19a8e656 1379 if (endptr[0] == '\0')
6e2efb6c 1380 r = rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid);
67e4b385 1381 else if (rules->resolve_name_timing == RESOLVE_NAME_EARLY && !strchr("$%", value[0])) {
912541b0 1382 uid = add_uid(rules, value);
6e2efb6c 1383 r = rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid);
c4d44cba 1384 } else if (rules->resolve_name_timing != RESOLVE_NAME_NEVER)
6e2efb6c
YW
1385 r = rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL);
1386 else {
57f08d5c 1387 LOG_RULE_DEBUG("Resolving user name is disabled, ignoring %s=%s", key, value);
6e2efb6c
YW
1388 continue;
1389 }
1390 if (r < 0)
1391 LOG_AND_RETURN_ADD_KEY;
1f6b4113 1392
912541b0 1393 rule_tmp.rule.rule.can_set_name = true;
912541b0 1394
99f16bb8 1395 } else if (streq(key, "GROUP")) {
912541b0
KS
1396 gid_t gid;
1397 char *endptr;
1398
19a8e656 1399 if (op == OP_REMOVE)
8c19dc54 1400 LOG_AND_RETURN("Invalid %s operation", key);
8e3ba377 1401
912541b0 1402 gid = strtoul(value, &endptr, 10);
19a8e656 1403 if (endptr[0] == '\0')
6e2efb6c 1404 r = rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid);
67e4b385 1405 else if ((rules->resolve_name_timing == RESOLVE_NAME_EARLY) && !strchr("$%", value[0])) {
912541b0 1406 gid = add_gid(rules, value);
6e2efb6c 1407 r = rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid);
c4d44cba 1408 } else if (rules->resolve_name_timing != RESOLVE_NAME_NEVER)
6e2efb6c
YW
1409 r = rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL);
1410 else {
57f08d5c 1411 LOG_RULE_DEBUG("Resolving group name is disabled, ignoring %s=%s", key, value);
6e2efb6c
YW
1412 continue;
1413 }
1414 if (r < 0)
1415 LOG_AND_RETURN_ADD_KEY;
1f6b4113 1416
912541b0 1417 rule_tmp.rule.rule.can_set_name = true;
912541b0 1418
99f16bb8 1419 } else if (streq(key, "MODE")) {
912541b0
KS
1420 mode_t mode;
1421 char *endptr;
1422
19a8e656 1423 if (op == OP_REMOVE)
8c19dc54 1424 LOG_AND_RETURN("Invalid %s operation", key);
8e3ba377 1425
912541b0
KS
1426 mode = strtol(value, &endptr, 8);
1427 if (endptr[0] == '\0')
6e2efb6c 1428 r = rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode);
912541b0 1429 else
6e2efb6c
YW
1430 r = rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL);
1431 if (r < 0)
1432 LOG_AND_RETURN_ADD_KEY;
1433
912541b0 1434 rule_tmp.rule.rule.can_set_name = true;
912541b0 1435
99f16bb8 1436 } else if (streq(key, "OPTIONS")) {
912541b0
KS
1437 const char *pos;
1438
19a8e656 1439 if (op == OP_REMOVE)
8c19dc54 1440 LOG_AND_RETURN("Invalid %s operation", key);
8e3ba377 1441
912541b0 1442 pos = strstr(value, "link_priority=");
67e4b385 1443 if (pos) {
fbd0b64f 1444 int prio = atoi(pos + STRLEN("link_priority="));
912541b0 1445
6e2efb6c
YW
1446 if (rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio) < 0)
1447 LOG_AND_RETURN_ADD_KEY;
912541b0
KS
1448 }
1449
9f20a8a3 1450 pos = strstr(value, "string_escape=");
67e4b385 1451 if (pos) {
fbd0b64f 1452 pos += STRLEN("string_escape=");
33502ffe 1453 if (startswith(pos, "none"))
6e2efb6c 1454 r = rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL);
33502ffe 1455 else if (startswith(pos, "replace"))
6e2efb6c
YW
1456 r = rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL);
1457 else {
1458 LOG_RULE_ERROR("OPTIONS: unknown string_escape mode '%s', ignoring", pos);
1459 r = 0;
1460 }
1461 if (r < 0)
1462 LOG_AND_RETURN_ADD_KEY;
912541b0
KS
1463 }
1464
1465 pos = strstr(value, "db_persist");
67e4b385 1466 if (pos)
6e2efb6c
YW
1467 if (rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL) < 0)
1468 LOG_AND_RETURN_ADD_KEY;
912541b0
KS
1469
1470 pos = strstr(value, "nowatch");
b33fa02b
LP
1471 if (pos) {
1472 static const int zero = 0;
6e2efb6c
YW
1473 if (rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &zero) < 0)
1474 LOG_AND_RETURN_ADD_KEY;
b33fa02b
LP
1475 } else {
1476 static const int one = 1;
912541b0 1477 pos = strstr(value, "watch");
6d5e65f6 1478 if (pos)
6e2efb6c
YW
1479 if (rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &one) < 0)
1480 LOG_AND_RETURN_ADD_KEY;
912541b0
KS
1481 }
1482
1483 pos = strstr(value, "static_node=");
67e4b385 1484 if (pos) {
fbd0b64f 1485 pos += STRLEN("static_node=");
6e2efb6c
YW
1486 if (rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, pos, NULL) < 0)
1487 LOG_AND_RETURN_ADD_KEY;
912541b0
KS
1488 rule_tmp.rule.rule.has_static_node = true;
1489 }
1490
19a8e656 1491 } else
8c19dc54 1492 LOG_AND_RETURN("Unknown key '%s'", key);
912541b0
KS
1493 }
1494
19a8e656 1495 /* add rule token and sort tokens */
912541b0 1496 rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur;
ef660d07 1497 if (add_token(rules, &rule_tmp.rule) < 0 || sort_token(rules, &rule_tmp) < 0)
8c19dc54 1498 LOG_RULE_ERROR("Failed to add rule token");
c7521974
KS
1499}
1500
9a07157d 1501static int parse_file(UdevRules *rules, const char *filename) {
6c8aaf0c 1502 _cleanup_fclose_ FILE *f = NULL;
14cb109d
YW
1503 unsigned first_token;
1504 unsigned filename_off;
912541b0
KS
1505 char line[UTIL_LINE_SIZE];
1506 int line_nr = 0;
14cb109d 1507 unsigned i;
912541b0 1508
ed88bcfb
ZJS
1509 f = fopen(filename, "re");
1510 if (!f) {
1511 if (errno == ENOENT)
1512 return 0;
fae0f8a0
LP
1513
1514 return -errno;
775f8b3c 1515 }
912541b0 1516
ed88bcfb
ZJS
1517 if (null_or_empty_fd(fileno(f))) {
1518 log_debug("Skipping empty file: %s", filename);
1519 return 0;
1520 } else
1521 log_debug("Reading rules file: %s", filename);
912541b0
KS
1522
1523 first_token = rules->token_cur;
915bf0f6 1524 filename_off = rules_add_string(rules, filename);
912541b0 1525
67e4b385 1526 while (fgets(line, sizeof(line), f)) {
912541b0
KS
1527 char *key;
1528 size_t len;
1529
1530 /* skip whitespace */
1531 line_nr++;
1532 key = line;
1533 while (isspace(key[0]))
1534 key++;
1535
1536 /* comment */
1537 if (key[0] == '#')
1538 continue;
1539
1540 len = strlen(line);
1541 if (len < 3)
1542 continue;
1543
1544 /* continue reading if backslash+newline is found */
1545 while (line[len-2] == '\\') {
67e4b385 1546 if (!fgets(&line[len-2], (sizeof(line)-len)+2, f))
912541b0
KS
1547 break;
1548 if (strlen(&line[len-2]) < 2)
1549 break;
1550 line_nr++;
1551 len = strlen(line);
1552 }
1553
1554 if (len+1 >= sizeof(line)) {
8c19dc54 1555 log_error("Line too long '%s':%u, ignored", filename, line_nr);
912541b0
KS
1556 continue;
1557 }
1558 add_rule(rules, key, filename, filename_off, line_nr);
1559 }
912541b0
KS
1560
1561 /* link GOTOs to LABEL rules in this file to be able to fast-forward */
1562 for (i = first_token+1; i < rules->token_cur; i++) {
1563 if (rules->tokens[i].type == TK_A_GOTO) {
915bf0f6 1564 char *label = rules_str(rules, rules->tokens[i].key.value_off);
14cb109d 1565 unsigned j;
912541b0
KS
1566
1567 for (j = i+1; j < rules->token_cur; j++) {
1568 if (rules->tokens[j].type != TK_RULE)
1569 continue;
1570 if (rules->tokens[j].rule.label_off == 0)
1571 continue;
915bf0f6 1572 if (!streq(label, rules_str(rules, rules->tokens[j].rule.label_off)))
912541b0
KS
1573 continue;
1574 rules->tokens[i].key.rule_goto = j;
1575 break;
1576 }
1577 if (rules->tokens[i].key.rule_goto == 0)
9f6445e3 1578 log_error("GOTO '%s' has no matching label in: '%s'", label, filename);
912541b0
KS
1579 }
1580 }
1581 return 0;
c7521974
KS
1582}
1583
9a07157d
ZJS
1584int udev_rules_new(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing) {
1585 _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
1d791281
ZJS
1586 _cleanup_strv_free_ char **files = NULL;
1587 char **f;
775f8b3c 1588 int r;
912541b0 1589
c4d44cba
YW
1590 assert(resolve_name_timing >= 0 && resolve_name_timing < _RESOLVE_NAME_TIMING_MAX);
1591
9a07157d 1592 rules = new(UdevRules, 1);
1017d66b 1593 if (!rules)
1d791281 1594 return -ENOMEM;
1017d66b 1595
9a07157d 1596 *rules = (UdevRules) {
c4d44cba 1597 .resolve_name_timing = resolve_name_timing,
1017d66b 1598 };
912541b0
KS
1599
1600 /* init token array and string buffer */
8419d457 1601 rules->tokens = malloc_multiply(PREALLOC_TOKEN, sizeof(struct token));
67e4b385 1602 if (!rules->tokens)
1d791281 1603 return -ENOMEM;
912541b0
KS
1604 rules->token_max = PREALLOC_TOKEN;
1605
915bf0f6
KS
1606 rules->strbuf = strbuf_new();
1607 if (!rules->strbuf)
1d791281 1608 return -ENOMEM;
0820a4f0 1609
3b8c1cb0
KS
1610 udev_rules_check_timestamp(rules);
1611
b5084605 1612 r = conf_files_list_strv(&files, ".rules", NULL, 0, rules_dirs);
1d791281
ZJS
1613 if (r < 0)
1614 return log_error_errno(r, "Failed to enumerate rules files: %m");
912541b0 1615
775f8b3c
KS
1616 /*
1617 * The offset value in the rules strct is limited; add all
1618 * rules file names to the beginning of the string buffer.
1619 */
1620 STRV_FOREACH(f, files)
915bf0f6 1621 rules_add_string(rules, *f);
912541b0 1622
775f8b3c
KS
1623 STRV_FOREACH(f, files)
1624 parse_file(rules, *f);
1625
1d791281 1626 struct token end_token = { .type = TK_END };
912541b0 1627 add_token(rules, &end_token);
8c19dc54 1628 log_debug("Rules contain %zu bytes tokens (%u * %zu bytes), %zu bytes strings",
915bf0f6 1629 rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->strbuf->len);
912541b0 1630
915bf0f6 1631 /* cleanup temporary strbuf data */
9f6445e3 1632 log_debug("%zu strings (%zu bytes), %zu de-duplicated (%zu bytes), %zu trie nodes used",
915bf0f6
KS
1633 rules->strbuf->in_count, rules->strbuf->in_len,
1634 rules->strbuf->dedup_count, rules->strbuf->dedup_len, rules->strbuf->nodes_count);
1635 strbuf_complete(rules->strbuf);
912541b0
KS
1636
1637 /* cleanup uid/gid cache */
a1e58e8e 1638 rules->uids = mfree(rules->uids);
912541b0
KS
1639 rules->uids_cur = 0;
1640 rules->uids_max = 0;
a1e58e8e 1641 rules->gids = mfree(rules->gids);
912541b0
KS
1642 rules->gids_cur = 0;
1643 rules->gids_max = 0;
1644
1645 dump_rules(rules);
1d791281
ZJS
1646 *ret_rules = TAKE_PTR(rules);
1647 return 0;
c7521974
KS
1648}
1649
9a07157d 1650UdevRules *udev_rules_free(UdevRules *rules) {
67e4b385 1651 if (!rules)
912541b0
KS
1652 return NULL;
1653 free(rules->tokens);
915bf0f6 1654 strbuf_cleanup(rules->strbuf);
912541b0
KS
1655 free(rules->uids);
1656 free(rules->gids);
6b430fdb 1657 return mfree(rules);
c7521974 1658}
6880b25d 1659
9a07157d 1660bool udev_rules_check_timestamp(UdevRules *rules) {
5c11fbe3
KS
1661 if (!rules)
1662 return false;
1663
2ad8416d 1664 return paths_check_timestamp(rules_dirs, &rules->dirs_ts_usec, true);
6ada823a
KS
1665}
1666
605aa52f 1667static bool match_key(UdevRules *rules, struct token *token, const char *val) {
915bf0f6 1668 char *key_value = rules_str(rules, token->key.value_off);
912541b0
KS
1669 char *pos;
1670 bool match = false;
1671
67e4b385 1672 if (!val)
912541b0
KS
1673 val = "";
1674
1675 switch (token->key.glob) {
1676 case GL_PLAIN:
bb175a03 1677 match = streq(key_value, val);
912541b0
KS
1678 break;
1679 case GL_GLOB:
1680 match = (fnmatch(key_value, val, 0) == 0);
1681 break;
1682 case GL_SPLIT:
1683 {
33502ffe 1684 const char *s;
912541b0
KS
1685 size_t len;
1686
915bf0f6 1687 s = rules_str(rules, token->key.value_off);
912541b0
KS
1688 len = strlen(val);
1689 for (;;) {
1690 const char *next;
1691
33502ffe 1692 next = strchr(s, '|');
67e4b385 1693 if (next) {
33502ffe 1694 size_t matchlen = (size_t)(next - s);
912541b0 1695
641906e9 1696 match = (matchlen == len && strneq(s, val, matchlen));
912541b0
KS
1697 if (match)
1698 break;
1699 } else {
bb175a03 1700 match = streq(s, val);
912541b0
KS
1701 break;
1702 }
33502ffe 1703 s = &next[1];
912541b0
KS
1704 }
1705 break;
1706 }
1707 case GL_SPLIT_GLOB:
1708 {
1709 char value[UTIL_PATH_SIZE];
1710
d5a89d7d 1711 strscpy(value, sizeof(value), rules_str(rules, token->key.value_off));
912541b0 1712 key_value = value;
67e4b385 1713 while (key_value) {
912541b0 1714 pos = strchr(key_value, '|');
67e4b385 1715 if (pos) {
912541b0
KS
1716 pos[0] = '\0';
1717 pos = &pos[1];
1718 }
912541b0
KS
1719 match = (fnmatch(key_value, val, 0) == 0);
1720 if (match)
1721 break;
1722 key_value = pos;
1723 }
1724 break;
1725 }
1726 case GL_SOMETHING:
1727 match = (val[0] != '\0');
1728 break;
1729 case GL_UNSET:
605aa52f 1730 return false;
912541b0
KS
1731 }
1732
605aa52f 1733 return token->key.op == (match ? OP_MATCH : OP_NOMATCH);
6880b25d
KS
1734}
1735
605aa52f 1736static bool match_attr(UdevRules *rules, sd_device *dev, UdevEvent *event, struct token *cur) {
5ba7e798
YW
1737 char nbuf[UTIL_NAME_SIZE], vbuf[UTIL_NAME_SIZE];
1738 const char *name, *value;
912541b0
KS
1739 size_t len;
1740
915bf0f6 1741 name = rules_str(rules, cur->key.attr_off);
912541b0
KS
1742 switch (cur->key.attrsubst) {
1743 case SB_FORMAT:
e20a9171 1744 udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false);
912541b0 1745 name = nbuf;
4831981d 1746 _fallthrough_;
912541b0 1747 case SB_NONE:
5ba7e798 1748 if (sd_device_get_sysattr_value(dev, name, &value) < 0)
605aa52f 1749 return false;
912541b0
KS
1750 break;
1751 case SB_SUBSYS:
76b9bdd9 1752 if (util_resolve_subsys_kernel(name, vbuf, sizeof(vbuf), true) < 0)
605aa52f 1753 return false;
912541b0
KS
1754 value = vbuf;
1755 break;
1756 default:
605aa52f 1757 return false;
912541b0
KS
1758 }
1759
1760 /* remove trailing whitespace, if not asked to match for it */
1761 len = strlen(value);
1762 if (len > 0 && isspace(value[len-1])) {
1763 const char *key_value;
1764 size_t klen;
1765
915bf0f6 1766 key_value = rules_str(rules, cur->key.value_off);
912541b0
KS
1767 klen = strlen(key_value);
1768 if (klen > 0 && !isspace(key_value[klen-1])) {
1769 if (value != vbuf) {
d5a89d7d 1770 strscpy(vbuf, sizeof(vbuf), value);
912541b0
KS
1771 value = vbuf;
1772 }
1773 while (len > 0 && isspace(vbuf[--len]))
1774 vbuf[len] = '\0';
912541b0
KS
1775 }
1776 }
1777
1778 return match_key(rules, cur, value);
6880b25d
KS
1779}
1780
1781enum escape_type {
912541b0
KS
1782 ESCAPE_UNSET,
1783 ESCAPE_NONE,
1784 ESCAPE_REPLACE,
6880b25d
KS
1785};
1786
d838e145 1787int udev_rules_apply_to_event(
9a07157d 1788 UdevRules *rules,
2e088715 1789 UdevEvent *event,
d838e145 1790 usec_t timeout_usec,
9b5150b6 1791 Hashmap *properties_list) {
cf28ad46 1792 sd_device *dev = event->dev;
912541b0 1793 enum escape_type esc = ESCAPE_UNSET;
cf697ec0
YW
1794 struct token *cur, *rule;
1795 const char *action, *val;
912541b0 1796 bool can_set_name;
88b013b2 1797 int r;
912541b0 1798
d838e145
YW
1799 if (!rules->tokens)
1800 return 0;
912541b0 1801
cf697ec0
YW
1802 r = sd_device_get_property_value(dev, "ACTION", &action);
1803 if (r < 0)
1804 return r;
1805
1806 can_set_name = (!streq(action, "remove") &&
1807 (sd_device_get_devnum(dev, NULL) >= 0 ||
1808 sd_device_get_ifindex(dev, NULL) >= 0));
912541b0
KS
1809
1810 /* loop through token list, match, run actions or forward to next rule */
1811 cur = &rules->tokens[0];
1812 rule = cur;
1813 for (;;) {
1814 dump_token(rules, cur);
1815 switch (cur->type) {
1816 case TK_RULE:
1817 /* current rule */
1818 rule = cur;
1819 /* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */
1820 if (!can_set_name && rule->rule.can_set_name)
1821 goto nomatch;
1822 esc = ESCAPE_UNSET;
1823 break;
1824 case TK_M_ACTION:
605aa52f 1825 if (!match_key(rules, cur, action))
912541b0
KS
1826 goto nomatch;
1827 break;
1828 case TK_M_DEVPATH:
cf697ec0
YW
1829 if (sd_device_get_devpath(dev, &val) < 0)
1830 goto nomatch;
605aa52f 1831 if (!match_key(rules, cur, val))
912541b0
KS
1832 goto nomatch;
1833 break;
1834 case TK_M_KERNEL:
cf697ec0
YW
1835 if (sd_device_get_sysname(dev, &val) < 0)
1836 goto nomatch;
605aa52f 1837 if (!match_key(rules, cur, val))
912541b0
KS
1838 goto nomatch;
1839 break;
1840 case TK_M_DEVLINK: {
dbea7f24 1841 const char *devlink;
912541b0
KS
1842 bool match = false;
1843
cf697ec0 1844 FOREACH_DEVICE_DEVLINK(dev, devlink)
605aa52f 1845 if (match_key(rules, cur, devlink + STRLEN("/dev/"))) {
912541b0
KS
1846 match = true;
1847 break;
1848 }
dbea7f24 1849
912541b0
KS
1850 if (!match)
1851 goto nomatch;
1852 break;
1853 }
1854 case TK_M_NAME:
605aa52f 1855 if (!match_key(rules, cur, event->name))
912541b0
KS
1856 goto nomatch;
1857 break;
1858 case TK_M_ENV: {
915bf0f6 1859 const char *key_name = rules_str(rules, cur->key.attr_off);
912541b0 1860
cf697ec0
YW
1861 if (sd_device_get_property_value(dev, key_name, &val) < 0) {
1862 /* check global properties */
1863 if (properties_list)
1864 val = hashmap_get(properties_list, key_name);
1865 else
1866 val = NULL;
1867 }
adeba500 1868
605aa52f 1869 if (!match_key(rules, cur, strempty(val)))
912541b0
KS
1870 goto nomatch;
1871 break;
1872 }
1873 case TK_M_TAG: {
912541b0 1874 bool match = false;
dbea7f24 1875 const char *tag;
912541b0 1876
cf697ec0 1877 FOREACH_DEVICE_TAG(dev, tag)
dbea7f24 1878 if (streq(rules_str(rules, cur->key.value_off), tag)) {
912541b0
KS
1879 match = true;
1880 break;
1881 }
dbea7f24 1882
4302857b
FF
1883 if ((!match && (cur->key.op != OP_NOMATCH)) ||
1884 (match && (cur->key.op == OP_NOMATCH)))
912541b0
KS
1885 goto nomatch;
1886 break;
1887 }
1888 case TK_M_SUBSYSTEM:
cf697ec0
YW
1889 if (sd_device_get_subsystem(dev, &val) < 0)
1890 goto nomatch;
605aa52f 1891 if (!match_key(rules, cur, val))
912541b0
KS
1892 goto nomatch;
1893 break;
1894 case TK_M_DRIVER:
cf697ec0
YW
1895 if (sd_device_get_driver(dev, &val) < 0)
1896 goto nomatch;
605aa52f 1897 if (!match_key(rules, cur, val))
912541b0
KS
1898 goto nomatch;
1899 break;
912541b0 1900 case TK_M_ATTR:
605aa52f 1901 if (!match_attr(rules, dev, event, cur))
912541b0
KS
1902 goto nomatch;
1903 break;
f4cf2e5b
KS
1904 case TK_M_SYSCTL: {
1905 char filename[UTIL_PATH_SIZE];
1906 _cleanup_free_ char *value = NULL;
1907 size_t len;
1908
e20a9171 1909 udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false);
f4cf2e5b
KS
1910 sysctl_normalize(filename);
1911 if (sysctl_read(filename, &value) < 0)
1912 goto nomatch;
1913
1914 len = strlen(value);
1915 while (len > 0 && isspace(value[--len]))
1916 value[len] = '\0';
605aa52f 1917 if (!match_key(rules, cur, value))
f4cf2e5b
KS
1918 goto nomatch;
1919 break;
1920 }
912541b0
KS
1921 case TK_M_KERNELS:
1922 case TK_M_SUBSYSTEMS:
1923 case TK_M_DRIVERS:
1924 case TK_M_ATTRS:
1925 case TK_M_TAGS: {
1926 struct token *next;
1927
1928 /* get whole sequence of parent matches */
1929 next = cur;
1930 while (next->type > TK_M_PARENTS_MIN && next->type < TK_M_PARENTS_MAX)
1931 next++;
1932
1933 /* loop over parents */
cf697ec0 1934 event->dev_parent = dev;
912541b0
KS
1935 for (;;) {
1936 struct token *key;
1937
912541b0
KS
1938 /* loop over sequence of parent match keys */
1939 for (key = cur; key < next; key++ ) {
1940 dump_token(rules, key);
1941 switch(key->type) {
1942 case TK_M_KERNELS:
f3d241fe
YW
1943 if (sd_device_get_sysname(event->dev_parent, &val) < 0)
1944 goto try_parent;
605aa52f 1945 if (!match_key(rules, key, val))
912541b0
KS
1946 goto try_parent;
1947 break;
1948 case TK_M_SUBSYSTEMS:
f3d241fe
YW
1949 if (sd_device_get_subsystem(event->dev_parent, &val) < 0)
1950 goto try_parent;
605aa52f 1951 if (!match_key(rules, key, val))
912541b0
KS
1952 goto try_parent;
1953 break;
1954 case TK_M_DRIVERS:
f3d241fe
YW
1955 if (sd_device_get_driver(event->dev_parent, &val) < 0)
1956 goto try_parent;
605aa52f 1957 if (!match_key(rules, key, val))
912541b0
KS
1958 goto try_parent;
1959 break;
1960 case TK_M_ATTRS:
605aa52f 1961 if (!match_attr(rules, event->dev_parent, event, key))
912541b0
KS
1962 goto try_parent;
1963 break;
1964 case TK_M_TAGS: {
f3d241fe 1965 bool match = sd_device_has_tag(event->dev_parent, rules_str(rules, cur->key.value_off));
912541b0
KS
1966
1967 if (match && key->key.op == OP_NOMATCH)
1968 goto try_parent;
1969 if (!match && key->key.op == OP_MATCH)
1970 goto try_parent;
1971 break;
1972 }
1973 default:
1974 goto nomatch;
1975 }
912541b0 1976 }
912541b0
KS
1977 break;
1978
1979 try_parent:
f3d241fe
YW
1980 if (sd_device_get_parent(event->dev_parent, &event->dev_parent) < 0) {
1981 event->dev_parent = NULL;
912541b0 1982 goto nomatch;
f3d241fe 1983 }
912541b0
KS
1984 }
1985 /* move behind our sequence of parent match keys */
1986 cur = next;
1987 continue;
1988 }
1989 case TK_M_TEST: {
1990 char filename[UTIL_PATH_SIZE];
1991 struct stat statbuf;
1992 int match;
1993
e20a9171 1994 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false);
76b9bdd9 1995 if (util_resolve_subsys_kernel(filename, filename, sizeof(filename), false) < 0) {
912541b0
KS
1996 if (filename[0] != '/') {
1997 char tmp[UTIL_PATH_SIZE];
1998
cf697ec0
YW
1999 if (sd_device_get_syspath(dev, &val) < 0)
2000 goto nomatch;
2001
d5a89d7d 2002 strscpy(tmp, sizeof(tmp), filename);
cf697ec0 2003 strscpyl(filename, sizeof(filename), val, "/", tmp, NULL);
912541b0
KS
2004 }
2005 }
2006 attr_subst_subdir(filename, sizeof(filename));
2007
2008 match = (stat(filename, &statbuf) == 0);
baa30fbc 2009 if (match && cur->key.mode > 0)
912541b0 2010 match = ((statbuf.st_mode & cur->key.mode) > 0);
912541b0
KS
2011 if (match && cur->key.op == OP_NOMATCH)
2012 goto nomatch;
2013 if (!match && cur->key.op == OP_MATCH)
2014 goto nomatch;
2015 break;
2016 }
912541b0 2017 case TK_M_PROGRAM: {
cf697ec0 2018 char program[UTIL_PATH_SIZE], result[UTIL_LINE_SIZE];
912541b0 2019
a1e58e8e 2020 event->program_result = mfree(event->program_result);
e20a9171 2021 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), program, sizeof(program), false);
b4ba2fe3
YW
2022 log_device_debug(dev, "PROGRAM '%s' %s:%u",
2023 program,
2024 rules_str(rules, rule->rule.filename_off),
2025 rule->rule.filename_line);
912541b0 2026
a7521142 2027 if (udev_event_spawn(event, timeout_usec, true, program, result, sizeof(result)) != 0) {
912541b0
KS
2028 if (cur->key.op != OP_NOMATCH)
2029 goto nomatch;
2030 } else {
2031 int count;
2032
7546145e 2033 delete_trailing_chars(result, "\n");
4c701096 2034 if (IN_SET(esc, ESCAPE_UNSET, ESCAPE_REPLACE)) {
912541b0
KS
2035 count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT);
2036 if (count > 0)
b4ba2fe3 2037 log_device_debug(dev, "Replaced %i character(s) from result of '%s'" , count, program);
912541b0
KS
2038 }
2039 event->program_result = strdup(result);
912541b0
KS
2040 if (cur->key.op == OP_NOMATCH)
2041 goto nomatch;
2042 }
2043 break;
2044 }
2045 case TK_M_IMPORT_FILE: {
2046 char import[UTIL_PATH_SIZE];
2047
e20a9171 2048 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
76b9bdd9 2049 if (import_file_into_properties(dev, import) < 0)
912541b0
KS
2050 if (cur->key.op != OP_NOMATCH)
2051 goto nomatch;
2052 break;
2053 }
2054 case TK_M_IMPORT_PROG: {
2055 char import[UTIL_PATH_SIZE];
2056
e20a9171 2057 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
b4ba2fe3
YW
2058 log_device_debug(dev, "IMPORT '%s' %s:%u",
2059 import,
2060 rules_str(rules, rule->rule.filename_off),
2061 rule->rule.filename_line);
912541b0 2062
76b9bdd9 2063 if (import_program_into_properties(event, timeout_usec, import) < 0)
912541b0
KS
2064 if (cur->key.op != OP_NOMATCH)
2065 goto nomatch;
2066 break;
2067 }
2068 case TK_M_IMPORT_BUILTIN: {
2069 char command[UTIL_PATH_SIZE];
2070
2071 if (udev_builtin_run_once(cur->key.builtin_cmd)) {
2072 /* check if we ran already */
2073 if (event->builtin_run & (1 << cur->key.builtin_cmd)) {
b4ba2fe3
YW
2074 log_device_debug(dev, "IMPORT builtin skip '%s' %s:%u",
2075 udev_builtin_name(cur->key.builtin_cmd),
2076 rules_str(rules, rule->rule.filename_off),
2077 rule->rule.filename_line);
912541b0
KS
2078 /* return the result from earlier run */
2079 if (event->builtin_ret & (1 << cur->key.builtin_cmd))
7d6884b6 2080 if (cur->key.op != OP_NOMATCH)
912541b0
KS
2081 goto nomatch;
2082 break;
2083 }
2084 /* mark as ran */
2085 event->builtin_run |= (1 << cur->key.builtin_cmd);
2086 }
2087
e20a9171 2088 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), command, sizeof(command), false);
b4ba2fe3
YW
2089 log_device_debug(dev, "IMPORT builtin '%s' %s:%u",
2090 udev_builtin_name(cur->key.builtin_cmd),
2091 rules_str(rules, rule->rule.filename_off),
2092 rule->rule.filename_line);
912541b0 2093
cf697ec0 2094 r = udev_builtin_run(dev, cur->key.builtin_cmd, command, false);
d354690e 2095 if (r < 0) {
912541b0 2096 /* remember failure */
b4ba2fe3
YW
2097 log_device_debug_errno(dev, r, "IMPORT builtin '%s' fails: %m",
2098 udev_builtin_name(cur->key.builtin_cmd));
912541b0
KS
2099 event->builtin_ret |= (1 << cur->key.builtin_cmd);
2100 if (cur->key.op != OP_NOMATCH)
2101 goto nomatch;
2102 }
2103 break;
2104 }
2105 case TK_M_IMPORT_DB: {
cf697ec0 2106 const char *key;
912541b0 2107
cf697ec0 2108 key = rules_str(rules, cur->key.value_off);
a6da77b7
YW
2109 if (event->dev_db_clone &&
2110 sd_device_get_property_value(event->dev_db_clone, key, &val) >= 0)
cf697ec0 2111 device_add_property(dev, key, val);
480ecb7d
YW
2112 else if (cur->key.op != OP_NOMATCH)
2113 goto nomatch;
912541b0
KS
2114 break;
2115 }
2116 case TK_M_IMPORT_CMDLINE: {
88b013b2 2117 _cleanup_free_ char *value = NULL;
912541b0 2118 bool imported = false;
88b013b2 2119 const char *key;
912541b0 2120
88b013b2 2121 key = rules_str(rules, cur->key.value_off);
88b013b2
LP
2122 r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &value);
2123 if (r < 0)
b4ba2fe3 2124 log_device_debug_errno(dev, r, "Failed to read %s from /proc/cmdline, ignoring: %m", key);
88b013b2
LP
2125 else if (r > 0) {
2126 imported = true;
2127
2128 if (value)
cf697ec0 2129 device_add_property(dev, key, value);
88b013b2
LP
2130 else
2131 /* we import simple flags as 'FLAG=1' */
cf697ec0 2132 device_add_property(dev, key, "1");
912541b0 2133 }
88b013b2 2134
912541b0
KS
2135 if (!imported && cur->key.op != OP_NOMATCH)
2136 goto nomatch;
2137 break;
2138 }
2139 case TK_M_IMPORT_PARENT: {
2140 char import[UTIL_PATH_SIZE];
2141
e20a9171 2142 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import), false);
76b9bdd9 2143 if (import_parent_into_properties(dev, import) < 0)
912541b0
KS
2144 if (cur->key.op != OP_NOMATCH)
2145 goto nomatch;
2146 break;
2147 }
2148 case TK_M_RESULT:
605aa52f 2149 if (!match_key(rules, cur, event->program_result))
912541b0
KS
2150 goto nomatch;
2151 break;
2152 case TK_A_STRING_ESCAPE_NONE:
2153 esc = ESCAPE_NONE;
2154 break;
2155 case TK_A_STRING_ESCAPE_REPLACE:
2156 esc = ESCAPE_REPLACE;
2157 break;
2158 case TK_A_DB_PERSIST:
cf697ec0 2159 device_set_db_persist(dev);
912541b0
KS
2160 break;
2161 case TK_A_INOTIFY_WATCH:
2162 if (event->inotify_watch_final)
2163 break;
2164 if (cur->key.op == OP_ASSIGN_FINAL)
2165 event->inotify_watch_final = true;
2166 event->inotify_watch = cur->key.watch;
2167 break;
2168 case TK_A_DEVLINK_PRIO:
cf697ec0 2169 device_set_devlink_priority(dev, cur->key.devlink_prio);
912541b0
KS
2170 break;
2171 case TK_A_OWNER: {
2172 char owner[UTIL_NAME_SIZE];
23bf8dd7 2173 const char *ow = owner;
912541b0
KS
2174
2175 if (event->owner_final)
2176 break;
2177 if (cur->key.op == OP_ASSIGN_FINAL)
2178 event->owner_final = true;
e20a9171 2179 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner), false);
1edefa4f 2180 event->owner_set = true;
fafff8f1 2181 r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
23bf8dd7 2182 if (r < 0) {
b4ba2fe3 2183 log_unknown_owner(dev, r, "user", owner);
23bf8dd7
TG
2184 event->uid = 0;
2185 }
b4ba2fe3
YW
2186 log_device_debug(dev, "OWNER %u %s:%u",
2187 event->uid,
2188 rules_str(rules, rule->rule.filename_off),
2189 rule->rule.filename_line);
912541b0
KS
2190 break;
2191 }
2192 case TK_A_GROUP: {
2193 char group[UTIL_NAME_SIZE];
23bf8dd7 2194 const char *gr = group;
912541b0
KS
2195
2196 if (event->group_final)
2197 break;
2198 if (cur->key.op == OP_ASSIGN_FINAL)
2199 event->group_final = true;
e20a9171 2200 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group), false);
1edefa4f 2201 event->group_set = true;
fafff8f1 2202 r = get_group_creds(&gr, &event->gid, USER_CREDS_ALLOW_MISSING);
23bf8dd7 2203 if (r < 0) {
b4ba2fe3 2204 log_unknown_owner(dev, r, "group", group);
23bf8dd7
TG
2205 event->gid = 0;
2206 }
b4ba2fe3
YW
2207 log_device_debug(dev, "GROUP %u %s:%u",
2208 event->gid,
2209 rules_str(rules, rule->rule.filename_off),
2210 rule->rule.filename_line);
912541b0
KS
2211 break;
2212 }
2213 case TK_A_MODE: {
b4ba2fe3 2214 char mode_str[UTIL_NAME_SIZE];
912541b0 2215 mode_t mode;
912541b0
KS
2216
2217 if (event->mode_final)
2218 break;
e20a9171 2219 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), mode_str, sizeof(mode_str), false);
b4ba2fe3
YW
2220 r = parse_mode(mode_str, &mode);
2221 if (r < 0) {
2222 log_device_error_errno(dev, r, "Failed to parse mode '%s': %m", mode_str);
912541b0
KS
2223 break;
2224 }
2225 if (cur->key.op == OP_ASSIGN_FINAL)
2226 event->mode_final = true;
2227 event->mode_set = true;
2228 event->mode = mode;
b4ba2fe3
YW
2229 log_device_debug(dev, "MODE %#o %s:%u",
2230 event->mode,
2231 rules_str(rules, rule->rule.filename_off),
2232 rule->rule.filename_line);
912541b0
KS
2233 break;
2234 }
2235 case TK_A_OWNER_ID:
2236 if (event->owner_final)
2237 break;
2238 if (cur->key.op == OP_ASSIGN_FINAL)
2239 event->owner_final = true;
1edefa4f 2240 event->owner_set = true;
912541b0 2241 event->uid = cur->key.uid;
b4ba2fe3
YW
2242 log_device_debug(dev, "OWNER %u %s:%u",
2243 event->uid,
2244 rules_str(rules, rule->rule.filename_off),
2245 rule->rule.filename_line);
912541b0
KS
2246 break;
2247 case TK_A_GROUP_ID:
2248 if (event->group_final)
2249 break;
2250 if (cur->key.op == OP_ASSIGN_FINAL)
2251 event->group_final = true;
1edefa4f 2252 event->group_set = true;
912541b0 2253 event->gid = cur->key.gid;
b4ba2fe3
YW
2254 log_device_debug(dev, "GROUP %u %s:%u",
2255 event->gid,
2256 rules_str(rules, rule->rule.filename_off),
2257 rule->rule.filename_line);
912541b0
KS
2258 break;
2259 case TK_A_MODE_ID:
2260 if (event->mode_final)
2261 break;
2262 if (cur->key.op == OP_ASSIGN_FINAL)
2263 event->mode_final = true;
2264 event->mode_set = true;
2265 event->mode = cur->key.mode;
b4ba2fe3
YW
2266 log_device_debug(dev, "MODE %#o %s:%u",
2267 event->mode,
2268 rules_str(rules, rule->rule.filename_off),
2269 rule->rule.filename_line);
912541b0 2270 break;
c26547d6 2271 case TK_A_SECLABEL: {
d838e145 2272 _cleanup_free_ char *name = NULL, *label = NULL;
4f985bd8 2273 char label_str[UTIL_LINE_SIZE] = {};
c26547d6 2274
d838e145
YW
2275 name = strdup(rules_str(rules, cur->key.attr_off));
2276 if (!name)
2277 return log_oom();
2278
e20a9171 2279 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), label_str, sizeof(label_str), false);
d838e145
YW
2280 if (!isempty(label_str))
2281 label = strdup(label_str);
4f985bd8 2282 else
d838e145
YW
2283 label = strdup(rules_str(rules, cur->key.value_off));
2284 if (!label)
2285 return log_oom();
4f985bd8 2286
4c701096 2287 if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
d838e145
YW
2288 hashmap_clear_free_free(event->seclabel_list);
2289
2290 r = hashmap_ensure_allocated(&event->seclabel_list, NULL);
2291 if (r < 0)
2292 return log_oom();
2293
2294 r = hashmap_put(event->seclabel_list, name, label);
2295 if (r < 0)
2296 return log_oom();
b4ba2fe3
YW
2297 log_device_debug(dev, "SECLABEL{%s}='%s' %s:%u",
2298 name, label,
2299 rules_str(rules, rule->rule.filename_off),
2300 rule->rule.filename_line);
a6ca3c19
ZJS
2301 name = label = NULL;
2302
c26547d6
KS
2303 break;
2304 }
912541b0 2305 case TK_A_ENV: {
07845c14 2306 char value_new[UTIL_NAME_SIZE];
cf697ec0 2307 const char *name, *value_old;
912541b0 2308
cf697ec0
YW
2309 name = rules_str(rules, cur->key.attr_off);
2310 val = rules_str(rules, cur->key.value_off);
2311 if (val[0] == '\0') {
07845c14
KS
2312 if (cur->key.op == OP_ADD)
2313 break;
cf697ec0 2314 device_add_property(dev, name, NULL);
07845c14 2315 break;
912541b0 2316 }
07845c14 2317
cf697ec0
YW
2318 if (cur->key.op == OP_ADD &&
2319 sd_device_get_property_value(dev, name, &value_old) >= 0) {
07845c14
KS
2320 char temp[UTIL_NAME_SIZE];
2321
2322 /* append value separated by space */
cf697ec0 2323 udev_event_apply_format(event, val, temp, sizeof(temp), false);
d5a89d7d 2324 strscpyl(value_new, sizeof(value_new), value_old, " ", temp, NULL);
07845c14 2325 } else
cf697ec0 2326 udev_event_apply_format(event, val, value_new, sizeof(value_new), false);
07845c14 2327
cf697ec0 2328 device_add_property(dev, name, value_new);
912541b0
KS
2329 break;
2330 }
2331 case TK_A_TAG: {
2332 char tag[UTIL_PATH_SIZE];
2333 const char *p;
2334
e20a9171 2335 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), tag, sizeof(tag), false);
4c701096 2336 if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
cf697ec0 2337 device_cleanup_tags(dev);
912541b0
KS
2338 for (p = tag; *p != '\0'; p++) {
2339 if ((*p >= 'a' && *p <= 'z') ||
2340 (*p >= 'A' && *p <= 'Z') ||
2341 (*p >= '0' && *p <= '9') ||
4c701096 2342 IN_SET(*p, '-', '_'))
912541b0 2343 continue;
b4ba2fe3 2344 log_device_error(dev, "Ignoring invalid tag name '%s'", tag);
912541b0
KS
2345 break;
2346 }
8e3ba377 2347 if (cur->key.op == OP_REMOVE)
cf697ec0 2348 device_remove_tag(dev, tag);
8e3ba377 2349 else
cf697ec0 2350 device_add_tag(dev, tag);
912541b0
KS
2351 break;
2352 }
2353 case TK_A_NAME: {
912541b0 2354 char name_str[UTIL_PATH_SIZE];
cf697ec0 2355 const char *name;
912541b0
KS
2356 int count;
2357
cf697ec0 2358 name = rules_str(rules, cur->key.value_off);
912541b0
KS
2359 if (event->name_final)
2360 break;
2361 if (cur->key.op == OP_ASSIGN_FINAL)
2362 event->name_final = true;
e20a9171 2363 udev_event_apply_format(event, name, name_str, sizeof(name_str), false);
4c701096 2364 if (IN_SET(esc, ESCAPE_UNSET, ESCAPE_REPLACE)) {
912541b0
KS
2365 count = util_replace_chars(name_str, "/");
2366 if (count > 0)
b4ba2fe3 2367 log_device_debug(dev, "Replaced %i character(s) from result of NAME=\"%s\"", count, name);
912541b0 2368 }
cf697ec0
YW
2369 if (sd_device_get_devnum(dev, NULL) >= 0 &&
2370 (sd_device_get_devname(dev, &val) < 0 ||
2371 !streq(name_str, val + STRLEN("/dev/")))) {
b4ba2fe3
YW
2372 log_device_error(dev, "Kernel device nodes cannot be renamed, ignoring NAME=\"%s\"; please fix it in %s:%u\n",
2373 name,
2374 rules_str(rules, rule->rule.filename_off),
2375 rule->rule.filename_line);
6ada823a 2376 break;
0ecfcbd4 2377 }
d838e145
YW
2378 if (free_and_strdup(&event->name, name_str) < 0)
2379 return log_oom();
2380
b4ba2fe3
YW
2381 log_device_debug(dev, "NAME '%s' %s:%u",
2382 event->name,
2383 rules_str(rules, rule->rule.filename_off),
2384 rule->rule.filename_line);
912541b0
KS
2385 break;
2386 }
2387 case TK_A_DEVLINK: {
cf697ec0 2388 char temp[UTIL_PATH_SIZE], filename[UTIL_PATH_SIZE], *pos, *next;
912541b0
KS
2389 int count = 0;
2390
2391 if (event->devlink_final)
2392 break;
cf697ec0 2393 if (sd_device_get_devnum(dev, NULL) < 0)
912541b0
KS
2394 break;
2395 if (cur->key.op == OP_ASSIGN_FINAL)
2396 event->devlink_final = true;
4c701096 2397 if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
cf697ec0 2398 device_cleanup_devlinks(dev);
912541b0
KS
2399
2400 /* allow multiple symlinks separated by spaces */
0a10235e 2401 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), temp, sizeof(temp), esc != ESCAPE_NONE);
912541b0
KS
2402 if (esc == ESCAPE_UNSET)
2403 count = util_replace_chars(temp, "/ ");
2404 else if (esc == ESCAPE_REPLACE)
2405 count = util_replace_chars(temp, "/");
2406 if (count > 0)
b4ba2fe3 2407 log_device_debug(dev, "Replaced %i character(s) from result of LINK" , count);
912541b0
KS
2408 pos = temp;
2409 while (isspace(pos[0]))
2410 pos++;
2411 next = strchr(pos, ' ');
cf697ec0 2412 while (next) {
912541b0 2413 next[0] = '\0';
b4ba2fe3
YW
2414 log_device_debug(dev, "LINK '%s' %s:%u", pos,
2415 rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
d5a89d7d 2416 strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
cf697ec0 2417 device_add_devlink(dev, filename);
912541b0
KS
2418 while (isspace(next[1]))
2419 next++;
2420 pos = &next[1];
2421 next = strchr(pos, ' ');
2422 }
2423 if (pos[0] != '\0') {
b4ba2fe3
YW
2424 log_device_debug(dev, "LINK '%s' %s:%u", pos,
2425 rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
d5a89d7d 2426 strscpyl(filename, sizeof(filename), "/dev/", pos, NULL);
cf697ec0 2427 device_add_devlink(dev, filename);
912541b0
KS
2428 }
2429 break;
2430 }
2431 case TK_A_ATTR: {
cf697ec0 2432 char attr[UTIL_PATH_SIZE], value[UTIL_NAME_SIZE];
fdd21be6 2433 _cleanup_fclose_ FILE *f = NULL;
cf697ec0 2434 const char *key_name;
912541b0 2435
cf697ec0 2436 key_name = rules_str(rules, cur->key.attr_off);
76b9bdd9 2437 if (util_resolve_subsys_kernel(key_name, attr, sizeof(attr), false) < 0 &&
cf697ec0
YW
2438 sd_device_get_syspath(dev, &val) >= 0)
2439 strscpyl(attr, sizeof(attr), val, "/", key_name, NULL);
912541b0
KS
2440 attr_subst_subdir(attr, sizeof(attr));
2441
e20a9171 2442 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
b4ba2fe3
YW
2443 log_device_debug(dev, "ATTR '%s' writing '%s' %s:%u", attr, value,
2444 rules_str(rules, rule->rule.filename_off),
2445 rule->rule.filename_line);
47ef94ac 2446 f = fopen(attr, "we");
67e4b385 2447 if (!f)
b4ba2fe3 2448 log_device_error_errno(dev, errno, "Failed to open ATTR{%s} for writing: %m", attr);
fdd21be6 2449 else if (fprintf(f, "%s", value) <= 0)
b4ba2fe3 2450 log_device_error_errno(dev, errno, "Failed to write ATTR{%s}: %m", attr);
912541b0
KS
2451 break;
2452 }
f4cf2e5b 2453 case TK_A_SYSCTL: {
cf697ec0 2454 char filename[UTIL_PATH_SIZE], value[UTIL_NAME_SIZE];
f4cf2e5b 2455
e20a9171 2456 udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename), false);
f4cf2e5b 2457 sysctl_normalize(filename);
e20a9171 2458 udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value), false);
b4ba2fe3
YW
2459 log_device_debug(dev, "SYSCTL '%s' writing '%s' %s:%u", filename, value,
2460 rules_str(rules, rule->rule.filename_off), rule->rule.filename_line);
f4cf2e5b
KS
2461 r = sysctl_write(filename, value);
2462 if (r < 0)
b4ba2fe3 2463 log_device_error_errno(dev, r, "Failed to write SYSCTL{%s}='%s': %m", filename, value);
f4cf2e5b
KS
2464 break;
2465 }
83cd6b75
KS
2466 case TK_A_RUN_BUILTIN:
2467 case TK_A_RUN_PROGRAM: {
29448498
YW
2468 _cleanup_free_ char *cmd = NULL;
2469
e924c60f
YW
2470 if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL))
2471 hashmap_clear_free_key(event->run_list);
29448498
YW
2472
2473 r = hashmap_ensure_allocated(&event->run_list, NULL);
2474 if (r < 0)
2475 return log_oom();
2476
2477 cmd = strdup(rules_str(rules, cur->key.value_off));
2478 if (!cmd)
2479 return log_oom();
2480
2481 r = hashmap_put(event->run_list, cmd, INT_TO_PTR(cur->key.builtin_cmd));
2482 if (r < 0)
2483 return log_oom();
2484
2485 cmd = NULL;
83cd6b75 2486
b4ba2fe3
YW
2487 log_device_debug(dev, "RUN '%s' %s:%u",
2488 rules_str(rules, cur->key.value_off),
2489 rules_str(rules, rule->rule.filename_off),
2490 rule->rule.filename_line);
912541b0
KS
2491 break;
2492 }
2493 case TK_A_GOTO:
2494 if (cur->key.rule_goto == 0)
2495 break;
2496 cur = &rules->tokens[cur->key.rule_goto];
2497 continue;
2498 case TK_END:
d838e145 2499 return 0;
912541b0
KS
2500
2501 case TK_M_PARENTS_MIN:
2502 case TK_M_PARENTS_MAX:
2503 case TK_M_MAX:
2504 case TK_UNSET:
b4ba2fe3 2505 log_device_error(dev, "Wrong type %u", cur->type);
912541b0
KS
2506 goto nomatch;
2507 }
2508
2509 cur++;
2510 continue;
2511 nomatch:
2512 /* fast-forward to next rule */
2513 cur = rule + rule->rule.token_count;
912541b0 2514 }
d838e145
YW
2515
2516 return 0;
6880b25d 2517}
761dfddc 2518
9a07157d 2519int udev_rules_apply_static_dev_perms(UdevRules *rules) {
912541b0
KS
2520 struct token *cur;
2521 struct token *rule;
2522 uid_t uid = 0;
2523 gid_t gid = 0;
2524 mode_t mode = 0;
84b6ad70
TG
2525 _cleanup_strv_free_ char **tags = NULL;
2526 char **t;
2527 FILE *f = NULL;
2528 _cleanup_free_ char *path = NULL;
fdd21be6 2529 int r;
912541b0 2530
67e4b385 2531 if (!rules->tokens)
84b6ad70 2532 return 0;
912541b0
KS
2533
2534 cur = &rules->tokens[0];
2535 rule = cur;
2536 for (;;) {
2537 switch (cur->type) {
2538 case TK_RULE:
2539 /* current rule */
2540 rule = cur;
2541
2542 /* skip rules without a static_node tag */
2543 if (!rule->rule.has_static_node)
2544 goto next;
2545
2546 uid = 0;
2547 gid = 0;
2548 mode = 0;
97b11eed 2549 tags = strv_free(tags);
912541b0
KS
2550 break;
2551 case TK_A_OWNER_ID:
2552 uid = cur->key.uid;
2553 break;
2554 case TK_A_GROUP_ID:
2555 gid = cur->key.gid;
2556 break;
2557 case TK_A_MODE_ID:
2558 mode = cur->key.mode;
84b6ad70
TG
2559 break;
2560 case TK_A_TAG:
2561 r = strv_extend(&tags, rules_str(rules, cur->key.value_off));
2562 if (r < 0)
2563 goto finish;
2564
912541b0
KS
2565 break;
2566 case TK_A_STATIC_NODE: {
84b6ad70
TG
2567 char device_node[UTIL_PATH_SIZE];
2568 char tags_dir[UTIL_PATH_SIZE];
2569 char tag_symlink[UTIL_PATH_SIZE];
912541b0
KS
2570 struct stat stats;
2571
2572 /* we assure, that the permissions tokens are sorted before the static token */
ca2bb160 2573
67e4b385 2574 if (mode == 0 && uid == 0 && gid == 0 && !tags)
912541b0 2575 goto next;
d6f116a7 2576
84b6ad70 2577 strscpyl(device_node, sizeof(device_node), "/dev/", rules_str(rules, cur->key.value_off), NULL);
76b9bdd9 2578 if (stat(device_node, &stats) < 0)
ca2bb160
KS
2579 break;
2580 if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode))
2581 break;
84b6ad70 2582
d6f116a7 2583 /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
84b6ad70 2584 if (tags) {
84b6ad70
TG
2585 STRV_FOREACH(t, tags) {
2586 _cleanup_free_ char *unescaped_filename = NULL;
2587
2588 strscpyl(tags_dir, sizeof(tags_dir), "/run/udev/static_node-tags/", *t, "/", NULL);
2589 r = mkdir_p(tags_dir, 0755);
f647962d 2590 if (r < 0)
8c19dc54 2591 return log_error_errno(r, "Failed to create %s: %m", tags_dir);
84b6ad70
TG
2592
2593 unescaped_filename = xescape(rules_str(rules, cur->key.value_off), "/.");
2594
2595 strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL);
2596 r = symlink(device_node, tag_symlink);
4a62c710 2597 if (r < 0 && errno != EEXIST)
8c19dc54 2598 return log_error_errno(errno, "Failed to create symlink %s -> %s: %m",
4a62c710 2599 tag_symlink, device_node);
84b6ad70
TG
2600 }
2601 }
2602
15a72200
TG
2603 /* don't touch the permissions if only the tags were set */
2604 if (mode == 0 && uid == 0 && gid == 0)
d6f116a7
KS
2605 break;
2606
912541b0
KS
2607 if (mode == 0) {
2608 if (gid > 0)
2609 mode = 0660;
2610 else
2611 mode = 0600;
2612 }
2613 if (mode != (stats.st_mode & 01777)) {
490f0087 2614 r = chmod(device_node, mode);
25f027c5
ZJS
2615 if (r < 0)
2616 return log_error_errno(errno, "Failed to chmod '%s' %#o: %m",
2617 device_node, mode);
2618 else
9f6445e3 2619 log_debug("chmod '%s' %#o", device_node, mode);
912541b0
KS
2620 }
2621
2622 if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) {
490f0087 2623 r = chown(device_node, uid, gid);
25f027c5
ZJS
2624 if (r < 0)
2625 return log_error_errno(errno, "Failed to chown '%s' %u %u: %m",
2626 device_node, uid, gid);
2627 else
9f6445e3 2628 log_debug("chown '%s' %u %u", device_node, uid, gid);
912541b0
KS
2629 }
2630
84b6ad70 2631 utimensat(AT_FDCWD, device_node, NULL, 0);
912541b0
KS
2632 break;
2633 }
2634 case TK_END:
84b6ad70 2635 goto finish;
912541b0
KS
2636 }
2637
2638 cur++;
2639 continue;
761dfddc 2640next:
912541b0
KS
2641 /* fast-forward to next rule */
2642 cur = rule + rule->rule.token_count;
2643 continue;
2644 }
84b6ad70
TG
2645
2646finish:
2647 if (f) {
2648 fflush(f);
2649 fchmod(fileno(f), 0644);
2650 if (ferror(f) || rename(path, "/run/udev/static_node-tags") < 0) {
fdd21be6
ZJS
2651 unlink_noerrno("/run/udev/static_node-tags");
2652 unlink_noerrno(path);
2653 return -errno;
84b6ad70 2654 }
84b6ad70
TG
2655 }
2656
fdd21be6 2657 return 0;
761dfddc 2658}