+static int node_symlink(const char *node, const char *slink)
+{
+ char target[PATH_SIZE] = "";
+ char buf[PATH_SIZE];
+ int i = 0;
+ int tail = 0;
+ int len;
+
+ /* use relative link */
+ while (node[i] && (node[i] == slink[i])) {
+ if (node[i] == '/')
+ tail = i+1;
+ i++;
+ }
+ while (slink[i] != '\0') {
+ if (slink[i] == '/')
+ strlcat(target, "../", sizeof(target));
+ i++;
+ }
+ strlcat(target, &node[tail], sizeof(target));
+
+ /* look if symlink already exists */
+ len = readlink(slink, buf, sizeof(buf));
+ if (len > 0) {
+ buf[len] = '\0';
+ if (strcmp(target, buf) == 0) {
+ info("preserve already existing symlink '%s' to '%s'", slink, target);
+ selinux_setfilecon(slink, NULL, S_IFLNK);
+ goto exit;
+ }
+ info("link '%s' points to different target '%s', delete it", slink, buf);
+ unlink(slink);
+ }
+
+ /* create link */
+ info("creating symlink '%s' to '%s'", slink, target);
+ selinux_setfscreatecon(slink, NULL, S_IFLNK);
+ if (symlink(target, slink) != 0)
+ err("symlink(%s, %s) failed: %s", target, slink, strerror(errno));
+ selinux_resetfscreatecon();
+
+exit:
+ return 0;
+}
+
+static int update_link(struct udevice *udev, const char *name)
+{
+ LIST_HEAD(name_list);
+ char slink[PATH_SIZE];
+ char node[PATH_SIZE];
+ struct udevice *udev_db;
+ struct name_entry *device;
+ char target[PATH_MAX] = "";
+ int count;
+ int priority = 0;
+ int rc = 0;
+
+ strlcpy(slink, udev_root, sizeof(slink));
+ strlcat(slink, "/", sizeof(slink));
+ strlcat(slink, name, sizeof(slink));
+
+ count = udev_db_get_devices_by_name(name, &name_list);
+ info("found %i devices with name '%s'", count, name);
+
+ /* if we don't have a reference, delete it */
+ if (count <= 0) {
+ info("no reference left, remove '%s'", name);
+ if (!udev->test_run) {
+ unlink(slink);
+ delete_path(slink);
+ }
+ goto out;
+ }
+
+ /* find the device with the highest priority */
+ list_for_each_entry(device, &name_list, node) {
+ info("found '%s' for '%s'", device->name, name);
+
+ /* did we find ourself? we win, if we have the same priority */
+ if (strcmp(udev->dev->devpath, device->name) == 0) {
+ info("compare (our own) priority of '%s' %i >= %i",
+ udev->dev->devpath, udev->link_priority, priority);
+ if (target[0] == '\0' || udev->link_priority >= priority) {
+ priority = udev->link_priority;
+ strlcpy(target, udev->name, sizeof(target));
+ }
+ continue;
+ }
+
+ /* or something else, then read priority from database */
+ udev_db = udev_device_init(NULL);
+ if (udev_db == NULL)
+ continue;
+ if (udev_db_get_device(udev_db, device->name) == 0) {
+ info("compare priority of '%s' %i > %i",
+ udev_db->dev->devpath, udev_db->link_priority, priority);
+ if (target[0] == '\0' || udev_db->link_priority > priority) {
+ priority = udev_db->link_priority;
+ strlcpy(target, udev_db->name, sizeof(target));
+ }
+ }
+ udev_device_cleanup(udev_db);
+ }
+ name_list_cleanup(&name_list);
+
+ if (target[0] == '\0') {
+ err("missing target for '%s'", name);
+ rc = -1;
+ goto out;
+ }
+
+ /* create symlink to the target with the highest priority */
+ strlcpy(node, udev_root, sizeof(node));
+ strlcat(node, "/", sizeof(node));
+ strlcat(node, target, sizeof(node));
+ info("'%s' with target '%s' has the highest priority %i, create it", name, target, priority);
+ if (!udev->test_run) {
+ create_path(slink);
+ node_symlink(node, slink);
+ }
+out:
+ return rc;
+}
+
+void udev_node_update_symlinks(struct udevice *udev, struct udevice *udev_old)