]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-node.c
c11eb8c1acee439498d4d1fbab8b3071bd297870
1 /* SPDX-License-Identifier: GPL-2.0+ */
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"
18 #include "format-util.h"
20 #include "libudev-util.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"
28 #include "udev-node.h"
30 static int node_symlink(sd_device
*dev
, const char *node
, const char *slink
) {
31 _cleanup_free_
char *slink_dirname
= NULL
, *target
= NULL
;
32 const char *id_filename
, *slink_tmp
;
40 slink_dirname
= dirname_malloc(slink
);
44 /* use relative link */
45 r
= path_make_relative(slink_dirname
, node
, &target
);
47 return log_device_error_errno(dev
, r
, "Failed to get relative path from '%s' to '%s': %m", slink
, node
);
49 /* preserve link with correct target, do not replace node of other device */
50 if (lstat(slink
, &stats
) == 0) {
51 if (S_ISBLK(stats
.st_mode
) || S_ISCHR(stats
.st_mode
)) {
52 log_device_error(dev
, "Conflicting device node '%s' found, link to '%s' will not be created.", slink
, node
);
54 } else if (S_ISLNK(stats
.st_mode
)) {
55 _cleanup_free_
char *buf
= NULL
;
57 if (readlink_malloc(slink
, &buf
) >= 0 &&
59 log_device_debug(dev
, "Preserve already existing symlink '%s' to '%s'", slink
, target
);
60 (void) label_fix(slink
, LABEL_IGNORE_ENOENT
);
61 (void) utimensat(AT_FDCWD
, slink
, NULL
, AT_SYMLINK_NOFOLLOW
);
66 log_device_debug(dev
, "Creating symlink '%s' to '%s'", slink
, target
);
68 r
= mkdir_parents_label(slink
, 0755);
69 if (!IN_SET(r
, 0, -ENOENT
))
71 mac_selinux_create_file_prepare(slink
, S_IFLNK
);
72 if (symlink(target
, slink
) < 0)
74 mac_selinux_create_file_clear();
75 } while (r
== -ENOENT
);
79 log_device_debug_errno(dev
, r
, "Failed to create symlink '%s' to '%s', trying to replace '%s': %m", slink
, target
, slink
);
82 log_device_debug(dev
, "Atomically replace '%s'", slink
);
83 r
= device_get_id_filename(dev
, &id_filename
);
85 return log_device_error_errno(dev
, r
, "Failed to get id_filename: %m");
86 slink_tmp
= strjoina(slink
, ".tmp-", id_filename
);
87 (void) unlink(slink_tmp
);
89 r
= mkdir_parents_label(slink_tmp
, 0755);
90 if (!IN_SET(r
, 0, -ENOENT
))
92 mac_selinux_create_file_prepare(slink_tmp
, S_IFLNK
);
93 if (symlink(target
, slink_tmp
) < 0)
95 mac_selinux_create_file_clear();
96 } while (r
== -ENOENT
);
98 return log_device_error_errno(dev
, r
, "Failed to create symlink '%s' to '%s': %m", slink_tmp
, target
);
100 if (rename(slink_tmp
, slink
) < 0) {
101 r
= log_device_error_errno(dev
, errno
, "Failed to rename '%s' to '%s' failed: %m", slink_tmp
, slink
);
102 (void) unlink(slink_tmp
);
108 /* find device node of device with highest priority */
109 static int link_find_prioritized(sd_device
*dev
, bool add
, const char *stackdir
, char **ret
) {
110 _cleanup_closedir_
DIR *dir
= NULL
;
111 _cleanup_free_
char *target
= NULL
;
122 r
= device_get_devlink_priority(dev
, &priority
);
126 r
= sd_device_get_devname(dev
, &devnode
);
130 target
= strdup(devnode
);
135 dir
= opendir(stackdir
);
138 *ret
= TAKE_PTR(target
);
145 FOREACH_DIRENT_ALL(dent
, dir
, break) {
146 _cleanup_(sd_device_unrefp
) sd_device
*dev_db
= NULL
;
147 const char *devnode
, *id_filename
;
150 if (dent
->d_name
[0] == '\0')
152 if (dent
->d_name
[0] == '.')
155 log_device_debug(dev
, "Found '%s' claiming '%s'", dent
->d_name
, stackdir
);
157 if (device_get_id_filename(dev
, &id_filename
) < 0)
160 /* did we find ourself? */
161 if (streq(dent
->d_name
, id_filename
))
164 if (sd_device_new_from_device_id(&dev_db
, dent
->d_name
) < 0)
167 if (sd_device_get_devname(dev_db
, &devnode
) < 0)
170 if (device_get_devlink_priority(dev_db
, &db_prio
) < 0)
173 if (target
&& db_prio
<= priority
)
176 log_device_debug(dev_db
, "Device claims priority %i for '%s'", db_prio
, stackdir
);
178 r
= free_and_strdup(&target
, devnode
);
184 *ret
= TAKE_PTR(target
);
188 /* manage "stack of names" with possibly specified device priorities */
189 static int link_update(sd_device
*dev
, const char *slink
, bool add
) {
190 _cleanup_free_
char *target
= NULL
, *filename
= NULL
, *dirname
= NULL
;
191 char name_enc
[PATH_MAX
];
192 const char *id_filename
;
198 r
= device_get_id_filename(dev
, &id_filename
);
200 return log_device_debug_errno(dev
, r
, "Failed to get id_filename: %m");
202 util_path_encode(slink
+ STRLEN("/dev"), name_enc
, sizeof(name_enc
));
203 dirname
= path_join("/run/udev/links/", name_enc
);
206 filename
= path_join(dirname
, id_filename
);
210 if (!add
&& unlink(filename
) == 0)
211 (void) rmdir(dirname
);
213 r
= link_find_prioritized(dev
, add
, dirname
, &target
);
215 log_device_debug(dev
, "No reference left, removing '%s'", slink
);
216 if (unlink(slink
) == 0)
217 (void) rmdir_parents(slink
, "/");
219 (void) node_symlink(dev
, target
, slink
);
223 _cleanup_close_
int fd
= -1;
225 r
= mkdir_parents(filename
, 0755);
226 if (!IN_SET(r
, 0, -ENOENT
))
228 fd
= open(filename
, O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_TRUNC
|O_NOFOLLOW
, 0444);
231 } while (r
== -ENOENT
);
236 int udev_node_update_old_links(sd_device
*dev
, sd_device
*dev_old
) {
237 const char *name
, *devpath
;
243 r
= sd_device_get_devpath(dev
, &devpath
);
245 return log_device_debug_errno(dev
, r
, "Failed to get devpath: %m");
247 /* update possible left-over symlinks */
248 FOREACH_DEVICE_DEVLINK(dev_old
, name
) {
249 const char *name_current
;
252 /* check if old link name still belongs to this device */
253 FOREACH_DEVICE_DEVLINK(dev
, name_current
)
254 if (streq(name
, name_current
)) {
262 log_device_debug(dev
, "Updating old name, '%s' no longer belonging to '%s'",
264 link_update(dev
, name
, false);
270 static int node_permissions_apply(sd_device
*dev
, bool apply
,
271 mode_t mode
, uid_t uid
, gid_t gid
,
272 Hashmap
*seclabel_list
) {
273 const char *devnode
, *subsystem
, *id_filename
= NULL
;
280 r
= sd_device_get_devname(dev
, &devnode
);
282 return log_device_debug_errno(dev
, r
, "Failed to get devname: %m");
283 r
= sd_device_get_subsystem(dev
, &subsystem
);
285 return log_device_debug_errno(dev
, r
, "Failed to get subsystem: %m");
286 r
= sd_device_get_devnum(dev
, &devnum
);
288 return log_device_debug_errno(dev
, r
, "Failed to get devnum: %m");
289 (void) device_get_id_filename(dev
, &id_filename
);
291 if (streq(subsystem
, "block"))
296 if (lstat(devnode
, &stats
) < 0)
297 return log_device_debug_errno(dev
, errno
, "cannot stat() node '%s' (%m)", devnode
);
299 if (((stats
.st_mode
& S_IFMT
) != (mode
& S_IFMT
)) || (stats
.st_rdev
!= devnum
))
300 return log_device_debug_errno(dev
, EEXIST
, "Found node '%s' with non-matching devnum %s, skip handling",
301 devnode
, id_filename
);
304 bool selinux
= false, smack
= false;
305 const char *name
, *label
;
308 if ((stats
.st_mode
& 0777) != (mode
& 0777) || stats
.st_uid
!= uid
|| stats
.st_gid
!= gid
) {
309 log_device_debug(dev
, "Setting permissions %s, %#o, uid=%u, gid=%u", devnode
, mode
, uid
, gid
);
310 if (chmod(devnode
, mode
) < 0)
311 r
= log_device_warning_errno(dev
, errno
, "Failed to set mode of %s to %#o: %m", devnode
, mode
);
312 if (chown(devnode
, uid
, gid
) < 0)
313 r
= log_device_warning_errno(dev
, errno
, "Failed to set owner of %s to uid=%u, gid=%u: %m", devnode
, uid
, gid
);
315 log_device_debug(dev
, "Preserve permissions of %s, %#o, uid=%u, gid=%u", devnode
, mode
, uid
, gid
);
317 /* apply SECLABEL{$module}=$label */
318 HASHMAP_FOREACH_KEY(label
, name
, seclabel_list
, i
) {
321 if (streq(name
, "selinux")) {
324 q
= mac_selinux_apply(devnode
, label
);
326 log_device_error_errno(dev
, q
, "SECLABEL: failed to set SELinux label '%s': %m", label
);
328 log_device_debug(dev
, "SECLABEL: set SELinux label '%s'", label
);
330 } else if (streq(name
, "smack")) {
333 q
= mac_smack_apply(devnode
, SMACK_ATTR_ACCESS
, label
);
335 log_device_error_errno(dev
, q
, "SECLABEL: failed to set SMACK label '%s': %m", label
);
337 log_device_debug(dev
, "SECLABEL: set SMACK label '%s'", label
);
340 log_device_error(dev
, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name
, label
);
343 /* set the defaults */
345 (void) mac_selinux_fix(devnode
, LABEL_IGNORE_ENOENT
);
347 (void) mac_smack_apply(devnode
, SMACK_ATTR_ACCESS
, NULL
);
350 /* always update timestamp when we re-use the node, like on media change events */
351 (void) utimensat(AT_FDCWD
, devnode
, NULL
, 0);
356 static int xsprintf_dev_num_path_from_sd_device(sd_device
*dev
, char **ret
) {
357 char filename
[DEV_NUM_PATH_MAX
], *s
;
358 const char *subsystem
;
364 r
= sd_device_get_subsystem(dev
, &subsystem
);
368 r
= sd_device_get_devnum(dev
, &devnum
);
372 xsprintf_dev_num_path(filename
,
373 streq(subsystem
, "block") ? "block" : "char",
376 s
= strdup(filename
);
384 int udev_node_add(sd_device
*dev
, bool apply
,
385 mode_t mode
, uid_t uid
, gid_t gid
,
386 Hashmap
*seclabel_list
) {
387 const char *devnode
, *devlink
;
388 _cleanup_free_
char *filename
= NULL
;
393 r
= sd_device_get_devname(dev
, &devnode
);
395 return log_device_debug_errno(dev
, r
, "Failed to get devnode: %m");
398 const char *id_filename
= NULL
;
400 (void) device_get_id_filename(dev
, &id_filename
);
401 log_device_debug(dev
, "Handling device node '%s', devnum=%s, mode=%#o, uid="UID_FMT
", gid="GID_FMT
,
402 devnode
, strnull(id_filename
), mode
, uid
, gid
);
405 r
= node_permissions_apply(dev
, apply
, mode
, uid
, gid
, seclabel_list
);
409 r
= xsprintf_dev_num_path_from_sd_device(dev
, &filename
);
411 return log_device_debug_errno(dev
, r
, "Failed to get device path: %m");
413 /* always add /dev/{block,char}/$major:$minor */
414 (void) node_symlink(dev
, devnode
, filename
);
416 /* create/update symlinks, add symlinks to name index */
417 FOREACH_DEVICE_DEVLINK(dev
, devlink
)
418 (void) link_update(dev
, devlink
, true);
423 int udev_node_remove(sd_device
*dev
) {
424 _cleanup_free_
char *filename
= NULL
;
430 /* remove/update symlinks, remove symlinks from name index */
431 FOREACH_DEVICE_DEVLINK(dev
, devlink
)
432 (void) link_update(dev
, devlink
, false);
434 r
= xsprintf_dev_num_path_from_sd_device(dev
, &filename
);
436 return log_device_debug_errno(dev
, r
, "Failed to get device path: %m");
438 /* remove /dev/{block,char}/$major:$minor */
439 (void) unlink(filename
);