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

systemd / systemd / 14414052759

12 Apr 2025 12:06AM UTC coverage: 72.01% (+0.04%) from 71.967%
14414052759

push

github

web-flow
fido2: hook up with plymouth for notifications (#37089)

Show notifications for fido2 messages in plymouth, so that they
show up in the initrd like the passphrase prompt already does.

12 of 29 new or added lines in 3 files covered. (41.38%)

572 existing lines in 41 files now uncovered.

297369 of 412956 relevant lines covered (72.01%)

685877.32 hits per line

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

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

3
#include <sys/file.h>
4

5
#include "sd-id128.h"
6

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

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

31
static int node_remove_symlink(sd_device *dev, const char *slink) {
71,655✔
32
        assert(dev);
71,655✔
33
        assert(slink);
71,655✔
34

35
        if (unlink(slink) < 0 && errno != ENOENT)
71,655✔
36
                return log_device_debug_errno(dev, errno, "Failed to remove '%s': %m", slink);
×
37

38
        (void) rmdir_parents(slink, "/dev");
71,655✔
39
        return 0;
71,655✔
40
}
41

42
static int node_create_symlink(sd_device *dev, const char *devnode, const char *slink) {
133,744✔
43
        struct stat st;
133,744✔
44
        int r;
133,744✔
45

46
        assert(dev);
133,744✔
47
        assert(slink);
133,744✔
48

49
        if (!devnode) {
133,744✔
50
                r = sd_device_get_devname(dev, &devnode);
52,831✔
51
                if (r < 0)
52,831✔
52
                        return log_device_debug_errno(dev, r, "Failed to get device node: %m");
×
53
        }
54

55
        if (lstat(slink, &st) >= 0) {
133,744✔
56
                if (!S_ISLNK(st.st_mode))
43,201✔
57
                        return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
×
58
                                                      "Conflicting inode '%s' found, symlink to '%s' will not be created.",
59
                                                      slink, devnode);
60
        } else if (errno != ENOENT)
90,543✔
61
                return log_device_debug_errno(dev, errno, "Failed to lstat() '%s': %m", slink);
×
62

63
        r = mkdir_parents_label(slink, 0755);
133,744✔
64
        if (r < 0)
133,744✔
65
                return log_device_debug_errno(dev, r, "Failed to create parent directory of '%s': %m", slink);
×
66

67
        /* use relative link */
68
        r = symlink_atomic_full_label(devnode, slink, /* make_relative = */ true);
133,744✔
69
        if (r < 0)
133,744✔
70
                return log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink, devnode);
1✔
71

72
        log_device_debug(dev, "Successfully created symlink '%s' to '%s'", slink, devnode);
135,520✔
73
        return 0;
133,743✔
74
}
75

76
static int stack_directory_read_one(int dirfd, const char *id, char **devnode, int *priority) {
447,186✔
77
        _cleanup_free_ char *buf = NULL;
447,186✔
78
        int tmp_prio, r;
447,186✔
79

80
        assert(dirfd >= 0);
447,186✔
81
        assert(id);
447,186✔
82
        assert(priority);
447,186✔
83

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

88
        /* First, let's try to read the entry with the new format, which should replace the old format pretty
89
         * quickly. */
90
        r = readlinkat_malloc(dirfd, id, &buf);
447,186✔
91
        if (r >= 0) {
447,186✔
92
                char *colon;
442,738✔
93

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

96
                colon = strchr(buf, ':');
442,738✔
97
                if (!colon || colon == buf)
442,738✔
98
                        return -EINVAL;
99

100
                *colon = '\0';
442,738✔
101

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

109
                r = safe_atoi(buf, &tmp_prio);
66,126✔
110
                if (r < 0)
66,126✔
111
                        return r;
112

113
                if (!devnode)
66,126✔
114
                        goto finalize;
60,705✔
115

116
                if (*devnode && tmp_prio <= *priority)
5,421✔
117
                        return 0; /* Unchanged */
118

119
                r = free_and_strdup(devnode, colon + 1);
1,350✔
120
                if (r < 0)
1,350✔
121
                        return r;
122

123
        } else if (r == -EINVAL) { /* Not a symlink ? try the old format */
4,448✔
124
                _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
×
125
                const char *val;
×
126

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

129
                r = sd_device_new_from_device_id(&dev, id);
×
130
                if (r < 0)
×
131
                        return r;
132

133
                r = device_get_devlink_priority(dev, &tmp_prio);
×
134
                if (r < 0)
×
135
                        return r;
136

137
                if (!devnode)
×
138
                        goto finalize;
×
139

140
                if (*devnode && tmp_prio <= *priority)
×
141
                        return 0; /* Unchanged */
142

143
                r = sd_device_get_devname(dev, &val);
×
144
                if (r < 0)
×
145
                        return r;
146

147
                r = free_and_strdup(devnode, val);
×
148
                if (r < 0)
×
149
                        return r;
150

151
        } else
152
                return r == -ENOENT ? -ENODEV : r;
4,448✔
153

154
finalize:
62,055✔
155
        *priority = tmp_prio;
62,055✔
156
        return 1; /* Updated */
62,055✔
157
}
158

