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

systemd / systemd / 15057632786

15 May 2025 09:01PM UTC coverage: 72.267% (+0.02%) from 72.244%
15057632786

push

github

bluca
man: document how to hook stuff into system wakeup

Fixes: #6364

298523 of 413084 relevant lines covered (72.27%)

738132.88 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

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 "errno-util.h"
13
#include "escape.h"
14
#include "fd-util.h"
15
#include "fileio.h"
16
#include "format-util.h"
17
#include "fs-util.h"
18
#include "hexdecoct.h"
19
#include "label-util.h"
20
#include "mkdir-label.h"
21
#include "parse-util.h"
22
#include "path-util.h"
23
#include "selinux-util.h"
24
#include "smack-util.h"
25
#include "stat-util.h"
26
#include "string-util.h"
27
#include "strv.h"
28
#include "udev-node.h"
29
#include "user-util.h"
30

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

33
static int node_remove_symlink(sd_device *dev, const char *slink) {
93,050✔
34
        assert(dev);
93,050✔
35
        assert(slink);
93,050✔
36

37
        if (unlink(slink) < 0 && errno != ENOENT)
93,050✔
38
                return log_device_debug_errno(dev, errno, "Failed to remove '%s': %m", slink);
×
39

40
        (void) rmdir_parents(slink, "/dev");
93,050✔
41
        return 0;
93,050✔
42
}
43

44
static int node_create_symlink(sd_device *dev, const char *devnode, const char *slink) {
155,597✔
45
        struct stat st;
155,597✔
46
        int r;
155,597✔
47

48
        assert(dev);
155,597✔
49
        assert(slink);
155,597✔
50

51
        if (!devnode) {
155,597✔
52
                r = sd_device_get_devname(dev, &devnode);
54,212✔
53
                if (r < 0)
54,212✔
54
                        return log_device_debug_errno(dev, r, "Failed to get device node: %m");
×
55
        }
56

57
        if (lstat(slink, &st) >= 0) {
155,597✔
58
                if (!S_ISLNK(st.st_mode))
41,375✔
59
                        return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
×
60
                                                      "Conflicting inode '%s' found, symlink to '%s' will not be created.",
61
                                                      slink, devnode);
62
        } else if (errno != ENOENT)
114,222✔
63
                return log_device_debug_errno(dev, errno, "Failed to lstat() '%s': %m", slink);
×
64

65
        r = mkdir_parents_label(slink, 0755);
155,597✔
66
        if (r < 0)
155,597✔
67
                return log_device_debug_errno(dev, r, "Failed to create parent directory of '%s': %m", slink);
×
68

69
        /* use relative link */
70
        r = symlink_atomic_full_label(devnode, slink, /* make_relative = */ true);
155,597✔
71
        if (r < 0)
155,597✔
72
                return log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink, devnode);
×
73

74
        log_device_debug(dev, "Successfully created symlink '%s' to '%s'", slink, devnode);
157,541✔
75
        return 0;
155,597✔
76
}
77

78
static int stack_directory_read_one(int dirfd, const char *id, char **devnode, int *priority) {
116,180✔
79
        _cleanup_free_ char *buf = NULL;
116,180✔
80
        int tmp_prio, r;
116,180✔
81

82
        assert(dirfd >= 0);
116,180✔
83
        assert(id);
116,180✔
84
        assert(priority);
116,180✔
85

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

90
        /* First, let's try to read the entry with the new format, which should replace the old format pretty
91
         * quickly. */
92
        r = readlinkat_malloc(dirfd, id, &buf);
116,180✔
93
        if (r >= 0) {
116,180✔
94
                char *colon;
109,296✔
95

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

98
                colon = strchr(buf, ':');
109,296✔
99
                if (!colon || colon == buf)
109,296✔
100
                        return -EINVAL;
101

102
                *colon = '\0';
109,296✔
103

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

111
                r = safe_atoi(buf, &tmp_prio);
45,303✔
112
                if (r < 0)
45,303✔
113
                        return r;
114

115
                if (!devnode)
45,303✔
116
                        goto finalize;
41,602✔
117

118
                if (*devnode && tmp_prio <= *priority)
3,701✔
119
                        return 0; /* Unchanged */
120

121
                r = free_and_strdup(devnode, colon + 1);
1,177✔
122
                if (r < 0)
1,177✔
123
                        return r;
124

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

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

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

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

139
                if (!devnode)
×
140
                        goto finalize;
×
141

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

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

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

153
        } else
154
                return r == -ENOENT ? -ENODEV : r;
6,884✔
155

156
finalize:
42,779✔
157
        *priority = tmp_prio;
42,779✔
158
        return 1; /* Updated */
42,779✔
159
}
160

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

