util: rework rm_rf() logic

- Move to its own file rm-rf.c

- Change parameters into a single flags parameter

- Remove "honour sticky" logic, it's unused these days
This commit is contained in:
Lennart Poettering 2015-04-04 11:52:57 +02:00
parent 2f653bded3
commit c687863750
34 changed files with 294 additions and 255 deletions

View file

@ -771,6 +771,8 @@ libsystemd_shared_la_SOURCES = \
src/shared/device-nodes.h \ src/shared/device-nodes.h \
src/shared/util.c \ src/shared/util.c \
src/shared/util.h \ src/shared/util.h \
src/shared/rm-rf.c \
src/shared/rm-rf.h \
src/shared/virt.c \ src/shared/virt.c \
src/shared/virt.h \ src/shared/virt.h \
src/shared/architecture.c \ src/shared/architecture.c \

View file

@ -41,6 +41,7 @@
#include "efivars.h" #include "efivars.h"
#include "build.h" #include "build.h"
#include "util.h" #include "util.h"
#include "rm-rf.h"
static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) { static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) {
struct statfs sfs; struct statfs sfs;
@ -1095,7 +1096,7 @@ static int remove_binaries(const char *esp_path) {
return -ENOMEM; return -ENOMEM;
} }
r = rm_rf(p, false, false, false); r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
free(p); free(p);
q = remove_boot_efi(esp_path); q = remove_boot_efi(esp_path);

View file

@ -49,6 +49,7 @@
#include <sys/apparmor.h> #include <sys/apparmor.h>
#endif #endif
#include "rm-rf.h"
#include "execute.h" #include "execute.h"
#include "strv.h" #include "strv.h"
#include "macro.h" #include "macro.h"
@ -2020,7 +2021,7 @@ int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_p
/* We execute this synchronously, since we need to be /* We execute this synchronously, since we need to be
* sure this is gone when we start the service * sure this is gone when we start the service
* next. */ * next. */
rm_rf(p, false, true, false); (void) rm_rf(p, REMOVE_ROOT);
} }
return 0; return 0;
@ -2846,7 +2847,7 @@ int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, co
static void *remove_tmpdir_thread(void *p) { static void *remove_tmpdir_thread(void *p) {
_cleanup_free_ char *path = p; _cleanup_free_ char *path = p;
rm_rf_dangerous(path, false, true, false); (void) rm_rf(path, REMOVE_ROOT|REMOVE_PHYSICAL);
return NULL; return NULL;
} }

View file