159
static int stack_directory_find_prioritized_devnode(sd_device *dev, int dirfd, bool add, char **ret) {
141,181✔
160
        _cleanup_closedir_ DIR *dir = NULL;
141,181✔
161
        _cleanup_free_ char *devnode = NULL;
141,181✔
162
        int r, priority;
141,181✔
163
        const char *id;
141,181✔
164

165
        assert(dev);
141,181✔
166
        assert(dirfd >= 0);
141,181✔
167
        assert(ret);
141,181✔
168

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

172
        if (add) {
141,181✔
173
                const char *n;
79,563✔
174

175
                r = device_get_devlink_priority(dev, &priority);
79,563✔
176
                if (r < 0)
79,563✔
177
                        return r;
×
178

179
                r = sd_device_get_devname(dev, &n);
79,563✔
180
                if (r < 0)
79,563✔
181
                        return r;
182

183
                devnode = strdup(n);
79,563✔
184
                if (!devnode)
79,563✔
185
                        return -ENOMEM;
186
        }
187

188
        dir = xopendirat(dirfd, ".", O_NOFOLLOW);
141,181✔
189
        if (!dir)
141,181✔
190
                return -errno;
×
191

192
        r = sd_device_get_device_id(dev, &id);
141,181✔
193
        if (r < 0)
141,181✔
194
                return r;
195

196
        FOREACH_DIRENT(de, dir, break) {
885,139✔
197

198
                /* skip ourself */
199
                if (streq(de->d_name, id))
461,596✔
200
                        continue;
79,563✔
201

202
                r = stack_directory_read_one(dirfd, de->d_name, &devnode, &priority);
382,033✔
203
                if (r < 0 && r != -ENODEV)
382,033✔
204
                        log_debug_errno(r, "Failed to read '%s', ignoring: %m", de->d_name);
743,958✔
205
        }
206

207
        *ret = TAKE_PTR(devnode);
141,181✔
208
        return !!*ret;
141,181✔
209
}
210

211
static int stack_directory_update(sd_device *dev, int fd, bool add) {
213,379✔
212
        const char *id;
213,379✔
213
        int r;
213,379✔
214

215
        assert(dev);
213,379✔
216
        assert(fd >= 0);
213,379✔
217

218
        r = sd_device_get_device_id(dev, &id);
213,379✔
219
        if (r < 0)
213,379✔
220
                return r;
213,379✔
221

222
        if (add) {
213,379✔
223
                _cleanup_free_ char *data = NULL, *buf = NULL;
140,262✔
224
                const char *devname;
140,262✔
225
                int priority;
140,262✔
226

227
                r = sd_device_get_devname(dev, &devname);
140,262✔
228
                if (r < 0)
140,262✔
229
                        return r;
230

231
                r = device_get_devlink_priority(dev, &priority);
140,262✔
232
                if (r < 0)
140,262✔
233
                        return r;
234

235
                if (asprintf(&data, "%i:%s", priority, devname) < 0)
140,262✔
236
                        return -ENOMEM;
237

238
                if (readlinkat_malloc(fd, id, &buf) >= 0 && streq(buf, data))
140,262✔
239
                        return 0; /* Unchanged. */
240

241
                (void) unlinkat(fd, id, 0);
85,254✔
242

243
                if (symlinkat(data, fd, id) < 0)
85,254✔
244
                        return -errno;
×
245

246
        } else {
247
                if (unlinkat(fd, id, 0) < 0) {
73,117✔
UNCOV
248
                        if (errno == ENOENT)
×
249
                                return 0; /* Unchanged. */
250
                        return -errno;
×
251
                }
252
        }
253

254
        return 1; /* Updated. */
255
}
256

