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

systemd / systemd / 26546993077

27 May 2026 08:34PM UTC coverage: 72.995% (+0.3%) from 72.667%
26546993077

push

github

bluca
test-pressure: set timeout to make not wait forever

If this runs on a slow or busy machine, then we may not get enough
pressure to trigger the event sources. In such case, the test does not
finish. It is problematic when the test is _not_ run with 'meson test',
e.g. debian/ubuntu CIs.

Let's introduce a timeout for each event loop, and skip test cases
gracefully.

8 of 12 new or added lines in 1 file covered. (66.67%)

19671 existing lines in 226 files now uncovered.

337119 of 461841 relevant lines covered (72.99%)

1326365.62 hits per line

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

83.42
/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.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) {
45,344✔
37
        assert(dev);
45,344✔
38
        assert(slink);
45,344✔
39

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

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

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

51
        assert(dev);
100,355✔
52
        assert(slink);
100,355✔
53

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

60
        if (lstat(slink, &st) >= 0) {
100,355✔
61
                if (!S_ISLNK(st.st_mode))
44,759✔
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)
55,596✔
66
                return log_device_debug_errno(dev, errno, "Failed to lstat() '%s': %m", slink);
×
67

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

72
        /* use relative link */
73
        r = symlinkat_atomic_full(devnode, AT_FDCWD, slink, SYMLINK_MAKE_RELATIVE|SYMLINK_LABEL);
100,355✔
74
        if (r < 0)
100,355✔
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);
102,131✔
78
        return 0;
100,355✔
79
}
80

