diff --git a/include/mount.h b/include/mount.h index 12281370e..b3bbdcea5 100644 --- a/include/mount.h +++ b/include/mount.h @@ -29,10 +29,13 @@ struct ext_mount { char *val; }; +#define MOUNT_INVALID_DEV (0) + struct mount_info { int mnt_id; int parent_mnt_id; unsigned int s_dev; + unsigned int s_dev_rt; char *root; /* * During dump mountpoint contains path with dot at the diff --git a/mount.c b/mount.c index 78cf0bba4..61459d5b5 100644 --- a/mount.c +++ b/mount.c @@ -1031,10 +1031,23 @@ int __open_mountpoint(struct mount_info *pm, int mnt_fd) goto err; } + if (pm->s_dev_rt == MOUNT_INVALID_DEV) { + pr_err("Resolving over unvalid device for %#x %s %s\n", + pm->s_dev, pm->fstype->name, pm->ns_mountpoint); + goto err; + } + dev = phys_stat_resolve_dev(pm->nsid, st.st_dev, pm->ns_mountpoint + 1); - if (dev != pm->s_dev) { - pr_err("The file system %#x (%#x) %s %s is inaccessible\n", - pm->s_dev, (int)dev, pm->fstype->name, pm->ns_mountpoint); + /* + * Always check for @s_dev_rt here, because the @s_dev + * from the image (in case of restore) has all rights + * to not match the device (say it's migrated and kernel + * allocates new device ID). + */ + if (dev != pm->s_dev_rt) { + pr_err("The file system %#x %#x (%#x) %s %s is inaccessible\n", + pm->s_dev, pm->s_dev_rt, (int)dev, + pm->fstype->name, pm->ns_mountpoint); goto err; } @@ -2102,6 +2115,7 @@ static int propagate_siblings(struct mount_info *mi) continue; pr_debug("\t\tBind share %s\n", t->mountpoint); t->bind = mi; + t->s_dev_rt = mi->s_dev_rt; } list_for_each_entry(t, &mi->mnt_slave_list, mnt_slave) { @@ -2109,6 +2123,7 @@ static int propagate_siblings(struct mount_info *mi) continue; pr_debug("\t\tBind slave %s\n", t->mountpoint); t->bind = mi; + t->s_dev_rt = mi->s_dev_rt; } return 0; @@ -2154,12 +2169,26 @@ skip_parent: if (t->master_id) continue; t->bind = mi; + t->s_dev_rt = mi->s_dev_rt; } } return 0; } +static int fetch_rt_stat(struct mount_info *m, const char *where) +{ + struct stat st; + + if (stat(where, &st)) { + pr_perror("Can't stat on %s\n", where); + return -1; + } + + m->s_dev_rt = MKKDEV(major(st.st_dev), minor(st.st_dev)); + return 0; +} + /* * Here are a set of flags which we know how to handle for the one mount call. * All of them except MS_RDONLY are set only as mnt flags. @@ -2383,7 +2412,7 @@ static int do_mount_root(struct mount_info *mi) mi->shared_id, mi->master_id)) return -1; - return 0; + return fetch_rt_stat(mi, mi->mountpoint); } static int do_mount_one(struct mount_info *mi) @@ -2409,6 +2438,9 @@ static int do_mount_one(struct mount_info *mi) else ret = do_bind_mount(mi); + if (ret == 0 && fetch_rt_stat(mi, mi->mountpoint)) + return -1; + if (ret == 0 && propagate_mount(mi)) return -1; @@ -2510,6 +2542,11 @@ struct mount_info *mnt_entry_alloc() { struct mount_info *new; + /* + * We rely on xzalloc here for MOUNT_INVALID_DEV. + */ + BUILD_BUG_ON(MOUNT_INVALID_DEV); + new = xzalloc(sizeof(struct mount_info)); if (new) { INIT_LIST_HEAD(&new->children); diff --git a/proc_parse.c b/proc_parse.c index c479b0fa9..b007c56e5 100644 --- a/proc_parse.c +++ b/proc_parse.c @@ -1093,7 +1093,7 @@ static int parse_mountinfo_ent(char *str, struct mount_info *new, char **fsname) if (!new->mountpoint) goto err; - new->s_dev = MKKDEV(kmaj, kmin); + new->s_dev = new->s_dev_rt = MKKDEV(kmaj, kmin); new->flags = 0; if (parse_mnt_flags(opt, &new->flags)) goto err;