257
size_t udev_node_escape_path(const char *src, char *dest, size_t size) {
213,390✔
258
        size_t i, j;
213,390✔
259
        uint64_t h;
213,390✔
260

261
        assert(src);
213,390✔
262
        assert(dest);
213,390✔
263
        assert(size >= 12);
213,390✔
264

265
        for (i = 0, j = 0; src[i] != '\0'; i++) {
10,961,520✔
266
                if (src[i] == '/') {
10,748,133✔
267
                        if (j+4 >= size - 12 + 1)
553,663✔
268
                                goto toolong;
×
269
                        memcpy(&dest[j], "\\x2f", 4);
553,663✔
270
                        j += 4;
553,663✔
271
                } else if (src[i] == '\\') {
10,194,470✔
272
                        if (j+4 >= size - 12 + 1)
2,740✔
273
                                goto toolong;
×
274
                        memcpy(&dest[j], "\\x5c", 4);
2,740✔
275
                        j += 4;
2,740✔
276
                } else {
277
                        if (j+1 >= size - 12 + 1)
10,191,730✔
278
                                goto toolong;
3✔
279
                        dest[j] = src[i];
10,191,727✔
280
                        j++;
10,191,727✔
281
                }
282
        }
283
        dest[j] = '\0';
213,387✔
284
        return j;
213,387✔
285

286
toolong:
3✔
287
        /* If the input path is too long to encode as a filename, then let's suffix with a string
288
         * generated from the hash of the path. */
289

290
        h = siphash24_string(src, UDEV_NODE_HASH_KEY.bytes);
3✔
291

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

295
        dest[size - 1] = '\0';
3✔
296
        return size - 1;
3✔
297
}
298

299
static int stack_directory_get_name(const char *slink, char **ret) {
213,379✔
300
        _cleanup_free_ char *s = NULL;
213,379✔
301
        char name_enc[NAME_MAX+1];
213,379✔
302
        const char *name;
213,379✔
303
        int r;
213,379✔
304

305
        assert(slink);
213,379✔
306
        assert(ret);
213,379✔
307

308
        r = path_simplify_alloc(slink, &s);
213,379✔
309
        if (r < 0)
213,379✔
310
                return r;
311

312
        if (!path_is_normalized(s))
213,379✔
313
                return -EINVAL;
314

315
        name = path_startswith(s, "/dev");
213,379✔
316
        if (empty_or_root(name))
213,379✔
317
                return -EINVAL;
318

319
        udev_node_escape_path(name, name_enc, sizeof(name_enc));
213,379✔
320

321
        return strdup_to(ret, name_enc);
213,379✔
322
}
323

324
static int stack_directory_open_and_lock(
213,379✔
325
                sd_device *dev,
326
                const char *slink,
327
                char **ret_dirpath,
328
                int *ret_dirfd,
329
                LockFile *ret_lockfile) {
330

331
        _cleanup_(release_lock_file) LockFile lockfile = LOCK_FILE_INIT;
×
332
        _cleanup_close_ int dirfd = -EBADF;
213,379✔
333
        _cleanup_free_ char *name = NULL, *dirpath = NULL, *lockname = NULL;
213,379✔
334
        int r;
213,379✔
335

336
        assert(dev);
213,379✔
337
        assert(slink);
213,379✔
338
        assert(ret_dirpath);
213,379✔
339
        assert(ret_dirfd);
213,379✔
340
        assert(ret_lockfile);
213,379✔
341

342
        r = stack_directory_get_name(slink, &name);
213,379✔
343
        if (r < 0)
213,379✔
344
                return log_device_debug_errno(dev, r, "Failed to build stack directory name for '%s': %m", slink);
×
345

346
        FOREACH_STRING(s, "/run/udev/links/", "/run/udev/links.lock/") {
640,137✔
347
                r = mkdir_p(s, 0755);
426,758✔
348
                if (r < 0)
426,758✔
349
                        return log_device_debug_errno(dev, r, "Failed to create '%s': %m", s);
×
350
        }
351

352
        /* 1. Take a lock for the stack directory. */
353
        lockname = path_join("/run/udev/links.lock/", name);
213,379✔
354
        if (!lockname)
213,379✔
355
                return -ENOMEM;
356

357
        r = make_lock_file(lockname, LOCK_EX, &lockfile);
213,379✔
358
        if (r < 0)
213,379✔
359
                return log_device_debug_errno(dev, r, "Failed to create and lock '%s': %m", lockname);
×
360

361
        /* 2. Create and open the stack directory. Do not create the stack directory before taking a lock,
362
         * otherwise the directory may be removed by another worker. */
363
        dirpath = path_join("/run/udev/links/", name);
213,379✔
364
        if (!dirpath)
213,379✔
365
                return -ENOMEM;
366

367
        dirfd = open_mkdir(dirpath, O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW | O_RDONLY, 0755);
213,379✔
368
        if (dirfd < 0)
213,379✔
369
                return log_device_debug_errno(dev, dirfd, "Failed to open stack directory '%s': %m", dirpath);
×
370

371
        *ret_dirpath = TAKE_PTR(dirpath);
213,379✔
372
        *ret_dirfd = TAKE_FD(dirfd);
213,379✔
373
        *ret_lockfile = TAKE_GENERIC(lockfile, LockFile, LOCK_FILE_INIT);
213,379✔
374
        return 0;
213,379✔
375
}
376

