rm-rf: fail closed when the root check fails

path_is_root_at() returns a negative errno when it cannot determine whether
a target is the root file system. rm_rf_at() treats those errors as a
negative answer and continued with destructive operations.

Propagate root-check errors before modifying the target, while preserving
REMOVE_MISSING_OK for an absent path.

Follow-up for c0228b4fa3
This commit is contained in:
Luca Boccassi
2026-07-15 21:13:24 +01:00
parent 57db794b9d
commit 9dd2dab37e
2 changed files with 30 additions and 1 deletions
+14 -1
View File
@@ -443,7 +443,20 @@ int rm_rf_at(int dir_fd, const char *path, RemoveFlags flags) {
/* 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_is_root_at(dir_fd, path) > 0)
r = path_is_root_at(dir_fd, path);
if (r == -ENOENT) {
struct stat st;
if (fstatat(dir_fd, path, &st, AT_SYMLINK_NOFOLLOW) < 0)
r = -errno;
else
r = 0; /* The entry exists, but is most likely a dangling symlink. */
}
if (FLAGS_SET(flags, REMOVE_MISSING_OK) && r == -ENOENT)
return 0;
if (r < 0)
return log_error_errno(r, "Failed to determine whether '%s' is the root file system: %m", path);
if (r > 0)
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
"Attempted to remove entire root file system, and we can't allow that.");
+16
View File
@@ -119,4 +119,20 @@ TEST(rm_rf_chmod) {
test_rm_rf_chmod_inner();
}
TEST(rm_rf_dangling_symlink) {
_cleanup_(rm_rf_physical_and_freep) char *d = NULL;
const char *link;
ASSERT_OK(mkdtemp_malloc("/tmp/test-rm-rf-dangling.XXXXXXX", &d));
link = strjoina(d, "/link");
ASSERT_OK_ERRNO(symlink("missing", link));
ASSERT_OK(rm_rf(link, REMOVE_ROOT|REMOVE_PHYSICAL));
ASSERT_ERROR_ERRNO(lstat(link, &(struct stat) {}), ENOENT);
ASSERT_OK_ERRNO(symlink("missing", link));
ASSERT_OK(rm_rf(link, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_MISSING_OK));
ASSERT_ERROR_ERRNO(lstat(link, &(struct stat) {}), ENOENT);
}
DEFINE_TEST_MAIN(LOG_DEBUG);