extlinux: use sysfs to find the device node if need be
authorH. Peter Anvin <hpa@zytor.com>
Wed, 8 Feb 2012 18:15:50 +0000 (10:15 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Wed, 8 Feb 2012 18:18:36 +0000 (10:18 -0800)
If neither /proc/mounts nor /etc/mtab contains a functional pointer to
the device node for the installer, try to see if we can find the
device node by looking for a symlink in /sys/dev/block.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
extlinux/main.c

index af87da2..5da89e2 100644 (file)
@@ -873,6 +873,54 @@ static const char *find_device(const char *mtab_file, dev_t dev)
 }
 #endif
 
+/*
+ * On newer Linux kernels we can use sysfs to get a backwards mapping
+ * from device names to standard filenames
+ */
+static const char *find_device_sysfs(dev_t dev)
+{
+    char sysname[64];
+    char linkname[PATH_MAX];
+    ssize_t llen;
+    char *p, *q;
+    char *buf = NULL;
+    struct stat st;
+
+    snprintf(sysname, sizeof sysname, "/sys/dev/block/%u:%u",
+            major(dev), minor(dev));
+
+    llen = readlink(sysname, linkname, sizeof linkname);
+    if (llen < 0 || llen >= sizeof linkname)
+       goto err;
+
+    linkname[llen] = '\0';
+
+    p = strrchr(linkname, '/');
+    p = p ? p+1 : linkname;    /* Leave basename */
+
+    buf = q = malloc(strlen(p) + 6);
+    if (!buf)
+       goto err;
+
+    memcpy(q, "/dev/", 5);
+    q += 5;
+
+    while (*p) {
+       *q++ = (*p == '!') ? '/' : *p;
+       p++;
+    }
+
+    *q = '\0';
+
+    if (!stat(buf, &st) && st.st_dev == dev)
+       return buf;             /* Found it! */
+
+err:
+    if (buf)
+       free(buf);
+    return NULL;
+}
+
 static const char *get_devname(const char *path)
 {
     const char *devname = NULL;
@@ -887,21 +935,26 @@ static const char *get_devname(const char *path)
        fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
        return devname;
     }
+
 #ifdef __KLIBC__
 
-    /* klibc doesn't have getmntent and friends; instead, just create
-       a new device with the appropriate device type */
-    snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
-            major(st.st_dev), minor(st.st_dev));
+    devname = find_device_sysfs(st.st_dev);
 
-    if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
-       fprintf(stderr, "%s: cannot create device %s\n", program, devname);
-       return devname;
+    if (!devname) {
+       /* klibc doesn't have getmntent and friends; instead, just create
+          a new device with the appropriate device type */
+       snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
+                major(st.st_dev), minor(st.st_dev));
+
+       if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
+           fprintf(stderr, "%s: cannot create device %s\n", program, devname);
+           return devname;
+       }
+       
+       atexit(device_cleanup); /* unlink the device node on exit */
+       devname = devname_buf;
     }
 
-    atexit(device_cleanup);    /* unlink the device node on exit */
-    devname = devname_buf;
-
 #else
 
     devname = find_device("/proc/mounts", st.st_dev);
@@ -910,11 +963,14 @@ static const char *get_devname(const char *path)
         devname = find_device("/etc/mtab", st.st_dev);
     }
     if (!devname) {
+       devname = find_device_sysfs(st.st_dev);
+
        fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
        return devname;
     }
 
     fprintf(stderr, "%s is device %s\n", path, devname);
+
 #endif
     return devname;
 }