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

systemd / systemd / 16484517872

23 Jul 2025 10:27PM UTC coverage: 72.168% (-0.005%) from 72.173%
16484517872

push

github

yuwata
test-unit-serialize.c: Migrate to new assertion macros

We recently added a new set of assertion macros such as ASSERT_GE, ASSERT_OK, ASSERT_EQ, ... which show not
only the expression that failed but also the values of the arguments of the expression. Let's use them.

4 of 4 new or added lines in 1 file covered. (100.0%)

321 existing lines in 44 files now uncovered.

302532 of 419205 relevant lines covered (72.17%)

743874.27 hits per line

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

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

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

7
#include "sd-id128.h"
8

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

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

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

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

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

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

51
        assert(dev);
160,543✔
52
        assert(slink);
160,543✔
53

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

60
        if (lstat(slink, &st) >= 0) {
160,543✔
61
                if (!S_ISLNK(st.st_mode))
41,728✔
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)
118,815✔
66
                return log_device_debug_errno(dev, errno, "Failed to lstat() '%s': %m", slink);
×
67

68
        r = mkdir_parents_label(slink, 0755);
160,543✔
69
        if (r < 0)
160,543✔
UNCOV
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);
160,543✔
74
        if (r < 0)
160,543✔
UNCOV
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);
162,519✔
78
        return 0;
160,543✔
79
}
80

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

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

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

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

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

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

105
                *colon = '\0';
54,727✔
106

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

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

118
                if (!devnode)
48,154✔
119
                        goto finalize;
43,268✔
120

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

177
        if (add) {
186,274✔
178
                const char *n;
103,663✔
179

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

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

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

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

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

201
        FOREACH_DIRENT(de, dir, break) {
673,944✔
202

203
                /* skip ourself */
204
                if (streq(de->d_name, id))
115,122✔
205
                        continue;
103,663✔
206

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

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

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

220
        assert(dev);
251,520✔
221
        assert(fd >= 0);
251,520✔
222

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

227
        if (add) {
251,520✔
228
                _cleanup_free_ char *data = NULL, *buf = NULL;
146,925✔
229
                const char *devname;
146,925✔
230
                int priority;
146,925✔
231

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

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

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

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

246
                (void) unlinkat(fd, id, 0);
117,555✔
247

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

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

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

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

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

270
        for (i = 0, j = 0; src[i] != '\0'; i++) {
13,027,881✔
271
                if (src[i] == '/') {
12,776,353✔
272
                        if (j+4 >= size - 12 + 1)
657,193✔
273
                                goto toolong;
×
274
                        memcpy(&dest[j], "\\x2f", 4);
657,193✔
275
                        j += 4;
657,193✔
276
                } else if (src[i] == '\\') {
12,119,160✔
277
                        if (j+4 >= size - 12 + 1)
3,160✔
278
                                goto toolong;
×
279
                        memcpy(&dest[j], "\\x5c", 4);
3,160✔
280
                        j += 4;
3,160✔
281
                } else {
282
                        if (j+1 >= size - 12 + 1)
12,116,000✔
283
                                goto toolong;
3✔
284
                        dest[j] = src[i];
12,115,997✔
285
                        j++;
12,115,997✔
286
                }
287
        }
288
        dest[j] = '\0';
251,528✔
289
        return j;
251,528✔
290

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

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

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

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

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

310
        assert(slink);
251,520✔
311
        assert(ret);
251,520✔
312

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

317
        if (!path_is_normalized(s))
251,520✔
318
                return -EINVAL;
319

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

324
        udev_node_escape_path(name, name_enc, sizeof(name_enc));
251,520✔
325

326
        return strdup_to(ret, name_enc);
251,520✔
327
}
328

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

426
        assert(dev);
251,520✔
427
        assert(slink);
251,520✔
428

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

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

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

441
        if (current_id) {
251,520✔
442
                const char *id;
121,487✔
443

444
                r = sd_device_get_device_id(dev, &id);
121,487✔
445
                if (r < 0)
121,487✔
446
                        return log_device_debug_errno(dev, r, "Failed to get device id: %m");
65,246✔
447

448
                if (add) {
121,487✔
449
                        int prio;
43,268✔
450

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

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

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

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

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

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

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

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

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

505
        assert(dev);
54,831✔
506
        assert(ret);
54,831✔
507

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

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

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

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

523
        assert(dev);
39,215✔
524
        assert(dev_old);
39,215✔
525

526
        /* update possible left-over symlinks */
527
        FOREACH_DEVICE_DEVLINK(dev_old, devlink) {
71,306✔
528
                /* check if old link name still belongs to this device */
529
                if (device_has_devlink(dev, devlink))
32,091✔
530
                        continue;
29,397✔
531

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

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

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

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

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

561
        return 0;
562
}
563

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

568
        assert(dev);
15,616✔
569

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

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

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

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

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

601
        assert(node_fd >= 0);
35,185✔
602
        assert(devnode);
35,185✔
603

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

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

611
        apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777);
22,083✔
612
        apply_uid = uid_is_valid(uid) && stats.st_uid != uid;
35,185✔
613
        apply_gid = gid_is_valid(gid) && stats.st_gid != gid;
35,185✔
614

615
        if (apply_mode || apply_uid || apply_gid || apply_mac) {
35,185✔
616
                bool selinux = false, smack = false;
22,305✔
617
                const char *name, *label;
22,305✔
618

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

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

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

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

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

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

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

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

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

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

682
        return 0;
683
}
684

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

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

697
        assert(dev);
39,215✔
698

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

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

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

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

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

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

729
        assert(name);
572✔
730

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

734
        devnode = strjoina("/dev/", name);
2,860✔
735

736
        node_fd = open(devnode, O_PATH|O_CLOEXEC);
572✔
737
        if (node_fd < 0) {
572✔
738
                bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
25✔
739
                log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
25✔
740
                               "Failed to open device node '%s'%s: %m",
741
                               devnode, ignore ? ", ignoring" : "");
742
                return ignore ? 0 : -errno;
25✔
743
        }
744

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

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

753
        if (!strv_isempty(tags)) {
547✔
754
                unescaped_filename = xescape(name, "/.");
104✔
755
                if (!unescaped_filename)
104✔
756
                        return log_oom();
×
757
        }
758

759
        /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
760
        STRV_FOREACH(t, tags) {
651✔
761
                _cleanup_free_ char *p = NULL;
104✔
762

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

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

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

776
        return udev_node_apply_permissions_impl(NULL, node_fd, devnode, false, mode, uid, gid, NULL);
547✔
777
}
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