summaryrefslogtreecommitdiffstats
path: root/libmount/src
diff options
context:
space:
mode:
authorKarel Zak2016-03-15 14:02:19 +0100
committerKarel Zak2016-03-15 14:07:34 +0100
commit40f00b4f8e50c201647e0adb1d44bf01efbed09f (patch)
treedba875d6bf9743892a0a6b5ec4faec3337307614 /libmount/src
parentswapon: remove unnecessary assert (diff)
downloadkernel-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.h1
-rw-r--r--libmount/src/tab_parse.c15
-rw-r--r--libmount/src/utils.c115
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>" },