• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 15263807472

26 May 2025 08:53PM UTC coverage: 72.046% (-0.002%) from 72.048%
15263807472

push

github

yuwata
src/core/manager.c: log preset activity on first boot

This gives us a little more information about what units were enabled
or disabled on that first boot and will be useful for OS developers
tracking down the source of unit state.

An example with this enabled looks like:

```
NET: Registered PF_VSOCK protocol family
systemd[1]: Applying preset policy.
systemd[1]: Unit /etc/systemd/system/dnsmasq.service is masked, ignoring.
systemd[1]: Unit /etc/systemd/system/systemd-repart.service is masked, ignoring.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket'.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir.mount' → '/etc/systemd/system/var-mnt-workdir.mount'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir\x2dtmp.mount' → '/etc/systemd/system/var-mnt-workdir\x2dtmp.mount'.
systemd[1]: Created symlink '/etc/systemd/system/afterburn-sshkeys.target.requires/afterburn-sshkeys@core.service' → '/usr/lib/systemd/system/afterburn-sshkeys@.service'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket' → '/usr/lib/systemd/system/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket' → '/usr/lib/systemd/system/systemd-resolved-monitor.socket'.
systemd[1]: Populated /etc with preset unit settings.
```

Considering it only happens on first boot and not on every boot I think
the extra information is worth the extra verbosity in the logs just for
that boot.

5 of 6 new or added lines in 1 file covered. (83.33%)

5463 existing lines in 165 files now uncovered.

299151 of 415222 relevant lines covered (72.05%)

702386.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.56
/src/udev/udev-node.c
1
/* SPDX-License-Identifier: GPL-2.0-or-later */
2

3
#include <sys/file.h>
4
#include <sys/stat.h>
5
#include <unistd.h>
6

7
#include "sd-id128.h"
8

9
#include "alloc-util.h"
10
#include "device-private.h"
11
#include "device-util.h"
12
#include "devnum-util.h"
13
#include "dirent-util.h"
14
#include "errno-util.h"
15
#include "escape.h"
16
#include "fd-util.h"
17
#include "fileio.h"
18
#include "format-util.h"
19
#include "fs-util.h"
20
#include "hashmap.h"
21
#include "hexdecoct.h"
22
#include "label-util.h"
23
#include "mkdir-label.h"
24
#include "parse-util.h"
25
#include "path-util.h"
26
#include "selinux-util.h"
27
#include "siphash24.h"
28
#include "smack-util.h"
29
#include "string-util.h"
30
#include "strv.h"
31
#include "udev-node.h"
32
#include "user-util.h"
33

34
#define UDEV_NODE_HASH_KEY SD_ID128_MAKE(b9,6a,f1,ce,40,31,44,1a,9e,19,ec,8b,ae,f3,e3,2f)
35

36
static int node_remove_symlink(sd_device *dev, const char *slink) {
92,231✔
37
        assert(dev);
92,231✔
38
        assert(slink);
92,231✔
39

40
        if (unlink(slink) < 0 && errno != ENOENT)
92,231✔
41
                return log_device_debug_errno(dev, errno, "Failed to remove '%s': %m", slink);
×
42

43
        (void) rmdir_parents(slink, "/dev");
92,231✔
44
        return 0;
92,231✔
45
}
46

47
static int node_create_symlink(sd_device *dev, const char *devnode, const char *slink) {
153,825✔
48
        struct stat st;
153,825✔
49
        int r;
153,825✔
50

51
        assert(dev);
153,825✔
52
        assert(slink);
153,825✔
53

54
        if (!devnode) {
153,825✔
55
                r = sd_device_get_devname(dev, &devnode);
53,891✔
56
                if (r < 0)
53,891✔
57
                        return log_device_debug_errno(dev, r, "Failed to get device node: %m");
×
58
        }
59

60
        if (lstat(slink, &st) >= 0) {
153,825✔
61
                if (!S_ISLNK(st.st_mode))
40,426✔
62
                        return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
×
63
                                                      "Conflicting inode '%s' found, symlink to '%s' will not be created.",
64
                                                      slink, devnode);
65
        } else if (errno != ENOENT)
113,399✔
66
                return log_device_debug_errno(dev, errno, "Failed to lstat() '%s': %m", slink);
×
67

68
        r = mkdir_parents_label(slink, 0755);
153,825✔
69
        if (r < 0)