167
        assert(dev);
179,376✔
168
        assert(dirfd >= 0);
179,376✔
169
        assert(ret);
179,376✔
170

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

174
        if (add) {
179,376✔
175
                const char *n;
100,208✔
176

177
                r = device_get_devlink_priority(dev, &priority);
100,208✔
178
                if (r < 0)
100,208✔
179
                        return r;
×
180

181
                r = sd_device_get_devname(dev, &n);
100,208✔
182
                if (r < 0)
100,208✔
183
                        return r;
184

185
                devnode = strdup(n);
100,208✔
186
                if (!devnode)
100,208✔
187
                        return -ENOMEM;
188
        }
189

190
        dir = xopendirat(dirfd, ".", O_NOFOLLOW);
179,376✔
191
        if (!dir)
179,376✔
192
                return -errno;
×
193

194
        r = sd_device_get_device_id(dev, &id);
179,376✔
195
        if (r < 0)
179,376✔
196
                return r;
197

198
        FOREACH_DIRENT(de, dir, break) {
706,030✔
199

200
                /* skip ourself */
201
                if (streq(de->d_name, id))
167,902✔
202
                        continue;
100,208✔
203

204
                r = stack_directory_read_one(dirfd, de->d_name, &devnode, &priority);
67,694✔
205
                if (r < 0 && r != -ENODEV)
67,694✔
206
                        log_debug_errno(r, "Failed to read '%s', ignoring: %m", de->d_name);
526,654✔
207
        }
208

209
        *ret = TAKE_PTR(devnode);
179,376✔
210
        return !!*ret;
179,376✔
211
}
212

213
static int stack_directory_update(sd_device *dev, int fd, bool add) {
242,143✔
214
        const char *id;
242,143✔
215
        int r;
242,143✔
216

217
        assert(dev);
242,143✔
218
        assert(fd >= 0);
242,143✔
219

220
        r = sd_device_get_device_id(dev, &id);
242,143✔
221
        if (r < 0)
242,143✔
222
                return r;
242,143✔
223

224
        if (add) {
242,143✔
225
                _cleanup_free_ char *data = NULL, *buf = NULL;
141,804✔
226
                const char *devname;
141,804✔
227
                int priority;
141,804✔
228

229
                r = sd_device_get_devname(dev, &devname);
141,804✔
230
                if (r < 0)
141,804✔
231
                        return r;
232

233
                r = device_get_devlink_priority(dev, &priority);
141,804✔
234
                if (r < 0)
141,804✔
235
                        return r;
236

237
                if (asprintf(&data, "%i:%s", priority, devname) < 0)
141,804✔
238
                        return -ENOMEM;
239

240
                if (readlinkat_malloc(fd, id, &buf) >= 0 && streq(buf, data))
141,804✔
241
                        return 0; /* Unchanged. */
242

243
                (void) unlinkat(fd, id, 0);
112,586✔
244

245
                if (symlinkat(data, fd, id) < 0)
112,586✔
246
                        return -errno;
×
247

248
        } else {
249
                if (unlinkat(fd, id, 0) < 0) {
100,339✔
250
                        if (errno == ENOENT)
×
251
                                return 0; /* Unchanged. */
252
                        return -errno;
×
253
                }
254
        }
255

256
        return 1; /* Updated. */
257
}
258

