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

systemd / systemd / 14119061320

27 Mar 2025 08:38PM UTC coverage: 71.976% (+0.02%) from 71.954%
14119061320

push

github

web-flow
test: Make it possible to run the integration tests standalone (#36868)

Currently, to run the integration tests, it's still necessary to
install various other build tools besides meson: A compiler, gperf,
libcap, ... which we want to avoid in CI systems where we receive
prebuilt systemd packages and only want to test them. Examples are
Debian's autopkgtest CI and Fedora CI. Let's make it possible for
these systems to run the integration tests without having to install
any other build dependency besides meson by extracting the logic
required to run the integration tests with meson into a separate
subdirectory and adding a standalone top-level meson.build file which
can be used to configure a meson tree with as its only purpose running
the integration tests.

Practically, we do the following:
- all the integration test directories and integration-test-wrapper.py
  are moved from test/ to test/integration-tests/.
- All the installation logic is kept out of test/integration-tests/ or
  any of its subdirectories and moved into test/meson.build instead.
- We add test/integration-tests/standalone/meson.build to run the
  integration tests standalone. This meson file includes
  test/integration-tests via a cute symlink hack to trick meson into
  including a parent directory with subdir().
- Documentation is included on how to use the new standalone mode.

296746 of 412283 relevant lines covered (71.98%)

719287.73 hits per line

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

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

3
#include <sys/file.h>
4

5
#include "sd-id128.h"
6

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

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

31
static int node_remove_symlink(sd_device *dev, const char *slink) {
70,448✔
32
        assert(dev);
70,448✔
33
        assert(slink);
70,448✔
34

35
        if (unlink(slink) < 0 && errno != ENOENT)
70,448✔
36
                return log_device_debug_errno(dev, errno, "Failed to remove '%s': %m", slink);
×
37

38
        (void) rmdir_parents(slink, "/dev");
70,448✔
39
        return 0;
70,448✔
40
}
41

42
static int node_create_symlink(sd_device *dev, const char *devnode, const char *slink) {
144,270✔
43
        struct stat st;
144,270✔
44
        int r;
144,270✔
45

46
        assert(dev);
144,270✔
47
        assert(slink);
144,270✔
48

49
        if (!devnode) {
144,270✔
50
                r = sd_device_get_devname(dev, &devnode);
60,237✔
51
                if (r < 0)
60,237✔
52
                        return log_device_debug_errno(dev, r, "Failed to get device node: %m");
×
53
        }
54

55
        if (lstat(slink, &st) >= 0) {
144,270✔
56
                if (!S_ISLNK(st.st_mode))
51,807✔
57
                        return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
×
58
                                                      "Conflicting inode '%s' found, symlink to '%s' will not be created.",
59
                                                      slink, devnode);
60
        } else if (errno != ENOENT)
92,463✔
61
                return log_device_debug_errno(dev, errno, "Failed to lstat() '%s': %m", slink);
×
62

63
        r = mkdir_parents_label(slink, 0755);
144,270✔
64
        if (r < 0)
144,270✔
65
                return log_device_debug_errno(dev, r, "Failed to create parent directory of '%s': %m", slink);
×
66

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

72
        log_device_debug(dev, "Successfully created symlink '%s' to '%s'", slink, devnode);
146,065✔
73
        return 0;
144,270✔
74
}
75

