util: rm_rf() refuse cleaning non-memory file systems, as extra paranoia

This commit is contained in:
Lennart Poettering 2012-07-10 19:05:58 +02:00
parent 825c6fe5eb
commit f56d5db919
3 changed files with 71 additions and 8 deletions

View File

@ -3243,7 +3243,7 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
return 0;
}
int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
DIR *d;
int ret = 0;
@ -3335,14 +3335,43 @@ int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root
return ret;
}
int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
int fd;
int r;
int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
struct statfs s;
assert(fd >= 0);
if (fstatfs(fd, &s) < 0) {
close_nointr_nofail(fd);
return -errno;
}
/* We refuse to clean disk file systems with this call. This
* is extra paranoia just to be sure we never ever remove
* non-state data */
if (s.f_type != TMPFS_MAGIC &&
s.f_type != RAMFS_MAGIC) {
log_error("Attempted to remove disk file system, and we can't allow that.");
close_nointr_nofail(fd);
return -EPERM;
}
return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
}
static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
int fd, r;
struct statfs s;
assert(path);
/* Be paranoid */
assert(!streq(path, "/"));
/* We refuse to clean the root file system with this
* call. This is extra paranoia to never cause a really
* seriously broken system. */
if (path_equal(path, "/")) {
log_error("Attempted to remove entire root file system, and we can't allow that.");
return -EPERM;
}
fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
if (fd < 0) {
@ -3350,6 +3379,17 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
if (errno != ENOTDIR)
return -errno;
if (!dangerous) {
if (statfs(path, &s) < 0)
return -errno;
if (s.f_type != TMPFS_MAGIC &&
s.f_type != RAMFS_MAGIC) {
log_error("Attempted to remove disk file system, and we can't allow that.");
return -EPERM;
}
}
if (delete_root && !only_dirs)
if (unlink(path) < 0 && errno != ENOENT)
return -errno;
@ -3357,8 +3397,21 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
return 0;
}
r = rm_rf_children(fd, only_dirs, honour_sticky, NULL);
if (!dangerous) {
if (fstatfs(fd, &s) < 0) {
close_nointr_nofail(fd);
return -errno;
}
if (s.f_type != TMPFS_MAGIC &&
s.f_type != RAMFS_MAGIC) {
log_error("Attempted to remove disk file system, and we can't allow that.");
close_nointr_nofail(fd);
return -EPERM;
}
}
r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL);
if (delete_root) {
if (honour_sticky && file_is_priv_sticky(path) > 0)
@ -3373,6 +3426,14 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
return r;
}
int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false);
}
int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true);
}
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
assert(path);

View File

@ -358,7 +358,9 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
int pipe_eof(int fd);

View File

@ -845,7 +845,7 @@ static int remove_item_instance(Item *i, const char *instance) {
case RECURSIVE_REMOVE_PATH:
/* FIXME: we probably should use dir_cleanup() here
* instead of rm_rf() so that 'x' is honoured. */
r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
if (r < 0 && r != -ENOENT) {
log_error("rm_rf(%s): %s", instance, strerror(-r));
return r;