]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-node.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
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"
16 #include "format-util.h"
18 #include "libudev-util.h"
20 #include "path-util.h"
21 #include "selinux-util.h"
22 #include "smack-util.h"
23 #include "stat-util.h"
24 #include "stdio-util.h"
25 #include "string-util.h"
27 #include "udev-node.h"
28 #include "user-util.h"
30 #define LINK_UPDATE_MAX_RETRIES 128
32 static int node_symlink(sd_device
*dev
, const char *node
, const char *slink
) {
33 _cleanup_free_
char *slink_dirname
= NULL
, *target
= NULL
;
34 const char *id_filename
, *slink_tmp
;
42 slink_dirname
= dirname_malloc(slink
);
46 /* use relative link */
47 r
= path_make_relative(slink_dirname
, node
, &target
);
49 return log_device_error_errno(dev
, r
, "Failed to get relative path from '%s' to '%s': %m", slink
, node
);
51 /* preserve link with correct target, do not replace node of other device */
52 if (lstat(slink
, &stats
) == 0) {
53 if (S_ISBLK(stats
.st_mode
) || S_ISCHR(stats
.st_mode
))
54 return log_device_error_errno(dev
, SYNTHETIC_ERRNO(EOPNOTSUPP
),
55 "Conflicting device node '%s' found, link to '%s' will not be created.", slink
, node
);
56 else if (S_ISLNK(stats
.st_mode
)) {
57 _cleanup_free_
char *buf
= NULL
;
59 if (readlink_malloc(slink
, &buf
) >= 0 &&
61 log_device_debug(dev
, "Preserve already existing symlink '%s' to '%s'", slink
, target
);
62 (void) label_fix(slink
, LABEL_IGNORE_ENOENT
);
63 (void) utimensat(AT_FDCWD
, slink
, NULL
, AT_SYMLINK_NOFOLLOW
);
68 log_device_debug(dev
, "Creating symlink '%s' to '%s'", slink
, target
);
70 r
= mkdir_parents_label(slink
, 0755);
71 if (!IN_SET(r
, 0, -ENOENT
))
73 mac_selinux_create_file_prepare(slink
, S_IFLNK
);
74 if (symlink(target
, slink
) < 0)
76 mac_selinux_create_file_clear();
77 } while (r
== -ENOENT
);
81 log_device_debug_errno(dev
, r
, "Failed to create symlink '%s' to '%s', trying to replace '%s': %m", slink
, target
, slink
);
84 log_device_debug(dev
, "Atomically replace '%s'", slink
);
85 r
= device_get_id_filename(dev
, &id_filename
);
87 return log_device_error_errno(dev
, r
, "Failed to get id_filename: %m");
88 slink_tmp
= strjoina(slink
, ".tmp-", id_filename
);
89 (void) unlink(slink_tmp
);
91 r
= mkdir_parents_label(slink_tmp
, 0755);
92 if (!IN_SET(r
, 0, -ENOENT
))
94 mac_selinux_create_file_prepare(slink_tmp
, S_IFLNK
);
95 if (symlink(target
, slink_tmp
) < 0)
97 mac_selinux_create_file_clear();
98 } while (r
== -ENOENT
);
100 return log_device_error_errno(dev
, r
, "Failed to create symlink '%s' to '%s': %m", slink_tmp
, target
);
102 if (rename(slink_tmp
, slink
) < 0) {
103 r
= log_device_error_errno(dev
, errno
, "Failed to rename '%s' to '%s': %m", slink_tmp
, slink
);
104 (void) unlink(slink_tmp
);
106 /* Tell caller that we replaced already existing symlink. */
112 /* find device node of device with highest priority */
113 static int link_find_prioritized(sd_device
*dev
, bool add
, const char *stackdir
, char **ret
) {
114 _cleanup_closedir_
DIR *dir
= NULL
;
115 _cleanup_free_
char *target
= NULL
;
126 r
= device_get_devlink_priority(dev
, &priority
);
130 r
= sd_device_get_devname(dev
, &devnode
);
134 target
= strdup(devnode
);
139 dir
= opendir(stackdir
);
142 *ret
= TAKE_PTR(target
);
149 FOREACH_DIRENT_ALL(dent
, dir
, break) {
150 _cleanup_(sd_device_unrefp
) sd_device
*dev_db
= NULL
;
151 const char *devnode
, *id_filename
;
154 if (dent
->d_name
[0] == '\0')
156 if (dent
->d_name
[0] == '.')
159 log_device_debug(dev
, "Found '%s' claiming '%s'", dent
->d_name
, stackdir
);
161 if (device_get_id_filename(dev
, &id_filename
) < 0)
164 /* did we find ourself? */
165 if (streq(dent
->d_name
, id_filename
))
168 if (sd_device_new_from_device_id(&dev_db
, dent
->d_name
) < 0)
171 if (sd_device_get_devname(dev_db
, &devnode
) < 0)
174 if (device_get_devlink_priority(dev_db
, &db_prio
) < 0)
177 if (target
&& db_prio
<= priority
)
180 log_device_debug(dev_db
, "Device claims priority %i for '%s'", db_prio
, stackdir
);
182 r
= free_and_strdup(&target
, devnode
);
191 *ret
= TAKE_PTR(target
);
195 /* manage "stack of names" with possibly specified device priorities */
196 static int link_update(sd_device
*dev
, const char *slink
, bool add
) {
197 _cleanup_free_
char *target
= NULL
, *filename
= NULL
, *dirname
= NULL
;
198 char name_enc
[PATH_MAX
];
199 const char *id_filename
;
205 r
= device_get_id_filename(dev
, &id_filename
);
207 return log_device_debug_errno(dev
, r
, "Failed to get id_filename: %m");
209 util_path_encode(slink
+ STRLEN("/dev"), name_enc
, sizeof(name_enc
));
210 dirname
= path_join("/run/udev/links/", name_enc
);
213 filename
= path_join(dirname
, id_filename
);
217 if (!add
&& unlink(filename
) == 0)
218 (void) rmdir(dirname
);
222 _cleanup_close_
int fd
= -1;
224 r
= mkdir_parents(filename
, 0755);
225 if (!IN_SET(r
, 0, -ENOENT
))
227 fd
= open(filename
, O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_TRUNC
|O_NOFOLLOW
, 0444);
230 } while (r
== -ENOENT
);
232 /* If the database entry is not written yet we will just do one iteration and possibly wrong symlink
233 * will be fixed in the second invocation. */
234 retries
= sd_device_get_is_initialized(dev
) > 0 ? LINK_UPDATE_MAX_RETRIES
: 1;
236 for (i
= 0; i
< retries
; i
++) {
237 struct stat st1
= {}, st2
= {};
239 r
= stat(dirname
, &st1
);
240 if (r
< 0 && errno
!= ENOENT
)
243 r
= link_find_prioritized(dev
, add
, dirname
, &target
);
245 log_device_debug(dev
, "No reference left, removing '%s'", slink
);
246 if (unlink(slink
) == 0)
247 (void) rmdir_parents(slink
, "/");
251 return log_device_error_errno(dev
, r
, "Failed to determine highest priority symlink: %m");
253 r
= node_symlink(dev
, target
, slink
);
255 (void) unlink(filename
);
258 /* We have replaced already existing symlink, possibly there is some other device trying
259 * to claim the same symlink. Let's do one more iteration to give us a chance to fix
260 * the error if other device actually claims the symlink with higher priority. */
263 /* Skip the second stat() if the first failed, stat_inode_unmodified() would return false regardless. */
264 if ((st1
.st_mode
& S_IFMT
) != 0) {
265 r
= stat(dirname
, &st2
);
266 if (r
< 0 && errno
!= ENOENT
)
269 if (stat_inode_unmodified(&st1
, &st2
))
274 return i
< LINK_UPDATE_MAX_RETRIES
? 0 : -ELOOP
;
277 int udev_node_update_old_links(sd_device
*dev
, sd_device
*dev_old
) {
278 const char *name
, *devpath
;
284 r
= sd_device_get_devpath(dev
, &devpath
);
286 return log_device_debug_errno(dev
, r
, "Failed to get devpath: %m");
288 /* update possible left-over symlinks */
289 FOREACH_DEVICE_DEVLINK(dev_old
, name
) {
290 const char *name_current
;
293 /* check if old link name still belongs to this device */
294 FOREACH_DEVICE_DEVLINK(dev
, name_current
)
295 if (streq(name
, name_current
)) {
303 log_device_debug(dev
, "Updating old name, '%s' no longer belonging to '%s'",
305 link_update(dev
, name
, false);
311 static int node_permissions_apply(sd_device
*dev
, bool apply_mac
,
312 mode_t mode
, uid_t uid
, gid_t gid
,
313 OrderedHashmap
*seclabel_list
) {
314 const char *devnode
, *subsystem
, *id_filename
= NULL
;
315 bool apply_mode
, apply_uid
, apply_gid
;
316 _cleanup_close_
int node_fd
= -1;
323 r
= sd_device_get_devname(dev
, &devnode
);
325 return log_device_debug_errno(dev
, r
, "Failed to get devname: %m");
326 r
= sd_device_get_subsystem(dev
, &subsystem
);
328 return log_device_debug_errno(dev
, r
, "Failed to get subsystem: %m");
329 r
= sd_device_get_devnum(dev
, &devnum
);
331 return log_device_debug_errno(dev
, r
, "Failed to get devnum: %m");
332 (void) device_get_id_filename(dev
, &id_filename
);
334 if (streq(subsystem
, "block"))
339 node_fd
= open(devnode
, O_PATH
|O_NOFOLLOW
|O_CLOEXEC
);
341 if (errno
== ENOENT
) {
342 log_device_debug_errno(dev
, errno
, "Device node %s is missing, skipping handling.", devnode
);
343 return 0; /* This is necessarily racey, so ignore missing the device */
346 return log_device_debug_errno(dev
, errno
, "Cannot open node %s: %m", devnode
);
349 if (fstat(node_fd
, &stats
) < 0)
350 return log_device_debug_errno(dev
, errno
, "cannot stat() node %s: %m", devnode
);
352 if ((mode
!= MODE_INVALID
&& (stats
.st_mode
& S_IFMT
) != (mode
& S_IFMT
)) || stats
.st_rdev
!= devnum
) {
353 log_device_debug(dev
, "Found node '%s' with non-matching devnum %s, skipping handling.",
354 devnode
, id_filename
);
355 return 0; /* We might process a device that already got replaced by the time we have a look
356 * at it, handle this gracefully and step away. */
359 apply_mode
= mode
!= MODE_INVALID
&& (stats
.st_mode
& 0777) != (mode
& 0777);
360 apply_uid
= uid_is_valid(uid
) && stats
.st_uid
!= uid
;
361 apply_gid
= gid_is_valid(gid
) && stats
.st_gid
!= gid
;
363 if (apply_mode
|| apply_uid
|| apply_gid
|| apply_mac
) {
364 bool selinux
= false, smack
= false;
365 const char *name
, *label
;
367 if (apply_mode
|| apply_uid
|| apply_gid
) {
368 log_device_debug(dev
, "Setting permissions %s, uid=" UID_FMT
", gid=" GID_FMT
", mode=%#o",
370 uid_is_valid(uid
) ? uid
: stats
.st_uid
,
371 gid_is_valid(gid
) ? gid
: stats
.st_gid
,
372 mode
!= MODE_INVALID
? mode
& 0777 : stats
.st_mode
& 0777);
374 r
= fchmod_and_chown(node_fd
, mode
, uid
, gid
);
376 log_device_full_errno(dev
, r
== -ENOENT
? LOG_DEBUG
: LOG_ERR
, r
,
377 "Failed to set owner/mode of %s to uid=" UID_FMT
378 ", gid=" GID_FMT
", mode=%#o: %m",
380 uid_is_valid(uid
) ? uid
: stats
.st_uid
,
381 gid_is_valid(gid
) ? gid
: stats
.st_gid
,
382 mode
!= MODE_INVALID
? mode
& 0777 : stats
.st_mode
& 0777);
384 log_device_debug(dev
, "Preserve permissions of %s, uid=" UID_FMT
", gid=" GID_FMT
", mode=%#o",
386 uid_is_valid(uid
) ? uid
: stats
.st_uid
,
387 gid_is_valid(gid
) ? gid
: stats
.st_gid
,
388 mode
!= MODE_INVALID
? mode
& 0777 : stats
.st_mode
& 0777);
390 /* apply SECLABEL{$module}=$label */
391 ORDERED_HASHMAP_FOREACH_KEY(label
, name
, seclabel_list
) {
394 if (streq(name
, "selinux")) {
397 q
= mac_selinux_apply_fd(node_fd
, devnode
, label
);
399 log_device_full_errno(dev
, q
== -ENOENT
? LOG_DEBUG
: LOG_ERR
, q
,
400 "SECLABEL: failed to set SELinux label '%s': %m", label
);
402 log_device_debug(dev
, "SECLABEL: set SELinux label '%s'", label
);
404 } else if (streq(name
, "smack")) {
407 q
= mac_smack_apply_fd(node_fd
, SMACK_ATTR_ACCESS
, label
);
409 log_device_full_errno(dev
, q
== -ENOENT
? LOG_DEBUG
: LOG_ERR
, q
,
410 "SECLABEL: failed to set SMACK label '%s': %m", label
);
412 log_device_debug(dev
, "SECLABEL: set SMACK label '%s'", label
);
415 log_device_error(dev
, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name
, label
);
418 /* set the defaults */
420 (void) mac_selinux_fix_fd(node_fd
, devnode
, LABEL_IGNORE_ENOENT
);
422 (void) mac_smack_apply_fd(node_fd
, SMACK_ATTR_ACCESS
, NULL
);
425 /* always update timestamp when we re-use the node, like on media change events */
426 r
= futimens_opath(node_fd
, NULL
);
428 log_device_debug_errno(dev
, r
, "Failed to adjust timestamp of node %s: %m", devnode
);
433 static int xsprintf_dev_num_path_from_sd_device(sd_device
*dev
, char **ret
) {
434 char filename
[DEV_NUM_PATH_MAX
], *s
;
435 const char *subsystem
;
441 r
= sd_device_get_subsystem(dev
, &subsystem
);
445 r
= sd_device_get_devnum(dev
, &devnum
);
449 xsprintf_dev_num_path(filename
,
450 streq(subsystem
, "block") ? "block" : "char",
453 s
= strdup(filename
);
461 int udev_node_add(sd_device
*dev
, bool apply
,
462 mode_t mode
, uid_t uid
, gid_t gid
,
463 OrderedHashmap
*seclabel_list
) {
464 const char *devnode
, *devlink
;
465 _cleanup_free_
char *filename
= NULL
;
470 r
= sd_device_get_devname(dev
, &devnode
);
472 return log_device_debug_errno(dev
, r
, "Failed to get devnode: %m");
475 const char *id_filename
= NULL
;
477 (void) device_get_id_filename(dev
, &id_filename
);
478 log_device_debug(dev
, "Handling device node '%s', devnum=%s", devnode
, strnull(id_filename
));
481 r
= node_permissions_apply(dev
, apply
, mode
, uid
, gid
, seclabel_list
);
485 r
= xsprintf_dev_num_path_from_sd_device(dev
, &filename
);
487 return log_device_debug_errno(dev
, r
, "Failed to get device path: %m");
489 /* always add /dev/{block,char}/$major:$minor */
490 (void) node_symlink(dev
, devnode
, filename
);
492 /* create/update symlinks, add symlinks to name index */
493 FOREACH_DEVICE_DEVLINK(dev
, devlink
) {
494 r
= link_update(dev
, devlink
, true);
496 log_device_info_errno(dev
, r
, "Failed to update device symlinks: %m");
502 int udev_node_remove(sd_device
*dev
) {
503 _cleanup_free_
char *filename
= NULL
;
509 /* remove/update symlinks, remove symlinks from name index */
510 FOREACH_DEVICE_DEVLINK(dev
, devlink
) {
511 r
= link_update(dev
, devlink
, false);
513 log_device_info_errno(dev
, r
, "Failed to update device symlinks: %m");
516 r
= xsprintf_dev_num_path_from_sd_device(dev
, &filename
);
518 return log_device_debug_errno(dev
, r
, "Failed to get device path: %m");
520 /* remove /dev/{block,char}/$major:$minor */
521 (void) unlink(filename
);