>From fadafa4348e00cd0391a8facb939510c196fc46f Mon Sep 17 00:00:00 2001
From: Tycho Andersen <tycho.andersen@canonical.com>
Date: Thu, 26 Mar 2015 15:36:53 -0600
Subject: [PATCH 1/2] c/r: tell CRIU about all external mounts in the config

Signed-off-by: Tycho Andersen <tycho.andersen@canonical.com>
---
 src/lxc/conf.c         | 18 ++++++++++---
 src/lxc/conf.h         |  2 ++
 src/lxc/list.h         | 11 ++++++++
 src/lxc/lxccontainer.c | 73 +++++++++++++++++++++++++++++++++++++++++++-------
 4 files changed, 91 insertions(+), 13 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 4e31a6d..f1e89d8 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -2051,18 +2051,16 @@ static int setup_mount(const struct lxc_rootfs *rootfs, const char *fstab,
 	return ret;
 }
 
-static int setup_mount_entries(const struct lxc_rootfs *rootfs, struct lxc_list *mount,
-	const char *lxc_name)
+FILE *write_mount_file(struct lxc_list *mount)
 {
 	FILE *file;
 	struct lxc_list *iterator;
 	char *mount_entry;
-	int ret;
 
 	file = tmpfile();
 	if (!file) {
 		ERROR("tmpfile error: %m");
-		return -1;
+		return NULL;
 	}
 
 	lxc_list_for_each(iterator, mount) {
@@ -2071,6 +2069,18 @@ static int setup_mount_entries(const struct lxc_rootfs *rootfs, struct lxc_list
 	}
 
 	rewind(file);
+	return file;
+}
+
+static int setup_mount_entries(const struct lxc_rootfs *rootfs, struct lxc_list *mount,
+	const char *lxc_name)
+{
+	FILE *file;
+	int ret;
+
+	file = write_mount_file(mount);
+	if (!file)
+		return -1;
 
 	ret = mount_file_entries(rootfs, file, lxc_name);
 
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index ae475ed..0c0475e 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -25,6 +25,7 @@
 
 #include "config.h"
 
+#include <stdio.h>
 #include <netinet/in.h>
 #include <net/if.h>
 #include <sys/param.h>
@@ -429,4 +430,5 @@ extern int parse_mntopts(const char *mntopts, unsigned long *mntflags,
 extern void tmp_proc_unmount(struct lxc_conf *lxc_conf);
 void remount_all_slave(void);
 extern void suggest_default_idmap(void);
+FILE *write_mount_file(struct lxc_list *mount);
 #endif
diff --git a/src/lxc/list.h b/src/lxc/list.h
index 0882da0..f16af54 100644
--- a/src/lxc/list.h
+++ b/src/lxc/list.h
@@ -99,4 +99,15 @@ static inline void lxc_list_del(struct lxc_list *list)
 	prev->next = next;
 }
 
+static inline int lxc_list_len(struct lxc_list *list)
+{
+	 int i = 0;
+	 struct lxc_list *iter;
+	 lxc_list_for_each(iter, list) {
+		i++;
+	 }
+
+	 return i;
+}
+
 #endif
diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
index 6ac829a..d488862 100644
--- a/src/lxc/lxccontainer.c
+++ b/src/lxc/lxccontainer.c
@@ -35,6 +35,8 @@
 #include <libgen.h>
 #include <stdint.h>
 #include <grp.h>
+#include <stdio.h>
+#include <mntent.h>
 #include <sys/syscall.h>
 
 #include <lxc/lxccontainer.h>
@@ -3701,11 +3703,15 @@ struct criu_opts {
 
 static void exec_criu(struct criu_opts *opts)
 {
-	char **argv, log[PATH_MAX], buf[257];
+	char **argv, log[PATH_MAX];
 	int static_args = 14, argc = 0, i, ret;
 	int netnr = 0;
 	struct lxc_list *it;
 
+	struct mntent mntent;
+	char buf[4096];
+	FILE *mnts = NULL;
+
 	/* The command line always looks like:
 	 * criu $(action) --tcp-established --file-locks --link-remap --force-irmap \
 	 * --manage-cgroups action-script foo.sh -D $(directory) \
@@ -3780,6 +3786,27 @@ static void exec_criu(struct criu_opts *opts)
 	if (opts->verbose)
 		DECLARE_ARG("-vvvvvv");
 
+	/*
+	 * Note: this macro is not intended to be called unless argc is equal
+	 * to the length of the array; there is nothing that keeps track of the
+	 * length of the array besides the location in the code that this is
+	 * called. (Yes this is bad, and we should fix it.)
+	 */
+#define RESIZE_ARGS(additional) 						\
+	do {									\
+		void *m;							\
+		if (additional < 0) {						\
+			ERROR("resizing by negative amount");			\
+			goto err;						\
+		} else if (additional == 0)					\
+			continue;						\
+										\
+		m = realloc(argv, (argc + additional + 1) * sizeof(*argv));	\
+		if (!m)								\
+			goto err;						\
+		argv = m;							\
+	} while (0)
+
 	if (strcmp(opts->action, "dump") == 0) {
 		char pid[32];
 
@@ -3811,9 +3838,10 @@ static void exec_criu(struct criu_opts *opts)
 		DECLARE_ARG("--cgroup-root");
 		DECLARE_ARG(opts->cgroup_path);
 
+		RESIZE_ARGS(lxc_list_len(&opts->c->lxc_conf->network) * 2);
+
 		lxc_list_for_each(it, &opts->c->lxc_conf->network) {
 			char eth[128], *veth;
-			void *m;
 			struct lxc_netdev *n = it->elem;
 
 			if (n->name) {
@@ -3829,18 +3857,42 @@ static void exec_criu(struct criu_opts *opts)
 			if (ret < 0 || ret >= sizeof(buf))
 				goto err;
 
-			/* final NULL and --veth-pair eth0=vethASDF */
-			m = realloc(argv, (argc + 1 + 2) * sizeof(*argv));
-			if (!m)
-				goto err;
-			argv = m;
-
 			DECLARE_ARG("--veth-pair");
 			DECLARE_ARG(buf);
-			argv[argc] = NULL;
+		}
+	}
 
+	// CRIU wants to know about any external bind mounts the
+	// container has.
+	mnts = write_mount_file(&opts->c->lxc_conf->mount_list);
+	if (!mnts)
+		goto err;
+
+	RESIZE_ARGS(lxc_list_len(&opts->c->lxc_conf->mount_list) * 2);
+
+	while (getmntent_r(mnts, &mntent, buf, sizeof(buf))) {
+		char arg[2048], *key, *val;
+		int ret;
+
+		if (strcmp(opts->action, "dump") == 0) {
+			key = mntent.mnt_fsname;
+			val = mntent.mnt_dir;
+		} else {
+			key = mntent.mnt_dir;
+			val = mntent.mnt_fsname;
 		}
+
+		ret = snprintf(arg, sizeof(arg), "%s:%s", key, val);
+		if (ret < 0 || ret >= sizeof(arg)) {
+			goto err;
+		}
+
+		DECLARE_ARG("--ext-mount-map");
+		DECLARE_ARG(arg);
 	}
+	fclose(mnts);
+
+	argv[argc] = NULL;
 
 	netnr = 0;
 	lxc_list_for_each(it, &opts->c->lxc_conf->network) {
@@ -3876,8 +3928,11 @@ static void exec_criu(struct criu_opts *opts)
 	}
 
 #undef DECLARE_ARG
+#undef RESIZE_ARGS
 	execv(argv[0], argv);
 err:
+	if (mnts)
+		fclose(mnts);
 	for (i = 0; argv[i]; i++)
 		free(argv[i]);
 	free(argv);
-- 
2.1.0