@ -313,7 +313,7 @@ int machine_id_commit(const char *root) {
if (r < 0) if (r < 0)
return log_error_errno(r, "We didn't find a valid machine ID in %s.", etc_machine_id); return log_error_errno(r, "We didn't find a valid machine ID in %s.", etc_machine_id);
r = is_fd_on_temporary_fs(fd); r = fd_is_temporary_fs(fd);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id); return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id);
if (r == 0) { if (r == 0) {

View file

@ -52,6 +52,7 @@
#include "locale-setup.h" #include "locale-setup.h"
#include "unit-name.h" #include "unit-name.h"
#include "missing.h" #include "missing.h"
#include "rm-rf.h"
#include "path-lookup.h" #include "path-lookup.h"
#include "special.h" #include "special.h"
#include "exit-status.h" #include "exit-status.h"
@ -2881,7 +2882,7 @@ static void remove_generator_dir(Manager *m, char **generator) {
return; return;
strv_remove(m->lookup_paths.unit_path, *generator); strv_remove(m->lookup_paths.unit_path, *generator);
rm_rf(*generator, false, true, false); (void) rm_rf(*generator, REMOVE_ROOT);
free(*generator); free(*generator);
*generator = NULL; *generator = NULL;

View file

@ -22,6 +22,7 @@
#include <ftw.h> #include <ftw.h>
#include "util.h" #include "util.h"
#include "rm-rf.h"
#include "aufs-util.h" #include "aufs-util.h"
static int nftw_cb( static int nftw_cb(
@ -43,7 +44,7 @@ static int nftw_cb(
return FTW_CONTINUE; return FTW_CONTINUE;
log_debug("Removing whiteout indicator %s.", fpath); log_debug("Removing whiteout indicator %s.", fpath);
r = rm_rf_dangerous(fpath, false, true, false); r = rm_rf(fpath, REMOVE_ROOT|REMOVE_PHYSICAL);
if (r < 0) if (r < 0)
return FTW_STOP; return FTW_STOP;
@ -53,7 +54,7 @@ static int nftw_cb(
strcpy(mempcpy(p, fpath, ftwbuf->base), original); strcpy(mempcpy(p, fpath, ftwbuf->base), original);
log_debug("Removing deleted file %s.", p); log_debug("Removing deleted file %s.", p);
r = rm_rf_dangerous(p, false, true, false); r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
if (r < 0) if (r < 0)
return FTW_STOP; return FTW_STOP;
} }

View file

@ -28,6 +28,7 @@
#include "btrfs-util.h" #include "btrfs-util.h"
#include "copy.h" #include "copy.h"
#include "mkdir.h" #include "mkdir.h"
#include "rm-rf.h"
#include "ratelimit.h" #include "ratelimit.h"
#include "machine-pool.h" #include "machine-pool.h"
#include "qcow2-util.h" #include "qcow2-util.h"
@ -242,7 +243,7 @@ static int raw_import_finish(RawImport *i) {
if (i->force_local) { if (i->force_local) {
(void) btrfs_subvol_remove(i->final_path); (void) btrfs_subvol_remove(i->final_path);
(void) rm_rf_dangerous(i->final_path, false, true, false); (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL);
} }
r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);

View file

@ -28,6 +28,7 @@
#include "btrfs-util.h" #include "btrfs-util.h"
#include "copy.h" #include "copy.h"
#include "mkdir.h" #include "mkdir.h"
#include "rm-rf.h"
#include "ratelimit.h" #include "ratelimit.h"
#include "machine-pool.h" #include "machine-pool.h"
#include "qcow2-util.h" #include "qcow2-util.h"
@ -87,7 +88,7 @@ TarImport* tar_import_unref(TarImport *i) {
if (i->temp_path) { if (i->temp_path) {
(void) btrfs_subvol_remove(i->temp_path); (void) btrfs_subvol_remove(i->temp_path);
(void) rm_rf_dangerous(i->temp_path, false, true, false); (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL);
free(i->temp_path); free(i->temp_path);
} }
@ -198,7 +199,7 @@ static int tar_import_finish(TarImport *i) {
if (i->force_local) { if (i->force_local) {
(void) btrfs_subvol_remove(i->final_path); (void) btrfs_subvol_remove(i->final_path);
(void) rm_rf_dangerous(i->final_path, false, true, false); (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL);
} }
r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);

View file

@ -24,6 +24,7 @@
#include "util.h" #include "util.h"
#include "strv.h" #include "strv.h"
#include "copy.h" #include "copy.h"
#include "rm-rf.h"
#include "btrfs-util.h" #include "btrfs-util.h"
#include "capability.h" #include "capability.h"
#include "pull-job.h" #include "pull-job.h"
@ -125,7 +126,7 @@ int pull_make_local_copy(const char *final, const char *image_root, const char *
if (force_local) { if (force_local) {
(void) btrfs_subvol_remove(p); (void) btrfs_subvol_remove(p);
(void) rm_rf_dangerous(p, false, true, false); (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
} }
r = btrfs_subvol_snapshot(final, p, false, false); r = btrfs_subvol_snapshot(final, p, false, false);
@ -418,7 +419,7 @@ finish:
unlink(sig_file_path); unlink(sig_file_path);
if (gpg_home_created) if (gpg_home_created)
rm_rf_dangerous(gpg_home, false, true, false); (void) rm_rf(gpg_home, REMOVE_ROOT|REMOVE_PHYSICAL);
return r; return r;
} }

View file

@ -28,6 +28,7 @@
#include "btrfs-util.h" #include "btrfs-util.h"
#include "utf8.h" #include "utf8.h"
#include "mkdir.h" #include "mkdir.h"
#include "rm-rf.h"
#include "path-util.h" #include "path-util.h"
#include "import-util.h" #include "import-util.h"
#include "curl-util.h" #include "curl-util.h"
@ -111,7 +112,7 @@ DkrPull* dkr_pull_unref(DkrPull *i) {
if (i->temp_path) { if (i->temp_path) {
(void) btrfs_subvol_remove(i->temp_path); (void) btrfs_subvol_remove(i->temp_path);
(void) rm_rf_dangerous(i->temp_path, false, true, false); (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL);
free(i->temp_path); free(i->temp_path);
} }

View file

@ -31,6 +31,7 @@
#include "util.h" #include "util.h"
#include "macro.h" #include "macro.h"
#include "mkdir.h" #include "mkdir.h"
#include "rm-rf.h"
#include "path-util.h" #include "path-util.h"
#include "import-util.h" #include "import-util.h"
#include "import-common.h" #include "import-common.h"
@ -278,7 +279,7 @@ static int raw_pull_make_local_copy(RawPull *i) {
if (i->force_local) { if (i->force_local) {
(void) btrfs_subvol_remove(p); (void) btrfs_subvol_remove(p);
(void) rm_rf_dangerous(p, false, true, false); (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
} }
r = tempfn_random(p, &tp); r = tempfn_random(p, &tp);

View file

@ -30,6 +30,7 @@
#include "util.h" #include "util.h"
#include "macro.h" #include "macro.h"
#include "mkdir.h" #include "mkdir.h"
#include "rm-rf.h"
#include "path-util.h" #include "path-util.h"
#include "import-util.h" #include "import-util.h"
#include "import-common.h" #include "import-common.h"
@ -88,7 +89,7 @@ TarPull* tar_pull_unref(TarPull *i) {
if (i->temp_path) { if (i->temp_path) {
(void) btrfs_subvol_remove(i->temp_path); (void) btrfs_subvol_remove(i->temp_path);
(void) rm_rf_dangerous(i->temp_path, false, true, false); (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL);
free(i->temp_path); free(i->temp_path);
} }

View file

@ -31,6 +31,7 @@
#include "sd-messages.h" #include "sd-messages.h"
#include "sd-daemon.h" #include "sd-daemon.h"
#include "mkdir.h" #include "mkdir.h"
#include "rm-rf.h"
#include "hashmap.h" #include "hashmap.h"
#include "journal-file.h" #include "journal-file.h"
#include "socket-util.h" #include "socket-util.h"
@ -1088,7 +1089,7 @@ finish:
s->runtime_journal = NULL; s->runtime_journal = NULL;
if (r >= 0) if (r >= 0)
rm_rf("/run/log/journal", false, true, false); (void) rm_rf("/run/log/journal", REMOVE_ROOT);
sd_journal_close(j); sd_journal_close(j);

View file

@ -23,6 +23,7 @@
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
#include "rm-rf.h"
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
sd_journal *j; sd_journal *j;
@ -58,7 +59,7 @@ int main(int argc, char *argv[]) {
assert_se(j == NULL); assert_se(j == NULL);
} }
assert_se(rm_rf_dangerous(t, false, true, false) >= 0); assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
return 0; return 0;
} }

View file

@ -23,12 +23,12 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include "systemd/sd-journal.h" #include "sd-journal.h"
#include "journal-file.h" #include "journal-file.h"
#include "journal-vacuum.h" #include "journal-vacuum.h"
#include "util.h" #include "util.h"
#include "log.h" #include "log.h"
#include "rm-rf.h"
/* This program tests skipping around in a multi-file journal. /* This program tests skipping around in a multi-file journal.
*/ */
@ -190,7 +190,7 @@ static void test_skip(void (*setup)(void)) {
else { else {
journal_directory_vacuum(".", 3000000, 0, NULL, true); journal_directory_vacuum(".", 3000000, 0, NULL, true);
assert_se(rm_rf_dangerous(t, false, true, false) >= 0); assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
} }
puts("------------------------------------------------------------"); puts("------------------------------------------------------------");
@ -275,7 +275,7 @@ static void test_sequence_numbers(void) {
else { else {
journal_directory_vacuum(".", 3000000, 0, NULL, true); journal_directory_vacuum(".", 3000000, 0, NULL, true);
assert_se(rm_rf_dangerous(t, false, true, false) >= 0); assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
} }
} }

View file

@ -22,13 +22,13 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include "systemd/sd-journal.h" #include "sd-journal.h"
#include "journal-file.h"
#include "journal-internal.h"
#include "util.h" #include "util.h"
#include "log.h" #include "log.h"
#include "macro.h" #include "macro.h"
#include "rm-rf.h"
#include "journal-file.h"
#include "journal-internal.h"
#define N_ENTRIES 200 #define N_ENTRIES 200
@ -180,7 +180,7 @@ int main(int argc, char *argv[]) {
SD_JOURNAL_FOREACH_UNIQUE(j, data, l) SD_JOURNAL_FOREACH_UNIQUE(j, data, l)
printf("%.*s\n", (int) l, (const char*) data); printf("%.*s\n", (int) l, (const char*) data);
assert_se(rm_rf_dangerous(t, false, true, false) >= 0); assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
return 0; return 0;
} }

View file

@ -25,6 +25,7 @@
#include "util.h" #include "util.h"
#include "log.h" #include "log.h"
#include "rm-rf.h"
#include "journal-file.h" #include "journal-file.h"
#include "journal-verify.h" #include "journal-verify.h"
@ -144,7 +145,7 @@ int main(int argc, char *argv[]) {
log_info("Exiting..."); log_info("Exiting...");
assert_se(rm_rf_dangerous(t, false, true, false) >= 0); assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
return 0; return 0;
} }

View file

@ -22,8 +22,8 @@
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include "log.h" #include "log.h"
#include "rm-rf.h"
#include "journal-file.h" #include "journal-file.h"
#include "journal-authenticate.h" #include "journal-authenticate.h"
#include "journal-vacuum.h" #include "journal-vacuum.h"
@ -118,7 +118,7 @@ static void test_non_empty(void) {
else { else {
journal_directory_vacuum(".", 3000000, 0, NULL, true); journal_directory_vacuum(".", 3000000, 0, NULL, true);
assert_se(rm_rf_dangerous(t, false, true, false) >= 0); assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
} }
puts("------------------------------------------------------------"); puts("------------------------------------------------------------");
@ -157,7 +157,7 @@ static void test_empty(void) {
else { else {
journal_directory_vacuum(".", 3000000, 0, NULL, true); journal_directory_vacuum(".", 3000000, 0, NULL, true);
assert_se(rm_rf_dangerous(t, false, true, false) >= 0); assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
} }
journal_file_close(f1); journal_file_close(f1);

View file

@ -26,6 +26,7 @@
#include "util.h" #include "util.h"
#include "mkdir.h" #include "mkdir.h"
#include "rm-rf.h"
#include "hashmap.h" #include "hashmap.h"
#include "fileio.h" #include "fileio.h"
#include "path-util.h" #include "path-util.h"
@ -521,7 +522,7 @@ static int user_remove_runtime_path(User *u) {
if (!u->runtime_path) if (!u->runtime_path)
return 0; return 0;
r = rm_rf(u->runtime_path, false, false, false); r = rm_rf(u->runtime_path, 0);
if (r < 0) if (r < 0)
log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
@ -532,7 +533,7 @@ static int user_remove_runtime_path(User *u) {
if (r < 0 && errno != EINVAL && errno != ENOENT) if (r < 0 && errno != EINVAL && errno != ENOENT)
log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path); log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
r = rm_rf(u->runtime_path, false, true, false); r = rm_rf(u->runtime_path, REMOVE_ROOT);
if (r < 0) if (r < 0)
log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);

View file

@ -59,6 +59,7 @@
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
#include "mkdir.h" #include "mkdir.h"
#include "rm-rf.h"
#include "macro.h" #include "macro.h"
#include "missing.h" #include "missing.h"
#include "cgroup-util.h" #include "cgroup-util.h"
@ -4467,7 +4468,7 @@ finish:
const char *p; const char *p;
p = strjoina("/run/systemd/nspawn/propagate/", arg_machine); p = strjoina("/run/systemd/nspawn/propagate/", arg_machine);
(void) rm_rf(p, false, true, false); (void) rm_rf(p, REMOVE_ROOT);
} }
free(arg_directory); free(arg_directory);

View file

@ -28,6 +28,7 @@
#include "path-util.h" #include "path-util.h"
#include "copy.h" #include "copy.h"
#include "mkdir.h" #include "mkdir.h"
#include "rm-rf.h"
#include "machine-image.h" #include "machine-image.h"
static const char image_search_path[] = static const char image_search_path[] =
@ -366,7 +367,7 @@ int image_remove(Image *i) {
/* fall through */ /* fall through */
case IMAGE_RAW: case IMAGE_RAW:
return rm_rf_dangerous(i->path, false, true, false); return rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;

173
src/shared/rm-rf.c Normal file
View file

@ -0,0 +1,173 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2015 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "util.h"
#include "path-util.h"
#include "rm-rf.h"
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
_cleanup_closedir_ DIR *d = NULL;
int ret = 0, r;
assert(fd >= 0);
/* This returns the first error we run into, but nevertheless
* tries to go on. This closes the passed fd. */
if (!(flags & REMOVE_PHYSICAL)) {
r = fd_is_temporary_fs(fd);
if (r < 0) {
safe_close(fd);
return r;
}
if (!r) {
/* We refuse to clean physical file systems
* with this call, unless explicitly
* requested. This is extra paranoia just to
* be sure we never ever remove non-state
* data */
log_error("Attempted to remove disk file system, and we can't allow that.");
safe_close(fd);
return -EPERM;
}
}
d = fdopendir(fd);
if (!d) {
safe_close(fd);
return errno == ENOENT ? 0 : -errno;
}
for (;;) {
struct dirent *de;
bool is_dir;
struct stat st;
errno = 0;
de = readdir(d);
if (!de) {
if (errno != 0 && ret == 0)
ret = -errno;
return ret;
}
if (streq(de->d_name, ".") || streq(de->d_name, ".."))
continue;
if (de->d_type == DT_UNKNOWN || (de->d_type == DT_DIR && root_dev)) {
if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
continue;
}
is_dir = S_ISDIR(st.st_mode);
} else
is_dir = de->d_type == DT_DIR;
if (is_dir) {
int subdir_fd;
/* if root_dev is set, remove subdirectories only, if device is same as dir */
if (root_dev && st.st_dev != root_dev->st_dev)
continue;
subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
if (subdir_fd < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
continue;
}
/* We pass REMOVE_PHYSICAL here, to avoid
* doing the fstatfs() to check the file
* system type again for each directory */
r = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev);
if (r < 0 && ret == 0)
ret = r;
if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
}
} else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
if (unlinkat(fd, de->d_name, 0) < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
}
}
}
}
int rm_rf(const char *path, RemoveFlags flags) {
int fd, r;
struct statfs s;
assert(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) {
if (errno != ENOTDIR && errno != ELOOP)
return -errno;
if (!(flags & REMOVE_PHYSICAL)) {
if (statfs(path, &s) < 0)
return -errno;
if (!is_temporary_fs(&s)) {
log_error("Attempted to remove disk file system, and we can't allow that.");
return -EPERM;
}
}
if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES))
if (unlink(path) < 0 && errno != ENOENT)
return -errno;
return 0;
}
r = rm_rf_children(fd, flags, NULL);
if (flags & REMOVE_ROOT) {
if (rmdir(path) < 0 && errno != ENOENT) {
if (r == 0)
r = -errno;
}
}
return r;
}

