[v3,33/33] ns: Allow nested user namespaces

Submitted by Kirill Tkhai on Feb. 16, 2017, 12:11 p.m.

Details

Message ID 148724708496.22444.5369790936403429554.stgit@localhost.localdomain
State New
Series "Nested user namespaces support"
Headers show

Commit Message

Kirill Tkhai Feb. 16, 2017, 12:11 p.m.
Everything is prepared for nested user namespaces support.
The only thing, we should do more, is to enter to dumped
user namespace's parent before the dump.
We use CLONE_VM for child tasks, so they may populate
user_ns maps in parent memory without any tricks.

v3: Check for WIFEXITED(). Fixed stack size.

Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com>
---
 criu/include/namespaces.h |    2 +
 criu/namespaces.c         |   64 ++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 61 insertions(+), 5 deletions(-)

Patch hide | download patch | download mbox

diff --git a/criu/include/namespaces.h b/criu/include/namespaces.h
index 8c194181b..660da0471 100644
--- a/criu/include/namespaces.h
+++ b/criu/include/namespaces.h
@@ -39,7 +39,7 @@ 
 #define CLONE_ALLNS	(CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWCGROUP)
 
 /* Nested namespaces are supported only for these types */
-#define CLONE_SUBNS	(CLONE_NEWNS | CLONE_NEWNET)
+#define CLONE_SUBNS	(CLONE_NEWNS | CLONE_NEWNET | CLONE_NEWUSER)
 
 #define EXTRA_SIZE	20
 
diff --git a/criu/namespaces.c b/criu/namespaces.c
index 311570707..fef5a3d87 100644
--- a/criu/namespaces.c
+++ b/criu/namespaces.c
@@ -32,6 +32,7 @@ 
 #include "images/ns.pb-c.h"
 #include "common/scm.h"
 #include "fdstore.h"
+#include "proc_parse.h"
 
 static struct ns_desc *ns_desc_array[] = {
 	&net_ns_desc,
@@ -975,11 +976,29 @@  static int parse_id_map(pid_t pid, char *name, UidGidExtent ***pb_exts)
 	return -1;
 }
 
-static int dump_user_ns(struct ns_id *ns);
+static int __dump_user_ns(struct ns_id *ns);
+
+static int dump_user_ns(void *arg)
+{
+	struct ns_id *ns = arg;
+
+	if (switch_ns(ns->parent->ns_pid, &user_ns_desc, NULL) < 0) {
+		pr_err("Can't enter user namespace\n");
+		return -1;
+	}
+
+	return __dump_user_ns(ns);
+}
 
 int collect_user_ns(struct ns_id *ns, void *oarg)
 {
+	int status, stack_size;
+	struct ns_id *p_ns;
+	pid_t pid = -1;
 	UsernsEntry *e;
+	char *stack;
+
+	p_ns = ns->parent;
 
 	e = xmalloc(sizeof(*e));
 	if (!e)
@@ -995,8 +1014,42 @@  int collect_user_ns(struct ns_id *ns, void *oarg)
 	 * mappings, which are used for convirting local id-s to
 	 * userns id-s (userns_uid(), userns_gid())
 	 */
-	if (dump_user_ns(ns))
-		return -1;
+	if (p_ns) {
+		/*
+		 * Currently, we are in NS_CRIU. To dump a NS_OTHER ns,
+		 * we need to enter its parent ns. As entered to user_ns
+		 * task has no a way back, we create a child for that.
+		 * NS_ROOT is dumped w/o clone(), it's xids maps is relatively
+		 * to NS_CRIU. We use CLONE_VM to make child share our memory,
+		 * and to allow us see allocated maps, he do. Child's open_proc()
+		 * may do changes about CRIU's internal files states in memory,
+		 * so pass CLONE_FILES to reflect that.
+		 */
+		stack_size = 2 * 1024 * 1024;
+		stack = mmap(NULL, stack_size, PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+		if (stack == MAP_FAILED) {
+			pr_perror("Can't allocate stack");
+			return -1;
+		}
+		pid = clone(dump_user_ns, stack + stack_size, CLONE_VM | CLONE_FILES | SIGCHLD, ns);
+		if (pid == -1) {
+			pr_perror("Can't clone");
+			return -1;
+		}
+		if (waitpid(pid, &status, 0) != pid) {
+			pr_perror("Unable to wait the %d process", pid);
+			return -1;
+		}
+		if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+			pr_err("Can't dump nested user_ns: %x\n", status);
+			return -1;
+		}
+		munmap(stack, stack_size);
+		return 0;
+	} else {
+		if (__dump_user_ns(ns))
+			return -1;
+	}
 
 	return 0;
 }
@@ -1035,6 +1088,9 @@  static int check_user_ns(struct ns_id *ns)
 	int status;
 	pid_t chld;
 
+	if (ns->type != NS_ROOT)
+		return 0;
+
 	chld = fork();
 	if (chld == -1) {
 		pr_perror("Unable to fork a process");
@@ -1130,7 +1186,7 @@  static int check_user_ns(struct ns_id *ns)
 	return 0;
 }
 
-static int dump_user_ns(struct ns_id *ns)
+static int __dump_user_ns(struct ns_id *ns)
 {
 	int ret, exit_code = -1;
 	pid_t pid = ns->ns_pid;