void *userdata) {
_cleanup_free_ DirectoryEntries *de = NULL;
+ STRUCT_STATX_DEFINE(root_sx);
int r;
assert(dir_fd >= 0);
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;
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)
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;
}
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 {