]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-node.c
1 /* SPDX-License-Identifier: GPL-2.0+ */
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 "stdio-util.h"
24 #include "string-util.h"
26 #include "udev-node.h"
27 #include "user-util.h"
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
;
39 slink_dirname
= dirname_malloc(slink
);
43 /* use relative link */
44 r
= path_make_relative(slink_dirname
, node
, &target
);
46 return log_device_error_errno(dev
, r
, "Failed to get relative path from '%s' to '%s': %m", slink
, node
);
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
;
56 if (readlink_malloc(slink
, &buf
) >= 0 &&
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
);
65 log_device_debug(dev
, "Creating symlink '%s' to '%s'", slink
, target
);
67 r
= mkdir_parents_label(slink
, 0755);
68 if (!IN_SET(r
, 0, -ENOENT
))
70 mac_selinux_create_file_prepare(slink
, S_IFLNK
);
71 if (symlink(target
, slink
) < 0)
73 mac_selinux_create_file_clear();
74 } while (r
== -ENOENT
);
78 log_device_debug_errno(dev
, r
, "Failed to create symlink '%s' to '%s', trying to replace '%s': %m", slink
, target
, slink
);
81 log_device_debug(dev
, "Atomically replace '%s'", slink
);
82 r
= device_get_id_filename(dev
, &id_filename
);
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
);
88 r
= mkdir_parents_label(slink_tmp
, 0755);
89 if (!IN_SET(r
, 0, -ENOENT
))
91 mac_selinux_create_file_prepare(slink_tmp
, S_IFLNK
);
92 if (symlink(target
, slink_tmp
) < 0)
94 mac_selinux_create_file_clear();
95 } while (r
== -ENOENT
);
97 return log_device_error_errno(dev
, r
, "Failed to create symlink '%s' to '%s': %m", slink_tmp
, target
);
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
);
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
;
121 r
= device_get_devlink_priority(dev
, &priority
);
125 r
= sd_device_get_devname(dev
, &devnode
);
129 target
= strdup(devnode
);
134 dir
= opendir(stackdir
);
137 *ret
= TAKE_PTR(target
);
144 FOREACH_DIRENT_ALL(dent
, dir
, break) {
145 _cleanup_(sd_device_unrefp
) sd_device
*dev_db
= NULL
;
146 const char *devnode
, *id_filename
;
149 if (dent
->d_name
[0] == '\0')
151 if (dent
->d_name
[0] == '.')
154 log_device_debug(dev
, "Found '%s' claiming '%s'", dent
->d_name
, stackdir
);
156 if (device_get_id_filename(dev
, &id_filename
) < 0)
159 /* did we find ourself? */
160 if (streq(dent
->d_name
, id_filename
))
163 if (sd_device_new_from_device_id(&dev_db
, dent
->d_name
) < 0)
166 if (sd_device_get_devname(dev_db
, &devnode
) < 0)
169 if (device_get_devlink_priority(dev_db
, &db_prio
) < 0)
172 if (target
&& db_prio
<= priority
)
175 log_device_debug(dev_db
, "Device claims priority %i for '%s'", db_prio
, stackdir
);
177 r
= free_and_strdup(&target
, devnode
);
186 *ret
= TAKE_PTR(target
);
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
;
200 r
= device_get_id_filename(dev
, &id_filename
);
202 return log_device_debug_errno(dev
, r
, "Failed to get id_filename: %m");
204 util_path_encode(slink
+ STRLEN("/dev"), name_enc
, sizeof(name_enc
));
205 dirname
= path_join("/run/udev/links/", name_enc
);
208 filename
= path_join(dirname
, id_filename
);
212 if (!add
&& unlink(filename
) == 0)
213 (void) rmdir(dirname
);
215 r
= link_find_prioritized(dev
, add
, dirname
, &target
);
217 log_device_debug(dev
, "No reference left, removing '%s'", slink
);
218 if (unlink(slink
) == 0)
219 (void) rmdir_parents(slink
, "/");
221 (void) node_symlink(dev
, target
, slink
);
225 _cleanup_close_
int fd
= -1;
227 r
= mkdir_parents(filename
, 0755);
228 if (!IN_SET(r
, 0, -ENOENT
))
230 fd
= open(filename
, O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_TRUNC
|O_NOFOLLOW
, 0444);
233 } while (r
== -ENOENT
);
238 int udev_node_update_old_links(sd_device
*dev
, sd_device
*dev_old
) {
239 const char *name
, *devpath
;
245 r
= sd_device_get_devpath(dev
, &devpath
);
247 return log_device_debug_errno(dev
, r
, "Failed to get devpath: %m");
249 /* update possible left-over symlinks */
250 FOREACH_DEVICE_DEVLINK(dev_old
, name
) {
251 const char *name_current
;
254 /* check if old link name still belongs to this device */
255 FOREACH_DEVICE_DEVLINK(dev
, name_current
)
256 if (streq(name
, name_current
)) {
264 log_device_debug(dev
, "Updating old name, '%s' no longer belonging to '%s'",
266 link_update(dev
, name
, false);
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
;
278 bool apply_mode
, apply_uid
, apply_gid
;
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) {
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
);
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
);
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
;
314 if (apply_mode
|| apply_uid
|| apply_gid
|| apply_mac
) {
315 bool selinux
= false, smack
= false;
316 const char *name
, *label
;
318 if (apply_mode
|| apply_uid
|| apply_gid
) {
319 log_device_debug(dev
, "Setting permissions %s, uid=" UID_FMT
", gid=" GID_FMT
", mode=%#o",
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);
325 r
= chmod_and_chown(devnode
, mode
, uid
, gid
);
327 log_device_full_errno(dev
, r
== -ENOENT
? LOG_DEBUG
: LOG_ERR
, r
,
328 "Failed to set owner/mode of %s to uid=" UID_FMT
329 ", gid=" GID_FMT
", mode=%#o: %m",
331 uid_is_valid(uid
) ? uid
: stats
.st_uid
,
332 gid_is_valid(gid
) ? gid
: stats
.st_gid
,
333 mode
!= MODE_INVALID
? mode
& 0777 : stats
.st_mode
& 0777);
335 log_device_debug(dev
, "Preserve permissions of %s, uid=" UID_FMT
", gid=" GID_FMT
", mode=%#o",
337 uid_is_valid(uid
) ? uid
: stats
.st_uid
,
338 gid_is_valid(gid
) ? gid
: stats
.st_gid
,
339 mode
!= MODE_INVALID
? mode
& 0777 : stats
.st_mode
& 0777);
341 /* apply SECLABEL{$module}=$label */
342 ORDERED_HASHMAP_FOREACH_KEY(label
, name
, seclabel_list
) {
345 if (streq(name
, "selinux")) {
348 q
= mac_selinux_apply(devnode
, label
);
350 log_device_full_errno(dev
, q
== -ENOENT
? LOG_DEBUG
: LOG_ERR
, q
,
351 "SECLABEL: failed to set SELinux label '%s': %m", label
);
353 log_device_debug(dev
, "SECLABEL: set SELinux label '%s'", label
);
355 } else if (streq(name
, "smack")) {
358 q
= mac_smack_apply(devnode
, SMACK_ATTR_ACCESS
, label
);
360 log_device_full_errno(dev
, q
== -ENOENT
? LOG_DEBUG
: LOG_ERR
, q
,
361 "SECLABEL: failed to set SMACK label '%s': %m", label
);
363 log_device_debug(dev
, "SECLABEL: set SMACK label '%s'", label
);
366 log_device_error(dev
, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name
, label
);
369 /* set the defaults */
371 (void) mac_selinux_fix(devnode
, LABEL_IGNORE_ENOENT
);
373 (void) mac_smack_apply(devnode
, SMACK_ATTR_ACCESS
, NULL
);
376 /* always update timestamp when we re-use the node, like on media change events */
377 (void) utimensat(AT_FDCWD
, devnode
, NULL
, 0);
382 static int xsprintf_dev_num_path_from_sd_device(sd_device
*dev
, char **ret
) {
383 char filename
[DEV_NUM_PATH_MAX
], *s
;
384 const char *subsystem
;
390 r
= sd_device_get_subsystem(dev
, &subsystem
);
394 r
= sd_device_get_devnum(dev
, &devnum
);
398 xsprintf_dev_num_path(filename
,
399 streq(subsystem
, "block") ? "block" : "char",
402 s
= strdup(filename
);
410 int udev_node_add(sd_device
*dev
, bool apply
,
411 mode_t mode
, uid_t uid
, gid_t gid
,
412 OrderedHashmap
*seclabel_list
) {
413 const char *devnode
, *devlink
;
414 _cleanup_free_
char *filename
= NULL
;
419 r
= sd_device_get_devname(dev
, &devnode
);
421 return log_device_debug_errno(dev
, r
, "Failed to get devnode: %m");
424 const char *id_filename
= NULL
;
426 (void) device_get_id_filename(dev
, &id_filename
);
427 log_device_debug(dev
, "Handling device node '%s', devnum=%s", devnode
, strnull(id_filename
));
430 r
= node_permissions_apply(dev
, apply
, mode
, uid
, gid
, seclabel_list
);
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 /* always add /dev/{block,char}/$major:$minor */
439 (void) node_symlink(dev
, devnode
, filename
);
441 /* create/update symlinks, add symlinks to name index */
442 FOREACH_DEVICE_DEVLINK(dev
, devlink
)
443 (void) link_update(dev
, devlink
, true);
448 int udev_node_remove(sd_device
*dev
) {
449 _cleanup_free_
char *filename
= NULL
;
455 /* remove/update symlinks, remove symlinks from name index */
456 FOREACH_DEVICE_DEVLINK(dev
, devlink
)
457 (void) link_update(dev
, devlink
, false);
459 r
= xsprintf_dev_num_path_from_sd_device(dev
, &filename
);
461 return log_device_debug_errno(dev
, r
, "Failed to get device path: %m");
463 /* remove /dev/{block,char}/$major:$minor */
464 (void) unlink(filename
);