From ad293f5a94d8124ece7d1cb860952a87b1c8d98f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 21 Aug 2011 20:05:51 +0200 Subject: [PATCH] cgroup: honour sticky bit when trimming cgroup trees --- src/cgroup-util.c | 33 +++++++++++++++++++++----- src/cgroup-util.h | 2 +- src/logind-user.c | 2 +- src/manager.c | 2 +- src/tmpfiles.c | 2 +- src/util.c | 60 +++++++++++++++++++++++++++++++++++++---------- src/util.h | 4 +++- 7 files changed, 82 insertions(+), 23 deletions(-) diff --git a/src/cgroup-util.c b/src/cgroup-util.c index fd0ac98e..ec48ea63 100644 --- a/src/cgroup-util.c +++ b/src/cgroup-util.c @@ -153,17 +153,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_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 +323,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 +487,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) @@ -543,7 +564,7 @@ int cg_trim(const char *controller, const char *path, bool delete_root) { if ((r = cg_get_path(controller, path, NULL, &fs)) < 0) return r; - r = rm_rf(fs, true, delete_root); + r = rm_rf(fs, true, delete_root, true); free(fs); return r == -ENOENT ? 0 : r; diff --git a/src/cgroup-util.h b/src/cgroup-util.h index d142af34..f09373bd 100644 --- a/src/cgroup-util.h +++ b/src/cgroup-util.h @@ -52,7 +52,7 @@ int cg_get_by_pid(const char *controller, pid_t pid, char **path); int cg_trim(const char *controller, const char *path, bool delete_root); -int cg_rmdir(const char *controller, const char *path); +int cg_rmdir(const char *controller, const char *path, bool honour_sticky); int cg_delete(const char *controller, const char *path); int cg_create(const char *controller, const char *path); diff --git a/src/logind-user.c b/src/logind-user.c index 613a5c35..56c7de44 100644 --- a/src/logind-user.c +++ b/src/logind-user.c @@ -409,7 +409,7 @@ static int user_remove_runtime_path(User *u) { if (!u->runtime_path) return 0; - r = rm_rf(u->runtime_path, false, true); + r = rm_rf(u->runtime_path, false, true, false); if (r < 0) log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r)); diff --git a/src/manager.c b/src/manager.c index a189479b..163f69c2 100644 --- a/src/manager.c +++ b/src/manager.c @@ -3073,7 +3073,7 @@ void manager_undo_generators(Manager *m) { return; strv_remove(m->lookup_paths.unit_path, m->generator_unit_path); - rm_rf(m->generator_unit_path, false, true); + rm_rf(m->generator_unit_path, false, true, false); free(m->generator_unit_path); m->generator_unit_path = NULL; diff --git a/src/tmpfiles.c b/src/tmpfiles.c index 421a9154..a6b8f859 100644 --- a/src/tmpfiles.c +++ b/src/tmpfiles.c @@ -586,7 +586,7 @@ static int remove_item(Item *i, const char *instance) { case TRUNCATE_DIRECTORY: case RECURSIVE_REMOVE_PATH: - if ((r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH)) < 0 && + if ((r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false)) < 0 && r != -ENOENT) { log_error("rm_rf(%s): %s", instance, strerror(-r)); return r; diff --git a/src/util.c b/src/util.c index 017b9958..ecfe450d 100644 --- a/src/util.c +++ b/src/util.c @@ -3354,7 +3354,7 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) { return 0; } -static int rm_rf_children(int fd, bool only_dirs) { +static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) { DIR *d; int ret = 0; @@ -3371,7 +3371,7 @@ static int rm_rf_children(int fd, bool only_dirs) { for (;;) { struct dirent buf, *de; - bool is_dir; + bool is_dir, keep_around = false; int r; if ((r = readdir_r(d, &buf, &de)) != 0) { @@ -3395,9 +3395,26 @@ static int rm_rf_children(int fd, bool only_dirs) { continue; } + if (honour_sticky) + keep_around = st.st_uid == 0 && (st.st_mode & S_ISVTX); + is_dir = S_ISDIR(st.st_mode); - } else + + } else { + if (honour_sticky) { + struct stat st; + + if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { + if (ret == 0 && errno != ENOENT) + ret = -errno; + continue; + } + + keep_around = st.st_uid == 0 && (st.st_mode & S_ISVTX); + } + is_dir = de->d_type == DT_DIR; + } if (is_dir) { int subdir_fd; @@ -3408,16 +3425,18 @@ static int rm_rf_children(int fd, bool only_dirs) { continue; } - if ((r = rm_rf_children(subdir_fd, only_dirs)) < 0) { + if ((r = rm_rf_children(subdir_fd, only_dirs, honour_sticky)) < 0) { if (ret == 0) ret = r; } - if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { - if (ret == 0 && errno != ENOENT) - ret = -errno; - } - } else if (!only_dirs) { + if (!keep_around) + if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { + if (ret == 0 && errno != ENOENT) + ret = -errno; + } + + } else if (!only_dirs && !keep_around) { if (unlinkat(fd, de->d_name, 0) < 0) { if (ret == 0 && errno != ENOENT) @@ -3431,7 +3450,7 @@ static int rm_rf_children(int fd, bool only_dirs) { return ret; } -int rm_rf(const char *path, bool only_dirs, bool delete_root) { +int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) { int fd; int r; @@ -3449,13 +3468,18 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root) { return 0; } - r = rm_rf_children(fd, only_dirs); + r = rm_rf_children(fd, only_dirs, honour_sticky); + + if (delete_root) { + + if (honour_sticky && file_is_sticky(path) > 0) + return r; - if (delete_root) if (rmdir(path) < 0) { if (r == 0) r = -errno; } + } return r; } @@ -5674,6 +5698,18 @@ int block_get_whole_disk(dev_t d, dev_t *ret) { return -ENOENT; } +int file_is_sticky(const char *p) { + struct stat st; + + assert(p); + + if (lstat(p, &st) < 0) + return -errno; + + return + st.st_uid == 0 && + (st.st_mode & S_ISVTX); +} static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", diff --git a/src/util.h b/src/util.h index e23f309d..b81edc8b 100644 --- a/src/util.h +++ b/src/util.h @@ -362,7 +362,7 @@ int get_ctty(pid_t, dev_t *_devnr, char **r); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); -int rm_rf(const char *path, bool only_dirs, bool delete_root); +int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky); int pipe_eof(int fd); @@ -467,6 +467,8 @@ bool in_charset(const char *s, const char* charset); int block_get_whole_disk(dev_t d, dev_t *ret); +int file_is_sticky(const char *p); + #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) -- 2.39.5