[lxc-devel] [PATCH v2] Add lxc-copy executable

Christian Brauner christianvanbrauner at gmail.com
Wed Nov 4 19:06:22 UTC 2015


This is a complete reimplementation of lxc-clone and lxc-start-ephemeral.
lxc-copy merges the functionalities of lxc-clone + lxc-start-ephemeral into one
executable.

(1) Cloning containers:

	(a) as copy:

		lxc-copy -n aa -N bb

	(b) as snapshot:

		lxc-copy -n aa -N bb -s

(2) Renaming containers:

	lxc-copy -n aa -N bb -R

(3) Starting ephemeral containers:

	(a) start ephemeral container as daemon with random name:

		lxc-copy -n aa -e

	(b) start ephemeral container in foreground mode with random name:

		lxc-copy -n aa -e -F

	(c) start ephemeral container with specified name as daemon:

		lxc-copy -n aa -N bb

	(d) start non-ephemeral container as daemon:

		lxc-copy -n aa -K

	(e) start ephemeral container with custom mounts (additional mounts can
            be of type {bind,aufs,overlay}) as daemon:

		lxc-copy -n aa -e -m bind=/src:/dest:ro,aufs=/src:/dest:rw,overlay=/src:/dest

(4) Other options:

	lxc-copy --help

In order to create a random containername and random upper- and workdirs for
custom mounts we use mkdtemp() to not just create the names but also directly
create the corresponding directories. This will give us more safety and make
the code considerably shorter.

Signed-off-by: Christian Brauner <christianvanbrauner at gmail.com>
---
 src/lxc/Makefile.am |   2 +
 src/lxc/arguments.h |   2 +
 src/lxc/lxc_copy.c  | 744 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 748 insertions(+)
 create mode 100644 src/lxc/lxc_copy.c

diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index ce46495..72f1e9b 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -183,6 +183,7 @@ bin_PROGRAMS = \
 	lxc-cgroup \
 	lxc-checkpoint \
 	lxc-clone \
+	lxc-copy \
 	lxc-config \
 	lxc-console \
 	lxc-create \
@@ -226,6 +227,7 @@ init_lxc_SOURCES = lxc_init.c
 lxc_monitor_SOURCES = lxc_monitor.c
 lxc_monitord_SOURCES = lxc_monitord.c
 lxc_clone_SOURCES = lxc_clone.c
+lxc_copy_SOURCES = lxc_copy.c
 lxc_start_SOURCES = lxc_start.c
 lxc_stop_SOURCES = lxc_stop.c
 lxc_top_SOURCES = lxc_top.c
diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h
index bf03dc5..c261935 100644
--- a/src/lxc/arguments.h
+++ b/src/lxc/arguments.h
@@ -100,6 +100,7 @@ struct lxc_arguments {
 
 	/* lxc-snapshot and lxc-clone */
 	enum task {
+		CLONE,
 		DESTROY,
 		LIST,
 		RESTORE,
@@ -111,6 +112,7 @@ struct lxc_arguments {
 	char *newname;
 	char *newpath;
 	char *snapname;
+	int keepdata;
 	int keepname;
 	int keepmac;
 
diff --git a/src/lxc/lxc_copy.c b/src/lxc/lxc_copy.c
new file mode 100644
index 0000000..ac1b051
--- /dev/null
+++ b/src/lxc/lxc_copy.c
@@ -0,0 +1,744 @@
+/*
+ *
+ * Copyright © 2015 Christian Brauner <christianvanbrauner at gmail.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdbool.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "attach.h"
+#include "log.h"
+#include "confile.h"
+#include "arguments.h"
+#include "lxc.h"
+#include "conf.h"
+#include "state.h"
+#include "utils.h"
+#include "bdev.h"
+
+lxc_log_define(lxc_copy_ui, lxc);
+
+enum mnttype {
+	LXC_MNT_BIND,
+	LXC_MNT_AUFS,
+	LXC_MNT_OVL,
+};
+
+struct mnts {
+	enum mnttype type;
+	char *src;
+	char *dest;
+	char *options;
+	char *upper;
+	char *workdir;
+	char *lower;
+};
+
+static unsigned int num = 0;
+static struct mnts *mnts = NULL;
+
+static int my_parser(struct lxc_arguments *args, int c, char *arg);
+
+static const struct option my_longopts[] = {
+	{ "newname", required_argument, 0, 'N'},
+	{ "newpath", required_argument, 0, 'p'},
+	{ "rename", no_argument, 0, 'R'},
+	{ "snapshot", no_argument, 0, 's'},
+	{ "foreground", no_argument, 0, 'F'},
+	{ "daemon", no_argument, 0, 'd'},
+	{ "ephemeral", no_argument, 0, 'e'},
+	{ "mount", required_argument, 0, 'm'},
+	{ "backingstore", required_argument, 0, 'B'},
+	{ "fssize", required_argument, 0, 'L'},
+	{ "keepdata", no_argument, 0, 'D'},
+	{ "keepname", no_argument, 0, 'K'},
+	{ "keepmac", no_argument, 0, 'M'},
+	LXC_COMMON_OPTIONS
+};
+
+/* mount keys */
+static char *const keys[] = {
+	[LXC_MNT_BIND] = "bind",
+	[LXC_MNT_AUFS] = "aufs",
+	[LXC_MNT_OVL] = "overlay",
+	NULL
+};
+
+static struct lxc_arguments my_args = {
+	.progname = "lxc-copy",
+	.help = "\
+--name=NAME [-P lxcpath] -N newname [-p newpath] [-B backingstorage] [-s] [-K] [-M] [-L size [unit]]\n\
+--name=NAME [-P lxcpath] [-N newname] [-p newpath] [-B backingstorage] -e [-d] [-D] [-K] [-M] [-m {bind,aufs,overlay}=/src:/dest]\n\
+--name=NAME [-P lxcpath] -N newname -R\n\
+\n\
+lxc-copy clone a container\n\
+\n\
+Options :\n\
+  -n, --name=NAME           NAME of the container\n\
+  -N, --newname=NEWNAME     NEWNAME for the restored container\n\
+  -p, --newpath=NEWPATH     NEWPATH for the container to be stored\n\
+  -R, --rename		    rename container\n\
+  -s, --snapshot	    create snapshot instead of clone\n\
+  -F, --foreground	    start with current tty attached to /dev/console\n\
+  -d, --daemon		    daemonize the container (default)\n\
+  -e, --ephemeral	    start ephemeral container\n\
+  -m, --mount	            directory to mount into container, either \n\
+			    {bind,aufs,overlay}=/src-path or {bind,aufs,overlay}=/src-path:/dst-path\n\
+  -B, --backingstorage=TYPE backingstorage type for the container\n\
+  -L, --fssize		    size of the new block device for block device containers\n\
+  -D, --keedata	            pass together with -e start a persistent snapshot \n\
+  -K, --keepname	    keep the hostname of the original container\n\
+  -M, --keepmac		    keep the MAC address of the original container\n",
+	.options = my_longopts,
+	.parser = my_parser,
+	.task = CLONE,
+	.daemonize = 1,
+};
+
+static struct mnts *add_mnt(struct mnts **mnts, unsigned int *num,
+			    enum mnttype type);
+static int mk_rand_ovl_dirs(struct mnts *mnts, struct lxc_arguments *my_args);
+static char *construct_path(char *path, bool as_prefix);
+static char *set_mnt_entry(struct mnts *m);
+static int do_clone(struct lxc_container *c, char *newname, char *newpath,
+		    int flags, char *bdevtype, uint64_t fssize, enum task task,
+		    char **args);
+static int do_clone_ephemeral(struct lxc_container *c,
+			      struct lxc_arguments *my_args, char **args,
+			      int flags);
+static int do_clone_rename(struct lxc_container *c, char *newname);
+static int do_clone_task(struct lxc_container *c, enum task task, int flags,
+			 char **args);
+static void free_mnts(struct mnts *m, unsigned int num);
+static uint64_t get_fssize(char *s);
+static int parse_mntsubopts(char *subopts, char *const *keys,
+			    char *mntparameters);
+static int parse_aufs_mnt(char *mntstring, enum mnttype type);
+static int parse_bind_mnt(char *mntstring, enum mnttype type);
+static int parse_ovl_mnt(char *mntstring, enum mnttype type);
+
+int main(int argc, char *argv[])
+{
+	struct lxc_container *c;
+	int flags = 0;
+	int ret;
+
+	if (lxc_arguments_parse(&my_args, argc, argv))
+		exit(EXIT_FAILURE);
+
+	if (!my_args.log_file)
+		my_args.log_file = "none";
+
+	if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+			 my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+		exit(EXIT_FAILURE);
+	lxc_log_options_no_override();
+
+	if (geteuid()) {
+		if (access(my_args.lxcpath[0], O_RDWR) < 0) {
+			fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]);
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if (!my_args.newname && !(my_args.task == DESTROY)) {
+		printf("Error: You must provide a NEWNAME for the clone.\n");
+		exit(EXIT_FAILURE);
+	}
+
+	if (my_args.task == SNAP || my_args.task == DESTROY)
+		flags |= LXC_CLONE_SNAPSHOT;
+	if (my_args.keepname)
+		flags |= LXC_CLONE_KEEPNAME;
+	if (my_args.keepmac)
+		flags |= LXC_CLONE_KEEPMACADDR;
+
+	if (!my_args.newpath)
+		my_args.newpath = (char *)my_args.lxcpath[0];
+
+	c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+	if (!c)
+		exit(EXIT_FAILURE);
+
+	if (!c->may_control(c)) {
+		fprintf(stderr, "Insufficent privileges to control %s\n",
+			c->name);
+		lxc_container_put(c);
+		exit(EXIT_FAILURE);
+	}
+
+	if (!c->is_defined(c)) {
+		fprintf(stderr, "Error: container %s is not defined\n",
+			c->name);
+		lxc_container_put(c);
+		exit(EXIT_FAILURE);
+	}
+
+	ret = do_clone_task(c, my_args.task, flags, &argv[optind]);
+
+	lxc_container_put(c);
+
+	if (ret == 0)
+		exit(EXIT_SUCCESS);
+	exit(EXIT_FAILURE);
+}
+
+static struct mnts *add_mnt(struct mnts **mnts, unsigned int *num, enum mnttype type)
+{
+	struct mnts *m, *n;
+
+	n = realloc(*mnts, (*num + 1) * sizeof(struct mnts));
+	if (!n)
+		return NULL;
+
+	*mnts = n;
+	m = *mnts + *num;
+	(*num)++;
+
+	*m = (struct mnts) {.type = type};
+
+	return m;
+}
+
+static int mk_rand_ovl_dirs(struct mnts *mnts, struct lxc_arguments *my_args)
+{
+	char upperdir[MAXPATHLEN];
+	char workdir[MAXPATHLEN];
+	unsigned int i;
+	int ret = 0;
+	struct mnts *m;
+
+	for (i = 0; i < num; i++) {
+		m = mnts + i;
+		if ((m->type == LXC_MNT_OVL) || (m->type == LXC_MNT_AUFS)) {
+			ret = snprintf(upperdir, MAXPATHLEN, "%s/%s/delta#XXXXXX",
+					my_args->newpath, my_args->newname);
+			if (ret < 0 || ret >= MAXPATHLEN)
+				goto err;
+			if (!mkdtemp(upperdir))
+				goto err;
+			m->upper = strdup(upperdir);
+			if (!m->upper)
+				goto err;
+		}
+
+		if (m->type == LXC_MNT_OVL) {
+			ret = snprintf(workdir, MAXPATHLEN, "%s/%s/work#XXXXXX",
+					my_args->newpath, my_args->newname);
+			if (ret < 0 || ret >= MAXPATHLEN)
+				goto err;
+			if (!mkdtemp(workdir))
+				goto err;
+			m->workdir = strdup(workdir);
+			if (!m->workdir)
+				goto err;
+		}
+	}
+
+	return 0;
+
+err:
+	free_mnts(mnts, num);
+	return -1;
+}
+
+static char *construct_path(char *path, bool as_prefix)
+{
+	char **components = NULL;
+	char *cleanpath = NULL;
+
+	components = lxc_normalize_path(path);
+	if (!components)
+		return NULL;
+
+	cleanpath = lxc_string_join("/", (const char **)components, as_prefix);
+	lxc_free_array((void **)components, free);
+
+	return cleanpath;
+}
+
+static char *set_mnt_entry(struct mnts *m)
+{
+	char *mntentry = NULL;
+	int ret = 0;
+	size_t len = 0;
+
+	if (m->type == LXC_MNT_AUFS) {
+		len = strlen("  aufs br==rw:=ro,xino=,create=dir") +
+		      2 * strlen(m->src) + strlen(m->dest) + strlen(m->upper) +
+		      strlen(m->workdir) + 1;
+
+		mntentry = malloc(len);
+		if (!mntentry)
+			goto err;
+
+		ret = snprintf(mntentry, len, "%s %s aufs br=%s=rw:%s=ro,xino=%s,create=dir",
+			       m->src, m->dest, m->upper, m->src, m->workdir);
+		if (ret < 0 || (size_t)ret >= len)
+			goto err;
+	} else if (m->type == LXC_MNT_OVL) {
+		len = strlen("  overlay lowerdir=,upperdir=,workdir=,create=dir") +
+		      2 * strlen(m->src) + strlen(m->dest) + strlen(m->upper) +
+		      strlen(m->workdir) + 1;
+
+		mntentry = malloc(len);
+		if (!mntentry)
+			goto err;
+
+		ret = snprintf(mntentry, len, "%s %s overlay lowerdir=%s,upperdir=%s,workdir=%s,create=dir",
+				m->src, m->dest, m->src, m->upper, m->workdir);
+		if (ret < 0 || (size_t)ret >= len)
+			goto err;
+	} else if (m->type == LXC_MNT_BIND) {
+		len = strlen("  none bind,optional,, 0 0") +
+		      strlen(is_dir(m->src) ? "create=dir" : "create=file") +
+		      strlen(m->src) + strlen(m->dest) + strlen(m->options) + 1;
+
+		mntentry = malloc(len);
+		if (!mntentry)
+			goto err;
+
+		ret = snprintf(mntentry, len, "%s %s none bind,optional,%s,%s 0 0",
+				m->src,	m->dest, m->options,
+				is_dir(m->src) ? "create=dir" : "create=file");
+		if (ret < 0 || (size_t)ret >= len)
+			goto err;
+	}
+
+	return mntentry;
+
+err:
+	free_mnts(mnts, num);
+	free(mntentry);
+	return NULL;
+}
+
+static int do_clone(struct lxc_container *c, char *newname, char *newpath,
+		    int flags, char *bdevtype, uint64_t fssize, enum task task,
+		    char **args)
+{
+	struct lxc_container *clone;
+
+	clone = c->clone(c, newname, newpath, flags, bdevtype, NULL, fssize,
+			 args);
+	if (!clone) {
+		fprintf(stderr, "clone failed\n");
+		return -1;
+	}
+
+	INFO("Created container %s as %s of %s\n", newname,
+	     task ? "snapshot" : "copy", c->name);
+
+	lxc_container_put(clone);
+
+	return 0;
+}
+
+static int do_clone_ephemeral(struct lxc_container *c,
+			      struct lxc_arguments *my_args, char **args,
+			      int flags)
+{
+	char randname[MAXPATHLEN];
+	unsigned int i;
+	int ret = 0;
+	bool bret = true;
+	struct lxc_container *clone;
+	struct mnts *n;
+
+	lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
+	attach_options.env_policy = LXC_ATTACH_CLEAR_ENV;
+
+	if (!my_args->newname) {
+		ret = snprintf(randname, MAXPATHLEN, "%s/%s_XXXXXX", my_args->newpath, my_args->name);
+		if (ret < 0 || ret >= MAXPATHLEN)
+			return -1;
+		if (!mkdtemp(randname))
+			return -1;
+		my_args->newname = randname + strlen(my_args->newpath) + 1;
+	}
+
+	clone = c->clone(c, my_args->newname, my_args->newpath, flags,
+			 my_args->bdevtype, NULL, my_args->fssize, args);
+	if (!clone)
+		goto err;
+
+	if (!my_args->keepdata)
+		if (!clone->set_config_item(clone, "lxc.ephemeral", "1"))
+			goto err;
+
+	/* allocate and create random upper- and workdirs for overlay mounts */
+	if (mk_rand_ovl_dirs(mnts, my_args) < 0)
+		goto err;
+
+	/* allocate and set mount entries */
+	for (i = 0; i < num; i++) {
+		char *mntentry = NULL;
+		n = mnts + i;
+		if ((mntentry = set_mnt_entry(n))) {
+			bret = clone->set_config_item(clone, "lxc.mount.entry", mntentry);
+			free(mntentry);
+		}
+		if (!mntentry || !bret)
+			goto err;
+	}
+
+	if (!clone->save_config(clone, NULL))
+		goto err;
+
+	printf("Created %s as %s of %s\n", my_args->name, "clone", my_args->newname);
+
+	if (!my_args->daemonize && my_args->argc) {
+		clone->want_daemonize(clone, true);
+		my_args->daemonize = 1;
+	} else if (!my_args->daemonize) {
+		clone->want_daemonize(clone, false);
+	}
+
+	if (!clone->start(clone, 0, NULL)) {
+		if (!(clone->lxc_conf->ephemeral == 1))
+			goto err;
+		goto put;
+	}
+
+	if (my_args->daemonize && my_args->argc) {
+		ret = clone->attach_run_wait(clone, &attach_options, my_args->argv[0], (const char *const *)my_args->argv);
+		if (ret < 0)
+			goto put;
+		clone->shutdown(clone, true);
+	}
+
+	lxc_container_put(clone);
+	return 0;
+
+err:
+	free_mnts(mnts, num);
+	clone->destroy(clone);
+put:
+	lxc_container_put(clone);
+	return -1;
+}
+
+static int do_clone_rename(struct lxc_container *c, char *newname)
+{
+	if (!c->rename(c, newname)) {
+		ERROR("Error: Renaming container %s to %s failed\n", c->name, newname);
+		return -1;
+	}
+
+	INFO("Renamed container %s to %s\n", c->name, newname);
+
+	return 0;
+}
+
+static int do_clone_task(struct lxc_container *c, enum task task, int flags,
+			 char **args)
+{
+	int ret = 0;
+
+	switch (task) {
+	case DESTROY:
+		ret = do_clone_ephemeral(c, &my_args, args, flags);
+		break;
+	case RENAME:
+		ret = do_clone_rename(c, my_args.newname);
+		break;
+	default:
+		ret = do_clone(c, my_args.newname, my_args.newpath, flags,
+			       my_args.bdevtype, my_args.fssize, my_args.task,
+			       args);
+		break;
+	}
+
+	return ret;
+}
+
+static void free_mnts(struct mnts *m, unsigned int num)
+{
+	unsigned int i;
+	struct mnts *n;
+
+	for (i = 0; i < num; i++) {
+		n = m + i;
+		free(n->src);
+		free(n->dest);
+		free(n->options);
+		free(n->upper);
+		free(n->workdir);
+	}
+	free(m);
+}
+
+/* we pass fssize in bytes */
+static uint64_t get_fssize(char *s)
+{
+	uint64_t ret;
+	char *end;
+
+	ret = strtoull(s, &end, 0);
+	if (end == s) {
+		fprintf(stderr, "Invalid blockdev size '%s', using default size\n", s);
+		return 0;
+	}
+	while (isblank(*end))
+		end++;
+	if (*end == '\0') {
+		ret *= 1024ULL * 1024ULL; // MB by default
+	} else if (*end == 'b' || *end == 'B') {
+		ret *= 1ULL;
+	} else if (*end == 'k' || *end == 'K') {
+		ret *= 1024ULL;
+	} else if (*end == 'm' || *end == 'M') {
+		ret *= 1024ULL * 1024ULL;
+	} else if (*end == 'g' || *end == 'G') {
+		ret *= 1024ULL * 1024ULL * 1024ULL;
+	} else if (*end == 't' || *end == 'T') {
+		ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL;
+	} else {
+		fprintf(stderr, "Invalid blockdev unit size '%c' in '%s', " "using default size\n", *end, s);
+		return 0;
+	}
+
+	return ret;
+}
+
+static int my_parser(struct lxc_arguments *args, int c, char *arg)
+{
+	char *subopts = NULL;
+	char *mntparameters = NULL;
+	switch (c) {
+	case 'N':
+		args->newname = arg;
+		break;
+	case 'p':
+		args->newpath = arg;
+		break;
+	case 'R':
+		args->task = RENAME;
+		break;
+	case 's':
+		args->task = SNAP;
+		break;
+	case 'F':
+		args->daemonize = 0;
+		break;
+	case 'd':
+		args->daemonize = 1;
+		break;
+	case 'e':
+		args->task = DESTROY;
+		break;
+	case 'm':
+		subopts = optarg;
+		if (parse_mntsubopts(subopts, keys, mntparameters) < 0)
+			return -1;
+		break;
+	case 'B':
+		args->bdevtype = arg;
+		break;
+	case 'L':
+		args->fssize = get_fssize(optarg);
+		break;
+	case 'D':
+		args->keepdata = 1;
+		break;
+	case 'K':
+		args->keepname = 1;
+		break;
+	case 'M':
+		args->keepmac = 1;
+		break;
+	}
+
+	return 0;
+}
+
+static int parse_aufs_mnt(char *mntstring, enum mnttype type)
+{
+	int len = 0;
+	const char *xinopath = "/dev/shm/aufs.xino";
+	char **mntarray = NULL;
+	struct mnts *m = NULL;
+
+	m = add_mnt(&mnts, &num, type);
+	if (!m)
+		goto err;
+
+	mntarray = lxc_string_split(mntstring, ':');
+	if (!mntarray)
+		goto err;
+
+	m->src = construct_path(mntarray[0], true);
+	if (!m->src)
+		goto err;
+
+	len = lxc_array_len((void **)mntarray);
+	if (len == 1) /* aufs=src */
+		m->dest = construct_path(mntarray[0], false);
+	else if (len == 2) /* aufs=src:dest */
+		m->dest = construct_path(mntarray[1], false);
+	else
+		INFO("Excess elements in mount specification");
+
+	if (!m->dest)
+		goto err;
+
+	m->workdir = strdup(xinopath);
+	if (!m->workdir)
+		goto err;
+
+	lxc_free_array((void **)mntarray, free);
+	return 0;
+
+err:
+	free_mnts(m, num);
+	lxc_free_array((void **)mntarray, free);
+	return -1;
+}
+
+static int parse_bind_mnt(char *mntstring, enum mnttype type)
+{
+	int len = 0;
+	char **mntarray = NULL;
+	struct mnts *m = NULL;
+
+	m = add_mnt(&mnts, &num, type);
+	if (!m)
+		goto err;
+
+	mntarray = lxc_string_split(mntstring, ':');
+	if (!mntarray)
+		goto err;
+
+	m->src = construct_path(mntarray[0], true);
+	if (!m->src)
+		goto err;
+
+	len = lxc_array_len((void **)mntarray);
+	if (len == 1) { /* bind=src */
+		m->dest = construct_path(mntarray[0], false);
+	} else if (len == 2) { /* bind=src:option or bind=src:dest */
+		if (strncmp(mntarray[1], "rw", strlen(mntarray[1])) == 0)
+			m->options = strdup("rw");
+
+		if (strncmp(mntarray[1], "ro", strlen(mntarray[1])) == 0)
+			m->options = strdup("ro");
+
+		if (m->options)
+			m->dest = construct_path(mntarray[0], false);
+		else
+			m->dest = construct_path(mntarray[1], false);
+	} else if (len == 3) { /* bind=src:dest:option */
+			m->dest = construct_path(mntarray[1], false);
+			m->options = strdup(mntarray[2]);
+	} else {
+		INFO("Excess elements in mount specification");
+	}
+
+	if (!m->dest)
+		goto err;
+
+	if (!m->options)
+		m->options = strdup("rw");
+
+	if (!m->options || (strncmp(m->options, "rw", strlen(m->options)) &&
+			    strncmp(m->options, "ro", strlen(m->options))))
+		goto err;
+
+	lxc_free_array((void **)mntarray, free);
+	return 0;
+
+err:
+	free_mnts(m, num);
+	lxc_free_array((void **)mntarray, free);
+	return -1;
+}
+
+static int parse_mntsubopts(char *subopts, char *const *keys, char *mntparameters)
+{
+	while (*subopts != '\0') {
+		switch (getsubopt(&subopts, keys, &mntparameters)) {
+		case LXC_MNT_BIND:
+			if (parse_bind_mnt(mntparameters, LXC_MNT_BIND) < 0)
+				return -1;
+			break;
+		case LXC_MNT_OVL:
+			if (parse_ovl_mnt(mntparameters, LXC_MNT_OVL) < 0)
+				return -1;
+			break;
+		case LXC_MNT_AUFS:
+			if (parse_aufs_mnt(mntparameters, LXC_MNT_AUFS) < 0)
+				return -1;
+			break;
+		default:
+			break;
+		}
+	}
+	return 0;
+}
+
+static int parse_ovl_mnt(char *mntstring, enum mnttype type)
+{
+	int len = 0;
+	char **mntarray = NULL;
+	struct mnts *m;
+
+	m = add_mnt(&mnts, &num, type);
+	if (!m)
+		goto err;
+
+	mntarray = lxc_string_split(mntstring, ':');
+	if (!mntarray)
+		goto err;
+
+	m->src = construct_path(mntarray[0], true);
+	if (!m->src)
+		goto err;
+
+	len = lxc_array_len((void **)mntarray);
+	if (len == 1) /* overlay=src */
+		m->dest = construct_path(mntarray[0], false);
+	else if (len == 2) /* overlay=src:dest */
+		m->dest = construct_path(mntarray[1], false);
+	else
+		INFO("Excess elements in mount specification");
+
+	if (!m->dest)
+		goto err;
+
+	lxc_free_array((void **)mntarray, free);
+	return 0;
+
+err:
+	free_mnts(m, num);
+	lxc_free_array((void **)mntarray, free);
+	return -1;
+}
+
-- 
2.6.2



More information about the lxc-devel mailing list