From 5229766b8f1ef3802ae4ae3d44da693b25155113 Mon Sep 17 00:00:00 2001
From: LiFeng <lifeng68@huawei.com>
Date: Fri, 11 Jan 2019 01:51:25 -0500
Subject: [PATCH 003/131] confile: add lxc.isulad.populate.device interface

Signed-off-by: LiFeng <lifeng68@huawei.com>
---
 src/lxc/conf.c    | 125 +++++++++++++++++++++++++++++++++++++++++++---
 src/lxc/conf.h    |  28 ++++++++++-
 src/lxc/confile.c | 124 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 269 insertions(+), 8 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index f20d6297..20b7abab 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -2745,6 +2745,10 @@ struct lxc_conf *lxc_conf_init(void)
 	memset(&new->cgroup_meta, 0, sizeof(struct lxc_cgroup));
 	memset(&new->ns_share, 0, sizeof(char *) * LXC_NS_MAX);
 
+	/* isulad add begin */
+	lxc_list_init(&new->populate_devs);
+	/* isulad add end */
+
 	return new;
 }
 
@@ -3487,6 +3491,85 @@ static bool execveat_supported(void)
 	return true;
 }
 
+/* isulad: setup devices which will be populated in the container.*/
+static int setup_populate_devs(const struct lxc_rootfs *rootfs, struct lxc_list *devs)
+{
+	int ret;
+	char *pathdirname;
+	char path[MAXPATHLEN];
+	mode_t cmask;
+	mode_t file_mode = 0;
+	struct lxc_populate_devs *dev_elem;
+        struct lxc_list *it;
+
+	INFO("Populating devices into container");
+	cmask = umask(S_IXUSR | S_IXGRP | S_IXOTH);
+	lxc_list_for_each(it, devs) {
+		dev_elem = it->elem;
+
+		ret = snprintf(path, MAXPATHLEN, "%s/%s", rootfs->path ? rootfs->mount : "", dev_elem->name);
+		if (ret < 0 || ret >= MAXPATHLEN)
+			return -1;
+
+		/* create any missing directories */
+		pathdirname = strdup(path);
+		pathdirname = dirname(pathdirname);
+		ret = mkdir_p(pathdirname, 0750);
+		free(pathdirname);
+		if (ret < 0) {
+			WARN("Failed to create target directory");
+			return -1;
+		}
+
+		if (!strcmp(dev_elem->type, "c")) {
+			file_mode = dev_elem->file_mode | S_IFCHR;
+		} else if (!strcmp(dev_elem->type, "b")) {
+			file_mode = dev_elem->file_mode | S_IFBLK;
+		} else {
+			ERROR("Failed to parse devices type '%s'", dev_elem->type);
+			return -1;
+		}
+
+		DEBUG("Try to mknod '%s':'%d':'%d':'%d'\n", path,
+				file_mode, dev_elem->maj, dev_elem->min);
+
+		ret = mknod(path, file_mode, makedev(dev_elem->maj, dev_elem->min));
+		if (ret && errno != EEXIST) {
+			SYSERROR("Failed to mknod '%s':'%d':'%d':'%d'", dev_elem->name,
+				file_mode, dev_elem->maj, dev_elem->min);
+
+			char hostpath[MAXPATHLEN];
+			FILE *pathfile;
+
+			// Unprivileged containers cannot create devices, so
+			// try to bind mount the device from the host
+			ret = snprintf(hostpath, MAXPATHLEN, "/dev/%s", dev_elem->name);
+			if (ret < 0 || ret >= MAXPATHLEN)
+				return -1;
+			pathfile = fopen(path, "wb");
+			if (!pathfile) {
+				SYSERROR("Failed to create device mount target '%s'", path);
+				return -1;
+			}
+			fclose(pathfile);
+			if (safe_mount(hostpath, path, 0, MS_BIND, NULL,
+						rootfs->path ? rootfs->mount : NULL) != 0) {
+				SYSERROR("Failed bind mounting device %s from host into container",
+					dev_elem->name);
+				return -1;
+			}
+		}
+		if (chown(path, dev_elem->uid, dev_elem->gid) < 0) {
+			ERROR("Error chowning %s", path);
+			return -1;
+		}
+	}
+	umask(cmask);
+
+	INFO("Populated devices into container /dev");
+	return 0;
+}
+
 int lxc_setup(struct lxc_handler *handler)
 {
 	int ret;
@@ -3584,6 +3667,16 @@ int lxc_setup(struct lxc_handler *handler)
 		return -1;
 	}
 
