]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
Allow full path in lxc.devices.allow 1708/head
authoraeris <aeris@imirhil.fr>
Wed, 19 Jul 2017 15:21:24 +0000 (17:21 +0200)
committeraeris <aeris@imirhil.fr>
Wed, 26 Jul 2017 10:17:33 +0000 (12:17 +0200)
Some devices like LVM or cryptsetup entries have no stable major/minor, changing between host reboots.
In this case, hardcoded numbers are not usable in config file and there is currently no way to use hook with lxc-device to do the link at guest startup :

    * `pre-start`/`autodev` hook runs in host context but has the guest in stopped state and so lxc-device not usable
    * `start` hook is in running state but runs in guest context and so lxc-device not available

This patch converts fullpath in lxc.devices.allow to current major/minor numbers to address those changing numbers.

Signed-off-by: aeris <aeris@imirhil.fr>
src/lxc/cgroups/cgfsng.c

index 1192d575f72c16dcaa2391c56ae4edfbcc548d93..9db0f7be2276cd5228da7a74a95f8bde36526ea0 100644 (file)
@@ -47,6 +47,9 @@
 #include <unistd.h>
 #include <sys/types.h>
 
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
 #include "bdev.h"
 #include "cgroup.h"
 #include "commands.h"
@@ -1927,16 +1930,105 @@ static int lxc_cgroup_set_data(const char *filename, const char *value, struct c
        char *subsystem = NULL, *p;
        int ret = -1;
        struct hierarchy *h;
+       char converted_value[50]; // "b|c <2^64-1>:<2^64-1> r|w|m" = 47 chars max
 
        subsystem = alloca(strlen(filename) + 1);
        strcpy(subsystem, filename);
        if ((p = strchr(subsystem, '.')) != NULL)
                *p = '\0';
 
+       if (strcmp("devices.allow", filename) == 0 && value[0] == '/') {
+               char *to_split1 = strdup(value);
+               if (to_split1 == NULL) {
+                       ret = -ENOMEM;
+               } else {
+                       char *saveptr = NULL;
+                       size_t n_parts = 0;
+                       while (strtok_r(n_parts ? NULL : to_split1, " ", &saveptr) != NULL) {
+                               ++n_parts;
+                       }
+                       free(to_split1);
+
+                       if (n_parts == 2) {
+                               size_t i;
+                               char **parts = malloc(sizeof(char*) * n_parts);
+                               if (parts == NULL) {
+                                       ret = -ENOMEM;
+                               } else {
+                                       ret = 0;
+                                       // We can't reuse to_split1 here, because strtok_r modifies its first argument
+                                       char *to_split2 = strdup(value);
+                                       if (to_split2 == NULL) {
+                                               ret = -ENOMEM;
+                                       } else {
+                                               for (i = 0; i < n_parts; ++i) {
+                                                       char *part = strtok_r(i ? NULL : to_split2, " ", &saveptr);
+                                                       char *subpart = strdup(part);
+                                                       if (subpart == NULL) {
+                                                               ret = -ENOMEM;
+                                                       }
+                                                       parts[i] = subpart;
+                                               }
+                                               free(to_split2);
+
+                                               if (ret >= 0) {
+                                                       const char *path = parts[0];
+                                                       const char *mode = parts[1];
+
+                                                       struct stat sb;
+                                                       stat(path, &sb);
+                                                       dev_t dev = sb.st_rdev;
+
+                                                       char type = 0;
+                                                       mode_t m = sb.st_mode & S_IFMT;
+                                                       switch (m) {
+                                                       case S_IFBLK:
+                                                               type = 'b';
+                                                               break;
+                                                       case S_IFCHR:
+                                                               type = 'c';
+                                                               break;
+                                                       }
+
+                                                       if (type == 0) {
+                                                               ERROR("Unsupported device type %i for %s", m, path);
+                                                               ret = -EINVAL;
+                                                       } else {
+                                                               unsigned long major = MAJOR(dev), minor = MINOR(dev);
+                                                               ret = snprintf(converted_value, 50,
+                                                                               "%c %lu:%lu %s", type, major, minor, mode);
+                                                               if (ret < 0 || ret >= 50) {
+                                                                       ERROR("Error on configuration value \"%c %lu:%lu %s\" (max 50 chars)",
+                                                                                       type, major, minor, mode);
+                                                                       ret = -ENAMETOOLONG;
+                                                               }
+                                                       }
+                                               }
+
+                                               for (i = 0; i < n_parts; ++i) {
+                                                       char *subpart = parts[i];
+                                                       if (subpart != NULL) {
+                                                               free(subpart);
+                                                       }
+                                               }
+                                               free(parts);
+                                       }
+                               }
+                       } else {
+                               strcpy(converted_value, value);
+                       }
+               }
+               if (ret <= 0) {
+                       return ret;
+               }
+       } else {
+               strcpy(converted_value, value);
+       }
+
        h = get_hierarchy(subsystem);
        if (h) {
                char *fullpath = must_make_path(h->fullcgpath, filename, NULL);
-               ret = lxc_write_to_file(fullpath, value, strlen(value), false);
+               ret = lxc_write_to_file(fullpath, converted_value, strlen(converted_value), false);
                free(fullpath);
        }
        return ret;