chiark / gitweb /
store devpath with the usual leading slash
[elogind.git] / udev_node.c
index 2a30fe32223351a76ef1f594de1e9d7f48dea608..d069dfc4aada7ab57596be9a6281db431d8c3252 100644 (file)
@@ -1,8 +1,6 @@
 /*
- * udev-node.c - device node handling
- *
  * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
- * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
  *
  *     This program is free software; you can redistribute it and/or modify it
  *     under the terms of the GNU General Public License as published by the
@@ -15,7 +13,7 @@
  * 
  *     You should have received a copy of the GNU General Public License along
  *     with this program; if not, write to the Free Software Foundation, Inc.,
- *     675 Mass Ave, Cambridge, MA 02139, USA.
+ *     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *
  */
 
@@ -51,7 +49,7 @@ int udev_node_mknod(struct udevice *udev, const char *file, dev_t devt, mode_t m
        /* preserve node with already correct numbers, to prevent changing the inode number */
        if ((stats.st_mode & S_IFMT) == (mode & S_IFMT) && (stats.st_rdev == devt)) {
                info("preserve file '%s', because it has correct dev_t", file);
-               selinux_setfilecon(file, udev->dev->kernel_name, stats.st_mode);
+               selinux_setfilecon(file, udev->dev->kernel, stats.st_mode);
                goto perms;
        }
 
@@ -61,7 +59,7 @@ int udev_node_mknod(struct udevice *udev, const char *file, dev_t devt, mode_t m
                dbg("already present file '%s' unlinked", file);
 
 create:
-       selinux_setfscreatecon(file, udev->dev->kernel_name, mode);
+       selinux_setfscreatecon(file, udev->dev->kernel, mode);
        retval = mknod(file, mode, devt);
        selinux_resetfscreatecon();
        if (retval != 0) {
@@ -90,24 +88,64 @@ exit:
        return retval;
 }
 
+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("preserving 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;
+}
+
 int udev_node_add(struct udevice *udev, struct udevice *udev_old)
 {
        char filename[PATH_SIZE];
-       struct name_entry *name_loop;
        uid_t uid;
        gid_t gid;
-       int tail;
        int i;
        int retval = 0;
 
-       selinux_init();
-
        snprintf(filename, sizeof(filename), "%s/%s", udev_root, udev->name);
        filename[sizeof(filename)-1] = '\0';
 
        /* create parent directories if needed */
-       if (strchr(udev->name, '/'))
-               create_path(filename);
+       create_path(filename);
 
        if (strcmp(udev->owner, "root") == 0)
                uid = 0;
@@ -174,46 +212,23 @@ int udev_node_add(struct udevice *udev, struct udevice *udev_old)
 
        /* create symlink(s) if requested */
        if (!list_empty(&udev->symlink_list)) {
-               char symlinks[512] = "";
+               struct name_entry *name_loop;
+               char symlinks[PATH_SIZE] = "";
 
                list_for_each_entry(name_loop, &udev->symlink_list, node) {
-                       char linktarget[PATH_SIZE];
+                       char slink[PATH_SIZE];
 
-                       snprintf(filename, sizeof(filename), "%s/%s", udev_root, name_loop->name);
-                       filename[sizeof(filename)-1] = '\0';
+                       strlcpy(slink, udev_root, sizeof(slink));
+                       strlcat(slink, "/", sizeof(slink));
+                       strlcat(slink, name_loop->name, sizeof(slink));
 
-                       dbg("symlink '%s' to node '%s' requested", filename, udev->name);
-                       if (!udev->test_run)
-                               if (strchr(filename, '/'))
-                                       create_path(filename);
-
-                       /* optimize relative link */
-                       linktarget[0] = '\0';
-                       i = 0;
-                       tail = 0;
-                       while (udev->name[i] && (udev->name[i] == name_loop->name[i])) {
-                               if (udev->name[i] == '/')
-                                       tail = i+1;
-                               i++;
-                       }
-                       while (name_loop->name[i] != '\0') {
-                               if (name_loop->name[i] == '/')
-                                       strlcat(linktarget, "../", sizeof(linktarget));
-                               i++;
-                       }
-
-                       strlcat(linktarget, &udev->name[tail], sizeof(linktarget));
-
-                       info("creating symlink '%s' to '%s'", filename, linktarget);
+                       info("creating symlink '%s' to node '%s'", slink, filename);
                        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();
+                               create_path(slink);
+                               node_symlink(filename, slink);
                        }
 
-                       strlcat(symlinks, filename, sizeof(symlinks));
+                       strlcat(symlinks, slink, sizeof(symlinks));
                        strlcat(symlinks, " ", sizeof(symlinks));
                }
 
@@ -233,9 +248,11 @@ void udev_node_remove_symlinks(struct udevice *udev)
        struct stat stats;
 
        if (!list_empty(&udev->symlink_list)) {
-               char symlinks[512] = "";
+               char symlinks[PATH_SIZE] = "";
 
                list_for_each_entry(name_loop, &udev->symlink_list, node) {
+                       char devpath[PATH_SIZE];
+
                        snprintf(filename, sizeof(filename), "%s/%s", udev_root, name_loop->name);
                        filename[sizeof(filename)-1] = '\0';
 
@@ -251,9 +268,32 @@ void udev_node_remove_symlinks(struct udevice *udev)
                        info("removing symlink '%s'", filename);
                        if (!udev->test_run) {
                                unlink(filename);
+                               delete_path(filename);
+                       }
 
-                               if (strchr(filename, '/'))
-                                       delete_path(filename);
+                       /* see if another device wants this symlink */
+                       if (udev_db_lookup_name(name_loop->name, devpath, sizeof(devpath)) == 0) {
+                               struct udevice *old;
+
+                               info("found overwritten symlink '%s' of '%s'", name_loop->name, devpath);
+                               old = udev_device_init();
+                               if (old != NULL) {
+                                       if (udev_db_get_device(old, devpath) == 0) {
+                                               char slink[PATH_SIZE];
+                                               char node[PATH_SIZE];
+
+                                               strlcpy(slink, udev_root, sizeof(slink));
+                                               strlcat(slink, "/", sizeof(slink));
+                                               strlcat(slink, name_loop->name, sizeof(slink));
+                                               strlcpy(node, udev_root, sizeof(node));
+                                               strlcat(node, "/", sizeof(node));
+                                               strlcat(node, old->name, sizeof(node));
+                                               info("restore symlink '%s' to '%s'", slink, node);
+                                               if (!udev->test_run)
+                                                       node_symlink(node, slink);
+                                       }
+                                       udev_device_cleanup(old);
+                               }
                        }
 
                        strlcat(symlinks, filename, sizeof(symlinks));
@@ -278,7 +318,6 @@ int udev_node_remove(struct udevice *udev)
 
        snprintf(filename, sizeof(filename), "%s/%s", udev_root, udev->name);
        filename[sizeof(filename)-1] = '\0';
-
        if (stat(filename, &stats) != 0) {
                dbg("device node '%s' not found", filename);
                return -1;
@@ -294,7 +333,6 @@ int udev_node_remove(struct udevice *udev)
                return retval;
 
        setenv("DEVNAME", filename, 1);
-
        num = udev->partitions;
        if (num > 0) {
                int i;
@@ -310,9 +348,6 @@ int udev_node_remove(struct udevice *udev)
                        unlink_secure(partitionname);
                }
        }
-
-       if (strchr(udev->name, '/'))
-               delete_path(filename);
-
+       delete_path(filename);
        return retval;
 }