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

systemd / systemd / 16791678039

06 Aug 2025 11:10PM UTC coverage: 72.181% (-0.04%) from 72.223%
16791678039

push

github

yuwata
logging: Improve logging messages related to NFTSet.

The 'NFTSet' directive in various units adds and removes entries in nftables
sets, it does not add or remove entire sets. The logging messages should
indicate that an entry was added or removed, not that a set was added or
removed.

2 of 6 new or added lines in 3 files covered. (33.33%)

496 existing lines in 52 files now uncovered.

302228 of 418708 relevant lines covered (72.18%)

647735.83 hits per line

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

81.0
/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) {
88,836✔
37
        assert(dev);
88,836✔
38
        assert(slink);
88,836✔
39

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

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

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

51
        assert(dev);
136,967✔
52
        assert(slink);
136,967✔
53

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

60
        if (lstat(slink, &st) >= 0) {
136,967✔
61
                if (!S_ISLNK(st.st_mode))
37,819✔
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)
99,148✔
66
                return log_device_debug_errno(dev, errno, "Failed to lstat() '%s': %m", slink);
×
67

68
        r = mkdir_parents_label(slink, 0755);
136,967✔
69
        if (r < 0)
136,967✔
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);
136,967✔
74
        if (r < 0)
136,967✔
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);
138,945✔
78
        return 0;
136,967✔
79
}
80

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

85
        assert(dirfd >= 0);
596,495✔
86
        assert(id);
596,495✔
87
        assert(priority);
596,495✔
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);
596,495✔
96
        if (r >= 0) {
596,495✔
97
                char *colon;
596,079✔
98

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

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

105
                *colon = '\0';
596,079✔
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)
596,079✔
112
                        return -ENODEV;
113

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

118
                if (!devnode)
574,979✔
119
                        goto finalize;
42,549✔
120

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

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

