]> git.proxmox.com Git - mirror_lxcfs.git/blobdiff - bindings.c
bindings: caller_may_see_dir()
[mirror_lxcfs.git] / bindings.c
index a62215759dd0a842db56c650327a4e854e40ff6b..fb905c19c62daa8736e6d495e2b5dc1c1d6e7200 100644 (file)
@@ -307,11 +307,11 @@ static void append_line(char **contents, size_t *len, char *line, ssize_t linele
        *len = newlen;
 }
 
-static char *slurp_file(const char *from)
+static char *slurp_file(const char *from, int fd)
 {
        char *line = NULL;
        char *contents = NULL;
-       FILE *f = fopen(from, "r");
+       FILE *f = fdopen(fd, "r");
        size_t len = 0, fulllen = 0;
        ssize_t linelen;
 
@@ -350,12 +350,6 @@ static bool write_string(const char *fnam, const char *string)
        return true;
 }
 
-/*
- * hierarchies, i.e. 'cpu,cpuacct'
- */
-char **hierarchies;
-int num_hierarchies;
-
 struct cgfs_files {
        char *name;
        uint32_t uid, gid;
@@ -375,7 +369,7 @@ static bool store_hierarchy(char *stridx, char *h)
                }
                hierarchies = tmp;
        }
-       
+
        hierarchies[num_hierarchies++] = must_copy_string(h);
        return true;
 }
