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