33
src/shared/rm-rf.h Normal file
View file

@ -0,0 +1,33 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2015 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/stat.h>
typedef enum RemoveFlags {
REMOVE_ONLY_DIRECTORIES = 1,
REMOVE_ROOT = 2,
REMOVE_PHYSICAL = 4, /* if not set, only removes files on tmpfs, never physical file systems */
} RemoveFlags;
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
int rm_rf(const char *path, RemoveFlags flags);

View file

@ -29,10 +29,11 @@
#include "util.h" #include "util.h"
#include "path-util.h" #include "path-util.h"
#include "switch-root.h"
#include "mkdir.h" #include "mkdir.h"
#include "rm-rf.h"
#include "base-filesystem.h" #include "base-filesystem.h"
#include "missing.h" #include "missing.h"
#include "switch-root.h"
int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags) { int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags) {
@ -142,7 +143,7 @@ int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot,
if (fstat(old_root_fd, &rb) < 0) if (fstat(old_root_fd, &rb) < 0)
log_warning_errno(errno, "Failed to stat old root directory, leaving: %m"); log_warning_errno(errno, "Failed to stat old root directory, leaving: %m");
else { else {
rm_rf_children(old_root_fd, false, false, &rb); (void) rm_rf_children(old_root_fd, 0, &rb);
old_root_fd = -1; old_root_fd = -1;
} }
} }

