summaryrefslogtreecommitdiffstats
path: root/debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch128
1 files changed, 128 insertions, 0 deletions
diff --git a/debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch b/debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch
new file mode 100644
index 0000000..67d959c
--- /dev/null
+++ b/debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch
@@ -0,0 +1,128 @@
+From: Lennart Poettering <lennart@poettering.net>
+Date: Tue, 26 Jan 2021 16:47:07 +0100
+Subject: rm-rf: fstatat() might fail if containing dir has limited access
+ mode, patch that too
+
+(cherry picked from commit 1b55621dabf741dd963f59ac706ea62cd6e3e95c)
+(cherry picked from commit ce53b81a600e2162ee86e2f4d202e7f28eceb2c6)
+---
+ 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 4c39ce8..2f2ebc3 100644
+--- a/src/basic/rm-rf.c
++++ b/src/basic/rm-rf.c
+@@ -23,13 +23,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
+@@ -41,22 +66,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 (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;
++ 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;
+ }
+
+@@ -112,9 +161,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;
+ }
+