The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxc/pull/2160

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Now the lxc provide the autodev hook to mknod in the container. 
I think we should provide a way to make the same thing in config.

Signed-off-by: LiFeng <lifen...@huawei.com>
From c79187df805fffe61b3b467646f54eb6d4f2bfc9 Mon Sep 17 00:00:00 2001
From: LiFeng <lifen...@huawei.com>
Date: Mon, 12 Feb 2018 11:39:07 -0500
Subject: [PATCH] [confile] Add config for populate devices.

Signed-off-by: LiFeng <lifen...@huawei.com>
---
 doc/lxc.container.conf.sgml.in |  23 ++++++++
 src/lxc/conf.c                 | 102 +++++++++++++++++++++++++++++++++++
 src/lxc/conf.h                 |  26 ++++++++-
 src/lxc/confile.c              | 120 +++++++++++++++++++++++++++++++++++++++++
 src/tests/get_item.c           |  45 ++++++++++++++++
 src/tests/parse_config_file.c  |   7 +++
 6 files changed, 322 insertions(+), 1 deletion(-)

diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in
index ccc6348c4..6e63f369f 100644
--- a/doc/lxc.container.conf.sgml.in
+++ b/doc/lxc.container.conf.sgml.in
@@ -1604,6 +1604,29 @@ dev/null proc/kcore none bind,relative 0 0
       </variablelist>
     </refsect2>
 
+    <refsect2>
+      <title>Populate devices</title>
+      <para>
+        Specify the devices on host to be populated into the container.
+        For instance, you can populate the block device '/dev/sda' on host 
into the container.
+      </para>
+      <variablelist>
+        <varlistentry>
+          <term>
+            <option>lxc.populate.device</option>
+          </term>
+          <listitem>
+            <para>
+              Seven values must be provided. First The absolute path of the 
device in the container.
+             Next a character, either 'b', or 'c', to specify the device type.
+             Next is the major device number. Next is the minor device number.
+             Next is the filemode of the device. Finally, user and group 
ownership of the devive.
+             </para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
     <refsect2>
       <title>Apparmor profile</title>
       <para>
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 05b17a499..1abc56be1 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -1256,6 +1256,82 @@ static int lxc_fill_autodev(const struct lxc_rootfs 
*rootfs)
        return 0;
 }
 
+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, 0755);
+               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;
+               }
+
+               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;
+}
+
+
 static int lxc_setup_rootfs(struct lxc_conf *conf)
 {
        int ret;
@@ -2585,6 +2661,8 @@ 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);
 
+       lxc_list_init(&new->populate_devs);
+
        return new;
 }
 
@@ -3290,6 +3368,14 @@ int lxc_setup(struct lxc_handler *handler)
                        ERROR("failed to populate /dev in the container");
                        return -1;
                }
+               /* 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;
+                       }
+               }
        }
 
        if (!lxc_list_empty(&lxc_conf->mount_list) && 
setup_mount_entries(lxc_conf, &lxc_conf->rootfs, &lxc_conf->mount_list, name, 
lxcpath)) {
@@ -3573,6 +3659,21 @@ int lxc_clear_procs(struct lxc_conf *c, const char *key)
        return 0;
 }
 
+int lxc_clear_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;
+}
+
 int lxc_clear_groups(struct lxc_conf *c)
 {
        struct lxc_list *it,*next;
@@ -3711,6 +3812,7 @@ 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_devices(conf);
        free(conf->cgroup_meta.dir);
        free(conf->cgroup_meta.controllers);
        free(conf);
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index c5f27336a..96aeded33 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -207,6 +207,27 @@ struct lxc_rootfs {
        char *bdev_type;
 };
 
+/*
+ * Defines a structure to store the devices which will
+ * be populated 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
+ * @uid        : owner uid for the device
+ * @gid        : owner gid 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
  */
@@ -415,6 +436,9 @@ struct lxc_conf {
 
        /* procs */
        struct lxc_list procs;
+
+       /* populate devices*/
+       struct lxc_list populate_devs;
 };
 
 extern int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf,
@@ -479,5 +503,5 @@ extern int setup_sysctl_parameters(struct lxc_list 
*sysctls);
 extern int lxc_clear_sysctls(struct lxc_conf *c, const char *key);
 extern int setup_proc_filesystem(struct lxc_list *procs, pid_t pid);
 extern int lxc_clear_procs(struct lxc_conf *c, const char *key);
-
+extern int lxc_clear_devices(struct lxc_conf *c);
 #endif /* __LXC_CONF_H */
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 66b7615fe..53a537e64 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -144,6 +144,7 @@ lxc_config_define(tty_dir);
 lxc_config_define(uts_name);
 lxc_config_define(sysctl);
 lxc_config_define(proc);
