1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 #pragma once
3 
4 #include <dirent.h>
5 #include <limits.h>
6 
7 #include "errno-list.h"
8 #include "stat-util.h"
9 #include "macro.h"
10 
11 typedef enum RecurseDirEvent {
12         RECURSE_DIR_ENTER,      /* only for dir inodes */
13         RECURSE_DIR_LEAVE,      /* only for dir inodes */
14         RECURSE_DIR_ENTRY,      /* only for non-dir inodes */
15         RECURSE_DIR_SKIP_MOUNT, /* only for dir inodes: when we don't descent into submounts */
16         RECURSE_DIR_SKIP_DEPTH, /* only for dir inodes: when we reached the max depth */
17 
18         /* If we hit an error opening/stating an entry, then we'll fire a
19          * 'RECURSE_DIR_SKIP_{OPEN_DIR|OPEN_INODE|STAT_INODE}_ERROR_BASE + errno' event. In this case 'de'
20          * will be valid, but the statx data NULL and the inode fd -1. */
21         RECURSE_DIR_SKIP_OPEN_DIR_ERROR_BASE,
22         RECURSE_DIR_SKIP_OPEN_DIR_ERROR_MAX = RECURSE_DIR_SKIP_OPEN_DIR_ERROR_BASE + ERRNO_MAX,
23 
24         RECURSE_DIR_SKIP_OPEN_INODE_ERROR_BASE,
25         RECURSE_DIR_SKIP_OPEN_INODE_ERROR_MAX = RECURSE_DIR_SKIP_OPEN_INODE_ERROR_BASE + ERRNO_MAX,
26 
27         RECURSE_DIR_SKIP_STAT_INODE_ERROR_BASE,
28         RECURSE_DIR_SKIP_STAT_INODE_ERROR_MAX = RECURSE_DIR_SKIP_STAT_INODE_ERROR_BASE + ERRNO_MAX,
29 
30         _RECURSE_DIR_EVENT_MAX,
31         _RECURSE_DIR_EVENT_INVALID = -EINVAL,
32 } RecurseDirEvent;
33 
34 #define RECURSE_DIR_CONTINUE 0
35 #define RECURSE_DIR_LEAVE_DIRECTORY INT_MIN
36 #define RECURSE_DIR_SKIP_ENTRY (INT_MIN+1)
37 
38 /* Make sure that the negative errno range and these two special returns don't overlap */
39 assert_cc(RECURSE_DIR_LEAVE_DIRECTORY < -ERRNO_MAX);
40 assert_cc(RECURSE_DIR_SKIP_ENTRY < -ERRNO_MAX);
41 
42 /* Prototype for the callback function that is called whenever we enter or leave a dir inode, or find another dir entry. Return values are:
43  *
44  * RECURSE_DIR_CONTINUE (i.e. 0) → continue with next entry
45  * RECURSE_DIR_LEAVE_DIRECTORY   → leave current directory immediately, don't process further siblings
46  * RECURSE_DIR_SKIP_ENTRY        → skip this entry otherwise (only makes sense on RECURSE_DIR_ENTER)
47  * others                        → terminate iteration entirely, return the specified value (idea is that
48  *                                 < 0 indicates errors and > 0 indicates various forms of success)
49  */
50 typedef int (*recurse_dir_func_t)(
51                 RecurseDirEvent event,
52                 const char *path,        /* Full non-normalized path, i.e. the path specified during recurise_dir() with what we found appended */
53                 int dir_fd,              /* fd of the current dir */
54                 int inode_fd,            /* fd of the current entry in the current dir (O_DIRECTORY if directory, and O_PATH otherwise, but only if RECURSE_DIR_INODE_FD was set) */
55                 const struct dirent *de, /* directory entry (always valid) */
56                 const struct statx *sx,  /* statx data (only if statx_mask was non-zero) */
57                 void *userdata);
58 
59 typedef enum RecurseDirFlags {
60         /* Interpreted by readdir_all() */
61         RECURSE_DIR_SORT         = 1 << 0,  /* sort file directory entries before processing them */
62         RECURSE_DIR_IGNORE_DOT   = 1 << 1,  /* ignore all dot files ("." and ".." are always ignored) */
63 
64         /* Interpreted by recurse_dir() */
65         RECURSE_DIR_ENSURE_TYPE  = 1 << 2,  /* guarantees that 'd_type' field of 'de' is not DT_UNKNOWN */
66         RECURSE_DIR_SAME_MOUNT   = 1 << 3,  /* skips over subdirectories that are submounts */
67         RECURSE_DIR_INODE_FD     = 1 << 4,  /* passes an opened inode fd (O_DIRECTORY fd in case of dirs, O_PATH otherwise) */
68 } RecurseDirFlags;
69 
70 typedef struct DirectoryEntries {
71         size_t n_entries;
72         struct dirent** entries;
73         size_t buffer_size;
74         struct dirent buffer[];
75 } DirectoryEntries;
76 
77 int readdir_all(int dir_fd, RecurseDirFlags flags, DirectoryEntries **ret);
78 
79 int recurse_dir(int dir_fd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata);
80 int recurse_dir_at(int atfd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata);
81