]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-node.c
1 /* SPDX-License-Identifier: GPL-2.0+ */
12 #include "device-nodes.h"
13 #include "dirent-util.h"
14 #include "format-util.h"
16 #include "selinux-util.h"
17 #include "smack-util.h"
18 #include "stdio-util.h"
19 #include "string-util.h"
22 static int node_symlink(struct udev_device
*dev
, const char *node
, const char *slink
) {
23 _cleanup_free_
char *slink_dirname
= NULL
, *target
= NULL
;
24 char slink_tmp
[UTIL_PATH_SIZE
+ 32];
28 slink_dirname
= dirname_malloc(slink
);
32 /* use relative link */
33 r
= path_make_relative(slink_dirname
, node
, &target
);
35 return log_error_errno(r
, "Failed to get relative path from '%s' to '%s': %m", slink
, node
);
37 /* preserve link with correct target, do not replace node of other device */
38 if (lstat(slink
, &stats
) == 0) {
39 if (S_ISBLK(stats
.st_mode
) || S_ISCHR(stats
.st_mode
)) {
40 log_error("conflicting device node '%s' found, link to '%s' will not be created", slink
, node
);
42 } else if (S_ISLNK(stats
.st_mode
)) {
43 char buf
[UTIL_PATH_SIZE
];
46 len
= readlink(slink
, buf
, sizeof(buf
));
47 if (len
> 0 && len
< (int)sizeof(buf
)) {
49 if (streq(target
, buf
)) {
50 log_debug("preserve already existing symlink '%s' to '%s'", slink
, target
);
51 (void) label_fix(slink
, LABEL_IGNORE_ENOENT
);
52 utimensat(AT_FDCWD
, slink
, NULL
, AT_SYMLINK_NOFOLLOW
);
58 log_debug("creating symlink '%s' to '%s'", slink
, target
);
60 err
= mkdir_parents_label(slink
, 0755);
61 if (!IN_SET(err
, 0, -ENOENT
))
63 mac_selinux_create_file_prepare(slink
, S_IFLNK
);
64 err
= symlink(target
, slink
);
67 mac_selinux_create_file_clear();
68 } while (err
== -ENOENT
);
73 log_debug("atomically replace '%s'", slink
);
74 strscpyl(slink_tmp
, sizeof(slink_tmp
), slink
, ".tmp-", udev_device_get_id_filename(dev
), NULL
);
77 err
= mkdir_parents_label(slink_tmp
, 0755);
78 if (!IN_SET(err
, 0, -ENOENT
))
80 mac_selinux_create_file_prepare(slink_tmp
, S_IFLNK
);
81 err
= symlink(target
, slink_tmp
);
84 mac_selinux_create_file_clear();
85 } while (err
== -ENOENT
);
87 log_error_errno(errno
, "symlink '%s' '%s' failed: %m", target
, slink_tmp
);
90 err
= rename(slink_tmp
, slink
);
92 log_error_errno(errno
, "rename '%s' '%s' failed: %m", slink_tmp
, slink
);
99 /* find device node of device with highest priority */
100 static const char *link_find_prioritized(struct udev_device
*dev
, bool add
, const char *stackdir
, char *buf
, size_t bufsize
) {
104 const char *target
= NULL
;
107 priority
= udev_device_get_devlink_priority(dev
);
108 strscpy(buf
, bufsize
, udev_device_get_devnode(dev
));
112 dir
= opendir(stackdir
);
115 FOREACH_DIRENT_ALL(dent
, dir
, break) {
116 struct udev_device
*dev_db
;
118 if (dent
->d_name
[0] == '\0')
120 if (dent
->d_name
[0] == '.')
123 log_debug("found '%s' claiming '%s'", dent
->d_name
, stackdir
);
125 /* did we find ourself? */
126 if (streq(dent
->d_name
, udev_device_get_id_filename(dev
)))
129 dev_db
= udev_device_new_from_device_id(NULL
, dent
->d_name
);
130 if (dev_db
!= NULL
) {
133 devnode
= udev_device_get_devnode(dev_db
);
134 if (devnode
!= NULL
) {
135 if (target
== NULL
|| udev_device_get_devlink_priority(dev_db
) > priority
) {
136 log_debug("'%s' claims priority %i for '%s'",
137 udev_device_get_syspath(dev_db
), udev_device_get_devlink_priority(dev_db
), stackdir
);
138 priority
= udev_device_get_devlink_priority(dev_db
);
139 strscpy(buf
, bufsize
, devnode
);
143 udev_device_unref(dev_db
);
150 /* manage "stack of names" with possibly specified device priorities */
151 static void link_update(struct udev_device
*dev
, const char *slink
, bool add
) {
152 char name_enc
[UTIL_PATH_SIZE
];
153 char filename
[UTIL_PATH_SIZE
* 2];
154 char dirname
[UTIL_PATH_SIZE
];
156 char buf
[UTIL_PATH_SIZE
];
158 util_path_encode(slink
+ STRLEN("/dev"), name_enc
, sizeof(name_enc
));
159 strscpyl(dirname
, sizeof(dirname
), "/run/udev/links/", name_enc
, NULL
);
160 strscpyl(filename
, sizeof(filename
), dirname
, "/", udev_device_get_id_filename(dev
), NULL
);
162 if (!add
&& unlink(filename
) == 0)
165 target
= link_find_prioritized(dev
, add
, dirname
, buf
, sizeof(buf
));
166 if (target
== NULL
) {
167 log_debug("no reference left, remove '%s'", slink
);
168 if (unlink(slink
) == 0)
169 rmdir_parents(slink
, "/");
171 log_debug("creating link '%s' to '%s'", slink
, target
);
172 node_symlink(dev
, target
, slink
);
181 err
= mkdir_parents(filename
, 0755);
182 if (!IN_SET(err
, 0, -ENOENT
))
184 fd
= open(filename
, O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_TRUNC
|O_NOFOLLOW
, 0444);
189 } while (err
== -ENOENT
);
193 void udev_node_update_old_links(struct udev_device
*dev
, struct udev_device
*dev_old
) {
194 struct udev_list_entry
*list_entry
;
196 /* update possible left-over symlinks */
197 udev_list_entry_foreach(list_entry
, udev_device_get_devlinks_list_entry(dev_old
)) {
198 const char *name
= udev_list_entry_get_name(list_entry
);
199 struct udev_list_entry
*list_entry_current
;
202 /* check if old link name still belongs to this device */
204 udev_list_entry_foreach(list_entry_current
, udev_device_get_devlinks_list_entry(dev
)) {
205 const char *name_current
= udev_list_entry_get_name(list_entry_current
);
207 if (streq(name
, name_current
)) {
215 log_debug("update old name, '%s' no longer belonging to '%s'",
216 name
, udev_device_get_devpath(dev
));
217 link_update(dev
, name
, false);
221 static int node_permissions_apply(struct udev_device
*dev
, bool apply
,
222 mode_t mode
, uid_t uid
, gid_t gid
,
223 struct udev_list
*seclabel_list
) {
224 const char *devnode
= udev_device_get_devnode(dev
);
225 dev_t devnum
= udev_device_get_devnum(dev
);
227 struct udev_list_entry
*entry
;
230 if (streq(udev_device_get_subsystem(dev
), "block"))
235 if (lstat(devnode
, &stats
) != 0) {
236 err
= log_debug_errno(errno
, "cannot stat() node '%s' (%m)", devnode
);
240 if (((stats
.st_mode
& S_IFMT
) != (mode
& S_IFMT
)) || (stats
.st_rdev
!= devnum
)) {
242 log_debug("found node '%s' with non-matching devnum %s, skip handling",
243 udev_device_get_devnode(dev
), udev_device_get_id_filename(dev
));
248 bool selinux
= false;
251 if ((stats
.st_mode
& 0777) != (mode
& 0777) || stats
.st_uid
!= uid
|| stats
.st_gid
!= gid
) {
252 log_debug("set permissions %s, %#o, uid=%u, gid=%u", devnode
, mode
, uid
, gid
);
253 err
= chmod(devnode
, mode
);
255 log_warning_errno(errno
, "setting mode of %s to %#o failed: %m", devnode
, mode
);
256 err
= chown(devnode
, uid
, gid
);
258 log_warning_errno(errno
, "setting owner of %s to uid=%u, gid=%u failed: %m", devnode
, uid
, gid
);
260 log_debug("preserve permissions %s, %#o, uid=%u, gid=%u", devnode
, mode
, uid
, gid
);
263 /* apply SECLABEL{$module}=$label */
264 udev_list_entry_foreach(entry
, udev_list_get_entry(seclabel_list
)) {
265 const char *name
, *label
;
268 name
= udev_list_entry_get_name(entry
);
269 label
= udev_list_entry_get_value(entry
);
271 if (streq(name
, "selinux")) {
274 r
= mac_selinux_apply(devnode
, label
);
276 log_error_errno(r
, "SECLABEL: failed to set SELinux label '%s': %m", label
);
278 log_debug("SECLABEL: set SELinux label '%s'", label
);
280 } else if (streq(name
, "smack")) {
283 r
= mac_smack_apply(devnode
, SMACK_ATTR_ACCESS
, label
);
285 log_error_errno(r
, "SECLABEL: failed to set SMACK label '%s': %m", label
);
287 log_debug("SECLABEL: set SMACK label '%s'", label
);
290 log_error("SECLABEL: unknown subsystem, ignoring '%s'='%s'", name
, label
);
293 /* set the defaults */
295 (void) mac_selinux_fix(devnode
, LABEL_IGNORE_ENOENT
);
297 mac_smack_apply(devnode
, SMACK_ATTR_ACCESS
, NULL
);
300 /* always update timestamp when we re-use the node, like on media change events */
301 utimensat(AT_FDCWD
, devnode
, NULL
, 0);
306 void udev_node_add(struct udev_device
*dev
, bool apply
,
307 mode_t mode
, uid_t uid
, gid_t gid
,
308 struct udev_list
*seclabel_list
) {
309 char filename
[DEV_NUM_PATH_MAX
];
310 struct udev_list_entry
*list_entry
;
312 log_debug("handling device node '%s', devnum=%s, mode=%#o, uid="UID_FMT
", gid="GID_FMT
,
313 udev_device_get_devnode(dev
), udev_device_get_id_filename(dev
), mode
, uid
, gid
);
315 if (node_permissions_apply(dev
, apply
, mode
, uid
, gid
, seclabel_list
) < 0)
318 /* always add /dev/{block,char}/$major:$minor */
319 xsprintf_dev_num_path(filename
,
320 streq(udev_device_get_subsystem(dev
), "block") ? "block" : "char",
321 udev_device_get_devnum(dev
));
322 node_symlink(dev
, udev_device_get_devnode(dev
), filename
);
324 /* create/update symlinks, add symlinks to name index */
325 udev_list_entry_foreach(list_entry
, udev_device_get_devlinks_list_entry(dev
))
326 link_update(dev
, udev_list_entry_get_name(list_entry
), true);
329 void udev_node_remove(struct udev_device
*dev
) {
330 struct udev_list_entry
*list_entry
;
331 char filename
[DEV_NUM_PATH_MAX
];
333 /* remove/update symlinks, remove symlinks from name index */
334 udev_list_entry_foreach(list_entry
, udev_device_get_devlinks_list_entry(dev
))
335 link_update(dev
, udev_list_entry_get_name(list_entry
), false);
337 /* remove /dev/{block,char}/$major:$minor */
338 xsprintf_dev_num_path(filename
,
339 streq(udev_device_get_subsystem(dev
), "block") ? "block" : "char",
340 udev_device_get_devnum(dev
));