136 lines
4.6 KiB
Diff
136 lines
4.6 KiB
Diff
From 3e4d0a7c37021bc5f75fa429991f597428d1b63a Mon Sep 17 00:00:00 2001
|
|
From: Lennart Poettering <lennart@poettering.net>
|
|
Date: Tue, 26 Jan 2021 16:47:07 +0100
|
|
Subject: [PATCH 3/9] rm-rf: fstatat() might fail if containing dir has limited
|
|
access mode, patch that too
|
|
|
|
Conflict:according 1d6cc5d0e5658848ac8cce5da22626d17f15b1ec, use FLAGS_SET
|
|
instead of "=="
|
|
Reference:https://github.com/systemd/systemd/commit/1b55621dabf741dd963f59ac706ea62cd6e3e95c
|
|
|
|
(cherry picked from commit 1b55621dabf741dd963f59ac706ea62cd6e3e95c)
|
|
---
|
|
src/basic/rm-rf.c | 82 ++++++++++++++++++++++++++++++++++++++---------
|
|
1 file changed, 66 insertions(+), 16 deletions(-)
|
|
|
|
diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
|
|
index 26b943e..602266d 100644
|
|
--- a/src/basic/rm-rf.c
|
|
+++ b/src/basic/rm-rf.c
|
|
@@ -25,13 +25,38 @@ static bool is_physical_fs(const struct statfs *sfs) {
|
|
return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs);
|
|
}
|
|
|
|
+static int patch_dirfd_mode(
|
|
+ int dfd,
|
|
+ mode_t *ret_old_mode) {
|
|
+
|
|
+ struct stat st;
|
|
+
|
|
+ assert(dfd >= 0);
|
|
+ assert(ret_old_mode);
|
|
+
|
|
+ if (fstat(dfd, &st) < 0)
|
|
+ return -errno;
|
|
+ if (!S_ISDIR(st.st_mode))
|
|
+ return -ENOTDIR;
|
|
+ if (FLAGS_SET(st.st_mode, 0700)) /* Already set? */
|
|
+ return -EACCES; /* original error */
|
|
+ if (st.st_uid != geteuid()) /* this only works if the UID matches ours */
|
|
+ return -EACCES;
|
|
+
|
|
+ if (fchmod(dfd, (st.st_mode | 0700) & 07777) < 0)
|
|
+ return -errno;
|
|
+
|
|
+ *ret_old_mode = st.st_mode;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int unlinkat_harder(
|
|
int dfd,
|
|
const char *filename,
|
|
int unlink_flags,
|
|
RemoveFlags remove_flags) {
|
|
|
|
- struct stat st;
|
|
+ mode_t old_mode;
|
|
int r;
|
|
|
|
/* Like unlinkat(), but tries harder: if we get EACCESS we'll try to set the r/w/x bits on the
|
|
@@ -43,22 +68,46 @@ static int unlinkat_harder(
|
|
if (errno != EACCES || !FLAGS_SET(remove_flags, REMOVE_CHMOD))
|
|
return -errno;
|
|
|
|
- if (fstat(dfd, &st) < 0)
|
|
- return -errno;
|
|
- if (!S_ISDIR(st.st_mode))
|
|
- return -ENOTDIR;
|
|
- if ((st.st_mode & 0700) == 0700) /* Already set? */
|
|
- return -EACCES; /* original error */
|
|
- if (st.st_uid != geteuid()) /* this only works if the UID matches ours */
|
|
- return -EACCES;
|
|
-
|
|
- if (fchmod(dfd, (st.st_mode | 0700) & 07777) < 0)
|
|
- return -errno;
|
|
+ r = patch_dirfd_mode(dfd, &old_mode);
|
|
+ if (r < 0)
|
|
+ return r;
|
|
|
|
if (unlinkat(dfd, filename, unlink_flags) < 0) {
|
|
r = -errno;
|
|
/* Try to restore the original access mode if this didn't work */
|
|
- (void) fchmod(dfd, st.st_mode & 07777);
|
|
+ (void) fchmod(dfd, old_mode);
|
|
+ return r;
|
|
+ }
|
|
+
|
|
+ /* If this worked, we won't reset the old mode, since we'll need it for other entries too, and we
|
|
+ * should destroy the whole thing */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int fstatat_harder(
|
|
+ int dfd,
|
|
+ const char *filename,
|
|
+ struct stat *ret,
|
|
+ int fstatat_flags,
|
|
+ RemoveFlags remove_flags) {
|
|
+
|
|
+ mode_t old_mode;
|
|
+ int r;
|
|
+
|
|
+ /* Like unlink_harder() but does the same for fstatat() */
|
|
+
|
|
+ if (fstatat(dfd, filename, ret, fstatat_flags) >= 0)
|
|
+ return 0;
|
|
+ if (errno != EACCES || !FLAGS_SET(remove_flags, REMOVE_CHMOD))
|
|
+ return -errno;
|
|
+
|
|
+ r = patch_dirfd_mode(dfd, &old_mode);
|
|
+ if (r < 0)
|
|
+ return r;
|
|
+
|
|
+ if (fstatat(dfd, filename, ret, fstatat_flags) < 0) {
|
|
+ r = -errno;
|
|
+ (void) fchmod(dfd, old_mode);
|
|
return r;
|
|
}
|
|
|
|
@@ -114,9 +163,10 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
|
|
|
|
if (de->d_type == DT_UNKNOWN ||
|
|
(de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
|
|
- if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
|
|
- if (ret == 0 && errno != ENOENT)
|
|
- ret = -errno;
|
|
+ r = fstatat_harder(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW, flags);
|
|
+ if (r < 0) {
|
|
+ if (ret == 0 && r != -ENOENT)
|
|
+ ret = r;
|
|
continue;
|
|
}
|
|
|
|
--
|
|
2.23.0
|
|
|