]>
Commit | Line | Data |
---|---|---|
2bd0ea18 | 1 | /* |
da23017d NS |
2 | * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. |
3 | * All Rights Reserved. | |
dfc130f3 | 4 | * |
da23017d NS |
5 | * This program is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU General Public License as | |
2bd0ea18 | 7 | * published by the Free Software Foundation. |
dfc130f3 | 8 | * |
da23017d NS |
9 | * This program is distributed in the hope that it would be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
dfc130f3 | 13 | * |
da23017d NS |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write the Free Software Foundation, | |
16 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
2bd0ea18 NS |
17 | */ |
18 | ||
19 | #include <libxfs.h> | |
20 | #include "avl.h" | |
21 | #include "globals.h" | |
22 | #include "incore.h" | |
23 | #include "err_protos.h" | |
24 | #include "dinode.h" | |
2bd0ea18 NS |
25 | #include "dir2.h" |
26 | #include "bmap.h" | |
cb5b3ef4 | 27 | #include "prefetch.h" |
2556c98b | 28 | #include "progress.h" |
2bd0ea18 | 29 | |
2bd0ea18 NS |
30 | /* |
31 | * Known bad inode list. These are seen when the leaf and node | |
32 | * block linkages are incorrect. | |
33 | */ | |
34 | typedef struct dir2_bad { | |
35 | xfs_ino_t ino; | |
36 | struct dir2_bad *next; | |
37 | } dir2_bad_t; | |
2bd0ea18 | 38 | |
8b8a6b02 CH |
39 | static dir2_bad_t *dir2_bad_list; |
40 | ||
41 | static void | |
2bd0ea18 NS |
42 | dir2_add_badlist( |
43 | xfs_ino_t ino) | |
44 | { | |
45 | dir2_bad_t *l; | |
46 | ||
47 | if ((l = malloc(sizeof(dir2_bad_t))) == NULL) { | |
507f4e33 | 48 | do_error( |
5d1b7f0f | 49 | _("malloc failed (%zu bytes) dir2_add_badlist:ino %" PRIu64 "\n"), |
2bd0ea18 NS |
50 | sizeof(dir2_bad_t), ino); |
51 | exit(1); | |
52 | } | |
53 | l->next = dir2_bad_list; | |
54 | dir2_bad_list = l; | |
55 | l->ino = ino; | |
56 | } | |
57 | ||
58 | int | |
59 | dir2_is_badino( | |
60 | xfs_ino_t ino) | |
61 | { | |
62 | dir2_bad_t *l; | |
63 | ||
64 | for (l = dir2_bad_list; l; l = l->next) | |
65 | if (l->ino == ino) | |
66 | return 1; | |
67 | return 0; | |
68 | } | |
69 | ||
9a048535 DC |
70 | /* |
71 | * takes a name and length (name need not be null-terminated) | |
72 | * and returns 1 if the name contains a '/' or a \0, returns 0 | |
73 | * otherwise | |
74 | */ | |
75 | int | |
76 | namecheck(char *name, int length) | |
77 | { | |
78 | char *c; | |
79 | int i; | |
80 | ||
81 | ASSERT(length < MAXNAMELEN); | |
82 | ||
83 | for (c = name, i = 0; i < length; i++, c++) { | |
84 | if (*c == '/' || *c == '\0') | |
85 | return(1); | |
86 | } | |
87 | ||
88 | return(0); | |
89 | } | |
90 | ||
2bd0ea18 NS |
91 | /* |
92 | * Multibuffer handling. | |
93 | * V2 directory blocks can be noncontiguous, needing multiple buffers. | |
94 | */ | |
a2ceac1f | 95 | static struct xfs_buf * |
2bd0ea18 NS |
96 | da_read_buf( |
97 | xfs_mount_t *mp, | |
98 | int nex, | |
75c8b434 DC |
99 | bmap_ext_t *bmp, |
100 | const struct xfs_buf_ops *ops) | |
2bd0ea18 | 101 | { |
a2ceac1f DC |
102 | #define MAP_ARRAY_SZ 4 |
103 | struct xfs_buf_map map_array[MAP_ARRAY_SZ]; | |
104 | struct xfs_buf_map *map; | |
105 | struct xfs_buf *bp; | |
f8bc5a6f | 106 | int i; |
2bd0ea18 | 107 | |
a2ceac1f DC |
108 | if (nex > MAP_ARRAY_SZ) { |
109 | map = calloc(nex, sizeof(*map)); | |
110 | if (map == NULL) { | |
1e77098c MV |
111 | do_error(_("couldn't malloc dir2 buffer list\n")); |
112 | exit(1); | |
113 | } | |
a2ceac1f | 114 | } else { |
1e77098c | 115 | /* common case avoids calloc/free */ |
a2ceac1f | 116 | map = map_array; |
2bd0ea18 | 117 | } |
f8bc5a6f | 118 | for (i = 0; i < nex; i++) { |
a2ceac1f DC |
119 | map[i].bm_bn = XFS_FSB_TO_DADDR(mp, bmp[i].startblock); |
120 | map[i].bm_len = XFS_FSB_TO_BB(mp, bmp[i].blockcount); | |
2556c98b | 121 | } |
75c8b434 | 122 | bp = libxfs_readbuf_map(mp->m_dev, map, nex, 0, ops); |
a2ceac1f DC |
123 | if (map != map_array) |
124 | free(map); | |
125 | return bp; | |
2bd0ea18 NS |
126 | } |
127 | ||
128 | /* | |
129 | * walk tree from root to the left-most leaf block reading in | |
130 | * blocks and setting up cursor. passes back file block number of the | |
131 | * left-most leaf block if successful (bno). returns 1 if successful, | |
132 | * 0 if unsuccessful. | |
133 | */ | |
8b8a6b02 | 134 | static int |
2bd0ea18 NS |
135 | traverse_int_dir2block(xfs_mount_t *mp, |
136 | dir2_bt_cursor_t *da_cursor, | |
137 | xfs_dablk_t *rbno) | |
138 | { | |
139 | bmap_ext_t *bmp; | |
140 | xfs_dablk_t bno; | |
a2ceac1f | 141 | struct xfs_buf *bp; |
2bd0ea18 NS |
142 | int i; |
143 | int nex; | |
144 | xfs_da_intnode_t *node; | |
1e77098c | 145 | bmap_ext_t lbmp; |
88b32f06 DC |
146 | struct xfs_da_node_entry *btree; |
147 | struct xfs_da3_icnode_hdr nodehdr; | |
2bd0ea18 NS |
148 | |
149 | /* | |
150 | * traverse down left-side of tree until we hit the | |
151 | * left-most leaf block setting up the btree cursor along | |
152 | * the way. | |
153 | */ | |
ff105f75 | 154 | bno = mp->m_dir_geo->leafblk; |
2bd0ea18 | 155 | i = -1; |
88b32f06 | 156 | node = NULL; |
2bd0ea18 NS |
157 | da_cursor->active = 0; |
158 | ||
159 | do { | |
160 | /* | |
161 | * read in each block along the way and set up cursor | |
162 | */ | |
ff105f75 DC |
163 | nex = blkmap_getn(da_cursor->blkmap, bno, |
164 | mp->m_dir_geo->fsbcount, &bmp, &lbmp); | |
2bd0ea18 NS |
165 | |
166 | if (nex == 0) | |
167 | goto error_out; | |
168 | ||
75c8b434 | 169 | bp = da_read_buf(mp, nex, bmp, &xfs_da3_node_buf_ops); |
1e77098c MV |
170 | if (bmp != &lbmp) |
171 | free(bmp); | |
2bd0ea18 | 172 | if (bp == NULL) { |
5d1b7f0f CH |
173 | do_warn( |
174 | _("can't read block %u for directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
175 | bno, da_cursor->ino); |
176 | goto error_out; | |
177 | } | |
178 | ||
88b32f06 | 179 | node = bp->b_addr; |
ff105f75 | 180 | M_DIROPS(mp)->node_hdr_from_disk(&nodehdr, node); |
2bd0ea18 | 181 | |
658ac3e3 DC |
182 | if (nodehdr.magic == XFS_DIR2_LEAFN_MAGIC || |
183 | nodehdr.magic == XFS_DIR3_LEAFN_MAGIC) { | |
2bd0ea18 | 184 | if ( i != -1 ) { |
5d1b7f0f CH |
185 | do_warn( |
186 | _("found non-root LEAFN node in inode %" PRIu64 " bno = %u\n"), | |
2bd0ea18 NS |
187 | da_cursor->ino, bno); |
188 | } | |
2bd0ea18 | 189 | *rbno = 0; |
a2ceac1f | 190 | libxfs_putbuf(bp); |
2bd0ea18 | 191 | return(1); |
658ac3e3 DC |
192 | } else if (!(nodehdr.magic == XFS_DA_NODE_MAGIC || |
193 | nodehdr.magic == XFS_DA3_NODE_MAGIC)) { | |
a2ceac1f | 194 | libxfs_putbuf(bp); |
5d1b7f0f CH |
195 | do_warn( |
196 | _("bad dir magic number 0x%x in inode %" PRIu64 " bno = %u\n"), | |
88b32f06 | 197 | nodehdr.magic, |
2bd0ea18 NS |
198 | da_cursor->ino, bno); |
199 | goto error_out; | |
200 | } | |
0519f662 DW |
201 | /* corrupt node; rebuild the dir. */ |
202 | if (bp->b_error == EFSBADCRC || bp->b_error == EFSCORRUPTED) { | |
203 | do_warn( | |
204 | _("corrupt tree block %u for directory inode %" PRIu64 "\n"), | |
205 | bno, da_cursor->ino); | |
206 | goto error_out; | |
207 | } | |
ff105f75 DC |
208 | btree = M_DIROPS(mp)->node_tree_p(node); |
209 | if (nodehdr.count > mp->m_dir_geo->node_ents) { | |
a2ceac1f | 210 | libxfs_putbuf(bp); |
5d1b7f0f | 211 | do_warn( |
88b32f06 DC |
212 | _("bad record count in inode %" PRIu64 ", count = %d, max = %d\n"), |
213 | da_cursor->ino, | |
214 | nodehdr.count, | |
ff105f75 | 215 | mp->m_dir_geo->node_ents); |
2bd0ea18 NS |
216 | goto error_out; |
217 | } | |
2bd0ea18 NS |
218 | /* |
219 | * maintain level counter | |
220 | */ | |
28148f62 | 221 | if (i == -1) { |
88b32f06 | 222 | i = da_cursor->active = nodehdr.level; |
44dae5e6 | 223 | if (i < 1 || i >= XFS_DA_NODE_MAXDEPTH) { |
5d1b7f0f CH |
224 | do_warn( |
225 | _("bad header depth for directory inode %" PRIu64 "\n"), | |
28148f62 | 226 | da_cursor->ino); |
a2ceac1f | 227 | libxfs_putbuf(bp); |
28148f62 ES |
228 | i = -1; |
229 | goto error_out; | |
230 | } | |
231 | } else { | |
88b32f06 | 232 | if (nodehdr.level == i - 1) { |
2bd0ea18 NS |
233 | i--; |
234 | } else { | |
5d1b7f0f CH |
235 | do_warn( |
236 | _("bad directory btree for directory inode %" PRIu64 "\n"), | |
2bd0ea18 | 237 | da_cursor->ino); |
a2ceac1f | 238 | libxfs_putbuf(bp); |
2bd0ea18 NS |
239 | goto error_out; |
240 | } | |
241 | } | |
242 | ||
88b32f06 | 243 | da_cursor->level[i].hashval = be32_to_cpu(btree[0].hashval); |
2bd0ea18 NS |
244 | da_cursor->level[i].bp = bp; |
245 | da_cursor->level[i].bno = bno; | |
246 | da_cursor->level[i].index = 0; | |
247 | ||
248 | /* | |
249 | * set up new bno for next level down | |
250 | */ | |
88b32f06 DC |
251 | bno = be32_to_cpu(btree[0].before); |
252 | } while (node != NULL && i > 1); | |
2bd0ea18 NS |
253 | |
254 | /* | |
255 | * now return block number and get out | |
256 | */ | |
257 | *rbno = da_cursor->level[0].bno = bno; | |
258 | return(1); | |
259 | ||
260 | error_out: | |
261 | while (i > 1 && i <= da_cursor->active) { | |
a2ceac1f | 262 | libxfs_putbuf(da_cursor->level[i].bp); |
2bd0ea18 NS |
263 | i++; |
264 | } | |
265 | ||
266 | return(0); | |
267 | } | |
268 | ||
269 | /* | |
270 | * blow out buffer for this level and all the rest above as well | |
271 | * if error == 0, we are not expecting to encounter any unreleased | |
272 | * buffers (e.g. if we do, it's a mistake). if error == 1, we're | |
273 | * in an error-handling case so unreleased buffers may exist. | |
274 | */ | |
8b8a6b02 | 275 | static void |
2bd0ea18 NS |
276 | release_dir2_cursor_int(xfs_mount_t *mp, |
277 | dir2_bt_cursor_t *cursor, | |
278 | int prev_level, | |
279 | int error) | |
280 | { | |
281 | int level = prev_level + 1; | |
282 | ||
283 | if (cursor->level[level].bp != NULL) { | |
284 | if (!error) { | |
507f4e33 NS |
285 | do_warn(_("release_dir2_cursor_int got unexpected " |
286 | "non-null bp, dabno = %u\n"), | |
2bd0ea18 NS |
287 | cursor->level[level].bno); |
288 | } | |
289 | ASSERT(error != 0); | |
290 | ||
a2ceac1f | 291 | libxfs_putbuf(cursor->level[level].bp); |
2bd0ea18 NS |
292 | cursor->level[level].bp = NULL; |
293 | } | |
294 | ||
295 | if (level < cursor->active) | |
296 | release_dir2_cursor_int(mp, cursor, level, error); | |
297 | ||
298 | return; | |
299 | } | |
300 | ||
8b8a6b02 | 301 | static void |
2bd0ea18 NS |
302 | release_dir2_cursor(xfs_mount_t *mp, |
303 | dir2_bt_cursor_t *cursor, | |
304 | int prev_level) | |
305 | { | |
306 | release_dir2_cursor_int(mp, cursor, prev_level, 0); | |
307 | } | |
308 | ||
8b8a6b02 | 309 | static void |
2bd0ea18 NS |
310 | err_release_dir2_cursor(xfs_mount_t *mp, |
311 | dir2_bt_cursor_t *cursor, | |
312 | int prev_level) | |
313 | { | |
314 | release_dir2_cursor_int(mp, cursor, prev_level, 1); | |
315 | } | |
316 | ||
317 | /* | |
318 | * make sure that all entries in all blocks along the right side of | |
319 | * of the tree are used and hashval's are consistent. level is the | |
320 | * level of the descendent block. returns 0 if good (even if it had | |
321 | * to be fixed up), and 1 if bad. The right edge of the tree is | |
322 | * technically a block boundary. This routine should be used then | |
323 | * instead of verify_dir2_path(). | |
324 | */ | |
8b8a6b02 | 325 | static int |
2bd0ea18 NS |
326 | verify_final_dir2_path(xfs_mount_t *mp, |
327 | dir2_bt_cursor_t *cursor, | |
328 | const int p_level) | |
329 | { | |
330 | xfs_da_intnode_t *node; | |
331 | int bad = 0; | |
332 | int entry; | |
333 | int this_level = p_level + 1; | |
88b32f06 DC |
334 | struct xfs_da_node_entry *btree; |
335 | struct xfs_da3_icnode_hdr nodehdr; | |
2bd0ea18 NS |
336 | |
337 | /* | |
338 | * the index should point to the next "unprocessed" entry | |
339 | * in the block which should be the final (rightmost) entry | |
340 | */ | |
341 | entry = cursor->level[this_level].index; | |
a2ceac1f | 342 | node = (xfs_da_intnode_t *)(cursor->level[this_level].bp->b_addr); |
ff105f75 DC |
343 | btree = M_DIROPS(mp)->node_tree_p(node); |
344 | M_DIROPS(mp)->node_hdr_from_disk(&nodehdr, node); | |
345 | ||
2bd0ea18 NS |
346 | /* |
347 | * check internal block consistency on this level -- ensure | |
348 | * that all entries are used, encountered and expected hashvals | |
349 | * match, etc. | |
350 | */ | |
88b32f06 | 351 | if (entry != nodehdr.count - 1) { |
507f4e33 NS |
352 | do_warn( |
353 | _("directory block used/count inconsistency - %d / %hu\n"), | |
88b32f06 | 354 | entry, nodehdr.count); |
2bd0ea18 NS |
355 | bad++; |
356 | } | |
357 | /* | |
358 | * hash values monotonically increasing ??? | |
359 | */ | |
507f4e33 | 360 | if (cursor->level[this_level].hashval >= |
88b32f06 | 361 | be32_to_cpu(btree[entry].hashval)) { |
507f4e33 NS |
362 | do_warn(_("directory/attribute block hashvalue inconsistency, " |
363 | "expected > %u / saw %u\n"), | |
2bd0ea18 | 364 | cursor->level[this_level].hashval, |
88b32f06 | 365 | be32_to_cpu(btree[entry].hashval)); |
2bd0ea18 NS |
366 | bad++; |
367 | } | |
88b32f06 | 368 | if (nodehdr.forw != 0) { |
507f4e33 NS |
369 | do_warn(_("bad directory/attribute forward block pointer, " |
370 | "expected 0, saw %u\n"), | |
88b32f06 | 371 | nodehdr.forw); |
2bd0ea18 NS |
372 | bad++; |
373 | } | |
374 | if (bad) { | |
5d1b7f0f | 375 | do_warn(_("bad directory block in inode %" PRIu64 "\n"), cursor->ino); |
2bd0ea18 NS |
376 | return(1); |
377 | } | |
378 | /* | |
379 | * keep track of greatest block # -- that gets | |
380 | * us the length of the directory | |
381 | */ | |
382 | if (cursor->level[this_level].bno > cursor->greatest_bno) | |
383 | cursor->greatest_bno = cursor->level[this_level].bno; | |
384 | ||
385 | /* | |
386 | * ok, now check descendant block number against this level | |
387 | */ | |
88b32f06 | 388 | if (cursor->level[p_level].bno != be32_to_cpu(btree[entry].before)) |
2bd0ea18 | 389 | return(1); |
2bd0ea18 | 390 | |
507f4e33 | 391 | if (cursor->level[p_level].hashval != |
88b32f06 | 392 | be32_to_cpu(btree[entry].hashval)) { |
2bd0ea18 | 393 | if (!no_modify) { |
5d1b7f0f CH |
394 | do_warn( |
395 | _("correcting bad hashval in non-leaf dir block\n" | |
396 | "\tin (level %d) in inode %" PRIu64 ".\n"), | |
2bd0ea18 | 397 | this_level, cursor->ino); |
88b32f06 | 398 | btree[entry].hashval = cpu_to_be32( |
5e656dbb | 399 | cursor->level[p_level].hashval); |
2bd0ea18 NS |
400 | cursor->level[this_level].dirty++; |
401 | } else { | |
5d1b7f0f CH |
402 | do_warn( |
403 | _("would correct bad hashval in non-leaf dir block\n" | |
404 | "\tin (level %d) in inode %" PRIu64 ".\n"), | |
2bd0ea18 NS |
405 | this_level, cursor->ino); |
406 | } | |
407 | } | |
408 | ||
409 | /* | |
410 | * release/write buffer | |
411 | */ | |
412 | ASSERT(cursor->level[this_level].dirty == 0 || | |
27527004 | 413 | (cursor->level[this_level].dirty && !no_modify)); |
2bd0ea18 NS |
414 | |
415 | if (cursor->level[this_level].dirty && !no_modify) | |
a2ceac1f | 416 | libxfs_writebuf(cursor->level[this_level].bp, 0); |
2bd0ea18 | 417 | else |
a2ceac1f | 418 | libxfs_putbuf(cursor->level[this_level].bp); |
2bd0ea18 NS |
419 | |
420 | cursor->level[this_level].bp = NULL; | |
421 | ||
422 | /* | |
423 | * bail out if this is the root block (top of tree) | |
424 | */ | |
4c0a98ae | 425 | if (this_level >= cursor->active) |
2bd0ea18 | 426 | return(0); |
2bd0ea18 NS |
427 | /* |
428 | * set hashvalue to correctl reflect the now-validated | |
429 | * last entry in this block and continue upwards validation | |
430 | */ | |
88b32f06 | 431 | cursor->level[this_level].hashval = be32_to_cpu(btree[entry].hashval); |
2bd0ea18 NS |
432 | |
433 | return(verify_final_dir2_path(mp, cursor, this_level)); | |
434 | } | |
435 | ||
436 | /* | |
437 | * Verifies the path from a descendant block up to the root. | |
438 | * Should be called when the descendant level traversal hits | |
439 | * a block boundary before crossing the boundary (reading in a new | |
440 | * block). | |
441 | * | |
442 | * the directory/attr btrees work differently to the other fs btrees. | |
443 | * each interior block contains records that are <hashval, bno> | |
444 | * pairs. The bno is a file bno, not a filesystem bno. The last | |
445 | * hashvalue in the block <bno> will be <hashval>. BUT unlike | |
446 | * the freespace btrees, the *last* value in each block gets | |
447 | * propagated up the tree instead of the first value in each block. | |
448 | * that is, the interior records point to child blocks and the *greatest* | |
449 | * hash value contained by the child block is the one the block above | |
450 | * uses as the key for the child block. | |
451 | * | |
452 | * level is the level of the descendent block. returns 0 if good, | |
453 | * and 1 if bad. The descendant block may be a leaf block. | |
454 | * | |
455 | * the invariant here is that the values in the cursor for the | |
456 | * levels beneath this level (this_level) and the cursor index | |
457 | * for this level *must* be valid. | |
458 | * | |
459 | * that is, the hashval/bno info is accurate for all | |
460 | * DESCENDANTS and match what the node[index] information | |
461 | * for the current index in the cursor for this level. | |
462 | * | |
463 | * the index values in the cursor for the descendant level | |
464 | * are allowed to be off by one as they will reflect the | |
465 | * next entry at those levels to be processed. | |
466 | * | |
467 | * the hashvalue for the current level can't be set until | |
468 | * we hit the last entry in the block so, it's garbage | |
469 | * until set by this routine. | |
470 | * | |
471 | * bno and bp for the current block/level are always valid | |
472 | * since they have to be set so we can get a buffer for the | |
473 | * block. | |
474 | */ | |
8b8a6b02 | 475 | static int |
2bd0ea18 NS |
476 | verify_dir2_path(xfs_mount_t *mp, |
477 | dir2_bt_cursor_t *cursor, | |
478 | const int p_level) | |
479 | { | |
480 | xfs_da_intnode_t *node; | |
481 | xfs_da_intnode_t *newnode; | |
482 | xfs_dablk_t dabno; | |
a2ceac1f | 483 | struct xfs_buf *bp; |
2bd0ea18 NS |
484 | int bad; |
485 | int entry; | |
486 | int this_level = p_level + 1; | |
487 | bmap_ext_t *bmp; | |
488 | int nex; | |
1e77098c | 489 | bmap_ext_t lbmp; |
88b32f06 DC |
490 | struct xfs_da_node_entry *btree; |
491 | struct xfs_da3_icnode_hdr nodehdr; | |
2bd0ea18 NS |
492 | |
493 | /* | |
494 | * index is currently set to point to the entry that | |
495 | * should be processed now in this level. | |
496 | */ | |
497 | entry = cursor->level[this_level].index; | |
a2ceac1f | 498 | node = cursor->level[this_level].bp->b_addr; |
ff105f75 DC |
499 | btree = M_DIROPS(mp)->node_tree_p(node); |
500 | M_DIROPS(mp)->node_hdr_from_disk(&nodehdr, node); | |
2bd0ea18 NS |
501 | |
502 | /* | |
503 | * if this block is out of entries, validate this | |
504 | * block and move on to the next block. | |
505 | * and update cursor value for said level | |
506 | */ | |
88b32f06 | 507 | if (entry >= nodehdr.count) { |
2bd0ea18 NS |
508 | /* |
509 | * update the hash value for this level before | |
510 | * validating it. bno value should be ok since | |
511 | * it was set when the block was first read in. | |
512 | */ | |
dfc130f3 | 513 | cursor->level[this_level].hashval = |
88b32f06 | 514 | be32_to_cpu(btree[entry - 1].hashval); |
2bd0ea18 NS |
515 | |
516 | /* | |
517 | * keep track of greatest block # -- that gets | |
518 | * us the length of the directory | |
519 | */ | |
520 | if (cursor->level[this_level].bno > cursor->greatest_bno) | |
521 | cursor->greatest_bno = cursor->level[this_level].bno; | |
522 | ||
523 | /* | |
524 | * validate the path for the current used-up block | |
525 | * before we trash it | |
526 | */ | |
527 | if (verify_dir2_path(mp, cursor, this_level)) | |
528 | return(1); | |
529 | /* | |
530 | * ok, now get the next buffer and check sibling pointers | |
531 | */ | |
88b32f06 | 532 | dabno = nodehdr.forw; |
2bd0ea18 | 533 | ASSERT(dabno != 0); |
ff105f75 | 534 | nex = blkmap_getn(cursor->blkmap, dabno, mp->m_dir_geo->fsbcount, |
1e77098c | 535 | &bmp, &lbmp); |
2bd0ea18 | 536 | if (nex == 0) { |
5d1b7f0f CH |
537 | do_warn( |
538 | _("can't get map info for block %u of directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
539 | dabno, cursor->ino); |
540 | return(1); | |
541 | } | |
542 | ||
75c8b434 | 543 | bp = da_read_buf(mp, nex, bmp, &xfs_da3_node_buf_ops); |
1e77098c MV |
544 | if (bmp != &lbmp) |
545 | free(bmp); | |
2bd0ea18 NS |
546 | |
547 | if (bp == NULL) { | |
5d1b7f0f CH |
548 | do_warn( |
549 | _("can't read block %u for directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
550 | dabno, cursor->ino); |
551 | return(1); | |
552 | } | |
553 | ||
a2ceac1f | 554 | newnode = bp->b_addr; |
ff105f75 DC |
555 | btree = M_DIROPS(mp)->node_tree_p(newnode); |
556 | M_DIROPS(mp)->node_hdr_from_disk(&nodehdr, newnode); | |
2bd0ea18 NS |
557 | /* |
558 | * verify magic number and back pointer, sanity-check | |
559 | * entry count, verify level | |
560 | */ | |
561 | bad = 0; | |
658ac3e3 DC |
562 | if (!(nodehdr.magic == XFS_DA_NODE_MAGIC || |
563 | nodehdr.magic == XFS_DA3_NODE_MAGIC)) { | |
5d1b7f0f CH |
564 | do_warn( |
565 | _("bad magic number %x in block %u for directory inode %" PRIu64 "\n"), | |
88b32f06 | 566 | nodehdr.magic, |
507f4e33 | 567 | dabno, cursor->ino); |
2bd0ea18 NS |
568 | bad++; |
569 | } | |
88b32f06 | 570 | if (nodehdr.back != cursor->level[this_level].bno) { |
5d1b7f0f CH |
571 | do_warn( |
572 | _("bad back pointer in block %u for directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
573 | dabno, cursor->ino); |
574 | bad++; | |
575 | } | |
ff105f75 | 576 | if (nodehdr.count > mp->m_dir_geo->node_ents) { |
5d1b7f0f CH |
577 | do_warn( |
578 | _("entry count %d too large in block %u for directory inode %" PRIu64 "\n"), | |
88b32f06 | 579 | nodehdr.count, |
507f4e33 | 580 | dabno, cursor->ino); |
2bd0ea18 NS |
581 | bad++; |
582 | } | |
88b32f06 | 583 | if (nodehdr.level != this_level) { |
5d1b7f0f CH |
584 | do_warn( |
585 | _("bad level %d in block %u for directory inode %" PRIu64 "\n"), | |
88b32f06 | 586 | nodehdr.level, |
507f4e33 | 587 | dabno, cursor->ino); |
2bd0ea18 NS |
588 | bad++; |
589 | } | |
590 | if (bad) { | |
a2ceac1f | 591 | libxfs_putbuf(bp); |
2bd0ea18 NS |
592 | return(1); |
593 | } | |
594 | /* | |
595 | * update cursor, write out the *current* level if | |
596 | * required. don't write out the descendant level | |
597 | */ | |
598 | ASSERT(cursor->level[this_level].dirty == 0 || | |
27527004 | 599 | (cursor->level[this_level].dirty && !no_modify)); |
575010d5 JK |
600 | /* |
601 | * If block looks ok but CRC didn't match, make sure to | |
602 | * recompute it. | |
603 | */ | |
604 | if (!no_modify && | |
12b53197 | 605 | cursor->level[this_level].bp->b_error == -EFSBADCRC) |
575010d5 | 606 | cursor->level[this_level].dirty = 1; |
2bd0ea18 | 607 | if (cursor->level[this_level].dirty && !no_modify) |
a2ceac1f | 608 | libxfs_writebuf(cursor->level[this_level].bp, 0); |
2bd0ea18 | 609 | else |
a2ceac1f | 610 | libxfs_putbuf(cursor->level[this_level].bp); |
2bd0ea18 NS |
611 | cursor->level[this_level].bp = bp; |
612 | cursor->level[this_level].dirty = 0; | |
613 | cursor->level[this_level].bno = dabno; | |
507f4e33 | 614 | cursor->level[this_level].hashval = |
88b32f06 | 615 | be32_to_cpu(btree[0].hashval); |
2bd0ea18 NS |
616 | |
617 | entry = cursor->level[this_level].index = 0; | |
618 | } | |
619 | /* | |
620 | * ditto for block numbers | |
621 | */ | |
88b32f06 | 622 | if (cursor->level[p_level].bno != be32_to_cpu(btree[entry].before)) |
2bd0ea18 | 623 | return(1); |
2bd0ea18 NS |
624 | /* |
625 | * ok, now validate last hashvalue in the descendant | |
626 | * block against the hashval in the current entry | |
627 | */ | |
507f4e33 | 628 | if (cursor->level[p_level].hashval != |
88b32f06 | 629 | be32_to_cpu(btree[entry].hashval)) { |
2bd0ea18 | 630 | if (!no_modify) { |
5d1b7f0f CH |
631 | do_warn( |
632 | _("correcting bad hashval in interior dir block\n" | |
633 | "\tin (level %d) in inode %" PRIu64 ".\n"), | |
2bd0ea18 | 634 | this_level, cursor->ino); |
88b32f06 | 635 | btree[entry].hashval = cpu_to_be32( |
5e656dbb | 636 | cursor->level[p_level].hashval); |
2bd0ea18 NS |
637 | cursor->level[this_level].dirty++; |
638 | } else { | |
5d1b7f0f CH |
639 | do_warn( |
640 | _("would correct bad hashval in interior dir block\n" | |
641 | "\tin (level %d) in inode %" PRIu64 ".\n"), | |
2bd0ea18 NS |
642 | this_level, cursor->ino); |
643 | } | |
644 | } | |
645 | /* | |
646 | * increment index for this level to point to next entry | |
647 | * (which should point to the next descendant block) | |
648 | */ | |
649 | cursor->level[this_level].index++; | |
650 | return(0); | |
651 | } | |
652 | ||
653 | /* | |
654 | * Fix up a shortform directory which was in long form (i8count set) | |
655 | * and is now in short form (i8count clear). | |
656 | * Return pointer to the end of the data when done. | |
657 | */ | |
658 | void | |
659 | process_sf_dir2_fixi8( | |
494434d7 | 660 | struct xfs_mount *mp, |
a354abc8 | 661 | struct xfs_dir2_sf_hdr *sfp, |
2bd0ea18 NS |
662 | xfs_dir2_sf_entry_t **next_sfep) |
663 | { | |
664 | xfs_ino_t ino; | |
a354abc8 | 665 | struct xfs_dir2_sf_hdr *newsfp; |
2bd0ea18 | 666 | xfs_dir2_sf_entry_t *newsfep; |
a354abc8 | 667 | struct xfs_dir2_sf_hdr *oldsfp; |
2bd0ea18 NS |
668 | xfs_dir2_sf_entry_t *oldsfep; |
669 | int oldsize; | |
670 | ||
671 | newsfp = sfp; | |
672 | oldsize = (__psint_t)*next_sfep - (__psint_t)sfp; | |
673 | oldsfp = malloc(oldsize); | |
674 | if (oldsfp == NULL) { | |
507f4e33 | 675 | do_error(_("couldn't malloc dir2 shortform copy\n")); |
2bd0ea18 NS |
676 | exit(1); |
677 | } | |
678 | memmove(oldsfp, newsfp, oldsize); | |
a354abc8 DC |
679 | newsfp->count = oldsfp->count; |
680 | newsfp->i8count = 0; | |
ff105f75 DC |
681 | ino = M_DIROPS(mp)->sf_get_parent_ino(sfp); |
682 | M_DIROPS(mp)->sf_put_parent_ino(newsfp, ino); | |
a354abc8 DC |
683 | oldsfep = xfs_dir2_sf_firstentry(oldsfp); |
684 | newsfep = xfs_dir2_sf_firstentry(newsfp); | |
2bd0ea18 NS |
685 | while ((int)((char *)oldsfep - (char *)oldsfp) < oldsize) { |
686 | newsfep->namelen = oldsfep->namelen; | |
5e656dbb BN |
687 | xfs_dir2_sf_put_offset(newsfep, |
688 | xfs_dir2_sf_get_offset(oldsfep)); | |
2bd0ea18 | 689 | memmove(newsfep->name, oldsfep->name, newsfep->namelen); |
ff105f75 DC |
690 | ino = M_DIROPS(mp)->sf_get_ino(oldsfp, oldsfep); |
691 | M_DIROPS(mp)->sf_put_ino(newsfp, newsfep, ino); | |
692 | oldsfep = M_DIROPS(mp)->sf_nextentry(oldsfp, oldsfep); | |
693 | newsfep = M_DIROPS(mp)->sf_nextentry(newsfp, newsfep); | |
2bd0ea18 NS |
694 | } |
695 | *next_sfep = newsfep; | |
696 | free(oldsfp); | |
697 | } | |
698 | ||
699 | /* | |
700 | * Regenerate legal (minimal) offsets for the shortform directory. | |
701 | */ | |
702 | static void | |
703 | process_sf_dir2_fixoff( | |
584f90b0 | 704 | xfs_mount_t *mp, |
2bd0ea18 NS |
705 | xfs_dinode_t *dip) |
706 | { | |
707 | int i; | |
708 | int offset; | |
709 | xfs_dir2_sf_entry_t *sfep; | |
a354abc8 | 710 | struct xfs_dir2_sf_hdr *sfp; |
2bd0ea18 | 711 | |
a354abc8 DC |
712 | sfp = (struct xfs_dir2_sf_hdr *)XFS_DFORK_DPTR(dip); |
713 | sfep = xfs_dir2_sf_firstentry(sfp); | |
ff105f75 | 714 | offset = M_DIROPS(mp)->data_first_offset; |
5e656dbb | 715 | |
a354abc8 | 716 | for (i = 0; i < sfp->count; i++) { |
5e656dbb | 717 | xfs_dir2_sf_put_offset(sfep, offset); |
ff105f75 DC |
718 | offset += M_DIROPS(mp)->data_entsize(sfep->namelen); |
719 | sfep = M_DIROPS(mp)->sf_nextentry(sfp, sfep); | |
2bd0ea18 NS |
720 | } |
721 | } | |
722 | ||
723 | /* | |
724 | * this routine performs inode discovery and tries to fix things | |
725 | * in place. available redundancy -- inode data size should match | |
726 | * used directory space in inode. | |
727 | * a non-zero return value means the directory is bogus and should be blasted. | |
728 | */ | |
729 | /* ARGSUSED */ | |
730 | static int | |
731 | process_sf_dir2( | |
732 | xfs_mount_t *mp, | |
733 | xfs_ino_t ino, | |
734 | xfs_dinode_t *dip, | |
735 | int ino_discovery, | |
736 | int *dino_dirty, /* out - 1 if dinode buffer dirty */ | |
737 | char *dirname, /* directory pathname */ | |
738 | xfs_ino_t *parent, /* out - NULLFSINO if entry not exist */ | |
739 | int *repair) /* out - 1 if dir was fixed up */ | |
740 | { | |
741 | int bad_offset; | |
742 | int bad_sfnamelen; | |
743 | int i; | |
744 | int i8; | |
745 | __int64_t ino_dir_size; | |
746 | int ino_off; | |
747 | ino_tree_node_t *irec_p; | |
748 | int junkit; | |
749 | char *junkreason = NULL; | |
750 | xfs_ino_t lino; | |
751 | int max_size; | |
752 | char name[MAXNAMELEN + 1]; | |
753 | int namelen; | |
754 | xfs_dir2_sf_entry_t *next_sfep; | |
755 | int num_entries; | |
756 | int offset; | |
a354abc8 | 757 | struct xfs_dir2_sf_hdr *sfp; |
2bd0ea18 NS |
758 | xfs_dir2_sf_entry_t *sfep; |
759 | int tmp_elen; | |
760 | int tmp_len; | |
761 | xfs_dir2_sf_entry_t *tmp_sfep; | |
762 | xfs_ino_t zero = 0; | |
763 | ||
a354abc8 | 764 | sfp = (struct xfs_dir2_sf_hdr *)XFS_DFORK_DPTR(dip); |
46eca962 | 765 | max_size = XFS_DFORK_DSIZE(dip, mp); |
a354abc8 | 766 | num_entries = sfp->count; |
56b2de80 | 767 | ino_dir_size = be64_to_cpu(dip->di_size); |
ff105f75 | 768 | offset = M_DIROPS(mp)->data_first_offset; |
e55d768a | 769 | bad_offset = *repair = 0; |
2bd0ea18 NS |
770 | |
771 | ASSERT(ino_dir_size <= max_size); | |
772 | ||
e55d768a NS |
773 | /* |
774 | * Initialize i8 based on size of parent inode number. | |
775 | */ | |
ff105f75 | 776 | i8 = (M_DIROPS(mp)->sf_get_parent_ino(sfp) > XFS_DIR2_MAX_SHORT_INUM); |
e55d768a | 777 | |
dfc130f3 | 778 | /* |
2bd0ea18 NS |
779 | * check for bad entry count |
780 | */ | |
ff105f75 | 781 | if (num_entries * M_DIROPS(mp)->sf_entsize(sfp, 1) + |
5e656dbb | 782 | xfs_dir2_sf_hdr_size(0) > max_size || num_entries == 0) |
2bd0ea18 NS |
783 | num_entries = 0xFF; |
784 | ||
785 | /* | |
786 | * run through entries, stop at first bad entry, don't need | |
787 | * to check for .. since that's encoded in its own field | |
788 | */ | |
d0bbcbcb | 789 | next_sfep = xfs_dir2_sf_firstentry(sfp); |
2bd0ea18 NS |
790 | for (i = 0; |
791 | i < num_entries && ino_dir_size > (char *)next_sfep - (char *)sfp; | |
792 | i++) { | |
793 | tmp_sfep = NULL; | |
794 | sfep = next_sfep; | |
795 | junkit = 0; | |
796 | bad_sfnamelen = 0; | |
ff105f75 | 797 | lino = M_DIROPS(mp)->sf_get_ino(sfp, sfep); |
2bd0ea18 NS |
798 | /* |
799 | * if entry points to self, junk it since only '.' or '..' | |
800 | * should do that and shortform dirs don't contain either | |
801 | * entry. if inode number is invalid, trash entry. | |
802 | * if entry points to special inodes, trash it. | |
803 | * if inode is unknown but number is valid, | |
804 | * add it to the list of uncertain inodes. don't | |
805 | * have to worry about an entry pointing to a | |
806 | * deleted lost+found inode because the entry was | |
807 | * deleted at the same time that the inode was cleared. | |
808 | */ | |
809 | if (lino == ino) { | |
810 | junkit = 1; | |
507f4e33 | 811 | junkreason = _("current"); |
2bd0ea18 NS |
812 | } else if (verify_inum(mp, lino)) { |
813 | junkit = 1; | |
507f4e33 | 814 | junkreason = _("invalid"); |
2bd0ea18 NS |
815 | } else if (lino == mp->m_sb.sb_rbmino) { |
816 | junkit = 1; | |
507f4e33 | 817 | junkreason = _("realtime bitmap"); |
2bd0ea18 NS |
818 | } else if (lino == mp->m_sb.sb_rsumino) { |
819 | junkit = 1; | |
507f4e33 | 820 | junkreason = _("realtime summary"); |
2bd0ea18 NS |
821 | } else if (lino == mp->m_sb.sb_uquotino) { |
822 | junkit = 1; | |
507f4e33 | 823 | junkreason = _("user quota"); |
b36eef04 | 824 | } else if (lino == mp->m_sb.sb_gquotino) { |
2bd0ea18 | 825 | junkit = 1; |
507f4e33 | 826 | junkreason = _("group quota"); |
0340d706 CS |
827 | } else if (lino == mp->m_sb.sb_pquotino) { |
828 | junkit = 1; | |
829 | junkreason = _("project quota"); | |
1ae311d5 LC |
830 | } else if ((irec_p = find_inode_rec(mp, |
831 | XFS_INO_TO_AGNO(mp, lino), | |
2bd0ea18 NS |
832 | XFS_INO_TO_AGINO(mp, lino))) != NULL) { |
833 | /* | |
834 | * if inode is marked free and we're in inode | |
835 | * discovery mode, leave the entry alone for now. | |
836 | * if the inode turns out to be used, we'll figure | |
837 | * that out when we scan it. If the inode really | |
838 | * is free, we'll hit this code again in phase 4 | |
839 | * after we've finished inode discovery and blow | |
840 | * out the entry then. | |
841 | */ | |
842 | ino_off = XFS_INO_TO_AGINO(mp, lino) - | |
843 | irec_p->ino_startnum; | |
844 | ASSERT(is_inode_confirmed(irec_p, ino_off)); | |
845 | if (is_inode_free(irec_p, ino_off) && !ino_discovery) { | |
846 | junkit = 1; | |
507f4e33 | 847 | junkreason = _("free"); |
2bd0ea18 NS |
848 | } |
849 | } else if (ino_discovery) { | |
850 | /* | |
851 | * put the inode on the uncertain list. we'll | |
852 | * pull the inode off the list and check it later. | |
853 | * if the inode turns out be bogus, we'll delete | |
854 | * this entry in phase 6. | |
855 | */ | |
856 | add_inode_uncertain(mp, lino, 0); | |
857 | } else { | |
858 | /* | |
859 | * blow the entry out. we know about all | |
860 | * undiscovered entries now (past inode discovery | |
861 | * phase) so this is clearly a bogus entry. | |
862 | */ | |
863 | junkit = 1; | |
507f4e33 | 864 | junkreason = _("non-existent"); |
2bd0ea18 NS |
865 | } |
866 | namelen = sfep->namelen; | |
867 | if (junkit) | |
5d1b7f0f CH |
868 | do_warn( |
869 | _("entry \"%*.*s\" in shortform directory %" PRIu64 " references %s inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
870 | namelen, namelen, sfep->name, ino, junkreason, |
871 | lino); | |
1c934a25 ES |
872 | |
873 | /* is dir namelen 0 or does this entry extend past dir size? */ | |
874 | if (namelen == 0) { | |
875 | junkreason = _("is zero length"); | |
2bd0ea18 | 876 | bad_sfnamelen = 1; |
1c934a25 | 877 | } else if ((__psint_t) sfep - (__psint_t) sfp + |
ff105f75 | 878 | M_DIROPS(mp)->sf_entsize(sfp, sfep->namelen) |
1c934a25 ES |
879 | > ino_dir_size) { |
880 | junkreason = _("extends past end of dir"); | |
881 | bad_sfnamelen = 1; | |
882 | } | |
2bd0ea18 | 883 | |
1c934a25 | 884 | if (bad_sfnamelen) { |
38c66abc ES |
885 | do_warn( |
886 | _("entry #%d %s in shortform dir %" PRIu64), | |
887 | i, junkreason, ino); | |
888 | if (!no_modify) | |
889 | do_warn(_(", junking %d entries\n"), | |
890 | num_entries - i); | |
891 | else | |
892 | do_warn(_(", would junk %d entries\n"), | |
893 | num_entries - i); | |
1c934a25 | 894 | /* |
38c66abc ES |
895 | * don't process the rest of the directory, |
896 | * break out of processing loop | |
1c934a25 | 897 | */ |
38c66abc | 898 | break; |
2bd0ea18 NS |
899 | } |
900 | ||
901 | /* | |
902 | * check for illegal chars in name. | |
903 | * no need to check for bad length because | |
904 | * the length value is stored in a byte | |
905 | * so it can't be too big, it can only wrap | |
906 | */ | |
907 | if (namecheck((char *)&sfep->name[0], namelen)) { | |
908 | /* | |
909 | * junk entry | |
910 | */ | |
5d1b7f0f CH |
911 | do_warn( |
912 | _("entry contains illegal character in shortform dir %" PRIu64 "\n"), | |
2bd0ea18 NS |
913 | ino); |
914 | junkit = 1; | |
915 | } | |
916 | ||
5e656dbb | 917 | if (xfs_dir2_sf_get_offset(sfep) < offset) { |
5d1b7f0f CH |
918 | do_warn( |
919 | _("entry contains offset out of order in shortform dir %" PRIu64 "\n"), | |
2bd0ea18 NS |
920 | ino); |
921 | bad_offset = 1; | |
922 | } | |
5e656dbb | 923 | offset = xfs_dir2_sf_get_offset(sfep) + |
ff105f75 | 924 | M_DIROPS(mp)->data_entsize(namelen); |
2bd0ea18 NS |
925 | |
926 | /* | |
927 | * junk the entry by copying up the rest of the | |
928 | * fork over the current entry and decrementing | |
929 | * the entry count. if we're in no_modify mode, | |
930 | * just issue the warning instead. then continue | |
931 | * the loop with the next_sfep pointer set to the | |
932 | * correct place in the fork and other counters | |
933 | * properly set to reflect the deletion if it | |
934 | * happened. | |
935 | */ | |
936 | if (junkit) { | |
dab9b8d6 | 937 | memmove(name, sfep->name, namelen); |
2bd0ea18 NS |
938 | name[namelen] = '\0'; |
939 | ||
940 | if (!no_modify) { | |
ff105f75 | 941 | tmp_elen = M_DIROPS(mp)->sf_entsize(sfp, |
a2ceac1f | 942 | sfep->namelen); |
56b2de80 | 943 | be64_add_cpu(&dip->di_size, -tmp_elen); |
2bd0ea18 NS |
944 | ino_dir_size -= tmp_elen; |
945 | ||
946 | tmp_sfep = (xfs_dir2_sf_entry_t *) | |
947 | ((__psint_t) sfep + tmp_elen); | |
948 | tmp_len = max_size - ((__psint_t) tmp_sfep | |
949 | - (__psint_t) sfp); | |
950 | ||
951 | memmove(sfep, tmp_sfep, tmp_len); | |
952 | ||
a354abc8 | 953 | sfp->count -= 1; |
2bd0ea18 | 954 | num_entries--; |
dab9b8d6 | 955 | memset((void *) ((__psint_t) sfep + tmp_len), 0, |
2bd0ea18 NS |
956 | tmp_elen); |
957 | ||
958 | /* | |
959 | * reset the tmp value to the current | |
960 | * pointer so we'll process the entry | |
961 | * we just moved up | |
962 | */ | |
963 | tmp_sfep = sfep; | |
964 | ||
965 | /* | |
966 | * WARNING: drop the index i by one | |
967 | * so it matches the decremented count | |
968 | * for accurate comparisons later | |
969 | */ | |
970 | i--; | |
971 | ||
972 | *dino_dirty = 1; | |
973 | *repair = 1; | |
974 | ||
5d1b7f0f CH |
975 | do_warn( |
976 | _("junking entry \"%s\" in directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
977 | name, ino); |
978 | } else { | |
5d1b7f0f CH |
979 | do_warn( |
980 | _("would have junked entry \"%s\" in directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
981 | name, ino); |
982 | } | |
983 | } else if (lino > XFS_DIR2_MAX_SHORT_INUM) | |
984 | i8++; | |
985 | /* | |
986 | * go onto next entry unless we've just junked an | |
987 | * entry in which the current entry pointer points | |
988 | * to an unprocessed entry. have to take into zero-len | |
989 | * entries into account in no modify mode since we | |
990 | * calculate size based on next_sfep. | |
991 | */ | |
992 | next_sfep = (tmp_sfep == NULL) | |
993 | ? (xfs_dir2_sf_entry_t *) ((__psint_t) sfep | |
a2ceac1f | 994 | + ((!bad_sfnamelen) |
ff105f75 DC |
995 | ? M_DIROPS(mp)->sf_entsize(sfp, sfep->namelen) |
996 | : M_DIROPS(mp)->sf_entsize(sfp, namelen))) | |
2bd0ea18 NS |
997 | : tmp_sfep; |
998 | } | |
999 | ||
1000 | /* sync up sizes and entry counts */ | |
1001 | ||
a354abc8 | 1002 | if (sfp->count != i) { |
2bd0ea18 | 1003 | if (no_modify) { |
5d1b7f0f CH |
1004 | do_warn( |
1005 | _("would have corrected entry count in directory %" PRIu64 " from %d to %d\n"), | |
a354abc8 | 1006 | ino, sfp->count, i); |
2bd0ea18 | 1007 | } else { |
5d1b7f0f | 1008 | do_warn( |
b52923f3 | 1009 | _("corrected entry count in directory %" PRIu64 ", was %d, now %d\n"), |
a354abc8 DC |
1010 | ino, sfp->count, i); |
1011 | sfp->count = i; | |
2bd0ea18 NS |
1012 | *dino_dirty = 1; |
1013 | *repair = 1; | |
1014 | } | |
1015 | } | |
1016 | ||
a354abc8 | 1017 | if (sfp->i8count != i8) { |
2bd0ea18 | 1018 | if (no_modify) { |
5d1b7f0f CH |
1019 | do_warn( |
1020 | _("would have corrected i8 count in directory %" PRIu64 " from %d to %d\n"), | |
a354abc8 | 1021 | ino, sfp->i8count, i8); |
2bd0ea18 | 1022 | } else { |
5d1b7f0f CH |
1023 | do_warn( |
1024 | _("corrected i8 count in directory %" PRIu64 ", was %d, now %d\n"), | |
a354abc8 | 1025 | ino, sfp->i8count, i8); |
2bd0ea18 | 1026 | if (i8 == 0) |
494434d7 | 1027 | process_sf_dir2_fixi8(mp, sfp, &next_sfep); |
2bd0ea18 | 1028 | else |
a354abc8 | 1029 | sfp->i8count = i8; |
2bd0ea18 NS |
1030 | *dino_dirty = 1; |
1031 | *repair = 1; | |
1032 | } | |
1033 | } | |
1034 | ||
5d1b7f0f | 1035 | if ((intptr_t)next_sfep - (intptr_t)sfp != ino_dir_size) { |
2bd0ea18 | 1036 | if (no_modify) { |
5d1b7f0f CH |
1037 | do_warn( |
1038 | _("would have corrected directory %" PRIu64 " size from %" PRId64 " to %" PRIdPTR "\n"), | |
1039 | ino, ino_dir_size, | |
1040 | (intptr_t)next_sfep - (intptr_t)sfp); | |
2bd0ea18 | 1041 | } else { |
5d1b7f0f CH |
1042 | do_warn( |
1043 | _("corrected directory %" PRIu64 " size, was %" PRId64 ", now %" PRIdPTR "\n"), | |
1044 | ino, ino_dir_size, | |
1045 | (intptr_t)next_sfep - (intptr_t)sfp); | |
2bd0ea18 | 1046 | |
56b2de80 | 1047 | dip->di_size = cpu_to_be64( |
5e656dbb | 1048 | (__psint_t)next_sfep - (__psint_t)sfp); |
2bd0ea18 NS |
1049 | *dino_dirty = 1; |
1050 | *repair = 1; | |
1051 | } | |
1052 | } | |
a354abc8 | 1053 | if (offset + (sfp->count + 2) * sizeof(xfs_dir2_leaf_entry_t) + |
ff105f75 | 1054 | sizeof(xfs_dir2_block_tail_t) > mp->m_dir_geo->blksize) { |
5d1b7f0f | 1055 | do_warn(_("directory %" PRIu64 " offsets too high\n"), ino); |
2bd0ea18 NS |
1056 | bad_offset = 1; |
1057 | } | |
1058 | if (bad_offset) { | |
1059 | if (no_modify) { | |
5d1b7f0f CH |
1060 | do_warn( |
1061 | _("would have corrected entry offsets in directory %" PRIu64 "\n"), | |
2bd0ea18 NS |
1062 | ino); |
1063 | } else { | |
5d1b7f0f CH |
1064 | do_warn( |
1065 | _("corrected entry offsets in directory %" PRIu64 "\n"), | |
2bd0ea18 | 1066 | ino); |
584f90b0 | 1067 | process_sf_dir2_fixoff(mp, dip); |
2bd0ea18 NS |
1068 | *dino_dirty = 1; |
1069 | *repair = 1; | |
1070 | } | |
1071 | } | |
1072 | ||
1073 | /* | |
1074 | * check parent (..) entry | |
1075 | */ | |
ff105f75 | 1076 | *parent = M_DIROPS(mp)->sf_get_parent_ino(sfp); |
2bd0ea18 NS |
1077 | |
1078 | /* | |
1079 | * if parent entry is bogus, null it out. we'll fix it later . | |
1080 | */ | |
1081 | if (verify_inum(mp, *parent)) { | |
1082 | ||
5d1b7f0f CH |
1083 | do_warn( |
1084 | _("bogus .. inode number (%" PRIu64 ") in directory inode %" PRIu64 ", "), | |
2bd0ea18 NS |
1085 | *parent, ino); |
1086 | *parent = NULLFSINO; | |
1087 | if (!no_modify) { | |
507f4e33 | 1088 | do_warn(_("clearing inode number\n")); |
2bd0ea18 | 1089 | |
ff105f75 | 1090 | M_DIROPS(mp)->sf_put_parent_ino(sfp, zero); |
2bd0ea18 NS |
1091 | *dino_dirty = 1; |
1092 | *repair = 1; | |
1093 | } else { | |
507f4e33 | 1094 | do_warn(_("would clear inode number\n")); |
2bd0ea18 NS |
1095 | } |
1096 | } else if (ino == mp->m_sb.sb_rootino && ino != *parent) { | |
1097 | /* | |
1098 | * root directories must have .. == . | |
1099 | */ | |
1100 | if (!no_modify) { | |
5d1b7f0f CH |
1101 | do_warn( |
1102 | _("corrected root directory %" PRIu64 " .. entry, was %" PRIu64 ", now %" PRIu64 "\n"), | |
2bd0ea18 NS |
1103 | ino, *parent, ino); |
1104 | *parent = ino; | |
ff105f75 | 1105 | M_DIROPS(mp)->sf_put_parent_ino(sfp, ino); |
2bd0ea18 NS |
1106 | *dino_dirty = 1; |
1107 | *repair = 1; | |
1108 | } else { | |
5d1b7f0f CH |
1109 | do_warn( |
1110 | _("would have corrected root directory %" PRIu64 " .. entry from %" PRIu64" to %" PRIu64 "\n"), | |
2bd0ea18 NS |
1111 | ino, *parent, ino); |
1112 | } | |
1113 | } else if (ino == *parent && ino != mp->m_sb.sb_rootino) { | |
1114 | /* | |
1115 | * likewise, non-root directories can't have .. pointing | |
1116 | * to . | |
1117 | */ | |
1118 | *parent = NULLFSINO; | |
5d1b7f0f CH |
1119 | do_warn( |
1120 | _("bad .. entry in directory inode %" PRIu64 ", points to self, "), | |
2bd0ea18 NS |
1121 | ino); |
1122 | if (!no_modify) { | |
507f4e33 | 1123 | do_warn(_("clearing inode number\n")); |
2bd0ea18 | 1124 | |
ff105f75 | 1125 | M_DIROPS(mp)->sf_put_parent_ino(sfp, zero); |
2bd0ea18 NS |
1126 | *dino_dirty = 1; |
1127 | *repair = 1; | |
1128 | } else { | |
507f4e33 | 1129 | do_warn(_("would clear inode number\n")); |
2bd0ea18 NS |
1130 | } |
1131 | } | |
1132 | ||
1133 | return(0); | |
1134 | } | |
1135 | ||
1136 | /* | |
1137 | * Process one directory data block. | |
1138 | */ | |
1139 | /* ARGSUSED */ | |
1140 | static int | |
1141 | process_dir2_data( | |
1142 | xfs_mount_t *mp, | |
1143 | xfs_ino_t ino, | |
1144 | xfs_dinode_t *dip, | |
1145 | int ino_discovery, | |
1146 | char *dirname, /* directory pathname */ | |
1147 | xfs_ino_t *parent, /* out - NULLFSINO if entry not exist */ | |
a2ceac1f | 1148 | struct xfs_buf *bp, |
2bd0ea18 NS |
1149 | int *dot, /* out - 1 if there is a dot, else 0 */ |
1150 | int *dotdot, /* out - 1 if there's a dotdot, else 0 */ | |
1151 | xfs_dablk_t da_bno, | |
a2ceac1f DC |
1152 | char *endptr, |
1153 | int *dirty) | |
2bd0ea18 NS |
1154 | { |
1155 | int badbest; | |
1156 | xfs_dir2_data_free_t *bf; | |
1157 | int clearino; | |
1158 | char *clearreason = NULL; | |
a354abc8 | 1159 | struct xfs_dir2_data_hdr *d; |
2bd0ea18 NS |
1160 | xfs_dir2_data_entry_t *dep; |
1161 | xfs_dir2_data_free_t *dfp; | |
1162 | xfs_dir2_data_unused_t *dup; | |
1163 | int freeseen; | |
1164 | int i; | |
1165 | int ino_off; | |
1166 | ino_tree_node_t *irec_p; | |
1167 | int junkit; | |
1168 | int lastfree; | |
1169 | int nm_illegal; | |
1170 | char *ptr; | |
5e656dbb | 1171 | xfs_ino_t ent_ino; |
2bd0ea18 | 1172 | |
a2ceac1f | 1173 | d = bp->b_addr; |
ff105f75 DC |
1174 | bf = M_DIROPS(mp)->data_bestfree_p(d); |
1175 | ptr = (char *)M_DIROPS(mp)->data_entry_p(d); | |
2bd0ea18 | 1176 | badbest = lastfree = freeseen = 0; |
5e656dbb BN |
1177 | if (be16_to_cpu(bf[0].length) == 0) { |
1178 | badbest |= be16_to_cpu(bf[0].offset) != 0; | |
2bd0ea18 NS |
1179 | freeseen |= 1 << 0; |
1180 | } | |
5e656dbb BN |
1181 | if (be16_to_cpu(bf[1].length) == 0) { |
1182 | badbest |= be16_to_cpu(bf[1].offset) != 0; | |
2bd0ea18 NS |
1183 | freeseen |= 1 << 1; |
1184 | } | |
5e656dbb BN |
1185 | if (be16_to_cpu(bf[2].length) == 0) { |
1186 | badbest |= be16_to_cpu(bf[2].offset) != 0; | |
2bd0ea18 NS |
1187 | freeseen |= 1 << 2; |
1188 | } | |
5e656dbb BN |
1189 | badbest |= be16_to_cpu(bf[0].length) < be16_to_cpu(bf[1].length); |
1190 | badbest |= be16_to_cpu(bf[1].length) < be16_to_cpu(bf[2].length); | |
2bd0ea18 NS |
1191 | while (ptr < endptr) { |
1192 | dup = (xfs_dir2_data_unused_t *)ptr; | |
1193 | /* | |
1194 | * If it's unused, look for the space in the bestfree table. | |
1195 | * If we find it, account for that, else make sure it doesn't | |
1196 | * need to be there. | |
1197 | */ | |
5e656dbb BN |
1198 | if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { |
1199 | if (ptr + be16_to_cpu(dup->length) > endptr || | |
1200 | be16_to_cpu(dup->length) == 0 || | |
1201 | (be16_to_cpu(dup->length) & (XFS_DIR2_DATA_ALIGN - 1))) | |
2bd0ea18 | 1202 | break; |
5e656dbb BN |
1203 | if (be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)) != |
1204 | (char *)dup - (char *)d) | |
2bd0ea18 NS |
1205 | break; |
1206 | badbest |= lastfree != 0; | |
ff105f75 | 1207 | dfp = xfs_dir2_data_freefind(d, bf, dup); |
2bd0ea18 NS |
1208 | if (dfp) { |
1209 | i = dfp - bf; | |
1210 | badbest |= (freeseen & (1 << i)) != 0; | |
1211 | freeseen |= 1 << i; | |
1212 | } else | |
5e656dbb BN |
1213 | badbest |= be16_to_cpu(dup->length) > |
1214 | be16_to_cpu(bf[2].length); | |
1215 | ptr += be16_to_cpu(dup->length); | |
2bd0ea18 NS |
1216 | lastfree = 1; |
1217 | continue; | |
1218 | } | |
1219 | dep = (xfs_dir2_data_entry_t *)ptr; | |
ff105f75 | 1220 | if (ptr + M_DIROPS(mp)->data_entsize(dep->namelen) > endptr) |
2bd0ea18 | 1221 | break; |
ff105f75 | 1222 | if (be16_to_cpu(*M_DIROPS(mp)->data_entry_tag_p(dep)) != |
5e656dbb | 1223 | (char *)dep - (char *)d) |
2bd0ea18 | 1224 | break; |
ff105f75 | 1225 | ptr += M_DIROPS(mp)->data_entsize(dep->namelen); |
2bd0ea18 NS |
1226 | lastfree = 0; |
1227 | } | |
1228 | /* | |
1229 | * Dropped out before we processed everything, give up. | |
1230 | * Phase 6 will kill this block if we don't kill the inode. | |
1231 | */ | |
1232 | if (ptr != endptr) { | |
5d1b7f0f | 1233 | do_warn(_("corrupt block %u in directory inode %" PRIu64 "\n"), |
2bd0ea18 NS |
1234 | da_bno, ino); |
1235 | if (!no_modify) | |
507f4e33 | 1236 | do_warn(_("\twill junk block\n")); |
2bd0ea18 | 1237 | else |
507f4e33 | 1238 | do_warn(_("\twould junk block\n")); |
2bd0ea18 NS |
1239 | return 1; |
1240 | } | |
ff105f75 | 1241 | ptr = (char *)M_DIROPS(mp)->data_entry_p(d); |
2bd0ea18 NS |
1242 | /* |
1243 | * Process the entries now. | |
1244 | */ | |
1245 | while (ptr < endptr) { | |
1246 | dup = (xfs_dir2_data_unused_t *)ptr; | |
5e656dbb BN |
1247 | if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { |
1248 | ptr += be16_to_cpu(dup->length); | |
2bd0ea18 NS |
1249 | continue; |
1250 | } | |
1251 | dep = (xfs_dir2_data_entry_t *)ptr; | |
5e656dbb BN |
1252 | ent_ino = be64_to_cpu(dep->inumber); |
1253 | clearino = 1; | |
1254 | clearreason = NULL; | |
2bd0ea18 NS |
1255 | /* |
1256 | * We may have to blow out an entry because of bad inode | |
1257 | * numbers. Do NOT touch the name until after we've computed | |
1258 | * the hashvalue and done a namecheck() on the name. | |
5e656dbb | 1259 | * |
4c0a98ae | 1260 | * Conditions must either set clearino to zero or set |
5e656dbb | 1261 | * clearreason why it's being cleared. |
2bd0ea18 | 1262 | */ |
871fbfd5 | 1263 | if (!ino_discovery && dep->name[0] == '/') { |
2bd0ea18 NS |
1264 | /* |
1265 | * Don't do a damned thing. We already found this | |
1266 | * (or did it ourselves) during phase 3. | |
1267 | */ | |
1268 | clearino = 0; | |
5e656dbb | 1269 | } else if (verify_inum(mp, ent_ino)) { |
2bd0ea18 NS |
1270 | /* |
1271 | * Bad inode number. Clear the inode number and the | |
1272 | * entry will get removed later. We don't trash the | |
1273 | * directory since it's still structurally intact. | |
1274 | */ | |
507f4e33 | 1275 | clearreason = _("invalid"); |
5e656dbb | 1276 | } else if (ent_ino == mp->m_sb.sb_rbmino) { |
507f4e33 | 1277 | clearreason = _("realtime bitmap"); |
5e656dbb | 1278 | } else if (ent_ino == mp->m_sb.sb_rsumino) { |
507f4e33 | 1279 | clearreason = _("realtime summary"); |
5e656dbb | 1280 | } else if (ent_ino == mp->m_sb.sb_uquotino) { |
507f4e33 | 1281 | clearreason = _("user quota"); |
5e656dbb | 1282 | } else if (ent_ino == mp->m_sb.sb_gquotino) { |
507f4e33 | 1283 | clearreason = _("group quota"); |
0340d706 CS |
1284 | } else if (ent_ino == mp->m_sb.sb_pquotino) { |
1285 | clearreason = _("project quota"); | |
2bd0ea18 | 1286 | } else { |
1ae311d5 LC |
1287 | irec_p = find_inode_rec(mp, |
1288 | XFS_INO_TO_AGNO(mp, ent_ino), | |
5e656dbb BN |
1289 | XFS_INO_TO_AGINO(mp, ent_ino)); |
1290 | if (irec_p == NULL) { | |
1291 | if (ino_discovery) { | |
1292 | add_inode_uncertain(mp, ent_ino, 0); | |
1293 | clearino = 0; | |
4c0a98ae | 1294 | } else |
5e656dbb BN |
1295 | clearreason = _("non-existent"); |
1296 | } else { | |
1297 | /* | |
1298 | * Inode recs should have only confirmed | |
1299 | * inodes in them. | |
1300 | */ | |
1301 | ino_off = XFS_INO_TO_AGINO(mp, ent_ino) | |
1302 | - irec_p->ino_startnum; | |
1303 | ASSERT(is_inode_confirmed(irec_p, ino_off)); | |
1304 | /* | |
1305 | * If inode is marked free and we're in inode | |
1306 | * discovery mode, leave the entry alone for | |
1307 | * now. If the inode turns out to be used, | |
1308 | * we'll figure that out when we scan it. | |
1309 | * If the inode really is free, we'll hit this | |
1310 | * code again in phase 4 after we've finished | |
1311 | * inode discovery and blow out the entry then. | |
1312 | */ | |
1313 | if (!ino_discovery && is_inode_free(irec_p, | |
1314 | ino_off)) | |
1315 | clearreason = _("free"); | |
1316 | else | |
1317 | clearino = 0; | |
1318 | } | |
2bd0ea18 | 1319 | } |
5e656dbb BN |
1320 | ASSERT((clearino == 0 && clearreason == NULL) || |
1321 | (clearino != 0 && clearreason != NULL)); | |
2bd0ea18 | 1322 | if (clearino) |
5d1b7f0f CH |
1323 | do_warn( |
1324 | _("entry \"%*.*s\" at block %d offset %" PRIdPTR " in directory inode %" PRIu64 | |
1325 | " references %s inode %" PRIu64 "\n"), | |
2bd0ea18 | 1326 | dep->namelen, dep->namelen, dep->name, |
5d1b7f0f | 1327 | da_bno, (intptr_t)ptr - (intptr_t)d, ino, |
5e656dbb | 1328 | clearreason, ent_ino); |
b8ec08a1 ES |
1329 | |
1330 | /* | |
1331 | * We have a special dot & dotdot fixer-upper below which can | |
1332 | * sort out the proper inode number, so don't clear it. | |
1333 | */ | |
1334 | if ((dep->namelen == 1 && dep->name[0] == '.') || | |
1335 | (dep->namelen == 2 && | |
1336 | dep->name[0] == '.' && dep->name[1] == '.')) { | |
1337 | clearino = 0; | |
1338 | clearreason = NULL; | |
1339 | } | |
1340 | ||
2bd0ea18 NS |
1341 | /* |
1342 | * If the name length is 0 (illegal) make it 1 and blast | |
1343 | * the entry. | |
1344 | */ | |
1345 | if (dep->namelen == 0) { | |
5d1b7f0f CH |
1346 | do_warn( |
1347 | _("entry at block %u offset %" PRIdPTR " in directory inode %" PRIu64 | |
1348 | "has 0 namelength\n"), | |
1349 | da_bno, (intptr_t)ptr - (intptr_t)d, ino); | |
2bd0ea18 NS |
1350 | if (!no_modify) |
1351 | dep->namelen = 1; | |
1352 | clearino = 1; | |
1353 | } | |
1354 | /* | |
1355 | * If needed to clear the inode number, do it now. | |
1356 | */ | |
1357 | if (clearino) { | |
1358 | if (!no_modify) { | |
5d1b7f0f CH |
1359 | do_warn( |
1360 | _("\tclearing inode number in entry at offset %" PRIdPTR "...\n"), | |
1361 | (intptr_t)ptr - (intptr_t)d); | |
871fbfd5 | 1362 | dep->name[0] = '/'; |
a2ceac1f | 1363 | *dirty = 1; |
2bd0ea18 | 1364 | } else { |
5d1b7f0f CH |
1365 | do_warn( |
1366 | _("\twould clear inode number in entry at offset %" PRIdPTR "...\n"), | |
1367 | (intptr_t)ptr - (intptr_t)d); | |
2bd0ea18 NS |
1368 | } |
1369 | } | |
1370 | /* | |
1371 | * Only complain about illegal names in phase 3 (when inode | |
1372 | * discovery is turned on). Otherwise, we'd complain a lot | |
1373 | * during phase 4. | |
1374 | */ | |
871fbfd5 | 1375 | junkit = dep->name[0] == '/'; |
2bd0ea18 NS |
1376 | nm_illegal = namecheck((char *)dep->name, dep->namelen); |
1377 | if (ino_discovery && nm_illegal) { | |
5d1b7f0f CH |
1378 | do_warn( |
1379 | _("entry at block %u offset %" PRIdPTR " in directory inode %" PRIu64 " has illegal name \"%*.*s\": "), | |
1380 | da_bno, (intptr_t)ptr - (intptr_t)d, ino, | |
2bd0ea18 NS |
1381 | dep->namelen, dep->namelen, dep->name); |
1382 | junkit = 1; | |
1383 | } | |
871fbfd5 | 1384 | |
2bd0ea18 | 1385 | /* |
871fbfd5 | 1386 | * Ensure we write back bad entries for later processing |
2bd0ea18 | 1387 | */ |
871fbfd5 | 1388 | if (!no_modify && dep->name[0] == '/') { |
a2ceac1f | 1389 | *dirty = 1; |
2bd0ea18 NS |
1390 | junkit = 0; |
1391 | } | |
871fbfd5 | 1392 | |
2bd0ea18 NS |
1393 | /* |
1394 | * Special .. entry processing. | |
1395 | */ | |
1396 | if (dep->namelen == 2 && | |
1397 | dep->name[0] == '.' && dep->name[1] == '.') { | |
1398 | if (!*dotdot) { | |
1399 | (*dotdot)++; | |
5e656dbb | 1400 | *parent = ent_ino; |
2bd0ea18 NS |
1401 | /* |
1402 | * What if .. == .? Legal only in the root | |
1403 | * inode. Blow out entry and set parent to | |
1404 | * NULLFSINO otherwise. | |
1405 | */ | |
5e656dbb BN |
1406 | if (ino == ent_ino && |
1407 | ino != mp->m_sb.sb_rootino) { | |
2bd0ea18 | 1408 | *parent = NULLFSINO; |
5d1b7f0f CH |
1409 | do_warn( |
1410 | _("bad .. entry in directory inode %" PRIu64 ", points to self: "), | |
2bd0ea18 NS |
1411 | ino); |
1412 | junkit = 1; | |
1413 | } | |
1414 | /* | |
1415 | * We have to make sure that . == .. in the | |
1416 | * root inode. | |
1417 | */ | |
5e656dbb BN |
1418 | else if (ino != ent_ino && |
1419 | ino == mp->m_sb.sb_rootino) { | |
5d1b7f0f CH |
1420 | do_warn( |
1421 | _("bad .. entry in root directory inode %" PRIu64 ", was %" PRIu64 ": "), | |
5e656dbb | 1422 | ino, ent_ino); |
2bd0ea18 | 1423 | if (!no_modify) { |
507f4e33 | 1424 | do_warn(_("correcting\n")); |
5e656dbb | 1425 | dep->inumber = cpu_to_be64(ino); |
a2ceac1f | 1426 | *dirty = 1; |
2bd0ea18 | 1427 | } else { |
507f4e33 | 1428 | do_warn(_("would correct\n")); |
2bd0ea18 | 1429 | } |
aedcecb5 | 1430 | *parent = ino; |
2bd0ea18 NS |
1431 | } |
1432 | } | |
1433 | /* | |
1434 | * Can't fix the directory unless we know which .. | |
1435 | * entry is the right one. Both have valid inode | |
1436 | * numbers or we wouldn't be here. So since both | |
1437 | * seem equally valid, trash this one. | |
1438 | */ | |
1439 | else { | |
5d1b7f0f CH |
1440 | do_warn( |
1441 | _("multiple .. entries in directory inode %" PRIu64 ": "), | |
2bd0ea18 NS |
1442 | ino); |
1443 | junkit = 1; | |
1444 | } | |
1445 | } | |
1446 | /* | |
1447 | * Special . entry processing. | |
1448 | */ | |
1449 | else if (dep->namelen == 1 && dep->name[0] == '.') { | |
1450 | if (!*dot) { | |
1451 | (*dot)++; | |
5e656dbb | 1452 | if (ent_ino != ino) { |
5d1b7f0f CH |
1453 | do_warn( |
1454 | _("bad . entry in directory inode %" PRIu64 ", was %" PRIu64 ": "), | |
5e656dbb | 1455 | ino, ent_ino); |
2bd0ea18 | 1456 | if (!no_modify) { |
507f4e33 | 1457 | do_warn(_("correcting\n")); |
5e656dbb | 1458 | dep->inumber = cpu_to_be64(ino); |
a2ceac1f | 1459 | *dirty = 1; |
2bd0ea18 | 1460 | } else { |
507f4e33 | 1461 | do_warn(_("would correct\n")); |
2bd0ea18 NS |
1462 | } |
1463 | } | |
1464 | } else { | |
5d1b7f0f CH |
1465 | do_warn( |
1466 | _("multiple . entries in directory inode %" PRIu64 ": "), | |
2bd0ea18 NS |
1467 | ino); |
1468 | junkit = 1; | |
1469 | } | |
1470 | } | |
1471 | /* | |
1472 | * All other entries -- make sure only . references self. | |
1473 | */ | |
5e656dbb | 1474 | else if (ent_ino == ino) { |
5d1b7f0f CH |
1475 | do_warn( |
1476 | _("entry \"%*.*s\" in directory inode %" PRIu64 " points to self: "), | |
2bd0ea18 NS |
1477 | dep->namelen, dep->namelen, dep->name, ino); |
1478 | junkit = 1; | |
1479 | } | |
1480 | /* | |
1481 | * Clear junked entries. | |
1482 | */ | |
1483 | if (junkit) { | |
1484 | if (!no_modify) { | |
1485 | dep->name[0] = '/'; | |
a2ceac1f | 1486 | *dirty = 1; |
507f4e33 | 1487 | do_warn(_("clearing entry\n")); |
2bd0ea18 | 1488 | } else { |
507f4e33 | 1489 | do_warn(_("would clear entry\n")); |
2bd0ea18 NS |
1490 | } |
1491 | } | |
1492 | /* | |
1493 | * Advance to the next entry. | |
1494 | */ | |
ff105f75 | 1495 | ptr += M_DIROPS(mp)->data_entsize(dep->namelen); |
2bd0ea18 NS |
1496 | } |
1497 | /* | |
1498 | * Check the bestfree table. | |
1499 | */ | |
1500 | if (freeseen != 7 || badbest) { | |
5d1b7f0f CH |
1501 | do_warn( |
1502 | _("bad bestfree table in block %u in directory inode %" PRIu64 ": "), | |
1503 | da_bno, ino); | |
2bd0ea18 | 1504 | if (!no_modify) { |
507f4e33 | 1505 | do_warn(_("repairing table\n")); |
ff105f75 DC |
1506 | libxfs_dir2_data_freescan(mp->m_dir_geo, M_DIROPS(mp), |
1507 | d, &i); | |
a2ceac1f | 1508 | *dirty = 1; |
2bd0ea18 | 1509 | } else { |
507f4e33 | 1510 | do_warn(_("would repair table\n")); |
2bd0ea18 NS |
1511 | } |
1512 | } | |
1513 | return 0; | |
1514 | } | |
1515 | ||
1516 | /* | |
1517 | * Process a block-format directory. | |
1518 | */ | |
1519 | /* ARGSUSED */ | |
1520 | static int | |
1521 | process_block_dir2( | |
1522 | xfs_mount_t *mp, | |
1523 | xfs_ino_t ino, | |
1524 | xfs_dinode_t *dip, | |
1525 | int ino_discovery, | |
1526 | int *dino_dirty, /* out - 1 if dinode buffer dirty */ | |
1527 | char *dirname, /* directory pathname */ | |
1528 | xfs_ino_t *parent, /* out - NULLFSINO if entry not exist */ | |
1529 | blkmap_t *blkmap, | |
1530 | int *dot, /* out - 1 if there is a dot, else 0 */ | |
1531 | int *dotdot, /* out - 1 if there's a dotdot, else 0 */ | |
1532 | int *repair) /* out - 1 if something was fixed */ | |
1533 | { | |
a354abc8 | 1534 | struct xfs_dir2_data_hdr *block; |
2bd0ea18 NS |
1535 | xfs_dir2_leaf_entry_t *blp; |
1536 | bmap_ext_t *bmp; | |
a2ceac1f | 1537 | struct xfs_buf *bp; |
2bd0ea18 NS |
1538 | xfs_dir2_block_tail_t *btp; |
1539 | int nex; | |
1540 | int rval; | |
1e77098c | 1541 | bmap_ext_t lbmp; |
a2ceac1f | 1542 | int dirty = 0; |
2bd0ea18 NS |
1543 | |
1544 | *repair = *dot = *dotdot = 0; | |
1545 | *parent = NULLFSINO; | |
ff105f75 DC |
1546 | nex = blkmap_getn(blkmap, mp->m_dir_geo->datablk, |
1547 | mp->m_dir_geo->fsbcount, &bmp, &lbmp); | |
2bd0ea18 | 1548 | if (nex == 0) { |
5d1b7f0f CH |
1549 | do_warn( |
1550 | _("block %u for directory inode %" PRIu64 " is missing\n"), | |
ff105f75 | 1551 | mp->m_dir_geo->datablk, ino); |
2bd0ea18 NS |
1552 | return 1; |
1553 | } | |
75c8b434 | 1554 | bp = da_read_buf(mp, nex, bmp, &xfs_dir3_block_buf_ops); |
1e77098c MV |
1555 | if (bmp != &lbmp) |
1556 | free(bmp); | |
2bd0ea18 | 1557 | if (bp == NULL) { |
5d1b7f0f CH |
1558 | do_warn( |
1559 | _("can't read block %u for directory inode %" PRIu64 "\n"), | |
ff105f75 | 1560 | mp->m_dir_geo->datablk, ino); |
2bd0ea18 NS |
1561 | return 1; |
1562 | } | |
1563 | /* | |
1564 | * Verify the block | |
1565 | */ | |
a2ceac1f | 1566 | block = bp->b_addr; |
a354abc8 DC |
1567 | if (!(be32_to_cpu(block->magic) == XFS_DIR2_BLOCK_MAGIC || |
1568 | be32_to_cpu(block->magic) == XFS_DIR3_BLOCK_MAGIC)) | |
5d1b7f0f CH |
1569 | do_warn( |
1570 | _("bad directory block magic # %#x in block %u for directory inode %" PRIu64 "\n"), | |
ff105f75 | 1571 | be32_to_cpu(block->magic), mp->m_dir_geo->datablk, ino); |
2bd0ea18 NS |
1572 | /* |
1573 | * process the data area | |
1574 | * this also checks & fixes the bestfree | |
1575 | */ | |
ff105f75 | 1576 | btp = xfs_dir2_block_tail_p(mp->m_dir_geo, block); |
5e656dbb | 1577 | blp = xfs_dir2_block_leaf_p(btp); |
2bd0ea18 NS |
1578 | /* |
1579 | * Don't let this go past the end of the block. | |
1580 | */ | |
1581 | if ((char *)blp > (char *)btp) | |
1582 | blp = (xfs_dir2_leaf_entry_t *)btp; | |
1583 | rval = process_dir2_data(mp, ino, dip, ino_discovery, dirname, parent, | |
ff105f75 | 1584 | bp, dot, dotdot, mp->m_dir_geo->datablk, (char *)blp, &dirty); |
575010d5 | 1585 | /* If block looks ok but CRC didn't match, make sure to recompute it. */ |
12b53197 | 1586 | if (!rval && bp->b_error == -EFSBADCRC) |
575010d5 | 1587 | dirty = 1; |
a2ceac1f | 1588 | if (dirty && !no_modify) { |
2bd0ea18 | 1589 | *repair = 1; |
a2ceac1f | 1590 | libxfs_writebuf(bp, 0); |
2bd0ea18 | 1591 | } else |
a2ceac1f | 1592 | libxfs_putbuf(bp); |
2bd0ea18 NS |
1593 | return rval; |
1594 | } | |
1595 | ||
1596 | /* | |
1597 | * Validates leaf contents, node format directories only. | |
1598 | * magic number and sibling pointers checked by caller. | |
1599 | * Returns 0 if block is ok, 1 if the block is bad. | |
1600 | * Looking for: out of order hash values, bad stale counts. | |
1601 | */ | |
1602 | static int | |
1603 | process_leaf_block_dir2( | |
1604 | xfs_mount_t *mp, | |
1605 | xfs_dir2_leaf_t *leaf, | |
1606 | xfs_dablk_t da_bno, | |
1607 | xfs_ino_t ino, | |
1608 | xfs_dahash_t last_hashval, | |
1609 | xfs_dahash_t *next_hashval) | |
1610 | { | |
1611 | int i; | |
1612 | int stale; | |
65b80c98 | 1613 | struct xfs_dir2_leaf_entry *ents; |
658ac3e3 | 1614 | struct xfs_dir3_icleaf_hdr leafhdr; |
65b80c98 | 1615 | |
ff105f75 DC |
1616 | M_DIROPS(mp)->leaf_hdr_from_disk(&leafhdr, leaf); |
1617 | ents = M_DIROPS(mp)->leaf_ents_p(leaf); | |
2bd0ea18 | 1618 | |
658ac3e3 | 1619 | for (i = stale = 0; i < leafhdr.count; i++) { |
ff105f75 | 1620 | if ((char *)&ents[i] >= (char *)leaf + mp->m_dir_geo->blksize) { |
5d1b7f0f CH |
1621 | do_warn( |
1622 | _("bad entry count in block %u of directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
1623 | da_bno, ino); |
1624 | return 1; | |
1625 | } | |
65b80c98 | 1626 | if (be32_to_cpu(ents[i].address) == XFS_DIR2_NULL_DATAPTR) |
2bd0ea18 | 1627 | stale++; |
65b80c98 | 1628 | else if (be32_to_cpu(ents[i].hashval) < last_hashval) { |
5d1b7f0f CH |
1629 | do_warn( |
1630 | _("bad hash ordering in block %u of directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
1631 | da_bno, ino); |
1632 | return 1; | |
1633 | } | |
65b80c98 | 1634 | *next_hashval = last_hashval = be32_to_cpu(ents[i].hashval); |
2bd0ea18 | 1635 | } |
658ac3e3 | 1636 | if (stale != leafhdr.stale) { |
5d1b7f0f CH |
1637 | do_warn( |
1638 | _("bad stale count in block %u of directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
1639 | da_bno, ino); |
1640 | return 1; | |
1641 | } | |
1642 | return 0; | |
1643 | } | |
1644 | ||
1645 | /* | |
1646 | * Returns 0 if the directory is ok, 1 if it has to be rebuilt. | |
1647 | */ | |
1648 | static int | |
1649 | process_leaf_level_dir2( | |
1650 | xfs_mount_t *mp, | |
1651 | dir2_bt_cursor_t *da_cursor, | |
1652 | int *repair) | |
dfc130f3 | 1653 | { |
2bd0ea18 | 1654 | bmap_ext_t *bmp; |
a2ceac1f | 1655 | struct xfs_buf *bp; |
2bd0ea18 NS |
1656 | int buf_dirty; |
1657 | xfs_dahash_t current_hashval; | |
1658 | xfs_dablk_t da_bno; | |
1659 | xfs_dahash_t greatest_hashval; | |
1660 | xfs_ino_t ino; | |
1661 | xfs_dir2_leaf_t *leaf; | |
1662 | int nex; | |
1663 | xfs_dablk_t prev_bno; | |
1e77098c | 1664 | bmap_ext_t lbmp; |
658ac3e3 | 1665 | struct xfs_dir3_icleaf_hdr leafhdr; |
2bd0ea18 NS |
1666 | |
1667 | da_bno = da_cursor->level[0].bno; | |
1668 | ino = da_cursor->ino; | |
1669 | prev_bno = 0; | |
1670 | bmp = NULL; | |
1671 | current_hashval = 0; | |
9234d416 | 1672 | greatest_hashval = 0; |
2bd0ea18 NS |
1673 | buf_dirty = 0; |
1674 | ||
1675 | do { | |
ff105f75 DC |
1676 | nex = blkmap_getn(da_cursor->blkmap, da_bno, |
1677 | mp->m_dir_geo->fsbcount, &bmp, &lbmp); | |
2bd0ea18 NS |
1678 | /* |
1679 | * Directory code uses 0 as the NULL block pointer since 0 | |
1680 | * is the root block and no directory block pointer can point | |
1681 | * to the root block of the btree. | |
1682 | */ | |
1683 | ASSERT(da_bno != 0); | |
1684 | ||
1685 | if (nex == 0) { | |
5d1b7f0f CH |
1686 | do_warn( |
1687 | _("can't map block %u for directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
1688 | da_bno, ino); |
1689 | goto error_out; | |
1690 | } | |
75c8b434 | 1691 | bp = da_read_buf(mp, nex, bmp, &xfs_dir3_leafn_buf_ops); |
1e77098c MV |
1692 | if (bmp != &lbmp) |
1693 | free(bmp); | |
2bd0ea18 NS |
1694 | bmp = NULL; |
1695 | if (bp == NULL) { | |
5d1b7f0f CH |
1696 | do_warn( |
1697 | _("can't read file block %u for directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
1698 | da_bno, ino); |
1699 | goto error_out; | |
1700 | } | |
a2ceac1f | 1701 | leaf = bp->b_addr; |
ff105f75 | 1702 | M_DIROPS(mp)->leaf_hdr_from_disk(&leafhdr, leaf); |
2bd0ea18 NS |
1703 | /* |
1704 | * Check magic number for leaf directory btree block. | |
1705 | */ | |
658ac3e3 DC |
1706 | if (!(leafhdr.magic == XFS_DIR2_LEAFN_MAGIC || |
1707 | leafhdr.magic == XFS_DIR3_LEAFN_MAGIC)) { | |
5d1b7f0f CH |
1708 | do_warn( |
1709 | _("bad directory leaf magic # %#x for directory inode %" PRIu64 " block %u\n"), | |
658ac3e3 | 1710 | leafhdr.magic, ino, da_bno); |
a2ceac1f | 1711 | libxfs_putbuf(bp); |
2bd0ea18 NS |
1712 | goto error_out; |
1713 | } | |
1714 | buf_dirty = 0; | |
1715 | /* | |
1716 | * For each block, process the block, verify its path, | |
1717 | * then get next block. Update cursor values along the way. | |
1718 | */ | |
1719 | if (process_leaf_block_dir2(mp, leaf, da_bno, ino, | |
1720 | current_hashval, &greatest_hashval)) { | |
a2ceac1f | 1721 | libxfs_putbuf(bp); |
2bd0ea18 NS |
1722 | goto error_out; |
1723 | } | |
1724 | /* | |
1725 | * Index can be set to hdr.count so match the indices of the | |
1726 | * interior blocks -- which at the end of the block will point | |
1727 | * to 1 after the final real entry in the block. | |
1728 | */ | |
1729 | da_cursor->level[0].hashval = greatest_hashval; | |
1730 | da_cursor->level[0].bp = bp; | |
1731 | da_cursor->level[0].bno = da_bno; | |
658ac3e3 | 1732 | da_cursor->level[0].index = leafhdr.count; |
2bd0ea18 NS |
1733 | da_cursor->level[0].dirty = buf_dirty; |
1734 | ||
658ac3e3 | 1735 | if (leafhdr.back != prev_bno) { |
5d1b7f0f CH |
1736 | do_warn( |
1737 | _("bad sibling back pointer for block %u in directory inode %" PRIu64 "\n"), | |
2bd0ea18 | 1738 | da_bno, ino); |
a2ceac1f | 1739 | libxfs_putbuf(bp); |
2bd0ea18 NS |
1740 | goto error_out; |
1741 | } | |
1742 | prev_bno = da_bno; | |
658ac3e3 | 1743 | da_bno = leafhdr.forw; |
2bd0ea18 NS |
1744 | if (da_bno != 0) { |
1745 | if (verify_dir2_path(mp, da_cursor, 0)) { | |
a2ceac1f | 1746 | libxfs_putbuf(bp); |
2bd0ea18 NS |
1747 | goto error_out; |
1748 | } | |
1749 | } | |
1750 | current_hashval = greatest_hashval; | |
575010d5 JK |
1751 | /* |
1752 | * If block looks ok but CRC didn't match, make sure to | |
1753 | * recompute it. | |
1754 | */ | |
12b53197 | 1755 | if (!no_modify && bp->b_error == -EFSBADCRC) |
575010d5 | 1756 | buf_dirty = 1; |
27527004 | 1757 | ASSERT(buf_dirty == 0 || (buf_dirty && !no_modify)); |
2bd0ea18 NS |
1758 | if (buf_dirty && !no_modify) { |
1759 | *repair = 1; | |
a2ceac1f | 1760 | libxfs_writebuf(bp, 0); |
2bd0ea18 | 1761 | } else |
a2ceac1f | 1762 | libxfs_putbuf(bp); |
2bd0ea18 NS |
1763 | } while (da_bno != 0); |
1764 | if (verify_final_dir2_path(mp, da_cursor, 0)) { | |
1765 | /* | |
1766 | * Verify the final path up (right-hand-side) if still ok. | |
1767 | */ | |
5d1b7f0f | 1768 | do_warn(_("bad hash path in directory %" PRIu64 "\n"), ino); |
2bd0ea18 NS |
1769 | goto error_out; |
1770 | } | |
1771 | /* | |
1772 | * Redundant but just for testing. | |
1773 | */ | |
1774 | release_dir2_cursor(mp, da_cursor, 0); | |
1775 | return 0; | |
1776 | ||
1777 | error_out: | |
1778 | /* | |
1779 | * Release all buffers holding interior btree blocks. | |
1780 | */ | |
1781 | err_release_dir2_cursor(mp, da_cursor, 0); | |
1e77098c | 1782 | if (bmp && (bmp != &lbmp)) |
2bd0ea18 NS |
1783 | free(bmp); |
1784 | return 1; | |
1785 | } | |
1786 | ||
1787 | /* | |
1788 | * Return 1 if the directory's leaf/node space is corrupted and | |
1789 | * needs to be rebuilt, 0 if it's ok. | |
1790 | */ | |
1791 | static int | |
1792 | process_node_dir2( | |
1793 | xfs_mount_t *mp, | |
1794 | xfs_ino_t ino, | |
1795 | xfs_dinode_t *dip, | |
1796 | blkmap_t *blkmap, | |
1797 | int *repair) | |
1798 | { | |
1799 | xfs_dablk_t bno; | |
1800 | dir2_bt_cursor_t da_cursor; | |
1801 | ||
1802 | /* | |
1803 | * Try again -- traverse down left-side of tree until we hit the | |
1804 | * left-most leaf block setting up the btree cursor along the way. | |
1805 | * Then walk the leaf blocks left-to-right, calling a parent | |
1806 | * verification routine each time we traverse a block. | |
1807 | */ | |
dab9b8d6 | 1808 | memset(&da_cursor, 0, sizeof(da_cursor)); |
2bd0ea18 NS |
1809 | da_cursor.ino = ino; |
1810 | da_cursor.dip = dip; | |
1811 | da_cursor.blkmap = blkmap; | |
1812 | ||
1813 | /* | |
1814 | * Now process interior node. | |
1815 | */ | |
1816 | if (traverse_int_dir2block(mp, &da_cursor, &bno) == 0) | |
1817 | return 1; | |
1818 | ||
1819 | /* | |
1820 | * Skip directories with a root marked XFS_DIR2_LEAFN_MAGIC | |
1821 | */ | |
1822 | if (bno == 0) { | |
1823 | release_dir2_cursor(mp, &da_cursor, 0); | |
1824 | return 0; | |
1825 | } else { | |
1826 | /* | |
1827 | * Now pass cursor and bno into leaf-block processing routine. | |
dfc130f3 | 1828 | * The leaf dir level routine checks the interior paths up to |
2bd0ea18 NS |
1829 | * the root including the final right-most path. |
1830 | */ | |
1831 | return process_leaf_level_dir2(mp, &da_cursor, repair); | |
1832 | } | |
1833 | } | |
1834 | ||
1835 | /* | |
1836 | * Process leaf and node directories. | |
1837 | * Process the data blocks then, if it's a node directory, check | |
1838 | * the consistency of those blocks. | |
1839 | */ | |
1840 | static int | |
1841 | process_leaf_node_dir2( | |
1842 | xfs_mount_t *mp, | |
1843 | xfs_ino_t ino, | |
1844 | xfs_dinode_t *dip, | |
1845 | int ino_discovery, | |
1846 | char *dirname, /* directory pathname */ | |
1847 | xfs_ino_t *parent, /* out - NULLFSINO if entry not exist */ | |
1848 | blkmap_t *blkmap, | |
1849 | int *dot, /* out - 1 if there is a dot, else 0 */ | |
1850 | int *dotdot, /* out - 1 if there's a dotdot, else 0 */ | |
1851 | int *repair, /* out - 1 if something was fixed */ | |
1852 | int isnode) /* node directory not leaf */ | |
1853 | { | |
1854 | bmap_ext_t *bmp; | |
a2ceac1f | 1855 | struct xfs_buf *bp; |
a354abc8 | 1856 | struct xfs_dir2_data_hdr *data; |
5a35bf2c | 1857 | xfs_fileoff_t dbno; |
2bd0ea18 NS |
1858 | int good; |
1859 | int i; | |
5a35bf2c | 1860 | xfs_fileoff_t ndbno; |
2bd0ea18 NS |
1861 | int nex; |
1862 | int t; | |
1e77098c | 1863 | bmap_ext_t lbmp; |
a2ceac1f | 1864 | int dirty = 0; |
2bd0ea18 NS |
1865 | |
1866 | *repair = *dot = *dotdot = good = 0; | |
1867 | *parent = NULLFSINO; | |
5a35bf2c | 1868 | ndbno = NULLFILEOFF; |
ff105f75 DC |
1869 | while ((dbno = blkmap_next_off(blkmap, ndbno, &t)) < mp->m_dir_geo->leafblk) { |
1870 | nex = blkmap_getn(blkmap, dbno, mp->m_dir_geo->fsbcount, &bmp, &lbmp); | |
9fe1cc86 ES |
1871 | /* Advance through map to last dfs block in this dir block */ |
1872 | ndbno = dbno; | |
ff105f75 | 1873 | while (ndbno < dbno + mp->m_dir_geo->fsbcount - 1) { |
9fe1cc86 ES |
1874 | ndbno = blkmap_next_off(blkmap, ndbno, &t); |
1875 | } | |
2bd0ea18 | 1876 | if (nex == 0) { |
5d1b7f0f CH |
1877 | do_warn( |
1878 | _("block %" PRIu64 " for directory inode %" PRIu64 " is missing\n"), | |
2bd0ea18 NS |
1879 | dbno, ino); |
1880 | continue; | |
1881 | } | |
75c8b434 | 1882 | bp = da_read_buf(mp, nex, bmp, &xfs_dir3_data_buf_ops); |
1e77098c MV |
1883 | if (bmp != &lbmp) |
1884 | free(bmp); | |
2bd0ea18 | 1885 | if (bp == NULL) { |
5d1b7f0f CH |
1886 | do_warn( |
1887 | _("can't read block %" PRIu64 " for directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
1888 | dbno, ino); |
1889 | continue; | |
1890 | } | |
a2ceac1f | 1891 | data = bp->b_addr; |
a354abc8 DC |
1892 | if (!(be32_to_cpu(data->magic) == XFS_DIR2_DATA_MAGIC || |
1893 | be32_to_cpu(data->magic) == XFS_DIR3_DATA_MAGIC)) | |
5d1b7f0f CH |
1894 | do_warn( |
1895 | _("bad directory block magic # %#x in block %" PRIu64 " for directory inode %" PRIu64 "\n"), | |
a354abc8 | 1896 | be32_to_cpu(data->magic), dbno, ino); |
2bd0ea18 NS |
1897 | i = process_dir2_data(mp, ino, dip, ino_discovery, dirname, |
1898 | parent, bp, dot, dotdot, (xfs_dablk_t)dbno, | |
ff105f75 | 1899 | (char *)data + mp->m_dir_geo->blksize, &dirty); |
575010d5 | 1900 | if (i == 0) { |
2bd0ea18 | 1901 | good++; |
575010d5 | 1902 | /* Maybe just CRC is wrong. Make sure we correct it. */ |
12b53197 | 1903 | if (bp->b_error == -EFSBADCRC) |
575010d5 JK |
1904 | dirty = 1; |
1905 | } | |
a2ceac1f | 1906 | if (dirty && !no_modify) { |
2bd0ea18 | 1907 | *repair = 1; |
a2ceac1f | 1908 | libxfs_writebuf(bp, 0); |
2bd0ea18 | 1909 | } else |
a2ceac1f | 1910 | libxfs_putbuf(bp); |
2bd0ea18 NS |
1911 | } |
1912 | if (good == 0) | |
1913 | return 1; | |
1914 | if (!isnode) | |
1915 | return 0; | |
1916 | if (dir2_is_badino(ino)) | |
1917 | return 0; | |
1918 | ||
1919 | if (process_node_dir2(mp, ino, dip, blkmap, repair)) | |
1920 | dir2_add_badlist(ino); | |
1921 | return 0; | |
1922 | ||
1923 | } | |
1924 | ||
1925 | /* | |
1926 | * Returns 1 if things are bad (directory needs to be junked) | |
1927 | * and 0 if things are ok. If ino_discovery is 1, add unknown | |
1928 | * inodes to uncertain inode list. | |
1929 | */ | |
1930 | int | |
1931 | process_dir2( | |
1932 | xfs_mount_t *mp, | |
1933 | xfs_ino_t ino, | |
1934 | xfs_dinode_t *dip, | |
1935 | int ino_discovery, | |
1936 | int *dino_dirty, | |
1937 | char *dirname, | |
1938 | xfs_ino_t *parent, | |
1939 | blkmap_t *blkmap) | |
1940 | { | |
1941 | int dot; | |
1942 | int dotdot; | |
5a35bf2c | 1943 | xfs_fileoff_t last; |
2bd0ea18 NS |
1944 | int repair; |
1945 | int res; | |
1946 | ||
1947 | *parent = NULLFSINO; | |
1948 | dot = dotdot = 0; | |
1949 | last = 0; | |
1950 | ||
1951 | /* | |
1952 | * branch off depending on the type of inode. This routine | |
1953 | * is only called ONCE so all the subordinate routines will | |
1954 | * fix '.' and junk '..' if they're bogus. | |
1955 | */ | |
1956 | if (blkmap) | |
1957 | last = blkmap_last_off(blkmap); | |
56b2de80 DC |
1958 | if (be64_to_cpu(dip->di_size) <= XFS_DFORK_DSIZE(dip, mp) && |
1959 | dip->di_format == XFS_DINODE_FMT_LOCAL) { | |
2bd0ea18 NS |
1960 | dot = dotdot = 1; |
1961 | res = process_sf_dir2(mp, ino, dip, ino_discovery, dino_dirty, | |
1962 | dirname, parent, &repair); | |
ff105f75 | 1963 | } else if (last == mp->m_dir_geo->fsbcount && |
56b2de80 DC |
1964 | (dip->di_format == XFS_DINODE_FMT_EXTENTS || |
1965 | dip->di_format == XFS_DINODE_FMT_BTREE)) { | |
2bd0ea18 NS |
1966 | res = process_block_dir2(mp, ino, dip, ino_discovery, |
1967 | dino_dirty, dirname, parent, blkmap, &dot, &dotdot, | |
1968 | &repair); | |
ff105f75 | 1969 | } else if (last >= mp->m_dir_geo->leafblk + mp->m_dir_geo->fsbcount && |
56b2de80 DC |
1970 | (dip->di_format == XFS_DINODE_FMT_EXTENTS || |
1971 | dip->di_format == XFS_DINODE_FMT_BTREE)) { | |
2bd0ea18 NS |
1972 | res = process_leaf_node_dir2(mp, ino, dip, ino_discovery, |
1973 | dirname, parent, blkmap, &dot, &dotdot, &repair, | |
ff105f75 | 1974 | last > mp->m_dir_geo->leafblk + mp->m_dir_geo->fsbcount); |
2bd0ea18 | 1975 | } else { |
5d1b7f0f | 1976 | do_warn(_("bad size/format for directory %" PRIu64 "\n"), ino); |
2bd0ea18 NS |
1977 | return 1; |
1978 | } | |
1979 | /* | |
1980 | * bad . entries in all directories will be fixed up in phase 6 | |
1981 | */ | |
1982 | if (dot == 0) { | |
5d1b7f0f | 1983 | do_warn(_("no . entry for directory %" PRIu64 "\n"), ino); |
2bd0ea18 NS |
1984 | } |
1985 | ||
1986 | /* | |
1987 | * shortform dirs always have a .. entry. .. for all longform | |
1988 | * directories will get fixed in phase 6. .. for other shortform | |
1989 | * dirs also get fixed there. .. for a shortform root was | |
1990 | * fixed in place since we know what it should be | |
1991 | */ | |
1992 | if (dotdot == 0 && ino != mp->m_sb.sb_rootino) { | |
5d1b7f0f | 1993 | do_warn(_("no .. entry for directory %" PRIu64 "\n"), ino); |
2bd0ea18 | 1994 | } else if (dotdot == 0 && ino == mp->m_sb.sb_rootino) { |
5d1b7f0f | 1995 | do_warn(_("no .. entry for root directory %" PRIu64 "\n"), ino); |
2bd0ea18 NS |
1996 | need_root_dotdot = 1; |
1997 | } | |
dfc130f3 | 1998 | |
27527004 NS |
1999 | ASSERT((ino != mp->m_sb.sb_rootino && ino != *parent) || |
2000 | (ino == mp->m_sb.sb_rootino && | |
2001 | (ino == *parent || need_root_dotdot == 1))); | |
2bd0ea18 NS |
2002 | |
2003 | return res; | |
2004 | } |