153,825✔
70
                return log_device_debug_errno(dev, r, "Failed to create parent directory of '%s': %m", slink);
×
71

72
        /* use relative link */
73
        r = symlink_atomic_full_label(devnode, slink, /* make_relative = */ true);
153,825✔
74
        if (r < 0)
153,825✔
75
                return log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink, devnode);
×
76

77
        log_device_debug(dev, "Successfully created symlink '%s' to '%s'", slink, devnode);
155,771✔
78
        return 0;
153,825✔
79
}
80

81
static int stack_directory_read_one(int dirfd, const char *id, char **devnode, int *priority) {
120,626✔
82
        _cleanup_free_ char *buf = NULL;
120,626✔
83
        int tmp_prio, r;
120,626✔
84

85
        assert(dirfd >= 0);
120,626✔
86
        assert(id);
120,626✔
87
        assert(priority);
120,626✔
88

89
        /* This reads priority and device node from the symlink under /run/udev/links (or udev database).
90
         * If 'devnode' is NULL, obtained priority is always set to '*priority'. If 'devnode' is non-NULL,
91
         * this updates '*devnode' and '*priority'. */
92

93
        /* First, let's try to read the entry with the new format, which should replace the old format pretty
94
         * quickly. */
95
        r = readlinkat_malloc(dirfd, id, &buf);
120,626✔
96
        if (r >= 0) {
120,626✔
97
                char *colon;
113,768✔
98

99
                /* With the new format, the devnode and priority can be obtained from symlink itself. */
100

101
                colon = strchr(buf, ':');
113,768✔
102
                if (!colon || colon == buf)
113,768✔
103
                        return -EINVAL;
104

105
                *colon = '\0';
113,768✔
106

107
                /* Of course, this check is racy, but it is not necessary to be perfect. Even if the device
108
                 * node will be removed after this check, we will receive 'remove' uevent, and the invalid
109
                 * symlink will be removed during processing the event. The check is just for shortening the
110
                 * timespan that the symlink points to a non-existing device node. */
111
                if (access(colon + 1, F_OK) < 0)
113,768✔
112
                        return -ENODEV;
113

114
                r = safe_atoi(buf, &tmp_prio);
49,888✔
115
                if (r < 0)
49,888✔
116
                        return r;
117

118
                if (!devnode)
49,888✔
119
                        goto finalize;
43,680✔
120

121
                if (*devnode && tmp_prio <= *priority)
6,208✔
122
                        return 0; /* Unchanged */
123

124
                r = free_and_strdup(devnode, colon + 1);
1,480✔
125
                if (r < 0)
1,480✔
126
                        return r;
127

128
        } else if (r == -EINVAL) { /* Not a symlink ? try the old format */
6,858✔
129
                _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
×
130
                const char *val;
×
131

132
                /* Old format. The devnode and priority must be obtained from uevent and udev database. */
133

134
                r = sd_device_new_from_device_id(&dev, id);
×
135
                if (r < 0)
×
136
                        return r;
137

138
                r = device_get_devlink_priority(dev, &tmp_prio);
×
139
                if (r < 0)
×
140
                        return r;
141

142
                if (!devnode)
×
143
                        goto finalize;
×
144

145
                if (*devnode && tmp_prio <= *priority)
×
146
                        return 0; /* Unchanged */
147

148
                r = sd_device_get_devname(dev, &val);
×
149
                if (r < 0)
×
150
                        return r;
151

152
                r = free_and_strdup(devnode, val);
×
153
                if (r < 0)
×
154
                        return r;
155

156
        } else
157
                return r == -ENOENT ? -ENODEV : r;
6,858✔
158

159
finalize:
45,160✔
160
        *priority = tmp_prio;
45,160✔
161
        return 1; /* Updated */
45,160✔
162
}
163

164
static int stack_directory_find_prioritized_devnode(sd_device *dev, int dirfd, bool add, char **ret) {
177,070✔
165
        _cleanup_closedir_ DIR *dir = NULL;
177,070✔
166
        _cleanup_free_ char *devnode = NULL;
177,070✔
167
        int r, priority;
177,070✔
168
        const char *id;
177,070✔
169

170
        assert(dev);
177,070✔
171
        assert(dirfd >= 0);
177,070✔
172
        assert(ret);
177,070✔
173

174
        /* Find device node of device with highest priority. This returns 1 if a device found, 0 if no
175
         * device found, or a negative errno on error. */
176

177
        if (add) {
177,070✔
178
                const char *n;
98,454✔
179

180
                r = device_get_devlink_priority(dev, &priority);
98,454✔
181
                if (r < 0)
98,454✔
182
                        return r;
×
183

184
                r = sd_device_get_devname(dev, &n);
98,454✔
185
                if (r < 0)
98,454✔
186
                        return r;
187

188
                devnode = strdup(n);
98,454✔
189
                if (!devnode)
98,454✔
190
                        return -ENOMEM;
191
        }
192

193
        dir = xopendirat(dirfd, ".", O_NOFOLLOW);
177,070✔
194
        if (!dir)
177,070✔
195
                return -errno;
×
196

197
        r = sd_device_get_device_id(dev, &id);
177,070✔
198
        if (r < 0)
177,070✔
199
                return r;
200

201
        FOREACH_DIRENT(de, dir, break) {
699,752✔
202

203
                /* skip ourself */
204
                if (streq(de->d_name, id))
168,542✔
205
                        continue;
98,454✔
206

207
                r = stack_directory_read_one(dirfd, de->d_name, &devnode, &priority);
70,088✔
208
                if (r < 0 && r != -ENODEV)
70,088✔
209
                        log_debug_errno(r, "Failed to read '%s', ignoring: %m", de->d_name);
522,682✔
210
        }
211

212
        *ret = TAKE_PTR(devnode);
177,070✔
213
        return !!*ret;
177,070✔
214
}
215

216
static int stack_directory_update(sd_device *dev, int fd, bool add) {
241,531✔
217
        const char *id;
241,531✔
218
        int r;
241,531✔
219

220
        assert(dev);
241,531✔
221
        assert(fd >= 0);
241,531✔
222

223
        r = sd_device_get_device_id(dev, &id);
241,531✔
224
        if (r < 0)
241,531✔
225
                return r;
241,531✔
226

227
        if (add) {
241,531✔
228
                _cleanup_free_ char *data = NULL, *buf = NULL;
142,128✔
229
                const char *devname;
142,128✔
230
                int priority;
142,128✔
231

232
                r = sd_device_get_devname(dev, &devname);
142,128✔
233
                if (r < 0)
142,128✔
234
                        return r;
235

236
                r = device_get_devlink_priority(dev, &priority);
142,128✔
237
                if (r < 0)
142,128✔
238
                        return r;
239

240
                if (asprintf(&data, "%i:%s", priority, devname) < 0)
142,128✔
241
                        return -ENOMEM;
242

243
                if (readlinkat_malloc(fd, id, &buf) >= 0 && streq(buf, data))
142,128✔
244
                        return 0; /* Unchanged. */
245

246
                (void) unlinkat(fd, id, 0);
111,541✔
247

248
                if (symlinkat(data, fd, id) < 0)
111,541✔
249
                        return -errno;
×
250

251
        } else {
252
                if (unlinkat(fd, id, 0) < 0) {
99,403✔
UNCOV
253
                        if (errno == ENOENT)
×
254
                                return 0; /* Unchanged. */
255
                        return -errno;
×
256
                }
257
        }
258

259
        return 1; /* Updated. */
260
}
261

262
size_t udev_node_escape_path(const char *src, char *dest, size_t size) {
241,542✔
263
        size_t i, j;
241,542✔
264
        uint64_t h;
241,542✔
265

266
        assert(src);
241,542✔
267
        assert(dest);
241,542✔
268
        assert(size >= 12);
241,542✔
269

270
        for (i = 0, j = 0; src[i] != '\0'; i++) {
12,463,535✔
271
                if (src[i] == '/') {
12,221,996✔
272
                        if (j+4 >= size - 12 + 1)
629,045✔
273
                                goto toolong;
×
274
                        memcpy(&dest[j], "\\x2f", 4);
629,045✔
275
                        j += 4;
629,045✔
276
                } else if (src[i] == '\\') {
11,592,951✔
277
                        if (j+4 >= size - 12 + 1)
3,161✔
278
                                goto toolong;
×
279
                        memcpy(&dest[j], "\\x5c", 4);
3,161✔
280
                        j += 4;
3,161✔
281
                } else {
282
                        if (j+1 >= size - 12 + 1)
11,589,790✔
283
                                goto toolong;
3✔
284
                        dest[j] = src[i];
11,589,787✔
285
                        j++;
11,589,787✔
286
                }
287
        }
288
        dest[j] = '\0';
241,539✔
289
        return j;
241,539✔
290

291
toolong:
3✔
292
        /* If the input path is too long to encode as a filename, then let's suffix with a string
293
         * generated from the hash of the path. */
294

295
        h = siphash24_string(src, UDEV_NODE_HASH_KEY.bytes);
3✔
296

297
        for (unsigned k = 0; k <= 10; k++)
36✔
298
                dest[size - k - 2] = urlsafe_base64char((h >> (k * 6)) & 63);
33✔
299

300
        dest[size - 1] = '\0';
3✔
301
        return size - 1;
3✔
302
}
303