128
        } else if (r == -EINVAL) { /* Not a symlink ? try the old format */
416✔
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;
416✔
158

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

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

170
        assert(dev);
166,255✔
171
        assert(dirfd >= 0);
166,255✔
172
        assert(ret);
166,255✔
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) {
166,255✔
178
                const char *n;
82,455✔
179

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

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

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

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

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

201
        FOREACH_DIRENT(de, dir, break) {
1,134,749✔
202

203
                /* skip ourself */
204
                if (streq(de->d_name, id))
635,984✔
205
                        continue;
82,455✔
206

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

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

216
static int link_search_and_update(sd_device *dev, const char *slink, int dirfd, bool add) {
166,255✔
217
        int r;
166,255✔
218

219
        assert(dev);
166,255✔
220
        assert(slink);
166,255✔
221
        assert(dirfd >= 0);
166,255✔
222

223
        _cleanup_free_ char *devnode = NULL;
166,255✔
224
        r = stack_directory_find_prioritized_devnode(dev, dirfd, add, &devnode);
166,255✔
225
        if (r < 0)
166,255✔
226
                return log_device_debug_errno(dev, r, "Failed to determine device node with the highest priority for '%s': %m", slink);
×
227
        if (r > 0)
166,255✔
228
                return node_create_symlink(dev, devnode, slink);
90,333✔
229

230
        log_device_debug(dev, "No reference left for '%s', removing", slink);
75,922✔
231
        return node_remove_symlink(dev, slink);
75,922✔
232
}
233

234
static int stack_directory_update(sd_device *dev, int fd, bool add) {
208,798✔
235
        const char *id;
208,798✔
236
        int r;
208,798✔
237

238
        assert(dev);
208,798✔
239
        assert(fd >= 0);
208,798✔
240

241
        r = sd_device_get_device_id(dev, &id);
208,798✔
242
        if (r < 0)
208,798✔
243
                return r;
208,798✔
244

245
        if (add) {
208,798✔
246
                _cleanup_free_ char *data = NULL, *buf = NULL;
124,998✔
247
                const char *devname;
124,998✔
248
                int priority;
124,998✔
249

250
                r = sd_device_get_devname(dev, &devname);
124,998✔
251
                if (r < 0)
124,998✔
252
                        return r;
253

254
                r = device_get_devlink_priority(dev, &priority);
124,998✔
255
                if (r < 0)
124,998✔
256
                        return r;
257

258
                if (asprintf(&data, "%i:%s", priority, devname) < 0)
124,998✔
259
                        return -ENOMEM;
260

261
                if (readlinkat_malloc(fd, id, &buf) >= 0 && streq(buf, data))
124,998✔
262
                        return 0; /* Unchanged. */
263

264
                (void) unlinkat(fd, id, 0);
88,493✔
265

266
                if (symlinkat(data, fd, id) < 0)
88,493✔
267
                        return -errno;
×
268

269
        } else {
270
                if (unlinkat(fd, id, 0) < 0) {
83,800✔
271
                        if (errno == ENOENT)
×
272
                                return 0; /* Unchanged. */
273
                        return -errno;
×
274
                }
275
        }
276

277
        return 1; /* Updated. */
278
}
279

280
size_t udev_node_escape_path(const char *src, char *dest, size_t size) {
208,809✔
281
        size_t i, j;
208,809✔
282
        uint64_t h;
208,809✔
283

284
        assert(src);
208,809✔
285
        assert(dest);
208,809✔
286
        assert(size >= 12);
208,809✔
287

288
        for (i = 0, j = 0; src[i] != '\0'; i++) {
10,636,559✔
289
                if (src[i] == '/') {
10,427,753✔
290
                        if (j+4 >= size - 12 + 1)
539,940✔
291
                                goto toolong;
×
292
                        memcpy(&dest[j], "\\x2f", 4);
539,940✔
293
                        j += 4;
539,940✔
294
                } else if (src[i] == '\\') {
9,887,813✔
295
                        if (j+4 >= size - 12 + 1)
3,229✔
296
                                goto toolong;
×
297
                        memcpy(&dest[j], "\\x5c", 4);
3,229✔
298
                        j += 4;
3,229✔
299
                } else {
300
                        if (j+1 >= size - 12 + 1)
9,884,584✔
301
                                goto toolong;
3✔
302
                        dest[j] = src[i];
9,884,581✔
303
                        j++;
9,884,581✔
304
                }
305
        }
306
        dest[j] = '\0';
208,806✔
307
        return j;
208,806✔
308

309
toolong:
3✔
310
        /* If the input path is too long to encode as a filename, then let's suffix with a string
311
         * generated from the hash of the path. */
312

313
        h = siphash24_string(src, UDEV_NODE_HASH_KEY.bytes);
3✔
314

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

318
        dest[size - 1] = '\0';
3✔
319
        return size - 1;
3✔
320
}
321

322
static int stack_directory_get_name(const char *slink, char **ret) {
208,798✔
323
        _cleanup_free_ char *s = NULL;
208,798✔
324
        char name_enc[NAME_MAX+1];
208,798✔
325
        const char *name;
208,798✔
326
        int r;
208,798✔
327

328
        assert(slink);
208,798✔
329
        assert(ret);
208,798✔
330

331
        r = path_simplify_alloc(slink, &s);
208,798✔
332
        if (r < 0)
208,798✔
333
                return r;
334

335
        if (!path_is_normalized(s))
208,798✔
336
                return -EINVAL;
337

338
        name = path_startswith(s, "/dev");
208,798✔
339
        if (empty_or_root(name))
208,798✔
340
                return -EINVAL;
341

342
        udev_node_escape_path(name, name_enc, sizeof(name_enc));
208,798✔
343

344
        return strdup_to(ret, name_enc);
208,798✔
345
}
346

347
static int stack_directory_open_and_lock(
208,798✔
348
                sd_device *dev,
349
                const char *slink,
350
                char **ret_dirpath,
351
                int *ret_dirfd,
352
                LockFile *ret_lockfile) {
353

354
        _cleanup_(release_lock_file) LockFile lockfile = LOCK_FILE_INIT;
×
355
        _cleanup_close_ int dirfd = -EBADF;
208,798✔
356
        _cleanup_free_ char *name = NULL, *dirpath = NULL, *lockname = NULL;
208,798✔
357
        int r;
208,798✔
358

359
        assert(dev);
208,798✔
360
        assert(slink);
208,798✔
361
        assert(ret_dirpath);
208,798✔
362
        assert(ret_dirfd);
208,798✔
363
        assert(ret_lockfile);
208,798✔
364

365
        r = stack_directory_get_name(slink, &name);
208,798✔
366
        if (r < 0)
208,798✔
367
                return log_device_debug_errno(dev, r, "Failed to build stack directory name for '%s': %m", slink);
×
368

369
        FOREACH_STRING(s, "/run/udev/links/", "/run/udev/links.lock/") {
626,394✔
370
                r = mkdir_p(s, 0755);
417,596✔
371
                if (r < 0)
417,596✔
372
                        return log_device_debug_errno(dev, r, "Failed to create '%s': %m", s);
×
373
        }
374

375
        /* 1. Take a lock for the stack directory. */
376
        lockname = path_join("/run/udev/links.lock/", name);
208,798✔
377
        if (!lockname)
208,798✔
378
                return -ENOMEM;
379

380
        r = make_lock_file(lockname, LOCK_EX, &lockfile);
208,798✔
381
        if (r < 0)
208,798✔
382
                return log_device_debug_errno(dev, r, "Failed to create and lock '%s': %m", lockname);
×
383

384
        /* 2. Create and open the stack directory. Do not create the stack directory before taking a lock,
385
         * otherwise the directory may be removed by another worker. */
386
        dirpath = path_join("/run/udev/links/", name);
208,798✔
387
        if (!dirpath)
208,798✔
388
                return -ENOMEM;
389

390
        dirfd = open_mkdir(dirpath, O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW | O_RDONLY, 0755);
208,798✔
391
        if (dirfd < 0)
208,798✔
392
                return log_device_debug_errno(dev, dirfd, "Failed to open stack directory '%s': %m", dirpath);
×
393

394
        *ret_dirpath = TAKE_PTR(dirpath);
208,798✔
395
        *ret_dirfd = TAKE_FD(dirfd);
208,798✔
396
        *ret_lockfile = TAKE_GENERIC(lockfile, LockFile, LOCK_FILE_INIT);
208,798✔
397
        return 0;
208,798✔
398
}
399

400
static int node_get_current(const char *slink, int dirfd, char **ret_id, int *ret_prio) {
124,998✔
401
        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
124,998✔
402
        _cleanup_free_ char *id_dup = NULL;
124,998✔
403
        const char *id;
124,998✔
404
        int r;
124,998✔
405

406
        assert(slink);
124,998✔
407
        assert(dirfd >= 0);
124,998✔
408
        assert(ret_id);
124,998✔
409

410
        r = sd_device_new_from_devname(&dev, slink);
124,998✔
411
        if (r < 0)
124,998✔
412
                return r;
413

414
        r = sd_device_get_device_id(dev, &id);
42,966✔
415
        if (r < 0)
42,966✔
416
                return r;
417

418
        id_dup = strdup(id);
42,966✔
419
        if (!id_dup)
42,966✔
420
                return -ENOMEM;
421

422
        if (ret_prio) {
42,966✔
423
                r = stack_directory_read_one(dirfd, id, NULL, ret_prio);
42,966✔
424
                if (r < 0)
42,966✔
425
                        return r;
426
        }
427

428
        *ret_id = TAKE_PTR(id_dup);
42,549✔
429
        return 0;
42,549✔
430
}
431

432
static int link_update(sd_device *dev, const char *slink, bool add) {
208,798✔
433
        /* On cleaning up,
434
         * 1. close the stack directory,
435
         * 2. remove the stack directory if it is empty,
436
         * 3. then finally release the lock.
437
         * Hence, the variables must be declared in the reverse order. */
438
        _cleanup_(release_lock_file) LockFile lockfile = LOCK_FILE_INIT; /* #3 */
208,798✔
439
        _cleanup_(rmdir_and_freep) char *dirpath = NULL; /* #2 */
×
440
        _cleanup_close_ int dirfd = -EBADF; /* #1 */
208,798✔
441
        _cleanup_free_ char *current_id = NULL;
208,798✔
442
        int r, current_prio;
208,798✔
443

444
        assert(dev);
208,798✔
445
        assert(slink);
208,798✔
446

447
        r = stack_directory_open_and_lock(dev, slink, &dirpath, &dirfd, &lockfile);
208,798✔
448
        if (r < 0)
208,798✔
449
                return r;
450

451
        if (add) {
208,798✔
452
                r = node_get_current(slink, dirfd, &current_id, &current_prio);
124,998✔
453
                if (r < 0 && !ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(r))
124,998✔
454
                        return log_device_debug_errno(dev, r, "Failed to get the current device node priority for '%s': %m", slink);
×
455
        }
456

457
        r = stack_directory_update(dev, dirfd, add);
208,798✔
458
        if (r < 0)
208,798✔
459
                return log_device_debug_errno(dev, r, "Failed to update stack directory for '%s': %m", slink);
×
460

461
        if (!add) {
208,798✔
462
                _cleanup_free_ char *target = NULL;
83,800✔
463

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

470
                r = readlink_malloc(slink, &target);
83,800✔
471
                if (r < 0) {
83,800✔
472
                        if (r != -ENOENT)
1,149✔
473
                                log_device_debug_errno(dev, r, "Failed to read symbolic link '%s', ignoring: %m", slink);
×
474

475
                        /* The devlink does not exist. Let's find the most suitable owner, and create the
476
                         * devlink. This is typically not necessary and does nothing, but for safety in the
477
                         * case that the devlink is removed manually. */
478
                        return link_search_and_update(dev, slink, dirfd, add);
1,149✔
479
                }
480

481
                const char *node;
82,651✔
482
                r = sd_device_get_devname(dev, &node);
82,651✔
483
                if (r < 0)
82,651✔
484
                        return log_device_debug_errno(dev, r, "Failed to get device node: %m");
×
485

486
                if (streq(node, target)) /* owned by us, needs to update. */
82,651✔
487
                        return link_search_and_update(dev, slink, dirfd, add);
×
488

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

492
                if (!path_startswith(target, "/dev/")) {
82,651✔
493
                        log_device_debug(dev, "Symbolic link '%s' points to '%s' which is outside of '/dev/', updating it.", slink, target);
82,651✔
494
                        return link_search_and_update(dev, slink, dirfd, add);
82,651✔
495
                }
496

497
                struct stat st;
×
498
                if (lstat(target, &st) < 0) {
×
499
                        if (errno != ENOENT)
×
500
                                log_device_debug_errno(dev, errno, "Failed to stat '%s', ignoring: %m", target);
×
501

502
                        /* The current target device is also already removed? Let's update. */
503
                        return link_search_and_update(dev, slink, dirfd, add);
×
504
                }
505

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

511
                return 0; /* the devlink is owned by another device, and we should keep it as is. */
512
        }
513

514
        if (!current_id)
124,998✔
515
                /* The requested devlink does not exist, or the target device does not exist and the devlink
516
                 * points to a non-existing device. Let's search the device that has the highest priority,
517
                 * and update the devlink. */
518
                return link_search_and_update(dev, slink, dirfd, add);
82,449✔
519

520
        const char *id;
42,549✔
521
        r = sd_device_get_device_id(dev, &id);
42,549✔
522
        if (r < 0)
42,549✔
523
                return log_device_debug_errno(dev, r, "Failed to get device id: %m");
×
524

525
        int prio;
42,549✔
526
        r = device_get_devlink_priority(dev, &prio);
42,549✔
527
        if (r < 0)
42,549✔
528
                return log_device_debug_errno(dev, r, "Failed to get devlink priority: %m");
×
529

530
        if (streq(current_id, id)) {
42,549✔
531
                if (current_prio <= prio)
30,632✔
532
                        /* The devlink is ours and already exists, and the new priority is equal or higher
533
                         * than the previous. Hence, it is not necessary to recreate it. */
534
                        return 0;
535

536
                /* The devlink priority is downgraded. Another device may have a higher priority now. Let's
537
                 * find the device node with the highest priority. */
538
                return link_search_and_update(dev, slink, dirfd, add);
6✔
539
        }
540

541
        if (current_prio > prio)
11,917✔
542
                /* The devlink with a higher priority already exists and is owned by another device. Hence,
543
                 * it is not necessary to recreate it. */
544
                return 0;
545

546
        /* This device has the equal or a higher priority than the current. Let's create the devlink to our
547
         * device node. */
548
        return node_create_symlink(dev, /* devnode = */ NULL, slink);
10,333✔
549
}
550

551
static int device_get_devpath_by_devnum(sd_device *dev, char **ret) {
49,215✔
552
        dev_t devnum;
49,215✔
553
        int r;
49,215✔
554

555
        assert(dev);
49,215✔
556
        assert(ret);
49,215✔
557

558
        r = sd_device_get_devnum(dev, &devnum);
49,215✔
559
        if (r < 0)
49,215✔
560
                return r;
49,215✔
561

562
        r = device_in_subsystem(dev, "block");
49,215✔
563
        if (r < 0)
49,215✔
564
                return r;
565

566
        return device_path_make_major_minor(r > 0 ? S_IFBLK : S_IFCHR, devnum, ret);
62,181✔
567
}
568

569
int udev_node_update(sd_device *dev, sd_device *dev_old) {
36,301✔
570
        _cleanup_free_ char *filename = NULL;
36,301✔
571
        int r;
36,301✔
572

573
        assert(dev);
36,301✔
574
        assert(dev_old);
36,301✔
575

576
        /* update possible left-over symlinks */
577
        FOREACH_DEVICE_DEVLINK(dev_old, devlink) {
75,345✔
578
                /* check if old link name still belongs to this device */
579
                if (device_has_devlink(dev, devlink))
39,044✔
580
                        continue;
36,517✔
581

582
                log_device_debug(dev,
2,527✔
583
                                 "Removing/updating old device symlink '%s', which is no longer belonging to this device.",
584
                                 devlink);
585

586
                r = link_update(dev, devlink, /* add = */ false);
2,527✔
587
                if (r < 0)
2,527✔
588
                        log_device_warning_errno(dev, r,
×
589
                                                 "Failed to remove/update device symlink '%s', ignoring: %m",
590
                                                 devlink);
591
        }
592

593
        /* create/update symlinks, add symlinks to name index */
594
        FOREACH_DEVICE_DEVLINK(dev, devlink) {
161,299✔
595
                r = link_update(dev, devlink, /* add = */ true);
124,998✔
596
                if (r < 0)
124,998✔
UNCOV
597
                        log_device_warning_errno(dev, r,
×
598
                                                 "Failed to create/update device symlink '%s', ignoring: %m",
599
                                                 devlink);
600
        }
601

602
        r = device_get_devpath_by_devnum(dev, &filename);
36,301✔
603
        if (r < 0)
36,301✔
604
                return log_device_debug_errno(dev, r, "Failed to get device path: %m");
×
605

606
        /* always add /dev/{block,char}/$major:$minor */
607
        r = node_create_symlink(dev, /* devnode = */ NULL, filename);
36,301✔
608
        if (r < 0)
36,301✔
609
                return log_device_warning_errno(dev, r, "Failed to create device symlink '%s': %m", filename);
×
610

611
        return 0;
612
}
613

614
int udev_node_remove(sd_device *dev) {
12,914✔
615
        _cleanup_free_ char *filename = NULL;
12,914✔
616
        int r;
12,914✔
617

618
        assert(dev);
12,914✔
619

620
        /* remove/update symlinks, remove symlinks from name index */
621
        FOREACH_DEVICE_DEVLINK(dev, devlink) {
94,187✔
622
                r = link_update(dev, devlink, /* add = */ false);
81,273✔
623
                if (r < 0)
81,273✔
624
                        log_device_warning_errno(dev, r,
×
625
                                                 "Failed to remove/update device symlink '%s', ignoring: %m",
626
                                                 devlink);
627
        }
628

629
        r = device_get_devpath_by_devnum(dev, &filename);
12,914✔
630
        if (r < 0)
12,914✔
631
                return log_device_debug_errno(dev, r, "Failed to get device path: %m");
×
632

633
        /* remove /dev/{block,char}/$major:$minor */
634
        return node_remove_symlink(dev, filename);
12,914✔
635
}
636

637
static int udev_node_apply_permissions_impl(
32,286✔
638
                sd_device *dev, /* can be NULL, only used for logging. */
639
                int node_fd,
640
                const char *devnode,
641
                bool apply_mac,
642
                mode_t mode,
643
                uid_t uid,
644
                gid_t gid,
645
                OrderedHashmap *seclabel_list) {
646

647
        bool apply_mode, apply_uid, apply_gid;
32,286✔
648
        struct stat stats;
32,286✔
649
        int r;
32,286✔
650

651
        assert(node_fd >= 0);
32,286✔
652
        assert(devnode);
32,286✔
653

654
        if (fstat(node_fd, &stats) < 0)
32,286✔
655
                return log_device_debug_errno(dev, errno, "cannot stat() node %s: %m", devnode);
×
656

657
        /* If group is set, but mode is not set, "upgrade" mode for the group. */
658
        if (mode == MODE_INVALID && gid_is_valid(gid) && gid > 0)
58,996✔
659
                mode = 0660;
660

661
        apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777);
18,475✔
662
        apply_uid = uid_is_valid(uid) && stats.st_uid != uid;
32,286✔
663
        apply_gid = gid_is_valid(gid) && stats.st_gid != gid;
32,286✔
664

665
        if (apply_mode || apply_uid || apply_gid || apply_mac) {
32,286✔
666
                bool selinux = false, smack = false;
18,605✔
667
                const char *name, *label;
18,605✔
668

669
                if (apply_mode || apply_uid || apply_gid) {
18,605✔
670
                        log_device_debug(dev, "Setting permissions %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
19,588✔
671
                                         devnode,
672
                                         uid_is_valid(uid) ? uid : stats.st_uid,
673
                                         gid_is_valid(gid) ? gid : stats.st_gid,
674
                                         mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
675

676
                        r = fchmod_and_chown(node_fd, mode, uid, gid);
9,794✔
677
                        if (r < 0)
9,794✔
678
                                log_device_full_errno(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
×
679
                                                      "Failed to set owner/mode of %s to uid=" UID_FMT
680
                                                      ", gid=" GID_FMT ", mode=%#o: %m",
681
                                                      devnode,
682
                                                      uid_is_valid(uid) ? uid : stats.st_uid,
683
                                                      gid_is_valid(gid) ? gid : stats.st_gid,
684
                                                      mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
685
                } else
686
                        log_device_debug(dev, "Preserve permissions of %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
17,622✔
687
                                         devnode,
688
                                         uid_is_valid(uid) ? uid : stats.st_uid,
689
                                         gid_is_valid(gid) ? gid : stats.st_gid,
690
                                         mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
691

692
                /* apply SECLABEL{$module}=$label */
693
                ORDERED_HASHMAP_FOREACH_KEY(label, name, seclabel_list) {
18,605✔
694
                        int q;
×
695

696
                        if (streq(name, "selinux")) {
×
697
                                selinux = true;
×
698

699
                                q = mac_selinux_apply_fd(node_fd, devnode, label);
×
700
                                if (q < 0)
×
701
                                        log_device_full_errno(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
×
702
                                                              "SECLABEL: failed to set SELinux label '%s': %m", label);
703
                                else
704
                                        log_device_debug(dev, "SECLABEL: set SELinux label '%s'", label);
×
705

706
                        } else if (streq(name, "smack")) {
×
707
                                smack = true;
×
708

709
                                q = mac_smack_apply_fd(node_fd, SMACK_ATTR_ACCESS, label);
×
710
                                if (q < 0)
×
711
                                        log_device_full_errno(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
×
712
                                                              "SECLABEL: failed to set SMACK label '%s': %m", label);
713
                                else
714
                                        log_device_debug(dev, "SECLABEL: set SMACK label '%s'", label);
×
715

716
                        } else
717
                                log_device_error(dev, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label);
×
718
                }
719

720
                /* set the defaults */
721
                if (!selinux)
18,605✔
722
                        (void) mac_selinux_fix_full(node_fd, NULL, devnode, LABEL_IGNORE_ENOENT);
18,605✔
723
                if (!smack)
18,605✔
724
                        (void) mac_smack_apply_fd(node_fd, SMACK_ATTR_ACCESS, NULL);
18,605✔
725
        }
726

727
        /* always update timestamp when we re-use the node, like on media change events */
728
        r = futimens_opath(node_fd, NULL);
32,286✔
729
        if (r < 0)
32,286✔
730
                log_device_debug_errno(dev, r, "Failed to adjust timestamp of node %s: %m", devnode);
×
731

732
        return 0;
733
}
734

735
int udev_node_apply_permissions(
36,301✔
736
                sd_device *dev,
737
                bool apply_mac,
738
                mode_t mode,
739
                uid_t uid,
740
                gid_t gid,
741
                OrderedHashmap *seclabel_list) {
742

743
        const char *devnode;
36,301✔
744
        _cleanup_close_ int node_fd = -EBADF;
36,301✔
745
        int r;
36,301✔
746

747
        assert(dev);
36,301✔
748

749
        r = sd_device_get_devname(dev, &devnode);
36,301✔
750
        if (r < 0)
36,301✔
751
                return log_device_debug_errno(dev, r, "Failed to get devname: %m");
×
752

753
        node_fd = sd_device_open(dev, O_PATH|O_CLOEXEC);
36,301✔
754
        if (node_fd < 0) {
36,301✔
755
                if (ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(node_fd)) {
4,552✔
756
                        log_device_debug_errno(dev, node_fd, "Device node %s is missing, skipping handling.", devnode);
4,552✔
757
                        return 0; /* This is necessarily racey, so ignore missing the device */
4,552✔
758
                }
759

760
                return log_device_debug_errno(dev, node_fd, "Cannot open node %s: %m", devnode);
×
761
        }
762

763
        return udev_node_apply_permissions_impl(dev, node_fd, devnode, apply_mac, mode, uid, gid, seclabel_list);
31,749✔
764
}
765

766
int static_node_apply_permissions(
561✔
767
                const char *name,
768
                mode_t mode,
769
                uid_t uid,
770
                gid_t gid,
771
                char **tags) {
772

773
        _cleanup_free_ char *unescaped_filename = NULL;
561✔
774
        _cleanup_close_ int node_fd = -EBADF;
561✔
775
        const char *devnode;
561✔
776
        struct stat stats;
561✔
777
        int r;
561✔
778

779
        assert(name);
561✔
780

781
        if (uid == UID_INVALID && gid == GID_INVALID && mode == MODE_INVALID && !tags)
561✔
782
                return 0;
783

784
        devnode = strjoina("/dev/", name);
2,805✔
785

786
        node_fd = open(devnode, O_PATH|O_CLOEXEC);
561✔
787
        if (node_fd < 0) {
561✔
788
                bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno);
24✔
789
                log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
24✔
790
                               "Failed to open device node '%s'%s: %m",
791
                               devnode, ignore ? ", ignoring" : "");
792
                return ignore ? 0 : -errno;
24✔
793
        }
794

795
        if (fstat(node_fd, &stats) < 0)
537✔
796
                return log_error_errno(errno, "Failed to stat %s: %m", devnode);
×
797

798
        if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) {
537✔
799
                log_warning("%s is neither block nor character device, ignoring.", devnode);
×
800
                return 0;
×
801
        }
802

803
        if (!strv_isempty(tags)) {
537✔
804
                unescaped_filename = xescape(name, "/.");
102✔
805
                if (!unescaped_filename)
102✔
806
                        return log_oom();
×
807
        }
808

809
        /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
810
        STRV_FOREACH(t, tags) {
639✔
811
                _cleanup_free_ char *p = NULL;
102✔
812

813
                p = path_join("/run/udev/static_node-tags/", *t, unescaped_filename);
102✔
814
                if (!p)
102✔
815
                        return log_oom();
×
816

817
                r = mkdir_parents(p, 0755);
102✔
818
                if (r < 0)
102✔
819
                        return log_error_errno(r, "Failed to create parent directory for %s: %m", p);
×
820

821
                r = symlink(devnode, p);
102✔
822
                if (r < 0 && errno != EEXIST)
102✔
823
                        return log_error_errno(errno, "Failed to create symlink %s -> %s: %m", p, devnode);
×
824
        }
825

826
        return udev_node_apply_permissions_impl(NULL, node_fd, devnode, false, mode, uid, gid, NULL);
537✔
827
}
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