81
static int stack_directory_read_one(int dirfd, const char *id, char **devnode, int *priority) {
70,890✔
82
        int r;
70,890✔
83

84
        assert(dirfd >= 0);
70,890✔
85
        assert(id);
70,890✔
86
        assert(priority);
70,890✔
87

88
        /* This reads priority and device node from the symlink under /run/udev/links/ directory.
89
         * If 'devnode' is NULL, obtained priority is always set to '*priority'. If 'devnode' is non-NULL,
90
         * this updates '*devnode' and '*priority' if the obtained one has a higher priority. */
91

92
        _cleanup_free_ char *buf = NULL;
70,890✔
93
        r = readlinkat_malloc(dirfd, id, &buf);
70,890✔
94
        if (r < 0)
70,890✔
95
                return r == -ENOENT ? -ENODEV : r;
440✔
96

97
        char *colon = strchr(buf, ':');
70,450✔
98
        if (!colon || colon == buf)
70,450✔
99
                return -EINVAL;
100

101
        *colon = '\0';
70,450✔
102

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

110
        int tmp_prio;
68,126✔
111
        r = safe_atoi(buf, &tmp_prio);
68,126✔
112
        if (r < 0)
68,126✔
113
                return r;
114

115
        if (devnode) {
68,126✔
116
                if (*devnode && tmp_prio <= *priority)
3,672✔
117
                        return 0; /* Unchanged */
118

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

124
        *priority = tmp_prio;
66,756✔
125
        return 1; /* Updated */
66,756✔
126
}
127

128
static int stack_directory_find_prioritized_devnode(sd_device *dev, int dirfd, bool add, char **ret) {
97,191✔
129
        _cleanup_closedir_ DIR *dir = NULL;
97,191✔
130
        _cleanup_free_ char *devnode = NULL;
97,191✔
131
        int r, priority;
97,191✔
132
        const char *id;
97,191✔
133

134
        assert(dev);
97,191✔
135
        assert(dirfd >= 0);
97,191✔
136
        assert(ret);
97,191✔
137

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

141
        if (add) {
97,191✔
142
                const char *n;
56,631✔
143

144
                r = device_get_devlink_priority(dev, &priority);
56,631✔
145
                if (r < 0)
56,631✔
146
                        return r;
×
147

148
                r = sd_device_get_devname(dev, &n);
56,631✔
149
                if (r < 0)
56,631✔
150
                        return r;
151

152
                devnode = strdup(n);
56,631✔
153
                if (!devnode)
56,631✔
154
                        return -ENOMEM;
155
        }
156

157
        dir = xopendirat(dirfd, /* path= */ NULL, /* flags= */ 0);
97,191✔
158
        if (!dir)
97,191✔
159
                return -errno;
×
160

161
        r = sd_device_get_device_id(dev, &id);
97,191✔
162
        if (r < 0)
97,191✔
163
                return r;
164

165
        FOREACH_DIRENT(de, dir, break) {
354,200✔
166

167
                /* skip ourself */
168
                if (streq(de->d_name, id))
62,627✔
169
                        continue;
56,631✔
170

171
                r = stack_directory_read_one(dirfd, de->d_name, &devnode, &priority);
5,996✔
172
                if (r < 0 && r != -ENODEV)
5,996✔
173
                        log_debug_errno(r, "Failed to read '%s', ignoring: %m", de->d_name);
257,009✔
174
        }
175

176
        *ret = TAKE_PTR(devnode);
97,191✔
177
        return !!*ret;
97,191✔
178
}
179

180
static int link_search_and_update(sd_device *dev, const char *slink, int dirfd, bool add) {
97,191✔
181
        int r;
97,191✔
182

183
        assert(dev);
97,191✔
184
        assert(slink);
97,191✔
185
        assert(dirfd >= 0);
97,191✔
186

187
        _cleanup_free_ char *devnode = NULL;
97,191✔
188
        r = stack_directory_find_prioritized_devnode(dev, dirfd, add, &devnode);
97,191✔
189
        if (r < 0)
97,191✔
190
                return log_device_debug_errno(dev, r, "Failed to determine device node with the highest priority for '%s': %m", slink);
×
191
        if (r > 0)
97,191✔
192
                return node_create_symlink(dev, devnode, slink);
58,655✔
193

194
        log_device_debug(dev, "No reference left for '%s', removing", slink);
38,536✔
195
        return node_remove_symlink(dev, slink);
38,536✔
196
}
197

198
static int stack_directory_update(sd_device *dev, int fd, bool add) {
161,645✔
199
        const char *id;
161,645✔
200
        int r;
161,645✔
201

202
        assert(dev);
161,645✔
203
        assert(fd >= 0);
161,645✔
204

205
        r = sd_device_get_device_id(dev, &id);
161,645✔
206
        if (r < 0)
161,645✔
207
                return r;
161,645✔
208

209
        if (add) {
161,645✔
210
                _cleanup_free_ char *data = NULL, *buf = NULL;
121,085✔
211
                const char *devname;
121,085✔
212
                int priority;
121,085✔
213

214
                r = sd_device_get_devname(dev, &devname);
121,085✔
215
                if (r < 0)
121,085✔
216
                        return r;
217

218
                r = device_get_devlink_priority(dev, &priority);
121,085✔
219
                if (r < 0)
121,085✔
220
                        return r;
221

222
                if (asprintf(&data, "%i:%s", priority, devname) < 0)
121,085✔
223
                        return -ENOMEM;
224

225
                if (readlinkat_malloc(fd, id, &buf) >= 0 && streq(buf, data))
121,085✔
226
                        return 0; /* Unchanged. */
227

228
                (void) unlinkat(fd, id, 0);
45,170✔
229

230
                if (symlinkat(data, fd, id) < 0)
45,170✔
231
                        return -errno;
×
232

233
        } else {
234
                if (unlinkat(fd, id, 0) < 0) {
40,560✔
235
                        if (errno == ENOENT)
×
236
                                return 0; /* Unchanged. */
237
                        return -errno;
×
238
                }
239
        }
240

241
        return 1; /* Updated. */
242
}
243

244
size_t udev_node_escape_path(const char *src, char *dest, size_t size) {
161,656✔
245
        size_t i, j;
161,656✔
246
        uint64_t h;
161,656✔
247

248
        assert(src);
161,656✔
249
        assert(dest);
161,656✔
250
        assert(size >= 12);
161,656✔
251

252
        for (i = 0, j = 0; src[i] != '\0'; i++) {
7,851,143✔
253
                if (src[i] == '/') {
7,689,490✔
254
                        if (j+4 >= size - 12 + 1)
405,107✔
255
                                goto toolong;
×
256
                        memcpy(&dest[j], "\\x2f", 4);
405,107✔
257
                        j += 4;
405,107✔
258
                } else if (src[i] == '\\') {
7,284,383✔
259
                        if (j+4 >= size - 12 + 1)
9,590✔
260
                                goto toolong;
×
261
                        memcpy(&dest[j], "\\x5c", 4);
9,590✔
262
                        j += 4;
9,590✔
263
                } else {
264
                        if (j+1 >= size - 12 + 1)
7,274,793✔
265
                                goto toolong;
3✔
266
                        dest[j] = src[i];
7,274,790✔
267
                        j++;
7,274,790✔
268
                }
269
        }
270
        dest[j] = '\0';
161,653✔
271
        return j;
161,653✔
272

273
toolong:
3✔
274
        /* If the input path is too long to encode as a filename, then let's suffix with a string
275
         * generated from the hash of the path. */
276

277
        h = siphash24_string(src, UDEV_NODE_HASH_KEY.bytes);
3✔
278

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

282
        dest[size - 1] = '\0';
3✔
283
        return size - 1;
3✔
284
}
285

286
static int stack_directory_get_name(const char *slink, char **ret) {
161,645✔
287
        _cleanup_free_ char *s = NULL;
161,645✔
288
        char name_enc[NAME_MAX+1];
161,645✔
289
        const char *name;
161,645✔
290
        int r;
161,645✔
291

292
        assert(slink);
161,645✔
293
        assert(ret);
161,645✔
294

295
        r = path_simplify_alloc(slink, &s);
161,645✔
296
        if (r < 0)
161,645✔
297
                return r;
298

299
        if (!path_is_normalized(s))
161,645✔
300
                return -EINVAL;
301

302
        name = path_startswith(s, "/dev");
161,645✔
303
        if (empty_or_root(name))
161,645✔
304
                return -EINVAL;
305

306
        udev_node_escape_path(name, name_enc, sizeof(name_enc));
161,645✔
307

308
        return strdup_to(ret, name_enc);
161,645✔
309
}
310

311
static int stack_directory_open_and_lock(
161,645✔
312
                sd_device *dev,
313
                const char *slink,
314
                char **ret_dirpath,
315
                int *ret_dirfd,
316
                LockFile *ret_lockfile) {
317

318
        _cleanup_(release_lock_file) LockFile lockfile = LOCK_FILE_INIT;
×
319
        _cleanup_close_ int dirfd = -EBADF;
161,645✔
320
        _cleanup_free_ char *name = NULL, *dirpath = NULL, *lockname = NULL;
161,645✔
321
        int r;
161,645✔
322

323
        assert(dev);
161,645✔
324
        assert(slink);
161,645✔
325
        assert(ret_dirpath);
161,645✔
326
        assert(ret_dirfd);
161,645✔
327
        assert(ret_lockfile);
161,645✔
328

329
        r = stack_directory_get_name(slink, &name);
161,645✔
330
        if (r < 0)
161,645✔
331
                return log_device_debug_errno(dev, r, "Failed to build stack directory name for '%s': %m", slink);
×
332

333
        FOREACH_STRING(s, "/run/udev/links/", "/run/udev/links.lock/") {
484,935✔
334
                r = mkdir_p(s, 0755);
323,290✔
335
                if (r < 0)
323,290✔
336
                        return log_device_debug_errno(dev, r, "Failed to create '%s': %m", s);
×
337
        }
338

339
        /* 1. Take a lock for the stack directory. */
340
        lockname = path_join("/run/udev/links.lock/", name);
161,645✔
341
        if (!lockname)
161,645✔
342
                return -ENOMEM;
343

344
        r = make_lock_file(lockname, LOCK_EX, &lockfile);
161,645✔
345
        if (r < 0)
161,645✔
346
                return log_device_debug_errno(dev, r, "Failed to create and lock '%s': %m", lockname);
×
347

348
        /* 2. Create and open the stack directory. Do not create the stack directory before taking a lock,
349
         * otherwise the directory may be removed by another worker. */
350
        dirpath = path_join("/run/udev/links/", name);
161,645✔
351
        if (!dirpath)
161,645✔
352
                return -ENOMEM;
353

354
        dirfd = open_mkdir(dirpath, O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW | O_RDONLY, 0755);
161,645✔
355
        if (dirfd < 0)
161,645✔
356
                return log_device_debug_errno(dev, dirfd, "Failed to open stack directory '%s': %m", dirpath);
×
357

358
        *ret_dirpath = TAKE_PTR(dirpath);
161,645✔
359
        *ret_dirfd = TAKE_FD(dirfd);
161,645✔
360
        *ret_lockfile = TAKE_GENERIC(lockfile, LockFile, LOCK_FILE_INIT);
161,645✔
361
        return 0;
161,645✔
362
}
363

364
static int node_get_current(const char *slink, int dirfd, char **ret_id, int *ret_prio) {
121,085✔
365
        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
121,085✔
366
        _cleanup_free_ char *id_dup = NULL;
121,085✔
367
        const char *id;
121,085✔
368
        int r;
121,085✔
369

370
        assert(slink);
121,085✔
371
        assert(dirfd >= 0);
121,085✔
372
        assert(ret_id);
121,085✔
373

374
        r = sd_device_new_from_devname(&dev, slink);
121,085✔
375
        if (r < 0)
121,085✔
376
                return r;
377

378
        r = sd_device_get_device_id(dev, &id);
64,894✔
379
        if (r < 0)
64,894✔
380
                return r;
381

382
        id_dup = strdup(id);
64,894✔
383
        if (!id_dup)
64,894✔
384
                return -ENOMEM;
385

386
        if (ret_prio) {
64,894✔
387
                r = stack_directory_read_one(dirfd, id, NULL, ret_prio);
64,894✔
388
                if (r < 0)
64,894✔
389
                        return r;
390
        }
391

392
        *ret_id = TAKE_PTR(id_dup);
64,454✔
393
        return 0;
64,454✔
394
}
395

396
static int link_update(sd_device *dev, const char *slink, bool add) {
161,645✔
397
        /* On cleaning up,
398
         * 1. close the stack directory,
399
         * 2. remove the stack directory if it is empty,
400
         * 3. then finally release the lock.
401
         * Hence, the variables must be declared in the reverse order. */
402
        _cleanup_(release_lock_file) LockFile lockfile = LOCK_FILE_INIT; /* #3 */
161,645✔
403
        _cleanup_(rmdir_and_freep) char *dirpath = NULL; /* #2 */
×
404
        _cleanup_close_ int dirfd = -EBADF; /* #1 */
161,645✔
405
        _cleanup_free_ char *current_id = NULL;
161,645✔
406
        int r, current_prio;
161,645✔
407

408
        assert(dev);
161,645✔
409
        assert(slink);
161,645✔
410

411
        r = stack_directory_open_and_lock(dev, slink, &dirpath, &dirfd, &lockfile);
161,645✔
412
        if (r < 0)
161,645✔
413
                return r;
414

415
        if (add) {
161,645✔
416
                r = node_get_current(slink, dirfd, &current_id, &current_prio);
121,085✔
417
                if (r < 0 && !ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(r))
121,085✔
418
                        return log_device_debug_errno(dev, r, "Failed to get the current device node priority for '%s': %m", slink);
×
419
        }
420

421
        r = stack_directory_update(dev, dirfd, add);
161,645✔
422
        if (r < 0)
161,645✔
423
                return log_device_debug_errno(dev, r, "Failed to update stack directory for '%s': %m", slink);
×
424

425
        if (!add) {
161,645✔
426
                _cleanup_free_ char *target = NULL;
40,560✔
427

428
                /* Especially on 'remove' event, the device ID obtained by node_get_current() may not be
429
                 * reliable, as the device node and/or device number may be reused. Hence, let's read the
430
                 * devlink here and if it points to our device node, then we need to update the devlink. If
431
                 * it points to another device node, then it is already owned by another device, hence we
432
                 * should not touch it and keep it as is. */
433

434
                r = readlink_malloc(slink, &target);
40,560✔
435
                if (r < 0) {
40,560✔
436
                        if (r != -ENOENT)
851✔
437
                                log_device_debug_errno(dev, r, "Failed to read symbolic link '%s', ignoring: %m", slink);
×
438

439
                        /* The devlink does not exist. Let's find the most suitable owner, and create the
440
                         * devlink. This is typically not necessary and does nothing, but for safety in the
441
                         * case that the devlink is removed manually. */
442
                        return link_search_and_update(dev, slink, dirfd, add);
851✔
443
                }
444

445
                const char *node;
39,709✔
446
                r = sd_device_get_devname(dev, &node);
39,709✔
447
                if (r < 0)
39,709✔
448
                        return log_device_debug_errno(dev, r, "Failed to get device node: %m");
×
449

450
                if (streq(node, target)) /* owned by us, needs to update. */
39,709✔
451
                        return link_search_and_update(dev, slink, dirfd, add);
×
452

453
                /* The devlink does not point to our device node. For extra safety, let's validate the
454
                 * current devlink, in case that the devlink is manually modified by user. */
455

456
                if (!path_startswith(target, "/dev/")) {
39,709✔
457
                        log_device_debug(dev, "Symbolic link '%s' points to '%s' which is outside of '/dev/', updating it.", slink, target);
39,709✔
458
                        return link_search_and_update(dev, slink, dirfd, add);
39,709✔
459
                }
460

461
                struct stat st;
×
462
                if (lstat(target, &st) < 0) {
×
463
                        if (errno != ENOENT)
×
464
                                log_device_debug_errno(dev, errno, "Failed to stat '%s', ignoring: %m", target);
×
465

466
                        /* The current target device is also already removed? Let's update. */
467
                        return link_search_and_update(dev, slink, dirfd, add);
×
468
                }
469

470
                if (!IN_SET(st.st_mode & S_IFMT, S_IFBLK, S_IFCHR)) {
×
471
                        log_device_debug(dev, "Symbolic link '%s' points to '%s' which is not a device node, updating it.", slink, target);
×
472
                        return link_search_and_update(dev, slink, dirfd, add);
×
473
                }
474

475
                return 0; /* the devlink is owned by another device, and we should keep it as is. */
476
        }
477

478
        if (!current_id)
121,085✔
479
                /* The requested devlink does not exist, or the target device does not exist and the devlink
480
                 * points to a non-existing device. Let's search the device that has the highest priority,
481
                 * and update the devlink. */
482
                return link_search_and_update(dev, slink, dirfd, add);
56,631✔
483

484
        const char *id;
64,454✔
485
        r = sd_device_get_device_id(dev, &id);
64,454✔
486
        if (r < 0)
64,454✔
487
                return log_device_debug_errno(dev, r, "Failed to get device id: %m");
×
488

489
        int prio;
64,454✔
490
        r = device_get_devlink_priority(dev, &prio);
64,454✔
491
        if (r < 0)
64,454✔
492
                return log_device_debug_errno(dev, r, "Failed to get devlink priority: %m");
×
493

494
        if (streq(current_id, id)) {
64,454✔
495
                if (current_prio <= prio)
57,672✔
496
                        /* The devlink is ours and already exists, and the new priority is equal or higher
497
                         * than the previous. Hence, it is not necessary to recreate it. */
498
                        return 0;
499

500
                /* The devlink priority is downgraded. Another device may have a higher priority now. Let's
501
                 * find the device node with the highest priority. */
UNCOV
502
                return link_search_and_update(dev, slink, dirfd, add);
×
503
        }
504

505
        if (current_prio > prio)
6,782✔
506
                /* The devlink with a higher priority already exists and is owned by another device. Hence,
507
                 * it is not necessary to recreate it. */
508
                return 0;
509

510
        /* This device has the equal or a higher priority than the current. Let's create the devlink to our
511
         * device node. */
512
        return node_create_symlink(dev, /* devnode= */ NULL, slink);
3,464✔
513
}
514

515
static int device_get_devpath_by_devnum(sd_device *dev, char **ret) {
45,044✔
516
        dev_t devnum;
45,044✔
517
        int r;
45,044✔
518

519
        assert(dev);
45,044✔
520
        assert(ret);
45,044✔
521

522
        r = sd_device_get_devnum(dev, &devnum);
45,044✔
523
        if (r < 0)
45,044✔
524
                return r;
45,044✔
525

526
        r = device_in_subsystem(dev, "block");
45,044✔
527
        if (r < 0)
45,044✔
528
                return r;
529

530
        return device_path_make_major_minor(r > 0 ? S_IFBLK : S_IFCHR, devnum, ret);
57,645✔
531
}
532

533
int udev_node_update(sd_device *dev, sd_device *dev_old) {
38,236✔
534
        _cleanup_free_ char *filename = NULL;
38,236✔
535
        int r;
38,236✔
536

537
        assert(dev);
38,236✔
538
        assert(dev_old);
38,236✔
539

540
        /* update possible left-over symlinks */
541
        FOREACH_DEVICE_DEVLINK(dev_old, devlink) {
118,489✔
542
                /* check if old link name still belongs to this device */
543
                if (device_has_devlink(dev, devlink))
80,253✔
544
                        continue;
75,921✔
545

546
                log_device_debug(dev,
4,332✔
547
                                 "Removing/updating old device symlink '%s', which is no longer belonging to this device.",
548
                                 devlink);
549

550
                r = link_update(dev, devlink, /* add= */ false);
4,332✔
551
                if (r < 0)
4,332✔
552
                        log_device_warning_errno(dev, r,
×
553
                                                 "Failed to remove/update device symlink '%s', ignoring: %m",
554
                                                 devlink);
555
        }
556

557
        /* create/update symlinks, add symlinks to name index */
558
        FOREACH_DEVICE_DEVLINK(dev, devlink) {
159,321✔
559
                r = link_update(dev, devlink, /* add= */ true);
121,085✔
560
                if (r < 0)
121,085✔
561
                        log_device_warning_errno(dev, r,
×
562
                                                 "Failed to create/update device symlink '%s', ignoring: %m",
563
                                                 devlink);
564
        }
565

566
        r = device_get_devpath_by_devnum(dev, &filename);
38,236✔
567
        if (r < 0)
38,236✔
568
                return log_device_debug_errno(dev, r, "Failed to get device path: %m");
×
569

570
        /* always add /dev/{block,char}/$major:$minor */
571
        r = node_create_symlink(dev, /* devnode= */ NULL, filename);
38,236✔
572
        if (r < 0)
38,236✔
573
                return log_device_warning_errno(dev, r, "Failed to create device symlink '%s': %m", filename);
×
574

575
        return 0;
576
}
577

578
int udev_node_remove(sd_device *dev) {
6,808✔
579
        _cleanup_free_ char *filename = NULL;
6,808✔
580
        int r;
6,808✔
581

582
        assert(dev);
6,808✔
583

584
        /* remove/update symlinks, remove symlinks from name index */
585
        FOREACH_DEVICE_DEVLINK(dev, devlink) {
43,036✔
586
                r = link_update(dev, devlink, /* add= */ false);
36,228✔
587
                if (r < 0)
36,228✔
588
                        log_device_warning_errno(dev, r,
×
589
                                                 "Failed to remove/update device symlink '%s', ignoring: %m",
590
                                                 devlink);
591
        }
592

593
        r = device_get_devpath_by_devnum(dev, &filename);
6,808✔
594
        if (r < 0)
6,808✔
595
                return log_device_debug_errno(dev, r, "Failed to get device path: %m");
×
596

597
        /* remove /dev/{block,char}/$major:$minor */
598
        return node_remove_symlink(dev, filename);
6,808✔
599
}
600

601
static int udev_node_apply_permissions_impl(
32,396✔
602
                sd_device *dev, /* can be NULL, only used for logging. */
603
                int node_fd,
604
                const char *devnode,
605
                bool apply_mac,
606
                mode_t mode,
607
                uid_t uid,
608
                gid_t gid,
609
                OrderedHashmap *seclabel_list) {
610

611
        bool apply_mode, apply_uid, apply_gid;
32,396✔
612
        struct stat stats;
32,396✔
613
        int r;
32,396✔
614

615
        assert(node_fd >= 0);
32,396✔
616
        assert(devnode);
32,396✔
617

618
        if (fstat(node_fd, &stats) < 0)
32,396✔
619
                return log_device_debug_errno(dev, errno, "cannot stat() node %s: %m", devnode);
×
620

621
        /* If group is set, but mode is not set, "upgrade" mode for the group. */
622
        if (mode == MODE_INVALID && gid_is_valid(gid) && gid > 0)
58,975✔
623
                mode = 0660;
624

625
        apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777);
13,620✔
626
        apply_uid = uid_is_valid(uid) && stats.st_uid != uid;
32,396✔
627
        apply_gid = gid_is_valid(gid) && stats.st_gid != gid;
32,396✔
628

629
        if (apply_mode || apply_uid || apply_gid || apply_mac) {
32,396✔
630
                bool selinux = false, smack = false;
13,269✔
631
                const char *name, *label;
13,269✔
632

633
                if (apply_mode || apply_uid || apply_gid) {
13,269✔
634
                        log_device_debug(dev, "Setting permissions %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
17,842✔
635
                                         devnode,
636
                                         uid_is_valid(uid) ? uid : stats.st_uid,
637
                                         gid_is_valid(gid) ? gid : stats.st_gid,
638
                                         mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
639

640
                        r = fchmod_and_chown(node_fd, mode, uid, gid);
8,921✔
641
                        if (r < 0)
8,921✔
642
                                log_device_full_errno(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
×
643
                                                      "Failed to set owner/mode of %s to uid=" UID_FMT
644
                                                      ", gid=" GID_FMT ", mode=%#o: %m",
645
                                                      devnode,
646
                                                      uid_is_valid(uid) ? uid : stats.st_uid,
647
                                                      gid_is_valid(gid) ? gid : stats.st_gid,
648
                                                      mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
649
                } else
650
                        log_device_debug(dev, "Preserve permissions of %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
8,696✔
651
                                         devnode,
652
                                         uid_is_valid(uid) ? uid : stats.st_uid,
653
                                         gid_is_valid(gid) ? gid : stats.st_gid,
654
                                         mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
655

656
                /* apply SECLABEL{$module}=$label */
657
                ORDERED_HASHMAP_FOREACH_KEY(label, name, seclabel_list) {
13,269✔
658
                        int q;
×
659

660
                        if (streq(name, "selinux")) {
×
661
                                selinux = true;
×
662

663
                                q = mac_selinux_apply_fd(node_fd, devnode, label);
×
664
                                if (q < 0)
×
665
                                        log_device_full_errno(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
×
666
                                                              "SECLABEL: failed to set SELinux label '%s': %m", label);
667
                                else
668
                                        log_device_debug(dev, "SECLABEL: set SELinux label '%s'", label);
×
669

670
                        } else if (streq(name, "smack")) {
×
671
                                smack = true;
×
672

673
                                q = mac_smack_apply_fd(node_fd, SMACK_ATTR_ACCESS, label);
×
674
                                if (q < 0)
×
675
                                        log_device_full_errno(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
×
676
                                                              "SECLABEL: failed to set SMACK label '%s': %m", label);
677
                                else
678
                                        log_device_debug(dev, "SECLABEL: set SMACK label '%s'", label);
×
679

680
                        } else
681
                                log_device_error(dev, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label);
×
682
                }
683

684
                /* set the defaults */
685
                if (!selinux)
13,269✔
686
                        (void) mac_selinux_fix_full(node_fd, NULL, devnode, LABEL_IGNORE_ENOENT);
13,269✔
687
                if (!smack)
13,269✔
688
                        (void) mac_smack_apply_fd(node_fd, SMACK_ATTR_ACCESS, NULL);
13,269✔
689
        }
690

691
        /* always update timestamp when we re-use the node, like on media change events */
692
        r = futimens_opath(node_fd, NULL);
32,396✔
693
        if (r < 0)
32,396✔
694
                log_device_debug_errno(dev, r, "Failed to adjust timestamp of node %s: %m", devnode);
×
695

696
        return 0;
697
}
698

699
int udev_node_apply_permissions(
38,236✔
700
                sd_device *dev,
701
                bool apply_mac,
702
                mode_t mode,
703
                uid_t uid,
704
                gid_t gid,
705
                OrderedHashmap *seclabel_list) {
706

707
        const char *devnode;
38,236✔
708
        _cleanup_close_ int node_fd = -EBADF;
38,236✔
709
        int r;
38,236✔
710

711
        assert(dev);
38,236✔
712

713
        r = sd_device_get_devname(dev, &devnode);
38,236✔
714
        if (r < 0)
38,236✔
715
                return log_device_debug_errno(dev, r, "Failed to get devname: %m");
×
716

717
        node_fd = sd_device_open(dev, O_PATH|O_CLOEXEC);
38,236✔
718
        if (node_fd < 0) {
38,236✔
719
                if (ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(node_fd)) {
6,429✔
720
                        log_device_debug_errno(dev, node_fd, "Device node %s is missing, skipping handling.", devnode);
6,429✔
721
                        return 0; /* This is necessarily racey, so ignore missing the device */
6,429✔
722
                }
723

724
                return log_device_debug_errno(dev, node_fd, "Cannot open node %s: %m", devnode);
×
725
        }
726

727
        return udev_node_apply_permissions_impl(dev, node_fd, devnode, apply_mac, mode, uid, gid, seclabel_list);
31,807✔
728
}
729

730
int static_node_apply_permissions(
616✔
731
                const char *name,
732
                mode_t mode,
733
                uid_t uid,
734
                gid_t gid,
735
                char **tags) {
736

737
        _cleanup_free_ char *unescaped_filename = NULL;
616✔
738
        _cleanup_close_ int node_fd = -EBADF;
616✔
739
        const char *devnode;
616✔
740
        struct stat stats;
616✔
741
        int r;
616✔
742

743
        assert(name);
616✔
744

745
        if (uid == UID_INVALID && gid == GID_INVALID && mode == MODE_INVALID && !tags)
616✔
746
                return 0;
747

748
        devnode = strjoina("/dev/", name);
3,080✔
749

750
        node_fd = open(devnode, O_PATH|O_CLOEXEC);
616✔
751
        if (node_fd < 0) {
616✔
752
                bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno);
27✔
753
                log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
27✔
754
                               "Failed to open device node '%s'%s: %m",
755
                               devnode, ignore ? ", ignoring" : "");
756
                return ignore ? 0 : -errno;
27✔
757
        }
758

759
        if (fstat(node_fd, &stats) < 0)
589✔
760
                return log_error_errno(errno, "Failed to stat %s: %m", devnode);
×
761

762
        if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) {
589✔
763
                log_warning("%s is neither block nor character device, ignoring.", devnode);
×
764
                return 0;
765
        }
766

767
        if (!strv_isempty(tags)) {
589✔
768
                unescaped_filename = xescape(name, "/.");
112✔
769
                if (!unescaped_filename)
112✔
770
                        return log_oom();
×
771
        }
772

773
        /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
774
        STRV_FOREACH(t, tags) {
701✔
775
                _cleanup_free_ char *p = NULL;
112✔
776

777
                p = path_join("/run/udev/static_node-tags/", *t, unescaped_filename);
112✔
778
                if (!p)
112✔
779
                        return log_oom();
×
780

781
                r = mkdir_parents(p, 0755);
112✔
782
                if (r < 0)
112✔
783
                        return log_error_errno(r, "Failed to create parent directory for %s: %m", p);
×
784

785
                r = symlink(devnode, p);
112✔
786
                if (r < 0 && errno != EEXIST)
112✔
787
                        return log_error_errno(errno, "Failed to create symlink %s -> %s: %m", p, devnode);
×
788
        }
789

790
        return udev_node_apply_permissions_impl(NULL, node_fd, devnode, false, mode, uid, gid, NULL);
589✔
791
}
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