304
static int stack_directory_get_name(const char *slink, char **ret) {
241,531✔
305
        _cleanup_free_ char *s = NULL;
241,531✔
306
        char name_enc[NAME_MAX+1];
241,531✔
307
        const char *name;
241,531✔
308
        int r;
241,531✔
309

310
        assert(slink);
241,531✔
311
        assert(ret);
241,531✔
312

313
        r = path_simplify_alloc(slink, &s);
241,531✔
314
        if (r < 0)
241,531✔
315
                return r;
316

317
        if (!path_is_normalized(s))
241,531✔
318
                return -EINVAL;
319

320
        name = path_startswith(s, "/dev");
241,531✔
321
        if (empty_or_root(name))
241,531✔
322
                return -EINVAL;
323

324
        udev_node_escape_path(name, name_enc, sizeof(name_enc));
241,531✔
325

326
        return strdup_to(ret, name_enc);
241,531✔
327
}
328

329
static int stack_directory_open_and_lock(
241,531✔
330
                sd_device *dev,
331
                const char *slink,
332
                char **ret_dirpath,
333
                int *ret_dirfd,
334
                LockFile *ret_lockfile) {
335

336
        _cleanup_(release_lock_file) LockFile lockfile = LOCK_FILE_INIT;
×
337
        _cleanup_close_ int dirfd = -EBADF;
241,531✔
338
        _cleanup_free_ char *name = NULL, *dirpath = NULL, *lockname = NULL;
241,531✔
339
        int r;
241,531✔
340

341
        assert(dev);
241,531✔
342
        assert(slink);
241,531✔
343
        assert(ret_dirpath);
241,531✔
344
        assert(ret_dirfd);
241,531✔
345
        assert(ret_lockfile);
241,531✔
346

347
        r = stack_directory_get_name(slink, &name);
241,531✔
348
        if (r < 0)
241,531✔
349
                return log_device_debug_errno(dev, r, "Failed to build stack directory name for '%s': %m", slink);
×
350

351
        FOREACH_STRING(s, "/run/udev/links/", "/run/udev/links.lock/") {
724,593✔
352
                r = mkdir_p(s, 0755);
483,062✔
353
                if (r < 0)
483,062✔
354
                        return log_device_debug_errno(dev, r, "Failed to create '%s': %m", s);
×
355
        }
356

357
        /* 1. Take a lock for the stack directory. */
358
        lockname = path_join("/run/udev/links.lock/", name);
241,531✔
359
        if (!lockname)
241,531✔
360
                return -ENOMEM;
361

362
        r = make_lock_file(lockname, LOCK_EX, &lockfile);
241,531✔
363
        if (r < 0)
241,531✔
364
                return log_device_debug_errno(dev, r, "Failed to create and lock '%s': %m", lockname);
×
365

366
        /* 2. Create and open the stack directory. Do not create the stack directory before taking a lock,
367
         * otherwise the directory may be removed by another worker. */
368
        dirpath = path_join("/run/udev/links/", name);
241,531✔
369
        if (!dirpath)
241,531✔
370
                return -ENOMEM;
371

372
        dirfd = open_mkdir(dirpath, O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW | O_RDONLY, 0755);
241,531✔
373
        if (dirfd < 0)
241,531✔
374
                return log_device_debug_errno(dev, dirfd, "Failed to open stack directory '%s': %m", dirpath);
×
375

376
        *ret_dirpath = TAKE_PTR(dirpath);
241,531✔
377
        *ret_dirfd = TAKE_FD(dirfd);
241,531✔
378
        *ret_lockfile = TAKE_GENERIC(lockfile, LockFile, LOCK_FILE_INIT);
241,531✔
379
        return 0;
241,531✔
380
}
381

