mdev: reinstate "follow symlinks" flag, this time with explanation
[oweals/busybox.git] / util-linux / mdev.c
index 5e1cd36bb72bbfae11d8cc1c06b9415cd0f55776..a4c0520ac302179d7ee58a77de0b68b8f8a2cc9e 100644 (file)
@@ -19,7 +19,8 @@ struct globals {
 #define root_major (G.root_major)
 #define root_minor (G.root_minor)
 
-#define MAX_SYSFS_DEPTH 3 /* prevent infinite loops in /sys symlinks */
+/* Prevent infinite loops in /sys symlinks */
+#define MAX_SYSFS_DEPTH 3
 
 /* We use additional 64+ bytes in make_device() */
 #define SCRATCH_SIZE 80
@@ -46,6 +47,7 @@ static void make_device(char *path, int delete)
        char *dev_maj_min = path + strlen(path);
        char *command = NULL;
        char *alias = NULL;
+       char aliaslink = aliaslink; /* for compiler */
 
        /* Force the configuration file settings exactly. */
        umask(0);
@@ -70,8 +72,14 @@ static void make_device(char *path, int delete)
        /* Determine device name, type, major and minor */
        device_name = bb_basename(path);
        /* http://kernel.org/doc/pending/hotplug.txt says that only
-        * "/sys/block/..." is for block devices. "sys/bus" etc is not! */
-       type = (strncmp(&path[5], "block/", 6) == 0 ? S_IFBLK : S_IFCHR);
+        * "/sys/block/..." is for block devices. "/sys/bus" etc is not!
+        * Since kernel 2.6.25 block devices are also in /sys/class/block. */
+       /* TODO: would it be acceptable to just use strstr(path, "/block/")? */
+       if (strncmp(&path[5], "class/block/"+6, 6) != 0
+        && strncmp(&path[5], "class/block/", 12) != 0)
+               type = S_IFCHR;
+       else
+               type = S_IFBLK;
 
        if (ENABLE_FEATURE_MDEV_CONF) {
                FILE *fp;
@@ -114,8 +122,11 @@ static void make_device(char *path, int delete)
 
                                /* If not this device, skip rest of line */
                                /* (regexec returns whole pattern as "range" 0) */
-                               if (result || off[0].rm_so || off[0].rm_eo != strlen(device_name))
+                               if (result || off[0].rm_so
+                                || ((int)off[0].rm_eo != (int)strlen(device_name))
+                               ) {
                                        goto next_line;
+                               }
                        }
 
                        /* This line matches: stop parsing the file
@@ -159,18 +170,20 @@ static void make_device(char *path, int delete)
 #if ENABLE_FEATURE_MDEV_RENAME
                        if (!next)
                                break;
-                       if (*next == '>') {
+                       if (*next == '>' || *next == '=') {
 #if ENABLE_FEATURE_MDEV_RENAME_REGEXP
                                char *s, *p;
                                unsigned i, n;
 
+                               aliaslink = *next;
                                val = next;
                                next = next_field(val);
                                /* substitute %1..9 with off[1..9], if any */
                                n = 0;
                                s = val;
-                               while (*s && *s++ == '%')
-                                       n++;
+                               while (*s)
+                                       if (*s++ == '%')
+                                               n++;
 
                                p = alias = xzalloc(strlen(val) + n * strlen(device_name));
                                s = val + 1;
@@ -189,6 +202,7 @@ static void make_device(char *path, int delete)
                                        s++;
                                }
 #else
+                               aliaslink = *next;
                                val = next;
                                next = next_field(val);
                                alias = xstrdup(val + 1);
@@ -259,10 +273,10 @@ static void make_device(char *path, int delete)
                                        }
                                }
 
-                               /* recreate device_name as a symlink to moved device node */
-                               if (rename(device_name, alias) == 0) {
+                               /* move the device, and optionally
+                                * make a symlink to moved device node */
+                               if (rename(device_name, alias) == 0 && aliaslink == '>')
                                        symlink(alias, device_name);
-                               }
 
                                free(alias);
                        }
@@ -286,10 +300,10 @@ static void make_device(char *path, int delete)
 }
 
 /* File callback for /sys/ traversal */
-static int fileAction(const char *fileName,
-                      struct stat *statbuf ATTRIBUTE_UNUSED,
-                      void *userData,
-                      int depth ATTRIBUTE_UNUSED)
+static int FAST_FUNC fileAction(const char *fileName,
+               struct stat *statbuf UNUSED_PARAM,
+               void *userData,
+               int depth UNUSED_PARAM)
 {
        size_t len = strlen(fileName) - 4; /* can't underflow */
        char *scratch = userData;
@@ -306,10 +320,10 @@ static int fileAction(const char *fileName,
 }
 
 /* Directory callback for /sys/ traversal */
-static int dirAction(const char *fileName ATTRIBUTE_UNUSED,
-                      struct stat *statbuf ATTRIBUTE_UNUSED,
-                      void *userData ATTRIBUTE_UNUSED,
-                      int depth)
+static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
+               struct stat *statbuf UNUSED_PARAM,
+               void *userData UNUSED_PARAM,
+               int depth)
 {
        return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
 }
@@ -379,6 +393,21 @@ int mdev_main(int argc, char **argv)
        char *env_path;
        RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
 
+       /* We can be called as hotplug helper */
+       /* Kernel cannot provide suitable stdio fds for us, do it ourself */
+#if 1
+       bb_sanitize_stdio();
+#else
+       /* Debug code */
+       /* Replace LOGFILE by other file or device name if you need */
+#define LOGFILE "/dev/console"
+       /* Just making sure fd 0 is not closed,
+        * we don't really intend to read from it */
+       xmove_fd(xopen("/", O_RDONLY), STDIN_FILENO);
+       xmove_fd(xopen(LOGFILE, O_WRONLY|O_APPEND), STDOUT_FILENO);
+       xmove_fd(xopen(LOGFILE, O_WRONLY|O_APPEND), STDERR_FILENO);
+#endif
+
        xchdir("/dev");
 
        if (argc == 2 && !strcmp(argv[1], "-s")) {
@@ -391,14 +420,17 @@ int mdev_main(int argc, char **argv)
                root_major = major(st.st_dev);
                root_minor = minor(st.st_dev);
 
+               /* ACTION_FOLLOWLINKS is needed since in newer kernels
+                * /sys/block/loop* (for example) are symlinks to dirs,
+                * not real directories.
+                * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs,
+                * but we can't enforce that on users) */
                recursive_action("/sys/block",
                        ACTION_RECURSE | ACTION_FOLLOWLINKS,
                        fileAction, dirAction, temp, 0);
-
                recursive_action("/sys/class",
                        ACTION_RECURSE | ACTION_FOLLOWLINKS,
                        fileAction, dirAction, temp, 0);
-
        } else {
                /* Hotplug:
                 * env ACTION=... DEVPATH=... mdev