diff options
Diffstat (limited to '')
-rw-r--r-- | debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch | 128 |
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; + } + |