382
static int node_get_current(const char *slink, int dirfd, char **ret_id, int *ret_prio) {
241,531✔
383
        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
241,531✔
384
        _cleanup_free_ char *id_dup = NULL;
241,531✔
385
        const char *id;
241,531✔
386
        int r;
241,531✔
387

388
        assert(slink);
241,531✔
389
        assert(dirfd >= 0);
241,531✔
390
        assert(ret_id);
241,531✔
391

392
        r = sd_device_new_from_devname(&dev, slink);
241,531✔
393
        if (r < 0)
241,531✔
394
                return r;
395

396
        r = sd_device_get_device_id(dev, &id);
122,428✔
397
        if (r < 0)
122,428✔
398
                return r;
399

400
        id_dup = strdup(id);
122,428✔
401
        if (!id_dup)
122,428✔
402
                return -ENOMEM;
403

404
        if (ret_prio) {
122,428✔
405
                r = stack_directory_read_one(dirfd, id, NULL, ret_prio);
50,538✔
406
                if (r < 0)
50,538✔
407
                        return r;
408
        }
409

410
        *ret_id = TAKE_PTR(id_dup);
115,570✔
411
        return 0;
115,570✔
412
}
413

414
static int link_update(sd_device *dev, const char *slink, bool add) {
241,531✔
415
        /* On cleaning up,
416
         * 1. close the stack directory,
417
         * 2. remove the stack directory if it is empty,
418
         * 3. then finally release the lock.
419
         * Hence, the variables must be declared in the reverse order. */
420
        _cleanup_(release_lock_file) LockFile lockfile = LOCK_FILE_INIT; /* #3 */
241,531✔
421
        _cleanup_(rmdir_and_freep) char *dirpath = NULL; /* #2 */
×
422
        _cleanup_close_ int dirfd = -EBADF; /* #1 */
241,531✔
423
        _cleanup_free_ char *current_id = NULL, *devnode = NULL;
241,531✔
424
        int r, current_prio;
241,531✔
425

426
        assert(dev);
241,531✔
427
        assert(slink);
241,531✔
428

429
        r = stack_directory_open_and_lock(dev, slink, &dirpath, &dirfd, &lockfile);
241,531✔
430
        if (r < 0)
241,531✔
431
                return r;
432

433
        r = node_get_current(slink, dirfd, &current_id, add ? &current_prio : NULL);
340,934✔
434
        if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
241,531✔
435
                return log_device_debug_errno(dev, r, "Failed to get the current device node priority for '%s': %m", slink);
×
436

437
        r = stack_directory_update(dev, dirfd, add);
241,531✔
438
        if (r < 0)
241,531✔
439
                return log_device_debug_errno(dev, r, "Failed to update stack directory for '%s': %m", slink);
×
440

441
        if (current_id) {
241,531✔
442
                const char *id;
115,570✔
443

444
                r = sd_device_get_device_id(dev, &id);
115,570✔
445
                if (r < 0)
115,570✔
446
                        return log_device_debug_errno(dev, r, "Failed to get device id: %m");
64,461✔
447

448
                if (add) {
115,570✔
449
                        int prio;
43,680✔
450

451
                        r = device_get_devlink_priority(dev, &prio);
43,680✔
452
                        if (r < 0)
43,680✔
453
                                return log_device_debug_errno(dev, r, "Failed to get devlink priority: %m");
43,674✔
454

455
                        if (streq(current_id, id)) {
43,680✔
456
                                if (current_prio <= prio)
26,856✔
457
                                        /* The devlink is ours and already exists, and the new priority is
458
                                         * equal or higher than the previous. Hence, it is not necessary to
459
                                         * recreate it. */
460
                                        return 0;
461

462
                                /* The devlink priority is downgraded. Another device may have a higher
463
                                 * priority now. Let's find the device node with the highest priority. */
464
                        } else {
465
                                if (current_prio > prio)
16,824✔
466
                                        /* The devlink with a higher priority already exists and is owned by
467
                                         * another device. Hence, it is not necessary to recreate it. */
468
                                        return 0;
469

470
                                /* This device has the equal or a higher priority than the current. Let's
471
                                 * create the devlink to our device node. */
472
                                return node_create_symlink(dev, /* devnode = */ NULL, slink);
15,215✔
473
                        }
474

475
                } else {
476
                        if (!streq(current_id, id))
71,890✔
477
                                /* The devlink already exists and is owned by another device. Hence, it is
478
                                 * not necessary to recreate it. */
479
                                return 0;
480

481
                        /* The current devlink is ours, and the target device will be removed. Hence, we need
482
                         * to search the device that has the highest priority. and update the devlink. */
483
                }
484
        } else {
485
                /* The requested devlink does not exist, or the target device does not exist and the devlink
486
                 * points to a non-existing device. Let's search the device that has the highest priority,
487
                 * and update the devlink. */
488
                ;
177,070✔
489
        }
490

491
        r = stack_directory_find_prioritized_devnode(dev, dirfd, add, &devnode);
177,070✔
492
        if (r < 0)
177,070✔
493
                return log_device_debug_errno(dev, r, "Failed to determine device node with the highest priority for '%s': %m", slink);
×
494
        if (r > 0)
177,070✔
495
                return node_create_symlink(dev, devnode, slink);
99,934✔
496

497
        log_device_debug(dev, "No reference left for '%s', removing", slink);
77,136✔
498
        return node_remove_symlink(dev, slink);
77,136✔
499
}
500

