[CRIU] [PATCH 1/2] mount: support mandatory locked mountpoints

Dmitry Safonov dsafonov at virtuozzo.com
Tue Jul 12 07:51:29 PDT 2016


To dump mountpoints in namespaces we need to read them.
It's complicated if process took exclusive lock on a file.
In case of binfmt fs will block on read call, in case of
tmpfs tar will return error:
  (00.171924) Dumping mountpoints
  (00.171930)     53: 23:/ @ ./tmp
  (00.171939) Path `/tmp' resolved to `./tmp' mountpoint
  tar: ./plop: Read error at byte 0, while reading 4 bytes: Resource temporarily unavailable
  tar: Exiting with failure status due to previous errors
  (00.174515) Error (util.c:596): exited, status=2
  (00.174529) Error (mount.c:1187): Can't dump tmpfs content

I solved this with remounting mountpoint with `nomand` option.
Processes at that moment are freezed, we can dump all interesting
stuff and remount the mountpoint back with `mand` option again.

Cc: Andrey Vagin <avagin at openvz.org>
Signed-off-by: Dmitry Safonov <dsafonov at virtuozzo.com>
---
 criu/mount.c      | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 criu/proc_parse.c |   2 +-
 2 files changed, 127 insertions(+), 5 deletions(-)

diff --git a/criu/mount.c b/criu/mount.c
index 5c2a252fcf10..c0fc1898f89c 100644
--- a/criu/mount.c
+++ b/criu/mount.c
@@ -30,6 +30,7 @@
 #include "sysfs_parse.h"
 #include "path.h"
 #include "autofs.h"
+#include "string.h"
 
 #include "images/mnt.pb-c.h"
 #include "images/binfmt-misc.pb-c.h"
@@ -88,8 +89,6 @@ static void mntinfo_add_list(struct mount_info *new)
 	}
 }
 
-static int open_mountpoint(struct mount_info *pm);
-
 static struct mount_info *mnt_build_tree(struct mount_info *list, struct mount_info *roots_mp);
 static int validate_mounts(struct mount_info *info, bool for_dump);
 
@@ -1052,6 +1051,101 @@ static char *get_clean_mnt(struct mount_info *mi, char *mnt_path_tmp, char *mnt_
 	return mnt_path;
 }
 
+static int open_mand_mount(struct mount_info *pm, char *mnt_path)
+{
+	unsigned long remount_flags =
+		(pm->flags | pm->sb_flags | MS_REMOUNT) & ~MS_MANDLOCK;
+	size_t mnt_len = strlen(mnt_path) + 1;
+	int fd;
+
+	if (mount(NULL, mnt_path, NULL, remount_flags, NULL)) {
+		pr_perror("Can't remount with `nomad` %s", mnt_path);
+		goto err_rmdir;
+	}
+
+	fd = open(mnt_path, O_RDONLY | O_DIRECTORY, 0);
+	if (fd < 0) {
+		pr_perror("Can't open directory %s: %d", mnt_path, fd);
+		goto err_umount;
+	}
+
+	pm->ns_mountpoint = xmalloc(mnt_len);
+	if (!pm->ns_mountpoint) {
+		pr_err("Failed to allocate ns_mountpoint for temp nomad mount\n");
+		goto err_close;
+	}
+	strlcpy(pm->ns_mountpoint, mnt_path, mnt_len);
+
+	return fd;
+
+err_close:
+	close_safe(&fd);
+err_umount:
+	if (umount2(mnt_path, MNT_DETACH))
+		pr_perror("Can't detach mount %s", mnt_path);
+err_rmdir:
+	if (rmdir(mnt_path))
+		pr_perror("Can't remove tmp dir %s", mnt_path);
+	return -1;
+}
+
+static int close_mountpoint(struct mount_info *pm)
+{
+	int cwd_fd;
+	int ns_old = -1;
+	unsigned long remount_flags = pm->flags | pm->sb_flags | MS_REMOUNT;
+
+	if (!(pm->sb_flags & MS_MANDLOCK))
+		return 0;
+
+	cwd_fd = open(".", O_DIRECTORY);
+	if (cwd_fd < 0) {
+		pr_perror("Unable to open cwd");
+		return -1;
+	}
+
+	if (switch_ns(pm->nsid->ns_pid, &mnt_ns_desc, &ns_old) < 0)
+		goto err_close;
+
+	if (mount(NULL, pm->ns_mountpoint, NULL, remount_flags, NULL)) {
+		pr_perror("Can't remount with `mad` %s", pm->ns_mountpoint);
+		goto err_restore_ns;
+	}
+
+	if (umount2(pm->ns_mountpoint, MNT_DETACH)) {
+		pr_perror("Can't detach mount %s", pm->ns_mountpoint);
+		goto err_restore_ns;
+	}
+
+	if (rmdir(pm->ns_mountpoint)) {
+		pr_perror("Can't remove tmp dir %s", pm->ns_mountpoint);
+		goto err_restore_ns;
+	}
+
+	if (restore_ns(ns_old, &mnt_ns_desc)) {
+		ns_old = -1;
+		goto err_restore_ns;
+	}
+	if (fchdir(cwd_fd)) {
+		pr_perror("Unable to restore cwd");
+		goto err_close;
+	}
+	xfree(pm->ns_mountpoint);
+	pm->ns_mountpoint = pm->mountpoint;
+	return 0;
+
+err_restore_ns:
+	if (ns_old >= 0)
+		 restore_ns(ns_old, &mnt_ns_desc);
+	if (fchdir(cwd_fd))
+		pr_perror("Unable to restore cwd");
+err_close:
+	close(cwd_fd);
+	xfree(pm->ns_mountpoint);
+	pm->ns_mountpoint = pm->mountpoint;
+	return -1;
+}
+
 #define MNT_UNREACHABLE INT_MIN
 static int open_mountpoint(struct mount_info *pm)
 {
@@ -1065,8 +1159,10 @@ static int open_mountpoint(struct mount_info *pm)
 	/*
 	 * If a mount doesn't have children, we can open a mount point,
 	 * otherwise we need to create a "private" copy.
+	 * For mandatory mounts, we need temporary bind-mount without
+	 * MS_MANDLOCK, so we could dump mountpoint contents.
 	 */
-	if (list_empty(&pm->children))
+	if (list_empty(&pm->children) && !(pm->sb_flags & MS_MANDLOCK))
 		return __open_mountpoint(pm, -1);
 
 	pr_info("Something is mounted on top of %s\n", pm->mountpoint);
@@ -1099,7 +1195,17 @@ static int open_mountpoint(struct mount_info *pm)
 	if (mnt_path == NULL)
 		goto out;
 
-	fd = open_detach_mount(mnt_path);
+	/*
+	 * We don't umount, rmdir on temporary mapping in case of
+	 * mandatory locking mount. This is done in close_mountpoint().
+	 * The reason -- we temporary remount it with `nomand`, so
+	 * the caller of open_mountpoint() could read/dump content
+	 * even if it's under exclusive locks.
+	 */
+	if (pm->sb_flags & MS_MANDLOCK)
+		fd = open_mand_mount(pm, mnt_path);
+	else
+		fd = open_detach_mount(mnt_path);
 	if (fd < 0)
 		goto out;
 
@@ -1111,6 +1217,8 @@ static int open_mountpoint(struct mount_info *pm)
 		pr_perror("Unable to restore cwd");
 		close(cwd_fd);
 		close(fd);
+		if (close_mountpoint(pm))
+			pr_err("Can't close mountpoint %s\n", pm->mountpoint);
 		return -1;
 	}
 	close(cwd_fd);
