#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R
+static int virCgroupPartitionNeedsEscaping(const char *path)
+{
+ FILE *fp = NULL;
+ int ret = 0;
+ char *line = NULL;
+ size_t len;
+
+ /* If it starts with 'cgroup.' or a '_' of any
+ * of the controller names from /proc/cgroups,
+ * then we must prefix a '_'
+ */
+ if (STRPREFIX(path, "cgroup."))
+ return 1;
+
+ if (path[0] == '_')
+ return 1;
+
+ if (!(fp = fopen("/proc/cgroups", "r")))
+ return -errno;
+
+ /*
+ * Data looks like this:
+ * #subsys_name hierarchy num_cgroups enabled
+ * cpuset 2 4 1
+ * cpu 3 48 1
+ * cpuacct 3 48 1
+ * memory 4 4 1
+ * devices 5 4 1
+ * freezer 6 4 1
+ * net_cls 7 1 1
+ */
+ while (getline(&line, &len, fp) > 0) {
+ if (STRPREFIX(line, "#subsys_name")) {
+ VIR_FREE(line);
+ continue;
+ }
+ char *tmp = strchr(line, ' ');
+ if (tmp)
+ *tmp = '\0';
+ len = tmp - line;
+
+ if (STRPREFIX(path, line) &&
+ path[len] == '.') {
+ ret = 1;
+ VIR_FREE(line);
+ goto cleanup;
+ }
+ VIR_FREE(line);
+ }
+
+ if (ferror(fp)) {
+ ret = -EIO;
+ goto cleanup;
+ }
+
+cleanup:
+ VIR_FORCE_FCLOSE(fp);
+ return ret;
+}
+
+static int virCgroupPartitionEscape(char **path)
+{
+ size_t len = strlen(*path) + 1;
+ int rc;
+ char escape = '_';
+
+ if ((rc = virCgroupPartitionNeedsEscaping(*path)) <= 0)
+ return rc;
+
+ if (VIR_INSERT_ELEMENT(*path, 0, len, escape) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
static char *virCgroupSetPartitionSuffix(const char *path)
{
char **tokens = virStringSplit(path, "/", 0);
}
strcat(tokens[i], ".partition");
}
+
+ if (virCgroupPartitionEscape(&(tokens[i])) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
}
if (!(ret = virStringJoin((const char **)tokens, "/")))
name, driver) < 0)
return -ENOMEM;
+ if ((rc = virCgroupPartitionEscape(&grpname)) < 0)
+ return rc;
+
rc = virCgroupNew(grpname, partition, -1, group);
if (rc == 0) {
* Co-mounting cpu & cpuacct controllers
* An anonymous controller for systemd
*/
-const char *mounts =
+const char *procmounts =
"rootfs / rootfs rw 0 0\n"
"tmpfs /run tmpfs rw,seclabel,nosuid,nodev,mode=755 0 0\n"
"tmpfs /not/really/sys/fs/cgroup tmpfs rw,seclabel,nosuid,nodev,noexec,mode=755 0 0\n"
"/dev/sda1 /boot ext4 rw,seclabel,relatime,data=ordered 0 0\n"
"tmpfs /tmp tmpfs rw,seclabel,relatime,size=1024000k 0 0\n";
-const char *cgroups =
+const char *procselfcgroups =
"115:memory:/\n"
"8:blkio:/\n"
"6:freezer:/\n"
"2:cpuset:/\n"
"1:name=systemd:/user/berrange/123\n";
+const char *proccgroups =
+ "#subsys_name hierarchy num_cgroups enabled\n"
+ "cpuset 2 4 1\n"
+ "cpu 3 48 1\n"
+ "cpuacct 3 48 1\n"
+ "memory 4 4 1\n"
+ "devices 5 4 1\n"
+ "freezer 6 4 1\n"
+ "blkio 8 4 1\n";
+
+
static int make_file(const char *path,
const char *name,
const char *value)
if (STREQ(path, "/proc/mounts")) {
if (STREQ(mode, "r")) {
- return fmemopen((void *)mounts, strlen(mounts), mode);
+ return fmemopen((void *)procmounts, strlen(procmounts), mode);
+ } else {
+ errno = EACCES;
+ return NULL;
+ }
+ }
+ if (STREQ(path, "/proc/cgroups")) {
+ if (STREQ(mode, "r")) {
+ return fmemopen((void *)proccgroups, strlen(proccgroups), mode);
} else {
errno = EACCES;
return NULL;
}
if (STREQ(path, "/proc/self/cgroup")) {
if (STREQ(mode, "r")) {
- return fmemopen((void *)cgroups, strlen(cgroups), mode);
+ return fmemopen((void *)procselfcgroups, strlen(procselfcgroups), mode);
} else {
errno = EACCES;
return NULL;
return ret;
}
+static int testCgroupNewForPartitionDomainEscaped(const void *args ATTRIBUTE_UNUSED)
+{
+ virCgroupPtr partitioncgroup1 = NULL;
+ virCgroupPtr partitioncgroup2 = NULL;
+ virCgroupPtr partitioncgroup3 = NULL;
+ virCgroupPtr domaincgroup = NULL;
+ int ret = -1;
+ int rv;
+ const char *placement[VIR_CGROUP_CONTROLLER_LAST] = {
+ [VIR_CGROUP_CONTROLLER_CPU] = "/_cgroup.evil/net_cls.evil/__evil.evil/_cpu.foo.libvirt-lxc",
+ [VIR_CGROUP_CONTROLLER_CPUACCT] = "/_cgroup.evil/net_cls.evil/__evil.evil/_cpu.foo.libvirt-lxc",
+ [VIR_CGROUP_CONTROLLER_CPUSET] = "/_cgroup.evil/net_cls.evil/__evil.evil/_cpu.foo.libvirt-lxc",
+ [VIR_CGROUP_CONTROLLER_MEMORY] = "/_cgroup.evil/net_cls.evil/__evil.evil/_cpu.foo.libvirt-lxc",
+ [VIR_CGROUP_CONTROLLER_DEVICES] = NULL,
+ [VIR_CGROUP_CONTROLLER_FREEZER] = "/_cgroup.evil/net_cls.evil/__evil.evil/_cpu.foo.libvirt-lxc",
+ [VIR_CGROUP_CONTROLLER_BLKIO] = "/_cgroup.evil/net_cls.evil/__evil.evil/_cpu.foo.libvirt-lxc",
+ };
+
+ if ((rv = virCgroupNewPartition("/cgroup.evil", true, -1, &partitioncgroup1)) != 0) {
+ fprintf(stderr, "Failed to create /cgroup.evil cgroup: %d\n", -rv);
+ goto cleanup;
+ }
+
+ if ((rv = virCgroupNewPartition("/cgroup.evil/net_cls.evil", true, -1, &partitioncgroup2)) != 0) {
+ fprintf(stderr, "Failed to create /cgroup.evil/cpu.evil cgroup: %d\n", -rv);
+ goto cleanup;
+ }
+
+ if ((rv = virCgroupNewPartition("/cgroup.evil/net_cls.evil/_evil.evil", true, -1, &partitioncgroup3)) != 0) {
+ fprintf(stderr, "Failed to create /cgroup.evil cgroup: %d\n", -rv);
+ goto cleanup;
+ }
+
+ if ((rv = virCgroupNewDomainPartition(partitioncgroup3, "lxc", "cpu.foo", true, &domaincgroup)) != 0) {
+ fprintf(stderr, "Cannot create LXC cgroup: %d\n", -rv);
+ goto cleanup;
+ }
+
+ /* NB we're not expecting 'net_cls.evil' to be escaped,
+ * since our fake /proc/cgroups pretends this controller
+ * isn't compiled into the kernel
+ */
+ ret = validateCgroup(domaincgroup, "/_cgroup.evil/net_cls.evil/__evil.evil/_cpu.foo.libvirt-lxc", mountsFull, links, placement);
+
+cleanup:
+ virCgroupFree(&partitioncgroup3);
+ virCgroupFree(&partitioncgroup2);
+ virCgroupFree(&partitioncgroup1);
+ virCgroupFree(&domaincgroup);
+ return ret;
+}
+
# define FAKESYSFSDIRTEMPLATE abs_builddir "/fakesysfsdir-XXXXXX"
static int
if (virtTestRun("New cgroup for domain partition", 1, testCgroupNewForPartitionDomain, NULL) < 0)
ret = -1;
+ if (virtTestRun("New cgroup for domain partition escaped", 1, testCgroupNewForPartitionDomainEscaped, NULL) < 0)
+ ret = -1;
if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL)
virFileDeleteTree(fakesysfsdir);