501
static int device_get_devpath_by_devnum(sd_device *dev, char **ret) {
53,771✔
502
        dev_t devnum;
53,771✔
503
        int r;
53,771✔
504

505
        assert(dev);
53,771✔
506
        assert(ret);
53,771✔
507

508
        r = sd_device_get_devnum(dev, &devnum);
53,771✔
509
        if (r < 0)
53,771✔
510
                return r;
53,771✔
511

512
        r = device_in_subsystem(dev, "block");
53,771✔
513
        if (r < 0)
53,771✔
514
                return r;
515

516
        return device_path_make_major_minor(r > 0 ? S_IFBLK : S_IFCHR, devnum, ret);
66,851✔
517
}
518

519
int udev_node_update(sd_device *dev, sd_device *dev_old) {
38,676✔
520
        _cleanup_free_ char *filename = NULL;
38,676✔
521
        int r;
38,676✔
522

523
        assert(dev);
38,676✔
524
        assert(dev_old);
38,676✔
525

526
        /* update possible left-over symlinks */
527
        FOREACH_DEVICE_DEVLINK(dev_old, devlink) {
71,900✔
528
                /* check if old link name still belongs to this device */
529
                if (device_has_devlink(dev, devlink))
33,224✔
530
                        continue;
30,599✔
531

532
                log_device_debug(dev,
2,625✔
533
                                 "Removing/updating old device symlink '%s', which is no longer belonging to this device.",
534
                                 devlink);
535

536
                r = link_update(dev, devlink, /* add = */ false);
2,625✔
537
                if (r < 0)
2,625✔
538
                        log_device_warning_errno(dev, r,
×
539
                                                 "Failed to remove/update device symlink '%s', ignoring: %m",
540
                                                 devlink);
541
        }
542

543
        /* create/update symlinks, add symlinks to name index */
544
        FOREACH_DEVICE_DEVLINK(dev, devlink) {
180,804✔
545
                r = link_update(dev, devlink, /* add = */ true);
142,128✔
546
                if (r < 0)
142,128✔
547
                        log_device_warning_errno(dev, r,
×
548
                                                 "Failed to create/update device symlink '%s', ignoring: %m",
549
                                                 devlink);
550
        }
551

552
        r = device_get_devpath_by_devnum(dev, &filename);
38,676✔
553
        if (r < 0)
38,676✔
554
                return log_device_debug_errno(dev, r, "Failed to get device path: %m");
×
555

556
        /* always add /dev/{block,char}/$major:$minor */
557
        r = node_create_symlink(dev, /* devnode = */ NULL, filename);
38,676✔
558
        if (r < 0)
38,676✔
559
                return log_device_warning_errno(dev, r, "Failed to create device symlink '%s': %m", filename);
×
560

561
        return 0;
562
}
563

564
int udev_node_remove(sd_device *dev) {
15,095✔
565
        _cleanup_free_ char *filename = NULL;
15,095✔
566
        int r;
15,095✔
567

568
        assert(dev);
15,095✔
569

570
        /* remove/update symlinks, remove symlinks from name index */
571
        FOREACH_DEVICE_DEVLINK(dev, devlink) {
111,873✔
572
                r = link_update(dev, devlink, /* add = */ false);
96,778✔
573
                if (r < 0)
96,778✔
574
                        log_device_warning_errno(dev, r,
×
575
                                                 "Failed to remove/update device symlink '%s', ignoring: %m",
576
                                                 devlink);
577
        }
578

579
        r = device_get_devpath_by_devnum(dev, &filename);
15,095✔
580
        if (r < 0)
15,095✔
581
                return log_device_debug_errno(dev, r, "Failed to get device path: %m");
×
582

583
        /* remove /dev/{block,char}/$major:$minor */
584
        return node_remove_symlink(dev, filename);
15,095✔
585
}
586

