]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
recurse-dir: optionally, call callback when entering/leaving toplevel dir, too
authorLennart Poettering <lennart@poettering.net>
Wed, 9 Nov 2022 10:31:15 +0000 (11:31 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 9 Nov 2022 17:55:58 +0000 (18:55 +0100)
So far recurse_dir() will call the callback whenever we enter a
directory, and then pass the struct dirent for that directory, and an fd
for the directory the dirent is part of (i.e. the parent of the
directory we call things for). For the top-level dir the function is
invoked for we will not call the callback however, because we have no
dirent for that, and not fd for the directory the top-level dir is part
of. Let's add a flag to call it anyway, and in that case pass a NULL
dirent and -1 as directory fd.

This is useful when we want to treat the top-level dir the same as any
dir further down.

This is done opt-in since the callback must be ablet to handle a NULL
dirent and a -1 directory fd.

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

index d16ca98f67a1785e4aa6d1c7bcdd9e5e91dde584..908e833501efbf30644844dea45a88116af3afa3 100644 (file)
@@ -126,6 +126,7 @@ int recurse_dir(
                 void *userdata) {
 
         _cleanup_free_ DirectoryEntries *de = NULL;
+        STRUCT_STATX_DEFINE(root_sx);
         int r;
 
         assert(dir_fd >= 0);
@@ -139,6 +140,26 @@ int recurse_dir(
         if (n_depth_max == UINT_MAX) /* special marker for "default" */
                 n_depth_max = DEFAULT_RECURSION_MAX;
 
+        if (FLAGS_SET(flags, RECURSE_DIR_TOPLEVEL)) {
+                if (statx_mask != 0) {
+                        r = statx_fallback(dir_fd, "", AT_EMPTY_PATH, statx_mask, &root_sx);
+                        if (r < 0)
+                                return r;
+                }
+
+                r = func(RECURSE_DIR_ENTER,
+                         path,
+                         -1, /* we have no parent fd */
+                         dir_fd,
+                         NULL, /* we have no dirent */
+                         statx_mask != 0 ? &root_sx : NULL,
+                         userdata);
+                if (IN_SET(r, RECURSE_DIR_LEAVE_DIRECTORY, RECURSE_DIR_SKIP_ENTRY))
+                        return 0;
+                if (r != RECURSE_DIR_CONTINUE)
+                        return r;
+        }
+
         r = readdir_all(dir_fd, flags, &de);
         if (r < 0)
                 return r;
@@ -397,7 +418,7 @@ int recurse_dir(
                                         p,
                                         statx_mask,
                                         n_depth_max - 1,
-                                        flags,
+                                        flags &~ RECURSE_DIR_TOPLEVEL, /* we already called the callback for this entry */
                                         func,
                                         userdata);
                         if (r != 0)
@@ -427,6 +448,19 @@ int recurse_dir(
                         return r;
         }
 
+        if (FLAGS_SET(flags, RECURSE_DIR_TOPLEVEL)) {
+
+                r = func(RECURSE_DIR_LEAVE,
+                         path,
+                         -1,
+                         dir_fd,
+                         NULL,
+                         statx_mask != 0 ? &root_sx : NULL,
+                         userdata);
+                if (!IN_SET(r, RECURSE_DIR_LEAVE_DIRECTORY, RECURSE_DIR_SKIP_ENTRY, RECURSE_DIR_CONTINUE))
+                        return r;
+        }
+
         return 0;
 }
 
index 779c91e90522bc266ded51a82db9adc848387397..c10c8ddc9de4db5868dbf6cd3c79cea9f0cf9180 100644 (file)
@@ -65,6 +65,7 @@ typedef enum RecurseDirFlags {
         RECURSE_DIR_ENSURE_TYPE  = 1 << 2,  /* guarantees that 'd_type' field of 'de' is not DT_UNKNOWN */
         RECURSE_DIR_SAME_MOUNT   = 1 << 3,  /* skips over subdirectories that are submounts */
         RECURSE_DIR_INODE_FD     = 1 << 4,  /* passes an opened inode fd (O_DIRECTORY fd in case of dirs, O_PATH otherwise) */
+        RECURSE_DIR_TOPLEVEL     = 1 << 5,  /* call RECURSE_DIR_ENTER/RECURSE_DIR_LEAVE once for top-level dir, too, with dir_fd=-1 and NULL dirent */
 } RecurseDirFlags;
 
 typedef struct DirectoryEntries {