377
static int node_get_current(const char *slink, int dirfd, char **ret_id, int *ret_prio) {
213,379✔
378
        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
213,379✔
379
        _cleanup_free_ char *id_dup = NULL;
213,379✔
380
        const char *id;
213,379✔
381
        int r;
213,379✔
382

383
        assert(slink);
213,379✔
384
        assert(dirfd >= 0);
213,379✔
385
        assert(ret_id);
213,379✔
386

387
        r = sd_device_new_from_devname(&dev, slink);
213,379✔
388
        if (r < 0)
213,379✔
389
                return r;
390

391
        r = sd_device_get_device_id(dev, &id);
108,465✔
392
        if (r < 0)
108,465✔
393
                return r;
394

395
        id_dup = strdup(id);
108,465✔
396
        if (!id_dup)
108,465✔
397
                return -ENOMEM;
398

399
        if (ret_prio) {
108,465✔
400
                r = stack_directory_read_one(dirfd, id, NULL, ret_prio);
65,153✔
401
                if (r < 0)
65,153✔
402
                        return r;
403
        }
404

405
        *ret_id = TAKE_PTR(id_dup);
104,017✔
406
        return 0;
104,017✔
407
}
408

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

421
        assert(dev);
213,379✔
422
        assert(slink);
213,379✔
423

424
        r = stack_directory_open_and_lock(dev, slink, &dirpath, &dirfd, &lockfile);
213,379✔
425
        if (r < 0)
213,379✔
426
                return r;
427

428
        r = node_get_current(slink, dirfd, &current_id, add ? &current_prio : NULL);
286,496✔
429
        if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
213,379✔
430
                return log_device_debug_errno(dev, r, "Failed to get the current device node priority for '%s': %m", slink);
×
431

432
        r = stack_directory_update(dev, dirfd, add);
213,379✔
433
        if (r < 0)
213,379✔
434
                return log_device_debug_errno(dev, r, "Failed to update stack directory for '%s': %m", slink);
×
435

436
        if (current_id) {
213,379✔
437
                const char *id;
104,017✔
438

439
                r = sd_device_get_device_id(dev, &id);
104,017✔
440
                if (r < 0)
104,017✔
441
                        return log_device_debug_errno(dev, r, "Failed to get device id: %m");
72,198✔
442

443
                if (add) {
104,017✔
444
                        int prio;
60,705✔
445

446
                        r = device_get_devlink_priority(dev, &prio);
60,705✔
447
                        if (r < 0)
60,705✔
448
                                return log_device_debug_errno(dev, r, "Failed to get devlink priority: %m");
60,699✔
449

450
                        if (streq(current_id, id)) {
60,705✔
451
                                if (current_prio <= prio)
43,725✔
452
                                        /* The devlink is ours and already exists, and the new priority is
453
                                         * equal or higher than the previous. Hence, it is not necessary to
454
                                         * recreate it. */
455
                                        return 0;
456

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

465
                                /* This device has the equal or a higher priority than the current. Let's
466
                                 * create the devlink to our device node. */
467
                                return node_create_symlink(dev, /* devnode = */ NULL, slink);
15,448✔
468
                        }
469

470
                } else {
471
                        if (!streq(current_id, id))
43,312✔
472
                                /* The devlink already exists and is owned by another device. Hence, it is
473
                                 * not necessary to recreate it. */
474
                                return 0;
475

476
                        /* The current devlink is ours, and the target device will be removed. Hence, we need
477
                         * to search the device that has the highest priority. and update the devlink. */
478
                }
479
        } else {
480
                /* The requested devlink does not exist, or the target device does not exist and the devlink
481
                 * points to a non-existing device. Let's search the device that has the highest priority,
482
                 * and update the devlink. */
483
                ;
141,181✔
484
        }
