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

systemd / systemd / 30227425323

26 Jul 2026 04:41PM UTC coverage: 73.446% (+0.1%) from 73.35%
30227425323

push

github

web-flow
test: add deb coverage and a few more sanity checks to TEST-88-UPGRADE (#43162)

348767 of 474865 relevant lines covered (73.45%)

1300322.74 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,473✔
37
        assert(dev);
45,473✔
38
        assert(slink);
45,473✔
39

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

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

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

51
        assert(dev);
103,471✔
52
        assert(slink);
103,471✔
53

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

60
        if (lstat(slink, &st) >= 0) {
103,471✔
61
                if (!S_ISLNK(st.st_mode))
47,438✔
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)
56,033✔
66
                return log_device_debug_errno(dev, errno, "Failed to lstat() '%s': %m", slink);
×
67

68
        r = mkdir_parents_label(slink, 0755);
103,471✔
69
        if (r < 0)
103,471✔
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);
103,471✔
74
        if (r < 0)
103,471✔
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);
105,248✔
78
        return 0;
103,471✔
79
}
80

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

84
        assert(dirfd >= 0);
69,898✔
85
        assert(id);
69,898✔
86
        assert(priority);
69,898✔
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;
69,898✔
93
        r = readlinkat_malloc(dirfd, id, &buf);
69,898✔
94
        if (r < 0)
69,898✔
95
                return r == -ENOENT ? -ENODEV : r;
762✔
96

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

101
        *colon = '\0';
69,136✔
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)
69,136✔
108
                return -ENODEV;
109

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

