]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-node.c
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
);
187 *ret
= TAKE_PTR(target
);
191 /* manage "stack of names" with possibly specified device priorities */
192 static int link_update(sd_device
*dev
, const char *slink
, bool add
) {
193 _cleanup_free_
char *target
= NULL
, *filename
= NULL
, *dirname
= NULL
;
194 char name_enc
[PATH_MAX
];
195 const char *id_filename
;
201 r
= device_get_id_filename(dev
, &id_filename
);
203 return log_device_debug_errno(dev
, r
, "Failed to get id_filename: %m");
205 util_path_encode(slink
+ STRLEN("/dev"), name_enc
, sizeof(name_enc
));
206 dirname
= path_join("/run/udev/links/", name_enc
);
209 filename
= path_join(dirname
, id_filename
);
213 if (!add
&& unlink(filename
) == 0)
214 (void) rmdir(dirname
);
216 r
= link_find_prioritized(dev
, add
, dirname
, &target
);
218 log_device_debug(dev
, "No reference left, removing '%s'", slink
);
219 if (unlink(slink
) == 0)
220 (void) rmdir_parents(slink
, "/");
222 (void) node_symlink(dev
, target
, slink
);
226 _cleanup_close_
int fd
= -1;
228 r
= mkdir_parents(filename
, 0755);
229 if (!IN_SET(r
, 0, -ENOENT
))
231 fd
= open(filename
, O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_TRUNC
|O_NOFOLLOW
, 0444);
234 } while (r
== -ENOENT
);
239 int udev_node_update_old_links(sd_device
*dev
, sd_device
*dev_old
) {
240 const char *name
, *devpath
;
246 r
= sd_device_get_devpath(dev
, &devpath
);
248 return log_device_debug_errno(dev
, r
, "Failed to get devpath: %m");
250 /* update possible left-over symlinks */
251 FOREACH_DEVICE_DEVLINK(dev_old
, name
) {
252 const char *name_current
;
255 /* check if old link name still belongs to this device */
256 FOREACH_DEVICE_DEVLINK(dev
, name_current
)
257 if (streq(name
, name_current
)) {
265 log_device_debug(dev
, "Updating old name, '%s' no longer belonging to '%s'",
267 link_update(dev
, name
, false);
273 static int node_permissions_apply(sd_device
*dev
, bool apply
,
274 mode_t mode
, uid_t uid
, gid_t gid
,
275 Hashmap
*seclabel_list
) {
276 const char *devnode
, *subsystem
, *id_filename
= NULL
;
283 r
= sd_device_get_devname(dev
, &devnode
);
285 return log_device_debug_errno(dev
, r
, "Failed to get devname: %m");
286 r
= sd_device_get_subsystem(dev
, &subsystem
);
288 return log_device_debug_errno(dev
, r
, "Failed to get subsystem: %m");
289 r
= sd_device_get_devnum(dev
, &devnum
);
291 return log_device_debug_errno(dev
, r
, "Failed to get devnum: %m");
292 (void) device_get_id_filename(dev
, &id_filename
);
294 if (streq(subsystem
, "block"))
299 if (lstat(devnode
, &stats
) < 0)
300 return log_device_debug_errno(dev
, errno
, "cannot stat() node '%s' (%m)", devnode
);
302 if (((stats
.st_mode
& S_IFMT
) != (mode
& S_IFMT
)) || (stats
.st_rdev
!= devnum
))
303 return log_device_debug_errno(dev
, SYNTHETIC_ERRNO(EEXIST
), "Found node '%s' with non-matching devnum %s, skip handling",
304 devnode
, id_filename
);
307 bool selinux
= false, smack
= false;
308 const char *name
, *label
;
311 if ((stats
.st_mode
& 0777) != (mode
& 0777) || stats
.st_uid
!= uid
|| stats
.st_gid
!= gid
) {
312 log_device_debug(dev
, "Setting permissions %s, %#o, uid=%u, gid=%u", devnode
, mode
, uid
, gid
);
313 if (chmod(devnode
, mode
) < 0)
314 r
= log_device_warning_errno(dev
, errno
, "Failed to set mode of %s to %#o: %m", devnode
, mode
);
315 if (chown(devnode
, uid
, gid
) < 0)
316 r
= log_device_warning_errno(dev
, errno
, "Failed to set owner of %s to uid=%u, gid=%u: %m", devnode
, uid
, gid
);
318 log_device_debug(dev
, "Preserve permissions of %s, %#o, uid=%u, gid=%u", devnode
, mode
, uid
, gid
);
320 /* apply SECLABEL{$module}=$label */
321 HASHMAP_FOREACH_KEY(label
, name
, seclabel_list
, i
) {
324 if (streq(name
, "selinux")) {
327 q
= mac_selinux_apply(devnode
, label
);
329 log_device_error_errno(dev
, q
, "SECLABEL: failed to set SELinux label '%s': %m", label
);
331 log_device_debug(dev
, "SECLABEL: set SELinux label '%s'", label
);
333 } else if (streq(name
, "smack")) {
336 q
= mac_smack_apply(devnode
, SMACK_ATTR_ACCESS
, label
);
338 log_device_error_errno(dev
, q
, "SECLABEL: failed to set SMACK label '%s': %m", label
);
340 log_device_debug(dev
, "SECLABEL: set SMACK label '%s'", label
);
343 log_device_error(dev
, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name
, label
);
346 /* set the defaults */
348 (void) mac_selinux_fix(devnode
, LABEL_IGNORE_ENOENT
);
350 (void) mac_smack_apply(devnode
, SMACK_ATTR_ACCESS
, NULL
);
353 /* always update timestamp when we re-use the node, like on media change events */
354 (void) utimensat(AT_FDCWD
, devnode
, NULL
, 0);
359 static int xsprintf_dev_num_path_from_sd_device(sd_device
*dev
, char **ret
) {
360 char filename
[DEV_NUM_PATH_MAX
], *s
;
361 const char *subsystem
;
367 r
= sd_device_get_subsystem(dev
, &subsystem
);
371 r
= sd_device_get_devnum(dev
, &devnum
);
375 xsprintf_dev_num_path(filename
,
376 streq(subsystem
, "block") ? "block" : "char",
379 s
= strdup(filename
);
387 int udev_node_add(sd_device
*dev
, bool apply
,
388 mode_t mode
, uid_t uid
, gid_t gid
,
389 Hashmap
*seclabel_list
) {
390 const char *devnode
, *devlink
;
391 _cleanup_free_
char *filename
= NULL
;
396 r
= sd_device_get_devname(dev
, &devnode
);
398 return log_device_debug_errno(dev
, r
, "Failed to get devnode: %m");
401 const char *id_filename
= NULL
;
403 (void) device_get_id_filename(dev
, &id_filename
);
404 log_device_debug(dev
, "Handling device node '%s', devnum=%s, mode=%#o, uid="UID_FMT
", gid="GID_FMT
,
405 devnode
, strnull(id_filename
), mode
, uid
, gid
);
408 r
= node_permissions_apply(dev
, apply
, mode
, uid
, gid
, seclabel_list
);
412 r
= xsprintf_dev_num_path_from_sd_device(dev
, &filename
);
414 return log_device_debug_errno(dev
, r
, "Failed to get device path: %m");
416 /* always add /dev/{block,char}/$major:$minor */
417 (void) node_symlink(dev
, devnode
, filename
);
419 /* create/update symlinks, add symlinks to name index */
420 FOREACH_DEVICE_DEVLINK(dev
, devlink
)
421 (void) link_update(dev
, devlink
, true);
426 int udev_node_remove(sd_device
*dev
) {
427 _cleanup_free_
char *filename
= NULL
;
433 /* remove/update symlinks, remove symlinks from name index */
434 FOREACH_DEVICE_DEVLINK(dev
, devlink
)
435 (void) link_update(dev
, devlink
, false);
437 r
= xsprintf_dev_num_path_from_sd_device(dev
, &filename
);
439 return log_device_debug_errno(dev
, r
, "Failed to get device path: %m");
441 /* remove /dev/{block,char}/$major:$minor */
442 (void) unlink(filename
);