]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
rm-rf: make sure we can safely remove dirs we have no access to via rm_rf_at()
authorLennart Poettering <lennart@poettering.net>
Wed, 20 Aug 2025 09:39:41 +0000 (11:39 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Thu, 6 Nov 2025 21:26:42 +0000 (21:26 +0000)
Previously, we'd first empty a dir, and then remove it. This works fine
as long as we have access to a dir. But in some cases (like for example
a foreign owned container tree) we might not have access to the dir, but
are still able to remove it (because it is empty, and in a dir we own).
Hence let's try that first. If it works, we do not need to enter the dir
(and thus fail).

(cherry picked from commit 502f7a2b804370d32adb373e661831f583565075)

src/shared/rm-rf.c

index 1b3ecf079011a139dd8df845b1484149a34c772e..9d917e9597147785119dce30c7aaad9a8cb1e520 100644 (file)
@@ -449,12 +449,18 @@ int rm_rf_at(int dir_fd, const char *path, RemoveFlags flags) {
                 if (FLAGS_SET(flags, REMOVE_MISSING_OK) && r == -ENOENT)
                         return 0;
 
-                if (!IN_SET(r, -ENOTTY, -EINVAL, -ENOTDIR))
+                if (!IN_SET(r, -ENOTTY, -EINVAL, -ENOTDIR, -EPERM, -EACCES))
                         return r;
 
-                /* Not btrfs or not a subvolume */
+                /* Not btrfs or not a subvolume, or permissions are not available (but might if we go via unlinkat()) */
         }
 
+        /* In the next step we'll try to open the directory in order to enumerate its contents. This might
+         * not work due to perms, but we might still be able to delete it, hence let's try that first. */
+        if (FLAGS_SET(flags, REMOVE_ROOT | REMOVE_PHYSICAL))
+                if (unlinkat(dir_fd, path, AT_REMOVEDIR) >= 0)
+                        return 0;
+
         fd = openat_harder(dir_fd, path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME, flags, &old_mode);
         if (fd >= 0) {
                 /* We have a dir */