+	/*isulad: move mount entrues here, before we do lxc_fill_autodev and populate devices  */
+	if (!lxc_list_empty(&lxc_conf->mount_list)) {
+		ret = setup_mount_entries(lxc_conf, &lxc_conf->rootfs,
+					  &lxc_conf->mount_list, name, lxcpath);
+		if (ret < 0) {
+			ERROR("Failed to setup mount entries");
+			return -1;
+		}
+	}
+
 	ret = run_lxc_hooks(name, "mount", lxc_conf, NULL);
 	if (ret < 0) {
 		ERROR("Failed to run mount hooks");
@@ -3604,12 +3697,11 @@ int lxc_setup(struct lxc_handler *handler)
 		}
 	}
 
-	if (!lxc_list_empty(&lxc_conf->mount_list)) {
-		ret = setup_mount_entries(lxc_conf, &lxc_conf->rootfs,
-					  &lxc_conf->mount_list, name, lxcpath);
-		if (ret < 0) {
-			ERROR("Failed to setup mount entries");
-			return -1;
+	/* isulad: setup devices which will be populated in the container. */
+	if (!lxc_list_empty(&lxc_conf->populate_devs)) {
+		if (setup_populate_devs(&lxc_conf->rootfs, &lxc_conf->populate_devs)) {
+			ERROR("Failed to setup devices in the container");
+			return -1;;
 		}
 	}
 
@@ -4026,6 +4118,22 @@ int lxc_clear_init_args(struct lxc_conf *lxc_conf)
 	return 0;
 }
 
+/*isulad: clear populate devices*/
+int lxc_clear_populate_devices(struct lxc_conf *c)
+{
+	struct lxc_list *it,*next;
+
+	lxc_list_for_each_safe(it, &c->populate_devs, next) {
+		struct lxc_populate_devs *dev_elem = it->elem;
+		lxc_list_del(it);
+		free(dev_elem->name);
+		free(dev_elem->type);
+		free(dev_elem);
+		free(it);
+	}
+	return 0;
+}
+
 void lxc_conf_free(struct lxc_conf *conf)
 {
 	if (!conf)
@@ -4069,9 +4177,12 @@ void lxc_conf_free(struct lxc_conf *conf)
 	lxc_clear_limits(conf, "lxc.prlimit");
 	lxc_clear_sysctls(conf, "lxc.sysctl");
 	lxc_clear_procs(conf, "lxc.proc");
-        lxc_clear_init_args(conf);
 	free(conf->cgroup_meta.dir);
 	free(conf->cgroup_meta.controllers);
+	/* isulad add begin */
+	lxc_clear_init_args(conf);
+	lxc_clear_populate_devices(conf);
+	/* isulad add end */
 	free(conf);
 }
 
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 95c30270..cced868c 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -170,6 +170,26 @@ struct lxc_rootfs {
 	char *data;
 };
 
+/*
+ * iSulad: Defines a structure to store the devices which will
+ * be attached in container
+ * @name       : the target device name in container
+ * @type       : the type of target device "c" or "b"
+ * @mode       : file mode for the device
+ * @maj        : major number for the device
+ * @min        : minor number for the device
+ */
+struct lxc_populate_devs {
+	char *name;
+	char *type;
+	mode_t file_mode;
+	int maj;
+	int min;
+	uid_t uid;
+	gid_t gid;
+};
+
+
 /*
  * Automatic mounts for LXC to perform inside the container
  */
@@ -377,9 +397,13 @@ struct lxc_conf {
 	/* procs */
 	struct lxc_list procs;
 
-    /* isulad add: init args used to repalce init_cmd*/
+	/* isulad add begin */
+	/* init args used to repalce init_cmd*/
 	char **init_argv;
 	size_t init_argc;
+	/* populate devices*/
+	struct lxc_list populate_devs;
+	/* isulad add end */
 };
 
 extern int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf,
@@ -448,6 +472,8 @@ extern int lxc_clear_procs(struct lxc_conf *c, const char *key);
 
 /* isulad add begin */
 int lxc_clear_init_args(struct lxc_conf *lxc_conf);
+int lxc_clear_populate_devices(struct lxc_conf *c);
+
 /* isulad add end */
 
 #endif /* __LXC_CONF_H */
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 7297b358..e3212d3d 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -152,6 +152,7 @@ lxc_config_define(sysctl);
 lxc_config_define(proc);
 /*isulad add begin*/
 lxc_config_define(init_args);
+lxc_config_define(populate_device);
 /*isulad add end*/
 
 
@@ -241,6 +242,7 @@ static struct lxc_config_t config_jump_table[] = {
 
         /*isulad add begin*/
         { "lxc.isulad.init.args",          set_config_init_args,                   get_config_init_args,                   clr_config_init_args,                 },
+        { "lxc.isulad.populate.device",    set_config_populate_device,             get_config_populate_device,             clr_config_populate_device,           },
         /*isulad add end*/
 };
 
@@ -2219,6 +2221,93 @@ static int set_config_init_args(const char *key, const char *value,
 	return 0;
 }
 
+/* isulad: set config for init args */
+static int set_config_populate_device(const char *key, const char *value,
+				 struct lxc_conf *lxc_conf, void *data)
+{
+        int ret = 0, major = 0, minor = 0;
+        uid_t uid = (uid_t)-1;
+        gid_t gid = (gid_t)-1;
+        char name[PATH_MAX] = {0};
+        char type[3] = {0};
+        char *replace_value = NULL;
+        mode_t filemode = 0;
+        struct lxc_list *iter;
+        struct lxc_list *dev_list = NULL;
+        struct lxc_populate_devs *dev_elem = NULL;
+
+        if (lxc_config_value_empty(value))
+                return lxc_clear_populate_devices(lxc_conf);
+
+        /* lxc.populate.device = PATH_IN_CONTAINER:DEVICETYPE:MAJOR:MINOR:MODE:UID:GID
+         * For e.g. lxc.populate.device = /dev/sda:b:8:0:0666:0:0
+         */
+        ret = sscanf(value, "%[^:]:%2[^:]:%i:%i:%i:%u:%u", name, type, &major, &minor, &filemode, &uid, &gid);
+        if (ret != 7)
+                return -1;
+
+        /* find existing list element */
+        lxc_list_for_each(iter, &lxc_conf->populate_devs) {
+                dev_elem = iter->elem;
+
+                if (strcmp(name, dev_elem->name) != 0)
+                        continue;
+
+                replace_value = strdup(type);
+                if (!replace_value)
+                        return -1;
+
+                free(dev_elem->type);
+                dev_elem->type = replace_value;
+                dev_elem->file_mode = filemode;
+                dev_elem->maj = major;
+                dev_elem->min = minor;
+                dev_elem->uid = (uid_t)uid;
+                dev_elem->gid = (gid_t)gid;
+                return 0;
+        }
+
+        /* allocate list element */
+        dev_list = malloc(sizeof(*dev_list));
+        if (!dev_list)
+                goto on_error;
+
+        lxc_list_init(dev_list);
+
+        dev_elem = malloc(sizeof(*dev_elem));
+        if (!dev_elem)
+                goto on_error;
+        memset(dev_elem, 0, sizeof(*dev_elem));
+
+        dev_elem->name = strdup(name);
+        if (!dev_elem->name)
+                goto on_error;
+
+        dev_elem->type = strdup(type);
+        if (!dev_elem->type)
+                goto on_error;
+
+        dev_elem->file_mode = filemode;
+        dev_elem->maj = major;
+        dev_elem->min = minor;
+
+        lxc_list_add_elem(dev_list, dev_elem);
+
+        lxc_list_add_tail(&lxc_conf->populate_devs, dev_list);
+
+        return 0;
+
+on_error:
+        free(dev_list);
+        if (dev_elem) {
+                free(dev_elem->name);
+                free(dev_elem->type);
+                free(dev_elem);
+        }
+        return -1;
+
+}
+
 struct parse_line_conf {
 	struct lxc_conf *conf;
 	bool from_include;
@@ -3770,6 +3859,34 @@ static int get_config_init_args(const char *key, char *retv, int inlen,
 	return fulllen;
 }
 
+/* isulad: get config populate device
+ * If you ask for 'lxc.populate.device', then all populate device
+ * entries will be printed, in 'lxc.populate.device = path_in_container:type:major:minor:mode:uid:gid' format.
+ * For e.g. lxc.populate.device = /dev/sda:b:8:0:0666:0:0
+ */
+static int get_config_populate_device(const char *key, char *retv, int inlen,
+				  struct lxc_conf *c, void *data)
+{
+	int len;
+	struct lxc_list *it;
+	int fulllen = 0;
+
+	if (!retv)
+		inlen = 0;
+	else
+		memset(retv, 0, inlen);
+
+	lxc_list_for_each(it, &c->populate_devs) {
+		struct lxc_populate_devs *elem = it->elem;
+		strprint(retv, inlen, "lxc.populate.device = %s:%s:%d:%d:%o:%u:%u\n",
+			 elem->name, elem->type, elem->maj,
+			 elem->min, elem->file_mode, elem->uid, elem->gid);
+	}
+
+	return fulllen;
+}
+
+
 /* Callbacks to clear config items. */
 static inline int clr_config_personality(const char *key, struct lxc_conf *c,
 					 void *data)
@@ -4581,6 +4698,13 @@ static inline int clr_config_init_args(const char *key, struct lxc_conf *c,
 	return lxc_clear_init_args(c);
 }
 
+/* isulad: clr config populate devices*/
+static inline int clr_config_populate_device(const char *key, struct lxc_conf *c,
+				   void *data)
+{
+	return lxc_clear_populate_devices(c);
+}
+
 static int get_config_net_nic(const char *key, char *retv, int inlen,
 			      struct lxc_conf *c, void *data)
 {
-- 
2.23.0

