]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0 |
360f4a2e ES |
2 | /* |
3 | * Copyright (c) 2015 Red Hat, Inc. | |
4 | * All Rights Reserved. | |
360f4a2e ES |
5 | */ |
6 | ||
7 | /* Various utilities for repair of directory and attribute metadata */ | |
8 | ||
9 | #include "libxfs.h" | |
10 | #include "globals.h" | |
11 | #include "err_protos.h" | |
12 | #include "bmap.h" | |
13 | #include "da_util.h" | |
14 | ||
360f4a2e ES |
15 | /* |
16 | * the cursor gets passed up and down the da btree processing | |
17 | * routines. The interior block processing routines use the | |
18 | * cursor to determine if the pointers to and from the preceding | |
19 | * and succeeding sibling blocks are ok and whether the values in | |
20 | * the current block are consistent with the entries in the parent | |
21 | * nodes. When a block is traversed, a parent-verification routine | |
22 | * is called to verify if the next logical entry in the next level up | |
23 | * is consistent with the greatest hashval in the next block of the | |
24 | * current level. The verification routine is itself recursive and | |
25 | * calls itself if it has to traverse an interior block to get | |
26 | * the next logical entry. The routine recurses upwards through | |
27 | * the tree until it finds a block where it can simply step to | |
28 | * the next entry. The hashval in that entry should be equal to | |
29 | * the hashval being passed to it (the greatest hashval in the block | |
30 | * that the entry points to). If that isn't true, then the tree | |
31 | * is blown and we need to trash it, salvage and trash it, or fix it. | |
32 | * Currently, we just trash it. | |
33 | */ | |
34 | ||
35 | /* | |
36 | * Multibuffer handling. | |
37 | * V2 directory blocks can be noncontiguous, needing multiple buffers. | |
5cd3b070 | 38 | * attr blocks are single blocks; this code handles that as well. |
360f4a2e ES |
39 | */ |
40 | struct xfs_buf * | |
41 | da_read_buf( | |
42 | xfs_mount_t *mp, | |
43 | int nex, | |
44 | bmap_ext_t *bmp, | |
45 | const struct xfs_buf_ops *ops) | |
46 | { | |
47 | #define MAP_ARRAY_SZ 4 | |
48 | struct xfs_buf_map map_array[MAP_ARRAY_SZ]; | |
49 | struct xfs_buf_map *map; | |
50 | struct xfs_buf *bp; | |
51 | int i; | |
52 | ||
53 | if (nex > MAP_ARRAY_SZ) { | |
54 | map = calloc(nex, sizeof(*map)); | |
55 | if (map == NULL) { | |
56 | do_error(_("couldn't malloc dir2 buffer list\n")); | |
57 | exit(1); | |
58 | } | |
59 | } else { | |
60 | /* common case avoids calloc/free */ | |
61 | map = map_array; | |
62 | } | |
63 | for (i = 0; i < nex; i++) { | |
64 | map[i].bm_bn = XFS_FSB_TO_DADDR(mp, bmp[i].startblock); | |
65 | map[i].bm_len = XFS_FSB_TO_BB(mp, bmp[i].blockcount); | |
66 | } | |
4c947857 DW |
67 | libxfs_buf_read_map(mp->m_dev, map, nex, LIBXFS_READBUF_SALVAGE, |
68 | &bp, ops); | |
360f4a2e ES |
69 | if (map != map_array) |
70 | free(map); | |
71 | return bp; | |
72 | } | |
73 | ||
5cd3b070 ES |
74 | #define FORKNAME(type) (type == XFS_DATA_FORK ? _("directory") : _("attribute")) |
75 | ||
360f4a2e ES |
76 | /* |
77 | * walk tree from root to the left-most leaf block reading in | |
78 | * blocks and setting up cursor. passes back file block number of the | |
79 | * left-most leaf block if successful (bno). returns 1 if successful, | |
80 | * 0 if unsuccessful. | |
81 | */ | |
82 | int | |
83 | traverse_int_dablock( | |
84 | xfs_mount_t *mp, | |
85 | da_bt_cursor_t *da_cursor, | |
86 | xfs_dablk_t *rbno, | |
87 | int whichfork) | |
88 | { | |
89 | bmap_ext_t *bmp; | |
90 | xfs_dablk_t bno; | |
91 | struct xfs_buf *bp; | |
92 | int i; | |
93 | int nex; | |
94 | xfs_da_intnode_t *node; | |
95 | bmap_ext_t lbmp; | |
96 | struct xfs_da_geometry *geo; | |
360f4a2e ES |
97 | struct xfs_da3_icnode_hdr nodehdr; |
98 | ||
99 | if (whichfork == XFS_DATA_FORK) { | |
100 | geo = mp->m_dir_geo; | |
101 | bno = geo->leafblk; | |
102 | } else { | |
103 | geo = mp->m_attr_geo; | |
104 | bno = 0; | |
105 | } | |
106 | ||
107 | /* | |
108 | * traverse down left-side of tree until we hit the | |
109 | * left-most leaf block setting up the btree cursor along | |
110 | * the way. | |
111 | */ | |
112 | i = -1; | |
113 | node = NULL; | |
114 | da_cursor->active = 0; | |
115 | ||
116 | do { | |
117 | /* | |
118 | * read in each block along the way and set up cursor | |
119 | */ | |
120 | nex = blkmap_getn(da_cursor->blkmap, bno, | |
121 | geo->fsbcount, &bmp, &lbmp); | |
122 | ||
123 | if (nex == 0) | |
124 | goto error_out; | |
125 | ||
126 | bp = da_read_buf(mp, nex, bmp, &xfs_da3_node_buf_ops); | |
127 | if (bmp != &lbmp) | |
128 | free(bmp); | |
129 | ||
130 | if (!bp) { | |
131 | do_warn( | |
5cd3b070 ES |
132 | _("can't read %s block %u for inode %" PRIu64 "\n"), |
133 | FORKNAME(whichfork), bno, da_cursor->ino); | |
360f4a2e ES |
134 | goto error_out; |
135 | } | |
136 | ||
be752639 DW |
137 | /* corrupt leafn/node; rebuild the dir. */ |
138 | if (bp->b_error == -EFSBADCRC || bp->b_error == -EFSCORRUPTED) { | |
139 | do_warn( | |
140 | _("corrupt %s tree block %u for inode %" PRIu64 "\n"), | |
141 | FORKNAME(whichfork), bno, da_cursor->ino); | |
142 | libxfs_buf_relse(bp); | |
143 | goto error_out; | |
144 | } | |
145 | ||
360f4a2e | 146 | node = bp->b_addr; |
08c16786 | 147 | libxfs_da3_node_hdr_from_disk(mp, &nodehdr, node); |
360f4a2e ES |
148 | |
149 | if (whichfork == XFS_DATA_FORK && | |
150 | (nodehdr.magic == XFS_DIR2_LEAFN_MAGIC || | |
151 | nodehdr.magic == XFS_DIR3_LEAFN_MAGIC)) { | |
152 | if (i != -1) { | |
153 | do_warn( | |
154 | _("found non-root LEAFN node in inode %" PRIu64 " bno = %u\n"), | |
155 | da_cursor->ino, bno); | |
156 | } | |
157 | *rbno = 0; | |
e02ba985 | 158 | libxfs_buf_relse(bp); |
360f4a2e ES |
159 | return 1; |
160 | } | |
161 | ||
162 | if (nodehdr.magic != XFS_DA_NODE_MAGIC && | |
163 | nodehdr.magic != XFS_DA3_NODE_MAGIC) { | |
164 | do_warn( | |
5cd3b070 ES |
165 | _("bad %s magic number 0x%x in inode %" PRIu64 " bno = %u\n"), |
166 | FORKNAME(whichfork), nodehdr.magic, | |
360f4a2e | 167 | da_cursor->ino, bno); |
e02ba985 | 168 | libxfs_buf_relse(bp); |
360f4a2e ES |
169 | goto error_out; |
170 | } | |
171 | ||
360f4a2e ES |
172 | if (nodehdr.count > geo->node_ents) { |
173 | do_warn( | |
5cd3b070 ES |
174 | _("bad %s record count in inode %" PRIu64 ", count = %d, max = %d\n"), |
175 | FORKNAME(whichfork), da_cursor->ino, | |
176 | nodehdr.count, geo->node_ents); | |
e02ba985 | 177 | libxfs_buf_relse(bp); |
360f4a2e ES |
178 | goto error_out; |
179 | } | |
180 | ||
181 | /* | |
182 | * maintain level counter | |
183 | */ | |
184 | if (i == -1) { | |
185 | i = da_cursor->active = nodehdr.level; | |
186 | if (i < 1 || i >= XFS_DA_NODE_MAXDEPTH) { | |
187 | do_warn( | |
188 | _("bad header depth for directory inode %" PRIu64 "\n"), | |
189 | da_cursor->ino); | |
e02ba985 | 190 | libxfs_buf_relse(bp); |
360f4a2e ES |
191 | i = -1; |
192 | goto error_out; | |
193 | } | |
194 | } else { | |
195 | if (nodehdr.level == i - 1) { | |
196 | i--; | |
197 | } else { | |
198 | do_warn( | |
5cd3b070 ES |
199 | _("bad %s btree for inode %" PRIu64 "\n"), |
200 | FORKNAME(whichfork), da_cursor->ino); | |
e02ba985 | 201 | libxfs_buf_relse(bp); |
360f4a2e ES |
202 | goto error_out; |
203 | } | |
204 | } | |
205 | ||
8faa51a8 CH |
206 | da_cursor->level[i].hashval = |
207 | be32_to_cpu(nodehdr.btree[0].hashval); | |
360f4a2e ES |
208 | da_cursor->level[i].bp = bp; |
209 | da_cursor->level[i].bno = bno; | |
210 | da_cursor->level[i].index = 0; | |
211 | ||
212 | /* | |
213 | * set up new bno for next level down | |
214 | */ | |
8faa51a8 | 215 | bno = be32_to_cpu(nodehdr.btree[0].before); |
360f4a2e ES |
216 | } while (node != NULL && i > 1); |
217 | ||
218 | /* | |
219 | * now return block number and get out | |
220 | */ | |
221 | *rbno = da_cursor->level[0].bno = bno; | |
222 | return 1; | |
223 | ||
224 | error_out: | |
225 | while (i > 1 && i <= da_cursor->active) { | |
e02ba985 | 226 | libxfs_buf_relse(da_cursor->level[i].bp); |
360f4a2e ES |
227 | i++; |
228 | } | |
229 | ||
230 | return 0; | |
231 | } | |
232 | ||
233 | /* | |
234 | * blow out buffer for this level and all the rest above as well | |
235 | * if error == 0, we are not expecting to encounter any unreleased | |
236 | * buffers (e.g. if we do, it's a mistake). if error == 1, we're | |
237 | * in an error-handling case so unreleased buffers may exist. | |
238 | */ | |
239 | static void | |
240 | release_da_cursor_int( | |
241 | xfs_mount_t *mp, | |
242 | da_bt_cursor_t *cursor, | |
243 | int prev_level, | |
244 | int error) | |
245 | { | |
246 | int level = prev_level + 1; | |
247 | ||
248 | if (cursor->level[level].bp != NULL) { | |
249 | if (!error) { | |
250 | do_warn(_("release_da_cursor_int got unexpected " | |
251 | "non-null bp, dabno = %u\n"), | |
252 | cursor->level[level].bno); | |
253 | } | |
254 | ASSERT(error != 0); | |
255 | ||
e02ba985 | 256 | libxfs_buf_relse(cursor->level[level].bp); |
360f4a2e ES |
257 | cursor->level[level].bp = NULL; |
258 | } | |
259 | ||
260 | if (level < cursor->active) | |
261 | release_da_cursor_int(mp, cursor, level, error); | |
262 | ||
263 | return; | |
264 | } | |
265 | ||
266 | void | |
267 | release_da_cursor( | |
268 | xfs_mount_t *mp, | |
269 | da_bt_cursor_t *cursor, | |
270 | int prev_level) | |
271 | { | |
272 | release_da_cursor_int(mp, cursor, prev_level, 0); | |
273 | } | |
274 | ||
275 | void | |
276 | err_release_da_cursor( | |
277 | xfs_mount_t *mp, | |
278 | da_bt_cursor_t *cursor, | |
279 | int prev_level) | |
280 | { | |
281 | release_da_cursor_int(mp, cursor, prev_level, 1); | |
282 | } | |
283 | ||
284 | /* | |
285 | * make sure that all entries in all blocks along the right side of | |
286 | * of the tree are used and hashval's are consistent. level is the | |
287 | * level of the descendent block. returns 0 if good (even if it had | |
288 | * to be fixed up), and 1 if bad. The right edge of the tree is | |
289 | * technically a block boundary. This routine should be used then | |
290 | * instead of verify_da_path(). | |
291 | */ | |
292 | int | |
293 | verify_final_da_path( | |
294 | xfs_mount_t *mp, | |
295 | da_bt_cursor_t *cursor, | |
5cd3b070 ES |
296 | const int p_level, |
297 | int whichfork) | |
360f4a2e ES |
298 | { |
299 | xfs_da_intnode_t *node; | |
300 | xfs_dahash_t hashval; | |
301 | int bad = 0; | |
302 | int entry; | |
303 | int this_level = p_level + 1; | |
360f4a2e ES |
304 | struct xfs_da3_icnode_hdr nodehdr; |
305 | ||
306 | #ifdef XR_DIR_TRACE | |
307 | fprintf(stderr, "in verify_final_da_path, this_level = %d\n", | |
308 | this_level); | |
309 | #endif | |
310 | ||
311 | /* | |
312 | * the index should point to the next "unprocessed" entry | |
313 | * in the block which should be the final (rightmost) entry | |
314 | */ | |
315 | entry = cursor->level[this_level].index; | |
316 | node = cursor->level[this_level].bp->b_addr; | |
08c16786 | 317 | libxfs_da3_node_hdr_from_disk(mp, &nodehdr, node); |
360f4a2e ES |
318 | |
319 | /* | |
320 | * check internal block consistency on this level -- ensure | |
321 | * that all entries are used, encountered and expected hashvals | |
322 | * match, etc. | |
323 | */ | |
324 | if (entry != nodehdr.count - 1) { | |
325 | do_warn( | |
5cd3b070 ES |
326 | _("%s block used/count inconsistency - %d/%hu\n"), |
327 | FORKNAME(whichfork), entry, nodehdr.count); | |
360f4a2e ES |
328 | bad++; |
329 | } | |
330 | /* | |
331 | * hash values monotonically increasing ??? | |
332 | */ | |
b7b81f33 | 333 | if (cursor->level[this_level].hashval > |
8faa51a8 | 334 | be32_to_cpu(nodehdr.btree[entry].hashval)) { |
5cd3b070 ES |
335 | do_warn( |
336 | _("%s block hashvalue inconsistency, expected > %u / saw %u\n"), | |
337 | FORKNAME(whichfork), | |
360f4a2e | 338 | cursor->level[this_level].hashval, |
8faa51a8 | 339 | be32_to_cpu(nodehdr.btree[entry].hashval)); |
360f4a2e ES |
340 | bad++; |
341 | } | |
342 | if (nodehdr.forw != 0) { | |
5cd3b070 ES |
343 | do_warn( |
344 | _("bad %s forward block pointer, expected 0, saw %u\n"), | |
345 | FORKNAME(whichfork), nodehdr.forw); | |
360f4a2e ES |
346 | bad++; |
347 | } | |
348 | if (bad) { | |
5cd3b070 ES |
349 | do_warn(_("bad %s block in inode %" PRIu64 "\n"), |
350 | FORKNAME(whichfork), cursor->ino); | |
360f4a2e ES |
351 | return 1; |
352 | } | |
353 | /* | |
354 | * keep track of greatest block # -- that gets | |
f8149110 | 355 | * us the length of the directory/attribute |
360f4a2e ES |
356 | */ |
357 | if (cursor->level[this_level].bno > cursor->greatest_bno) | |
358 | cursor->greatest_bno = cursor->level[this_level].bno; | |
359 | ||
360 | /* | |
361 | * ok, now check descendant block number against this level | |
362 | */ | |
8faa51a8 CH |
363 | if (cursor->level[p_level].bno != |
364 | be32_to_cpu(nodehdr.btree[entry].before)) { | |
360f4a2e | 365 | #ifdef XR_DIR_TRACE |
5cd3b070 | 366 | fprintf(stderr, "bad %s btree pointer, child bno should " |
360f4a2e | 367 | "be %d, block bno is %d, hashval is %u\n", |
8faa51a8 CH |
368 | FORKNAME(whichfork), |
369 | be16_to_cpu(nodehdr.btree[entry].before), | |
360f4a2e ES |
370 | cursor->level[p_level].bno, |
371 | cursor->level[p_level].hashval); | |
372 | fprintf(stderr, "verify_final_da_path returns 1 (bad) #1a\n"); | |
373 | #endif | |
374 | return 1; | |
375 | } | |
376 | ||
377 | if (cursor->level[p_level].hashval != | |
8faa51a8 | 378 | be32_to_cpu(nodehdr.btree[entry].hashval)) { |
360f4a2e ES |
379 | if (!no_modify) { |
380 | do_warn( | |
5cd3b070 | 381 | _("correcting bad hashval in non-leaf %s block\n" |
360f4a2e | 382 | "\tin (level %d) in inode %" PRIu64 ".\n"), |
5cd3b070 | 383 | FORKNAME(whichfork), this_level, cursor->ino); |
8faa51a8 | 384 | nodehdr.btree[entry].hashval = cpu_to_be32( |
360f4a2e ES |
385 | cursor->level[p_level].hashval); |
386 | cursor->level[this_level].dirty++; | |
387 | } else { | |
388 | do_warn( | |
5cd3b070 | 389 | _("would correct bad hashval in non-leaf %s block\n" |
360f4a2e | 390 | "\tin (level %d) in inode %" PRIu64 ".\n"), |
5cd3b070 | 391 | FORKNAME(whichfork), this_level, cursor->ino); |
360f4a2e ES |
392 | } |
393 | } | |
394 | ||
395 | /* | |
396 | * Note: squirrel hashval away _before_ releasing the | |
397 | * buffer, preventing a use-after-free problem. | |
398 | */ | |
8faa51a8 | 399 | hashval = be32_to_cpu(nodehdr.btree[entry].hashval); |
360f4a2e ES |
400 | |
401 | /* | |
402 | * release/write buffer | |
403 | */ | |
404 | ASSERT(cursor->level[this_level].dirty == 0 || | |
405 | (cursor->level[this_level].dirty && !no_modify)); | |
406 | ||
18b4f688 | 407 | if (cursor->level[this_level].dirty && !no_modify) { |
f524ae04 | 408 | libxfs_buf_mark_dirty(cursor->level[this_level].bp); |
18b4f688 DW |
409 | libxfs_buf_relse(cursor->level[this_level].bp); |
410 | } | |
360f4a2e | 411 | else |
e02ba985 | 412 | libxfs_buf_relse(cursor->level[this_level].bp); |
360f4a2e ES |
413 | |
414 | cursor->level[this_level].bp = NULL; | |
415 | ||
416 | /* | |
417 | * bail out if this is the root block (top of tree) | |
418 | */ | |
419 | if (this_level >= cursor->active) { | |
420 | #ifdef XR_DIR_TRACE | |
421 | fprintf(stderr, "verify_final_da_path returns 0 (ok)\n"); | |
422 | #endif | |
423 | return 0; | |
424 | } | |
425 | /* | |
426 | * set hashvalue to correctly reflect the now-validated | |
427 | * last entry in this block and continue upwards validation | |
428 | */ | |
429 | cursor->level[this_level].hashval = hashval; | |
430 | ||
5cd3b070 | 431 | return verify_final_da_path(mp, cursor, this_level, whichfork); |
360f4a2e ES |
432 | } |
433 | ||
434 | /* | |
435 | * Verifies the path from a descendant block up to the root. | |
436 | * Should be called when the descendant level traversal hits | |
437 | * a block boundary before crossing the boundary (reading in a new | |
438 | * block). | |
439 | * | |
440 | * the directory/attr btrees work differently to the other fs btrees. | |
441 | * each interior block contains records that are <hashval, bno> | |
442 | * pairs. The bno is a file bno, not a filesystem bno. The last | |
443 | * hashvalue in the block <bno> will be <hashval>. BUT unlike | |
444 | * the freespace btrees, the *last* value in each block gets | |
445 | * propagated up the tree instead of the first value in each block. | |
446 | * that is, the interior records point to child blocks and the *greatest* | |
447 | * hash value contained by the child block is the one the block above | |
448 | * uses as the key for the child block. | |
449 | * | |
450 | * level is the level of the descendent block. returns 0 if good, | |
451 | * and 1 if bad. The descendant block may be a leaf block. | |
452 | * | |
453 | * the invariant here is that the values in the cursor for the | |
454 | * levels beneath this level (this_level) and the cursor index | |
455 | * for this level *must* be valid. | |
456 | * | |
457 | * that is, the hashval/bno info is accurate for all | |
458 | * DESCENDANTS and match what the node[index] information | |
459 | * for the current index in the cursor for this level. | |
460 | * | |
461 | * the index values in the cursor for the descendant level | |
462 | * are allowed to be off by one as they will reflect the | |
463 | * next entry at those levels to be processed. | |
464 | * | |
465 | * the hashvalue for the current level can't be set until | |
466 | * we hit the last entry in the block so, it's garbage | |
467 | * until set by this routine. | |
468 | * | |
469 | * bno and bp for the current block/level are always valid | |
470 | * since they have to be set so we can get a buffer for the | |
471 | * block. | |
472 | */ | |
473 | int | |
474 | verify_da_path( | |
475 | xfs_mount_t *mp, | |
476 | da_bt_cursor_t *cursor, | |
477 | const int p_level, | |
478 | int whichfork) | |
479 | { | |
480 | xfs_da_intnode_t *node; | |
481 | xfs_da_intnode_t *newnode; | |
482 | xfs_dablk_t dabno; | |
483 | struct xfs_buf *bp; | |
484 | int bad; | |
485 | int entry; | |
486 | int this_level = p_level + 1; | |
487 | bmap_ext_t *bmp; | |
488 | int nex; | |
489 | bmap_ext_t lbmp; | |
490 | struct xfs_da_geometry *geo; | |
360f4a2e ES |
491 | struct xfs_da3_icnode_hdr nodehdr; |
492 | ||
493 | if (whichfork == XFS_DATA_FORK) | |
494 | geo = mp->m_dir_geo; | |
495 | else | |
496 | geo = mp->m_attr_geo; | |
497 | ||
3cc93dda DW |
498 | /* No buffer at this level, tree is corrupt. */ |
499 | if (cursor->level[this_level].bp == NULL) | |
500 | return 1; | |
501 | ||
360f4a2e ES |
502 | /* |
503 | * index is currently set to point to the entry that | |
504 | * should be processed now in this level. | |
505 | */ | |
506 | entry = cursor->level[this_level].index; | |
507 | node = cursor->level[this_level].bp->b_addr; | |
08c16786 | 508 | libxfs_da3_node_hdr_from_disk(mp, &nodehdr, node); |
360f4a2e | 509 | |
3cc93dda DW |
510 | /* No entries in this node? Tree is corrupt. */ |
511 | if (nodehdr.count == 0) | |
512 | return 1; | |
513 | ||
360f4a2e ES |
514 | /* |
515 | * if this block is out of entries, validate this | |
516 | * block and move on to the next block. | |
517 | * and update cursor value for said level | |
518 | */ | |
519 | if (entry >= nodehdr.count) { | |
520 | /* | |
521 | * update the hash value for this level before | |
522 | * validating it. bno value should be ok since | |
523 | * it was set when the block was first read in. | |
524 | */ | |
525 | cursor->level[this_level].hashval = | |
8faa51a8 | 526 | be32_to_cpu(nodehdr.btree[entry - 1].hashval); |
360f4a2e ES |
527 | |
528 | /* | |
529 | * keep track of greatest block # -- that gets | |
530 | * us the length of the directory | |
531 | */ | |
532 | if (cursor->level[this_level].bno > cursor->greatest_bno) | |
533 | cursor->greatest_bno = cursor->level[this_level].bno; | |
534 | ||
535 | /* | |
536 | * validate the path for the current used-up block | |
537 | * before we trash it | |
538 | */ | |
539 | if (verify_da_path(mp, cursor, this_level, whichfork)) | |
540 | return 1; | |
541 | /* | |
542 | * ok, now get the next buffer and check sibling pointers | |
543 | */ | |
544 | dabno = nodehdr.forw; | |
545 | ASSERT(dabno != 0); | |
546 | nex = blkmap_getn(cursor->blkmap, dabno, geo->fsbcount, | |
547 | &bmp, &lbmp); | |
548 | if (nex == 0) { | |
549 | do_warn( | |
5cd3b070 ES |
550 | _("can't get map info for %s block %u of inode %" PRIu64 "\n"), |
551 | FORKNAME(whichfork), dabno, cursor->ino); | |
360f4a2e ES |
552 | return 1; |
553 | } | |
554 | ||
555 | bp = da_read_buf(mp, nex, bmp, &xfs_da3_node_buf_ops); | |
556 | if (bmp != &lbmp) | |
557 | free(bmp); | |
558 | ||
559 | if (!bp) { | |
560 | do_warn( | |
5cd3b070 ES |
561 | _("can't read %s block %u for inode %" PRIu64 "\n"), |
562 | FORKNAME(whichfork), dabno, cursor->ino); | |
360f4a2e ES |
563 | return 1; |
564 | } | |
be752639 DW |
565 | if (bp->b_error == -EFSCORRUPTED || bp->b_error == -EFSBADCRC) { |
566 | do_warn( | |
567 | _("corrupt %s tree block %u for inode %" PRIu64 "\n"), | |
568 | FORKNAME(whichfork), dabno, cursor->ino); | |
569 | libxfs_buf_relse(bp); | |
570 | return 1; | |
571 | } | |
360f4a2e ES |
572 | |
573 | newnode = bp->b_addr; | |
08c16786 | 574 | libxfs_da3_node_hdr_from_disk(mp, &nodehdr, newnode); |
360f4a2e ES |
575 | |
576 | /* | |
577 | * verify magic number and back pointer, sanity-check | |
578 | * entry count, verify level | |
579 | */ | |
580 | bad = 0; | |
581 | if (nodehdr.magic != XFS_DA_NODE_MAGIC && | |
582 | nodehdr.magic != XFS_DA3_NODE_MAGIC) { | |
583 | do_warn( | |
5cd3b070 ES |
584 | _("bad magic number %x in %s block %u for inode %" PRIu64 "\n"), |
585 | nodehdr.magic, FORKNAME(whichfork), | |
360f4a2e ES |
586 | dabno, cursor->ino); |
587 | bad++; | |
588 | } | |
589 | if (nodehdr.back != cursor->level[this_level].bno) { | |
590 | do_warn( | |
5cd3b070 ES |
591 | _("bad back pointer in %s block %u for inode %" PRIu64 "\n"), |
592 | FORKNAME(whichfork), dabno, cursor->ino); | |
360f4a2e ES |
593 | bad++; |
594 | } | |
595 | if (nodehdr.count > geo->node_ents) { | |
596 | do_warn( | |
5cd3b070 ES |
597 | _("entry count %d too large in %s block %u for inode %" PRIu64 "\n"), |
598 | nodehdr.count, FORKNAME(whichfork), | |
360f4a2e ES |
599 | dabno, cursor->ino); |
600 | bad++; | |
601 | } | |
602 | if (nodehdr.level != this_level) { | |
603 | do_warn( | |
5cd3b070 ES |
604 | _("bad level %d in %s block %u for inode %" PRIu64 "\n"), |
605 | nodehdr.level, FORKNAME(whichfork), | |
360f4a2e ES |
606 | dabno, cursor->ino); |
607 | bad++; | |
608 | } | |
609 | if (bad) { | |
610 | #ifdef XR_DIR_TRACE | |
611 | fprintf(stderr, "verify_da_path returns 1 (bad) #4\n"); | |
612 | #endif | |
e02ba985 | 613 | libxfs_buf_relse(bp); |
360f4a2e ES |
614 | return 1; |
615 | } | |
616 | ||
617 | /* | |
618 | * update cursor, write out the *current* level if | |
619 | * required. don't write out the descendant level | |
620 | */ | |
621 | ASSERT(cursor->level[this_level].dirty == 0 || | |
622 | (cursor->level[this_level].dirty && !no_modify)); | |
623 | ||
624 | /* | |
625 | * If block looks ok but CRC didn't match, make sure to | |
626 | * recompute it. | |
627 | */ | |
628 | if (!no_modify && | |
629 | cursor->level[this_level].bp->b_error == -EFSBADCRC) | |
630 | cursor->level[this_level].dirty = 1; | |
631 | ||
18b4f688 | 632 | if (cursor->level[this_level].dirty && !no_modify) { |
f524ae04 | 633 | libxfs_buf_mark_dirty(cursor->level[this_level].bp); |
18b4f688 DW |
634 | libxfs_buf_relse(cursor->level[this_level].bp); |
635 | } | |
360f4a2e | 636 | else |
e02ba985 | 637 | libxfs_buf_relse(cursor->level[this_level].bp); |
360f4a2e ES |
638 | |
639 | /* switch cursor to point at the new buffer we just read */ | |
640 | cursor->level[this_level].bp = bp; | |
641 | cursor->level[this_level].dirty = 0; | |
642 | cursor->level[this_level].bno = dabno; | |
643 | cursor->level[this_level].hashval = | |
8faa51a8 | 644 | be32_to_cpu(nodehdr.btree[0].hashval); |
360f4a2e ES |
645 | |
646 | entry = cursor->level[this_level].index = 0; | |
647 | } | |
648 | /* | |
649 | * ditto for block numbers | |
650 | */ | |
8faa51a8 CH |
651 | if (cursor->level[p_level].bno != |
652 | be32_to_cpu(nodehdr.btree[entry].before)) { | |
360f4a2e | 653 | #ifdef XR_DIR_TRACE |
5cd3b070 | 654 | fprintf(stderr, "bad %s btree pointer, child bno " |
360f4a2e | 655 | "should be %d, block bno is %d, hashval is %u\n", |
8faa51a8 CH |
656 | FORKNAME(whichfork), |
657 | be32_to_cpu(nodehdr.btree[entry].before), | |
360f4a2e ES |
658 | cursor->level[p_level].bno, |
659 | cursor->level[p_level].hashval); | |
660 | fprintf(stderr, "verify_da_path returns 1 (bad) #1a\n"); | |
661 | #endif | |
662 | return 1; | |
663 | } | |
664 | /* | |
665 | * ok, now validate last hashvalue in the descendant | |
666 | * block against the hashval in the current entry | |
667 | */ | |
668 | if (cursor->level[p_level].hashval != | |
8faa51a8 | 669 | be32_to_cpu(nodehdr.btree[entry].hashval)) { |
360f4a2e ES |
670 | if (!no_modify) { |
671 | do_warn( | |
5cd3b070 | 672 | _("correcting bad hashval in interior %s block\n" |
360f4a2e | 673 | "\tin (level %d) in inode %" PRIu64 ".\n"), |
5cd3b070 | 674 | FORKNAME(whichfork), this_level, cursor->ino); |
8faa51a8 | 675 | nodehdr.btree[entry].hashval = cpu_to_be32( |
360f4a2e ES |
676 | cursor->level[p_level].hashval); |
677 | cursor->level[this_level].dirty++; | |
678 | } else { | |
679 | do_warn( | |
5cd3b070 | 680 | _("would correct bad hashval in interior %s block\n" |
360f4a2e | 681 | "\tin (level %d) in inode %" PRIu64 ".\n"), |
5cd3b070 | 682 | FORKNAME(whichfork), this_level, cursor->ino); |
360f4a2e ES |
683 | } |
684 | } | |
685 | /* | |
686 | * increment index for this level to point to next entry | |
687 | * (which should point to the next descendant block) | |
688 | */ | |
689 | cursor->level[this_level].index++; | |
690 | #ifdef XR_DIR_TRACE | |
691 | fprintf(stderr, "verify_da_path returns 0 (ok)\n"); | |
692 | #endif | |
693 | return 0; | |
694 | } |