485

486
        r = stack_directory_find_prioritized_devnode(dev, dirfd, add, &devnode);
141,181✔
487
        if (r < 0)
141,181✔
488
                return log_device_debug_errno(dev, r, "Failed to determine device node with the highest priority for '%s': %m", slink);
×
489
        if (r > 0)
141,181✔
490
                return node_create_symlink(dev, devnode, slink);
80,913✔
491

492
        log_device_debug(dev, "No reference left for '%s', removing", slink);
60,268✔
493
        return node_remove_symlink(dev, slink);
60,268✔
494
}
495

496
static int device_get_devpath_by_devnum(sd_device *dev, char **ret) {
48,770✔
497
        dev_t devnum;
48,770✔
498
        int r;
48,770✔
499

500
        assert(dev);
48,770✔
501
        assert(ret);
48,770✔
502

503
        r = sd_device_get_devnum(dev, &devnum);
48,770✔
504
        if (r < 0)
48,770✔
505
                return r;
48,770✔
506

507
        return device_path_make_major_minor(device_in_subsystem(dev, "block") ? S_IFBLK : S_IFCHR, devnum, ret);
61,272✔
508
}
509

510
int udev_node_update(sd_device *dev, sd_device *dev_old) {
37,383✔
511
        _cleanup_free_ char *filename = NULL;
37,383✔
512
        int r;
37,383✔
513

514
        assert(dev);
37,383✔
515
        assert(dev_old);
37,383✔
516

517
        /* update possible left-over symlinks */
518
        FOREACH_DEVICE_DEVLINK(dev_old, devlink) {
97,559✔
519
                /* check if old link name still belongs to this device */
520
                if (device_has_devlink(dev, devlink))
60,176✔
521
                        continue;
55,020✔
522

523
                log_device_debug(dev,
5,156✔
524
                                 "Removing/updating old device symlink '%s', which is no longer belonging to this device.",
525
                                 devlink);
526

527
                r = link_update(dev, devlink, /* add = */ false);
5,156✔
528
                if (r < 0)
5,156✔
529
                        log_device_warning_errno(dev, r,
×
530
                                                 "Failed to remove/update device symlink '%s', ignoring: %m",
531
                                                 devlink);
532
        }
533

534
        /* create/update symlinks, add symlinks to name index */
535
        FOREACH_DEVICE_DEVLINK(dev, devlink) {
177,645✔
536
                r = link_update(dev, devlink, /* add = */ true);
140,262✔
537
                if (r < 0)
140,262✔
538
                        log_device_warning_errno(dev, r,
1✔
539
                                                 "Failed to create/update device symlink '%s', ignoring: %m",
540
                                                 devlink);
541
        }
542

543
        r = device_get_devpath_by_devnum(dev, &filename);
37,383✔
544
        if (r < 0)
37,383✔
545
                return log_device_debug_errno(dev, r, "Failed to get device path: %m");
×
546

547
        /* always add /dev/{block,char}/$major:$minor */
548
        r = node_create_symlink(dev, /* devnode = */ NULL, filename);
37,383✔
549
        if (r < 0)
37,383✔
550
                return log_device_warning_errno(dev, r, "Failed to create device symlink '%s': %m", filename);
×
551

552
        return 0;
553
}
554

555
int udev_node_remove(sd_device *dev) {
11,387✔
556
        _cleanup_free_ char *filename = NULL;
11,387✔
557
        int r;
11,387✔
558

559
        assert(dev);
11,387✔
560

561
        /* remove/update symlinks, remove symlinks from name index */
562
        FOREACH_DEVICE_DEVLINK(dev, devlink) {
79,348✔
563
                r = link_update(dev, devlink, /* add = */ false);
67,961✔
564
                if (r < 0)
67,961✔
565
                        log_device_warning_errno(dev, r,
×
566
                                                 "Failed to remove/update device symlink '%s', ignoring: %m",
567
                                                 devlink);
568
        }
569

570
        r = device_get_devpath_by_devnum(dev, &filename);
11,387✔
571
        if (r < 0)
11,387✔
572
                return log_device_debug_errno(dev, r, "Failed to get device path: %m");
×
573

574
        /* remove /dev/{block,char}/$major:$minor */
575
        return node_remove_symlink(dev, filename);
11,387✔
576
}
577

578
static int udev_node_apply_permissions_impl(
33,232✔
579
                sd_device *dev, /* can be NULL, only used for logging. */
580
                int node_fd,
581
                const char *devnode,
582
                bool apply_mac,
583
                mode_t mode,
584
                uid_t uid,
585
                gid_t gid,
586
                OrderedHashmap *seclabel_list) {
587

588
        bool apply_mode, apply_uid, apply_gid;
33,232✔
589
        struct stat stats;
33,232✔
590
        int r;
33,232✔
591

592
        assert(node_fd >= 0);
33,232✔
593
        assert(devnode);
33,232✔
594

595
        if (fstat(node_fd, &stats) < 0)
33,232✔
596
                return log_device_debug_errno(dev, errno, "cannot stat() node %s: %m", devnode);
×
597

598
        /* If group is set, but mode is not set, "upgrade" mode for the group. */
599
        if (mode == MODE_INVALID && gid_is_valid(gid) && gid > 0)
61,228✔
600
                mode = 0660;
601

602
        apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777);
17,707✔
603
        apply_uid = uid_is_valid(uid) && stats.st_uid != uid;
33,232✔
604
        apply_gid = gid_is_valid(gid) && stats.st_gid != gid;
33,232✔
605