76
static int stack_directory_read_one(int dirfd, const char *id, char **devnode, int *priority) {
214,461✔
77
        _cleanup_free_ char *buf = NULL;
214,461✔
78
        int tmp_prio, r;
214,461✔
79

80
        assert(dirfd >= 0);
214,461✔
81
        assert(id);
214,461✔
82
        assert(priority);
214,461✔
83

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

88
        /* First, let's try to read the entry with the new format, which should replace the old format pretty
89
         * quickly. */
90
        r = readlinkat_malloc(dirfd, id, &buf);
214,461✔
91
        if (r >= 0) {
214,461✔
92
                char *colon;
207,369✔
93

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

96
                colon = strchr(buf, ':');
207,369✔
97
                if (!colon || colon == buf)
207,369✔
98
                        return -EINVAL;
99

100
                *colon = '\0';
207,369✔
101

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

109
                r = safe_atoi(buf, &tmp_prio);
62,790✔
110
                if (r < 0)
62,790✔
111
                        return r;
112

113
                if (!devnode)
62,790✔
114
                        goto finalize;
57,701✔
115

116
                if (*devnode && tmp_prio <= *priority)
5,089✔
117
                        return 0; /* Unchanged */
118

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

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

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

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

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

137
                if (!devnode)
×
138
                        goto finalize;
×
139

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

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

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

151
        } else
152
                return r == -ENOENT ? -ENODEV : r;
7,092✔
153

154
finalize:
59,089✔
155
        *priority = tmp_prio;
59,089✔
156
        return 1; /* Updated */
59,089✔
157
}
158

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

165
        assert(dev);
143,007✔
166
        assert(dirfd >= 0);
143,007✔
167
        assert(ret);
143,007✔
168

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

172
        if (add) {
143,007✔
173
                const char *n;
82,659✔
174

175
                r = device_get_devlink_priority(dev, &priority);
82,659✔
176
                if (r < 0)
82,659✔
177
                        return r;
×
178

179
                r = sd_device_get_devname(dev, &n);
82,659✔
180
                if (r < 0)
82,659✔
181
                        return r;
182

183
                devnode = strdup(n);
82,659✔
184
                if (!devnode)
82,659✔
185
                        return -ENOMEM;
186
        }
187

188
        dir = xopendirat(dirfd, ".", O_NOFOLLOW);
143,007✔
189
        if (!dir)
143,007✔
190
                return -errno;
×
191

192
        r = sd_device_get_device_id(dev, &id);
143,007✔
193
        if (r < 0)
143,007✔
194
                return r;
195

196
        FOREACH_DIRENT(de, dir, break) {
661,348✔
197

198
                /* skip ourself */
199
                if (streq(de->d_name, id))
232,327✔
200
                        continue;
82,659✔
201

202
                r = stack_directory_read_one(dirfd, de->d_name, &devnode, &priority);
149,668✔
203
                if (r < 0 && r != -ENODEV)
149,668✔
204
                        log_debug_errno(r, "Failed to read '%s', ignoring: %m", de->d_name);
518,341✔
205
        }
206

207
        *ret = TAKE_PTR(devnode);
143,007✔
208
        return !!*ret;
143,007✔
209
}
210

211
static int stack_directory_update(sd_device *dev, int fd, bool add) {
215,281✔
212
        const char *id;
215,281✔
213
        int r;
215,281✔
214

215
        assert(dev);
215,281✔
216
        assert(fd >= 0);
215,281✔
217

218
        r = sd_device_get_device_id(dev, &id);
215,281✔
219
        if (r < 0)
215,281✔
220
                return r;
215,281✔
221

222
        if (add) {
215,281✔
223
                _cleanup_free_ char *data = NULL, *buf = NULL;
140,354✔
224
                const char *devname;
140,354✔
225
                int priority;
140,354✔
226

227
                r = sd_device_get_devname(dev, &devname);
140,354✔
228
                if (r < 0)
140,354✔
229
                        return r;
230

231
                r = device_get_devlink_priority(dev, &priority);
140,354✔
232
                if (r < 0)
140,354✔
233
                        return r;
234

235
                if (asprintf(&data, "%i:%s", priority, devname) < 0)
140,354✔
236
                        return -ENOMEM;
237

238
                if (readlinkat_malloc(fd, id, &buf) >= 0 && streq(buf, data))
140,354✔
239
                        return 0; /* Unchanged. */
240

241
                (void) unlinkat(fd, id, 0);
88,629✔
242

243
                if (symlinkat(data, fd, id) < 0)
88,629✔
244
                        return -errno;
×
245

246
        } else {
247
                if (unlinkat(fd, id, 0) < 0) {
74,927✔
248
                        if (errno == ENOENT)
×
249
                                return 0; /* Unchanged. */
250
                        return -errno;
×
251
                }
252
        }
253

254
        return 1; /* Updated. */
255
}
256

