This directory was fallout from the great feature freeze of 2003, which led
[oweals/busybox.git] / util-linux / mdev.c
index f6d4d8df00c103ef4ec7af8bf699b75a9f6352c6..73a82314c3cf3ef572ffcdf7b697898c83c2c06f 100644 (file)
@@ -16,6 +16,7 @@
 #include <string.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
+#include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <stdlib.h>
 
 #include <busybox.h>
 
+struct mdev_globals
+{
+       int root_major, root_minor;
+} mdev_globals;
+
+#define bbg mdev_globals
+
 /* mknod in /dev based on a path like "/sys/block/hda/hda1" */
 static void make_device(char *path)
 {
@@ -35,23 +43,25 @@ static void make_device(char *path)
        int mode = 0660;
        uid_t uid = 0;
        gid_t gid = 0;
+       char *temp = path + strlen(path);
 
-       RESERVE_CONFIG_BUFFER(temp, PATH_MAX);
+       /* Try to read major/minor string.  Note that the kernel puts \n after
+        * the data, so we don't need to worry about null terminating the string
+        * because sscanf() will stop at the first nondigit, which \n is.  We
+        * also depend on path having writeable space after it. */
 
-       /* Try to read major/minor string */
-
-       snprintf(temp, PATH_MAX, "%s/dev", path);
-       fd = open(temp, O_RDONLY);
-       len = read(fd, temp, PATH_MAX-1);
+       strcat(path, "/dev");
+       fd = open(path, O_RDONLY);
+       len = read(fd, temp + 1, 64);
+       *temp++ = 0;
        close(fd);
-       if (len < 1) goto end;
+       if (len < 1) return;
 
        /* Determine device name, type, major and minor */
 
        device_name = strrchr(path, '/') + 1;
-       type = strncmp(path+5, "block/", 6) ? S_IFCHR : S_IFBLK;
-       if (sscanf(temp, "%d:%d", &major, &minor) != 2)
-               goto end;
+       type = path[5]=='c' ? S_IFCHR : S_IFBLK;
+       if (sscanf(temp, "%d:%d", &major, &minor) != 2) return;
 
        /* If we have a config file, look up permissions for this device */
 
@@ -164,15 +174,14 @@ found_device:
                }
        }
 
-       sprintf(temp, "%s/%s", DEV_PATH, device_name);
        umask(0);
-       if (mknod(temp, mode | type, makedev(major, minor)) && errno != EEXIST)
-               bb_perror_msg_and_die("mknod %s failed", temp);
-
-       if (ENABLE_FEATURE_MDEV_CONF) chown(temp,uid,gid);
+       if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST)
+               bb_perror_msg_and_die("mknod %s failed", device_name);
 
-end:
-       RELEASE_CONFIG_BUFFER(temp);
+       if (major==bbg.root_major && minor==bbg.root_minor)
+               symlink(device_name, "root");
+       
+       if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid);
 }
 
 /* Recursive search of /sys/block or /sys/class.  path must be a writeable
@@ -188,17 +197,18 @@ static void find_dev(char *path)
                return;
 
        while ((entry = readdir(dir)) != NULL) {
+               struct stat st;
 
                /* Skip "." and ".." (also skips hidden files, which is ok) */
 
                if (entry->d_name[0] == '.')
                        continue;
 
-               if (entry->d_type == DT_DIR) {
-                       snprintf(path+len, PATH_MAX-len, "/%s", entry->d_name);
-                       find_dev(path);
-                       path[len] = 0;
-               }
+               // uClibc doesn't fill out entry->d_type reliably. so we use lstat().
+
+               snprintf(path+len, PATH_MAX-len, "/%s", entry->d_name);
+               if (!lstat(path, &st) && S_ISDIR(st.st_mode)) find_dev(path);
+               path[len] = 0;
 
                /* If there's a dev entry, mknod it */
 
@@ -214,9 +224,16 @@ int mdev_main(int argc, char *argv[])
        char *env_path;
        RESERVE_CONFIG_BUFFER(temp,PATH_MAX);
 
+       bb_xchdir(DEV_PATH);
+
        /* Scan */
 
        if (argc == 2 && !strcmp(argv[1],"-s")) {
+               struct stat st;
+
+               stat("/", &st);  // If this fails, we have bigger problems.
+               bbg.root_major=major(st.st_dev);
+               bbg.root_minor=minor(st.st_dev);
                strcpy(temp,"/sys/block");
                find_dev(temp);
                strcpy(temp,"/sys/class");
@@ -234,8 +251,7 @@ int mdev_main(int argc, char *argv[])
                        sprintf(temp, "/sys%s", env_path);
                        make_device(temp);
                } else if (!strcmp(action, "remove")) {
-                       sprintf(temp, "%s/%s", DEV_PATH, strrchr(env_path, '/') + 1);
-                       unlink(temp);
+                       unlink(strrchr(env_path, '/') + 1);
                }
        }