Skip to content

Commit

Permalink
cgroups: fd-only cgroup tree pruning
Browse files Browse the repository at this point in the history
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
  • Loading branch information
Christian Brauner committed Feb 16, 2021
1 parent 6347774 commit c55fe36
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 13 deletions.
32 changes: 20 additions & 12 deletions src/lxc/cgroups/cgfsng.c
Expand Up @@ -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++) {
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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)
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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;
}
Expand Down
7 changes: 6 additions & 1 deletion src/lxc/cgroups/cgroup.c
Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions src/lxc/cgroups/cgroup.h
Expand Up @@ -134,6 +134,7 @@ struct cgroup_ops {
char **cgroup_use;
char *cgroup_pattern;
char *container_cgroup;
char *container_limit_cgroup;
char *monitor_cgroup;

/* @hierarchies
Expand Down
64 changes: 64 additions & 0 deletions src/lxc/cgroups/cgroup_utils.c
Expand Up @@ -13,10 +13,13 @@
#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))
Expand Down Expand Up @@ -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;
}
2 changes: 2 additions & 0 deletions src/lxc/cgroups/cgroup_utils.h
Expand Up @@ -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 */
1 change: 1 addition & 0 deletions src/lxc/file_utils.h
Expand Up @@ -13,6 +13,7 @@
#include <unistd.h>

#include "compiler.h"
#include "memory_utils.h"
#include "syscall_wrappers.h"

/* read and write whole files */
Expand Down

0 comments on commit c55fe36

Please sign in to comment.