]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
recursedir: introduce new RECURSE_DIR_UNLINK callback return value
authorLennart Poettering <lennart@poettering.net>
Fri, 28 Nov 2025 15:12:22 +0000 (16:12 +0100)
committerLennart Poettering <lennart@poettering.net>
Sun, 21 Dec 2025 06:04:42 +0000 (07:04 +0100)
This introduces RECURSE_DIR_UNLINK + RECURSE_DIR_UNLINK_GRACEFUL as new
return values for recurse_dir() callbacks. If either is returned the
inode currently processed will be removed and iteration continues with
the next item.

This is useful to have in the recurse_dir() mechanism itself (rather
than implement in the callback itself), due to ordering: we want to pin
the inodes via an fd while calling the callbacks, but we have to close
it before removal of the inodes. By moving this into the recurse_dir()
infra, we can implement this easily.

src/basic/recurse-dir.c
src/basic/recurse-dir.h

index 9eef4e1d8c42b815b7ea68bdee7ce6603cfa0b9e..f7f8e4a7ab6191aa7715c284108b766e472e34a9 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <sys/stat.h>
+#include <unistd.h>
 
 #include "alloc-util.h"
 #include "dirent-util.h"
@@ -433,30 +434,25 @@ int recurse_dir(
                                  i,
                                  statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
                                  userdata);
-                        if (r == RECURSE_DIR_LEAVE_DIRECTORY)
-                                break;
-                        if (r == RECURSE_DIR_SKIP_ENTRY)
-                                continue;
-                        if (r != RECURSE_DIR_CONTINUE)
-                                return r;
-
-                        r = recurse_dir(subdir_fd,
-                                        p,
-                                        statx_mask,
-                                        n_depth_max - 1,
-                                        flags & ~RECURSE_DIR_TOPLEVEL, /* we already called the callback for this entry */
-                                        func,
-                                        userdata);
-                        if (r != 0)
-                                return r;
+                        if (r == RECURSE_DIR_CONTINUE) {
+                                r = recurse_dir(subdir_fd,
+                                                p,
+                                                statx_mask,
+                                                n_depth_max - 1,
+                                                flags & ~RECURSE_DIR_TOPLEVEL, /* we already called the callback for this entry */
+                                                func,
+                                                userdata);
+                                if (r != 0)
+                                        return r;
 
-                        r = func(RECURSE_DIR_LEAVE,
-                                 p,
-                                 dir_fd,
-                                 subdir_fd,
-                                 i,
-                                 statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
-                                 userdata);
+                                r = func(RECURSE_DIR_LEAVE,
+                                         p,
+                                         dir_fd,
+                                         subdir_fd,
+                                         i,
+                                         statx_mask != 0 ? &sx : NULL, /* only pass sx if user asked for it */
+                                         userdata);
+                        }
                 } else
                         /* Non-directory inode */
                         r = func(RECURSE_DIR_ENTRY,
@@ -469,7 +465,21 @@ int recurse_dir(
 
                 if (r == RECURSE_DIR_LEAVE_DIRECTORY)
                         break;
-                if (!IN_SET(r, RECURSE_DIR_SKIP_ENTRY, RECURSE_DIR_CONTINUE))
+
+                if (IN_SET(r, RECURSE_DIR_UNLINK, RECURSE_DIR_UNLINK_GRACEFUL)) {
+                        int f = subdir_fd >= 0 ? AT_REMOVEDIR : 0;
+
+                        /* Close inodes before we try to delete them */
+                        subdir_fd = safe_close(subdir_fd);
+                        inode_fd = safe_close(inode_fd);
+
+                        if (unlinkat(dir_fd, i->d_name, f) < 0) {
+                                if (r != RECURSE_DIR_UNLINK_GRACEFUL)
+                                        return -errno;
+
+                                log_debug_errno(errno, "Unable to remove '%s', ignoring: %m", p);
+                        }
+                } else if (!IN_SET(r, RECURSE_DIR_SKIP_ENTRY, RECURSE_DIR_CONTINUE))
                         return r;
         }
 
index 68abb279f4076ef4f3cacf9f6a3a33d87567be00..af33c239f7017166f51c5f62abd2734d5bc74512 100644 (file)
@@ -53,6 +53,8 @@ typedef enum RecurseDirEvent {
 #define RECURSE_DIR_CONTINUE 0
 #define RECURSE_DIR_LEAVE_DIRECTORY INT_MIN
 #define RECURSE_DIR_SKIP_ENTRY (INT_MIN+1)
+#define RECURSE_DIR_UNLINK (INT_MIN+2)
+#define RECURSE_DIR_UNLINK_GRACEFUL (INT_MIN+3)
 
 /* Make sure that the negative errno range and these two special returns don't overlap */
 assert_cc(RECURSE_DIR_LEAVE_DIRECTORY < -ERRNO_MAX);