]> git.proxmox.com Git - mirror_lxc.git/commitdiff
cgroups: fd-only cgroup tree pruning
authorChristian Brauner <christian.brauner@ubuntu.com>
Tue, 16 Feb 2021 22:05:23 +0000 (23:05 +0100)
committerChristian Brauner <christian.brauner@ubuntu.com>
Tue, 16 Feb 2021 23:42:40 +0000 (00:42 +0100)
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/cgroups/cgfsng.c
src/lxc/cgroups/cgroup.c
src/lxc/cgroups/cgroup.h
src/lxc/cgroups/cgroup_utils.c
src/lxc/cgroups/cgroup_utils.h
src/lxc/file_utils.h

index f1c35760a7ded90b1b1448850e3b6ebb17b318f8..80137a2a7fb2cdb4a2bb59b7d1766d4a9de3f47d 100644 (file)
@@ -777,9 +777,9 @@ static void lxc_cgfsng_print_basecg_debuginfo(char *basecginfo, char **klist,
                TRACE("named subsystem %d: %s", k, *it);
 }
 
-static int cgroup_tree_remove(struct hierarchy **hierarchies, const char *container_cgroup)
+static int cgroup_tree_remove(struct hierarchy **hierarchies, const char *path_prune)
 {
-       if (!container_cgroup || !hierarchies)
+       if (!path_prune || !hierarchies)
                return 0;
 
        for (int i = 0; hierarchies[i]; i++) {
@@ -789,9 +789,11 @@ static int cgroup_tree_remove(struct hierarchy **hierarchies, const char *contai
                if (!h->container_limit_path)
                        continue;
 
-               ret = lxc_rm_rf(h->container_limit_path);
+               ret = cgroup_tree_prune(h->dfd_base, path_prune);
                if (ret < 0)
-                       WARN("Failed to destroy \"%s\"", h->container_limit_path);
+                       SYSWARN("Failed to destroy %d(%s)", h->dfd_base, path_prune);
+               else
+                       TRACE("Removed cgroup tree %d(%s)", h->dfd_base, path_prune);
 
                if (h->container_limit_path != h->container_full_path)
                        free_disarm(h->container_limit_path);
@@ -803,7 +805,7 @@ static int cgroup_tree_remove(struct hierarchy **hierarchies, const char *contai
 
 struct generic_userns_exec_data {
        struct hierarchy **hierarchies;
-       const char *container_cgroup;
+       const char *path_prune;
        struct lxc_conf *conf;
        uid_t origuid; /* target uid in parent namespace */
        char *path;
@@ -829,7 +831,7 @@ static int cgroup_tree_remove_wrapper(void *data)
                return log_error_errno(-1, errno, "Failed to setresuid(%d, %d, %d)",
                                       (int)nsuid, (int)nsuid, (int)nsuid);
 
-       return cgroup_tree_remove(arg->hierarchies, arg->container_cgroup);
+       return cgroup_tree_remove(arg->hierarchies, arg->path_prune);
 }
 
 __cgfsng_ops static void cgfsng_payload_destroy(struct cgroup_ops *ops,
@@ -864,14 +866,14 @@ __cgfsng_ops static void cgfsng_payload_destroy(struct cgroup_ops *ops,
        if (!lxc_list_empty(&handler->conf->id_map)) {
                struct generic_userns_exec_data wrap = {
                        .conf                   = handler->conf,
-                       .container_cgroup       = ops->container_cgroup,
+                       .path_prune             = ops->container_limit_cgroup,
                        .hierarchies            = ops->hierarchies,
                        .origuid                = 0,
                };
                ret = userns_exec_1(handler->conf, cgroup_tree_remove_wrapper,
                                    &wrap, "cgroup_tree_remove_wrapper");
        } else {
-               ret = cgroup_tree_remove(ops->hierarchies, ops->container_cgroup);
+               ret = cgroup_tree_remove(ops->hierarchies, ops->container_limit_cgroup);
        }
        if (ret < 0)
                SYSWARN("Failed to destroy cgroups");
@@ -1221,7 +1223,7 @@ __cgfsng_ops static void cgfsng_monitor_destroy(struct cgroup_ops *ops,
                /* Monitor might have died before we entered the cgroup. */
                if (handler->monitor_pid <= 0) {
                        WARN("No valid monitor process found while destroying cgroups");
-                       goto try_lxc_rm_rf;
+                       goto cgroup_prune_tree;
                }
 
                if (conf->cgroup_meta.monitor_pivot_dir)
@@ -1247,10 +1249,12 @@ __cgfsng_ops static void cgfsng_monitor_destroy(struct cgroup_ops *ops,
                        continue;
                }
 
-try_lxc_rm_rf:
-               ret = lxc_rm_rf(h->monitor_full_path);
+cgroup_prune_tree:
+               ret = cgroup_tree_prune(h->dfd_base, ops->monitor_cgroup);
                if (ret < 0)
-                       WARN("Failed to destroy \"%s\"", h->monitor_full_path);
+                       SYSWARN("Failed to destroy %d(%s)", h->dfd_base, ops->monitor_cgroup);
+               else
+                       TRACE("Removed cgroup tree %d(%s)", h->dfd_base, ops->monitor_cgroup);
        }
 }
 
@@ -1468,6 +1472,10 @@ __cgfsng_ops static bool cgfsng_payload_create(struct cgroup_ops *ops, struct lx
                return log_error_errno(false, ERANGE, "Failed to create container cgroup");
 
        ops->container_cgroup = move_ptr(container_cgroup);
+       if (limiting_cgroup)
+               ops->container_limit_cgroup = move_ptr(limiting_cgroup);
+       else
+               ops->container_limit_cgroup = ops->container_cgroup;
        INFO("The container process uses \"%s\" as cgroup", ops->container_cgroup);
        return true;
 }
index 96049b359e80e1c0ef62f82ae15135ca9fcedfee..4fb92698e3b8735c60708b4730ceb9d0b62e4949 100644 (file)
@@ -66,9 +66,14 @@ void cgroup_exit(struct cgroup_ops *ops)
                free(*cur);
 
        free(ops->cgroup_pattern);
-       free(ops->container_cgroup);
        free(ops->monitor_cgroup);
 
+       {
+               if (ops->container_cgroup != ops->container_limit_cgroup)
+                       free(ops->container_limit_cgroup);
+               free(ops->container_cgroup);
+       }
+
        if (ops->cgroup2_devices)
                bpf_program_free(ops->cgroup2_devices);
 
index 2ec5f0a7ca1bcb417015891aec33d76398913ebf..7a620a190046d6497b8e1de2d62166b4072f46b8 100644 (file)
@@ -134,6 +134,7 @@ struct cgroup_ops {
        char **cgroup_use;
        char *cgroup_pattern;
        char *container_cgroup;
+       char *container_limit_cgroup;
        char *monitor_cgroup;
 
        /* @hierarchies
index fb936393d42095e33ee101cf8cb3db7d3f3b38dd..ac748fe6df74b5a77a1d025231a9380de6e34b30 100644 (file)
 #include "cgroup_utils.h"
 #include "config.h"
 #include "file_utils.h"
+#include "log.h"
 #include "macro.h"
 #include "memory_utils.h"
 #include "utils.h"
 
+lxc_log_define(cgroup_utils, lxc);
+
 int get_cgroup_version(char *line)
 {
        if (is_cgroupfs_v1(line))
@@ -95,3 +98,64 @@ int unified_cgroup_fd(int fd)
 
        return false;
 }
+
+int cgroup_tree_prune(int dfd, const char *path)
+{
+       __do_close int dfd_disown = -EBADF, dfd_dup = -EBADF;
+       __do_closedir DIR *dir = NULL;
+       int ret;
+       struct dirent *direntp;
+
+       /*
+        * The unlinkat() syscall doesn't work with empty paths, i.e. it isn't
+        * possible to remove the fd itself.
+        */
+       if (is_empty_string(path) || strequal(path, "."))
+               return ret_errno(EINVAL);
+
+       /*
+        * Note that O_PATH file descriptors can't be used with getdents() and
+        * therefore with readdir().
+        */
+       dfd_disown = open_at(dfd, path, PROTECT_OPEN,
+                            PROTECT_LOOKUP_BENEATH_WITH_SYMLINKS, 0);
+       if (dfd_disown < 0)
+               return -errno;
+
+       dfd_dup = dup_cloexec(dfd_disown);
+       if (dfd_dup < 0)
+               return -errno;
+
+       dir = fdopendir(dfd_disown);
+       if (!dir)
+               return -errno;
+
+       /* Transfer ownership to fdopendir(). */
+       move_fd(dfd_disown);
+
+       while ((direntp = readdir(dir))) {
+               struct stat st;
+
+               if (strequal(direntp->d_name, ".") ||
+                   strequal(direntp->d_name, ".."))
+                       continue;
+
+               ret = fstatat(dfd_dup, direntp->d_name, &st,
+                             AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW);
+               if (ret < 0)
+                       continue;
+
+               if (!S_ISDIR(st.st_mode))
+                       continue;
+
+               ret = cgroup_tree_prune(dfd_dup, direntp->d_name);
+               if (ret < 0)
+                       return -errno;
+       }
+
+       ret = unlinkat(dfd, path, AT_REMOVEDIR);
+       if (ret < 0)
+               return -errno;
+
+       return 0;
+}
index f85ac35d46fa260f098e1604dab6223363298a8a..142b4db79a21cc8755a0f36e0090a8478a24efd3 100644 (file)
@@ -41,4 +41,6 @@ static inline bool cgns_supported(void)
        return supported == 1;
 }
 
+__hidden extern int cgroup_tree_prune(int dfd, const char *path);
+
 #endif /* __LXC_CGROUP_UTILS_H */
index af8014e7bf255ac102108af8c4183855959327b4..cd9f447ffeb694e16b6f21d03c63bac2adf91dca 100644 (file)
@@ -13,6 +13,7 @@
 #include <unistd.h>
 
 #include "compiler.h"
+#include "memory_utils.h"
 #include "syscall_wrappers.h"
 
 /* read and write whole files */