From 6217a84ae3aeffe8845e730dfcaade80546cf63f Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Wed, 2 Dec 2015 17:26:38 +0300 Subject: [PATCH] mnt: Carry run-time device ID in mount_info When we're restoring fsnotify watchees we need to resolve path to a handle at some mountpoint referred by @s_dev member (device ID) which is saved inside image. This ID actually may be changed at the every mount (say one restores container after machine reboot) or in case of container's migration. Thus the test for overmounting in __open_mountpoint will fail and we get an error. Lets do a trick: introduce @s_dev_rt member which is supposed to carry run-time device ID. When dumping this member simply equal to traditional @s_dev fetched from the procfs, but when restoring we fetch it from stat call once mountpoint become alive. https://jira.sw.ru/browse/PSBM-41610 v2: - predefine MOUNT_INVALID_DEV - use fetch_rt_stat instead of assigning device in restore_shared_options - copy @s_dev_rt in propagate_siblings and propagate_mount Signed-off-by: Cyrill Gorcunov Signed-off-by: Pavel Emelyanov --- include/mount.h | 3 +++ mount.c | 45 +++++++++++++++++++++++++++++++++++++++++---- proc_parse.c | 2 +- 3 files changed, 45 insertions(+), 5 deletions(-) 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;