@@ -1123,6 +1231,8 @@ out:
 	if (fchdir(cwd_fd))
 		pr_perror("Unable to restore cwd");
 	close(cwd_fd);
+	if (close_mountpoint(pm))
+		pr_err("Can't close mountpoint %s\n", pm->mountpoint);
 	return -1;
 }
 
@@ -1201,6 +1311,8 @@ static int tmpfs_dump(struct mount_info *pm)
 	close_image(img);
 out:
 	close_safe(&fd);
+	if (close_mountpoint(pm))
+		pr_err("Can't close mountpoint %s\n", pm->mountpoint);
 	return ret;
 }
 
@@ -1376,6 +1488,8 @@ static int binfmt_misc_dump(struct mount_info *pm)
 	fdir = fdopendir(fd);
 	if (fdir == NULL) {
 		close(fd);
+		if (close_mountpoint(pm))
+			pr_err("Can't close mountpoint %s\n", pm->mountpoint);
 		return -1;
 	}
 
@@ -1401,6 +1515,8 @@ out:
 	if (img)
 		close_image(img);
 	closedir(fdir);
+	if (close_mountpoint(pm))
+		pr_err("Can't close mountpoint %s\n", pm->mountpoint);
 	return ret;
 }
 
@@ -1547,6 +1663,8 @@ static int fusectl_dump(struct mount_info *pm)
 	fdir = fdopendir(fd);
 	if (fdir == NULL) {
 		close(fd);
+		if (close_mountpoint(pm))
+			pr_err("Can't close mountpoint %s\n", pm->mountpoint);
 		return -1;
 	}
 
@@ -1574,6 +1692,8 @@ static int fusectl_dump(struct mount_info *pm)
 	ret = 0;
 out:
 	closedir(fdir);
+	if (close_mountpoint(pm))
+		pr_err("Can't close mountpoint %s\n", pm->mountpoint);
 	return ret;
 }
 
@@ -1619,6 +1739,8 @@ static int dump_empty_fs(struct mount_info *pm)
 
 	ret = is_empty_dir(fd);
 	close(fd);
+	if (close_mountpoint(pm))
+		pr_err("Can't close mountpoint %s\n", pm->mountpoint);
 	if (ret < 0) {
 		pr_err("%s isn't empty\n", pm->fstype->name);
 		return -1;
diff --git a/criu/proc_parse.c b/criu/proc_parse.c
index c25077d181b7..5583e75ecf1c 100644
--- a/criu/proc_parse.c
+++ b/criu/proc_parse.c
@@ -1182,7 +1182,7 @@ static int parse_sb_opt(char *opt, unsigned *flags, char *uopt)
 		{ "ro",		MS_RDONLY,	},
 		{ "sync",	MS_SYNC,	},
 		{ "dirsync",	MS_DIRSYNC,	},
-		{ "mad",	MS_MANDLOCK,	},
+		{ "mand",	MS_MANDLOCK,	},
 		{ },
 	};
 
-- 
2.9.0



More information about the CRIU mailing list