259
size_t udev_node_escape_path(const char *src, char *dest, size_t size) {
242,154✔
260
        size_t i, j;
242,154✔
261
        uint64_t h;
242,154✔
262

263
        assert(src);
242,154✔
264
        assert(dest);
242,154✔
265
        assert(size >= 12);
242,154✔
266

267
        for (i = 0, j = 0; src[i] != '\0'; i++) {
12,517,531✔
268
                if (src[i] == '/') {
12,275,380✔
269
                        if (j+4 >= size - 12 + 1)
631,537✔
270
                                goto toolong;
×
271
                        memcpy(&dest[j], "\\x2f", 4);
631,537✔
272
                        j += 4;
631,537✔
273
                } else if (src[i] == '\\') {
11,643,843✔
274
                        if (j+4 >= size - 12 + 1)
3,134✔
275
                                goto toolong;
×
276
                        memcpy(&dest[j], "\\x5c", 4);
3,134✔
277
                        j += 4;
3,134✔
278
                } else {
279
                        if (j+1 >= size - 12 + 1)
11,640,709✔
280
                                goto toolong;
3✔
281
                        dest[j] = src[i];
11,640,706✔
282
                        j++;
11,640,706✔
283
                }
284
        }
285
        dest[j] = '\0';
242,151✔
286
        return j;
242,151✔
287

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

292
        h = siphash24_string(src, UDEV_NODE_HASH_KEY.bytes);
3✔
293

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

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

301
static int stack_directory_get_name(const char *slink, char **ret) {
242,143✔
302
        _cleanup_free_ char *s = NULL;
242,143✔
303
        char name_enc[NAME_MAX+1];
242,143✔
304
        const char *name;
242,143✔
305
        int r;
242,143✔
306

307
        assert(slink);
242,143✔
308
        assert(ret);
242,143✔
309

310
        r = path_simplify_alloc(slink, &s);
242,143✔
311
        if (r < 0)
242,143✔
312
                return r;
313

314
        if (!path_is_normalized(s))
242,143✔
315
                return -EINVAL;
316

317
        name = path_startswith(s, "/dev");
242,143✔
318
        if (empty_or_root(name))
242,143✔
319
                return -EINVAL;
320

321
        udev_node_escape_path(name, name_enc, sizeof(name_enc));
242,143✔
322

323
        return strdup_to(ret, name_enc);
242,143✔
324
}
325

326
static int stack_directory_open_and_lock(
242,143✔
327
                sd_device *dev,
328
                const char *slink,
329
                char **ret_dirpath,
330
                int *ret_dirfd,
331
                LockFile *ret_lockfile) {
332

333
        _cleanup_(release_lock_file) LockFile lockfile = LOCK_FILE_INIT;
×
334
        _cleanup_close_ int dirfd = -EBADF;
242,143✔
335
        _cleanup_free_ char *name = NULL, *dirpath = NULL, *lockname = NULL;
242,143✔
336
        int r;
242,143✔
337

338
        assert(dev);
242,143✔
339
        assert(slink);
242,143✔
340
        assert(ret_dirpath);
242,143✔
341
        assert(ret_dirfd);
242,143✔
342
        assert(ret_lockfile);
242,143✔
343

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

348
        FOREACH_STRING(s, "/run/udev/links/", "/run/udev/links.lock/") {
726,429✔
349
                r = mkdir_p(s, 0755);
484,286✔
350
                if (r < 0)
484,286✔
351
                        return log_device_debug_errno(dev, r, "Failed to create '%s': %m", s);
×
352
        }
353

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

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

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

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

373
        *ret_dirpath = TAKE_PTR(dirpath);
242,143✔
374
        *ret_dirfd = TAKE_FD(dirfd);
242,143✔
375
        *ret_lockfile = TAKE_GENERIC(lockfile, LockFile, LOCK_FILE_INIT);
242,143✔
376
        return 0;
242,143✔
377
}
378

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

385
        assert(slink);
242,143✔
386
        assert(dirfd >= 0);
242,143✔
387
        assert(ret_id);
242,143✔
388

389
        r = sd_device_new_from_devname(&dev, slink);
242,143✔
390
        if (r < 0)
242,143✔
391
                return r;
392

393
        r = sd_device_get_device_id(dev, &id);
121,943✔
394
        if (r < 0)
121,943✔
395
                return r;
396

397
        id_dup = strdup(id);
121,943✔
398
        if (!id_dup)
121,943✔
399
                return -ENOMEM;
400

401
        if (ret_prio) {
121,943✔
402
                r = stack_directory_read_one(dirfd, id, NULL, ret_prio);
48,486✔
403
                if (r < 0)
48,486✔
404
                        return r;
405
        }
406

407
        *ret_id = TAKE_PTR(id_dup);
115,059✔
408
        return 0;
115,059✔
409
}
410

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

423
        assert(dev);
242,143✔
424
        assert(slink);
242,143✔
425

426
        r = stack_directory_open_and_lock(dev, slink, &dirpath, &dirfd, &lockfile);
242,143✔
427
        if (r < 0)
242,143✔
428
                return r;
429

430
        r = node_get_current(slink, dirfd, &current_id, add ? &current_prio : NULL);
342,482✔
431
        if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
242,143✔
432
                return log_device_debug_errno(dev, r, "Failed to get the current device node priority for '%s': %m", slink);
×
433

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