587
static int udev_node_apply_permissions_impl(
34,476✔
588
                sd_device *dev, /* can be NULL, only used for logging. */
589
                int node_fd,
590
                const char *devnode,
591
                bool apply_mac,
592
                mode_t mode,
593
                uid_t uid,
594
                gid_t gid,
595
                OrderedHashmap *seclabel_list) {
596

597
        bool apply_mode, apply_uid, apply_gid;
34,476✔
598
        struct stat stats;
34,476✔
599
        int r;
34,476✔
600

601
        assert(node_fd >= 0);
34,476✔
602
        assert(devnode);
34,476✔
603

604
        if (fstat(node_fd, &stats) < 0)
34,476✔
605
                return log_device_debug_errno(dev, errno, "cannot stat() node %s: %m", devnode);
×
606

607
        /* If group is set, but mode is not set, "upgrade" mode for the group. */
608
        if (mode == MODE_INVALID && gid_is_valid(gid) && gid > 0)
63,640✔
609
                mode = 0660;
610

611
        apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777);
20,886✔
612
        apply_uid = uid_is_valid(uid) && stats.st_uid != uid;
34,476✔
613
        apply_gid = gid_is_valid(gid) && stats.st_gid != gid;
34,476✔
614

615
        if (apply_mode || apply_uid || apply_gid || apply_mac) {
34,476✔
616
                bool selinux = false, smack = false;
21,408✔
617
                const char *name, *label;
21,408✔
618

619
                if (apply_mode || apply_uid || apply_gid) {
21,408✔
620
                        log_device_debug(dev, "Setting permissions %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
25,680✔
621
                                         devnode,
622
                                         uid_is_valid(uid) ? uid : stats.st_uid,
623
                                         gid_is_valid(gid) ? gid : stats.st_gid,
624
                                         mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
625

626
                        r = fchmod_and_chown(node_fd, mode, uid, gid);
12,840✔
627
                        if (r < 0)
12,840✔
628
                                log_device_full_errno(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
×
629
                                                      "Failed to set owner/mode of %s to uid=" UID_FMT
630
                                                      ", gid=" GID_FMT ", mode=%#o: %m",
631
                                                      devnode,
632
                                                      uid_is_valid(uid) ? uid : stats.st_uid,
633
                                                      gid_is_valid(gid) ? gid : stats.st_gid,
634
                                                      mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
635
                } else
636
                        log_device_debug(dev, "Preserve permissions of %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
17,136✔
637
                                         devnode,
638
                                         uid_is_valid(uid) ? uid : stats.st_uid,
639
                                         gid_is_valid(gid) ? gid : stats.st_gid,
640
                                         mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
641

642
                /* apply SECLABEL{$module}=$label */
643
                ORDERED_HASHMAP_FOREACH_KEY(label, name, seclabel_list) {
21,408✔
644
                        int q;
×
645

646
                        if (streq(name, "selinux")) {
×
647
                                selinux = true;
×
648

649
                                q = mac_selinux_apply_fd(node_fd, devnode, label);
×
650
                                if (q < 0)
×
651
                                        log_device_full_errno(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
×
652
                                                              "SECLABEL: failed to set SELinux label '%s': %m", label);
653
                                else
654
                                        log_device_debug(dev, "SECLABEL: set SELinux label '%s'", label);
×
655

656
                        } else if (streq(name, "smack")) {
×
657
                                smack = true;
×
658

659
                                q = mac_smack_apply_fd(node_fd, SMACK_ATTR_ACCESS, label);
×
660
                                if (q < 0)
×
661
                                        log_device_full_errno(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
×
662
                                                              "SECLABEL: failed to set SMACK label '%s': %m", label);
663
                                else
664
                                        log_device_debug(dev, "SECLABEL: set SMACK label '%s'", label);
×
665

666
                        } else
667
                                log_device_error(dev, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label);
×
668
                }
669

670
                /* set the defaults */
671
                if (!selinux)
21,408✔
672
                        (void) mac_selinux_fix_full(node_fd, NULL, devnode, LABEL_IGNORE_ENOENT);
21,408✔
673
                if (!smack)
21,408✔
674
                        (void) mac_smack_apply_fd(node_fd, SMACK_ATTR_ACCESS, NULL);
21,408✔
675
        }
676

677
        /* always update timestamp when we re-use the node, like on media change events */
678
        r = futimens_opath(node_fd, NULL);
34,476✔
679
        if (r < 0)
34,476✔
680
                log_device_debug_errno(dev, r, "Failed to adjust timestamp of node %s: %m", devnode);
×
681

682
        return 0;
683
}
684

685
int udev_node_apply_permissions(
38,676✔
686
                sd_device *dev,
687
                bool apply_mac,
688
                mode_t mode,
689
                uid_t uid,
690
                gid_t gid,
691
                OrderedHashmap *seclabel_list) {
692

693
        const char *devnode;
38,676✔
694
        _cleanup_close_ int node_fd = -EBADF;
38,676✔
695
        int r;
38,676✔
696

697
        assert(dev);
38,676✔
698

699
        r = sd_device_get_devname(dev, &devnode);
38,676✔
700
        if (r < 0)
38,676✔
701
                return log_device_debug_errno(dev, r, "Failed to get devname: %m");
×
702

703
        node_fd = sd_device_open(dev, O_PATH|O_CLOEXEC);
38,676✔
704
        if (node_fd < 0) {
38,676✔
705
                if (ERRNO_IS_DEVICE_ABSENT(node_fd)) {
4,787✔
706
                        log_device_debug_errno(dev, node_fd, "Device node %s is missing, skipping handling.", devnode);
4,787✔
707
                        return 0; /* This is necessarily racey, so ignore missing the device */
4,787✔
708
                }
709

710
                return log_device_debug_errno(dev, node_fd, "Cannot open node %s: %m", devnode);
×
711
        }
712

713
        return udev_node_apply_permissions_impl(dev, node_fd, devnode, apply_mac, mode, uid, gid, seclabel_list);
33,889✔
714
}
715

716
int static_node_apply_permissions(
616✔
717
                const char *name,
718
                mode_t mode,
719
                uid_t uid,
720
                gid_t gid,
721
                char **tags) {
722

723
        _cleanup_free_ char *unescaped_filename = NULL;
616✔
724
        _cleanup_close_ int node_fd = -EBADF;
616✔
725
        const char *devnode;
616✔
726
        struct stat stats;
616✔
727
        int r;
616✔
728

729
        assert(name);
616✔
730

731
        if (uid == UID_INVALID && gid == GID_INVALID && mode == MODE_INVALID && !tags)
616✔
732
                return 0;
733

734
        devnode = strjoina("/dev/", name);
3,080✔
735

736
        node_fd = open(devnode, O_PATH|O_CLOEXEC);
616✔
737
        if (node_fd < 0) {
616✔
738
                if (errno != ENOENT)
29✔
739
                        return log_error_errno(errno, "Failed to open %s: %m", devnode);
×
740
                return 0;
741
        }
742

743
        if (fstat(node_fd, &stats) < 0)
587✔
744
                return log_error_errno(errno, "Failed to stat %s: %m", devnode);
×
745

746
        if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) {
587✔
747
                log_warning("%s is neither block nor character device, ignoring.", devnode);
×
748
                return 0;
×
749
        }
750

751
        if (!strv_isempty(tags)) {
587✔
752
                unescaped_filename = xescape(name, "/.");
112✔
753
                if (!unescaped_filename)
112✔
754
                        return log_oom();
×
755
        }
756

757
        /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
758
        STRV_FOREACH(t, tags) {
699✔
759
                _cleanup_free_ char *p = NULL;
112✔
760

761
                p = path_join("/run/udev/static_node-tags/", *t, unescaped_filename);
112✔
762
                if (!p)
112✔
763
                        return log_oom();
×
764

765
                r = mkdir_parents(p, 0755);
112✔
766
                if (r < 0)
112✔
767
                        return log_error_errno(r, "Failed to create parent directory for %s: %m", p);
×
768

769
                r = symlink(devnode, p);
112✔
770
                if (r < 0 && errno != EEXIST)
112✔
771
                        return log_error_errno(errno, "Failed to create symlink %s -> %s: %m", p, devnode);
×
772
        }
773

774
        return udev_node_apply_permissions_impl(NULL, node_fd, devnode, false, mode, uid, gid, NULL);
587✔
775
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc