]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udev-node.c
Merge pull request #15669 from andir/systemd-ipv6-pd-subnet-id
[thirdparty/systemd.git] / src / udev / udev-node.c
CommitLineData
e7145211 1/* SPDX-License-Identifier: GPL-2.0+ */
ea733a2f 2
07630cea
LP
3#include <errno.h>
4#include <fcntl.h>
5#include <stdbool.h>
6#include <stddef.h>
32ff5bca 7#include <sys/stat.h>
07630cea 8#include <unistd.h>
ea733a2f 9
a2554ace 10#include "alloc-util.h"
c67f84b0 11#include "device-nodes.h"
a2554ace
YW
12#include "device-private.h"
13#include "device-util.h"
8fb3f009 14#include "dirent-util.h"
a2554ace 15#include "fd-util.h"
f97b34a6 16#include "format-util.h"
f4f15635 17#include "fs-util.h"
5ea78a39
YW
18#include "libudev-util.h"
19#include "mkdir.h"
a2554ace 20#include "path-util.h"
07630cea
LP
21#include "selinux-util.h"
22#include "smack-util.h"
d054f0a4 23#include "stdio-util.h"
07630cea 24#include "string-util.h"
5ea78a39 25#include "strxcpyx.h"
a2554ace 26#include "udev-node.h"
3708c0f4 27#include "user-util.h"
9825617b 28
a2554ace 29static int node_symlink(sd_device *dev, const char *node, const char *slink) {
c1ee2674 30 _cleanup_free_ char *slink_dirname = NULL, *target = NULL;
a2554ace 31 const char *id_filename, *slink_tmp;
c1ee2674 32 struct stat stats;
a2554ace
YW
33 int r;
34
35 assert(dev);
36 assert(node);
37 assert(slink);
c1ee2674
YW
38
39 slink_dirname = dirname_malloc(slink);
40 if (!slink_dirname)
41 return log_oom();
912541b0
KS
42
43 /* use relative link */
c1ee2674
YW
44 r = path_make_relative(slink_dirname, node, &target);
45 if (r < 0)
e0ca42e3 46 return log_device_error_errno(dev, r, "Failed to get relative path from '%s' to '%s': %m", slink, node);
912541b0
KS
47
48 /* preserve link with correct target, do not replace node of other device */
49 if (lstat(slink, &stats) == 0) {
50 if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) {
e0ca42e3 51 log_device_error(dev, "Conflicting device node '%s' found, link to '%s' will not be created.", slink, node);
a2554ace 52 return -EOPNOTSUPP;
912541b0 53 } else if (S_ISLNK(stats.st_mode)) {
e7aa9512 54 _cleanup_free_ char *buf = NULL;
912541b0 55
e7aa9512
YW
56 if (readlink_malloc(slink, &buf) >= 0 &&
57 streq(target, buf)) {
e0ca42e3 58 log_device_debug(dev, "Preserve already existing symlink '%s' to '%s'", slink, target);
e7aa9512
YW
59 (void) label_fix(slink, LABEL_IGNORE_ENOENT);
60 (void) utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW);
61 return 0;
912541b0
KS
62 }
63 }
64 } else {
e0ca42e3 65 log_device_debug(dev, "Creating symlink '%s' to '%s'", slink, target);
912541b0 66 do {
a2554ace
YW
67 r = mkdir_parents_label(slink, 0755);
68 if (!IN_SET(r, 0, -ENOENT))
912541b0 69 break;
ecabcf8b 70 mac_selinux_create_file_prepare(slink, S_IFLNK);
a2554ace
YW
71 if (symlink(target, slink) < 0)
72 r = -errno;
ecabcf8b 73 mac_selinux_create_file_clear();
a2554ace
YW
74 } while (r == -ENOENT);
75 if (r == 0)
76 return 0;
6bee2065
YW
77 if (r < 0)
78 log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s', trying to replace '%s': %m", slink, target, slink);
912541b0
KS
79 }
80
e0ca42e3 81 log_device_debug(dev, "Atomically replace '%s'", slink);
a2554ace
YW
82 r = device_get_id_filename(dev, &id_filename);
83 if (r < 0)
e0ca42e3 84 return log_device_error_errno(dev, r, "Failed to get id_filename: %m");
a2554ace
YW
85 slink_tmp = strjoina(slink, ".tmp-", id_filename);
86 (void) unlink(slink_tmp);
912541b0 87 do {
a2554ace
YW
88 r = mkdir_parents_label(slink_tmp, 0755);
89 if (!IN_SET(r, 0, -ENOENT))
912541b0 90 break;
ecabcf8b 91 mac_selinux_create_file_prepare(slink_tmp, S_IFLNK);
a2554ace
YW
92 if (symlink(target, slink_tmp) < 0)
93 r = -errno;
ecabcf8b 94 mac_selinux_create_file_clear();
a2554ace
YW
95 } while (r == -ENOENT);
96 if (r < 0)
e0ca42e3 97 return log_device_error_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink_tmp, target);
a2554ace
YW
98
99 if (rename(slink_tmp, slink) < 0) {
3fb10690 100 r = log_device_error_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink);
a2554ace 101 (void) unlink(slink_tmp);
912541b0 102 }
a2554ace
YW
103
104 return r;
fa33d857
KS
105}
106
6c29f2b9 107/* find device node of device with highest priority */
a2554ace
YW
108static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, char **ret) {
109 _cleanup_closedir_ DIR *dir = NULL;
110 _cleanup_free_ char *target = NULL;
8fb3f009 111 struct dirent *dent;
a2554ace
YW
112 int r, priority = 0;
113
114 assert(!add || dev);
115 assert(stackdir);
116 assert(ret);
912541b0
KS
117
118 if (add) {
a2554ace
YW
119 const char *devnode;
120
121 r = device_get_devlink_priority(dev, &priority);
122 if (r < 0)
123 return r;
124
125 r = sd_device_get_devname(dev, &devnode);
126 if (r < 0)
127 return r;
128
129 target = strdup(devnode);
130 if (!target)
131 return -ENOMEM;
912541b0
KS
132 }
133
134 dir = opendir(stackdir);
a2554ace
YW
135 if (!dir) {
136 if (target) {
137 *ret = TAKE_PTR(target);
138 return 0;
139 }
140
141 return -errno;
142 }
143
8fb3f009 144 FOREACH_DIRENT_ALL(dent, dir, break) {
a2554ace
YW
145 _cleanup_(sd_device_unrefp) sd_device *dev_db = NULL;
146 const char *devnode, *id_filename;
147 int db_prio = 0;
912541b0 148
8fb3f009 149 if (dent->d_name[0] == '\0')
912541b0
KS
150 break;
151 if (dent->d_name[0] == '.')
152 continue;
153
e0ca42e3 154 log_device_debug(dev, "Found '%s' claiming '%s'", dent->d_name, stackdir);
a2554ace
YW
155
156 if (device_get_id_filename(dev, &id_filename) < 0)
157 continue;
912541b0
KS
158
159 /* did we find ourself? */
a2554ace 160 if (streq(dent->d_name, id_filename))
912541b0
KS
161 continue;
162
a2554ace
YW
163 if (sd_device_new_from_device_id(&dev_db, dent->d_name) < 0)
164 continue;
165
166 if (sd_device_get_devname(dev_db, &devnode) < 0)
167 continue;
168
169 if (device_get_devlink_priority(dev_db, &db_prio) < 0)
170 continue;
171
172 if (target && db_prio <= priority)
173 continue;
174
e0ca42e3 175 log_device_debug(dev_db, "Device claims priority %i for '%s'", db_prio, stackdir);
a2554ace
YW
176
177 r = free_and_strdup(&target, devnode);
178 if (r < 0)
179 return r;
180 priority = db_prio;
912541b0 181 }
a2554ace 182
82d9ac23
YW
183 if (!target)
184 return -ENOENT;
185
a2554ace
YW
186 *ret = TAKE_PTR(target);
187 return 0;
aa8734ff
KS
188}
189
6c29f2b9 190/* manage "stack of names" with possibly specified device priorities */
a2554ace
YW
191static int link_update(sd_device *dev, const char *slink, bool add) {
192 _cleanup_free_ char *target = NULL, *filename = NULL, *dirname = NULL;
193 char name_enc[PATH_MAX];
194 const char *id_filename;
195 int r;
196
197 assert(dev);
198 assert(slink);
199
200 r = device_get_id_filename(dev, &id_filename);
201 if (r < 0)
e0ca42e3 202 return log_device_debug_errno(dev, r, "Failed to get id_filename: %m");
912541b0 203
fbd0b64f 204 util_path_encode(slink + STRLEN("/dev"), name_enc, sizeof(name_enc));
62a85ee0 205 dirname = path_join("/run/udev/links/", name_enc);
a2554ace
YW
206 if (!dirname)
207 return log_oom();
62a85ee0 208 filename = path_join(dirname, id_filename);
a2554ace
YW
209 if (!filename)
210 return log_oom();
912541b0 211
baa30fbc 212 if (!add && unlink(filename) == 0)
a2554ace 213 (void) rmdir(dirname);
912541b0 214
a2554ace
YW
215 r = link_find_prioritized(dev, add, dirname, &target);
216 if (r < 0) {
e0ca42e3 217 log_device_debug(dev, "No reference left, removing '%s'", slink);
912541b0 218 if (unlink(slink) == 0)
a2554ace 219 (void) rmdir_parents(slink, "/");
a126a38a 220 } else
a2554ace 221 (void) node_symlink(dev, target, slink);
912541b0 222
a2554ace 223 if (add)
912541b0 224 do {
a2554ace 225 _cleanup_close_ int fd = -1;
912541b0 226
a2554ace
YW
227 r = mkdir_parents(filename, 0755);
228 if (!IN_SET(r, 0, -ENOENT))
912541b0
KS
229 break;
230 fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
a2554ace
YW
231 if (fd < 0)
232 r = -errno;
233 } while (r == -ENOENT);
234
235 return r;
24f0605c
KS
236}
237
a2554ace
YW
238int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) {
239 const char *name, *devpath;
240 int r;
241
242 assert(dev);
243 assert(dev_old);
244
245 r = sd_device_get_devpath(dev, &devpath);
246 if (r < 0)
e0ca42e3 247 return log_device_debug_errno(dev, r, "Failed to get devpath: %m");
912541b0
KS
248
249 /* update possible left-over symlinks */
a2554ace
YW
250 FOREACH_DEVICE_DEVLINK(dev_old, name) {
251 const char *name_current;
252 bool found = false;
912541b0
KS
253
254 /* check if old link name still belongs to this device */
a2554ace 255 FOREACH_DEVICE_DEVLINK(dev, name_current)
090be865 256 if (streq(name, name_current)) {
a2554ace 257 found = true;
912541b0
KS
258 break;
259 }
a2554ace 260
912541b0
KS
261 if (found)
262 continue;
263
e0ca42e3
YW
264 log_device_debug(dev, "Updating old name, '%s' no longer belonging to '%s'",
265 name, devpath);
8a173387 266 link_update(dev, name, false);
912541b0 267 }
a2554ace
YW
268
269 return 0;
24f0605c
KS
270}
271
3708c0f4 272static int node_permissions_apply(sd_device *dev, bool apply_mac,
c26547d6 273 mode_t mode, uid_t uid, gid_t gid,
39a15c8a 274 OrderedHashmap *seclabel_list) {
a2554ace 275 const char *devnode, *subsystem, *id_filename = NULL;
a2554ace
YW
276 struct stat stats;
277 dev_t devnum;
3708c0f4
ZJS
278 bool apply_mode, apply_uid, apply_gid;
279 int r;
a2554ace
YW
280
281 assert(dev);
282
283 r = sd_device_get_devname(dev, &devnode);
284 if (r < 0)
e0ca42e3 285 return log_device_debug_errno(dev, r, "Failed to get devname: %m");
a2554ace
YW
286 r = sd_device_get_subsystem(dev, &subsystem);
287 if (r < 0)
e0ca42e3 288 return log_device_debug_errno(dev, r, "Failed to get subsystem: %m");
a2554ace
YW
289 r = sd_device_get_devnum(dev, &devnum);
290 if (r < 0)
e0ca42e3 291 return log_device_debug_errno(dev, r, "Failed to get devnum: %m");
a2554ace 292 (void) device_get_id_filename(dev, &id_filename);
912541b0 293
a2554ace 294 if (streq(subsystem, "block"))
912541b0
KS
295 mode |= S_IFBLK;
296 else
297 mode |= S_IFCHR;
298
b64b83d1
YW
299 if (lstat(devnode, &stats) < 0) {
300 if (errno == ENOENT)
301 return 0; /* this is necessarily racey, so ignore missing the device */
ab3a7372 302 return log_device_debug_errno(dev, errno, "cannot stat() node %s: %m", devnode);
b64b83d1 303 }
912541b0 304
3708c0f4
ZJS
305 if ((mode != MODE_INVALID && (stats.st_mode & S_IFMT) != (mode & S_IFMT)) || stats.st_rdev != devnum)
306 return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
307 "Found node '%s' with non-matching devnum %s, skip handling",
e0ca42e3 308 devnode, id_filename);
912541b0 309
3708c0f4
ZJS
310 apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777);
311 apply_uid = uid_is_valid(uid) && stats.st_uid != uid;
312 apply_gid = gid_is_valid(gid) && stats.st_gid != gid;
313
314 if (apply_mode || apply_uid || apply_gid || apply_mac) {
a2554ace 315 bool selinux = false, smack = false;
d838e145
YW
316 const char *name, *label;
317 Iterator i;
b7e2b764 318
3708c0f4 319 if (apply_mode || apply_uid || apply_gid) {
20f45f4b
YW
320 log_device_debug(dev, "Setting permissions %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
321 devnode,
322 uid_is_valid(uid) ? uid : stats.st_uid,
323 gid_is_valid(gid) ? gid : stats.st_gid,
324 mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
4b3b5bc7
LP
325
326 r = chmod_and_chown(devnode, mode, uid, gid);
327 if (r < 0)
b64b83d1
YW
328 log_device_full(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
329 "Failed to set owner/mode of %s to uid=" UID_FMT
330 ", gid=" GID_FMT ", mode=%#o: %m",
331 devnode,
332 uid_is_valid(uid) ? uid : stats.st_uid,
333 gid_is_valid(gid) ? gid : stats.st_gid,
334 mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
a2554ace 335 } else
20f45f4b
YW
336 log_device_debug(dev, "Preserve permissions of %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
337 devnode,
338 uid_is_valid(uid) ? uid : stats.st_uid,
339 gid_is_valid(gid) ? gid : stats.st_gid,
340 mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
c26547d6 341
c26547d6 342 /* apply SECLABEL{$module}=$label */
39a15c8a 343 ORDERED_HASHMAP_FOREACH_KEY(label, name, seclabel_list, i) {
a2554ace 344 int q;
c26547d6 345
c26547d6 346 if (streq(name, "selinux")) {
b7e2b764 347 selinux = true;
d53e386d 348
a2554ace
YW
349 q = mac_selinux_apply(devnode, label);
350 if (q < 0)
b64b83d1
YW
351 log_device_full(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
352 "SECLABEL: failed to set SELinux label '%s': %m", label);
463b5dbb 353 else
e0ca42e3 354 log_device_debug(dev, "SECLABEL: set SELinux label '%s'", label);
c26547d6 355
9a4e038c 356 } else if (streq(name, "smack")) {
b7e2b764 357 smack = true;
d53e386d 358
a2554ace
YW
359 q = mac_smack_apply(devnode, SMACK_ATTR_ACCESS, label);
360 if (q < 0)
b64b83d1
YW
361 log_device_full(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
362 "SECLABEL: failed to set SMACK label '%s': %m", label);
c26547d6 363 else
e0ca42e3 364 log_device_debug(dev, "SECLABEL: set SMACK label '%s'", label);
c26547d6
KS
365
366 } else
e0ca42e3 367 log_device_error(dev, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label);
c26547d6 368 }
b7e2b764
KS
369
370 /* set the defaults */
371 if (!selinux)
08c84981 372 (void) mac_selinux_fix(devnode, LABEL_IGNORE_ENOENT);
9a4e038c 373 if (!smack)
a2554ace 374 (void) mac_smack_apply(devnode, SMACK_ATTR_ACCESS, NULL);
48a849ee 375 }
912541b0
KS
376
377 /* always update timestamp when we re-use the node, like on media change events */
a2554ace
YW
378 (void) utimensat(AT_FDCWD, devnode, NULL, 0);
379
380 return r;
220893b3
KS
381}
382
a2554ace
YW
383static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) {
384 char filename[DEV_NUM_PATH_MAX], *s;
385 const char *subsystem;
386 dev_t devnum;
387 int r;
388
389 assert(ret);
912541b0 390
a2554ace
YW
391 r = sd_device_get_subsystem(dev, &subsystem);
392 if (r < 0)
393 return r;
912541b0 394
a2554ace
YW
395 r = sd_device_get_devnum(dev, &devnum);
396 if (r < 0)
397 return r;
912541b0 398
c67f84b0 399 xsprintf_dev_num_path(filename,
a2554ace
YW
400 streq(subsystem, "block") ? "block" : "char",
401 devnum);
402
403 s = strdup(filename);
404 if (!s)
405 return -ENOMEM;
406
407 *ret = s;
408 return 0;
409}
410
411int udev_node_add(sd_device *dev, bool apply,
412 mode_t mode, uid_t uid, gid_t gid,
39a15c8a 413 OrderedHashmap *seclabel_list) {
a2554ace
YW
414 const char *devnode, *devlink;
415 _cleanup_free_ char *filename = NULL;
416 int r;
417
418 assert(dev);
419
420 r = sd_device_get_devname(dev, &devnode);
421 if (r < 0)
e0ca42e3 422 return log_device_debug_errno(dev, r, "Failed to get devnode: %m");
a2554ace
YW
423
424 if (DEBUG_LOGGING) {
425 const char *id_filename = NULL;
426
427 (void) device_get_id_filename(dev, &id_filename);
20f45f4b 428 log_device_debug(dev, "Handling device node '%s', devnum=%s", devnode, strnull(id_filename));
a2554ace
YW
429 }
430
431 r = node_permissions_apply(dev, apply, mode, uid, gid, seclabel_list);
432 if (r < 0)
433 return r;
434
435 r = xsprintf_dev_num_path_from_sd_device(dev, &filename);
436 if (r < 0)
e0ca42e3 437 return log_device_debug_errno(dev, r, "Failed to get device path: %m");
a2554ace
YW
438
439 /* always add /dev/{block,char}/$major:$minor */
440 (void) node_symlink(dev, devnode, filename);
912541b0
KS
441
442 /* create/update symlinks, add symlinks to name index */
a2554ace
YW
443 FOREACH_DEVICE_DEVLINK(dev, devlink)
444 (void) link_update(dev, devlink, true);
445
446 return 0;
ea733a2f
GKH
447}
448
a2554ace
YW
449int udev_node_remove(sd_device *dev) {
450 _cleanup_free_ char *filename = NULL;
451 const char *devlink;
452 int r;
453
454 assert(dev);
912541b0
KS
455
456 /* remove/update symlinks, remove symlinks from name index */
a2554ace
YW
457 FOREACH_DEVICE_DEVLINK(dev, devlink)
458 (void) link_update(dev, devlink, false);
459
460 r = xsprintf_dev_num_path_from_sd_device(dev, &filename);
461 if (r < 0)
e0ca42e3 462 return log_device_debug_errno(dev, r, "Failed to get device path: %m");
912541b0
KS
463
464 /* remove /dev/{block,char}/$major:$minor */
a2554ace
YW
465 (void) unlink(filename);
466
467 return 0;
ea733a2f 468}