438
        if (current_id) {
242,143✔
439
                const char *id;
115,059✔
440

441
                r = sd_device_get_device_id(dev, &id);
115,059✔
442
                if (r < 0)
115,059✔
443
                        return log_device_debug_errno(dev, r, "Failed to get device id: %m");
62,767✔
444

445
                if (add) {
115,059✔
446
                        int prio;
41,602✔
447

448
                        r = device_get_devlink_priority(dev, &prio);
41,602✔
449
                        if (r < 0)
41,602✔
450
                                return log_device_debug_errno(dev, r, "Failed to get devlink priority: %m");
41,596✔
451

452
                        if (streq(current_id, id)) {
41,602✔
453
                                if (current_prio <= prio)
25,022✔
454
                                        /* The devlink is ours and already exists, and the new priority is
455
                                         * equal or higher than the previous. Hence, it is not necessary to
456
                                         * recreate it. */
457
                                        return 0;
458

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

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

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

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

488
        r = stack_directory_find_prioritized_devnode(dev, dirfd, add, &devnode);
179,376✔
489
        if (r < 0)
179,376✔
490
                return log_device_debug_errno(dev, r, "Failed to determine device node with the highest priority for '%s': %m", slink);
×
491
        if (r > 0)
179,376✔
492
                return node_create_symlink(dev, devnode, slink);
101,385✔
493

494
        log_device_debug(dev, "No reference left for '%s', removing", slink);
77,991✔
495
        return node_remove_symlink(dev, slink);
77,991✔
496
}
497

498
static int device_get_devpath_by_devnum(sd_device *dev, char **ret) {
54,077✔
499
        dev_t devnum;
54,077✔
500
        int r;
54,077✔
501

502
        assert(dev);
54,077✔
503
        assert(ret);
54,077✔
504

505
        r = sd_device_get_devnum(dev, &devnum);
54,077✔
506
        if (r < 0)
54,077✔
507
                return r;
54,077✔
508

509
        r = device_in_subsystem(dev, "block");
54,077✔
510
        if (r < 0)
54,077✔
511
                return r;
512

513
        return device_path_make_major_minor(r > 0 ? S_IFBLK : S_IFCHR, devnum, ret);
67,682✔
514
}
515

516
int udev_node_update(sd_device *dev, sd_device *dev_old) {
39,018✔
517
        _cleanup_free_ char *filename = NULL;
39,018✔
518
        int r;
39,018✔
519

520
        assert(dev);
39,018✔
521
        assert(dev_old);
39,018✔
522

523
        /* update possible left-over symlinks */
524
        FOREACH_DEVICE_DEVLINK(dev_old, devlink) {
71,179✔
525
                /* check if old link name still belongs to this device */
526
                if (device_has_devlink(dev, devlink))
32,161✔
527
                        continue;
29,230✔
528

529
                log_device_debug(dev,
2,931✔
530
                                 "Removing/updating old device symlink '%s', which is no longer belonging to this device.",
531
                                 devlink);
532

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

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

549
        r = device_get_devpath_by_devnum(dev, &filename);
39,018✔
550
        if (r < 0)
39,018✔
551
                return log_device_debug_errno(dev, r, "Failed to get device path: %m");
×
552

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

558
        return 0;
559
}
560

561
int udev_node_remove(sd_device *dev) {
15,059✔
562
        _cleanup_free_ char *filename = NULL;
15,059✔
563
        int r;
15,059✔
564

565
        assert(dev);
15,059✔
566

567
        /* remove/update symlinks, remove symlinks from name index */
568
        FOREACH_DEVICE_DEVLINK(dev, devlink) {
112,467✔
569
                r = link_update(dev, devlink, /* add = */ false);
97,408✔
570
                if (r < 0)
97,408✔
571
                        log_device_warning_errno(dev, r,
×
572
                                                 "Failed to remove/update device symlink '%s', ignoring: %m",
573
                                                 devlink);
574
        }
575

576
        r = device_get_devpath_by_devnum(dev, &filename);
15,059✔
577
        if (r < 0)
15,059✔
578
                return log_device_debug_errno(dev, r, "Failed to get device path: %m");
×
579

580
        /* remove /dev/{block,char}/$major:$minor */
581
        return node_remove_symlink(dev, filename);
15,059✔
582
}
583

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

594
        bool apply_mode, apply_uid, apply_gid;
34,848✔
595
        struct stat stats;
34,848✔
596
        int r;
34,848✔
597

598
        assert(node_fd >= 0);
34,848✔
599
        assert(devnode);
34,848✔
600

601
        if (fstat(node_fd, &stats) < 0)
34,848✔
602
                return log_device_debug_errno(dev, errno, "cannot stat() node %s: %m", devnode);
×
603

604
        /* If group is set, but mode is not set, "upgrade" mode for the group. */
605
        if (mode == MODE_INVALID && gid_is_valid(gid) && gid > 0)
64,112✔
606
                mode = 0660;
607

608
        apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777);
21,558✔
609
        apply_uid = uid_is_valid(uid) && stats.st_uid != uid;
34,848✔
610
        apply_gid = gid_is_valid(gid) && stats.st_gid != gid;
34,848✔
611

612
        if (apply_mode || apply_uid || apply_gid || apply_mac) {
34,848✔
613
                bool selinux = false, smack = false;
22,127✔
614
                const char *name, *label;
22,127✔
615

616
                if (apply_mode || apply_uid || apply_gid) {
22,127✔
617
                        log_device_debug(dev, "Setting permissions %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
26,030✔
618
                                         devnode,
619
                                         uid_is_valid(uid) ? uid : stats.st_uid,
620
                                         gid_is_valid(gid) ? gid : stats.st_gid,
621
                                         mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
622

623
                        r = fchmod_and_chown(node_fd, mode, uid, gid);
13,015✔
624
                        if (r < 0)
13,015✔
625
                                log_device_full_errno(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
×
626
                                                      "Failed to set owner/mode of %s to uid=" UID_FMT
627
                                                      ", gid=" GID_FMT ", mode=%#o: %m",
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
                } else
633
                        log_device_debug(dev, "Preserve permissions of %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
18,224✔
634
                                         devnode,
635
                                         uid_is_valid(uid) ? uid : stats.st_uid,
636
                                         gid_is_valid(gid) ? gid : stats.st_gid,
637
                                         mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
638

639
                /* apply SECLABEL{$module}=$label */
640
                ORDERED_HASHMAP_FOREACH_KEY(label, name, seclabel_list) {
22,127✔
641
                        int q;
×
642

643
                        if (streq(name, "selinux")) {
×
644
                                selinux = true;
×
645

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

653
                        } else if (streq(name, "smack")) {
×
654
                                smack = true;
×
655

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

663
                        } else
664
                                log_device_error(dev, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label);
×
665
                }
666

667
                /* set the defaults */
668
                if (!selinux)
22,127✔
669
                        (void) mac_selinux_fix_full(node_fd, NULL, devnode, LABEL_IGNORE_ENOENT);
22,127✔
670
                if (!smack)
22,127✔
671
                        (void) mac_smack_apply_fd(node_fd, SMACK_ATTR_ACCESS, NULL);
22,127✔
672
        }
673

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

679
        return 0;
680
}
681

682
int udev_node_apply_permissions(
39,018✔
683
                sd_device *dev,
684
                bool apply_mac,
685
                mode_t mode,
686
                uid_t uid,
687
                gid_t gid,
688
                OrderedHashmap *seclabel_list) {
689

690
        const char *devnode;
39,018✔
691
        _cleanup_close_ int node_fd = -EBADF;
39,018✔
692
        int r;
39,018✔
693

694
        assert(dev);
39,018✔
695

696
        r = sd_device_get_devname(dev, &devnode);
39,018✔
697
        if (r < 0)
39,018✔
698
                return log_device_debug_errno(dev, r, "Failed to get devname: %m");
×
699

700
        node_fd = sd_device_open(dev, O_PATH|O_CLOEXEC);
39,018✔
701
        if (node_fd < 0) {
39,018✔
702
                if (ERRNO_IS_DEVICE_ABSENT(node_fd)) {
4,757✔
703
                        log_device_debug_errno(dev, node_fd, "Device node %s is missing, skipping handling.", devnode);
4,757✔
704
                        return 0; /* This is necessarily racey, so ignore missing the device */
4,757✔
705
                }
706

707
                return log_device_debug_errno(dev, node_fd, "Cannot open node %s: %m", devnode);
×
708
        }
709

710
        return udev_node_apply_permissions_impl(dev, node_fd, devnode, apply_mac, mode, uid, gid, seclabel_list);
34,261✔
711
}
712

713
int static_node_apply_permissions(
616✔
714
                const char *name,
715
                mode_t mode,
716
                uid_t uid,
717
                gid_t gid,
718
                char **tags) {
719

720
        _cleanup_free_ char *unescaped_filename = NULL;
616✔
721
        _cleanup_close_ int node_fd = -EBADF;
616✔
722
        const char *devnode;
616✔
723
        struct stat stats;
616✔
724
        int r;
616✔
725

726
        assert(name);
616✔
727

728
        if (uid == UID_INVALID && gid == GID_INVALID && mode == MODE_INVALID && !tags)
616✔
729
                return 0;
730

731
        devnode = strjoina("/dev/", name);
3,080✔
732

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

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

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

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

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

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

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

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

771
        return udev_node_apply_permissions_impl(NULL, node_fd, devnode, false, mode, uid, gid, NULL);
587✔
772
}
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