cgroups: fd-based only cgroup creation

parent 3486d993
...@@ -1133,32 +1133,62 @@ try_lxc_rm_rf: ...@@ -1133,32 +1133,62 @@ try_lxc_rm_rf:
} }
} }
static int mkdir_eexist_on_last(const char *dir, mode_t mode) static int __cgroup_tree_create(int dfd_base, const char *path, mode_t mode)
{ {
const char *tmp = dir; __do_close int dfd_final = -EBADF;
const char *orig = dir; int dfd_cur = dfd_base;
size_t orig_len; int ret = 0;
size_t len;
char *cur;
char buf[PATH_MAX];
orig_len = strlen(dir); if (is_empty_string(path))
do { return ret_errno(-EINVAL);
__do_free char *makeme = NULL;
int ret;
size_t cur_len;
dir = tmp + strspn(tmp, "/"); len = strlcpy(buf, path, sizeof(buf));
tmp = dir + strcspn(dir, "/"); if (len >= sizeof(buf))
return -E2BIG;
cur_len = dir - orig; lxc_iterate_parts(cur, buf, "/") {
makeme = strndup(orig, cur_len); /*
if (!makeme) * Even though we vetted the paths when we parsed the config
return ret_set_errno(-1, ENOMEM); * we're paranoid here and check that the path is neither
* absolute nor walks upwards.
*/
if (abspath(buf))
return syserrno_set(-EINVAL, "No absolute paths allowed");
ret = mkdir(makeme, mode); if (strnequal(buf, "..", STRLITERALLEN("..")))
if (ret < 0 && ((errno != EEXIST) || (orig_len == cur_len))) return syserrno_set(-EINVAL, "No upward walking paths allowed");
return log_warn_errno(-1, errno, "Failed to create directory \"%s\"", makeme);
} while (tmp != dir);
return 0; ret = mkdirat(dfd_cur, cur, mode);
if (ret < 0) {
if (errno != EEXIST)
return syserrno(-errno, "Failed to create %d(%s)", dfd_cur, cur);
ret = -EEXIST;
}
TRACE("%s %d(%s) cgroup", !ret ? "Created" : "Reusing", dfd_cur, cur);
dfd_final = open_at(dfd_cur, cur, PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_BENEATH, 0);
if (dfd_final < 0)
return syserrno(-errno, "Fail to open%s directory %d(%s)",
!ret ? " newly created" : "", dfd_base, cur);
if (dfd_cur != dfd_base)
close(dfd_cur);
/*
* Leave dfd_final pointing to the last fd we opened so it will
* be automatically zapped if we return early.
*/
dfd_cur = dfd_final;
}
/* The final cgroup must be succesfully creatd by us. */
if (ret)
return syserrno_set(ret, "Creating the final cgroup %d(%s) failed", dfd_base, path);
return move_fd(dfd_final);
} }
static bool cgroup_tree_create(struct cgroup_ops *ops, struct lxc_conf *conf, static bool cgroup_tree_create(struct cgroup_ops *ops, struct lxc_conf *conf,
...@@ -1166,34 +1196,25 @@ static bool cgroup_tree_create(struct cgroup_ops *ops, struct lxc_conf *conf, ...@@ -1166,34 +1196,25 @@ static bool cgroup_tree_create(struct cgroup_ops *ops, struct lxc_conf *conf,
const char *cgroup_leaf, bool payload, const char *cgroup_leaf, bool payload,
const char *cgroup_limit_dir) const char *cgroup_limit_dir)
{ {
__do_close int fd_limit = -EBADF, fd_final = -EBADF;
__do_free char *path = NULL, *limit_path = NULL; __do_free char *path = NULL, *limit_path = NULL;
int ret, ret_cpuset; int ret_cpuset;
path = must_make_path(h->mountpoint, h->container_base_path, cgroup_leaf, NULL); /* Don't bother with all the rest if the final cgroup already exists. */
if (dir_exists(path)) if (exists_dir_at(h->dfd_base, cgroup_leaf))
return log_warn_errno(false, errno, "The %s cgroup already existed", path); return syswarn(false, "The %d(%s) cgroup already existed", h->dfd_base, cgroup_leaf);
ret_cpuset = cg_legacy_handle_cpuset_hierarchy(h, cgroup_leaf); ret_cpuset = cg_legacy_handle_cpuset_hierarchy(h, cgroup_leaf);
if (ret_cpuset < 0) if (ret_cpuset < 0)
return log_error_errno(false, errno, "Failed to handle legacy cpuset controller"); return log_error_errno(false, errno, "Failed to handle legacy cpuset controller");
if (payload && cgroup_limit_dir) { if (payload && cgroup_limit_dir) {
/* with isolation both parts need to not already exist */ /* With isolation both parts need to not already exist. */
limit_path = must_make_path(h->mountpoint, fd_limit = __cgroup_tree_create(h->dfd_base, cgroup_limit_dir, 0755);
h->container_base_path, if (fd_limit < 0)
cgroup_limit_dir, NULL); return syserrno(false, "Failed to create limiting cgroup %d(%s)", h->dfd_base, cgroup_limit_dir);
ret = mkdir_eexist_on_last(limit_path, 0755); limit_path = must_make_path(h->mountpoint, h->container_base_path, cgroup_limit_dir, NULL);
if (ret < 0)
return log_debug_errno(false,
errno, "Failed to create %s limiting cgroup",
limit_path);
h->cgfd_limit = lxc_open_dirfd(limit_path);
if (h->cgfd_limit < 0)
return log_error_errno(false, errno,
"Failed to open %s", path);
h->container_limit_path = move_ptr(limit_path);
/* /*
* With isolation the devices legacy cgroup needs to be * With isolation the devices legacy cgroup needs to be
...@@ -1206,30 +1227,33 @@ static bool cgroup_tree_create(struct cgroup_ops *ops, struct lxc_conf *conf, ...@@ -1206,30 +1227,33 @@ static bool cgroup_tree_create(struct cgroup_ops *ops, struct lxc_conf *conf,
return log_error(false, "Failed to setup legacy device limits"); return log_error(false, "Failed to setup legacy device limits");
} }
ret = mkdir_eexist_on_last(path, 0755); fd_final = __cgroup_tree_create(h->dfd_base, cgroup_leaf, 0755);
if (ret < 0) { if (fd_final < 0) {
/* /*
* This is the cpuset controller and * This is the cpuset controller and
* cg_legacy_handle_cpuset_hierarchy() has created our target * cg_legacy_handle_cpuset_hierarchy() has created our target
* directory for us to ensure correct initialization. * directory for us to ensure correct initialization.
*/ */
if (ret_cpuset != 1 || cgroup_tree) if (ret_cpuset != 1 || cgroup_tree)
return log_debug_errno(false, errno, "Failed to create %s cgroup", path); return sysdebug(false, "Failed to create payload cgroup %d(%s)", h->dfd_base, cgroup_leaf);
} }
path = must_make_path(h->mountpoint, h->container_base_path, cgroup_leaf, NULL);
if (payload) { if (payload) {
h->cgfd_con = lxc_open_dirfd(path); h->cgfd_con = move_fd(fd_final);
if (h->cgfd_con < 0)
return log_error_errno(false, errno, "Failed to open %s", path);
h->container_full_path = move_ptr(path); h->container_full_path = move_ptr(path);
if (h->cgfd_limit < 0)
if (fd_limit < 0)
h->cgfd_limit = h->cgfd_con; h->cgfd_limit = h->cgfd_con;
if (!h->container_limit_path) else
h->cgfd_limit = move_fd(fd_limit);
if (!limit_path)
h->container_limit_path = h->container_full_path; h->container_limit_path = h->container_full_path;
else
h->container_limit_path = move_ptr(limit_path);
} else { } else {
h->cgfd_mon = lxc_open_dirfd(path); h->cgfd_mon = move_fd(fd_final);
if (h->cgfd_mon < 0)
return log_error_errno(false, errno, "Failed to open %s", path);
h->monitor_full_path = move_ptr(path); h->monitor_full_path = move_ptr(path);
} }
...@@ -1356,7 +1380,7 @@ __cgfsng_ops static bool cgfsng_monitor_create(struct cgroup_ops *ops, struct lx ...@@ -1356,7 +1380,7 @@ __cgfsng_ops static bool cgfsng_monitor_create(struct cgroup_ops *ops, struct lx
monitor_cgroup, false, NULL)) monitor_cgroup, false, NULL))
continue; continue;
DEBUG("Failed to create cgroup \"%s\"", ops->hierarchies[i]->monitor_full_path ?: "(null)"); DEBUG("Failed to create cgroup \"%s\"", maybe_empty(ops->hierarchies[i]->monitor_full_path));
for (int j = 0; j < i; j++) for (int j = 0; j < i; j++)
cgroup_tree_leaf_remove(ops->hierarchies[j], false); cgroup_tree_leaf_remove(ops->hierarchies[j], false);
......
...@@ -630,21 +630,31 @@ int timens_offset_write(clockid_t clk_id, int64_t s_offset, int64_t ns_offset) ...@@ -630,21 +630,31 @@ int timens_offset_write(clockid_t clk_id, int64_t s_offset, int64_t ns_offset)
bool exists_dir_at(int dir_fd, const char *path) bool exists_dir_at(int dir_fd, const char *path)
{ {
struct stat sb;
int ret; int ret;
struct stat sb;
ret = fstatat(dir_fd, path, &sb, 0); ret = fstatat(dir_fd, path, &sb, 0);
if (ret < 0) if (ret < 0)
return false; return false;
return S_ISDIR(sb.st_mode); ret = S_ISDIR(sb.st_mode);
if (ret)
errno = EEXIST;
else
errno = ENOTDIR;
return ret;
} }
bool exists_file_at(int dir_fd, const char *path) bool exists_file_at(int dir_fd, const char *path)
{ {
int ret;
struct stat sb; struct stat sb;
return fstatat(dir_fd, path, &sb, 0) == 0; ret = fstatat(dir_fd, path, &sb, 0);
if (ret == 0)
errno = EEXIST;
return ret == 0;
} }
int open_at(int dfd, const char *path, unsigned int o_flags, int open_at(int dfd, const char *path, unsigned int o_flags,
......
...@@ -501,6 +501,20 @@ __lxc_unused static inline void LXC_##LEVEL(struct lxc_log_locinfo* locinfo, \ ...@@ -501,6 +501,20 @@ __lxc_unused static inline void LXC_##LEVEL(struct lxc_log_locinfo* locinfo, \
__internal_ret__; \ __internal_ret__; \
}) })
#define syswarn(__ret__, format, ...) \
({ \
typeof(__ret__) __internal_ret__ = (__ret__); \
SYSWARN(format, ##__VA_ARGS__); \
__internal_ret__; \
})
#define sysdebug(__ret__, format, ...) \
({ \
typeof(__ret__) __internal_ret__ = (__ret__); \
SYSDEBUG(format, ##__VA_ARGS__); \
__internal_ret__; \
})
#define syserrno_set(__ret__, format, ...) \ #define syserrno_set(__ret__, format, ...) \
({ \ ({ \
typeof(__ret__) __internal_ret__ = (__ret__); \ typeof(__ret__) __internal_ret__ = (__ret__); \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment