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