summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xtest/udev-test.pl1
-rw-r--r--udev_device.c35
-rw-r--r--udev_node.c39
3 files changed, 61 insertions, 14 deletions
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 33da68a386..fafaa96ab6 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -862,6 +862,7 @@ EOF
devpath => "/class/tty/tty0",
exp_name => "link",
exp_target => "link",
+ exp_add_error => "yes",
exp_rem_error => "yes",
option => "clean",
rules => <<EOF
diff --git a/udev_device.c b/udev_device.c
index 6dc843ed22..a7a8b13c96 100644
--- a/udev_device.c
+++ b/udev_device.c
@@ -132,17 +132,38 @@ int udev_device_event(struct udev_rules *rules, struct udevice *udev)
/* read current database entry, we may want to cleanup symlinks */
udev_old = udev_device_init();
if (udev_old != NULL) {
- if (udev_db_get_device(udev_old, udev->dev->devpath) == 0) {
- info("device '%s' already known, remove possible symlinks", udev->dev->devpath);
- udev_node_remove_symlinks(udev_old);
- }
- udev_device_cleanup(udev_old);
+ if (udev_db_get_device(udev_old, udev->dev->devpath) != 0) {
+ udev_device_cleanup(udev_old);
+ udev_old = NULL;
+ } else
+ info("device '%s' already in database, validate currently present symlinks",
+ udev->dev->devpath);
}
- /* create node and symlinks, store record in database */
+ /* create node and symlinks */
retval = udev_node_add(udev, udev_old);
- if (retval == 0)
+ if (retval == 0) {
+ /* store record in database */
udev_db_add_device(udev);
+
+ /* remove possibly left-over symlinks */
+ if (udev_old != NULL) {
+ struct name_entry *link_loop;
+ struct name_entry *link_old_loop;
+ struct name_entry *link_old_tmp_loop;
+
+ /* remove still valid symlinks from old list */
+ list_for_each_entry_safe(link_old_loop, link_old_tmp_loop, &udev_old->symlink_list, node)
+ list_for_each_entry(link_loop, &udev->symlink_list, node)
+ if (strcmp(link_old_loop->name, link_loop->name) == 0) {
+ dbg("symlink '%s' still valid, keep it", link_old_loop->name);
+ list_del(&link_old_loop->node);
+ free(link_old_loop);
+ }
+ udev_node_remove_symlinks(udev_old);
+ udev_device_cleanup(udev_old);
+ }
+ }
goto exit;
}
diff --git a/udev_node.c b/udev_node.c
index 2a30fe3222..ee59d4ae7c 100644
--- a/udev_node.c
+++ b/udev_node.c
@@ -90,6 +90,36 @@ exit:
return retval;
}
+static int udev_node_symlink(struct udevice *udev, const char *linktarget, const char *filename)
+{
+ char target[PATH_SIZE];
+ int len;
+
+ /* look if symlink already exists */
+ len = readlink(filename, target, sizeof(target));
+ if (len > 0) {
+ target[len] = '\0';
+ if (strcmp(linktarget, target) == 0) {
+ info("preserving symlink '%s' to '%s'", filename, linktarget);
+ selinux_setfilecon(filename, NULL, S_IFLNK);
+ goto exit;
+ } else {
+ info("link '%s' points to different target '%s', delete it", filename, target);
+ unlink(filename);
+ }
+ }
+
+ /* create link */
+ info("creating symlink '%s' to '%s'", filename, linktarget);
+ selinux_setfscreatecon(filename, NULL, S_IFLNK);
+ if (symlink(linktarget, filename) != 0)
+ err("symlink(%s, %s) failed: %s", linktarget, filename, strerror(errno));
+ selinux_resetfscreatecon();
+
+exit:
+ return 0;
+}
+
int udev_node_add(struct udevice *udev, struct udevice *udev_old)
{
char filename[PATH_SIZE];
@@ -205,13 +235,8 @@ int udev_node_add(struct udevice *udev, struct udevice *udev_old)
strlcat(linktarget, &udev->name[tail], sizeof(linktarget));
info("creating symlink '%s' to '%s'", filename, linktarget);
- if (!udev->test_run) {
- unlink(filename);
- selinux_setfscreatecon(filename, NULL, S_IFLNK);
- if (symlink(linktarget, filename) != 0)
- err("symlink(%s, %s) failed: %s", linktarget, filename, strerror(errno));
- selinux_resetfscreatecon();
- }
+ if (!udev->test_run)
+ udev_node_symlink(udev, linktarget, filename);
strlcat(symlinks, filename, sizeof(symlinks));
strlcat(symlinks, " ", sizeof(symlinks));