]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: improve conversion from root= to the devname
authorKarel Zak <kzak@redhat.com>
Tue, 15 Mar 2016 13:02:19 +0000 (14:02 +0100)
committerKarel Zak <kzak@redhat.com>
Tue, 15 Mar 2016 13:07:34 +0000 (14:07 +0100)
Currently the code supports /dev/name or PARTUUID= only. We also
need to support 'maj:min' and 'hexhex' notations.

Reported-by: George Spelvin <linux@horizon.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
include/strutils.h
lib/strutils.c
libmount/src/mountP.h
libmount/src/tab_parse.c
libmount/src/utils.c

index 4bb3b2dc35b893c7f56afedeab1ffe9f972d32c1..e9f7921758d3aa4c6befb7b5d690d60fa155406c 100644 (file)
@@ -36,6 +36,7 @@ extern void strtotimeval_or_err(const char *str, struct timeval *tv,
                const char *errmesg);
 
 extern int isdigit_string(const char *str);
+extern int isxdigit_string(const char *str);
 
 extern int parse_switch(const char *arg, const char *errmesg, ...);
 
index 2d7cb59eda84d3f88e2ed2118ceabbb8fcb949b1..d58b4b468c6e3edd7439444e70cbc2c169c22d24 100644 (file)
@@ -183,6 +183,15 @@ int isdigit_string(const char *str)
        return p && p > str && !*p;
 }
 
+int isxdigit_string(const char *str)
+{
+       const char *p;
+
+       for (p = str; p && *p && isxdigit((unsigned char) *p); p++);
+
+       return p && p > str && !*p;
+}
+
 /*
  *  parse_switch(argv[i], "on", "off",  "yes", "no",  NULL);
  */
index 46919f2a0e9ab184520de9197114e585e13b51ad..2baab55de5202077a67291c821d1ea2cc53fb076 100644 (file)
@@ -101,6 +101,7 @@ extern int mnt_get_filesystems(char ***filesystems, const char *pattern);
 extern void mnt_free_filesystems(char **filesystems);
 
 extern char *mnt_get_kernel_cmdline_option(const char *name);
+extern int mnt_guess_system_root(dev_t devno, struct libmnt_cache *cache, char **path);
 extern int mnt_stat_mountpoint(const char *target, struct stat *st);
 
 /* tab.c */
