1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2022-2023 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <djwong@kernel.org>
5 */
6 #include "xfs.h"
7 #include "xfs_fs.h"
8 #include "xfs_shared.h"
9 #include "xfs_format.h"
10 #include "xfs_log_format.h"
11 #include "xfs_trans_resv.h"
12 #include "xfs_mount.h"
13 #include "xfs_inode.h"
14 #include "xfs_dir2.h"
15 #include "xfs_dir2_priv.h"
16 #include "xfs_trace.h"
17 #include "xfs_bmap.h"
18 #include "xfs_trans.h"
19 #include "xfs_error.h"
20 #include "scrub/scrub.h"
21 #include "scrub/readdir.h"
22
23 /* Call a function for every entry in a shortform directory. */
24 STATIC int
xchk_dir_walk_sf(struct xfs_scrub * sc,struct xfs_inode * dp,xchk_dirent_fn dirent_fn,void * priv)25 xchk_dir_walk_sf(
26 struct xfs_scrub *sc,
27 struct xfs_inode *dp,
28 xchk_dirent_fn dirent_fn,
29 void *priv)
30 {
31 struct xfs_name name = {
32 .name = ".",
33 .len = 1,
34 .type = XFS_DIR3_FT_DIR,
35 };
36 struct xfs_mount *mp = dp->i_mount;
37 struct xfs_da_geometry *geo = mp->m_dir_geo;
38 struct xfs_dir2_sf_entry *sfep;
39 struct xfs_dir2_sf_hdr *sfp;
40 xfs_ino_t ino;
41 xfs_dir2_dataptr_t dapos;
42 unsigned int i;
43 int error;
44
45 ASSERT(dp->i_df.if_bytes == dp->i_disk_size);
46 ASSERT(dp->i_df.if_u1.if_data != NULL);
47
48 sfp = (struct xfs_dir2_sf_hdr *)dp->i_df.if_u1.if_data;
49
50 /* dot entry */
51 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
52 geo->data_entry_offset);
53
54 error = dirent_fn(sc, dp, dapos, &name, dp->i_ino, priv);
55 if (error)
56 return error;
57
58 /* dotdot entry */
59 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
60 geo->data_entry_offset +
61 xfs_dir2_data_entsize(mp, sizeof(".") - 1));
62 ino = xfs_dir2_sf_get_parent_ino(sfp);
63 name.name = "..";
64 name.len = 2;
65
66 error = dirent_fn(sc, dp, dapos, &name, ino, priv);
67 if (error)
68 return error;
69
70 /* iterate everything else */
71 sfep = xfs_dir2_sf_firstentry(sfp);
72 for (i = 0; i < sfp->count; i++) {
73 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
74 xfs_dir2_sf_get_offset(sfep));
75 ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
76 name.name = sfep->name;
77 name.len = sfep->namelen;
78 name.type = xfs_dir2_sf_get_ftype(mp, sfep);
79
80 error = dirent_fn(sc, dp, dapos, &name, ino, priv);
81 if (error)
82 return error;
83
84 sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
85 }
86
87 return 0;
88 }
89
90 /* Call a function for every entry in a block directory. */
91 STATIC int
xchk_dir_walk_block(struct xfs_scrub * sc,struct xfs_inode * dp,xchk_dirent_fn dirent_fn,void * priv)92 xchk_dir_walk_block(
93 struct xfs_scrub *sc,
94 struct xfs_inode *dp,
95 xchk_dirent_fn dirent_fn,
96 void *priv)
97 {
98 struct xfs_mount *mp = dp->i_mount;
99 struct xfs_da_geometry *geo = mp->m_dir_geo;
100 struct xfs_buf *bp;
101 unsigned int off, next_off, end;
102 int error;
103
104 error = xfs_dir3_block_read(sc->tp, dp, &bp);
105 if (error)
106 return error;
107
108 /* Walk each directory entry. */
109 end = xfs_dir3_data_end_offset(geo, bp->b_addr);
110 for (off = geo->data_entry_offset; off < end; off = next_off) {
111 struct xfs_name name = { };
112 struct xfs_dir2_data_unused *dup = bp->b_addr + off;
113 struct xfs_dir2_data_entry *dep = bp->b_addr + off;
114 xfs_ino_t ino;
115 xfs_dir2_dataptr_t dapos;
116
117 /* Skip an empty entry. */
118 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
119 next_off = off + be16_to_cpu(dup->length);
120 continue;
121 }
122
123 /* Otherwise, find the next entry and report it. */
124 next_off = off + xfs_dir2_data_entsize(mp, dep->namelen);
125 if (next_off > end)
126 break;
127
128 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, off);
129 ino = be64_to_cpu(dep->inumber);
130 name.name = dep->name;
131 name.len = dep->namelen;
132 name.type = xfs_dir2_data_get_ftype(mp, dep);
133
134 error = dirent_fn(sc, dp, dapos, &name, ino, priv);
135 if (error)
136 break;
137 }
138
139 xfs_trans_brelse(sc->tp, bp);
140 return error;
141 }
142
143 /* Read a leaf-format directory buffer. */
144 STATIC int
xchk_read_leaf_dir_buf(struct xfs_trans * tp,struct xfs_inode * dp,struct xfs_da_geometry * geo,xfs_dir2_off_t * curoff,struct xfs_buf ** bpp)145 xchk_read_leaf_dir_buf(
146 struct xfs_trans *tp,
147 struct xfs_inode *dp,
148 struct xfs_da_geometry *geo,
149 xfs_dir2_off_t *curoff,
150 struct xfs_buf **bpp)
151 {
152 struct xfs_iext_cursor icur;
153 struct xfs_bmbt_irec map;
154 struct xfs_ifork *ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);
155 xfs_dablk_t last_da;
156 xfs_dablk_t map_off;
157 xfs_dir2_off_t new_off;
158
159 *bpp = NULL;
160
161 /*
162 * Look for mapped directory blocks at or above the current offset.
163 * Truncate down to the nearest directory block to start the scanning
164 * operation.
165 */
166 last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
167 map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *curoff));
168
169 if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map))
170 return 0;
171 if (map.br_startoff >= last_da)
172 return 0;
173 xfs_trim_extent(&map, map_off, last_da - map_off);
174
175 /* Read the directory block of that first mapping. */
176 new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);
177 if (new_off > *curoff)
178 *curoff = new_off;
179
180 return xfs_dir3_data_read(tp, dp, map.br_startoff, 0, bpp);
181 }
182
183 /* Call a function for every entry in a leaf directory. */
184 STATIC int
xchk_dir_walk_leaf(struct xfs_scrub * sc,struct xfs_inode * dp,xchk_dirent_fn dirent_fn,void * priv)185 xchk_dir_walk_leaf(
186 struct xfs_scrub *sc,
187 struct xfs_inode *dp,
188 xchk_dirent_fn dirent_fn,
189 void *priv)
190 {
191 struct xfs_mount *mp = dp->i_mount;
192 struct xfs_da_geometry *geo = mp->m_dir_geo;
193 struct xfs_buf *bp = NULL;
194 xfs_dir2_off_t curoff = 0;
195 unsigned int offset = 0;
196 int error;
197
198 /* Iterate every directory offset in this directory. */
199 while (curoff < XFS_DIR2_LEAF_OFFSET) {
200 struct xfs_name name = { };
201 struct xfs_dir2_data_unused *dup;
202 struct xfs_dir2_data_entry *dep;
203 xfs_ino_t ino;
204 unsigned int length;
205 xfs_dir2_dataptr_t dapos;
206
207 /*
208 * If we have no buffer, or we're off the end of the
209 * current buffer, need to get another one.
210 */
211 if (!bp || offset >= geo->blksize) {
212 if (bp) {
213 xfs_trans_brelse(sc->tp, bp);
214 bp = NULL;
215 }
216
217 error = xchk_read_leaf_dir_buf(sc->tp, dp, geo, &curoff,
218 &bp);
219 if (error || !bp)
220 break;
221
222 /*
223 * Find our position in the block.
224 */
225 offset = geo->data_entry_offset;
226 curoff += geo->data_entry_offset;
227 }
228
229 /* Skip an empty entry. */
230 dup = bp->b_addr + offset;
231 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
232 length = be16_to_cpu(dup->length);
233 offset += length;
234 curoff += length;
235 continue;
236 }
237
238 /* Otherwise, find the next entry and report it. */
239 dep = bp->b_addr + offset;
240 length = xfs_dir2_data_entsize(mp, dep->namelen);
241
242 dapos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
243 ino = be64_to_cpu(dep->inumber);
244 name.name = dep->name;
245 name.len = dep->namelen;
246 name.type = xfs_dir2_data_get_ftype(mp, dep);
247
248 error = dirent_fn(sc, dp, dapos, &name, ino, priv);
249 if (error)
250 break;
251
252 /* Advance to the next entry. */
253 offset += length;
254 curoff += length;
255 }
256
257 if (bp)
258 xfs_trans_brelse(sc->tp, bp);
259 return error;
260 }
261
262 /*
263 * Call a function for every entry in a directory.
264 *
265 * Callers must hold the ILOCK. File types are XFS_DIR3_FT_*.
266 */
267 int
xchk_dir_walk(struct xfs_scrub * sc,struct xfs_inode * dp,xchk_dirent_fn dirent_fn,void * priv)268 xchk_dir_walk(
269 struct xfs_scrub *sc,
270 struct xfs_inode *dp,
271 xchk_dirent_fn dirent_fn,
272 void *priv)
273 {
274 struct xfs_da_args args = {
275 .dp = dp,
276 .geo = dp->i_mount->m_dir_geo,
277 .trans = sc->tp,
278 };
279 bool isblock;
280 int error;
281
282 if (xfs_is_shutdown(dp->i_mount))
283 return -EIO;
284
285 ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
286 ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
287
288 if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
289 return xchk_dir_walk_sf(sc, dp, dirent_fn, priv);
290
291 /* dir2 functions require that the data fork is loaded */
292 error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
293 if (error)
294 return error;
295
296 error = xfs_dir2_isblock(&args, &isblock);
297 if (error)
298 return error;
299
300 if (isblock)
301 return xchk_dir_walk_block(sc, dp, dirent_fn, priv);
302
303 return xchk_dir_walk_leaf(sc, dp, dirent_fn, priv);
304 }
305
306 /*
307 * Look up the inode number for an exact name in a directory.
308 *
309 * Callers must hold the ILOCK. File types are XFS_DIR3_FT_*. Names are not
310 * checked for correctness.
311 */
312 int
xchk_dir_lookup(struct xfs_scrub * sc,struct xfs_inode * dp,const struct xfs_name * name,xfs_ino_t * ino)313 xchk_dir_lookup(
314 struct xfs_scrub *sc,
315 struct xfs_inode *dp,
316 const struct xfs_name *name,
317 xfs_ino_t *ino)
318 {
319 struct xfs_da_args args = {
320 .dp = dp,
321 .geo = dp->i_mount->m_dir_geo,
322 .trans = sc->tp,
323 .name = name->name,
324 .namelen = name->len,
325 .filetype = name->type,
326 .hashval = xfs_dir2_hashname(dp->i_mount, name),
327 .whichfork = XFS_DATA_FORK,
328 .op_flags = XFS_DA_OP_OKNOENT,
329 };
330 bool isblock, isleaf;
331 int error;
332
333 if (xfs_is_shutdown(dp->i_mount))
334 return -EIO;
335
336 ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
337 ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
338
339 if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
340 error = xfs_dir2_sf_lookup(&args);
341 goto out_check_rval;
342 }
343
344 /* dir2 functions require that the data fork is loaded */
345 error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
346 if (error)
347 return error;
348
349 error = xfs_dir2_isblock(&args, &isblock);
350 if (error)
351 return error;
352
353 if (isblock) {
354 error = xfs_dir2_block_lookup(&args);
355 goto out_check_rval;
356 }
357
358 error = xfs_dir2_isleaf(&args, &isleaf);
359 if (error)
360 return error;
361
362 if (isleaf) {
363 error = xfs_dir2_leaf_lookup(&args);
364 goto out_check_rval;
365 }
366
367 error = xfs_dir2_node_lookup(&args);
368
369 out_check_rval:
370 if (error == -EEXIST)
371 error = 0;
372 if (!error)
373 *ino = args.inumber;
374 return error;
375 }
376