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>
This commit is contained in:
Karel Zak 2016-03-15 14:02:19 +01:00
parent 12f5cbe159
commit 40f00b4f8e
5 changed files with 132 additions and 9 deletions

View file

@ -36,6 +36,7 @@ extern void strtotimeval_or_err(const char *str, struct timeval *tv,
const char *errmesg); const char *errmesg);
extern int isdigit_string(const char *str); extern int isdigit_string(const char *str);
extern int isxdigit_string(const char *str);
extern int parse_switch(const char *arg, const char *errmesg, ...); extern int parse_switch(const char *arg, const char *errmesg, ...);

View file

@ -183,6 +183,15 @@ int isdigit_string(const char *str)
return p && p > str && !*p; 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); * parse_switch(argv[i], "on", "off", "yes", "no", NULL);
*/ */

View file

@ -101,6 +101,7 @@ extern int mnt_get_filesystems(char ***filesystems, const char *pattern);
extern void mnt_free_filesystems(char **filesystems); extern void mnt_free_filesystems(char **filesystems);
extern char *mnt_get_kernel_cmdline_option(const char *name); 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); extern int mnt_stat_mountpoint(const char *target, struct stat *st);
/* tab.c */ /* tab.c */

View file

@ -580,19 +580,16 @@ static int kernel_fs_postparse(struct libmnt_table *tb,
* Convert obscure /dev/root to something more usable * Convert obscure /dev/root to something more usable
*/ */
if (src && strcmp(src, "/dev/root") == 0) { if (src && strcmp(src, "/dev/root") == 0) {
char *spec = mnt_get_kernel_cmdline_option("root=");
char *real = NULL; char *real = NULL;
DBG(TAB, ul_debugobj(tb, "root FS: %s", spec)); rc = mnt_guess_system_root(fs->devno, tb->cache, &real);
if (spec) if (rc < 0)
real = mnt_resolve_spec(spec, tb->cache); return rc;
if (real) {
if (rc == 0 && real) {
DBG(TAB, ul_debugobj(tb, "canonical root FS: %s", real)); DBG(TAB, ul_debugobj(tb, "canonical root FS: %s", real));
rc = mnt_fs_set_source(fs, real); rc = __mnt_fs_set_source_ptr(fs, real);
if (!tb->cache)
free(real);
} }
free(spec);
} }
return rc; return rc;

View file

@ -25,6 +25,7 @@
#include "match.h" #include "match.h"
#include "fileutils.h" #include "fileutils.h"
#include "statfs_magic.h" #include "statfs_magic.h"
#include "sysfs.h"
int append_string(char **a, const char *b) int append_string(char **a, const char *b)
{ {
@ -1095,6 +1096,92 @@ char *mnt_get_kernel_cmdline_option(const char *name)
return res; 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 #ifdef TEST_PROGRAM
static int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[]) 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; 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[]) static int test_mkdir(struct libmnt_test *ts, int argc, char *argv[])
{ {
int rc; int rc;
@ -1247,6 +1361,7 @@ int main(int argc, char *argv[])
{ "--mountpoint", test_mountpoint, "<path>" }, { "--mountpoint", test_mountpoint, "<path>" },
{ "--cd-parent", test_chdir, "<path>" }, { "--cd-parent", test_chdir, "<path>" },
{ "--kernel-cmdline",test_kernel_cmdline, "<option> | <option>=" }, { "--kernel-cmdline",test_kernel_cmdline, "<option> | <option>=" },
{ "--guess-root", test_guess_root, "[<maj:min>]" },
{ "--mkdir", test_mkdir, "<path>" }, { "--mkdir", test_mkdir, "<path>" },
{ "--statfs-type", test_statfs_type, "<path>" }, { "--statfs-type", test_statfs_type, "<path>" },