1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2017-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_trans_resv.h" |
11 | #include "xfs_mount.h" |
12 | #include "xfs_log_format.h" |
13 | #include "xfs_trans.h" |
14 | #include "xfs_inode.h" |
15 | #include "xfs_icache.h" |
16 | #include "xfs_dir2.h" |
17 | #include "xfs_dir2_priv.h" |
18 | #include "xfs_health.h" |
19 | #include "scrub/scrub.h" |
20 | #include "scrub/common.h" |
21 | #include "scrub/dabtree.h" |
22 | #include "scrub/readdir.h" |
23 | #include "scrub/health.h" |
24 | |
25 | /* Set us up to scrub directories. */ |
26 | int |
27 | xchk_setup_directory( |
28 | struct xfs_scrub *sc) |
29 | { |
30 | return xchk_setup_inode_contents(sc, 0); |
31 | } |
32 | |
33 | /* Directories */ |
34 | |
35 | /* Scrub a directory entry. */ |
36 | |
37 | /* Check that an inode's mode matches a given XFS_DIR3_FT_* type. */ |
38 | STATIC void |
39 | xchk_dir_check_ftype( |
40 | struct xfs_scrub *sc, |
41 | xfs_fileoff_t offset, |
42 | struct xfs_inode *ip, |
43 | int ftype) |
44 | { |
45 | struct xfs_mount *mp = sc->mp; |
46 | |
47 | if (!xfs_has_ftype(mp)) { |
48 | if (ftype != XFS_DIR3_FT_UNKNOWN && ftype != XFS_DIR3_FT_DIR) |
49 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
50 | return; |
51 | } |
52 | |
53 | if (xfs_mode_to_ftype(VFS_I(ip)->i_mode) != ftype) |
54 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
55 | } |
56 | |
57 | /* |
58 | * Scrub a single directory entry. |
59 | * |
60 | * Check the inode number to make sure it's sane, then we check that we can |
61 | * look up this filename. Finally, we check the ftype. |
62 | */ |
63 | STATIC int |
64 | xchk_dir_actor( |
65 | struct xfs_scrub *sc, |
66 | struct xfs_inode *dp, |
67 | xfs_dir2_dataptr_t dapos, |
68 | const struct xfs_name *name, |
69 | xfs_ino_t ino, |
70 | void *priv) |
71 | { |
72 | struct xfs_mount *mp = dp->i_mount; |
73 | struct xfs_inode *ip; |
74 | xfs_ino_t lookup_ino; |
75 | xfs_dablk_t offset; |
76 | int error = 0; |
77 | |
78 | offset = xfs_dir2_db_to_da(mp->m_dir_geo, |
79 | xfs_dir2_dataptr_to_db(mp->m_dir_geo, dapos)); |
80 | |
81 | if (xchk_should_terminate(sc, &error)) |
82 | return error; |
83 | |
84 | /* Does this inode number make sense? */ |
85 | if (!xfs_verify_dir_ino(mp, ino)) { |
86 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
87 | return -ECANCELED; |
88 | } |
89 | |
90 | /* Does this name make sense? */ |
91 | if (!xfs_dir2_namecheck(name->name, name->len)) { |
92 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
93 | return -ECANCELED; |
94 | } |
95 | |
96 | if (xfs_dir2_samename(name, &xfs_name_dot)) { |
97 | /* If this is "." then check that the inum matches the dir. */ |
98 | if (ino != dp->i_ino) |
99 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
100 | } else if (xfs_dir2_samename(name, &xfs_name_dotdot)) { |
101 | /* |
102 | * If this is ".." in the root inode, check that the inum |
103 | * matches this dir. |
104 | */ |
105 | if (dp->i_ino == mp->m_sb.sb_rootino && ino != dp->i_ino) |
106 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
107 | } |
108 | |
109 | /* Verify that we can look up this name by hash. */ |
110 | error = xchk_dir_lookup(sc, dp, name, &lookup_ino); |
111 | /* ENOENT means the hash lookup failed and the dir is corrupt */ |
112 | if (error == -ENOENT) |
113 | error = -EFSCORRUPTED; |
114 | if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, offset, &error)) |
115 | goto out; |
116 | if (lookup_ino != ino) { |
117 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
118 | return -ECANCELED; |
119 | } |
120 | |
121 | /* |
122 | * Grab the inode pointed to by the dirent. We release the inode |
123 | * before we cancel the scrub transaction. |
124 | * |
125 | * If _iget returns -EINVAL or -ENOENT then the child inode number is |
126 | * garbage and the directory is corrupt. If the _iget returns |
127 | * -EFSCORRUPTED or -EFSBADCRC then the child is corrupt which is a |
128 | * cross referencing error. Any other error is an operational error. |
129 | */ |
130 | error = xchk_iget(sc, ino, &ip); |
131 | if (error == -EINVAL || error == -ENOENT) { |
132 | error = -EFSCORRUPTED; |
133 | xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error); |
134 | goto out; |
135 | } |
136 | if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, offset, &error)) |
137 | goto out; |
138 | |
139 | xchk_dir_check_ftype(sc, offset, ip, name->type); |
140 | xchk_irele(sc, ip); |
141 | out: |
142 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
143 | return -ECANCELED; |
144 | return error; |
145 | } |
146 | |
147 | /* Scrub a directory btree record. */ |
148 | STATIC int |
149 | xchk_dir_rec( |
150 | struct xchk_da_btree *ds, |
151 | int level) |
152 | { |
153 | struct xfs_name dname = { }; |
154 | struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; |
155 | struct xfs_mount *mp = ds->state->mp; |
156 | struct xfs_inode *dp = ds->dargs.dp; |
157 | struct xfs_da_geometry *geo = mp->m_dir_geo; |
158 | struct xfs_dir2_data_entry *dent; |
159 | struct xfs_buf *bp; |
160 | struct xfs_dir2_leaf_entry *ent; |
161 | unsigned int end; |
162 | unsigned int iter_off; |
163 | xfs_ino_t ino; |
164 | xfs_dablk_t rec_bno; |
165 | xfs_dir2_db_t db; |
166 | xfs_dir2_data_aoff_t off; |
167 | xfs_dir2_dataptr_t ptr; |
168 | xfs_dahash_t calc_hash; |
169 | xfs_dahash_t hash; |
170 | struct xfs_dir3_icleaf_hdr hdr; |
171 | unsigned int tag; |
172 | int error; |
173 | |
174 | ASSERT(blk->magic == XFS_DIR2_LEAF1_MAGIC || |
175 | blk->magic == XFS_DIR2_LEAFN_MAGIC); |
176 | |
177 | xfs_dir2_leaf_hdr_from_disk(mp, &hdr, blk->bp->b_addr); |
178 | ent = hdr.ents + blk->index; |
179 | |
180 | /* Check the hash of the entry. */ |
181 | error = xchk_da_btree_hash(ds, level, &ent->hashval); |
182 | if (error) |
183 | goto out; |
184 | |
185 | /* Valid hash pointer? */ |
186 | ptr = be32_to_cpu(ent->address); |
187 | if (ptr == 0) |
188 | return 0; |
189 | |
190 | /* Find the directory entry's location. */ |
191 | db = xfs_dir2_dataptr_to_db(geo, ptr); |
192 | off = xfs_dir2_dataptr_to_off(geo, ptr); |
193 | rec_bno = xfs_dir2_db_to_da(geo, db); |
194 | |
195 | if (rec_bno >= geo->leafblk) { |
196 | xchk_da_set_corrupt(ds, level); |
197 | goto out; |
198 | } |
199 | error = xfs_dir3_data_read(ds->dargs.trans, dp, rec_bno, |
200 | XFS_DABUF_MAP_HOLE_OK, &bp); |
201 | if (!xchk_fblock_process_error(ds->sc, XFS_DATA_FORK, rec_bno, |
202 | &error)) |
203 | goto out; |
204 | if (!bp) { |
205 | xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); |
206 | goto out; |
207 | } |
208 | xchk_buffer_recheck(ds->sc, bp); |
209 | |
210 | if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
211 | goto out_relse; |
212 | |
213 | dent = bp->b_addr + off; |
214 | |
215 | /* Make sure we got a real directory entry. */ |
216 | iter_off = geo->data_entry_offset; |
217 | end = xfs_dir3_data_end_offset(geo, bp->b_addr); |
218 | if (!end) { |
219 | xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); |
220 | goto out_relse; |
221 | } |
222 | for (;;) { |
223 | struct xfs_dir2_data_entry *dep = bp->b_addr + iter_off; |
224 | struct xfs_dir2_data_unused *dup = bp->b_addr + iter_off; |
225 | |
226 | if (iter_off >= end) { |
227 | xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); |
228 | goto out_relse; |
229 | } |
230 | |
231 | if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { |
232 | iter_off += be16_to_cpu(dup->length); |
233 | continue; |
234 | } |
235 | if (dep == dent) |
236 | break; |
237 | iter_off += xfs_dir2_data_entsize(mp, dep->namelen); |
238 | } |
239 | |
240 | /* Retrieve the entry, sanity check it, and compare hashes. */ |
241 | ino = be64_to_cpu(dent->inumber); |
242 | hash = be32_to_cpu(ent->hashval); |
243 | tag = be16_to_cpup(xfs_dir2_data_entry_tag_p(mp, dent)); |
244 | if (!xfs_verify_dir_ino(mp, ino) || tag != off) |
245 | xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); |
246 | if (dent->namelen == 0) { |
247 | xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); |
248 | goto out_relse; |
249 | } |
250 | |
251 | /* Does the directory hash match? */ |
252 | dname.name = dent->name; |
253 | dname.len = dent->namelen; |
254 | calc_hash = xfs_dir2_hashname(mp, &dname); |
255 | if (calc_hash != hash) |
256 | xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); |
257 | |
258 | out_relse: |
259 | xfs_trans_brelse(ds->dargs.trans, bp); |
260 | out: |
261 | return error; |
262 | } |
263 | |
264 | /* |
265 | * Is this unused entry either in the bestfree or smaller than all of |
266 | * them? We've already checked that the bestfrees are sorted longest to |
267 | * shortest, and that there aren't any bogus entries. |
268 | */ |
269 | STATIC void |
270 | xchk_directory_check_free_entry( |
271 | struct xfs_scrub *sc, |
272 | xfs_dablk_t lblk, |
273 | struct xfs_dir2_data_free *bf, |
274 | struct xfs_dir2_data_unused *dup) |
275 | { |
276 | struct xfs_dir2_data_free *dfp; |
277 | unsigned int dup_length; |
278 | |
279 | dup_length = be16_to_cpu(dup->length); |
280 | |
281 | /* Unused entry is shorter than any of the bestfrees */ |
282 | if (dup_length < be16_to_cpu(bf[XFS_DIR2_DATA_FD_COUNT - 1].length)) |
283 | return; |
284 | |
285 | for (dfp = &bf[XFS_DIR2_DATA_FD_COUNT - 1]; dfp >= bf; dfp--) |
286 | if (dup_length == be16_to_cpu(dfp->length)) |
287 | return; |
288 | |
289 | /* Unused entry should be in the bestfrees but wasn't found. */ |
290 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
291 | } |
292 | |
293 | /* Check free space info in a directory data block. */ |
294 | STATIC int |
295 | xchk_directory_data_bestfree( |
296 | struct xfs_scrub *sc, |
297 | xfs_dablk_t lblk, |
298 | bool is_block) |
299 | { |
300 | struct xfs_dir2_data_unused *dup; |
301 | struct xfs_dir2_data_free *dfp; |
302 | struct xfs_buf *bp; |
303 | struct xfs_dir2_data_free *bf; |
304 | struct xfs_mount *mp = sc->mp; |
305 | u16 tag; |
306 | unsigned int nr_bestfrees = 0; |
307 | unsigned int nr_frees = 0; |
308 | unsigned int smallest_bestfree; |
309 | int newlen; |
310 | unsigned int offset; |
311 | unsigned int end; |
312 | int error; |
313 | |
314 | if (is_block) { |
315 | /* dir block format */ |
316 | if (lblk != XFS_B_TO_FSBT(mp, XFS_DIR2_DATA_OFFSET)) |
317 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
318 | error = xfs_dir3_block_read(sc->tp, sc->ip, &bp); |
319 | } else { |
320 | /* dir data format */ |
321 | error = xfs_dir3_data_read(sc->tp, sc->ip, lblk, 0, &bp); |
322 | } |
323 | if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) |
324 | goto out; |
325 | xchk_buffer_recheck(sc, bp); |
326 | |
327 | /* XXX: Check xfs_dir3_data_hdr.pad is zero once we start setting it. */ |
328 | |
329 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
330 | goto out_buf; |
331 | |
332 | /* Do the bestfrees correspond to actual free space? */ |
333 | bf = xfs_dir2_data_bestfree_p(mp, bp->b_addr); |
334 | smallest_bestfree = UINT_MAX; |
335 | for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) { |
336 | offset = be16_to_cpu(dfp->offset); |
337 | if (offset == 0) |
338 | continue; |
339 | if (offset >= mp->m_dir_geo->blksize) { |
340 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
341 | goto out_buf; |
342 | } |
343 | dup = bp->b_addr + offset; |
344 | tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)); |
345 | |
346 | /* bestfree doesn't match the entry it points at? */ |
347 | if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG) || |
348 | be16_to_cpu(dup->length) != be16_to_cpu(dfp->length) || |
349 | tag != offset) { |
350 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
351 | goto out_buf; |
352 | } |
353 | |
354 | /* bestfree records should be ordered largest to smallest */ |
355 | if (smallest_bestfree < be16_to_cpu(dfp->length)) { |
356 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
357 | goto out_buf; |
358 | } |
359 | |
360 | smallest_bestfree = be16_to_cpu(dfp->length); |
361 | nr_bestfrees++; |
362 | } |
363 | |
364 | /* Make sure the bestfrees are actually the best free spaces. */ |
365 | offset = mp->m_dir_geo->data_entry_offset; |
366 | end = xfs_dir3_data_end_offset(mp->m_dir_geo, bp->b_addr); |
367 | |
368 | /* Iterate the entries, stopping when we hit or go past the end. */ |
369 | while (offset < end) { |
370 | dup = bp->b_addr + offset; |
371 | |
372 | /* Skip real entries */ |
373 | if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG)) { |
374 | struct xfs_dir2_data_entry *dep = bp->b_addr + offset; |
375 | |
376 | newlen = xfs_dir2_data_entsize(mp, dep->namelen); |
377 | if (newlen <= 0) { |
378 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, |
379 | lblk); |
380 | goto out_buf; |
381 | } |
382 | offset += newlen; |
383 | continue; |
384 | } |
385 | |
386 | /* Spot check this free entry */ |
387 | tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)); |
388 | if (tag != offset) { |
389 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
390 | goto out_buf; |
391 | } |
392 | |
393 | /* |
394 | * Either this entry is a bestfree or it's smaller than |
395 | * any of the bestfrees. |
396 | */ |
397 | xchk_directory_check_free_entry(sc, lblk, bf, dup); |
398 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
399 | goto out_buf; |
400 | |
401 | /* Move on. */ |
402 | newlen = be16_to_cpu(dup->length); |
403 | if (newlen <= 0) { |
404 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
405 | goto out_buf; |
406 | } |
407 | offset += newlen; |
408 | if (offset <= end) |
409 | nr_frees++; |
410 | } |
411 | |
412 | /* We're required to fill all the space. */ |
413 | if (offset != end) |
414 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
415 | |
416 | /* Did we see at least as many free slots as there are bestfrees? */ |
417 | if (nr_frees < nr_bestfrees) |
418 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
419 | out_buf: |
420 | xfs_trans_brelse(sc->tp, bp); |
421 | out: |
422 | return error; |
423 | } |
424 | |
425 | /* |
426 | * Does the free space length in the free space index block ($len) match |
427 | * the longest length in the directory data block's bestfree array? |
428 | * Assume that we've already checked that the data block's bestfree |
429 | * array is in order. |
430 | */ |
431 | STATIC void |
432 | xchk_directory_check_freesp( |
433 | struct xfs_scrub *sc, |
434 | xfs_dablk_t lblk, |
435 | struct xfs_buf *dbp, |
436 | unsigned int len) |
437 | { |
438 | struct xfs_dir2_data_free *dfp; |
439 | |
440 | dfp = xfs_dir2_data_bestfree_p(sc->mp, dbp->b_addr); |
441 | |
442 | if (len != be16_to_cpu(dfp->length)) |
443 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
444 | |
445 | if (len > 0 && be16_to_cpu(dfp->offset) == 0) |
446 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
447 | } |
448 | |
449 | /* Check free space info in a directory leaf1 block. */ |
450 | STATIC int |
451 | xchk_directory_leaf1_bestfree( |
452 | struct xfs_scrub *sc, |
453 | struct xfs_da_args *args, |
454 | xfs_dir2_db_t last_data_db, |
455 | xfs_dablk_t lblk) |
456 | { |
457 | struct xfs_dir3_icleaf_hdr leafhdr; |
458 | struct xfs_dir2_leaf_tail *ltp; |
459 | struct xfs_dir2_leaf *leaf; |
460 | struct xfs_buf *dbp; |
461 | struct xfs_buf *bp; |
462 | struct xfs_da_geometry *geo = sc->mp->m_dir_geo; |
463 | __be16 *bestp; |
464 | __u16 best; |
465 | __u32 hash; |
466 | __u32 lasthash = 0; |
467 | __u32 bestcount; |
468 | unsigned int stale = 0; |
469 | int i; |
470 | int error; |
471 | |
472 | /* Read the free space block. */ |
473 | error = xfs_dir3_leaf_read(sc->tp, sc->ip, lblk, &bp); |
474 | if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) |
475 | return error; |
476 | xchk_buffer_recheck(sc, bp); |
477 | |
478 | leaf = bp->b_addr; |
479 | xfs_dir2_leaf_hdr_from_disk(sc->ip->i_mount, &leafhdr, leaf); |
480 | ltp = xfs_dir2_leaf_tail_p(geo, leaf); |
481 | bestcount = be32_to_cpu(ltp->bestcount); |
482 | bestp = xfs_dir2_leaf_bests_p(ltp); |
483 | |
484 | if (xfs_has_crc(sc->mp)) { |
485 | struct xfs_dir3_leaf_hdr *hdr3 = bp->b_addr; |
486 | |
487 | if (hdr3->pad != cpu_to_be32(0)) |
488 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
489 | } |
490 | |
491 | /* |
492 | * There must be enough bestfree slots to cover all the directory data |
493 | * blocks that we scanned. It is possible for there to be a hole |
494 | * between the last data block and i_disk_size. This seems like an |
495 | * oversight to the scrub author, but as we have been writing out |
496 | * directories like this (and xfs_repair doesn't mind them) for years, |
497 | * that's what we have to check. |
498 | */ |
499 | if (bestcount != last_data_db + 1) { |
500 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
501 | goto out; |
502 | } |
503 | |
504 | /* Is the leaf count even remotely sane? */ |
505 | if (leafhdr.count > geo->leaf_max_ents) { |
506 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
507 | goto out; |
508 | } |
509 | |
510 | /* Leaves and bests don't overlap in leaf format. */ |
511 | if ((char *)&leafhdr.ents[leafhdr.count] > (char *)bestp) { |
512 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
513 | goto out; |
514 | } |
515 | |
516 | /* Check hash value order, count stale entries. */ |
517 | for (i = 0; i < leafhdr.count; i++) { |
518 | hash = be32_to_cpu(leafhdr.ents[i].hashval); |
519 | if (i > 0 && lasthash > hash) |
520 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
521 | lasthash = hash; |
522 | if (leafhdr.ents[i].address == |
523 | cpu_to_be32(XFS_DIR2_NULL_DATAPTR)) |
524 | stale++; |
525 | } |
526 | if (leafhdr.stale != stale) |
527 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
528 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
529 | goto out; |
530 | |
531 | /* Check all the bestfree entries. */ |
532 | for (i = 0; i < bestcount; i++, bestp++) { |
533 | best = be16_to_cpu(*bestp); |
534 | error = xfs_dir3_data_read(sc->tp, sc->ip, |
535 | xfs_dir2_db_to_da(args->geo, i), |
536 | XFS_DABUF_MAP_HOLE_OK, |
537 | &dbp); |
538 | if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, |
539 | &error)) |
540 | break; |
541 | |
542 | if (!dbp) { |
543 | if (best != NULLDATAOFF) { |
544 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, |
545 | lblk); |
546 | break; |
547 | } |
548 | continue; |
549 | } |
550 | |
551 | if (best == NULLDATAOFF) |
552 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
553 | else |
554 | xchk_directory_check_freesp(sc, lblk, dbp, best); |
555 | xfs_trans_brelse(sc->tp, dbp); |
556 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
557 | break; |
558 | } |
559 | out: |
560 | xfs_trans_brelse(sc->tp, bp); |
561 | return error; |
562 | } |
563 | |
564 | /* Check free space info in a directory freespace block. */ |
565 | STATIC int |
566 | xchk_directory_free_bestfree( |
567 | struct xfs_scrub *sc, |
568 | struct xfs_da_args *args, |
569 | xfs_dablk_t lblk) |
570 | { |
571 | struct xfs_dir3_icfree_hdr freehdr; |
572 | struct xfs_buf *dbp; |
573 | struct xfs_buf *bp; |
574 | __u16 best; |
575 | unsigned int stale = 0; |
576 | int i; |
577 | int error; |
578 | |
579 | /* Read the free space block */ |
580 | error = xfs_dir2_free_read(sc->tp, sc->ip, lblk, &bp); |
581 | if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) |
582 | return error; |
583 | xchk_buffer_recheck(sc, bp); |
584 | |
585 | if (xfs_has_crc(sc->mp)) { |
586 | struct xfs_dir3_free_hdr *hdr3 = bp->b_addr; |
587 | |
588 | if (hdr3->pad != cpu_to_be32(0)) |
589 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
590 | } |
591 | |
592 | /* Check all the entries. */ |
593 | xfs_dir2_free_hdr_from_disk(sc->ip->i_mount, &freehdr, bp->b_addr); |
594 | for (i = 0; i < freehdr.nvalid; i++) { |
595 | best = be16_to_cpu(freehdr.bests[i]); |
596 | if (best == NULLDATAOFF) { |
597 | stale++; |
598 | continue; |
599 | } |
600 | error = xfs_dir3_data_read(sc->tp, sc->ip, |
601 | (freehdr.firstdb + i) * args->geo->fsbcount, |
602 | 0, &dbp); |
603 | if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, |
604 | &error)) |
605 | goto out; |
606 | xchk_directory_check_freesp(sc, lblk, dbp, best); |
607 | xfs_trans_brelse(sc->tp, dbp); |
608 | } |
609 | |
610 | if (freehdr.nused + stale != freehdr.nvalid) |
611 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
612 | out: |
613 | xfs_trans_brelse(sc->tp, bp); |
614 | return error; |
615 | } |
616 | |
617 | /* Check free space information in directories. */ |
618 | STATIC int |
619 | xchk_directory_blocks( |
620 | struct xfs_scrub *sc) |
621 | { |
622 | struct xfs_bmbt_irec got; |
623 | struct xfs_da_args args = { |
624 | .dp = sc ->ip, |
625 | .whichfork = XFS_DATA_FORK, |
626 | .geo = sc->mp->m_dir_geo, |
627 | .trans = sc->tp, |
628 | }; |
629 | struct xfs_ifork *ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK); |
630 | struct xfs_mount *mp = sc->mp; |
631 | xfs_fileoff_t leaf_lblk; |
632 | xfs_fileoff_t free_lblk; |
633 | xfs_fileoff_t lblk; |
634 | struct xfs_iext_cursor icur; |
635 | xfs_dablk_t dabno; |
636 | xfs_dir2_db_t last_data_db = 0; |
637 | bool found; |
638 | bool is_block = false; |
639 | int error; |
640 | |
641 | /* Ignore local format directories. */ |
642 | if (ifp->if_format != XFS_DINODE_FMT_EXTENTS && |
643 | ifp->if_format != XFS_DINODE_FMT_BTREE) |
644 | return 0; |
645 | |
646 | lblk = XFS_B_TO_FSB(mp, XFS_DIR2_DATA_OFFSET); |
647 | leaf_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_LEAF_OFFSET); |
648 | free_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_FREE_OFFSET); |
649 | |
650 | /* Is this a block dir? */ |
651 | error = xfs_dir2_isblock(&args, &is_block); |
652 | if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) |
653 | goto out; |
654 | |
655 | /* Iterate all the data extents in the directory... */ |
656 | found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got); |
657 | while (found && !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) { |
658 | /* No more data blocks... */ |
659 | if (got.br_startoff >= leaf_lblk) |
660 | break; |
661 | |
662 | /* |
663 | * Check each data block's bestfree data. |
664 | * |
665 | * Iterate all the fsbcount-aligned block offsets in |
666 | * this directory. The directory block reading code is |
667 | * smart enough to do its own bmap lookups to handle |
668 | * discontiguous directory blocks. When we're done |
669 | * with the extent record, re-query the bmap at the |
670 | * next fsbcount-aligned offset to avoid redundant |
671 | * block checks. |
672 | */ |
673 | for (lblk = roundup((xfs_dablk_t)got.br_startoff, |
674 | args.geo->fsbcount); |
675 | lblk < got.br_startoff + got.br_blockcount; |
676 | lblk += args.geo->fsbcount) { |
677 | last_data_db = xfs_dir2_da_to_db(args.geo, lblk); |
678 | error = xchk_directory_data_bestfree(sc, lblk, |
679 | is_block); |
680 | if (error) |
681 | goto out; |
682 | } |
683 | dabno = got.br_startoff + got.br_blockcount; |
684 | lblk = roundup(dabno, args.geo->fsbcount); |
685 | found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got); |
686 | } |
687 | |
688 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
689 | goto out; |
690 | |
691 | /* Look for a leaf1 block, which has free info. */ |
692 | if (xfs_iext_lookup_extent(sc->ip, ifp, leaf_lblk, &icur, &got) && |
693 | got.br_startoff == leaf_lblk && |
694 | got.br_blockcount == args.geo->fsbcount && |
695 | !xfs_iext_next_extent(ifp, &icur, &got)) { |
696 | if (is_block) { |
697 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
698 | goto out; |
699 | } |
700 | error = xchk_directory_leaf1_bestfree(sc, &args, last_data_db, |
701 | leaf_lblk); |
702 | if (error) |
703 | goto out; |
704 | } |
705 | |
706 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
707 | goto out; |
708 | |
709 | /* Scan for free blocks */ |
710 | lblk = free_lblk; |
711 | found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got); |
712 | while (found && !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) { |
713 | /* |
714 | * Dirs can't have blocks mapped above 2^32. |
715 | * Single-block dirs shouldn't even be here. |
716 | */ |
717 | lblk = got.br_startoff; |
718 | if (lblk & ~0xFFFFFFFFULL) { |
719 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
720 | goto out; |
721 | } |
722 | if (is_block) { |
723 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); |
724 | goto out; |
725 | } |
726 | |
727 | /* |
728 | * Check each dir free block's bestfree data. |
729 | * |
730 | * Iterate all the fsbcount-aligned block offsets in |
731 | * this directory. The directory block reading code is |
732 | * smart enough to do its own bmap lookups to handle |
733 | * discontiguous directory blocks. When we're done |
734 | * with the extent record, re-query the bmap at the |
735 | * next fsbcount-aligned offset to avoid redundant |
736 | * block checks. |
737 | */ |
738 | for (lblk = roundup((xfs_dablk_t)got.br_startoff, |
739 | args.geo->fsbcount); |
740 | lblk < got.br_startoff + got.br_blockcount; |
741 | lblk += args.geo->fsbcount) { |
742 | error = xchk_directory_free_bestfree(sc, &args, |
743 | lblk); |
744 | if (error) |
745 | goto out; |
746 | } |
747 | dabno = got.br_startoff + got.br_blockcount; |
748 | lblk = roundup(dabno, args.geo->fsbcount); |
749 | found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got); |
750 | } |
751 | out: |
752 | return error; |
753 | } |
754 | |
755 | /* Scrub a whole directory. */ |
756 | int |
757 | xchk_directory( |
758 | struct xfs_scrub *sc) |
759 | { |
760 | int error; |
761 | |
762 | if (!S_ISDIR(VFS_I(sc->ip)->i_mode)) |
763 | return -ENOENT; |
764 | |
765 | if (xchk_file_looks_zapped(sc, XFS_SICK_INO_DIR_ZAPPED)) { |
766 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); |
767 | return 0; |
768 | } |
769 | |
770 | /* Plausible size? */ |
771 | if (sc->ip->i_disk_size < xfs_dir2_sf_hdr_size(0)) { |
772 | xchk_ino_set_corrupt(sc, sc->ip->i_ino); |
773 | return 0; |
774 | } |
775 | |
776 | /* Check directory tree structure */ |
777 | error = xchk_da_btree(sc, XFS_DATA_FORK, xchk_dir_rec, NULL); |
778 | if (error) |
779 | return error; |
780 | |
781 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
782 | return 0; |
783 | |
784 | /* Check the freespace. */ |
785 | error = xchk_directory_blocks(sc); |
786 | if (error) |
787 | return error; |
788 | |
789 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
790 | return 0; |
791 | |
792 | /* Look up every name in this directory by hash. */ |
793 | error = xchk_dir_walk(sc, sc->ip, xchk_dir_actor, NULL); |
794 | if (error && error != -ECANCELED) |
795 | return error; |
796 | |
797 | /* If the dir is clean, it is clearly not zapped. */ |
798 | xchk_mark_healthy_if_clean(sc, XFS_SICK_INO_DIR_ZAPPED); |
799 | return 0; |
800 | } |
801 | |
802 | /* |
803 | * Decide if this directory has been zapped to satisfy the inode and ifork |
804 | * verifiers. Checking and repairing should be postponed until the directory |
805 | * is fixed. |
806 | */ |
807 | bool |
808 | xchk_dir_looks_zapped( |
809 | struct xfs_inode *dp) |
810 | { |
811 | /* Repair zapped this dir's data fork a short time ago */ |
812 | if (xfs_ifork_zapped(dp, XFS_DATA_FORK)) |
813 | return true; |
814 | |
815 | /* |
816 | * If the dinode repair found a bad data fork, it will reset the fork |
817 | * to extents format with zero records and wait for the bmapbtd |
818 | * scrubber to reconstruct the block mappings. Directories always |
819 | * contain some content, so this is a clear sign of a zapped directory. |
820 | * The state checked by xfs_ifork_zapped is not persisted, so this is |
821 | * the secondary strategy if repairs are interrupted by a crash or an |
822 | * unmount. |
823 | */ |
824 | return dp->i_df.if_format == XFS_DINODE_FMT_EXTENTS && |
825 | dp->i_df.if_nextents == 0; |
826 | } |
827 | |