257
size_t udev_node_escape_path(const char *src, char *dest, size_t size) {
215,292✔
258
        size_t i, j;
215,292✔
259
        uint64_t h;
215,292✔
260

261
        assert(src);
215,292✔
262
        assert(dest);
215,292✔
263
        assert(size >= 12);
215,292✔
264

265
        for (i = 0, j = 0; src[i] != '\0'; i++) {
11,088,252✔
266
                if (src[i] == '/') {
10,872,963✔
267
                        if (j+4 >= size - 12 + 1)
560,292✔
268
                                goto toolong;
×
269
                        memcpy(&dest[j], "\\x2f", 4);
560,292✔
270
                        j += 4;
560,292✔
271
                } else if (src[i] == '\\') {
10,312,671✔
272
                        if (j+4 >= size - 12 + 1)
3,090✔
273
                                goto toolong;
×
274
                        memcpy(&dest[j], "\\x5c", 4);
3,090✔
275
                        j += 4;
3,090✔
276
                } else {
277
                        if (j+1 >= size - 12 + 1)
10,309,581✔
278
                                goto toolong;
3✔
279
                        dest[j] = src[i];
10,309,578✔
280
                        j++;
10,309,578✔
281
                }
282
        }
283
        dest[j] = '\0';
215,289✔
284
        return j;
215,289✔
285

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

290
        h = siphash24_string(src, UDEV_NODE_HASH_KEY.bytes);
3✔
291

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

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

299
static int stack_directory_get_name(const char *slink, char **ret) {
215,281✔
300
        _cleanup_free_ char *s = NULL;
215,281✔
301
        char name_enc[NAME_MAX+1];
215,281✔
302
        const char *name;
215,281✔
303
        int r;
215,281✔
304

305
        assert(slink);
215,281✔
306
        assert(ret);
215,281✔
307

308
        r = path_simplify_alloc(slink, &s);
215,281✔
309
        if (r < 0)
215,281✔
310
                return r;
311

312
        if (!path_is_normalized(s))
215,281✔
313
                return -EINVAL;
314

315
        name = path_startswith(s, "/dev");
215,281✔
316
        if (empty_or_root(name))
215,281✔
317
                return -EINVAL;
318

319
        udev_node_escape_path(name, name_enc, sizeof(name_enc));
215,281✔
320

321
        return strdup_to(ret, name_enc);
215,281✔
322
}
323

324
static int stack_directory_open_and_lock(
215,281✔
325
                sd_device *dev,
326
                const char *slink,
327
                char **ret_dirpath,
328
                int *ret_dirfd,
329
                LockFile *ret_lockfile) {
330

331
        _cleanup_(release_lock_file) LockFile lockfile = LOCK_FILE_INIT;
×
332
        _cleanup_close_ int dirfd = -EBADF;
215,281✔
333
        _cleanup_free_ char *name = NULL, *dirpath = NULL, *lockname = NULL;
215,281✔
334
        int r;
215,281✔
335

336
        assert(dev);
215,281✔
337
        assert(slink);
215,281✔
338
        assert(ret_dirpath);
215,281✔
339
        assert(ret_dirfd);
215,281✔
340
        assert(ret_lockfile);
215,281✔
341

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

346
        FOREACH_STRING(s, "/run/udev/links/", "/run/udev/links.lock/") {
645,843✔
347
                r = mkdir_p(s, 0755);
430,562✔
348
                if (r < 0)
430,562✔
349
                        return log_device_debug_errno(dev, r, "Failed to create '%s': %m", s);
×
350
        }
351

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

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

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

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

371
        *ret_dirpath = TAKE_PTR(dirpath);
215,281✔
372
        *ret_dirfd = TAKE_FD(dirfd);
215,281✔
373
        *ret_lockfile = TAKE_GENERIC(lockfile, LockFile, LOCK_FILE_INIT);
215,281✔
374
        return 0;
215,281✔
375
}
376

377
static int node_get_current(const char *slink, int dirfd, char **ret_id, int *ret_prio) {
215,281✔
378
        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
215,281✔
379
        _cleanup_free_ char *id_dup = NULL;
215,281✔
380
        const char *id;
215,281✔
381
        int r;
215,281✔
382

383
        assert(slink);
215,281✔
384
        assert(dirfd >= 0);
215,281✔
385
        assert(ret_id);
215,281✔
386

387
        r = sd_device_new_from_devname(&dev, slink);
215,281✔
388
        if (r < 0)
215,281✔
389
                return r;
390

391
        r = sd_device_get_device_id(dev, &id);
111,010✔
392
        if (r < 0)
111,010✔
393
                return r;
394

395
        id_dup = strdup(id);
111,010✔
396
        if (!id_dup)
111,010✔
397
                return -ENOMEM;
398

399
        if (ret_prio) {
111,010✔
400
                r = stack_directory_read_one(dirfd, id, NULL, ret_prio);
64,793✔
401
                if (r < 0)
64,793✔
402
                        return r;
403
        }
404

405
        *ret_id = TAKE_PTR(id_dup);
103,918✔
406
        return 0;
103,918✔
407
}
408

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

421
        assert(dev);
215,281✔
422
        assert(slink);
215,281✔
423

424
        r = stack_directory_open_and_lock(dev, slink, &dirpath, &dirfd, &lockfile);
215,281✔
425
        if (r < 0)
215,281✔
426
                return r;
427

428
        r = node_get_current(slink, dirfd, &current_id, add ? &current_prio : NULL);
290,208✔
429
        if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
215,281✔
430
                return log_device_debug_errno(dev, r, "Failed to get the current device node priority for '%s': %m", slink);
×
431

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

436
        if (current_id) {
215,281✔
437
                const char *id;
103,918✔
438

439
                r = sd_device_get_device_id(dev, &id);
103,918✔
440
                if (r < 0)
103,918✔
441
                        return log_device_debug_errno(dev, r, "Failed to get device id: %m");
72,274✔
442

443
                if (add) {
103,918✔
444
                        int prio;
57,701✔
445

446
                        r = device_get_devlink_priority(dev, &prio);
57,701✔
447
                        if (r < 0)
57,701✔
448
                                return log_device_debug_errno(dev, r, "Failed to get devlink priority: %m");
57,695✔
449

450
                        if (streq(current_id, id)) {
57,701✔
451
                                if (current_prio <= prio)
40,863✔
452
                                        /* The devlink is ours and already exists, and the new priority is
453
                                         * equal or higher than the previous. Hence, it is not necessary to
454
                                         * recreate it. */
455
                                        return 0;
456

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

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

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

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

486
        r = stack_directory_find_prioritized_devnode(dev, dirfd, add, &devnode);
143,007✔
487
        if (r < 0)
143,007✔
488
                return log_device_debug_errno(dev, r, "Failed to determine device node with the highest priority for '%s': %m", slink);
×
489
        if (r > 0)
143,007✔
490
                return node_create_symlink(dev, devnode, slink);
84,033✔
491

492
        log_device_debug(dev, "No reference left for '%s', removing", slink);
58,974✔
493
        return node_remove_symlink(dev, slink);
58,974✔
494
}
495

496
static int device_get_devpath_by_devnum(sd_device *dev, char **ret) {
56,689✔
497
        dev_t devnum;
56,689✔
498
        int r;
56,689✔
499

500
        assert(dev);
56,689✔
501
        assert(ret);
56,689✔
502

503
        r = sd_device_get_devnum(dev, &devnum);
56,689✔
504
        if (r < 0)
56,689✔
505
                return r;
56,689✔
506

507
        return device_path_make_major_minor(device_in_subsystem(dev, "block") ? S_IFBLK : S_IFCHR, devnum, ret);
77,285✔
508
}
509

510
int udev_node_update(sd_device *dev, sd_device *dev_old) {
45,215✔
511
        _cleanup_free_ char *filename = NULL;
45,215✔
512
        int r;
45,215✔
513

514
        assert(dev);
45,215✔
515
        assert(dev_old);
45,215✔
516

517
        /* update possible left-over symlinks */
518
        FOREACH_DEVICE_DEVLINK(dev_old, devlink) {
102,517✔
519
                /* check if old link name still belongs to this device */
520
                if (device_has_devlink(dev, devlink))
57,302✔
521
                        continue;
51,737✔
522

523
                log_device_debug(dev,
5,565✔
524
                                 "Removing/updating old device symlink '%s', which is no longer belonging to this device.",
525
                                 devlink);
526

527
                r = link_update(dev, devlink, /* add = */ false);
5,565✔
528
                if (r < 0)
5,565✔
529
                        log_device_warning_errno(dev, r,
×
530
                                                 "Failed to remove/update device symlink '%s', ignoring: %m",
531
                                                 devlink);
532
        }
533

534
        /* create/update symlinks, add symlinks to name index */
535
        FOREACH_DEVICE_DEVLINK(dev, devlink) {
185,569✔
536
                r = link_update(dev, devlink, /* add = */ true);
140,354✔
537
                if (r < 0)
140,354✔
538
                        log_device_warning_errno(dev, r,
×
539
                                                 "Failed to create/update device symlink '%s', ignoring: %m",
540
                                                 devlink);
541
        }
542

543
        r = device_get_devpath_by_devnum(dev, &filename);
45,215✔
544
        if (r < 0)
45,215✔
545
                return log_device_debug_errno(dev, r, "Failed to get device path: %m");
×
546

547
        /* always add /dev/{block,char}/$major:$minor */
548
        r = node_create_symlink(dev, /* devnode = */ NULL, filename);
45,215✔
549
        if (r < 0)
45,215✔
550
                return log_device_warning_errno(dev, r, "Failed to create device symlink '%s': %m", filename);
×
551

552
        return 0;
553
}
554

555
int udev_node_remove(sd_device *dev) {
11,474✔
556
        _cleanup_free_ char *filename = NULL;
11,474✔
557
        int r;
11,474✔
558

559
        assert(dev);
11,474✔
560

561
        /* remove/update symlinks, remove symlinks from name index */
562
        FOREACH_DEVICE_DEVLINK(dev, devlink) {
80,836✔
563
                r = link_update(dev, devlink, /* add = */ false);
69,362✔
564
                if (r < 0)
69,362✔
565
                        log_device_warning_errno(dev, r,
×
566
                                                 "Failed to remove/update device symlink '%s', ignoring: %m",
567
                                                 devlink);
568
        }
569

570
        r = device_get_devpath_by_devnum(dev, &filename);
11,474✔
571
        if (r < 0)
11,474✔
572
                return log_device_debug_errno(dev, r, "Failed to get device path: %m");
×
573

574
        /* remove /dev/{block,char}/$major:$minor */
575
        return node_remove_symlink(dev, filename);
11,474✔
576
}
577

578
static int udev_node_apply_permissions_impl(
41,735✔
579
                sd_device *dev, /* can be NULL, only used for logging. */
580
                int node_fd,
581
                const char *devnode,
582
                bool apply_mac,
583
                mode_t mode,
584
                uid_t uid,
585
                gid_t gid,
586
                OrderedHashmap *seclabel_list) {
587

588
        bool apply_mode, apply_uid, apply_gid;
41,735✔
589
        struct stat stats;
41,735✔
590
        int r;
41,735✔
591

592
        assert(node_fd >= 0);
41,735✔
593
        assert(devnode);
41,735✔
594

595
        if (fstat(node_fd, &stats) < 0)
41,735✔
596
                return log_device_debug_errno(dev, errno, "cannot stat() node %s: %m", devnode);
×
597

598
        /* If group is set, but mode is not set, "upgrade" mode for the group. */
599
        if (mode == MODE_INVALID && gid_is_valid(gid) && gid > 0)
73,118✔
600
                mode = 0660;
601

602
        apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777);
25,854✔
603
        apply_uid = uid_is_valid(uid) && stats.st_uid != uid;
41,735✔
604
        apply_gid = gid_is_valid(gid) && stats.st_gid != gid;
41,735✔
605

606
        if (apply_mode || apply_uid || apply_gid || apply_mac) {
41,735✔
607
                bool selinux = false, smack = false;
26,950✔
608
                const char *name, *label;
26,950✔
609

610
                if (apply_mode || apply_uid || apply_gid) {
26,950✔
611
                        log_device_debug(dev, "Setting permissions %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
29,826✔
612
                                         devnode,
613
                                         uid_is_valid(uid) ? uid : stats.st_uid,
614
                                         gid_is_valid(gid) ? gid : stats.st_gid,
615
                                         mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
616

617
                        r = fchmod_and_chown(node_fd, mode, uid, gid);
14,913✔
618
                        if (r < 0)
14,913✔
619
                                log_device_full_errno(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
×
620
                                                      "Failed to set owner/mode of %s to uid=" UID_FMT
621
                                                      ", gid=" GID_FMT ", mode=%#o: %m",
622
                                                      devnode,
623
                                                      uid_is_valid(uid) ? uid : stats.st_uid,
624
                                                      gid_is_valid(gid) ? gid : stats.st_gid,
625
                                                      mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
626
                } else
627
                        log_device_debug(dev, "Preserve permissions of %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
24,074✔
628
                                         devnode,
629
                                         uid_is_valid(uid) ? uid : stats.st_uid,
630
                                         gid_is_valid(gid) ? gid : stats.st_gid,
631
                                         mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
632

633
                /* apply SECLABEL{$module}=$label */
634
                ORDERED_HASHMAP_FOREACH_KEY(label, name, seclabel_list) {
26,950✔
635
                        int q;
×
636

637
                        if (streq(name, "selinux")) {
×
638
                                selinux = true;
×
639

640
                                q = mac_selinux_apply_fd(node_fd, devnode, label);
×
641
                                if (q < 0)
×
642
                                        log_device_full_errno(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
×
643
                                                              "SECLABEL: failed to set SELinux label '%s': %m", label);
644
                                else
645
                                        log_device_debug(dev, "SECLABEL: set SELinux label '%s'", label);
×
646

647
                        } else if (streq(name, "smack")) {
×
648
                                smack = true;
×
649

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

657
                        } else
658
                                log_device_error(dev, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label);
×
659
                }
660

661
                /* set the defaults */
662
                if (!selinux)
26,950✔
663
                        (void) mac_selinux_fix_full(node_fd, NULL, devnode, LABEL_IGNORE_ENOENT);
26,950✔
664
                if (!smack)
26,950✔
665
                        (void) mac_smack_apply_fd(node_fd, SMACK_ATTR_ACCESS, NULL);
26,950✔
666
        }
667

668
        /* always update timestamp when we re-use the node, like on media change events */
669
        r = futimens_opath(node_fd, NULL);
41,735✔
670
        if (r < 0)
41,735✔
671
                log_device_debug_errno(dev, r, "Failed to adjust timestamp of node %s: %m", devnode);
×
672

673
        return 0;
674
}
675

676
int udev_node_apply_permissions(
45,215✔
677
                sd_device *dev,
678
                bool apply_mac,
679
                mode_t mode,
680
                uid_t uid,
681
                gid_t gid,
682
                OrderedHashmap *seclabel_list) {
683

684
        const char *devnode;
45,215✔
685
        _cleanup_close_ int node_fd = -EBADF;
45,215✔
686
        int r;
45,215✔
687

688
        assert(dev);
45,215✔
689

690
        r = sd_device_get_devname(dev, &devnode);
45,215✔
691
        if (r < 0)
45,215✔
692
                return log_device_debug_errno(dev, r, "Failed to get devname: %m");
×
693

694
        node_fd = sd_device_open(dev, O_PATH|O_CLOEXEC);
45,215✔
695
        if (node_fd < 0) {
45,215✔
696
                if (ERRNO_IS_DEVICE_ABSENT(node_fd)) {
4,553✔
697
                        log_device_debug_errno(dev, node_fd, "Device node %s is missing, skipping handling.", devnode);
4,553✔
698
                        return 0; /* This is necessarily racey, so ignore missing the device */
4,553✔
699
                }
700

701
                return log_device_debug_errno(dev, node_fd, "Cannot open node %s: %m", devnode);
×
702
        }
703

704
        return udev_node_apply_permissions_impl(dev, node_fd, devnode, apply_mac, mode, uid, gid, seclabel_list);
40,662✔
705
}
706

707
int static_node_apply_permissions(
1,155✔
708
                const char *name,
709
                mode_t mode,
710
                uid_t uid,
711
                gid_t gid,
712
                char **tags) {
713

714
        _cleanup_free_ char *unescaped_filename = NULL;
1,155✔
715
        _cleanup_close_ int node_fd = -EBADF;
1,155✔
716
        const char *devnode;
1,155✔
717
        struct stat stats;
1,155✔
718
        int r;
1,155✔
719

720
        assert(name);
1,155✔
721

722
        if (uid == UID_INVALID && gid == GID_INVALID && mode == MODE_INVALID && !tags)
1,155✔
723
                return 0;
724

725
        devnode = strjoina("/dev/", name);
5,775✔
726

727
        node_fd = open(devnode, O_PATH|O_CLOEXEC);
1,155✔
728
        if (node_fd < 0) {
1,155✔
729
                if (errno != ENOENT)
82✔
730
                        return log_error_errno(errno, "Failed to open %s: %m", devnode);
×
731
                return 0;
732
        }
733

734
        if (fstat(node_fd, &stats) < 0)
1,073✔
735
                return log_error_errno(errno, "Failed to stat %s: %m", devnode);
×
736

737
        if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) {
1,073✔
738
                log_warning("%s is neither block nor character device, ignoring.", devnode);
×
739
                return 0;
×
740
        }
741

742
        if (!strv_isempty(tags)) {
1,073✔
743
                unescaped_filename = xescape(name, "/.");
210✔
744
                if (!unescaped_filename)
210✔
745
                        return log_oom();
×
746
        }
747

748
        /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
749
        STRV_FOREACH(t, tags) {
1,283✔
750
                _cleanup_free_ char *p = NULL;
210✔
751

752
                p = path_join("/run/udev/static_node-tags/", *t, unescaped_filename);
210✔
753
                if (!p)
210✔
754
                        return log_oom();
×
755

756
                r = mkdir_parents(p, 0755);
210✔
757
                if (r < 0)
210✔
758
                        return log_error_errno(r, "Failed to create parent directory for %s: %m", p);
×
759

760
                r = symlink(devnode, p);
210✔
761
                if (r < 0 && errno != EEXIST)
210✔
762
                        return log_error_errno(errno, "Failed to create symlink %s -> %s: %m", p, devnode);
×
763
        }
764

765
        return udev_node_apply_permissions_impl(NULL, node_fd, devnode, false, mode, uid, gid, NULL);
1,073✔
766
}
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