@@ -384,7 +378,7 @@ static void print_subsystems(void)
 {
        int i;
 
-       fprintf(stderr, "hierarchies:");
+       fprintf(stderr, "hierarchies:\n");
        for (i = 0; i < num_hierarchies; i++) {
                if (hierarchies[i])
                        fprintf(stderr, " %d: %s\n", i, hierarchies[i]);
@@ -411,17 +405,25 @@ static bool in_comma_list(const char *needle, const char *haystack)
 }
 
 /* do we need to do any massaging here?  I'm not sure... */
-static char *find_mounted_controller(const char *controller)
+/* Return the mounted controller and store the corresponding open file descriptor
+ * referring to the controller mountpoint in the private lxcfs namespace in
+ * @cfd.
+ */
+static char *find_mounted_controller(const char *controller, int *cfd)
 {
        int i;
 
        for (i = 0; i < num_hierarchies; i++) {
                if (!hierarchies[i])
                        continue;
-               if (strcmp(hierarchies[i], controller) == 0)
+               if (strcmp(hierarchies[i], controller) == 0) {
+                       *cfd = fd_hierarchies[i];
                        return hierarchies[i];
-               if (in_comma_list(controller, hierarchies[i]))
+               }
+               if (in_comma_list(controller, hierarchies[i])) {
+                       *cfd = fd_hierarchies[i];
                        return hierarchies[i];
+               }
        }
 
        return NULL;
@@ -430,16 +432,17 @@ static char *find_mounted_controller(const char *controller)
 bool cgfs_set_value(const char *controller, const char *cgroup, const char *file,
                const char *value)
 {
+       int cfd;
        size_t len;
-       char *fnam, *tmpc = find_mounted_controller(controller);
+       char *fnam, *tmpc = find_mounted_controller(controller, &cfd);
 
        if (!tmpc)
                return false;
-       /* basedir / tmpc / cgroup / file \0 */
-       len = strlen(basedir) + strlen(tmpc) + strlen(cgroup) + strlen(file) + 4;
+       /* BASEDIR / tmpc / cgroup / file \0 */
+       len = strlen(BASEDIR) + strlen(tmpc) + strlen(cgroup) + strlen(file) + 4;
        fnam = alloca(len);
-       snprintf(fnam, len, "%s/%s/%s/%s", basedir, tmpc, cgroup, file);
-       
+       snprintf(fnam, len, "%s/%s/%s/%s", BASEDIR, tmpc, cgroup, file);
+
        return write_string(fnam, value);
 }
 
@@ -481,15 +484,16 @@ static void chown_all_cgroup_files(const char *dirname, uid_t uid, gid_t gid)
 
 int cgfs_create(const char *controller, const char *cg, uid_t uid, gid_t gid)
 {
+       int cfd;
        size_t len;
-       char *dirnam, *tmpc = find_mounted_controller(controller);
+       char *dirnam, *tmpc = find_mounted_controller(controller, &cfd);
 
        if (!tmpc)
                return -EINVAL;
-       /* basedir / tmpc / cg \0 */
-       len = strlen(basedir) + strlen(tmpc) + strlen(cg) + 3;
+       /* BASEDIR / tmpc / cg \0 */
+       len = strlen(BASEDIR) + strlen(tmpc) + strlen(cg) + 3;
        dirnam = alloca(len);
-       snprintf(dirnam, len, "%s/%s/%s", basedir,tmpc, cg);
+       snprintf(dirnam, len, "%s/%s/%s", BASEDIR,tmpc, cg);
 
        if (mkdir(dirnam, 0755) < 0)
                return -errno;
@@ -571,29 +575,31 @@ static bool recursive_rmdir(const char *dirname)
 
 bool cgfs_remove(const char *controller, const char *cg)
 {
+       int cfd;
        size_t len;
-       char *dirnam, *tmpc = find_mounted_controller(controller);
+       char *dirnam, *tmpc = find_mounted_controller(controller, &cfd);
 
        if (!tmpc)
                return false;
-       /* basedir / tmpc / cg \0 */
-       len = strlen(basedir) + strlen(tmpc) + strlen(cg) + 3;
+       /* BASEDIR / tmpc / cg \0 */
+       len = strlen(BASEDIR) + strlen(tmpc) + strlen(cg) + 3;
        dirnam = alloca(len);
-       snprintf(dirnam, len, "%s/%s/%s", basedir,tmpc, cg);
+       snprintf(dirnam, len, "%s/%s/%s", BASEDIR,tmpc, cg);
        return recursive_rmdir(dirnam);
 }
 
 bool cgfs_chmod_file(const char *controller, const char *file, mode_t mode)
 {
+       int cfd;
        size_t len;
-       char *pathname, *tmpc = find_mounted_controller(controller);
+       char *pathname, *tmpc = find_mounted_controller(controller, &cfd);
 
        if (!tmpc)
                return false;
-       /* basedir / tmpc / file \0 */
-       len = strlen(basedir) + strlen(tmpc) + strlen(file) + 3;
+       /* BASEDIR / tmpc / file \0 */
+       len = strlen(BASEDIR) + strlen(tmpc) + strlen(file) + 3;
        pathname = alloca(len);
-       snprintf(pathname, len, "%s/%s/%s", basedir, tmpc, file);
+       snprintf(pathname, len, "%s/%s/%s", BASEDIR, tmpc, file);
        if (chmod(pathname, mode) < 0)
                return false;
        return true;
@@ -617,15 +623,16 @@ static int chown_tasks_files(const char *dirname, uid_t uid, gid_t gid)
 
 int cgfs_chown_file(const char *controller, const char *file, uid_t uid, gid_t gid)
 {
+       int cfd;
        size_t len;
-       char *pathname, *tmpc = find_mounted_controller(controller);
+       char *pathname, *tmpc = find_mounted_controller(controller, &cfd);
 
        if (!tmpc)
                return -EINVAL;
-       /* basedir / tmpc / file \0 */
-       len = strlen(basedir) + strlen(tmpc) + strlen(file) + 3;
+       /* BASEDIR / tmpc / file \0 */
+       len = strlen(BASEDIR) + strlen(tmpc) + strlen(file) + 3;
        pathname = alloca(len);
-       snprintf(pathname, len, "%s/%s/%s", basedir, tmpc, file);
+       snprintf(pathname, len, "%s/%s/%s", BASEDIR, tmpc, file);
        if (chown(pathname, uid, gid) < 0)
                return -errno;
 
@@ -638,15 +645,16 @@ int cgfs_chown_file(const char *controller, const char *file, uid_t uid, gid_t g
 
 FILE *open_pids_file(const char *controller, const char *cgroup)
 {
+       int cfd;
        size_t len;
-       char *pathname, *tmpc = find_mounted_controller(controller);
+       char *pathname, *tmpc = find_mounted_controller(controller, &cfd);
 
        if (!tmpc)
                return NULL;
-       /* basedir / tmpc / cgroup / "cgroup.procs" \0 */
-       len = strlen(basedir) + strlen(tmpc) + strlen(cgroup) + 4 + strlen("cgroup.procs");
+       /* BASEDIR / tmpc / cgroup / "cgroup.procs" \0 */
+       len = strlen(BASEDIR) + strlen(tmpc) + strlen(cgroup) + 4 + strlen("cgroup.procs");
        pathname = alloca(len);
-       snprintf(pathname, len, "%s/%s/%s/cgroup.procs", basedir, tmpc, cgroup);
+       snprintf(pathname, len, "%s/%s/%s/cgroup.procs", BASEDIR, tmpc, cgroup);
        return fopen(pathname, "w");
 }
 
@@ -654,45 +662,50 @@ static bool cgfs_iterate_cgroup(const char *controller, const char *cgroup, bool
                                 void ***list, size_t typesize,
                                 void* (*iterator)(const char*, const char*, const char*))
 {
+       int cfd, fd, ret;
        size_t len;
-       char *dirname, *tmpc = find_mounted_controller(controller);
+       char *cg, *tmpc;
        char pathname[MAXPATHLEN];
        size_t sz = 0, asz = 0;
-       struct dirent dirent, *direntp;
+       struct dirent *dirent;
        DIR *dir;
-       int ret;
 
+       tmpc = find_mounted_controller(controller, &cfd);
        *list = NULL;
        if (!tmpc)
                return false;
 
-       /* basedir / tmpc / cgroup \0 */
-       len = strlen(basedir) + strlen(tmpc) + strlen(cgroup) + 3;
-       dirname = alloca(len);
-       snprintf(dirname, len, "%s/%s/%s", basedir, tmpc, cgroup);
+       /* Make sure we pass a relative path to openat(). */
+       len = strlen(cgroup) + 1 /* . */ + 1 /* \0 */;
+       cg = alloca(len);
+       ret = snprintf(cg, len, "%s%s", *cgroup == '/' ? "." : "", cgroup);
+       if (ret < 0 || (size_t)ret >= len) {
+               fprintf(stderr, "%s: pathname too long under %s\n", __func__, cgroup);
+               return false;
+       }
 
-       dir = opendir(dirname);
+       fd = openat(cfd, cg, O_DIRECTORY);
+       if (fd < 0)
+               return false;
+
+       dir = fdopendir(fd);
        if (!dir)
                return false;
 
-       while (!readdir_r(dir, &dirent, &direntp)) {
+       while ((dirent = readdir(dir))) {
                struct stat mystat;
-               int rc;
-
-               if (!direntp)
-                       break;
 
-               if (!strcmp(direntp->d_name, ".") ||
-                   !strcmp(direntp->d_name, ".."))
+               if (!strcmp(dirent->d_name, ".") ||
+                   !strcmp(dirent->d_name, ".."))
                        continue;
 
-               rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name);
-               if (rc < 0 || rc >= MAXPATHLEN) {
-                       fprintf(stderr, "%s: pathname too long under %s\n", __func__, dirname);
+               ret = snprintf(pathname, MAXPATHLEN, "%s/%s", cg, dirent->d_name);
+               if (ret < 0 || ret >= MAXPATHLEN) {
+                       fprintf(stderr, "%s: pathname too long under %s\n", __func__, cg);
                        continue;
                }
 
-               ret = lstat(pathname, &mystat);
+               ret = fstatat(cfd, pathname, &mystat, AT_SYMLINK_NOFOLLOW);
                if (ret) {
                        fprintf(stderr, "%s: failed to stat %s: %s\n", __func__, pathname, strerror(errno));
                        continue;
@@ -709,12 +722,12 @@ static bool cgfs_iterate_cgroup(const char *controller, const char *cgroup, bool
                        } while  (!tmp);
                        *list = tmp;
                }
-               (*list)[sz] = (*iterator)(controller, cgroup, direntp->d_name);
+               (*list)[sz] = (*iterator)(controller, cg, dirent->d_name);
                (*list)[sz+1] = NULL;
                sz++;
        }
        if (closedir(dir) < 0) {
-               fprintf(stderr, "%s: failed closedir for %s: %s\n", __func__, dirname, strerror(errno));
+               fprintf(stderr, "%s: failed closedir for %s: %s\n", __func__, cgroup, strerror(errno));
                return false;
        }
        return true;
@@ -756,27 +769,34 @@ void free_keys(struct cgfs_files **keys)
 
 bool cgfs_get_value(const char *controller, const char *cgroup, const char *file, char **value)
 {
+       int ret, fd, cfd;
        size_t len;
-       char *fnam, *tmpc = find_mounted_controller(controller);
+       char *fnam, *tmpc = find_mounted_controller(controller, &cfd);
 
        if (!tmpc)
                return false;
-       /* basedir / tmpc / cgroup / file \0 */
-       len = strlen(basedir) + strlen(tmpc) + strlen(cgroup) + strlen(file) + 4;
+       /* . + /cgroup + / + file + \0 */
+       len = strlen(cgroup) + strlen(file) + 3;
        fnam = alloca(len);
-       snprintf(fnam, len, "%s/%s/%s/%s", basedir, tmpc, cgroup, file);
+       ret = snprintf(fnam, len, "%s%s/%s", *cgroup == '/' ? "." : "", cgroup, file);
+       if (ret < 0 || (size_t)ret >= len)
+               return NULL;
+
+       fd = openat(cfd, fnam, O_RDONLY);
+       if (fd < 0)
+               return NULL;
 
-       *value = slurp_file(fnam);
+       *value = slurp_file(fnam, fd);
        return *value != NULL;
 }
 
 struct cgfs_files *cgfs_get_key(const char *controller, const char *cgroup, const char *file)
 {
+       int ret, cfd;
        size_t len;
-       char *fnam, *tmpc = find_mounted_controller(controller);
+       char *fnam, *tmpc = find_mounted_controller(controller, &cfd);
        struct stat sb;
        struct cgfs_files *newkey;
-       int ret;
 
        if (!tmpc)
                return false;
@@ -787,15 +807,15 @@ struct cgfs_files *cgfs_get_key(const char *controller, const char *cgroup, cons
        if (file && index(file, '/'))
                return NULL;
 
-       /* basedir / tmpc / cgroup / file \0 */
-       len = strlen(basedir) + strlen(tmpc) + strlen(cgroup) + 3;
+       /* . + /cgroup + / + file + \0 */
+       len = strlen(cgroup) + 3;
        if (file)
                len += strlen(file) + 1;
        fnam = alloca(len);
-       snprintf(fnam, len, "%s/%s/%s%s%s", basedir, tmpc, cgroup,
-               file ? "/" : "", file ? file : "");
+       snprintf(fnam, len, "%s%s%s%s", *cgroup == '/' ? "." : "", cgroup,
+                file ? "/" : "", file ? file : "");
 
-       ret = stat(fnam, &sb);
+       ret = fstatat(cfd, fnam, &sb, 0);
        if (ret < 0)
                return NULL;
 
@@ -831,19 +851,23 @@ bool cgfs_list_keys(const char *controller, const char *cgroup, struct cgfs_file
 }
 
 bool is_child_cgroup(const char *controller, const char *cgroup, const char *f)
-{      size_t len;
-       char *fnam, *tmpc = find_mounted_controller(controller);
+{
+       int cfd;
+       size_t len;
+       char *fnam, *tmpc = find_mounted_controller(controller, &cfd);
        int ret;
        struct stat sb;
 
        if (!tmpc)
                return false;
-       /* basedir / tmpc / cgroup / f \0 */
-       len = strlen(basedir) + strlen(tmpc) + strlen(cgroup) + strlen(f) + 4;
+       /* . + /cgroup + / + f + \0 */
+       len = strlen(cgroup) + strlen(f) + 3;
        fnam = alloca(len);
-       snprintf(fnam, len, "%s/%s/%s/%s", basedir, tmpc, cgroup, f);
+       ret = snprintf(fnam, len, "%s%s/%s", *cgroup == '/' ? "." : "", cgroup, f);
+       if (ret < 0 || (size_t)ret >= len)
+               return false;
 
-       ret = stat(fnam, &sb);
+       ret = fstatat(cfd, fnam, &sb, 0);
        if (ret < 0 || !S_ISDIR(sb.st_mode))
                return false;
        return true;
@@ -1179,13 +1203,14 @@ static void stripnewline(char *x)
 
 static char *get_pid_cgroup(pid_t pid, const char *contrl)
 {
+       int cfd;
        char fnam[PROCLEN];
        FILE *f;
        char *answer = NULL;
        char *line = NULL;
        size_t len = 0;
        int ret;
-       const char *h = find_mounted_controller(contrl);
+       const char *h = find_mounted_controller(contrl, &cfd);
        if (!h)
                return NULL;
 
@@ -1298,10 +1323,18 @@ static bool caller_is_in_ancestor(pid_t pid, const char *contrl, const char *cg,
        prune_init_slice(c2);
 
        /*
-        * callers pass in '/' for root cgroup, otherwise they pass
-        * in a cgroup without leading '/'
+        * callers pass in '/' or './' (openat()) for root cgroup, otherwise
+        * they pass in a cgroup without leading '/'
+        *
+        * The original line here was:
+        *      linecmp = *cg == '/' ? c2 : c2+1;
+        * TODO: I'm not sure why you'd want to increment when *cg != '/'?
+        *       Serge, do you know?
         */
-       linecmp = *cg == '/' ? c2 : c2+1;
+       if (*cg == '/' || !strncmp(cg, "./", 2))
+               linecmp = c2;
+       else
+               linecmp = c2 + 1;
        if (strncmp(linecmp, cg, strlen(linecmp)) != 0) {
                if (nextcg) {
                        *nextcg = get_next_cgroup_dir(linecmp, cg);
@@ -1324,7 +1357,7 @@ static bool caller_may_see_dir(pid_t pid, const char *contrl, const char *cg)
        char *c2, *task_cg;
        size_t target_len, task_len;
 
-       if (strcmp(cg, "/") == 0)
+       if (strcmp(cg, "/") == 0 || strcmp(cg, "./") == 0)
                return true;
 
        c2 = get_pid_cgroup(pid, contrl);
@@ -1682,22 +1715,29 @@ out:
        return ret;
 }
 
-static void do_release_file_info(struct file_info *f)
+static void do_release_file_info(struct fuse_file_info *fi)
 {
+       struct file_info *f = (struct file_info *)fi->fh;
+
        if (!f)
                return;
+
+       fi->fh = 0;
+
        free(f->controller);
+       f->controller = NULL;
        free(f->cgroup);
+       f->cgroup = NULL;
        free(f->file);
+       f->file = NULL;
        free(f->buf);
+       f->buf = NULL;
        free(f);
 }
 
 int cg_releasedir(const char *path, struct fuse_file_info *fi)
 {
-       struct file_info *d = (struct file_info *)fi->fh;
-
-       do_release_file_info(d);
+       do_release_file_info(fi);
        return 0;
 }
 
@@ -1784,8 +1824,12 @@ int cg_access(const char *path, int mode)
        if (!controller)
                return -EIO;
        cgroup = find_cgroup_in_path(path);
-       if (!cgroup)
-               return -EINVAL;
+       if (!cgroup) {
+               // access("/sys/fs/cgroup/systemd", mode) - rx allowed, w not
+               if ((mode & W_OK) == 0)
+                       return 0;
+               return -EACCES;
+       }
 
        get_cgdir_and_path(cgroup, &cgdir, &last);
        if (!last) {
@@ -1798,7 +1842,10 @@ int cg_access(const char *path, int mode)
 
        k = cgfs_get_key(controller, path1, path2);
        if (!k) {
-               ret = -EINVAL;
+               if ((mode & W_OK) == 0)
+                       ret = 0;
+               else
+                       ret = -EACCES;
                goto out;
        }
        free_key(k);
@@ -1824,9 +1871,7 @@ out:
 
 int cg_release(const char *path, struct fuse_file_info *fi)
 {
-       struct file_info *f = (struct file_info *)fi->fh;
-
-       do_release_file_info(f);
+       do_release_file_info(fi);
        return 0;
 }
 
@@ -2853,7 +2898,7 @@ static int read_file(const char *path, char *buf, size_t size,
                return 0;
 
        while (getline(&line, &linelen, f) != -1) {
-               size_t l = snprintf(cache, cache_size, "%s", line);
+               ssize_t l = snprintf(cache, cache_size, "%s", line);
                if (l < 0) {
                        perror("Error writing to cache");
                        rv = 0;
@@ -2870,7 +2915,8 @@ static int read_file(const char *path, char *buf, size_t size,
        }
 
        d->size = total_len;
-       if (total_len > size ) total_len = size;
+       if (total_len > size)
+               total_len = size;
 
        /* read from off 0 */
        memcpy(buf, d->buf, total_len);
@@ -2991,7 +3037,7 @@ static int proc_meminfo_read(char *buf, size_t size, off_t offset,
                goto err;
 
        while (getline(&line, &linelen, f) != -1) {
-               size_t l;
+               ssize_t l;
                char *printme, lbuf[100];
 
                memset(lbuf, 0, 100);
@@ -3011,7 +3057,7 @@ static int proc_meminfo_read(char *buf, size_t size, off_t offset,
                        snprintf(lbuf, 100, "SwapTotal:      %8lu kB\n", memswlimit - memlimit);
                        printme = lbuf;
                } else if (startswith(line, "SwapFree:") && memswlimit > 0 && memswusage > 0) {
-                       snprintf(lbuf, 100, "SwapFree:       %8lu kB\n", 
+                       snprintf(lbuf, 100, "SwapFree:       %8lu kB\n",
                                (memswlimit - memlimit) - (memswusage - memusage));
                        printme = lbuf;
                } else if (startswith(line, "Slab:")) {
@@ -3112,8 +3158,8 @@ static int proc_cpuinfo_read(char *buf, size_t size, off_t offset,
        char *cpuset = NULL;
        char *line = NULL;
        size_t linelen = 0, total_len = 0, rv = 0;
-       bool am_printing = false;
-       int curcpu = -1;
+       bool am_printing = false, firstline = true, is_s390x = false;
+       int curcpu = -1, cpu;
        char *cache = d->buf;
        size_t cache_size = d->buflen;
        FILE *f = NULL;
@@ -3146,7 +3192,17 @@ static int proc_cpuinfo_read(char *buf, size_t size, off_t offset,
                goto err;
 
        while (getline(&line, &linelen, f) != -1) {
-               size_t l;
+               ssize_t l;
+               if (firstline) {
+                       firstline = false;
+                       if (strstr(line, "IBM/S390") != NULL) {
+                               is_s390x = true;
+                               am_printing = true;
+                               continue;
+                       }
+               }
+               if (strncmp(line, "# processors:", 12) == 0)
+                       continue;
                if (is_processor_line(line)) {
                        am_printing = cpuline_in_cpuset(line, cpuset);
                        if (am_printing) {
@@ -3167,6 +3223,31 @@ static int proc_cpuinfo_read(char *buf, size_t size, off_t offset,
                                total_len += l;
                        }
                        continue;
+               } else if (is_s390x && sscanf(line, "processor %d:", &cpu) == 1) {
+                       char *p;
+                       if (!cpu_in_cpuset(cpu, cpuset))
+                               continue;
+                       curcpu ++;
+                       p = strchr(line, ':');
+                       if (!p || !*p)
+                               goto err;
+                       p++;
+                       l = snprintf(cache, cache_size, "processor %d:%s", curcpu, p);
+                       if (l < 0) {
+                               perror("Error writing to cache");
+                               rv = 0;
+                               goto err;
+                       }
+                       if (l >= cache_size) {
+                               fprintf(stderr, "Internal error: truncated write to cache\n");
+                               rv = 0;
+                               goto err;
+                       }
+                       cache += l;
+                       cache_size -= l;
+                       total_len += l;
+                       continue;
+
                }
                if (am_printing) {
                        l = snprintf(cache, cache_size, "%s", line);
@@ -3186,6 +3267,38 @@ static int proc_cpuinfo_read(char *buf, size_t size, off_t offset,
                }
        }
 
+       if (is_s390x) {
+               char *origcache = d->buf;
+               ssize_t l;
+               do {
+                       d->buf = malloc(d->buflen);
+               } while (!d->buf);
+               cache = d->buf;
+               cache_size = d->buflen;
+               total_len = 0;
+               l = snprintf(cache, cache_size, "vendor_id       : IBM/S390\n");
+               if (l < 0 || l >= cache_size) {
+                       free(origcache);
+                       goto err;
+               }
+               cache_size -= l;
+               cache += l;
+               total_len += l;
+               l = snprintf(cache, cache_size, "# processors    : %d\n", curcpu + 1);
+               if (l < 0 || l >= cache_size) {
+                       free(origcache);
+                       goto err;
+               }
+               cache_size -= l;
+               cache += l;
+               total_len += l;
+               l = snprintf(cache, cache_size, "%s", origcache);
+               free(origcache);
+               if (l < 0 || l >= cache_size)
+                       goto err;
+               total_len += l;
+       }
+
        d->cached = 1;
        d->size = total_len;
        if (total_len > size ) total_len = size;
@@ -3256,7 +3369,7 @@ static int proc_stat_read(char *buf, size_t size, off_t offset,
        }
 
        while (getline(&line, &linelen, f) != -1) {
-               size_t l;
+               ssize_t l;
                int cpu;
                char cpu_char[10]; /* That's a lot of cores */
                char *c;
@@ -3428,7 +3541,7 @@ static int proc_uptime_read(char *buf, size_t size, off_t offset,
        long int reaperage = getreaperage(fc->pid);
        unsigned long int busytime = get_reaper_busy(fc->pid), idletime;
        char *cache = d->buf;
-       size_t total_len = 0;
+       ssize_t total_len = 0;
 
 #if RELOADTEST
        iwashere();
@@ -3506,15 +3619,15 @@ static int proc_diskstats_read(char *buf, size_t size, off_t offset,
                return read_file("/proc/diskstats", buf, size, d);
        prune_init_slice(cg);
 
-       if (!cgfs_get_value("blkio", cg, "blkio.io_serviced", &io_serviced_str))
+       if (!cgfs_get_value("blkio", cg, "blkio.io_serviced_recursive", &io_serviced_str))
                goto err;
-       if (!cgfs_get_value("blkio", cg, "blkio.io_merged", &io_merged_str))
+       if (!cgfs_get_value("blkio", cg, "blkio.io_merged_recursive", &io_merged_str))
                goto err;
-       if (!cgfs_get_value("blkio", cg, "blkio.io_service_bytes", &io_service_bytes_str))
+       if (!cgfs_get_value("blkio", cg, "blkio.io_service_bytes_recursive", &io_service_bytes_str))
                goto err;
-       if (!cgfs_get_value("blkio", cg, "blkio.io_wait_time", &io_wait_time_str))
+       if (!cgfs_get_value("blkio", cg, "blkio.io_wait_time_recursive", &io_wait_time_str))
                goto err;
-       if (!cgfs_get_value("blkio", cg, "blkio.io_service_time", &io_service_time_str))
+       if (!cgfs_get_value("blkio", cg, "blkio.io_service_time_recursive", &io_service_time_str))
                goto err;
 
 
@@ -3523,48 +3636,46 @@ static int proc_diskstats_read(char *buf, size_t size, off_t offset,
                goto err;
 
        while (getline(&line, &linelen, f) != -1) {
-               size_t l;
-               char *printme, lbuf[256];
+               ssize_t l;
+               char lbuf[256];
 
                i = sscanf(line, "%u %u %71s", &major, &minor, dev_name);
-               if(i == 3){
-                       get_blkio_io_value(io_serviced_str, major, minor, "Read", &read);
-                       get_blkio_io_value(io_serviced_str, major, minor, "Write", &write);
-                       get_blkio_io_value(io_merged_str, major, minor, "Read", &read_merged);
-                       get_blkio_io_value(io_merged_str, major, minor, "Write", &write_merged);
-                       get_blkio_io_value(io_service_bytes_str, major, minor, "Read", &read_sectors);
-                       read_sectors = read_sectors/512;
-                       get_blkio_io_value(io_service_bytes_str, major, minor, "Write", &write_sectors);
-                       write_sectors = write_sectors/512;
-
-                       get_blkio_io_value(io_service_time_str, major, minor, "Read", &rd_svctm);
-                       rd_svctm = rd_svctm/1000000;
-                       get_blkio_io_value(io_wait_time_str, major, minor, "Read", &rd_wait);
-                       rd_wait = rd_wait/1000000;
-                       read_ticks = rd_svctm + rd_wait;
-
-                       get_blkio_io_value(io_service_time_str, major, minor, "Write", &wr_svctm);
-                       wr_svctm =  wr_svctm/1000000;
-                       get_blkio_io_value(io_wait_time_str, major, minor, "Write", &wr_wait);
-                       wr_wait =  wr_wait/1000000;
-                       write_ticks = wr_svctm + wr_wait;
-
-                       get_blkio_io_value(io_service_time_str, major, minor, "Total", &tot_ticks);
-                       tot_ticks =  tot_ticks/1000000;
-               }else{
+               if (i != 3)
                        continue;
-               }
+
+               get_blkio_io_value(io_serviced_str, major, minor, "Read", &read);
+               get_blkio_io_value(io_serviced_str, major, minor, "Write", &write);
+               get_blkio_io_value(io_merged_str, major, minor, "Read", &read_merged);
+               get_blkio_io_value(io_merged_str, major, minor, "Write", &write_merged);
+               get_blkio_io_value(io_service_bytes_str, major, minor, "Read", &read_sectors);
+               read_sectors = read_sectors/512;
+               get_blkio_io_value(io_service_bytes_str, major, minor, "Write", &write_sectors);
+               write_sectors = write_sectors/512;
+
+               get_blkio_io_value(io_service_time_str, major, minor, "Read", &rd_svctm);
+               rd_svctm = rd_svctm/1000000;
+               get_blkio_io_value(io_wait_time_str, major, minor, "Read", &rd_wait);
+               rd_wait = rd_wait/1000000;
+               read_ticks = rd_svctm + rd_wait;
+
+               get_blkio_io_value(io_service_time_str, major, minor, "Write", &wr_svctm);
+               wr_svctm =  wr_svctm/1000000;
+               get_blkio_io_value(io_wait_time_str, major, minor, "Write", &wr_wait);
+               wr_wait =  wr_wait/1000000;
+               write_ticks = wr_svctm + wr_wait;
+
+               get_blkio_io_value(io_service_time_str, major, minor, "Total", &tot_ticks);
+               tot_ticks =  tot_ticks/1000000;
 
                memset(lbuf, 0, 256);
-               if (read || write || read_merged || write_merged || read_sectors || write_sectors || read_ticks || write_ticks) {
+               if (read || write || read_merged || write_merged || read_sectors || write_sectors || read_ticks || write_ticks)
                        snprintf(lbuf, 256, "%u       %u %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
                                major, minor, dev_name, read, read_merged, read_sectors, read_ticks,
                                write, write_merged, write_sectors, write_ticks, ios_pgr, tot_ticks, rq_ticks);
-                       printme = lbuf;
-               } else
+               else
                        continue;
 
-               l = snprintf(cache, cache_size, "%s", printme);
+               l = snprintf(cache, cache_size, "%s", lbuf);
                if (l < 0) {
                        perror("Error writing to fuse buf");
                        rv = 0;
@@ -3608,7 +3719,8 @@ static int proc_swaps_read(char *buf, size_t size, off_t offset,
        char *memswlimit_str = NULL, *memlimit_str = NULL, *memusage_str = NULL, *memswusage_str = NULL,
              *memswlimit_default_str = NULL, *memswusage_default_str = NULL;
        unsigned long memswlimit = 0, memlimit = 0, memusage = 0, memswusage = 0, swap_total = 0, swap_free = 0;
-       size_t total_len = 0, rv = 0;
+       ssize_t total_len = 0, rv = 0;
+       ssize_t l = 0;
        char *cache = d->buf;
 
        if (offset) {
@@ -3684,12 +3796,13 @@ static int proc_swaps_read(char *buf, size_t size, off_t offset,
        }
 
        if (swap_total > 0) {
-               total_len += snprintf(d->buf + total_len, d->size - total_len,
-                                "none%*svirtual\t\t%lu\t%lu\t0\n", 36, " ",
-                                swap_total, swap_free);
+               l = snprintf(d->buf + total_len, d->size - total_len,
+                               "none%*svirtual\t\t%lu\t%lu\t0\n", 36, " ",
+                               swap_total, swap_free);
+               total_len += l;
        }
 
-       if (total_len < 0) {
+       if (total_len < 0 || l < 0) {
                perror("Error writing to cache");
                rv = 0;
                goto err;
@@ -3821,9 +3934,7 @@ int proc_access(const char *path, int mask)
 
 int proc_release(const char *path, struct fuse_file_info *fi)
 {
-       struct file_info *f = (struct file_info *)fi->fh;
-
-       do_release_file_info(f);
+       do_release_file_info(fi);
        return 0;
 }
 
@@ -3873,6 +3984,14 @@ static void __attribute__((constructor)) collect_subsystems(void)
                        goto out;
                *p2 = '\0';
 
+               /* With cgroupv2 /proc/self/cgroup can contain entries of the
+                * form: 0::/ This will cause lxcfs to fail the cgroup mounts
+                * because it parses out the empty string "" and later on passes
+                * it to mount(). Let's skip such entries.
+                */
+               if (!strcmp(p, ""))
+                       continue;
+
                if (!store_hierarchy(line, p))
                        goto out;
        }