X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcgroup-util.c;h=904d30095226a6f027f1d527634cb4e9cc311b24;hp=fd0ac98e28faad081e1b2faa17425b592f2e9ea7;hb=b412ab3bcca61cf4402a6df2888b22ce88eaee08;hpb=3bfc71846c3eac9011f0182b0c8d5639a9549228 diff --git a/src/cgroup-util.c b/src/cgroup-util.c index fd0ac98e2..904d30095 100644 --- a/src/cgroup-util.c +++ b/src/cgroup-util.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "cgroup-util.h" #include "log.h" @@ -153,17 +154,38 @@ int cg_read_subgroup(DIR *d, char **fn) { return 0; } -int cg_rmdir(const char *controller, const char *path) { +int cg_rmdir(const char *controller, const char *path, bool honour_sticky) { char *p; int r; - if ((r = cg_get_path(controller, path, NULL, &p)) < 0) + r = cg_get_path(controller, path, NULL, &p); + if (r < 0) return r; + if (honour_sticky) { + char *tasks; + + /* If the sticky bit is set don't remove the directory */ + + tasks = strappend(p, "/tasks"); + if (!tasks) { + free(p); + return -ENOMEM; + } + + r = file_is_priv_sticky(tasks); + free(tasks); + + if (r > 0) { + free(p); + return 0; + } + } + r = rmdir(p); free(p); - return r < 0 ? -errno : 0; + return (r < 0 && errno != ENOENT) ? -errno : 0; } int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) { @@ -302,7 +324,7 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool si ret = r; if (rem) - if ((r = cg_rmdir(controller, path)) < 0) { + if ((r = cg_rmdir(controller, path, true)) < 0) { if (ret >= 0 && r != -ENOENT && r != -EBUSY) @@ -466,7 +488,7 @@ int cg_migrate_recursive(const char *controller, const char *from, const char *t ret = r; if (rem) - if ((r = cg_rmdir(controller, from)) < 0) { + if ((r = cg_rmdir(controller, from, true)) < 0) { if (ret >= 0 && r != -ENOENT && r != -EBUSY) @@ -491,7 +513,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch if (_unlikely_(!good)) { int r; - r = path_is_mount_point("/sys/fs/cgroup"); + r = path_is_mount_point("/sys/fs/cgroup", false); if (r <= 0) return r < 0 ? r : -ENOENT; @@ -533,20 +555,70 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch return 0; } +static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { + char *p; + bool is_sticky; + + if (typeflag != FTW_DP) + return 0; + + if (ftwbuf->level < 1) + return 0; + + p = strappend(path, "/tasks"); + if (!p) { + errno = ENOMEM; + return 1; + } + + is_sticky = file_is_priv_sticky(p) > 0; + free(p); + + if (is_sticky) + return 0; + + rmdir(path); + return 0; +} + int cg_trim(const char *controller, const char *path, bool delete_root) { char *fs; - int r; + int r = 0; assert(controller); assert(path); - if ((r = cg_get_path(controller, path, NULL, &fs)) < 0) + r = cg_get_path(controller, path, NULL, &fs); + if (r < 0) return r; - r = rm_rf(fs, true, delete_root); + errno = 0; + if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0) + r = errno ? -errno : -EIO; + + if (delete_root) { + bool is_sticky; + char *p; + + p = strappend(fs, "/tasks"); + if (!p) { + free(fs); + return -ENOMEM; + } + + is_sticky = file_is_priv_sticky(p) > 0; + free(p); + + if (!is_sticky) + if (rmdir(fs) < 0 && errno != ENOENT) { + if (r == 0) + r = -errno; + } + } + free(fs); - return r == -ENOENT ? 0 : r; + return r; } int cg_delete(const char *controller, const char *path) { @@ -640,7 +712,11 @@ int cg_set_group_access(const char *controller, const char *path, mode_t mode, u assert(controller); assert(path); - if ((r = cg_get_path(controller, path, NULL, &fs)) < 0) + if (mode != (mode_t) -1) + mode &= 0777; + + r = cg_get_path(controller, path, NULL, &fs); + if (r < 0) return r; r = chmod_and_chown(fs, mode, uid, gid); @@ -649,16 +725,47 @@ int cg_set_group_access(const char *controller, const char *path, mode_t mode, u return r; } -int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid) { +int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid, int sticky) { char *fs; int r; assert(controller); assert(path); - if ((r = cg_get_path(controller, path, "tasks", &fs)) < 0) + if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1 && sticky < 0) + return 0; + + if (mode != (mode_t) -1) + mode &= 0666; + + r = cg_get_path(controller, path, "tasks", &fs); + if (r < 0) return r; + if (sticky >= 0 && mode != (mode_t) -1) + /* Both mode and sticky param are passed */ + mode |= (sticky ? S_ISVTX : 0); + else if ((sticky >= 0 && mode == (mode_t) -1) || + (mode != (mode_t) -1 && sticky < 0)) { + struct stat st; + + /* Only one param is passed, hence read the current + * mode from the file itself */ + + r = lstat(fs, &st); + if (r < 0) { + free(fs); + return -errno; + } + + if (mode == (mode_t) -1) + /* No mode set, we just shall set the sticky bit */ + mode = (st.st_mode & ~S_ISVTX) | (sticky ? S_ISVTX : 0); + else + /* Only mode set, leave sticky bit untouched */ + mode = (st.st_mode & ~0777) | mode; + } + r = chmod_and_chown(fs, mode, uid, gid); free(fs);