606
        if (apply_mode || apply_uid || apply_gid || apply_mac) {
33,232✔
607
                bool selinux = false, smack = false;
18,305✔
608
                const char *name, *label;
18,305✔
609

610
                if (apply_mode || apply_uid || apply_gid) {
18,305✔
611
                        log_device_debug(dev, "Setting permissions %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
26,488✔
612
                                         devnode,
613
                                         uid_is_valid(uid) ? uid : stats.st_uid,
614
                                         gid_is_valid(gid) ? gid : stats.st_gid,
615
                                         mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
616

617
                        r = fchmod_and_chown(node_fd, mode, uid, gid);
13,244✔
618
                        if (r < 0)
13,244✔
619
                                log_device_full_errno(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
×
620
                                                      "Failed to set owner/mode of %s to uid=" UID_FMT
621
                                                      ", gid=" GID_FMT ", mode=%#o: %m",
622
                                                      devnode,
623
                                                      uid_is_valid(uid) ? uid : stats.st_uid,
624
                                                      gid_is_valid(gid) ? gid : stats.st_gid,
625
                                                      mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
626
                } else
627
                        log_device_debug(dev, "Preserve permissions of %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
10,122✔
628
                                         devnode,
629
                                         uid_is_valid(uid) ? uid : stats.st_uid,
630
                                         gid_is_valid(gid) ? gid : stats.st_gid,
631
                                         mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
632

633
                /* apply SECLABEL{$module}=$label */
634
                ORDERED_HASHMAP_FOREACH_KEY(label, name, seclabel_list) {
18,305✔
635
                        int q;
×
636

637
                        if (streq(name, "selinux")) {
×
638
                                selinux = true;
×
639

640
                                q = mac_selinux_apply_fd(node_fd, devnode, label);
×
641
                                if (q < 0)
×
642
                                        log_device_full_errno(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
×
643
                                                              "SECLABEL: failed to set SELinux label '%s': %m", label);
644
                                else
645
                                        log_device_debug(dev, "SECLABEL: set SELinux label '%s'", label);
×
646

647
                        } else if (streq(name, "smack")) {
×
648
                                smack = true;
×
649

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

657
                        } else
658
                                log_device_error(dev, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label);
×
659
                }
660

661
                /* set the defaults */
662
                if (!selinux)
18,305✔
663
                        (void) mac_selinux_fix_full(node_fd, NULL, devnode, LABEL_IGNORE_ENOENT);
18,305✔
664
                if (!smack)
18,305✔
665
                        (void) mac_smack_apply_fd(node_fd, SMACK_ATTR_ACCESS, NULL);
18,305✔
666
        }
667

668
        /* always update timestamp when we re-use the node, like on media change events */
669
        r = futimens_opath(node_fd, NULL);
33,232✔
670
        if (r < 0)
33,232✔
671
                log_device_debug_errno(dev, r, "Failed to adjust timestamp of node %s: %m", devnode);
×
672

673
        return 0;
674
}
675

676
int udev_node_apply_permissions(
37,383✔
677
                sd_device *dev,
678
                bool apply_mac,
679
                mode_t mode,
680
                uid_t uid,
681
                gid_t gid,
682
                OrderedHashmap *seclabel_list) {
683

684
        const char *devnode;
37,383✔
685
        _cleanup_close_ int node_fd = -EBADF;
37,383✔
686
        int r;
37,383✔
687

688
        assert(dev);
37,383✔
689

690
        r = sd_device_get_devname(dev, &devnode);
37,383✔
691
        if (r < 0)
37,383✔
692
                return log_device_debug_errno(dev, r, "Failed to get devname: %m");
×
693

694
        node_fd = sd_device_open(dev, O_PATH|O_CLOEXEC);
37,383✔
695
        if (node_fd < 0) {
37,383✔
696
                if (ERRNO_IS_DEVICE_ABSENT(node_fd)) {
4,727✔
697
                        log_device_debug_errno(dev, node_fd, "Device node %s is missing, skipping handling.", devnode);
4,727✔
698
                        return 0; /* This is necessarily racey, so ignore missing the device */
4,727✔
699
                }
700

701
                return log_device_debug_errno(dev, node_fd, "Cannot open node %s: %m", devnode);
×
702
        }
703

704
        return udev_node_apply_permissions_impl(dev, node_fd, devnode, apply_mac, mode, uid, gid, seclabel_list);
32,656✔
705
}
706

707
int static_node_apply_permissions(
605✔
708
                const char *name,
709
                mode_t mode,
710
                uid_t uid,
711
                gid_t gid,
712
                char **tags) {
713

714
        _cleanup_free_ char *unescaped_filename = NULL;
605✔
715
        _cleanup_close_ int node_fd = -EBADF;
605✔
716
        const char *devnode;
605✔
717
        struct stat stats;
605✔
718
        int r;
605✔
719

720
        assert(name);
605✔
721

722
        if (uid == UID_INVALID && gid == GID_INVALID && mode == MODE_INVALID && !tags)
605✔
723
                return 0;
724

725
        devnode = strjoina("/dev/", name);
3,025✔
726

727
        node_fd = open(devnode, O_PATH|O_CLOEXEC);
605✔
728
        if (node_fd < 0) {
605✔
729
                if (errno != ENOENT)
29✔
730
                        return log_error_errno(errno, "Failed to open %s: %m", devnode);
×
731
                return 0;
732
        }
733

734
        if (fstat(node_fd, &stats) < 0)
576✔
735
                return log_error_errno(errno, "Failed to stat %s: %m", devnode);
×
736

737
        if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) {
576✔
738
                log_warning("%s is neither block nor character device, ignoring.", devnode);
×
739
                return 0;
×
740
        }
741

742
        if (!strv_isempty(tags)) {
576✔
743
                unescaped_filename = xescape(name, "/.");
110✔
744
                if (!unescaped_filename)
110✔
745
                        return log_oom();
×
746
        }
747

748
        /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
749
        STRV_FOREACH(t, tags) {
686✔
750
                _cleanup_free_ char *p = NULL;
110✔
751

752
                p = path_join("/run/udev/static_node-tags/", *t, unescaped_filename);
110✔
753
                if (!p)
110✔
754
                        return log_oom();
×
755

756
                r = mkdir_parents(p, 0755);
110✔
757
                if (r < 0)
110✔
758
                        return log_error_errno(r, "Failed to create parent directory for %s: %m", p);
×
759

760
                r = symlink(devnode, p);
110✔
761
                if (r < 0 && errno != EEXIST)
110✔
762
                        return log_error_errno(errno, "Failed to create symlink %s -> %s: %m", p, devnode);
×
763
        }
764

765
        return udev_node_apply_permissions_impl(NULL, node_fd, devnode, false, mode, uid, gid, NULL);
576✔
766
}
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