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