]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-node.c
Merge pull request #16490 from yuwata/network-radv-ndisc-cleanups
[thirdparty/systemd.git] / src / udev / udev-node.c
1 /* SPDX-License-Identifier: GPL-2.0+ */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <stdbool.h>
6 #include <stddef.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9
10 #include "alloc-util.h"
11 #include "device-nodes.h"
12 #include "device-private.h"
13 #include "device-util.h"
14 #include "dirent-util.h"
15 #include "fd-util.h"
16 #include "format-util.h"
17 #include "fs-util.h"
18 #include "libudev-util.h"
19 #include "mkdir.h"
20 #include "path-util.h"
21 #include "selinux-util.h"
22 #include "smack-util.h"
23 #include "stdio-util.h"
24 #include "string-util.h"
25 #include "strxcpyx.h"
26 #include "udev-node.h"
27 #include "user-util.h"
28
29 static int node_symlink(sd_device *dev, const char *node, const char *slink) {
30 _cleanup_free_ char *slink_dirname = NULL, *target = NULL;
31 const char *id_filename, *slink_tmp;
32 struct stat stats;
33 int r;
34
35 assert(dev);
36 assert(node);
37 assert(slink);
38
39 slink_dirname = dirname_malloc(slink);
40 if (!slink_dirname)
41 return log_oom();
42
43 /* use relative link */
44 r = path_make_relative(slink_dirname, node, &target);
45 if (r < 0)
46 return log_device_error_errno(dev, r, "Failed to get relative path from '%s' to '%s': %m", slink, node);
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))
51 return log_device_error_errno(dev, SYNTHETIC_ERRNO(EOPNOTSUPP),
52 "Conflicting device node '%s' found, link to '%s' will not be created.", slink, node);
53 else if (S_ISLNK(stats.st_mode)) {
54 _cleanup_free_ char *buf = NULL;
55
56 if (readlink_malloc(slink, &buf) >= 0 &&
57 streq(target, buf)) {
58 log_device_debug(dev, "Preserve already existing symlink '%s' to '%s'", slink, target);
59 (void) label_fix(slink, LABEL_IGNORE_ENOENT);
60 (void) utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW);
61 return 0;
62 }
63 }
64 } else {
65 log_device_debug(dev, "Creating symlink '%s' to '%s'", slink, target);
66 do {
67 r = mkdir_parents_label(slink, 0755);
68 if (!IN_SET(r, 0, -ENOENT))
69 break;
70 mac_selinux_create_file_prepare(slink, S_IFLNK);
71 if (symlink(target, slink) < 0)
72 r = -errno;
73 mac_selinux_create_file_clear();
74 } while (r == -ENOENT);
75 if (r == 0)
76 return 0;
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);
79 }
80
81 log_device_debug(dev, "Atomically replace '%s'", slink);
82 r = device_get_id_filename(dev, &id_filename);
83 if (r < 0)
84 return log_device_error_errno(dev, r, "Failed to get id_filename: %m");
85 slink_tmp = strjoina(slink, ".tmp-", id_filename);
86 (void) unlink(slink_tmp);
87 do {
88 r = mkdir_parents_label(slink_tmp, 0755);
89 if (!IN_SET(r, 0, -ENOENT))
90 break;
91 mac_selinux_create_file_prepare(slink_tmp, S_IFLNK);
92 if (symlink(target, slink_tmp) < 0)
93 r = -errno;
94 mac_selinux_create_file_clear();
95 } while (r == -ENOENT);
96 if (r < 0)
97 return log_device_error_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink_tmp, target);
98
99 if (rename(slink_tmp, slink) < 0) {
100 r = log_device_error_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink);
101 (void) unlink(slink_tmp);
102 }
103
104 return r;
105 }
106
107 /* find device node of device with highest priority */
108 static 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;
111 struct dirent *dent;
112 int r, priority = 0;
113
114 assert(!add || dev);
115 assert(stackdir);
116 assert(ret);
117
118 if (add) {
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;
132 }
133
134 dir = opendir(stackdir);
135 if (!dir) {
136 if (target) {
137 *ret = TAKE_PTR(target);
138 return 0;
139 }
140
141 return -errno;
142 }
143
144 FOREACH_DIRENT_ALL(dent, dir, break) {
145 _cleanup_(sd_device_unrefp) sd_device *dev_db = NULL;
146 const char *devnode, *id_filename;
147 int db_prio = 0;
148
149 if (dent->d_name[0] == '\0')
150 break;
151 if (dent->d_name[0] == '.')
152 continue;
153
154 log_device_debug(dev, "Found '%s' claiming '%s'", dent->d_name, stackdir);
155
156 if (device_get_id_filename(dev, &id_filename) < 0)
157 continue;
158
159 /* did we find ourself? */
160 if (streq(dent->d_name, id_filename))
161 continue;
162
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
175 log_device_debug(dev_db, "Device claims priority %i for '%s'", db_prio, stackdir);
176
177 r = free_and_strdup(&target, devnode);
178 if (r < 0)
179 return r;
180 priority = db_prio;
181 }
182
183 if (!target)
184 return -ENOENT;
185
186 *ret = TAKE_PTR(target);
187 return 0;
188 }
189
190 /* manage "stack of names" with possibly specified device priorities */
191 static 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)
202 return log_device_debug_errno(dev, r, "Failed to get id_filename: %m");
203
204 util_path_encode(slink + STRLEN("/dev"), name_enc, sizeof(name_enc));
205 dirname = path_join("/run/udev/links/", name_enc);
206 if (!dirname)
207 return log_oom();
208 filename = path_join(dirname, id_filename);
209 if (!filename)
210 return log_oom();
211
212 if (!add && unlink(filename) == 0)
213 (void) rmdir(dirname);
214
215 r = link_find_prioritized(dev, add, dirname, &target);
216 if (r < 0) {
217 log_device_debug(dev, "No reference left, removing '%s'", slink);
218 if (unlink(slink) == 0)
219 (void) rmdir_parents(slink, "/");
220 } else
221 (void) node_symlink(dev, target, slink);
222
223 if (add)
224 do {
225 _cleanup_close_ int fd = -1;
226
227 r = mkdir_parents(filename, 0755);
228 if (!IN_SET(r, 0, -ENOENT))
229 break;
230 fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
231 if (fd < 0)
232 r = -errno;
233 } while (r == -ENOENT);
234
235 return r;
236 }
237
238 int 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)
247 return log_device_debug_errno(dev, r, "Failed to get devpath: %m");
248
249 /* update possible left-over symlinks */
250 FOREACH_DEVICE_DEVLINK(dev_old, name) {
251 const char *name_current;
252 bool found = false;
253
254 /* check if old link name still belongs to this device */
255 FOREACH_DEVICE_DEVLINK(dev, name_current)
256 if (streq(name, name_current)) {
257 found = true;
258 break;
259 }
260
261 if (found)
262 continue;
263
264 log_device_debug(dev, "Updating old name, '%s' no longer belonging to '%s'",
265 name, devpath);
266 link_update(dev, name, false);
267 }
268
269 return 0;
270 }
271
272 static int node_permissions_apply(sd_device *dev, bool apply_mac,
273 mode_t mode, uid_t uid, gid_t gid,
274 OrderedHashmap *seclabel_list) {
275 const char *devnode, *subsystem, *id_filename = NULL;
276 struct stat stats;
277 dev_t devnum;
278 bool apply_mode, apply_uid, apply_gid;
279 int r;
280
281 assert(dev);
282
283 r = sd_device_get_devname(dev, &devnode);
284 if (r < 0)
285 return log_device_debug_errno(dev, r, "Failed to get devname: %m");
286 r = sd_device_get_subsystem(dev, &subsystem);
287 if (r < 0)
288 return log_device_debug_errno(dev, r, "Failed to get subsystem: %m");
289 r = sd_device_get_devnum(dev, &devnum);
290 if (r < 0)
291 return log_device_debug_errno(dev, r, "Failed to get devnum: %m");
292 (void) device_get_id_filename(dev, &id_filename);
293
294 if (streq(subsystem, "block"))
295 mode |= S_IFBLK;
296 else
297 mode |= S_IFCHR;
298
299 if (lstat(devnode, &stats) < 0) {
300 if (errno == ENOENT)
301 return 0; /* this is necessarily racey, so ignore missing the device */
302 return log_device_debug_errno(dev, errno, "cannot stat() node %s: %m", devnode);
303 }
304
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",
308 devnode, id_filename);
309
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) {
315 bool selinux = false, smack = false;
316 const char *name, *label;
317 Iterator i;
318
319 if (apply_mode || apply_uid || apply_gid) {
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);
325
326 r = chmod_and_chown(devnode, mode, uid, gid);
327 if (r < 0)
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);
335 } else
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);
341
342 /* apply SECLABEL{$module}=$label */
343 ORDERED_HASHMAP_FOREACH_KEY(label, name, seclabel_list, i) {
344 int q;
345
346 if (streq(name, "selinux")) {
347 selinux = true;
348
349 q = mac_selinux_apply(devnode, label);
350 if (q < 0)
351 log_device_full(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
352 "SECLABEL: failed to set SELinux label '%s': %m", label);
353 else
354 log_device_debug(dev, "SECLABEL: set SELinux label '%s'", label);
355
356 } else if (streq(name, "smack")) {
357 smack = true;
358
359 q = mac_smack_apply(devnode, SMACK_ATTR_ACCESS, label);
360 if (q < 0)
361 log_device_full(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
362 "SECLABEL: failed to set SMACK label '%s': %m", label);
363 else
364 log_device_debug(dev, "SECLABEL: set SMACK label '%s'", label);
365
366 } else
367 log_device_error(dev, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label);
368 }
369
370 /* set the defaults */
371 if (!selinux)
372 (void) mac_selinux_fix(devnode, LABEL_IGNORE_ENOENT);
373 if (!smack)
374 (void) mac_smack_apply(devnode, SMACK_ATTR_ACCESS, NULL);
375 }
376
377 /* always update timestamp when we re-use the node, like on media change events */
378 (void) utimensat(AT_FDCWD, devnode, NULL, 0);
379
380 return r;
381 }
382
383 static 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);
390
391 r = sd_device_get_subsystem(dev, &subsystem);
392 if (r < 0)
393 return r;
394
395 r = sd_device_get_devnum(dev, &devnum);
396 if (r < 0)
397 return r;
398
399 xsprintf_dev_num_path(filename,
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
411 int udev_node_add(sd_device *dev, bool apply,
412 mode_t mode, uid_t uid, gid_t gid,
413 OrderedHashmap *seclabel_list) {
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)
422 return log_device_debug_errno(dev, r, "Failed to get devnode: %m");
423
424 if (DEBUG_LOGGING) {
425 const char *id_filename = NULL;
426
427 (void) device_get_id_filename(dev, &id_filename);
428 log_device_debug(dev, "Handling device node '%s', devnum=%s", devnode, strnull(id_filename));
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)
437 return log_device_debug_errno(dev, r, "Failed to get device path: %m");
438
439 /* always add /dev/{block,char}/$major:$minor */
440 (void) node_symlink(dev, devnode, filename);
441
442 /* create/update symlinks, add symlinks to name index */
443 FOREACH_DEVICE_DEVLINK(dev, devlink)
444 (void) link_update(dev, devlink, true);
445
446 return 0;
447 }
448
449 int udev_node_remove(sd_device *dev) {
450 _cleanup_free_ char *filename = NULL;
451 const char *devlink;
452 int r;
453
454 assert(dev);
455
456 /* remove/update symlinks, remove symlinks from name index */
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)
462 return log_device_debug_errno(dev, r, "Failed to get device path: %m");
463
464 /* remove /dev/{block,char}/$major:$minor */
465 (void) unlink(filename);
466
467 return 0;
468 }