index cee70081691892f97626a22aecdb53171fb412c9..23076bfc0d55bdfaf5516e28e46956d98fa702be 100644 (file)
@@ -580,19 +580,16 @@ static int kernel_fs_postparse(struct libmnt_table *tb,
         * Convert obscure /dev/root to something more usable
         */
        if (src && strcmp(src, "/dev/root") == 0) {
-               char *spec = mnt_get_kernel_cmdline_option("root=");
                char *real = NULL;
 
-               DBG(TAB, ul_debugobj(tb, "root FS: %s", spec));
-               if (spec)
-                       real = mnt_resolve_spec(spec, tb->cache);
-               if (real) {
+               rc = mnt_guess_system_root(fs->devno, tb->cache, &real);
+               if (rc < 0)
+                       return rc;
+
+               if (rc == 0 && real) {
                        DBG(TAB, ul_debugobj(tb, "canonical root FS: %s", real));
-                       rc = mnt_fs_set_source(fs, real);
-                       if (!tb->cache)
-                               free(real);
+                       rc = __mnt_fs_set_source_ptr(fs, real);
                }
-               free(spec);
        }
 
        return rc;
index 5a62879a49813e857301a06e94a16fc933d6e8bf..0fcc1d0704c4617d9d215c047625063098d10896 100644 (file)
@@ -25,6 +25,7 @@
 #include "match.h"
 #include "fileutils.h"
 #include "statfs_magic.h"
+#include "sysfs.h"
 
 int append_string(char **a, const char *b)
 {
@@ -1095,6 +1096,92 @@ char *mnt_get_kernel_cmdline_option(const char *name)
        return res;
 }
 
+/*
+ * Converts @devno to the real device name if devno major number is greater
+ * than zero, otherwise use root= kernel cmdline option to get device name.
+ *
+ * The function uses /sys to convert devno to device name.
+ *
+ * Returns: 0 = success, 1 = not found, <0 = error
+ */
+int mnt_guess_system_root(dev_t devno, struct libmnt_cache *cache, char **path)
+{
+       char buf[PATH_MAX];
+       char *dev = NULL, *spec;
+       unsigned int x, y;
+       int allocated = 0;
+
+       assert(path);
+
+       DBG(UTILS, ul_debug("guessing system root [devno %u:%u]", major(devno), minor(devno)));
+
+       /* The pseudo-fs, net-fs or btrfs devno is useless, otherwise it
+        * usually matches with the source device, let's try to use it.
+        */
+       if (major(devno) > 0) {
+               dev = sysfs_devno_to_devpath(devno, buf, sizeof(buf));
+               if (dev) {
+                       DBG(UTILS, ul_debug("  devno converted to %s", dev));
+                       goto done;
+               }
+       }
+
+       /* Let's try to use root= kernel command line option
+        */
+       spec = mnt_get_kernel_cmdline_option("root=");
+       if (!spec)
+               goto done;
+
+       /* maj:min notation */
+       if (sscanf(spec, "%u:%u", &x, &y) == 2) {
+               dev = sysfs_devno_to_devpath(makedev(x, y), buf, sizeof(buf));
+               if (dev) {
+                       DBG(UTILS, ul_debug("  root=%s converted to %s", spec, dev));
+                       goto done;
+               }
+
+       /* hexhex notation */
+       } else if (isxdigit_string(spec)) {
+               char *end = NULL;
+               uint32_t n;
+
+               errno = 0;
+               n = strtoul(spec, &end, 16);
+
+               if (errno || spec == end || (end && *end))
+                       DBG(UTILS, ul_debug("  failed to parse root='%s'", spec));
+               else {
+                       /* kernel new_decode_dev() */
+                       x = (n & 0xfff00) >> 8;
+                       y = (n & 0xff) | ((n >> 12) & 0xfff00);
+                       dev = sysfs_devno_to_devpath(makedev(x, y), buf, sizeof(buf));
+                       if (dev) {
+                               DBG(UTILS, ul_debug("  root=%s converted to %s", spec, dev));
+                               goto done;
+                       }
+               }
+
+       /* devname or PARTUUID= etc. */
+       } else {
+               DBG(UTILS, ul_debug("  converting root='%s'", spec));
+
+               dev = mnt_resolve_spec(spec, cache);
+               if (dev && !cache)
+                       allocated = 1;
+       }
+       free(spec);
+done:
+       if (dev) {
+               *path = allocated ? dev : strdup(dev);
+               if (!path)
+                       return -ENOMEM;
+               return 0;
+       }
+
+       return 1;
+}
+
+
 #ifdef TEST_PROGRAM
 static int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
 {
@@ -1207,6 +1294,33 @@ static int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[])
        return 0;
 }
 
+
+static int test_guess_root(struct libmnt_test *ts, int argc, char *argv[])
+{
+       int rc;
+       char *real;
+       dev_t devno = 0;
+
+       if (argc) {
+               unsigned int x, y;
+
+               if (sscanf(argv[1], "%u:%u", &x, &y) != 2)
+                       return -EINVAL;
+               devno = makedev(x, y);
+       }
+
+       rc = mnt_guess_system_root(devno, NULL, &real);
+       if (rc < 0)
+               return rc;
+       if (rc == 1)
+               fputs("not found\n", stdout);
+       else {
+               printf("%s\n", real);
+               free(real);
+       }
+       return 0;
+}
+
 static int test_mkdir(struct libmnt_test *ts, int argc, char *argv[])
 {
        int rc;
@@ -1247,6 +1361,7 @@ int main(int argc, char *argv[])
        { "--mountpoint",    test_mountpoint,      "<path>" },
        { "--cd-parent",     test_chdir,           "<path>" },
        { "--kernel-cmdline",test_kernel_cmdline,  "<option> | <option>=" },
+       { "--guess-root",    test_guess_root,      "[<maj:min>]" },
        { "--mkdir",         test_mkdir,           "<path>" },
        { "--statfs-type",   test_statfs_type,     "<path>" },