115
        if (devnode) {
66,789✔
116
                if (*devnode && tmp_prio <= *priority)
3,033✔
117
                        return 0; /* Unchanged */
118

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

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

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

134
        assert(dev);
97,858✔
135
        assert(dirfd >= 0);
97,858✔
136
        assert(ret);
97,858✔
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,858✔
142
                const char *n;
57,441✔
143

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

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

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

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

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

165
        FOREACH_DIRENT(de, dir, break) {
356,394✔
166

167
                /* skip ourself */
168
                if (streq(de->d_name, id))
62,820✔
169
                        continue;
57,441✔
170

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

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

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

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

187
        _cleanup_free_ char *devnode = NULL;
97,858✔
188
        r = stack_directory_find_prioritized_devnode(dev, dirfd, add, &devnode);
97,858✔
189
        if (r < 0)
97,858✔
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,858✔
192
                return node_create_symlink(dev, devnode, slink);
59,264✔
193

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

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

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

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

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

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

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

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

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

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

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

233
        } else {
234
                if (unlinkat(fd, id, 0) < 0) {
40,417✔
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,625✔
245
        size_t i, j;
161,625✔
246
        uint64_t h;
161,625✔
247

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

252
        for (i = 0, j = 0; src[i] != '\0'; i++) {
7,781,885✔
253
                if (src[i] == '/') {
7,620,263✔
254
                        if (j+4 >= size - 12 + 1)
401,774✔
255
                                goto toolong;
×
256
                        memcpy(&dest[j], "\\x2f", 4);
401,774✔
257
                        j += 4;
401,774✔
258
                } else if (src[i] == '\\') {
7,218,489✔
259
                        if (j+4 >= size - 12 + 1)
14,410✔
260
                                goto toolong;
×
261
                        memcpy(&dest[j], "\\x5c", 4);
14,410✔
262
                        j += 4;
14,410✔
263
                } else {
264
                        if (j+1 >= size - 12 + 1)
7,204,079✔
265
                                goto toolong;
3✔
266
                        dest[j] = src[i];
7,204,076✔
267
                        j++;
7,204,076✔
268
                }
269
        }
270
        dest[j] = '\0';
161,622✔
271
        return j;
161,622✔
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,614✔
287
        _cleanup_free_ char *s = NULL;
161,614✔
288
        char name_enc[NAME_MAX+1];
161,614✔
289
        const char *name;
161,614✔
290
        int r;
161,614✔
291

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

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

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

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

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

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

311
static int stack_directory_open_and_lock(
161,614✔
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,614✔
320
        _cleanup_free_ char *name = NULL, *dirpath = NULL, *lockname = NULL;
161,614✔
321
        int r;
161,614✔
322

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

329
        r = stack_directory_get_name(slink, &name);
161,614✔
330
        if (r < 0)
161,614✔
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,842✔
334
                r = mkdir_p(s, 0755);
323,228✔
335
                if (r < 0)
323,228✔
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,614✔
341
        if (!lockname)
161,614✔
342
                return -ENOMEM;
343

344
        r = make_lock_file(lockname, LOCK_EX, &lockfile);
161,614✔
345
        if (r < 0)
161,614✔
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,614✔
351
        if (!dirpath)
161,614✔
352
                return -ENOMEM;
353

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

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

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

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

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

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

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

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

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

396
static int link_update(sd_device *dev, const char *slink, bool add) {
161,614✔
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,614✔
403
        _cleanup_(rmdir_and_freep) char *dirpath = NULL; /* #2 */
×
404
        _cleanup_close_ int dirfd = -EBADF; /* #1 */
161,614✔
405
        _cleanup_free_ char *current_id = NULL;
161,614✔
406
        int r, current_prio;
161,614✔
407

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

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

415
        if (add) {
161,614✔
416
                r = node_get_current(slink, dirfd, &current_id, &current_prio);
121,197✔
417
                if (r < 0 && !ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(r))
121,197✔
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,614✔
422
        if (r < 0)
161,614✔
423
                return log_device_debug_errno(dev, r, "Failed to update stack directory for '%s': %m", slink);
×
424

425
        if (!add) {
161,614✔
426
                _cleanup_free_ char *target = NULL;
40,417✔
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,417✔
435
                if (r < 0) {
40,417✔
436
                        if (r != -ENOENT)
853✔
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);
853✔
443
                }
444

445
                const char *node;
39,564✔
446
                r = sd_device_get_devname(dev, &node);
39,564✔
447
                if (r < 0)
39,564✔
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,564✔
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,564✔
457
                        log_device_debug(dev, "Symbolic link '%s' points to '%s' which is outside of '/dev/', updating it.", slink, target);
39,564✔
458
                        return link_search_and_update(dev, slink, dirfd, add);
39,564✔
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,197✔
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);
57,441✔
483

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

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

494
        if (streq(current_id, id)) {
63,756✔
495
                if (current_prio <= prio)
57,304✔
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. */
502
                return link_search_and_update(dev, slink, dirfd, add);
×
503
        }
504

505
        if (current_prio > prio)
6,452✔
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,774✔
513
}
514

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

519
        assert(dev);
47,312✔
520
        assert(ret);
47,312✔
521

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

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

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

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

537
        assert(dev);
40,433✔
538
        assert(dev_old);
40,433✔
539

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

546
                log_device_debug(dev,
4,525✔
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,525✔
551
                if (r < 0)
4,525✔
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) {
161,630✔
559
                r = link_update(dev, devlink, /* add= */ true);
121,197✔
560
                if (r < 0)
121,197✔
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);
40,433✔
567
        if (r < 0)
40,433✔
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);
40,433✔
572
        if (r < 0)
40,433✔
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,879✔
579
        _cleanup_free_ char *filename = NULL;
6,879✔
580
        int r;
6,879✔
581

582
        assert(dev);
6,879✔
583

584
        /* remove/update symlinks, remove symlinks from name index */
585
        FOREACH_DEVICE_DEVLINK(dev, devlink) {
42,771✔
586
                r = link_update(dev, devlink, /* add= */ false);
35,892✔
587
                if (r < 0)
35,892✔
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,879✔
594
        if (r < 0)
6,879✔
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,879✔
599
}
600

601
static int udev_node_apply_permissions_impl(
34,394✔
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;
34,394✔
612
        struct stat stats;
34,394✔
613
        int r;
34,394✔
614

615
        assert(node_fd >= 0);
34,394✔
616
        assert(devnode);
34,394✔
617

618
        if (fstat(node_fd, &stats) < 0)
34,394✔
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)
62,306✔
623
                mode = 0660;
624

625
        apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777);
14,816✔
626
        apply_uid = uid_is_valid(uid) && stats.st_uid != uid;
34,394✔
627
        apply_gid = gid_is_valid(gid) && stats.st_gid != gid;
34,394✔
628

629
        if (apply_mode || apply_uid || apply_gid || apply_mac) {
34,394✔
630
                bool selinux = false, smack = false;
14,590✔
631
                const char *name, *label;
14,590✔
632

633
                if (apply_mode || apply_uid || apply_gid) {
14,590✔
634
                        log_device_debug(dev, "Setting permissions %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
18,336✔
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);
9,168✔
641
                        if (r < 0)
9,168✔
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",
10,844✔
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) {
14,590✔
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)
14,590✔
686
                        (void) mac_selinux_fix_full(node_fd, NULL, devnode, LABEL_IGNORE_ENOENT);
14,590✔
687
                if (!smack)
14,590✔
688
                        (void) mac_smack_apply_fd(node_fd, SMACK_ATTR_ACCESS, NULL);
14,590✔
689
        }
690

691
        /* always update timestamp when we re-use the node, like on media change events */
692
        r = futimens_opath(node_fd, NULL);
34,394✔
693
        if (r < 0)
34,394✔
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(
40,433✔
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;
40,433✔
708
        _cleanup_close_ int node_fd = -EBADF;
40,433✔
709
        int r;
40,433✔
710

711
        assert(dev);
40,433✔
712

713
        r = sd_device_get_devname(dev, &devnode);
40,433✔
714
        if (r < 0)
40,433✔
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);
40,433✔
718
        if (node_fd < 0) {
40,433✔
719
                if (ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(node_fd)) {
6,658✔
720
                        log_device_debug_errno(dev, node_fd, "Device node %s is missing, skipping handling.", devnode);
6,658✔
721
                        return 0; /* This is necessarily racey, so ignore missing the device */
6,658✔
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);
33,775✔
728
}
729

730
int static_node_apply_permissions(
649✔
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;
649✔
738
        _cleanup_close_ int node_fd = -EBADF;
649✔
739
        const char *devnode;
649✔
740
        struct stat stats;
649✔
741
        int r;
649✔
742

743
        assert(name);
649✔
744

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

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

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

759
        if (fstat(node_fd, &stats) < 0)
619✔
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)) {
619✔
763
                log_warning("%s is neither block nor character device, ignoring.", devnode);
×
764
                return 0;
765
        }
766

767
        if (!strv_isempty(tags)) {
619✔
768
                unescaped_filename = xescape(name, "/.");
118✔
769
                if (!unescaped_filename)
118✔
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) {
737✔
775
                _cleanup_free_ char *p = NULL;
118✔
776

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

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

785
                r = symlink(devnode, p);
118✔
786
                if (r < 0 && errno != EEXIST)
118✔
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);
619✔
791
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc