From: Karel Zak Date: Tue, 15 Mar 2016 13:02:19 +0000 (+0100) Subject: libmount: improve conversion from root= to the devname X-Git-Tag: v2.28-rc2~53 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=40f00b4f8e50c201647e0adb1d44bf01efbed09f;p=thirdparty%2Futil-linux.git libmount: improve conversion from root= to the devname Currently the code supports /dev/name or PARTUUID= only. We also need to support 'maj:min' and 'hexhex' notations. Reported-by: George Spelvin Signed-off-by: Karel Zak --- diff --git a/include/strutils.h b/include/strutils.h index 4bb3b2dc35..e9f7921758 100644 --- a/include/strutils.h +++ b/include/strutils.h @@ -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, ...); diff --git a/lib/strutils.c b/lib/strutils.c index 2d7cb59eda..d58b4b468c 100644 --- a/lib/strutils.c +++ b/lib/strutils.c @@ -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); */ diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 46919f2a0e..2baab55de5 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -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 */ diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c index cee7008169..23076bfc0d 100644 --- a/libmount/src/tab_parse.c +++ b/libmount/src/tab_parse.c @@ -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; diff --git a/libmount/src/utils.c b/libmount/src/utils.c index 5a62879a49..0fcc1d0704 100644 --- a/libmount/src/utils.c +++ b/libmount/src/utils.c @@ -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, "" }, { "--cd-parent", test_chdir, "" }, { "--kernel-cmdline",test_kernel_cmdline, "