]> git.proxmox.com Git - mirror_lxcfs.git/blobdiff - pam/pam_cgfs.c
pam_cgfs: make sure that **p is not NULL
[mirror_lxcfs.git] / pam / pam_cgfs.c
index b823cda1cfbea364c8ec816f633814717c61d89c..fd47d7076a2271a1997eb164c9fdf2a3d2a2e184 100644 (file)
  * See COPYING file for details.
  */
 
+#include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <pwd.h>
 #include <stdarg.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #define CGROUP2_SUPER_MAGIC 0x63677270
 #endif
 
+/* Taken over modified from the kernel sources. */
+#define NBITS 32 /* bits in uint32_t */
+#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, NBITS)
+
 static enum cg_mount_mode {
        CGROUP_UNKNOWN = -1,
        CGROUP_MIXED = 0,
@@ -72,16 +80,24 @@ static enum cg_mount_mode {
        CGROUP_UNINITIALIZED = 3,
 } cg_mount_mode = CGROUP_UNINITIALIZED;
 
-/* Common helper prototypes. */
+/* Common helper functions. Most of these have been taken from LXC. */
 static void append_line(char **dest, size_t oldlen, char *new, size_t newlen);
 static int append_null_to_list(void ***list);
 static void batch_realloc(char **mem, size_t oldlen, size_t newlen);
+static inline void clear_bit(unsigned bit, uint32_t *bitarr)
+{
+       bitarr[bit / NBITS] &= ~(1 << (bit % NBITS));
+}
 static char *copy_to_eol(char *s);
 static bool file_exists(const char *f);
 static void free_string_list(char **list);
 static char *get_mountpoint(char *line);
 static bool get_uid_gid(const char *user, uid_t *uid, gid_t *gid);
 static int handle_login(const char *user, uid_t uid, gid_t gid);
+static inline bool is_set(unsigned bit, uint32_t *bitarr)
+{
+       return (bitarr[bit / NBITS] & (1 << (bit % NBITS))) != 0;
+}
 /* __typeof__ should be safe to use with all compilers. */
 typedef __typeof__(((struct statfs *)NULL)->f_type) fs_type_magic;
 static bool has_fs_type(const struct statfs *fs, fs_type_magic magic_val);
@@ -99,16 +115,33 @@ static char *must_make_path(const char *first, ...) __attribute__((sentinel));
 static void *must_realloc(void *orig, size_t sz);
 static void mysyslog(int err, const char *format, ...) __attribute__((sentinel));
 static char *read_file(char *fnam);
+static int read_from_file(const char *filename, void* buf, size_t count);
 static int recursive_rmdir(char *dirname);
+static inline void set_bit(unsigned bit, uint32_t *bitarr)
+{
+       bitarr[bit / NBITS] |= (1 << (bit % NBITS));
+}
 static bool string_in_list(char **list, const char *entry);
+static char *string_join(const char *sep, const char **parts, bool use_as_prefix);
 static void trim(char *s);
 static bool write_int(char *path, int v);
+static ssize_t write_nointr(int fd, const void* buf, size_t count);
+static int write_to_file(const char *filename, const void *buf, size_t count,
+                        bool add_newline);
 
 /* cgroupfs prototypes. */
+static bool cg_belongs_to_uid_gid(const char *path, uid_t uid, gid_t gid);
+static uint32_t *cg_cpumask(char *buf, size_t nbits);
+static bool cg_copy_parent_file(char *path, char *file);
+static char *cg_cpumask_to_cpulist(uint32_t *bitarr, size_t nbits);
+static bool cg_enter(const char *cgroup);
+static void cg_escape(void);
+static bool cg_filter_and_set_cpus(char *path, bool am_initialized);
+static ssize_t cg_get_max_cpus(char *cpulist);
+static int cg_get_version_of_mntpt(const char *path);
+static bool cg_init(uid_t uid, gid_t gid);
 static void cg_mark_to_make_rw(const char *cstring);
-static bool cg_systemd_under_user_slice_1(const char *in, uid_t uid);
-static bool cg_systemd_under_user_slice_2(const char *base_cgroup,
-                                         const char *init_cgroup, uid_t uid);
+static void cg_prune_empty_cgroups(const char *user);
 static bool cg_systemd_created_user_slice(const char *base_cgroup,
                                          const char *init_cgroup,
                                          const char *in, uid_t uid);
@@ -116,12 +149,10 @@ static bool cg_systemd_chown_existing_cgroup(const char *mountpoint,
                                             const char *base_cgroup, uid_t uid,
                                             gid_t gid,
                                             bool systemd_user_slice);
-static int cg_get_version_of_mntpt(const char *path);
-static bool cg_enter(const char *cgroup);
-static void cg_escape(void);
-static bool cg_init(uid_t uid, gid_t gid);
+static bool cg_systemd_under_user_slice_1(const char *in, uid_t uid);
+static bool cg_systemd_under_user_slice_2(const char *base_cgroup,
+                                         const char *init_cgroup, uid_t uid);
 static void cg_systemd_prune_init_scope(char *cg);
-static void cg_prune_empty_cgroups(const char *user);
 static bool is_lxcfs(const char *line);
 
 /* cgroupfs v1 prototypes. */
@@ -153,6 +184,9 @@ static bool cgv1_get_controllers(char ***klist, char ***nlist);
 static char *cgv1_get_current_cgroup(char *basecginfo, char *controller);
 static char **cgv1_get_proc_mountinfo_controllers(char **klist, char **nlist,
                                                  char *line);
+static bool cgv1_handle_cpuset_hierarchy(struct cgv1_hierarchy *h,
+                                        const char *cgroup);
+static bool cgv1_handle_root_cpuset_hierarchy(struct cgv1_hierarchy *h);
 static bool cgv1_init(uid_t uid, gid_t gid);
 static void cgv1_mark_to_make_rw(char **clist);
 static char *cgv1_must_prefix_named(char *entry);
@@ -191,13 +225,13 @@ static bool cgv2_prune_empty_cgroups(const char *user);
 static bool cgv2_remove(const char *cgroup);
 static bool is_cgv2(char *line);
 
-/* Common helper functions. */
+/* Common helper functions. Most of these have been taken from LXC. */
 static void mysyslog(int err, const char *format, ...)
 {
        va_list args;
 
        va_start(args, format);
-       openlog("PAM-CGFS", LOG_CONS|LOG_PID, LOG_AUTH);
+       openlog("PAM-CGFS", LOG_CONS | LOG_PID, LOG_AUTH);
        vsyslog(err, format, args);
        va_end(args);
        closelog();
@@ -484,7 +518,7 @@ static bool mkdir_p(const char *root, char *path)
                        goto next;
 
                if (mkdir(path, 0755) < 0) {
-                       lxcfs_debug("Failed to create %s: %m.\n", path);
+                       lxcfs_debug("Failed to create %s: %s.\n", path, strerror(errno));
                        return false;
                }
 
@@ -541,13 +575,13 @@ next:
 
        if (rmdir(dirname) < 0) {
                if (!r)
-                       lxcfs_debug("Failed to delete %s: %m.\n", dirname);
+                       lxcfs_debug("Failed to delete %s: %s.\n", dirname, strerror(errno));
                r = -1;
        }
 
        if (closedir(dir) < 0) {
                if (!r)
-                       lxcfs_debug("Failed to delete %s: %m.\n", dirname);
+                       lxcfs_debug("Failed to delete %s: %s.\n", dirname, strerror(errno));
                r = -1;
        }
 
@@ -1139,8 +1173,9 @@ static bool cg_systemd_chown_existing_cgroup(const char *mountpoint,
         * need to chown it.
         */
        if (chown(path, uid, gid) < 0)
-               mysyslog(LOG_WARNING, "Failed to chown %s to %d:%d: %m.\n",
-                        path, (int)uid, (int)gid, NULL);
+               mysyslog(LOG_WARNING, "Failed to chown %s to %d:%d: %s.\n",
+                        path, (int)uid, (int)gid, strerror(errno), NULL);
+       lxcfs_debug("Chowned %s to %d:%d.\n", path, (int)uid, (int)gid);
 
        free(path);
        return true;
@@ -1283,7 +1318,6 @@ static int cg_get_version_of_mntpt(const char *path)
 static bool cgv2_init(uid_t uid, gid_t gid)
 {
        char *mountpoint;
-       bool ret = false;
        FILE *f = NULL;
        char *current_cgroup = NULL, *init_cgroup = NULL;
        char * line = NULL;
@@ -1292,7 +1326,6 @@ static bool cgv2_init(uid_t uid, gid_t gid)
        current_cgroup = cgv2_get_current_cgroup(getpid());
        if (!current_cgroup) {
                /* No v2 hierarchy present. We're done. */
-               ret = true;
                goto cleanup;
        }
 
@@ -1325,7 +1358,6 @@ static bool cgv2_init(uid_t uid, gid_t gid)
 
                cgv2_add_controller(NULL, mountpoint, current_cgroup, init_cgroup, has_user_slice);
 
-               ret = true;
                goto cleanup;
        }
 
@@ -1421,8 +1453,13 @@ static bool cgv1_enter(const char *cgroup)
                     controller++) {
                        char *path;
 
-                       if ((*it)->systemd_user_slice)
-                               continue;
+                       /* We've already been placed in a user slice, so we
+                        * don't need to enter the cgroup again.
+                        */
+                       if ((*it)->systemd_user_slice) {
+                               entered = true;
+                               break;
+                       }
 
                        path = must_make_path((*it)->mountpoint,
                                              (*it)->init_cgroup,
@@ -1505,6 +1542,17 @@ static bool cg_enter(const char *cgroup)
 /* Escape to root cgroup in all detected cgroupfs v1 hierarchies. */
 static void cgv1_escape(void)
 {
+       struct cgv1_hierarchy **it;
+
+       /* In case systemd hasn't already placed us in a user slice for the
+        * cpuset v1 controller we will reside in the root cgroup. This means
+        * that cgroup.clone_children will not have been initialized for us so
+        * we need to do it.
+        */
+       for (it = cgv1_hierarchies; it && *it; it++)
+               if (!cgv1_handle_root_cpuset_hierarchy(*it))
+                       mysyslog(LOG_WARNING, "cgroupfs v1: Failed to initialize cpuset.\n", NULL);
+
        if (!cgv1_enter("/"))
                mysyslog(LOG_WARNING, "cgroupfs v1: Failed to escape to init's cgroup.\n", NULL);
 }
@@ -1538,6 +1586,500 @@ static bool get_uid_gid(const char *user, uid_t *uid, gid_t *gid)
        return true;
 }
 
+/* Check if cgroup belongs to our uid and gid. If so, reuse it. */
+static bool cg_belongs_to_uid_gid(const char *path, uid_t uid, gid_t gid)
+{
+       struct stat statbuf;
+
+       if (stat(path, &statbuf) < 0)
+               return false;
+
+       if (!(statbuf.st_uid == uid) || !(statbuf.st_gid == gid))
+               return false;
+
+       return true;
+}
+
+/* Create cpumask from cpulist aka turn:
+ *
+ *     0,2-3
+ *
+ *  into bit array
+ *
+ *     1 0 1 1
+ */
+static uint32_t *cg_cpumask(char *buf, size_t nbits)
+{
+       char *token;
+       char *saveptr = NULL;
+       size_t arrlen = BITS_TO_LONGS(nbits);
+       uint32_t *bitarr = calloc(arrlen, sizeof(uint32_t));
+       if (!bitarr)
+               return NULL;
+
+       for (; (token = strtok_r(buf, ",", &saveptr)); buf = NULL) {
+               errno = 0;
+               unsigned start = strtoul(token, NULL, 0);
+               unsigned end = start;
+
+               char *range = strchr(token, '-');
+               if (range)
+                       end = strtoul(range + 1, NULL, 0);
+               if (!(start <= end)) {
+                       free(bitarr);
+                       return NULL;
+               }
+
+               if (end >= nbits) {
+                       free(bitarr);
+                       return NULL;
+               }
+
+               while (start <= end)
+                       set_bit(start++, bitarr);
+       }
+
+       return bitarr;
+}
+
+static char *string_join(const char *sep, const char **parts, bool use_as_prefix)
+{
+       char *result;
+       char **p;
+       size_t sep_len = strlen(sep);
+       size_t result_len = use_as_prefix * sep_len;
+
+       if (!parts)
+               return NULL;
+
+       /* calculate new string length */
+       for (p = (char **)parts; *p; p++)
+               result_len += (p > (char **)parts) * sep_len + strlen(*p);
+
+       result = calloc(result_len + 1, 1);
+       if (!result)
+               return NULL;
+
+       if (use_as_prefix)
+               strcpy(result, sep);
+       for (p = (char **)parts; *p; p++) {
+               if (p > (char **)parts)
+                       strcat(result, sep);
+               strcat(result, *p);
+       }
+
+       return result;
+}
+
+/* The largest integer that can fit into long int is 2^64. This is a
+ * 20-digit number.
+ */
+#define __IN_TO_STR_LEN 21
+/* Turn cpumask into simple, comma-separated cpulist. */
+static char *cg_cpumask_to_cpulist(uint32_t *bitarr, size_t nbits)
+{
+       size_t i;
+       int ret;
+       char numstr[__IN_TO_STR_LEN] = {0};
+       char **cpulist = NULL;
+
+       for (i = 0; i <= nbits; i++) {
+               if (is_set(i, bitarr)) {
+                       ret = snprintf(numstr, __IN_TO_STR_LEN, "%zu", i);
+                       if (ret < 0 || (size_t)ret >= __IN_TO_STR_LEN) {
+                               free_string_list(cpulist);
+                               return NULL;
+                       }
+                       must_append_string(&cpulist, numstr);
+               }
+       }
+       return string_join(",", (const char **)cpulist, false);
+}
+
+static ssize_t cg_get_max_cpus(char *cpulist)
+{
+       char *c1, *c2;
+       char *maxcpus = cpulist;
+       size_t cpus = 0;
+
+       c1 = strrchr(maxcpus, ',');
+       if (c1)
+               c1++;
+
+       c2 = strrchr(maxcpus, '-');
+       if (c2)
+               c2++;
+
+       if (!c1 && !c2)
+               c1 = maxcpus;
+       else if (c1 < c2)
+               c1 = c2;
+
+       /* If the above logic is correct, c1 should always hold a valid string
+        * here.
+        */
+
+       errno = 0;
+       cpus = strtoul(c1, NULL, 0);
+       if (errno != 0)
+               return -1;
+
+       return cpus;
+}
+
+static ssize_t write_nointr(int fd, const void* buf, size_t count)
+{
+       ssize_t ret;
+again:
+       ret = write(fd, buf, count);
+       if (ret < 0 && errno == EINTR)
+               goto again;
+       return ret;
+}
+
+static int write_to_file(const char *filename, const void* buf, size_t count, bool add_newline)
+{
+       int fd, saved_errno;
+       ssize_t ret;
+
+       fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, 0666);
+       if (fd < 0)
+               return -1;
+       ret = write_nointr(fd, buf, count);
+       if (ret < 0)
+               goto out_error;
+       if ((size_t)ret != count)
+               goto out_error;
+       if (add_newline) {
+               ret = write_nointr(fd, "\n", 1);
+               if (ret != 1)
+                       goto out_error;
+       }
+       close(fd);
+       return 0;
+
+out_error:
+       saved_errno = errno;
+       close(fd);
+       errno = saved_errno;
+       return -1;
+}
+
+#define __ISOL_CPUS "/sys/devices/system/cpu/isolated"
+static bool cg_filter_and_set_cpus(char *path, bool am_initialized)
+{
+       char *lastslash, *fpath, oldv;
+       int ret;
+       ssize_t i;
+
+       ssize_t maxposs = 0, maxisol = 0;
+       char *cpulist = NULL, *posscpus = NULL, *isolcpus = NULL;
+       uint32_t *possmask = NULL, *isolmask = NULL;
+       bool bret = false, flipped_bit = false;
+
+       lastslash = strrchr(path, '/');
+       if (!lastslash) { // bug...  this shouldn't be possible
+               lxcfs_debug("Invalid path: %s.\n", path);
+               return bret;
+       }
+       oldv = *lastslash;
+       *lastslash = '\0';
+       fpath = must_make_path(path, "cpuset.cpus", NULL);
+       posscpus = read_file(fpath);
+       if (!posscpus) {
+               lxcfs_debug("Could not read file: %s.\n", fpath);
+               goto on_error;
+       }
+
+       /* Get maximum number of cpus found in possible cpuset. */
+       maxposs = cg_get_max_cpus(posscpus);
+       if (maxposs < 0)
+               goto on_error;
+
+       if (!file_exists(__ISOL_CPUS)) {
+               /* This system doesn't expose isolated cpus. */
+               lxcfs_debug("%s", "Path: "__ISOL_CPUS" to read isolated cpus from does not exist.\n");
+               cpulist = posscpus;
+               /* No isolated cpus but we weren't already initialized by
+                * someone. We should simply copy the parents cpuset.cpus
+                * values.
+                */
+               if (!am_initialized) {
+                       lxcfs_debug("%s", "Copying cpuset of parent cgroup.\n");
+                       goto copy_parent;
+               }
+               /* No isolated cpus but we were already initialized by someone.
+                * Nothing more to do for us.
+                */
+               goto on_success;
+       }
+
+       isolcpus = read_file(__ISOL_CPUS);
+       if (!isolcpus) {
+               lxcfs_debug("%s", "Could not read file "__ISOL_CPUS"\n");
+               goto on_error;
+       }
+       if (!isdigit(isolcpus[0])) {
+               lxcfs_debug("%s", "No isolated cpus detected.\n");
+               cpulist = posscpus;
+               /* No isolated cpus but we weren't already initialized by
+                * someone. We should simply copy the parents cpuset.cpus
+                * values.
+                */
+               if (!am_initialized) {
+                       lxcfs_debug("%s", "Copying cpuset of parent cgroup.\n");
+                       goto copy_parent;
+               }
+               /* No isolated cpus but we were already initialized by someone.
+                * Nothing more to do for us.
+                */
+               goto on_success;
+       }
+
+       /* Get maximum number of cpus found in isolated cpuset. */
+       maxisol = cg_get_max_cpus(isolcpus);
+       if (maxisol < 0)
+               goto on_error;
+
+       if (maxposs < maxisol)
+               maxposs = maxisol;
+       maxposs++;
+
+       possmask = cg_cpumask(posscpus, maxposs);
+       if (!possmask) {
+               lxcfs_debug("%s", "Could not create cpumask for all possible cpus.\n");
+               goto on_error;
+       }
+
+       isolmask = cg_cpumask(isolcpus, maxposs);
+       if (!isolmask) {
+               lxcfs_debug("%s", "Could not create cpumask for all isolated cpus.\n");
+               goto on_error;
+       }
+
+       for (i = 0; i <= maxposs; i++) {
+               if (is_set(i, isolmask) && is_set(i, possmask)) {
+                       flipped_bit = true;
+                       clear_bit(i, possmask);
+               }
+       }
+
+       if (!flipped_bit) {
+               lxcfs_debug("%s", "No isolated cpus present in cpuset.\n");
+               goto on_success;
+       }
+       lxcfs_debug("%s", "Removed isolated cpus from cpuset.\n");
+
+       cpulist = cg_cpumask_to_cpulist(possmask, maxposs);
+       if (!cpulist) {
+               lxcfs_debug("%s", "Could not create cpu list.\n");
+               goto on_error;
+       }
+
+copy_parent:
+       *lastslash = oldv;
+       fpath = must_make_path(path, "cpuset.cpus", NULL);
+       ret = write_to_file(fpath, cpulist, strlen(cpulist), false);
+       if (ret < 0) {
+               lxcfs_debug("Could not write cpu list to: %s.\n", fpath);
+               goto on_error;
+       }
+
+on_success:
+       bret = true;
+
+on_error:
+       free(fpath);
+
+       free(isolcpus);
+       free(isolmask);
+
+       if (posscpus != cpulist)
+               free(posscpus);
+       free(possmask);
+
+       free(cpulist);
+       return bret;
+}
+
+int read_from_file(const char *filename, void* buf, size_t count)
+{
+       int fd = -1, saved_errno;
+       ssize_t ret;
+
+       fd = open(filename, O_RDONLY | O_CLOEXEC);
+       if (fd < 0)
+               return -1;
+
+       if (!buf || !count) {
+               char buf2[100];
+               size_t count2 = 0;
+               while ((ret = read(fd, buf2, 100)) > 0)
+                       count2 += ret;
+               if (ret >= 0)
+                       ret = count2;
+       } else {
+               memset(buf, 0, count);
+               ret = read(fd, buf, count);
+       }
+
+       if (ret < 0)
+               lxcfs_debug("read %s: %s", filename, strerror(errno));
+
+       saved_errno = errno;
+       close(fd);
+       errno = saved_errno;
+       return ret;
+}
+
+/* Copy contents of parent(@path)/@file to @path/@file */
+static bool cg_copy_parent_file(char *path, char *file)
+{
+       char *lastslash, *value = NULL, *fpath, oldv;
+       int len = 0;
+       int ret;
+
+       lastslash = strrchr(path, '/');
+       if (!lastslash) { // bug...  this shouldn't be possible
+               lxcfs_debug("cgfsng:copy_parent_file: bad path %s", path);
+               return false;
+       }
+       oldv = *lastslash;
+       *lastslash = '\0';
+       fpath = must_make_path(path, file, NULL);
+       len = read_from_file(fpath, NULL, 0);
+       if (len <= 0)
+               goto bad;
+       value = must_alloc(len + 1);
+       if (read_from_file(fpath, value, len) != len)
+               goto bad;
+       free(fpath);
+       *lastslash = oldv;
+       fpath = must_make_path(path, file, NULL);
+       ret = write_to_file(fpath, value, len, false);
+       if (ret < 0)
+               lxcfs_debug("Unable to write %s to %s", value, fpath);
+       free(fpath);
+       free(value);
+       return ret >= 0;
+
+bad:
+       lxcfs_debug("Error reading '%s'", fpath);
+       free(fpath);
+       free(value);
+       return false;
+}
+
+/* In case systemd hasn't already placed us in a user slice for the cpuset v1
+ * controller we will reside in the root cgroup. This means that
+ * cgroup.clone_children will not have been initialized for us so we need to do
+ * it.
+ */
+static bool cgv1_handle_root_cpuset_hierarchy(struct cgv1_hierarchy *h)
+{
+       char *clonechildrenpath, v;
+
+       if (!string_in_list(h->controllers, "cpuset"))
+               return true;
+
+       clonechildrenpath = must_make_path(h->mountpoint, "cgroup.clone_children", NULL);
+
+       if (read_from_file(clonechildrenpath, &v, 1) < 0) {
+               lxcfs_debug("Failed to read '%s'", clonechildrenpath);
+               free(clonechildrenpath);
+               return false;
+       }
+
+       if (v == '1') {  /* already set for us by someone else */
+               free(clonechildrenpath);
+               return true;
+       }
+
+       if (write_to_file(clonechildrenpath, "1", 1, false) < 0) {
+               /* Set clone_children so children inherit our settings */
+               lxcfs_debug("Failed to write 1 to %s", clonechildrenpath);
+               free(clonechildrenpath);
+               return false;
+       }
+       free(clonechildrenpath);
+       return true;
+}
+
+/*
+ * Initialize the cpuset hierarchy in first directory of @gname and
+ * set cgroup.clone_children so that children inherit settings.
+ * Since the h->base_path is populated by init or ourselves, we know
+ * it is already initialized.
+ */
+static bool cgv1_handle_cpuset_hierarchy(struct cgv1_hierarchy *h,
+                                        const char *cgroup)
+{
+       char *cgpath, *clonechildrenpath, v, *slash;
+
+       if (!string_in_list(h->controllers, "cpuset"))
+               return true;
+
+       if (*cgroup == '/')
+               cgroup++;
+       slash = strchr(cgroup, '/');
+       if (slash)
+               *slash = '\0';
+
+       cgpath = must_make_path(h->mountpoint, h->base_cgroup, cgroup, NULL);
+       if (slash)
+               *slash = '/';
+       if (mkdir(cgpath, 0755) < 0 && errno != EEXIST) {
+               lxcfs_debug("Failed to create '%s'", cgpath);
+               free(cgpath);
+               return false;
+       }
+       clonechildrenpath = must_make_path(cgpath, "cgroup.clone_children", NULL);
+       if (!file_exists(clonechildrenpath)) { /* unified hierarchy doesn't have clone_children */
+               free(clonechildrenpath);
+               free(cgpath);
+               return true;
+       }
+       if (read_from_file(clonechildrenpath, &v, 1) < 0) {
+               lxcfs_debug("Failed to read '%s'", clonechildrenpath);
+               free(clonechildrenpath);
+               free(cgpath);
+               return false;
+       }
+
+       /* Make sure any isolated cpus are removed from cpuset.cpus. */
+       if (!cg_filter_and_set_cpus(cgpath, v == '1')) {
+               lxcfs_debug("%s", "Failed to remove isolated cpus.\n");
+               free(clonechildrenpath);
+               free(cgpath);
+               return false;
+       }
+
+       if (v == '1') {  /* already set for us by someone else */
+               lxcfs_debug("%s", "\"cgroup.clone_children\" was already set to \"1\".\n");
+               free(clonechildrenpath);
+               free(cgpath);
+               return true;
+       }
+
+       /* copy parent's settings */
+       if (!cg_copy_parent_file(cgpath, "cpuset.mems")) {
+               lxcfs_debug("%s", "Failed to copy \"cpuset.mems\" settings.\n");
+               free(cgpath);
+               free(clonechildrenpath);
+               return false;
+       }
+       free(cgpath);
+
+       if (write_to_file(clonechildrenpath, "1", 1, false) < 0) {
+               /* Set clone_children so children inherit our settings */
+               lxcfs_debug("Failed to write 1 to %s", clonechildrenpath);
+               free(clonechildrenpath);
+               return false;
+       }
+       free(clonechildrenpath);
+       return true;
+}
+
 /* Create and chown @cgroup for all given controllers in a cgroupfs v1 hierarchy
  * (For example, create @cgroup for the cpu and cpuacct controller mounted into
  * /sys/fs/cgroup/cpu,cpuacct). Check if the path already exists and report back
@@ -1552,10 +2094,15 @@ static bool cgv1_create_one(struct cgv1_hierarchy *h, const char *cgroup, uid_t
        struct cgv1_hierarchy *it;
        bool created = false;
 
+       *existed = false;
        it = h;
        for (controller = it->controllers; controller && *controller;
             controller++) {
                created = false;
+
+               if (!cgv1_handle_cpuset_hierarchy(it, cgroup))
+                       return false;
+
                /* If systemd has already created a cgroup for us, keep using
                 * it.
                 */
@@ -1587,10 +2134,14 @@ static bool cgv1_create_one(struct cgv1_hierarchy *h, const char *cgroup, uid_t
                path = must_make_path(it->mountpoint, it->init_cgroup, cgroup, NULL);
                lxcfs_debug("Constructing path: %s.\n", path);
                if (file_exists(path)) {
-                       *existed = true;
-                       lxcfs_debug("%s existed.\n", path);
+                       bool our_cg = cg_belongs_to_uid_gid(path, uid, gid);
+                       lxcfs_debug("%s existed and does %shave our uid: %d and gid: %d.\n", path, our_cg ? "" : "not ", uid, gid);
                        free(path);
-                       return false;
+                       if (our_cg)
+                               *existed = false;
+                       else
+                               *existed = true;
+                       return our_cg;
                }
                created = mkdir_p(it->mountpoint, path);
                if (!created) {
@@ -1598,16 +2149,15 @@ static bool cgv1_create_one(struct cgv1_hierarchy *h, const char *cgroup, uid_t
                        continue;
                }
                if (chown(path, uid, gid) < 0)
-                       lxcfs_debug("Failed to chown %s to %d:%d: %m.\n", path,
-                                   (int)uid, (int)gid);
+                       mysyslog(LOG_WARNING,
+                                "Failed to chown %s to %d:%d: %s.\n", path,
+                                (int)uid, (int)gid, strerror(errno), NULL);
+               lxcfs_debug("Chowned %s to %d:%d.\n", path, (int)uid, (int)gid);
                free(path);
                break;
        }
 
-       if (!created)
-               return false;
-
-       return true;
+       return created;
 }
 
 /* Try to remove @cgroup for all given controllers in a cgroupfs v1 hierarchy
@@ -1701,6 +2251,8 @@ static bool cgv2_create(const char *cgroup, uid_t uid, gid_t gid, bool *existed)
        struct cgv2_hierarchy *v2;
        bool created = false;
 
+       *existed = false;
+
        if (!cgv2_hierarchies || !(*cgv2_hierarchies)->create_rw_cgroup)
                return true;
 
@@ -1731,10 +2283,14 @@ static bool cgv2_create(const char *cgroup, uid_t uid, gid_t gid, bool *existed)
        path = must_make_path(v2->mountpoint, v2->base_cgroup, cgroup, NULL);
        lxcfs_debug("Constructing path \"%s\".\n", path);
        if (file_exists(path)) {
-               *existed = true;
-               lxcfs_debug("%s existed.\n", path);
+               bool our_cg = cg_belongs_to_uid_gid(path, uid, gid);
+               lxcfs_debug("%s existed and does %shave our uid: %d and gid: %d.\n", path, our_cg ? "" : "not ", uid, gid);
                free(path);
-               return false;
+               if (our_cg)
+                       *existed = false;
+               else
+                       *existed = true;
+               return our_cg;
        }
 
        created = mkdir_p(v2->mountpoint, path);
@@ -1744,8 +2300,9 @@ static bool cgv2_create(const char *cgroup, uid_t uid, gid_t gid, bool *existed)
        }
 
        if (chown(path, uid, gid) < 0)
-               mysyslog(LOG_WARNING, "Failed to chown %s to %d:%d: %m.\n",
-                        path, (int)uid, (int)gid, NULL);
+               mysyslog(LOG_WARNING, "Failed to chown %s to %d:%d: %s.\n",
+                        path, (int)uid, (int)gid, strerror(errno), NULL);
+       lxcfs_debug("Chowned %s to %d:%d.\n", path, (int)uid, (int)gid);
        free(path);
 
        return true;