]> git.proxmox.com Git - mirror_lxcfs.git/blobdiff - bindings.c
bindings: caller_may_see_dir()
[mirror_lxcfs.git] / bindings.c
index 8186ca9a7d8b234d55eebcbd9799affe0b8fe7bf..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;
@@ -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,15 +432,16 @@ 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);