View file

@ -2988,101 +2988,14 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
return 0; return 0;
} }
int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) { bool is_temporary_fs(const struct statfs *s) {
_cleanup_closedir_ DIR *d = NULL;
int ret = 0;
assert(fd >= 0);
/* This returns the first error we run into, but nevertheless
* tries to go on. This closes the passed fd. */
d = fdopendir(fd);
if (!d) {
safe_close(fd);
return errno == ENOENT ? 0 : -errno;
}
for (;;) {
struct dirent *de;
bool is_dir, keep_around;
struct stat st;
int r;
errno = 0;
de = readdir(d);
if (!de) {
if (errno != 0 && ret == 0)
ret = -errno;
return ret;
}
if (streq(de->d_name, ".") || streq(de->d_name, ".."))
continue;
if (de->d_type == DT_UNKNOWN ||
honour_sticky ||
(de->d_type == DT_DIR && root_dev)) {
if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
continue;
}
is_dir = S_ISDIR(st.st_mode);
keep_around =
honour_sticky &&
(st.st_uid == 0 || st.st_uid == getuid()) &&
(st.st_mode & S_ISVTX);
} else {
is_dir = de->d_type == DT_DIR;
keep_around = false;
}
if (is_dir) {
int subdir_fd;
/* if root_dev is set, remove subdirectories only, if device is same as dir */
if (root_dev && st.st_dev != root_dev->st_dev)
continue;
subdir_fd = openat(fd, de->d_name,
O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
if (subdir_fd < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
continue;
}
r = rm_rf_children_dangerous(subdir_fd, only_dirs, honour_sticky, root_dev);
if (r < 0 && ret == 0)
ret = r;
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)
ret = -errno;
}
}
}
}
_pure_ static int is_temporary_fs(struct statfs *s) {
assert(s); assert(s);
return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) || return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC); F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
} }
int is_fd_on_temporary_fs(int fd) { int fd_is_temporary_fs(int fd) {
struct statfs s; struct statfs s;
if (fstatfs(fd, &s) < 0) if (fstatfs(fd, &s) < 0)
@ -3091,114 +3004,6 @@ int is_fd_on_temporary_fs(int fd) {
return is_temporary_fs(&s); return is_temporary_fs(&s);
} }
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) {
safe_close(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 (!is_temporary_fs(&s)) {
log_error("Attempted to remove disk file system, and we can't allow that.");
safe_close(fd);
return -EPERM;
}
return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
}
static int file_is_priv_sticky(const char *p) {
struct stat st;
assert(p);
if (lstat(p, &st) < 0)
return -errno;
return
(st.st_uid == 0 || st.st_uid == getuid()) &&
(st.st_mode & S_ISVTX);
}
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);
/* 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) {
if (errno != ENOTDIR && errno != ELOOP)
return -errno;
if (!dangerous) {
if (statfs(path, &s) < 0)
return -errno;
if (!is_temporary_fs(&s)) {
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;
return 0;
}
if (!dangerous) {
if (fstatfs(fd, &s) < 0) {
safe_close(fd);
return -errno;
}
if (!is_temporary_fs(&s)) {
log_error("Attempted to remove disk file system, and we can't allow that.");
safe_close(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)
return r;
if (rmdir(path) < 0 && errno != ENOENT) {
if (r == 0)
r = -errno;
}
}
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) { int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
assert(path); assert(path);

View file

@ -41,6 +41,7 @@
#include <locale.h> #include <locale.h>
#include <mntent.h> #include <mntent.h>
#include <sys/inotify.h> #include <sys/inotify.h>
#include <sys/statfs.h>
#if SIZEOF_PID_T == 4 #if SIZEOF_PID_T == 4
# define PID_PRI PRIi32 # define PID_PRI PRIi32
@ -461,12 +462,8 @@ 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 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 fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
int is_fd_on_temporary_fs(int fd); bool is_temporary_fs(const struct statfs *s) _pure_;
int fd_is_temporary_fs(int fd);
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); int pipe_eof(int fd);

View file

@ -26,7 +26,7 @@
#include "macro.h" #include "macro.h"
#include "strv.h" #include "strv.h"
#include "util.h" #include "util.h"
#include "rm-rf.h"
static void setup_test_dir(char *tmp_dir, const char *files, ...) { static void setup_test_dir(char *tmp_dir, const char *files, ...) {
va_list ap; va_list ap;
@ -74,7 +74,7 @@ static void test_conf_files_list(bool use_root) {
assert_se(streq_ptr(found_files[1], expect_b)); assert_se(streq_ptr(found_files[1], expect_b));
assert_se(found_files[2] == NULL); assert_se(found_files[2] == NULL);
assert_se(rm_rf_dangerous(tmp_dir, false, true, false) == 0); assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {

View file

@ -26,6 +26,7 @@
#include "strv.h" #include "strv.h"
#include "macro.h" #include "macro.h"
#include "util.h" #include "util.h"
#include "rm-rf.h"
static void test_copy_file(void) { static void test_copy_file(void) {
_cleanup_free_ char *buf = NULL; _cleanup_free_ char *buf = NULL;
@ -86,8 +87,8 @@ static void test_copy_tree(void) {
"link2", "dir1/file"); "link2", "dir1/file");
char **p, **link; char **p, **link;
rm_rf_dangerous(copy_dir, false, true, false); (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
rm_rf_dangerous(original_dir, false, true, false); (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
STRV_FOREACH(p, files) { STRV_FOREACH(p, files) {
char *f = strjoina(original_dir, *p); char *f = strjoina(original_dir, *p);
@ -128,8 +129,8 @@ static void test_copy_tree(void) {
assert_se(copy_tree(original_dir, copy_dir, false) < 0); assert_se(copy_tree(original_dir, copy_dir, false) < 0);
assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, false) < 0); assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, false) < 0);
rm_rf_dangerous(copy_dir, false, true, false); (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
rm_rf_dangerous(original_dir, false, true, false); (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {

View file

@ -24,6 +24,7 @@
#include "util.h" #include "util.h"
#include "macro.h" #include "macro.h"
#include "mkdir.h" #include "mkdir.h"
#include "rm-rf.h"
typedef void (*test_function_t)(Manager *m); typedef void (*test_function_t)(Manager *m);
@ -72,7 +73,7 @@ static void test_exec_workingdirectory(Manager *m) {
test(m, "exec-workingdirectory.service", 0, CLD_EXITED); test(m, "exec-workingdirectory.service", 0, CLD_EXITED);
rm_rf_dangerous("/tmp/test-exec_workingdirectory", false, true, false); (void) rm_rf("/tmp/test-exec_workingdirectory", REMOVE_ROOT|REMOVE_PHYSICAL);
} }
static void test_exec_personality(Manager *m) { static void test_exec_personality(Manager *m) {

View file

@ -24,6 +24,7 @@
#include "path-lookup.h" #include "path-lookup.h"
#include "log.h" #include "log.h"
#include "strv.h" #include "strv.h"
#include "rm-rf.h"
static void test_paths(SystemdRunningAs running_as, bool personal) { static void test_paths(SystemdRunningAs running_as, bool personal) {
char template[] = "/tmp/test-path-lookup.XXXXXXX"; char template[] = "/tmp/test-path-lookup.XXXXXXX";
@ -42,7 +43,7 @@ static void test_paths(SystemdRunningAs running_as, bool personal) {
assert_se(strv_contains(lp.unit_path, exists)); assert_se(strv_contains(lp.unit_path, exists));
assert_se(strv_contains(lp.unit_path, not)); assert_se(strv_contains(lp.unit_path, not));
assert_se(rm_rf_dangerous(template, false, true, false) >= 0); assert_se(rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
} }
static void print_generator_paths(SystemdRunningAs running_as) { static void print_generator_paths(SystemdRunningAs running_as) {

View file

@ -26,6 +26,7 @@
#include "util.h" #include "util.h"
#include "macro.h" #include "macro.h"
#include "strv.h" #include "strv.h"
#include "rm-rf.h"
#define test_path_compare(a, b, result) { \ #define test_path_compare(a, b, result) { \
assert_se(path_compare(a, b) == result); \ assert_se(path_compare(a, b) == result); \
@ -256,7 +257,7 @@ static void test_strv_resolve(void) {
assert_se(streq(search_dirs[1], "/dir2")); assert_se(streq(search_dirs[1], "/dir2"));
assert_se(streq(search_dirs[2], "/dir2")); assert_se(streq(search_dirs[2], "/dir2"));
assert_se(rm_rf_dangerous(tmp_dir, false, true, false) == 0); assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
} }
static void test_path_startswith(void) { static void test_path_startswith(void) {

View file

@ -26,6 +26,7 @@
#include "macro.h" #include "macro.h"
#include "strv.h" #include "strv.h"
#include "mkdir.h" #include "mkdir.h"
#include "rm-rf.h"
typedef void (*test_function_t)(Manager *m); typedef void (*test_function_t)(Manager *m);
@ -47,7 +48,12 @@ static int setup_test(Manager **m) {
assert_se(manager_startup(tmp, NULL, NULL) >= 0); assert_se(manager_startup(tmp, NULL, NULL) >= 0);
STRV_FOREACH(test_path, tests_path) { STRV_FOREACH(test_path, tests_path) {
rm_rf_dangerous(strjoina("/tmp/test-path_", *test_path), false, true, false); _cleanup_free_ char *p = NULL;
p = strjoin("/tmp/test-path_", *test_path, NULL);
assert_se(p);
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
} }
*m = tmp; *m = tmp;
@ -104,7 +110,7 @@ static void check_stop_unlink(Manager *m, Unit *unit, const char *test_path, con
} }
assert_se(UNIT_VTABLE(unit)->stop(unit) >= 0); assert_se(UNIT_VTABLE(unit)->stop(unit) >= 0);
rm_rf_dangerous(test_path, false, true, false); (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
} }
static void test_path_exists(Manager *m) { static void test_path_exists(Manager *m) {
@ -228,7 +234,7 @@ static void test_path_makedirectory_directorymode(Manager *m) {
assert_se((s.st_mode & S_IRWXO) == 0004); assert_se((s.st_mode & S_IRWXO) == 0004);
assert_se(UNIT_VTABLE(unit)->stop(unit) >= 0); assert_se(UNIT_VTABLE(unit)->stop(unit) >= 0);
rm_rf_dangerous(test_path, false, true, false); (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {

View file

@ -31,6 +31,7 @@
#include "util.h" #include "util.h"
#include "mkdir.h" #include "mkdir.h"
#include "rm-rf.h"
#include "strv.h" #include "strv.h"
#include "def.h" #include "def.h"
#include "fileio.h" #include "fileio.h"
@ -1017,7 +1018,7 @@ static void test_readlink_and_make_absolute(void) {
free(r); free(r);
assert_se(unlink(name_alias) >= 0); assert_se(unlink(name_alias) >= 0);
assert_se(rm_rf_dangerous(tempdir, false, true, false) >= 0); assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
} }
static void test_read_one_char(void) { static void test_read_one_char(void) {
@ -1274,8 +1275,8 @@ static void test_execute_directory(void) {
assert_se(access("it_works2", F_OK) >= 0); assert_se(access("it_works2", F_OK) >= 0);
assert_se(access("failed", F_OK) < 0); assert_se(access("failed", F_OK) < 0);
rm_rf_dangerous(template_lo, false, true, false); (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
rm_rf_dangerous(template_hi, false, true, false); (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
} }
static void test_unquote_first_word(void) { static void test_unquote_first_word(void) {

View file

@ -52,6 +52,7 @@
#include "specifier.h" #include "specifier.h"
#include "build.h" #include "build.h"
#include "copy.h" #include "copy.h"
#include "rm-rf.h"
#include "selinux-util.h" #include "selinux-util.h"
#include "btrfs-util.h" #include "btrfs-util.h"
#include "acl-util.h" #include "acl-util.h"
@ -1359,7 +1360,7 @@ static int remove_item_instance(Item *i, const char *instance) {
/* FIXME: we probably should use dir_cleanup() here /* FIXME: we probably should use dir_cleanup() here
* instead of rm_rf() so that 'x' is honoured. */ * instead of rm_rf() so that 'x' is honoured. */
log_debug("rm -rf \"%s\"", instance); log_debug("rm -rf \"%s\"", instance);
r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false); r = rm_rf(instance, (i->type == RECURSIVE_REMOVE_PATH ? REMOVE_ROOT : 0) | REMOVE_PHYSICAL);
if (r < 0 && r != -ENOENT) if (r < 0 && r != -ENOENT)
return log_error_errno(r, "rm_rf(%s): %m", instance); return log_error_errno(r, "rm_rf(%s): %m", instance);