]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-device/device-private.c
Merge pull request #11960 from mrc0mmand/more-fuzzers
[thirdparty/systemd.git] / src / libsystemd / sd-device / device-private.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
57fa1d09
TG
2
3#include <ctype.h>
57fa1d09 4#include <net/if.h>
07630cea 5#include <sys/types.h>
57fa1d09
TG
6
7#include "sd-device.h"
8
b5efdb8a 9#include "alloc-util.h"
57fa1d09
TG
10#include "device-internal.h"
11#include "device-private.h"
07630cea 12#include "device-util.h"
3ffd4af2 13#include "fd-util.h"
07630cea 14#include "fileio.h"
f4f15635 15#include "fs-util.h"
07630cea
LP
16#include "hashmap.h"
17#include "macro.h"
18#include "mkdir.h"
6bedfcbb 19#include "parse-util.h"
07630cea 20#include "path-util.h"
07630cea 21#include "set.h"
8b43440b 22#include "string-table.h"
07630cea
LP
23#include "string-util.h"
24#include "strv.h"
25#include "strxcpyx.h"
e4de7287 26#include "tmpfile-util.h"
ee104e11 27#include "user-util.h"
07630cea 28#include "util.h"
57fa1d09
TG
29
30int device_add_property(sd_device *device, const char *key, const char *value) {
31 int r;
32
33 assert(device);
34 assert(key);
35
36 r = device_add_property_aux(device, key, value, false);
37 if (r < 0)
38 return r;
39
40 if (key[0] != '.') {
41 r = device_add_property_aux(device, key, value, true);
42 if (r < 0)
43 return r;
44 }
45
46 return 0;
47}
48
57fa1d09
TG
49void device_set_devlink_priority(sd_device *device, int priority) {
50 assert(device);
51
52 device->devlink_priority = priority;
53}
54
55void device_set_is_initialized(sd_device *device) {
56 assert(device);
57
58 device->is_initialized = true;
59}
60
61int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) {
dc5042c0 62 usec_t when;
57fa1d09
TG
63
64 assert(device);
65
66 if (device_old && device_old->usec_initialized > 0)
dc5042c0 67 when = device_old->usec_initialized;
57fa1d09 68 else
dc5042c0 69 when = now(CLOCK_MONOTONIC);
57fa1d09 70
dc5042c0 71 return device_set_usec_initialized(device, when);
57fa1d09
TG
72}
73
57fa1d09
TG
74uint64_t device_get_properties_generation(sd_device *device) {
75 assert(device);
76
77 return device->properties_generation;
78}
79
80uint64_t device_get_tags_generation(sd_device *device) {
81 assert(device);
82
83 return device->tags_generation;
84}
85
86uint64_t device_get_devlinks_generation(sd_device *device) {
87 assert(device);
88
89 return device->devlinks_generation;
90}
91
92int device_get_devnode_mode(sd_device *device, mode_t *mode) {
93 int r;
94
95 assert(device);
57fa1d09
TG
96
97 r = device_read_db(device);
98 if (r < 0)
99 return r;
100
dcfbde3a
YW
101 if (device->devmode == (mode_t) -1)
102 return -ENOENT;
103
78ffb476
YW
104 if (mode)
105 *mode = device->devmode;
57fa1d09
TG
106
107 return 0;
108}
109
110int device_get_devnode_uid(sd_device *device, uid_t *uid) {
111 int r;
112
113 assert(device);
57fa1d09
TG
114
115 r = device_read_db(device);
116 if (r < 0)
117 return r;
118
dcfbde3a
YW
119 if (device->devuid == (uid_t) -1)
120 return -ENOENT;
121
78ffb476
YW
122 if (uid)
123 *uid = device->devuid;
57fa1d09
TG
124
125 return 0;
126}
127
128static int device_set_devuid(sd_device *device, const char *uid) {
129 unsigned u;
130 int r;
131
132 assert(device);
133 assert(uid);
134
135 r = safe_atou(uid, &u);
136 if (r < 0)
137 return r;
138
139 r = device_add_property_internal(device, "DEVUID", uid);
140 if (r < 0)
141 return r;
142
143 device->devuid = u;
144
145 return 0;
146}
147
148int device_get_devnode_gid(sd_device *device, gid_t *gid) {
149 int r;
150
151 assert(device);
57fa1d09
TG
152
153 r = device_read_db(device);
154 if (r < 0)
155 return r;
156
dcfbde3a
YW
157 if (device->devgid == (gid_t) -1)
158 return -ENOENT;
159
78ffb476
YW
160 if (gid)
161 *gid = device->devgid;
57fa1d09
TG
162
163 return 0;
164}
165
166static int device_set_devgid(sd_device *device, const char *gid) {
167 unsigned g;
168 int r;
169
170 assert(device);
171 assert(gid);
172
173 r = safe_atou(gid, &g);
174 if (r < 0)
175 return r;
176
177 r = device_add_property_internal(device, "DEVGID", gid);
178 if (r < 0)
179 return r;
180
181 device->devgid = g;
182
183 return 0;
184}
185
401cb614 186static int device_amend(sd_device *device, const char *key, const char *value) {
57fa1d09
TG
187 int r;
188
189 assert(device);
190 assert(key);
191 assert(value);
192
193 if (streq(key, "DEVPATH")) {
194 char *path;
195
196 path = strjoina("/sys", value);
197
198 /* the caller must verify or trust this data (e.g., if it comes from the kernel) */
199 r = device_set_syspath(device, path, false);
200 if (r < 0)
c7d54dae 201 return log_device_debug_errno(device, r, "sd-device: Failed to set syspath to '%s': %m", path);
57fa1d09
TG
202 } else if (streq(key, "SUBSYSTEM")) {
203 r = device_set_subsystem(device, value);
204 if (r < 0)
c7d54dae 205 return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem to '%s': %m", value);
57fa1d09
TG
206 } else if (streq(key, "DEVTYPE")) {
207 r = device_set_devtype(device, value);
208 if (r < 0)
c7d54dae 209 return log_device_debug_errno(device, r, "sd-device: Failed to set devtype to '%s': %m", value);
57fa1d09
TG
210 } else if (streq(key, "DEVNAME")) {
211 r = device_set_devname(device, value);
212 if (r < 0)
c7d54dae 213 return log_device_debug_errno(device, r, "sd-device: Failed to set devname to '%s': %m", value);
57fa1d09 214 } else if (streq(key, "USEC_INITIALIZED")) {
dc5042c0
ZJS
215 usec_t t;
216
217 r = safe_atou64(value, &t);
218 if (r < 0)
219 return log_device_debug_errno(device, r, "sd-device: Failed to parse timestamp '%s': %m", value);
220
221 r = device_set_usec_initialized(device, t);
57fa1d09 222 if (r < 0)
c7d54dae 223 return log_device_debug_errno(device, r, "sd-device: Failed to set usec-initialized to '%s': %m", value);
57fa1d09
TG
224 } else if (streq(key, "DRIVER")) {
225 r = device_set_driver(device, value);
226 if (r < 0)
c7d54dae 227 return log_device_debug_errno(device, r, "sd-device: Failed to set driver to '%s': %m", value);
57fa1d09
TG
228 } else if (streq(key, "IFINDEX")) {
229 r = device_set_ifindex(device, value);
230 if (r < 0)
c7d54dae 231 return log_device_debug_errno(device, r, "sd-device: Failed to set ifindex to '%s': %m", value);
57fa1d09
TG
232 } else if (streq(key, "DEVMODE")) {
233 r = device_set_devmode(device, value);
234 if (r < 0)
c7d54dae 235 return log_device_debug_errno(device, r, "sd-device: Failed to set devmode to '%s': %m", value);
57fa1d09
TG
236 } else if (streq(key, "DEVUID")) {
237 r = device_set_devuid(device, value);
238 if (r < 0)
c7d54dae 239 return log_device_debug_errno(device, r, "sd-device: Failed to set devuid to '%s': %m", value);
57fa1d09
TG
240 } else if (streq(key, "DEVGID")) {
241 r = device_set_devgid(device, value);
242 if (r < 0)
c7d54dae 243 return log_device_debug_errno(device, r, "sd-device: Failed to set devgid to '%s': %m", value);
57fa1d09 244 } else if (streq(key, "DEVLINKS")) {
4df4fd11
TG
245 const char *word, *state;
246 size_t l;
57fa1d09 247
4df4fd11 248 FOREACH_WORD(word, l, value, state) {
de9b34b6 249 char devlink[l + 1];
57fa1d09 250
de9b34b6
TG
251 strncpy(devlink, word, l);
252 devlink[l] = '\0';
57fa1d09 253
4df4fd11 254 r = device_add_devlink(device, devlink);
57fa1d09 255 if (r < 0)
c7d54dae 256 return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", devlink);
57fa1d09
TG
257 }
258 } else if (streq(key, "TAGS")) {
4df4fd11
TG
259 const char *word, *state;
260 size_t l;
57fa1d09 261
4df4fd11 262 FOREACH_WORD_SEPARATOR(word, l, value, ":", state) {
de9b34b6 263 char tag[l + 1];
57fa1d09 264
db2f8a2e 265 (void) strncpy(tag, word, l);
de9b34b6 266 tag[l] = '\0';
57fa1d09 267
4df4fd11 268 r = device_add_tag(device, tag);
57fa1d09 269 if (r < 0)
c7d54dae 270 return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", tag);
57fa1d09
TG
271 }
272 } else {
273 r = device_add_property_internal(device, key, value);
274 if (r < 0)
c7d54dae 275 return log_device_debug_errno(device, r, "sd-device: Failed to add property '%s=%s': %m", key, value);
57fa1d09
TG
276 }
277
278 return 0;
279}
280
281static const char* const device_action_table[_DEVICE_ACTION_MAX] = {
282 [DEVICE_ACTION_ADD] = "add",
283 [DEVICE_ACTION_REMOVE] = "remove",
284 [DEVICE_ACTION_CHANGE] = "change",
285 [DEVICE_ACTION_MOVE] = "move",
286 [DEVICE_ACTION_ONLINE] = "online",
287 [DEVICE_ACTION_OFFLINE] = "offline",
9a39e1ce
LP
288 [DEVICE_ACTION_BIND] = "bind",
289 [DEVICE_ACTION_UNBIND] = "unbind",
57fa1d09
TG
290};
291
292DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction);
293
294static int device_append(sd_device *device, char *key, const char **_major, const char **_minor, uint64_t *_seqnum,
295 DeviceAction *_action) {
296 DeviceAction action = _DEVICE_ACTION_INVALID;
297 uint64_t seqnum = 0;
298 const char *major = NULL, *minor = NULL;
299 char *value;
300 int r;
301
302 assert(device);
303 assert(key);
304 assert(_major);
305 assert(_minor);
306 assert(_seqnum);
307 assert(_action);
308
309 value = strchr(key, '=');
310 if (!value) {
c7d54dae 311 log_device_debug(device, "sd-device: Not a key-value pair: '%s'", key);
57fa1d09
TG
312 return -EINVAL;
313 }
314
315 *value = '\0';
316
317 value++;
318
319 if (streq(key, "MAJOR"))
320 major = value;
321 else if (streq(key, "MINOR"))
322 minor = value;
323 else {
324 if (streq(key, "ACTION")) {
325 action = device_action_from_string(value);
326 if (action == _DEVICE_ACTION_INVALID)
327 return -EINVAL;
328 } else if (streq(key, "SEQNUM")) {
329 r = safe_atou64(value, &seqnum);
330 if (r < 0)
331 return r;
332 else if (seqnum == 0)
333 /* kernel only sends seqnum > 0 */
334 return -EINVAL;
335 }
336
401cb614 337 r = device_amend(device, key, value);
57fa1d09
TG
338 if (r < 0)
339 return r;
340 }
341
342 if (major != 0)
343 *_major = major;
344
345 if (minor != 0)
346 *_minor = minor;
347
348 if (action != _DEVICE_ACTION_INVALID)
349 *_action = action;
350
351 if (seqnum > 0)
352 *_seqnum = seqnum;
353
354 return 0;
355}
356
357void device_seal(sd_device *device) {
358 assert(device);
359
360 device->sealed = true;
361}
362
363static int device_verify(sd_device *device, DeviceAction action, uint64_t seqnum) {
364 assert(device);
365
366 if (!device->devpath || !device->subsystem || action == _DEVICE_ACTION_INVALID || seqnum == 0) {
c7d54dae 367 log_device_debug(device, "sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum.");
57fa1d09
TG
368 return -EINVAL;
369 }
370
371 device->sealed = true;
372
373 return 0;
374}
375
376int device_new_from_strv(sd_device **ret, char **strv) {
4afd3348 377 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
57fa1d09
TG
378 char **key;
379 const char *major = NULL, *minor = NULL;
380 DeviceAction action = _DEVICE_ACTION_INVALID;
8d578a2e 381 uint64_t seqnum = 0;
57fa1d09
TG
382 int r;
383
384 assert(ret);
385 assert(strv);
386
387 r = device_new_aux(&device);
388 if (r < 0)
389 return r;
390
391 STRV_FOREACH(key, strv) {
392 r = device_append(device, *key, &major, &minor, &seqnum, &action);
393 if (r < 0)
394 return r;
395 }
396
397 if (major) {
398 r = device_set_devnum(device, major, minor);
399 if (r < 0)
c7d54dae 400 return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor);
57fa1d09
TG
401 }
402
403 r = device_verify(device, action, seqnum);
404 if (r < 0)
405 return r;
406
1cc6c93a 407 *ret = TAKE_PTR(device);
57fa1d09
TG
408
409 return 0;
410}
411
412int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) {
4afd3348 413 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
57fa1d09
TG
414 const char *major = NULL, *minor = NULL;
415 DeviceAction action = _DEVICE_ACTION_INVALID;
8d578a2e 416 uint64_t seqnum = 0;
57fa1d09
TG
417 unsigned i = 0;
418 int r;
419
420 assert(ret);
421 assert(nulstr);
422 assert(len);
423
424 r = device_new_aux(&device);
425 if (r < 0)
426 return r;
427
428 while (i < len) {
429 char *key;
430 const char *end;
431
432 key = (char*)&nulstr[i];
433 end = memchr(key, '\0', len - i);
434 if (!end) {
c7d54dae 435 log_device_debug(device, "sd-device: Failed to parse nulstr");
57fa1d09
TG
436 return -EINVAL;
437 }
438 i += end - key + 1;
439
440 r = device_append(device, key, &major, &minor, &seqnum, &action);
441 if (r < 0)
442 return r;
443 }
444
445 if (major) {
446 r = device_set_devnum(device, major, minor);
447 if (r < 0)
c7d54dae 448 return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor);
57fa1d09
TG
449 }
450
451 r = device_verify(device, action, seqnum);
452 if (r < 0)
453 return r;
454
1cc6c93a 455 *ret = TAKE_PTR(device);
57fa1d09
TG
456
457 return 0;
458}
459
460static int device_update_properties_bufs(sd_device *device) {
461 const char *val, *prop;
ccc1002a
TG
462 _cleanup_free_ char **buf_strv = NULL;
463 _cleanup_free_ uint8_t *buf_nulstr = NULL;
d854ba50 464 size_t allocated_nulstr = 0;
ccc1002a 465 size_t nulstr_len = 0, num = 0, i = 0;
57fa1d09
TG
466
467 assert(device);
468
aa20f49a
TG
469 if (!device->properties_buf_outdated)
470 return 0;
471
57fa1d09
TG
472 FOREACH_DEVICE_PROPERTY(device, prop, val) {
473 size_t len = 0;
474
475 len = strlen(prop) + 1 + strlen(val);
476
477 buf_nulstr = GREEDY_REALLOC0(buf_nulstr, allocated_nulstr, nulstr_len + len + 2);
478 if (!buf_nulstr)
479 return -ENOMEM;
480
57fa1d09
TG
481 strscpyl((char *)buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL);
482 nulstr_len += len + 1;
d854ba50 483 ++num;
57fa1d09
TG
484 }
485
ccc1002a
TG
486 /* build buf_strv from buf_nulstr */
487 buf_strv = new0(char *, num + 1);
488 if (!buf_strv)
489 return -ENOMEM;
d854ba50 490
d854ba50 491 NULSTR_FOREACH(val, (char*) buf_nulstr) {
ccc1002a 492 buf_strv[i] = (char *) val;
d854ba50
MP
493 assert(i < num);
494 i++;
495 }
57fa1d09 496
f9ecfd3b 497 free_and_replace(device->properties_nulstr, buf_nulstr);
ccc1002a 498 device->properties_nulstr_len = nulstr_len;
f9ecfd3b 499 free_and_replace(device->properties_strv, buf_strv);
ccc1002a 500
57fa1d09
TG
501 device->properties_buf_outdated = false;
502
503 return 0;
504}
505
506int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len) {
507 int r;
508
509 assert(device);
510 assert(nulstr);
511 assert(len);
512
aa20f49a
TG
513 r = device_update_properties_bufs(device);
514 if (r < 0)
515 return r;
57fa1d09
TG
516
517 *nulstr = device->properties_nulstr;
518 *len = device->properties_nulstr_len;
519
520 return 0;
521}
522
523int device_get_properties_strv(sd_device *device, char ***strv) {
524 int r;
525
526 assert(device);
527 assert(strv);
528
529 r = device_update_properties_bufs(device);
530 if (r < 0)
531 return r;
532
533 *strv = device->properties_strv;
534
535 return 0;
536}
537
538int device_get_devlink_priority(sd_device *device, int *priority) {
539 int r;
540
541 assert(device);
542 assert(priority);
543
544 r = device_read_db(device);
545 if (r < 0)
546 return r;
547
548 *priority = device->devlink_priority;
549
550 return 0;
551}
552
553int device_get_watch_handle(sd_device *device, int *handle) {
554 int r;
555
556 assert(device);
57fa1d09
TG
557
558 r = device_read_db(device);
559 if (r < 0)
560 return r;
561
dcfbde3a
YW
562 if (device->watch_handle < 0)
563 return -ENOENT;
564
78ffb476
YW
565 if (handle)
566 *handle = device->watch_handle;
57fa1d09
TG
567
568 return 0;
569}
570
571void device_set_watch_handle(sd_device *device, int handle) {
572 assert(device);
573
574 device->watch_handle = handle;
575}
576
577int device_rename(sd_device *device, const char *name) {
578 _cleanup_free_ char *dirname = NULL;
579 char *new_syspath;
580 const char *interface;
581 int r;
582
583 assert(device);
584 assert(name);
585
586 dirname = dirname_malloc(device->syspath);
587 if (!dirname)
588 return -ENOMEM;
589
590 new_syspath = strjoina(dirname, "/", name);
591
592 /* the user must trust that the new name is correct */
593 r = device_set_syspath(device, new_syspath, false);
594 if (r < 0)
595 return r;
596
597 r = sd_device_get_property_value(device, "INTERFACE", &interface);
598 if (r >= 0) {
f7b1c8d1
EV
599 /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */
600 r = device_add_property_internal(device, "INTERFACE_OLD", interface);
57fa1d09
TG
601 if (r < 0)
602 return r;
603
f7b1c8d1 604 r = device_add_property_internal(device, "INTERFACE", name);
57fa1d09
TG
605 if (r < 0)
606 return r;
607 } else if (r != -ENOENT)
608 return r;
609
610 return 0;
611}
612
613int device_shallow_clone(sd_device *old_device, sd_device **new_device) {
4afd3348 614 _cleanup_(sd_device_unrefp) sd_device *ret = NULL;
57fa1d09
TG
615 int r;
616
617 assert(old_device);
618 assert(new_device);
619
620 r = device_new_aux(&ret);
621 if (r < 0)
622 return r;
623
624 r = device_set_syspath(ret, old_device->syspath, false);
625 if (r < 0)
626 return r;
627
628 r = device_set_subsystem(ret, old_device->subsystem);
629 if (r < 0)
630 return r;
631
632 ret->devnum = old_device->devnum;
633
1cc6c93a 634 *new_device = TAKE_PTR(ret);
57fa1d09
TG
635
636 return 0;
637}
638
639int device_clone_with_db(sd_device *old_device, sd_device **new_device) {
4afd3348 640 _cleanup_(sd_device_unrefp) sd_device *ret = NULL;
57fa1d09
TG
641 int r;
642
643 assert(old_device);
644 assert(new_device);
645
646 r = device_shallow_clone(old_device, &ret);
647 if (r < 0)
648 return r;
649
650 r = device_read_db(ret);
651 if (r < 0)
652 return r;
653
654 ret->sealed = true;
655
1cc6c93a 656 *new_device = TAKE_PTR(ret);
57fa1d09
TG
657
658 return 0;
659}
660
661int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action) {
4afd3348 662 _cleanup_(sd_device_unrefp) sd_device *ret = NULL;
57fa1d09
TG
663 int r;
664
665 assert(new_device);
666 assert(syspath);
667 assert(action);
668
669 r = sd_device_new_from_syspath(&ret, syspath);
670 if (r < 0)
671 return r;
672
673 r = device_read_uevent_file(ret);
674 if (r < 0)
675 return r;
676
677 r = device_add_property_internal(ret, "ACTION", action);
678 if (r < 0)
679 return r;
680
1cc6c93a 681 *new_device = TAKE_PTR(ret);
57fa1d09
TG
682
683 return 0;
684}
685
ad5944d7
YW
686int device_new_from_stat_rdev(sd_device **ret, const struct stat *st) {
687 char type;
688
689 assert(ret);
690 assert(st);
691
692 if (S_ISBLK(st->st_mode))
693 type = 'b';
694 else if (S_ISCHR(st->st_mode))
695 type = 'c';
696 else
697 return -ENOTTY;
698
699 return sd_device_new_from_devnum(ret, type, st->st_rdev);
700}
701
57fa1d09
TG
702int device_copy_properties(sd_device *device_dst, sd_device *device_src) {
703 const char *property, *value;
a3ce8136 704 Iterator i;
57fa1d09
TG
705 int r;
706
707 assert(device_dst);
708 assert(device_src);
709
a3ce8136
YW
710 r = device_properties_prepare(device_src);
711 if (r < 0)
712 return r;
713
5ce41697 714 ORDERED_HASHMAP_FOREACH_KEY(value, property, device_src->properties_db, i) {
a3ce8136
YW
715 r = device_add_property_aux(device_dst, property, value, true);
716 if (r < 0)
717 return r;
718 }
719
5ce41697 720 ORDERED_HASHMAP_FOREACH_KEY(value, property, device_src->properties, i) {
a3ce8136 721 r = device_add_property_aux(device_dst, property, value, false);
57fa1d09
TG
722 if (r < 0)
723 return r;
724 }
725
726 return 0;
727}
728
729void device_cleanup_tags(sd_device *device) {
730 assert(device);
731
732 set_free_free(device->tags);
733 device->tags = NULL;
734 device->property_tags_outdated = true;
313cefa1 735 device->tags_generation++;
57fa1d09
TG
736}
737
738void device_cleanup_devlinks(sd_device *device) {
739 assert(device);
740
741 set_free_free(device->devlinks);
742 device->devlinks = NULL;
743 device->property_devlinks_outdated = true;
313cefa1 744 device->devlinks_generation++;
57fa1d09
TG
745}
746
747void device_remove_tag(sd_device *device, const char *tag) {
748 assert(device);
749 assert(tag);
750
751 free(set_remove(device->tags, tag));
752 device->property_tags_outdated = true;
313cefa1 753 device->tags_generation++;
57fa1d09
TG
754}
755
756static int device_tag(sd_device *device, const char *tag, bool add) {
757 const char *id;
758 char *path;
759 int r;
760
761 assert(device);
762 assert(tag);
763
764 r = device_get_id_filename(device, &id);
765 if (r < 0)
766 return r;
767
768 path = strjoina("/run/udev/tags/", tag, "/", id);
769
770 if (add) {
771 r = touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444);
772 if (r < 0)
773 return r;
774 } else {
775 r = unlink(path);
776 if (r < 0 && errno != ENOENT)
777 return -errno;
778 }
779
780 return 0;
781}
782
783int device_tag_index(sd_device *device, sd_device *device_old, bool add) {
784 const char *tag;
785 int r = 0, k;
786
787 if (add && device_old) {
788 /* delete possible left-over tags */
789 FOREACH_DEVICE_TAG(device_old, tag) {
790 if (!sd_device_has_tag(device, tag)) {
791 k = device_tag(device_old, tag, false);
792 if (r >= 0 && k < 0)
793 r = k;
794 }
795 }
796 }
797
798 FOREACH_DEVICE_TAG(device, tag) {
799 k = device_tag(device, tag, add);
800 if (r >= 0 && k < 0)
801 r = k;
802 }
803
804 return r;
805}
806
807static bool device_has_info(sd_device *device) {
808 assert(device);
809
810 if (!set_isempty(device->devlinks))
811 return true;
812
813 if (device->devlink_priority != 0)
814 return true;
815
816 if (!ordered_hashmap_isempty(device->properties_db))
817 return true;
818
819 if (!set_isempty(device->tags))
820 return true;
821
822 if (device->watch_handle >= 0)
823 return true;
824
825 return false;
826}
827
828void device_set_db_persist(sd_device *device) {
829 assert(device);
830
831 device->db_persist = true;
832}
833
834int device_update_db(sd_device *device) {
835 const char *id;
836 char *path;
837 _cleanup_fclose_ FILE *f = NULL;
838 _cleanup_free_ char *path_tmp = NULL;
839 bool has_info;
840 int r;
841
842 assert(device);
843
844 has_info = device_has_info(device);
845
846 r = device_get_id_filename(device, &id);
847 if (r < 0)
848 return r;
849
850 path = strjoina("/run/udev/data/", id);
851
852 /* do not store anything for otherwise empty devices */
853 if (!has_info && major(device->devnum) == 0 && device->ifindex == 0) {
854 r = unlink(path);
855 if (r < 0 && errno != ENOENT)
856 return -errno;
857
858 return 0;
859 }
860
861 /* write a database file */
862 r = mkdir_parents(path, 0755);
863 if (r < 0)
864 return r;
865
866 r = fopen_temporary(path, &f, &path_tmp);
867 if (r < 0)
868 return r;
869
870 /*
871 * set 'sticky' bit to indicate that we should not clean the
872 * database when we transition from initramfs to the real root
873 */
874 if (device->db_persist) {
875 r = fchmod(fileno(f), 01644);
876 if (r < 0) {
877 r = -errno;
878 goto fail;
879 }
880 } else {
881 r = fchmod(fileno(f), 0644);
882 if (r < 0) {
883 r = -errno;
884 goto fail;
885 }
886 }
887
888 if (has_info) {
889 const char *property, *value, *tag;
890 Iterator i;
891
892 if (major(device->devnum) > 0) {
893 const char *devlink;
894
895 FOREACH_DEVICE_DEVLINK(device, devlink)
fbd0b64f 896 fprintf(f, "S:%s\n", devlink + STRLEN("/dev/"));
57fa1d09
TG
897
898 if (device->devlink_priority != 0)
899 fprintf(f, "L:%i\n", device->devlink_priority);
900
901 if (device->watch_handle >= 0)
902 fprintf(f, "W:%i\n", device->watch_handle);
903 }
904
905 if (device->usec_initialized > 0)
906 fprintf(f, "I:"USEC_FMT"\n", device->usec_initialized);
907
908 ORDERED_HASHMAP_FOREACH_KEY(value, property, device->properties_db, i)
909 fprintf(f, "E:%s=%s\n", property, value);
910
911 FOREACH_DEVICE_TAG(device, tag)
912 fprintf(f, "G:%s\n", tag);
913 }
914
915 r = fflush_and_check(f);
916 if (r < 0)
917 goto fail;
918
919 r = rename(path_tmp, path);
920 if (r < 0) {
921 r = -errno;
922 goto fail;
923 }
924
c7d54dae
YW
925 log_device_debug(device, "sd-device: Created %s file '%s' for '%s'", has_info ? "db" : "empty",
926 path, device->devpath);
57fa1d09
TG
927
928 return 0;
929
930fail:
dacd6cee
LP
931 (void) unlink(path);
932 (void) unlink(path_tmp);
57fa1d09 933
c7d54dae 934 return log_device_debug_errno(device, r, "sd-device: Failed to create %s file '%s' for '%s'", has_info ? "db" : "empty", path, device->devpath);
57fa1d09
TG
935}
936
937int device_delete_db(sd_device *device) {
938 const char *id;
939 char *path;
940 int r;
941
942 assert(device);
943
944 r = device_get_id_filename(device, &id);
945 if (r < 0)
946 return r;
947
948 path = strjoina("/run/udev/data/", id);
949
950 r = unlink(path);
951 if (r < 0 && errno != ENOENT)
952 return -errno;
953
954 return 0;
955}