diff options
author | Karel Zak | 2016-03-15 14:02:19 +0100 |
---|---|---|
committer | Karel Zak | 2016-03-15 14:07:34 +0100 |
commit | 40f00b4f8e50c201647e0adb1d44bf01efbed09f (patch) | |
tree | dba875d6bf9743892a0a6b5ec4faec3337307614 /libmount/src | |
parent | swapon: remove unnecessary assert (diff) | |
download | kernel-qcow2-util-linux-40f00b4f8e50c201647e0adb1d44bf01efbed09f.tar.gz kernel-qcow2-util-linux-40f00b4f8e50c201647e0adb1d44bf01efbed09f.tar.xz kernel-qcow2-util-linux-40f00b4f8e50c201647e0adb1d44bf01efbed09f.zip |
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 <linux@horizon.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'libmount/src')
-rw-r--r-- | libmount/src/mountP.h | 1 | ||||
-rw-r--r-- | libmount/src/tab_parse.c | 15 | ||||
-rw-r--r-- | libmount/src/utils.c | 115 |
3 files changed, 122 insertions, 9 deletions
diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 46919f2a0..2baab55de 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 cee700816..23076bfc0 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 5a62879a4..0fcc1d070 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, "<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>" }, |