1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * Copyright (c) 2023-2024 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <djwong@kernel.org>
5 */
6 #ifndef __XFS_SCRUB_DIRTREE_H__
7 #define __XFS_SCRUB_DIRTREE_H__
8
9 /*
10 * Each of these represents one parent pointer path step in a chain going
11 * up towards the directory tree root. These are stored inside an xfarray.
12 */
13 struct xchk_dirpath_step {
14 /* Directory entry name associated with this parent link. */
15 xfblob_cookie name_cookie;
16 unsigned int name_len;
17
18 /* Handle of the parent directory. */
19 struct xfs_parent_rec pptr_rec;
20 };
21
22 enum xchk_dirpath_outcome {
23 XCHK_DIRPATH_SCANNING = 0, /* still being put together */
24 XCHK_DIRPATH_DELETE, /* delete this path */
25 XCHK_DIRPATH_CORRUPT, /* corruption detected in path */
26 XCHK_DIRPATH_LOOP, /* cycle detected further up */
27 XCHK_DIRPATH_STALE, /* path is stale */
28 XCHK_DIRPATH_OK, /* path reaches the root */
29
30 XREP_DIRPATH_DELETING, /* path is being deleted */
31 XREP_DIRPATH_DELETED, /* path has been deleted */
32 XREP_DIRPATH_ADOPTING, /* path is being adopted */
33 XREP_DIRPATH_ADOPTED, /* path has been adopted */
34 };
35
36 /*
37 * Each of these represents one parent pointer path out of the directory being
38 * scanned. These exist in-core, and hopefully there aren't more than a
39 * handful of them.
40 */
41 struct xchk_dirpath {
42 struct list_head list;
43
44 /* Index of the first step in this path. */
45 xfarray_idx_t first_step;
46
47 /* Index of the second step in this path. */
48 xfarray_idx_t second_step;
49
50 /* Inodes seen while walking this path. */
51 struct xino_bitmap seen_inodes;
52
53 /* Number of steps in this path. */
54 unsigned int nr_steps;
55
56 /* Which path is this? */
57 unsigned int path_nr;
58
59 /* What did we conclude from following this path? */
60 enum xchk_dirpath_outcome outcome;
61 };
62
63 struct xchk_dirtree_outcomes {
64 /* Number of XCHK_DIRPATH_DELETE */
65 unsigned int bad;
66
67 /* Number of XCHK_DIRPATH_CORRUPT or XCHK_DIRPATH_LOOP */
68 unsigned int suspect;
69
70 /* Number of XCHK_DIRPATH_OK */
71 unsigned int good;
72
73 /* Directory needs to be added to lost+found */
74 bool needs_adoption;
75 };
76
77 struct xchk_dirtree {
78 struct xfs_scrub *sc;
79
80 /* Root inode that we're looking for. */
81 xfs_ino_t root_ino;
82
83 /*
84 * This is the inode that we're scanning. The live update hook can
85 * continue to be called after xchk_teardown drops sc->ip but before
86 * it calls buf_cleanup, so we keep a copy.
87 */
88 xfs_ino_t scan_ino;
89
90 /*
91 * If we start deleting redundant paths to this subdirectory, this is
92 * the inode number of the surviving parent and the dotdot entry will
93 * be set to this value. If the value is NULLFSINO, then use @root_ino
94 * as a stand-in until the orphanage can adopt the subdirectory.
95 */
96 xfs_ino_t parent_ino;
97
98 /* Scratch buffer for scanning pptr xattrs */
99 struct xfs_parent_rec pptr_rec;
100 struct xfs_da_args pptr_args;
101
102 /* Name buffer */
103 struct xfs_name xname;
104 char namebuf[MAXNAMELEN];
105
106 /* Information for reparenting this directory. */
107 struct xrep_adoption adoption;
108
109 /*
110 * Hook into directory updates so that we can receive live updates
111 * from other writer threads.
112 */
113 struct xfs_dir_hook dhook;
114
115 /* Parent pointer update arguments. */
116 struct xfs_parent_args ppargs;
117
118 /* lock for everything below here */
119 struct mutex lock;
120
121 /* buffer for the live update functions to use for dirent names */
122 struct xfs_name hook_xname;
123 unsigned char hook_namebuf[MAXNAMELEN];
124
125 /*
126 * All path steps observed during this scan. Each of the path
127 * steps for a particular pathwalk are recorded in sequential
128 * order in the xfarray. A pathwalk ends either with a step
129 * pointing to the root directory (success) or pointing to NULLFSINO
130 * (loop detected, empty dir detected, etc).
131 */
132 struct xfarray *path_steps;
133
134 /* All names observed during this scan. */
135 struct xfblob *path_names;
136
137 /* All paths being tracked by this scanner. */
138 struct list_head path_list;
139
140 /* Number of paths in path_list. */
141 unsigned int nr_paths;
142
143 /* Number of parents found by a pptr scan. */
144 unsigned int parents_found;
145
146 /* Have the path data been invalidated by a concurrent update? */
147 bool stale:1;
148
149 /* Has the scan been aborted? */
150 bool aborted:1;
151 };
152
153 #define xchk_dirtree_for_each_path_safe(dl, path, n) \
154 list_for_each_entry_safe((path), (n), &(dl)->path_list, list)
155
156 #define xchk_dirtree_for_each_path(dl, path) \
157 list_for_each_entry((path), &(dl)->path_list, list)
158
159 static inline bool
xchk_dirtree_parentless(const struct xchk_dirtree * dl)160 xchk_dirtree_parentless(const struct xchk_dirtree *dl)
161 {
162 struct xfs_scrub *sc = dl->sc;
163
164 if (sc->ip == sc->mp->m_rootip)
165 return true;
166 if (VFS_I(sc->ip)->i_nlink == 0)
167 return true;
168 return false;
169 }
170
171 int xchk_dirtree_find_paths_to_root(struct xchk_dirtree *dl);
172 int xchk_dirpath_append(struct xchk_dirtree *dl, struct xfs_inode *ip,
173 struct xchk_dirpath *path, const struct xfs_name *name,
174 const struct xfs_parent_rec *pptr);
175 void xchk_dirtree_evaluate(struct xchk_dirtree *dl,
176 struct xchk_dirtree_outcomes *oc);
177
178 #endif /* __XFS_SCRUB_DIRTREE_H__ */
179