+lxc_config_define(populate_device);
 
 static struct lxc_config_t config[] = {
        { "lxc.arch",                      set_config_personality,              
   get_config_personality,                 clr_config_personality,              
 },
@@ -228,6 +229,7 @@ static struct lxc_config_t config[] = {
        { "lxc.uts.name",                  set_config_uts_name,                 
   get_config_uts_name,                    clr_config_uts_name,                 
 },
        { "lxc.sysctl",                    set_config_sysctl,                   
   get_config_sysctl,                      clr_config_sysctl,                   
 },
        { "lxc.proc",                      set_config_proc,                     
   get_config_proc,                        clr_config_proc,                     
 },
+       { "lxc.populate.device",           set_config_populate_device,          
   get_config_populate_device,             clr_config_populate_device,          
        },
 };
 
 struct signame {
@@ -1623,6 +1625,89 @@ static int set_config_proc(const char *key, const char 
*value,
        return -1;
 }
 
+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[2] = {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_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;
+
+       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;
+}
+
 static int set_config_idmaps(const char *key, const char *value,
                             struct lxc_conf *lxc_conf, void *data)
 {
@@ -3554,6 +3639,35 @@ static int get_config_proc(const char *key, char *retv, 
int inlen,
        return fulllen;
 }
 
+/* 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);
+
+       if (strcmp(key, "lxc.populate.device") != 0)
+               return -1;
+
+       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;
+}
+
 static int get_config_namespace_clone(const char *key, char *retv, int inlen,
                                      struct lxc_conf *c, void *data)
 {
@@ -3977,6 +4091,12 @@ static inline int clr_config_sysctl(const char *key, 
struct lxc_conf *c,
        return lxc_clear_sysctls(c, key);
 }
 
+static inline int clr_config_populate_device(const char *key, struct lxc_conf 
*c,
+                                  void *data)
+{
+       return lxc_clear_devices(c);
+}
+
 static inline int clr_config_proc(const char *key, struct lxc_conf *c,
                                   void *data)
 {
diff --git a/src/tests/get_item.c b/src/tests/get_item.c
index e7667c482..81c8c7e86 100644
--- a/src/tests/get_item.c
+++ b/src/tests/get_item.c
@@ -552,6 +552,51 @@ int main(int argc, char *argv[])
                goto out;
        }
 
+#define POPULATE_DEVICE_SDA1 "lxc.populate.device = /dev/sda1:b:8:1:0666:0:0\n"
+#define ALL_POPULATE_DEVICE "lxc.populate.device = /dev/sda:b:8:0:0666:0:0\n" 
POPULATE_DEVICE_SDA1
+
+       ret = c->get_config_item(c, "lxc.populate.device", v3, 2047);
+       if (ret != 0) {
+               fprintf(stderr, "%d: get_config_item(populate.device) returned 
%d\n", __LINE__, ret);
+               goto out;
+       }
+
+       if (!c->set_config_item(c, "lxc.populate.device", 
"/dev/sda:b:8:0:0666:0:0")) {
+               fprintf(stderr, "%d: failed to set lxc.populate.device = 
/dev/sda:b:8:0:0666:0:0\n", __LINE__);
+               goto out;
+       }
+       ret = c->get_config_item(c, "lxc.populate.device", v2, 255);
+       if (ret < 0) {
+               fprintf(stderr, "%d: get_config_item(lxc.populate.device) 
returned %d\n", __LINE__, ret);
+               goto out;
+       }
+       if (strcmp(v2, "/dev/sda:b:8:0:0666:0:0")) {
+               fprintf(stderr, "%d: lxc.populate.device returned wrong value: 
%d %s not /dev/sda:b:8:0:0666:0:0\n", __LINE__, ret, v2);
+               goto out;
+       }
+       printf("lxc.populate.device returned: %d %s\n", ret, v2);
+
+       if (!c->set_config_item(c, "lxc.populate.device", 
"/dev/sda1:b:8:1:0666:0:0")) {
+               fprintf(stderr, "%d: failed to set lxc.populate.device = 
/dev/sda1:b:8:1:0666:0:0\n", __LINE__);
+               goto out;
+       }
+
+       ret = c->get_config_item(c, "lxc.populate.device", v3, 2047);
+       if (ret != sizeof(ALL_POPULATE_DEVICE)-1) {
+               fprintf(stderr, "%d: get_config_item(lxc.populate.device) 
returned %d\n", __LINE__, ret);
+               goto out;
+       }
+       if (strcmp(v3, ALL_POPULATE_DEVICE)) {
+               fprintf(stderr, "%d: lxc.populate.device returned wrong value: 
%d %s not %d %s\n", __LINE__, ret, v3, (int)sizeof(ALL_POPULATE_DEVICE) - 1, 
ALL_POPULATE_DEVICE);
+               goto out;
+       }
+       printf("lxc.populate.device returned %d %s\n", ret, v3);
+
+       if (!c->clear_config_item(c, "lxc.populate.device")) {
+               fprintf(stderr, "%d: failed clearing lxc.populate.device\n", 
__LINE__);
+               goto out;
+       }
+
        printf("All get_item tests passed\n");
        fret = EXIT_SUCCESS;
 out:
diff --git a/src/tests/parse_config_file.c b/src/tests/parse_config_file.c
index aad4d32b6..7568e800e 100644
--- a/src/tests/parse_config_file.c
+++ b/src/tests/parse_config_file.c
@@ -726,6 +726,13 @@ int main(int argc, char *argv[])
                goto non_test_error;
        }
 
+       /* lxc.populate.device */
+       if (set_get_compare_clear_save_load(c, "lxc.populate.device", 
"/dev/sda:b:8:0:0666:0:0", tmpf,
+                                           true) < 0) {
+               lxc_error("%s\n", "lxc.populate.device");
+               goto non_test_error;
+       }
+
        /* lxc.proc */
        if (set_get_compare_clear_save_load(c, "lxc.proc.oom_score_adj", "10", 
tmpf,
                                            true) < 0) {
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to