]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2bd0ea18 | 2 | /* |
da23017d NS |
3 | * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. |
4 | * All Rights Reserved. | |
2bd0ea18 NS |
5 | */ |
6 | ||
6b803e5a | 7 | #include "libxfs.h" |
2bd0ea18 NS |
8 | #include "avl.h" |
9 | #include "globals.h" | |
10 | #include "incore.h" | |
11 | #include "err_protos.h" | |
12 | #include "dinode.h" | |
2bd0ea18 NS |
13 | #include "dir2.h" |
14 | #include "bmap.h" | |
360f4a2e | 15 | #include "da_util.h" |
cb5b3ef4 | 16 | #include "prefetch.h" |
2556c98b | 17 | #include "progress.h" |
2bd0ea18 | 18 | |
2bd0ea18 NS |
19 | /* |
20 | * Known bad inode list. These are seen when the leaf and node | |
21 | * block linkages are incorrect. | |
22 | */ | |
fdfd70e7 | 23 | struct dir2_bad { |
2bd0ea18 NS |
24 | xfs_ino_t ino; |
25 | struct dir2_bad *next; | |
fdfd70e7 | 26 | }; |
2bd0ea18 | 27 | |
fdfd70e7 DC |
28 | static struct dir2_bad *dir2_bad_list; |
29 | pthread_mutex_t dir2_bad_list_lock = PTHREAD_MUTEX_INITIALIZER; | |
8b8a6b02 CH |
30 | |
31 | static void | |
2bd0ea18 NS |
32 | dir2_add_badlist( |
33 | xfs_ino_t ino) | |
34 | { | |
fdfd70e7 | 35 | struct dir2_bad *l; |
2bd0ea18 | 36 | |
fdfd70e7 DC |
37 | l = malloc(sizeof(*l)); |
38 | if (!l) { | |
507f4e33 | 39 | do_error( |
5d1b7f0f | 40 | _("malloc failed (%zu bytes) dir2_add_badlist:ino %" PRIu64 "\n"), |
fdfd70e7 | 41 | sizeof(*l), ino); |
2bd0ea18 NS |
42 | exit(1); |
43 | } | |
fdfd70e7 | 44 | pthread_mutex_lock(&dir2_bad_list_lock); |
2bd0ea18 NS |
45 | l->next = dir2_bad_list; |
46 | dir2_bad_list = l; | |
47 | l->ino = ino; | |
fdfd70e7 | 48 | pthread_mutex_unlock(&dir2_bad_list_lock); |
2bd0ea18 NS |
49 | } |
50 | ||
fdfd70e7 | 51 | bool |
2bd0ea18 NS |
52 | dir2_is_badino( |
53 | xfs_ino_t ino) | |
54 | { | |
fdfd70e7 DC |
55 | struct dir2_bad *l; |
56 | bool ret = false; | |
2bd0ea18 | 57 | |
fdfd70e7 DC |
58 | pthread_mutex_lock(&dir2_bad_list_lock); |
59 | for (l = dir2_bad_list; l; l = l->next) { | |
60 | if (l->ino == ino) { | |
61 | ret = true; | |
62 | break; | |
63 | } | |
64 | } | |
65 | pthread_mutex_unlock(&dir2_bad_list_lock); | |
66 | return ret; | |
2bd0ea18 NS |
67 | } |
68 | ||
2bd0ea18 NS |
69 | /* |
70 | * Fix up a shortform directory which was in long form (i8count set) | |
71 | * and is now in short form (i8count clear). | |
72 | * Return pointer to the end of the data when done. | |
73 | */ | |
74 | void | |
75 | process_sf_dir2_fixi8( | |
494434d7 | 76 | struct xfs_mount *mp, |
a354abc8 | 77 | struct xfs_dir2_sf_hdr *sfp, |
2bd0ea18 NS |
78 | xfs_dir2_sf_entry_t **next_sfep) |
79 | { | |
80 | xfs_ino_t ino; | |
a354abc8 | 81 | struct xfs_dir2_sf_hdr *newsfp; |
2bd0ea18 | 82 | xfs_dir2_sf_entry_t *newsfep; |
a354abc8 | 83 | struct xfs_dir2_sf_hdr *oldsfp; |
2bd0ea18 NS |
84 | xfs_dir2_sf_entry_t *oldsfep; |
85 | int oldsize; | |
86 | ||
87 | newsfp = sfp; | |
ee6cd73e | 88 | oldsize = (intptr_t)*next_sfep - (intptr_t)sfp; |
2bd0ea18 NS |
89 | oldsfp = malloc(oldsize); |
90 | if (oldsfp == NULL) { | |
507f4e33 | 91 | do_error(_("couldn't malloc dir2 shortform copy\n")); |
2bd0ea18 NS |
92 | exit(1); |
93 | } | |
94 | memmove(oldsfp, newsfp, oldsize); | |
a354abc8 DC |
95 | newsfp->count = oldsfp->count; |
96 | newsfp->i8count = 0; | |
c29a562f | 97 | ino = libxfs_dir2_sf_get_parent_ino(oldsfp); |
8a7190bd | 98 | libxfs_dir2_sf_put_parent_ino(newsfp, ino); |
a354abc8 DC |
99 | oldsfep = xfs_dir2_sf_firstentry(oldsfp); |
100 | newsfep = xfs_dir2_sf_firstentry(newsfp); | |
2bd0ea18 NS |
101 | while ((int)((char *)oldsfep - (char *)oldsfp) < oldsize) { |
102 | newsfep->namelen = oldsfep->namelen; | |
5e656dbb BN |
103 | xfs_dir2_sf_put_offset(newsfep, |
104 | xfs_dir2_sf_get_offset(oldsfep)); | |
2bd0ea18 | 105 | memmove(newsfep->name, oldsfep->name, newsfep->namelen); |
e96bd2d3 CH |
106 | ino = libxfs_dir2_sf_get_ino(mp, oldsfp, oldsfep); |
107 | libxfs_dir2_sf_put_ino(mp, newsfp, newsfep, ino); | |
660836c9 CH |
108 | oldsfep = libxfs_dir2_sf_nextentry(mp, oldsfp, oldsfep); |
109 | newsfep = libxfs_dir2_sf_nextentry(mp, newsfp, newsfep); | |
2bd0ea18 NS |
110 | } |
111 | *next_sfep = newsfep; | |
112 | free(oldsfp); | |
113 | } | |
114 | ||
115 | /* | |
116 | * Regenerate legal (minimal) offsets for the shortform directory. | |
117 | */ | |
118 | static void | |
119 | process_sf_dir2_fixoff( | |
584f90b0 | 120 | xfs_mount_t *mp, |
2bd0ea18 NS |
121 | xfs_dinode_t *dip) |
122 | { | |
123 | int i; | |
124 | int offset; | |
125 | xfs_dir2_sf_entry_t *sfep; | |
a354abc8 | 126 | struct xfs_dir2_sf_hdr *sfp; |
2bd0ea18 | 127 | |
a354abc8 DC |
128 | sfp = (struct xfs_dir2_sf_hdr *)XFS_DFORK_DPTR(dip); |
129 | sfep = xfs_dir2_sf_firstentry(sfp); | |
58a1d356 | 130 | offset = mp->m_dir_geo->data_first_offset; |
5e656dbb | 131 | |
a354abc8 | 132 | for (i = 0; i < sfp->count; i++) { |
5e656dbb | 133 | xfs_dir2_sf_put_offset(sfep, offset); |
271a654f | 134 | offset += libxfs_dir2_data_entsize(mp, sfep->namelen); |
660836c9 | 135 | sfep = libxfs_dir2_sf_nextentry(mp, sfp, sfep); |
2bd0ea18 NS |
136 | } |
137 | } | |
138 | ||
139 | /* | |
140 | * this routine performs inode discovery and tries to fix things | |
141 | * in place. available redundancy -- inode data size should match | |
142 | * used directory space in inode. | |
143 | * a non-zero return value means the directory is bogus and should be blasted. | |
144 | */ | |
145 | /* ARGSUSED */ | |
146 | static int | |
147 | process_sf_dir2( | |
148 | xfs_mount_t *mp, | |
149 | xfs_ino_t ino, | |
150 | xfs_dinode_t *dip, | |
151 | int ino_discovery, | |
152 | int *dino_dirty, /* out - 1 if dinode buffer dirty */ | |
153 | char *dirname, /* directory pathname */ | |
154 | xfs_ino_t *parent, /* out - NULLFSINO if entry not exist */ | |
155 | int *repair) /* out - 1 if dir was fixed up */ | |
156 | { | |
157 | int bad_offset; | |
158 | int bad_sfnamelen; | |
159 | int i; | |
160 | int i8; | |
14f8b681 | 161 | int64_t ino_dir_size; |
2bd0ea18 NS |
162 | int ino_off; |
163 | ino_tree_node_t *irec_p; | |
164 | int junkit; | |
165 | char *junkreason = NULL; | |
166 | xfs_ino_t lino; | |
167 | int max_size; | |
168 | char name[MAXNAMELEN + 1]; | |
169 | int namelen; | |
170 | xfs_dir2_sf_entry_t *next_sfep; | |
171 | int num_entries; | |
172 | int offset; | |
a354abc8 | 173 | struct xfs_dir2_sf_hdr *sfp; |
2bd0ea18 NS |
174 | xfs_dir2_sf_entry_t *sfep; |
175 | int tmp_elen; | |
176 | int tmp_len; | |
177 | xfs_dir2_sf_entry_t *tmp_sfep; | |
2bd0ea18 | 178 | |
a354abc8 | 179 | sfp = (struct xfs_dir2_sf_hdr *)XFS_DFORK_DPTR(dip); |
46eca962 | 180 | max_size = XFS_DFORK_DSIZE(dip, mp); |
a354abc8 | 181 | num_entries = sfp->count; |
56b2de80 | 182 | ino_dir_size = be64_to_cpu(dip->di_size); |
58a1d356 | 183 | offset = mp->m_dir_geo->data_first_offset; |
e55d768a | 184 | bad_offset = *repair = 0; |
2bd0ea18 NS |
185 | |
186 | ASSERT(ino_dir_size <= max_size); | |
187 | ||
e55d768a NS |
188 | /* |
189 | * Initialize i8 based on size of parent inode number. | |
190 | */ | |
8a7190bd | 191 | i8 = (libxfs_dir2_sf_get_parent_ino(sfp) > XFS_DIR2_MAX_SHORT_INUM); |
e55d768a | 192 | |
dfc130f3 | 193 | /* |
2bd0ea18 NS |
194 | * check for bad entry count |
195 | */ | |
660836c9 | 196 | if (num_entries * libxfs_dir2_sf_entsize(mp, sfp, 1) + |
5e656dbb | 197 | xfs_dir2_sf_hdr_size(0) > max_size || num_entries == 0) |
2bd0ea18 NS |
198 | num_entries = 0xFF; |
199 | ||
200 | /* | |
201 | * run through entries, stop at first bad entry, don't need | |
202 | * to check for .. since that's encoded in its own field | |
203 | */ | |
d0bbcbcb | 204 | next_sfep = xfs_dir2_sf_firstentry(sfp); |
2bd0ea18 NS |
205 | for (i = 0; |
206 | i < num_entries && ino_dir_size > (char *)next_sfep - (char *)sfp; | |
207 | i++) { | |
208 | tmp_sfep = NULL; | |
209 | sfep = next_sfep; | |
210 | junkit = 0; | |
211 | bad_sfnamelen = 0; | |
e96bd2d3 | 212 | lino = libxfs_dir2_sf_get_ino(mp, sfp, sfep); |
2bd0ea18 NS |
213 | /* |
214 | * if entry points to self, junk it since only '.' or '..' | |
215 | * should do that and shortform dirs don't contain either | |
216 | * entry. if inode number is invalid, trash entry. | |
217 | * if entry points to special inodes, trash it. | |
218 | * if inode is unknown but number is valid, | |
219 | * add it to the list of uncertain inodes. don't | |
220 | * have to worry about an entry pointing to a | |
221 | * deleted lost+found inode because the entry was | |
222 | * deleted at the same time that the inode was cleared. | |
223 | */ | |
224 | if (lino == ino) { | |
225 | junkit = 1; | |
507f4e33 | 226 | junkreason = _("current"); |
017e979e | 227 | } else if (!libxfs_verify_dir_ino(mp, lino)) { |
2bd0ea18 | 228 | junkit = 1; |
507f4e33 | 229 | junkreason = _("invalid"); |
2bd0ea18 NS |
230 | } else if (lino == mp->m_sb.sb_rbmino) { |
231 | junkit = 1; | |
507f4e33 | 232 | junkreason = _("realtime bitmap"); |
2bd0ea18 NS |
233 | } else if (lino == mp->m_sb.sb_rsumino) { |
234 | junkit = 1; | |
507f4e33 | 235 | junkreason = _("realtime summary"); |
2bd0ea18 NS |
236 | } else if (lino == mp->m_sb.sb_uquotino) { |
237 | junkit = 1; | |
507f4e33 | 238 | junkreason = _("user quota"); |
b36eef04 | 239 | } else if (lino == mp->m_sb.sb_gquotino) { |
2bd0ea18 | 240 | junkit = 1; |
507f4e33 | 241 | junkreason = _("group quota"); |
0340d706 CS |
242 | } else if (lino == mp->m_sb.sb_pquotino) { |
243 | junkit = 1; | |
244 | junkreason = _("project quota"); | |
1ae311d5 LC |
245 | } else if ((irec_p = find_inode_rec(mp, |
246 | XFS_INO_TO_AGNO(mp, lino), | |
2bd0ea18 NS |
247 | XFS_INO_TO_AGINO(mp, lino))) != NULL) { |
248 | /* | |
249 | * if inode is marked free and we're in inode | |
250 | * discovery mode, leave the entry alone for now. | |
251 | * if the inode turns out to be used, we'll figure | |
252 | * that out when we scan it. If the inode really | |
253 | * is free, we'll hit this code again in phase 4 | |
254 | * after we've finished inode discovery and blow | |
255 | * out the entry then. | |
256 | */ | |
257 | ino_off = XFS_INO_TO_AGINO(mp, lino) - | |
258 | irec_p->ino_startnum; | |
259 | ASSERT(is_inode_confirmed(irec_p, ino_off)); | |
260 | if (is_inode_free(irec_p, ino_off) && !ino_discovery) { | |
261 | junkit = 1; | |
507f4e33 | 262 | junkreason = _("free"); |
2bd0ea18 NS |
263 | } |
264 | } else if (ino_discovery) { | |
265 | /* | |
266 | * put the inode on the uncertain list. we'll | |
267 | * pull the inode off the list and check it later. | |
268 | * if the inode turns out be bogus, we'll delete | |
269 | * this entry in phase 6. | |
270 | */ | |
271 | add_inode_uncertain(mp, lino, 0); | |
272 | } else { | |
273 | /* | |
274 | * blow the entry out. we know about all | |
275 | * undiscovered entries now (past inode discovery | |
276 | * phase) so this is clearly a bogus entry. | |
277 | */ | |
278 | junkit = 1; | |
507f4e33 | 279 | junkreason = _("non-existent"); |
2bd0ea18 NS |
280 | } |
281 | namelen = sfep->namelen; | |
282 | if (junkit) | |
5d1b7f0f CH |
283 | do_warn( |
284 | _("entry \"%*.*s\" in shortform directory %" PRIu64 " references %s inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
285 | namelen, namelen, sfep->name, ino, junkreason, |
286 | lino); | |
1c934a25 ES |
287 | |
288 | /* is dir namelen 0 or does this entry extend past dir size? */ | |
289 | if (namelen == 0) { | |
290 | junkreason = _("is zero length"); | |
2bd0ea18 | 291 | bad_sfnamelen = 1; |
ee6cd73e | 292 | } else if ((intptr_t) sfep - (intptr_t) sfp + |
660836c9 | 293 | libxfs_dir2_sf_entsize(mp, sfp, sfep->namelen) |
1c934a25 ES |
294 | > ino_dir_size) { |
295 | junkreason = _("extends past end of dir"); | |
296 | bad_sfnamelen = 1; | |
297 | } | |
2bd0ea18 | 298 | |
1c934a25 | 299 | if (bad_sfnamelen) { |
38c66abc ES |
300 | do_warn( |
301 | _("entry #%d %s in shortform dir %" PRIu64), | |
302 | i, junkreason, ino); | |
303 | if (!no_modify) | |
304 | do_warn(_(", junking %d entries\n"), | |
305 | num_entries - i); | |
306 | else | |
307 | do_warn(_(", would junk %d entries\n"), | |
308 | num_entries - i); | |
1c934a25 | 309 | /* |
38c66abc ES |
310 | * don't process the rest of the directory, |
311 | * break out of processing loop | |
1c934a25 | 312 | */ |
38c66abc | 313 | break; |
2bd0ea18 NS |
314 | } |
315 | ||
316 | /* | |
317 | * check for illegal chars in name. | |
318 | * no need to check for bad length because | |
319 | * the length value is stored in a byte | |
320 | * so it can't be too big, it can only wrap | |
321 | */ | |
b8aa6f27 | 322 | if (!libxfs_dir2_namecheck(sfep->name, namelen)) { |
2bd0ea18 NS |
323 | /* |
324 | * junk entry | |
325 | */ | |
5d1b7f0f CH |
326 | do_warn( |
327 | _("entry contains illegal character in shortform dir %" PRIu64 "\n"), | |
2bd0ea18 NS |
328 | ino); |
329 | junkit = 1; | |
330 | } | |
331 | ||
5e656dbb | 332 | if (xfs_dir2_sf_get_offset(sfep) < offset) { |
5d1b7f0f CH |
333 | do_warn( |
334 | _("entry contains offset out of order in shortform dir %" PRIu64 "\n"), | |
2bd0ea18 NS |
335 | ino); |
336 | bad_offset = 1; | |
337 | } | |
5e656dbb | 338 | offset = xfs_dir2_sf_get_offset(sfep) + |
271a654f | 339 | libxfs_dir2_data_entsize(mp, namelen); |
2bd0ea18 NS |
340 | |
341 | /* | |
342 | * junk the entry by copying up the rest of the | |
343 | * fork over the current entry and decrementing | |
344 | * the entry count. if we're in no_modify mode, | |
345 | * just issue the warning instead. then continue | |
346 | * the loop with the next_sfep pointer set to the | |
347 | * correct place in the fork and other counters | |
348 | * properly set to reflect the deletion if it | |
349 | * happened. | |
350 | */ | |
351 | if (junkit) { | |
dab9b8d6 | 352 | memmove(name, sfep->name, namelen); |
2bd0ea18 NS |
353 | name[namelen] = '\0'; |
354 | ||
355 | if (!no_modify) { | |
660836c9 | 356 | tmp_elen = libxfs_dir2_sf_entsize(mp, sfp, |
a2ceac1f | 357 | sfep->namelen); |
56b2de80 | 358 | be64_add_cpu(&dip->di_size, -tmp_elen); |
2bd0ea18 NS |
359 | ino_dir_size -= tmp_elen; |
360 | ||
361 | tmp_sfep = (xfs_dir2_sf_entry_t *) | |
ee6cd73e CH |
362 | ((intptr_t) sfep + tmp_elen); |
363 | tmp_len = max_size - ((intptr_t) tmp_sfep | |
364 | - (intptr_t) sfp); | |
2bd0ea18 NS |
365 | |
366 | memmove(sfep, tmp_sfep, tmp_len); | |
367 | ||
a354abc8 | 368 | sfp->count -= 1; |
2bd0ea18 | 369 | num_entries--; |
ee6cd73e | 370 | memset((void *) ((intptr_t) sfep + tmp_len), 0, |
2bd0ea18 NS |
371 | tmp_elen); |
372 | ||
373 | /* | |
374 | * reset the tmp value to the current | |
375 | * pointer so we'll process the entry | |
376 | * we just moved up | |
377 | */ | |
378 | tmp_sfep = sfep; | |
379 | ||
380 | /* | |
381 | * WARNING: drop the index i by one | |
382 | * so it matches the decremented count | |
383 | * for accurate comparisons later | |
384 | */ | |
385 | i--; | |
386 | ||
387 | *dino_dirty = 1; | |
388 | *repair = 1; | |
389 | ||
5d1b7f0f CH |
390 | do_warn( |
391 | _("junking entry \"%s\" in directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
392 | name, ino); |
393 | } else { | |
5d1b7f0f CH |
394 | do_warn( |
395 | _("would have junked entry \"%s\" in directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
396 | name, ino); |
397 | } | |
398 | } else if (lino > XFS_DIR2_MAX_SHORT_INUM) | |
399 | i8++; | |
400 | /* | |
401 | * go onto next entry unless we've just junked an | |
402 | * entry in which the current entry pointer points | |
403 | * to an unprocessed entry. have to take into zero-len | |
404 | * entries into account in no modify mode since we | |
405 | * calculate size based on next_sfep. | |
406 | */ | |
407 | next_sfep = (tmp_sfep == NULL) | |
ee6cd73e | 408 | ? (xfs_dir2_sf_entry_t *) ((intptr_t) sfep |
a2ceac1f | 409 | + ((!bad_sfnamelen) |
660836c9 CH |
410 | ? libxfs_dir2_sf_entsize(mp, sfp, sfep->namelen) |
411 | : libxfs_dir2_sf_entsize(mp, sfp, namelen))) | |
2bd0ea18 NS |
412 | : tmp_sfep; |
413 | } | |
414 | ||
415 | /* sync up sizes and entry counts */ | |
416 | ||
a354abc8 | 417 | if (sfp->count != i) { |
2bd0ea18 | 418 | if (no_modify) { |
5d1b7f0f CH |
419 | do_warn( |
420 | _("would have corrected entry count in directory %" PRIu64 " from %d to %d\n"), | |
a354abc8 | 421 | ino, sfp->count, i); |
2bd0ea18 | 422 | } else { |
5d1b7f0f | 423 | do_warn( |
b52923f3 | 424 | _("corrected entry count in directory %" PRIu64 ", was %d, now %d\n"), |
a354abc8 DC |
425 | ino, sfp->count, i); |
426 | sfp->count = i; | |
2bd0ea18 NS |
427 | *dino_dirty = 1; |
428 | *repair = 1; | |
429 | } | |
430 | } | |
431 | ||
a354abc8 | 432 | if (sfp->i8count != i8) { |
2bd0ea18 | 433 | if (no_modify) { |
5d1b7f0f CH |
434 | do_warn( |
435 | _("would have corrected i8 count in directory %" PRIu64 " from %d to %d\n"), | |
a354abc8 | 436 | ino, sfp->i8count, i8); |
2bd0ea18 | 437 | } else { |
5d1b7f0f CH |
438 | do_warn( |
439 | _("corrected i8 count in directory %" PRIu64 ", was %d, now %d\n"), | |
a354abc8 | 440 | ino, sfp->i8count, i8); |
2bd0ea18 | 441 | if (i8 == 0) |
494434d7 | 442 | process_sf_dir2_fixi8(mp, sfp, &next_sfep); |
2bd0ea18 | 443 | else |
a354abc8 | 444 | sfp->i8count = i8; |
2bd0ea18 NS |
445 | *dino_dirty = 1; |
446 | *repair = 1; | |
447 | } | |
448 | } | |
449 | ||
5d1b7f0f | 450 | if ((intptr_t)next_sfep - (intptr_t)sfp != ino_dir_size) { |
2bd0ea18 | 451 | if (no_modify) { |
5d1b7f0f CH |
452 | do_warn( |
453 | _("would have corrected directory %" PRIu64 " size from %" PRId64 " to %" PRIdPTR "\n"), | |
454 | ino, ino_dir_size, | |
455 | (intptr_t)next_sfep - (intptr_t)sfp); | |
2bd0ea18 | 456 | } else { |
5d1b7f0f CH |
457 | do_warn( |
458 | _("corrected directory %" PRIu64 " size, was %" PRId64 ", now %" PRIdPTR "\n"), | |
459 | ino, ino_dir_size, | |
460 | (intptr_t)next_sfep - (intptr_t)sfp); | |
2bd0ea18 | 461 | |
56b2de80 | 462 | dip->di_size = cpu_to_be64( |
ee6cd73e | 463 | (intptr_t)next_sfep - (intptr_t)sfp); |
2bd0ea18 NS |
464 | *dino_dirty = 1; |
465 | *repair = 1; | |
466 | } | |
467 | } | |
a354abc8 | 468 | if (offset + (sfp->count + 2) * sizeof(xfs_dir2_leaf_entry_t) + |
ff105f75 | 469 | sizeof(xfs_dir2_block_tail_t) > mp->m_dir_geo->blksize) { |
5d1b7f0f | 470 | do_warn(_("directory %" PRIu64 " offsets too high\n"), ino); |
2bd0ea18 NS |
471 | bad_offset = 1; |
472 | } | |
473 | if (bad_offset) { | |
474 | if (no_modify) { | |
5d1b7f0f CH |
475 | do_warn( |
476 | _("would have corrected entry offsets in directory %" PRIu64 "\n"), | |
2bd0ea18 NS |
477 | ino); |
478 | } else { | |
5d1b7f0f CH |
479 | do_warn( |
480 | _("corrected entry offsets in directory %" PRIu64 "\n"), | |
2bd0ea18 | 481 | ino); |
584f90b0 | 482 | process_sf_dir2_fixoff(mp, dip); |
2bd0ea18 NS |
483 | *dino_dirty = 1; |
484 | *repair = 1; | |
485 | } | |
486 | } | |
487 | ||
488 | /* | |
489 | * check parent (..) entry | |
490 | */ | |
8a7190bd | 491 | *parent = libxfs_dir2_sf_get_parent_ino(sfp); |
1b0bfeb5 BF |
492 | /* |
493 | * If this function is called during inode discovery (phase 3), it will | |
494 | * set a bad sf dir parent pointer to the root directory. This fixes | |
495 | * the directory enough to pass the inode fork verifier in phase 6 when | |
496 | * we try to reset the parent pointer to the correct value. There is no | |
497 | * need to re-check the parent pointer during phase 4. | |
498 | */ | |
499 | if (!ino_discovery) | |
500 | return 0; | |
2bd0ea18 NS |
501 | |
502 | /* | |
503 | * if parent entry is bogus, null it out. we'll fix it later . | |
77b3425e MB |
504 | * If the validation fails for the root inode we fix it in |
505 | * the next else case. | |
2bd0ea18 | 506 | */ |
017e979e | 507 | if (!libxfs_verify_dir_ino(mp, *parent) && ino != mp->m_sb.sb_rootino) { |
5d1b7f0f CH |
508 | do_warn( |
509 | _("bogus .. inode number (%" PRIu64 ") in directory inode %" PRIu64 ", "), | |
2bd0ea18 NS |
510 | *parent, ino); |
511 | *parent = NULLFSINO; | |
512 | if (!no_modify) { | |
507f4e33 | 513 | do_warn(_("clearing inode number\n")); |
2bd0ea18 | 514 | |
993ca3ee | 515 | libxfs_dir2_sf_put_parent_ino(sfp, mp->m_sb.sb_rootino); |
2bd0ea18 NS |
516 | *dino_dirty = 1; |
517 | *repair = 1; | |
518 | } else { | |
507f4e33 | 519 | do_warn(_("would clear inode number\n")); |
2bd0ea18 NS |
520 | } |
521 | } else if (ino == mp->m_sb.sb_rootino && ino != *parent) { | |
522 | /* | |
523 | * root directories must have .. == . | |
524 | */ | |
525 | if (!no_modify) { | |
5d1b7f0f CH |
526 | do_warn( |
527 | _("corrected root directory %" PRIu64 " .. entry, was %" PRIu64 ", now %" PRIu64 "\n"), | |
2bd0ea18 NS |
528 | ino, *parent, ino); |
529 | *parent = ino; | |
8a7190bd | 530 | libxfs_dir2_sf_put_parent_ino(sfp, ino); |
2bd0ea18 NS |
531 | *dino_dirty = 1; |
532 | *repair = 1; | |
533 | } else { | |
5d1b7f0f CH |
534 | do_warn( |
535 | _("would have corrected root directory %" PRIu64 " .. entry from %" PRIu64" to %" PRIu64 "\n"), | |
2bd0ea18 NS |
536 | ino, *parent, ino); |
537 | } | |
538 | } else if (ino == *parent && ino != mp->m_sb.sb_rootino) { | |
539 | /* | |
540 | * likewise, non-root directories can't have .. pointing | |
541 | * to . | |
542 | */ | |
543 | *parent = NULLFSINO; | |
5d1b7f0f CH |
544 | do_warn( |
545 | _("bad .. entry in directory inode %" PRIu64 ", points to self, "), | |
2bd0ea18 NS |
546 | ino); |
547 | if (!no_modify) { | |
507f4e33 | 548 | do_warn(_("clearing inode number\n")); |
2bd0ea18 | 549 | |
993ca3ee | 550 | libxfs_dir2_sf_put_parent_ino(sfp, mp->m_sb.sb_rootino); |
2bd0ea18 NS |
551 | *dino_dirty = 1; |
552 | *repair = 1; | |
553 | } else { | |
507f4e33 | 554 | do_warn(_("would clear inode number\n")); |
2bd0ea18 NS |
555 | } |
556 | } | |
557 | ||
558 | return(0); | |
559 | } | |
560 | ||
561 | /* | |
562 | * Process one directory data block. | |
563 | */ | |
564 | /* ARGSUSED */ | |
565 | static int | |
566 | process_dir2_data( | |
567 | xfs_mount_t *mp, | |
568 | xfs_ino_t ino, | |
569 | xfs_dinode_t *dip, | |
570 | int ino_discovery, | |
571 | char *dirname, /* directory pathname */ | |
572 | xfs_ino_t *parent, /* out - NULLFSINO if entry not exist */ | |
a2ceac1f | 573 | struct xfs_buf *bp, |
2bd0ea18 NS |
574 | int *dot, /* out - 1 if there is a dot, else 0 */ |
575 | int *dotdot, /* out - 1 if there's a dotdot, else 0 */ | |
576 | xfs_dablk_t da_bno, | |
a2ceac1f DC |
577 | char *endptr, |
578 | int *dirty) | |
2bd0ea18 NS |
579 | { |
580 | int badbest; | |
581 | xfs_dir2_data_free_t *bf; | |
582 | int clearino; | |
583 | char *clearreason = NULL; | |
a354abc8 | 584 | struct xfs_dir2_data_hdr *d; |
2bd0ea18 NS |
585 | xfs_dir2_data_entry_t *dep; |
586 | xfs_dir2_data_free_t *dfp; | |
587 | xfs_dir2_data_unused_t *dup; | |
588 | int freeseen; | |
589 | int i; | |
590 | int ino_off; | |
591 | ino_tree_node_t *irec_p; | |
592 | int junkit; | |
593 | int lastfree; | |
594 | int nm_illegal; | |
595 | char *ptr; | |
5e656dbb | 596 | xfs_ino_t ent_ino; |
2bd0ea18 | 597 | |
a2ceac1f | 598 | d = bp->b_addr; |
04f6f354 | 599 | bf = libxfs_dir2_data_bestfree_p(mp, d); |
58a1d356 | 600 | ptr = (char *)d + mp->m_dir_geo->data_entry_offset; |
2bd0ea18 | 601 | badbest = lastfree = freeseen = 0; |
5e656dbb BN |
602 | if (be16_to_cpu(bf[0].length) == 0) { |
603 | badbest |= be16_to_cpu(bf[0].offset) != 0; | |
2bd0ea18 NS |
604 | freeseen |= 1 << 0; |
605 | } | |
5e656dbb BN |
606 | if (be16_to_cpu(bf[1].length) == 0) { |
607 | badbest |= be16_to_cpu(bf[1].offset) != 0; | |
2bd0ea18 NS |
608 | freeseen |= 1 << 1; |
609 | } | |
5e656dbb BN |
610 | if (be16_to_cpu(bf[2].length) == 0) { |
611 | badbest |= be16_to_cpu(bf[2].offset) != 0; | |
2bd0ea18 NS |
612 | freeseen |= 1 << 2; |
613 | } | |
5e656dbb BN |
614 | badbest |= be16_to_cpu(bf[0].length) < be16_to_cpu(bf[1].length); |
615 | badbest |= be16_to_cpu(bf[1].length) < be16_to_cpu(bf[2].length); | |
2bd0ea18 NS |
616 | while (ptr < endptr) { |
617 | dup = (xfs_dir2_data_unused_t *)ptr; | |
618 | /* | |
619 | * If it's unused, look for the space in the bestfree table. | |
620 | * If we find it, account for that, else make sure it doesn't | |
621 | * need to be there. | |
622 | */ | |
5e656dbb BN |
623 | if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { |
624 | if (ptr + be16_to_cpu(dup->length) > endptr || | |
625 | be16_to_cpu(dup->length) == 0 || | |
626 | (be16_to_cpu(dup->length) & (XFS_DIR2_DATA_ALIGN - 1))) | |
2bd0ea18 | 627 | break; |
5e656dbb BN |
628 | if (be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)) != |
629 | (char *)dup - (char *)d) | |
2bd0ea18 NS |
630 | break; |
631 | badbest |= lastfree != 0; | |
ff105f75 | 632 | dfp = xfs_dir2_data_freefind(d, bf, dup); |
2bd0ea18 NS |
633 | if (dfp) { |
634 | i = dfp - bf; | |
635 | badbest |= (freeseen & (1 << i)) != 0; | |
636 | freeseen |= 1 << i; | |
637 | } else | |
5e656dbb BN |
638 | badbest |= be16_to_cpu(dup->length) > |
639 | be16_to_cpu(bf[2].length); | |
640 | ptr += be16_to_cpu(dup->length); | |
2bd0ea18 NS |
641 | lastfree = 1; |
642 | continue; | |
643 | } | |
644 | dep = (xfs_dir2_data_entry_t *)ptr; | |
271a654f | 645 | if (ptr + libxfs_dir2_data_entsize(mp, dep->namelen) > endptr) |
2bd0ea18 | 646 | break; |
823711f2 | 647 | if (be16_to_cpu(*libxfs_dir2_data_entry_tag_p(mp, dep)) != |
5e656dbb | 648 | (char *)dep - (char *)d) |
2bd0ea18 | 649 | break; |
271a654f | 650 | ptr += libxfs_dir2_data_entsize(mp, dep->namelen); |
2bd0ea18 NS |
651 | lastfree = 0; |
652 | } | |
653 | /* | |
654 | * Dropped out before we processed everything, give up. | |
655 | * Phase 6 will kill this block if we don't kill the inode. | |
656 | */ | |
657 | if (ptr != endptr) { | |
5d1b7f0f | 658 | do_warn(_("corrupt block %u in directory inode %" PRIu64 "\n"), |
2bd0ea18 NS |
659 | da_bno, ino); |
660 | if (!no_modify) | |
507f4e33 | 661 | do_warn(_("\twill junk block\n")); |
2bd0ea18 | 662 | else |
507f4e33 | 663 | do_warn(_("\twould junk block\n")); |
2bd0ea18 NS |
664 | return 1; |
665 | } | |
58a1d356 | 666 | ptr = (char *)d + mp->m_dir_geo->data_entry_offset; |
2bd0ea18 NS |
667 | /* |
668 | * Process the entries now. | |
669 | */ | |
670 | while (ptr < endptr) { | |
671 | dup = (xfs_dir2_data_unused_t *)ptr; | |
5e656dbb BN |
672 | if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { |
673 | ptr += be16_to_cpu(dup->length); | |
2bd0ea18 NS |
674 | continue; |
675 | } | |
676 | dep = (xfs_dir2_data_entry_t *)ptr; | |
5e656dbb BN |
677 | ent_ino = be64_to_cpu(dep->inumber); |
678 | clearino = 1; | |
679 | clearreason = NULL; | |
2bd0ea18 NS |
680 | /* |
681 | * We may have to blow out an entry because of bad inode | |
682 | * numbers. Do NOT touch the name until after we've computed | |
683 | * the hashvalue and done a namecheck() on the name. | |
5e656dbb | 684 | * |
4c0a98ae | 685 | * Conditions must either set clearino to zero or set |
5e656dbb | 686 | * clearreason why it's being cleared. |
2bd0ea18 | 687 | */ |
871fbfd5 | 688 | if (!ino_discovery && dep->name[0] == '/') { |
2bd0ea18 NS |
689 | /* |
690 | * Don't do a damned thing. We already found this | |
691 | * (or did it ourselves) during phase 3. | |
692 | */ | |
693 | clearino = 0; | |
017e979e | 694 | } else if (!libxfs_verify_dir_ino(mp, ent_ino)) { |
2bd0ea18 NS |
695 | /* |
696 | * Bad inode number. Clear the inode number and the | |
697 | * entry will get removed later. We don't trash the | |
698 | * directory since it's still structurally intact. | |
699 | */ | |
507f4e33 | 700 | clearreason = _("invalid"); |
5e656dbb | 701 | } else if (ent_ino == mp->m_sb.sb_rbmino) { |
507f4e33 | 702 | clearreason = _("realtime bitmap"); |
5e656dbb | 703 | } else if (ent_ino == mp->m_sb.sb_rsumino) { |
507f4e33 | 704 | clearreason = _("realtime summary"); |
5e656dbb | 705 | } else if (ent_ino == mp->m_sb.sb_uquotino) { |
507f4e33 | 706 | clearreason = _("user quota"); |
5e656dbb | 707 | } else if (ent_ino == mp->m_sb.sb_gquotino) { |
507f4e33 | 708 | clearreason = _("group quota"); |
0340d706 CS |
709 | } else if (ent_ino == mp->m_sb.sb_pquotino) { |
710 | clearreason = _("project quota"); | |
2bd0ea18 | 711 | } else { |
1ae311d5 LC |
712 | irec_p = find_inode_rec(mp, |
713 | XFS_INO_TO_AGNO(mp, ent_ino), | |
5e656dbb BN |
714 | XFS_INO_TO_AGINO(mp, ent_ino)); |
715 | if (irec_p == NULL) { | |
716 | if (ino_discovery) { | |
717 | add_inode_uncertain(mp, ent_ino, 0); | |
718 | clearino = 0; | |
4c0a98ae | 719 | } else |
5e656dbb BN |
720 | clearreason = _("non-existent"); |
721 | } else { | |
722 | /* | |
723 | * Inode recs should have only confirmed | |
724 | * inodes in them. | |
725 | */ | |
726 | ino_off = XFS_INO_TO_AGINO(mp, ent_ino) | |
727 | - irec_p->ino_startnum; | |
728 | ASSERT(is_inode_confirmed(irec_p, ino_off)); | |
729 | /* | |
730 | * If inode is marked free and we're in inode | |
731 | * discovery mode, leave the entry alone for | |
732 | * now. If the inode turns out to be used, | |
733 | * we'll figure that out when we scan it. | |
734 | * If the inode really is free, we'll hit this | |
735 | * code again in phase 4 after we've finished | |
736 | * inode discovery and blow out the entry then. | |
737 | */ | |
738 | if (!ino_discovery && is_inode_free(irec_p, | |
739 | ino_off)) | |
740 | clearreason = _("free"); | |
741 | else | |
742 | clearino = 0; | |
743 | } | |
2bd0ea18 | 744 | } |
5e656dbb BN |
745 | ASSERT((clearino == 0 && clearreason == NULL) || |
746 | (clearino != 0 && clearreason != NULL)); | |
2bd0ea18 | 747 | if (clearino) |
5d1b7f0f CH |
748 | do_warn( |
749 | _("entry \"%*.*s\" at block %d offset %" PRIdPTR " in directory inode %" PRIu64 | |
750 | " references %s inode %" PRIu64 "\n"), | |
2bd0ea18 | 751 | dep->namelen, dep->namelen, dep->name, |
5d1b7f0f | 752 | da_bno, (intptr_t)ptr - (intptr_t)d, ino, |
5e656dbb | 753 | clearreason, ent_ino); |
b8ec08a1 ES |
754 | |
755 | /* | |
756 | * We have a special dot & dotdot fixer-upper below which can | |
757 | * sort out the proper inode number, so don't clear it. | |
758 | */ | |
759 | if ((dep->namelen == 1 && dep->name[0] == '.') || | |
760 | (dep->namelen == 2 && | |
761 | dep->name[0] == '.' && dep->name[1] == '.')) { | |
762 | clearino = 0; | |
763 | clearreason = NULL; | |
764 | } | |
765 | ||
2bd0ea18 NS |
766 | /* |
767 | * If the name length is 0 (illegal) make it 1 and blast | |
768 | * the entry. | |
769 | */ | |
770 | if (dep->namelen == 0) { | |
5d1b7f0f CH |
771 | do_warn( |
772 | _("entry at block %u offset %" PRIdPTR " in directory inode %" PRIu64 | |
773 | "has 0 namelength\n"), | |
774 | da_bno, (intptr_t)ptr - (intptr_t)d, ino); | |
2bd0ea18 NS |
775 | if (!no_modify) |
776 | dep->namelen = 1; | |
777 | clearino = 1; | |
778 | } | |
779 | /* | |
780 | * If needed to clear the inode number, do it now. | |
781 | */ | |
782 | if (clearino) { | |
783 | if (!no_modify) { | |
5d1b7f0f CH |
784 | do_warn( |
785 | _("\tclearing inode number in entry at offset %" PRIdPTR "...\n"), | |
786 | (intptr_t)ptr - (intptr_t)d); | |
871fbfd5 | 787 | dep->name[0] = '/'; |
a2ceac1f | 788 | *dirty = 1; |
2bd0ea18 | 789 | } else { |
5d1b7f0f CH |
790 | do_warn( |
791 | _("\twould clear inode number in entry at offset %" PRIdPTR "...\n"), | |
792 | (intptr_t)ptr - (intptr_t)d); | |
2bd0ea18 NS |
793 | } |
794 | } | |
795 | /* | |
796 | * Only complain about illegal names in phase 3 (when inode | |
797 | * discovery is turned on). Otherwise, we'd complain a lot | |
798 | * during phase 4. | |
799 | */ | |
871fbfd5 | 800 | junkit = dep->name[0] == '/'; |
b8aa6f27 | 801 | nm_illegal = !libxfs_dir2_namecheck(dep->name, dep->namelen); |
2bd0ea18 | 802 | if (ino_discovery && nm_illegal) { |
5d1b7f0f CH |
803 | do_warn( |
804 | _("entry at block %u offset %" PRIdPTR " in directory inode %" PRIu64 " has illegal name \"%*.*s\": "), | |
805 | da_bno, (intptr_t)ptr - (intptr_t)d, ino, | |
2bd0ea18 NS |
806 | dep->namelen, dep->namelen, dep->name); |
807 | junkit = 1; | |
808 | } | |
871fbfd5 | 809 | |
2bd0ea18 | 810 | /* |
871fbfd5 | 811 | * Ensure we write back bad entries for later processing |
2bd0ea18 | 812 | */ |
871fbfd5 | 813 | if (!no_modify && dep->name[0] == '/') { |
a2ceac1f | 814 | *dirty = 1; |
2bd0ea18 NS |
815 | junkit = 0; |
816 | } | |
871fbfd5 | 817 | |
2bd0ea18 NS |
818 | /* |
819 | * Special .. entry processing. | |
820 | */ | |
821 | if (dep->namelen == 2 && | |
822 | dep->name[0] == '.' && dep->name[1] == '.') { | |
823 | if (!*dotdot) { | |
824 | (*dotdot)++; | |
5e656dbb | 825 | *parent = ent_ino; |
2bd0ea18 NS |
826 | /* |
827 | * What if .. == .? Legal only in the root | |
828 | * inode. Blow out entry and set parent to | |
829 | * NULLFSINO otherwise. | |
830 | */ | |
5e656dbb BN |
831 | if (ino == ent_ino && |
832 | ino != mp->m_sb.sb_rootino) { | |
2bd0ea18 | 833 | *parent = NULLFSINO; |
5d1b7f0f CH |
834 | do_warn( |
835 | _("bad .. entry in directory inode %" PRIu64 ", points to self: "), | |
2bd0ea18 NS |
836 | ino); |
837 | junkit = 1; | |
838 | } | |
839 | /* | |
840 | * We have to make sure that . == .. in the | |
841 | * root inode. | |
842 | */ | |
5e656dbb BN |
843 | else if (ino != ent_ino && |
844 | ino == mp->m_sb.sb_rootino) { | |
5d1b7f0f CH |
845 | do_warn( |
846 | _("bad .. entry in root directory inode %" PRIu64 ", was %" PRIu64 ": "), | |
5e656dbb | 847 | ino, ent_ino); |
2bd0ea18 | 848 | if (!no_modify) { |
507f4e33 | 849 | do_warn(_("correcting\n")); |
5e656dbb | 850 | dep->inumber = cpu_to_be64(ino); |
a2ceac1f | 851 | *dirty = 1; |
2bd0ea18 | 852 | } else { |
507f4e33 | 853 | do_warn(_("would correct\n")); |
2bd0ea18 | 854 | } |
aedcecb5 | 855 | *parent = ino; |
2bd0ea18 | 856 | } |
75547072 DW |
857 | /* |
858 | * Make sure our parent directory doesn't point | |
859 | * off into space. | |
860 | */ | |
861 | if (!junkit && | |
862 | *parent != NULLFSINO && | |
863 | !libxfs_verify_ino(mp, *parent)) { | |
864 | do_warn( | |
865 | _("bad .. entry in directory inode %" PRIu64 ", was %" PRIu64 ": "), | |
866 | ino, *parent); | |
867 | if (!no_modify) { | |
868 | do_warn(_("correcting\n")); | |
869 | } else { | |
870 | do_warn(_("would correct\n")); | |
871 | } | |
872 | *parent = NULLFSINO; | |
873 | } | |
2bd0ea18 NS |
874 | } |
875 | /* | |
876 | * Can't fix the directory unless we know which .. | |
877 | * entry is the right one. Both have valid inode | |
878 | * numbers or we wouldn't be here. So since both | |
879 | * seem equally valid, trash this one. | |
880 | */ | |
881 | else { | |
5d1b7f0f CH |
882 | do_warn( |
883 | _("multiple .. entries in directory inode %" PRIu64 ": "), | |
2bd0ea18 NS |
884 | ino); |
885 | junkit = 1; | |
886 | } | |
887 | } | |
888 | /* | |
889 | * Special . entry processing. | |
890 | */ | |
891 | else if (dep->namelen == 1 && dep->name[0] == '.') { | |
892 | if (!*dot) { | |
893 | (*dot)++; | |
5e656dbb | 894 | if (ent_ino != ino) { |
5d1b7f0f CH |
895 | do_warn( |
896 | _("bad . entry in directory inode %" PRIu64 ", was %" PRIu64 ": "), | |
5e656dbb | 897 | ino, ent_ino); |
2bd0ea18 | 898 | if (!no_modify) { |
507f4e33 | 899 | do_warn(_("correcting\n")); |
5e656dbb | 900 | dep->inumber = cpu_to_be64(ino); |
a2ceac1f | 901 | *dirty = 1; |
2bd0ea18 | 902 | } else { |
507f4e33 | 903 | do_warn(_("would correct\n")); |
2bd0ea18 NS |
904 | } |
905 | } | |
906 | } else { | |
5d1b7f0f CH |
907 | do_warn( |
908 | _("multiple . entries in directory inode %" PRIu64 ": "), | |
2bd0ea18 NS |
909 | ino); |
910 | junkit = 1; | |
911 | } | |
912 | } | |
913 | /* | |
914 | * All other entries -- make sure only . references self. | |
915 | */ | |
5e656dbb | 916 | else if (ent_ino == ino) { |
5d1b7f0f CH |
917 | do_warn( |
918 | _("entry \"%*.*s\" in directory inode %" PRIu64 " points to self: "), | |
2bd0ea18 NS |
919 | dep->namelen, dep->namelen, dep->name, ino); |
920 | junkit = 1; | |
921 | } | |
922 | /* | |
923 | * Clear junked entries. | |
924 | */ | |
925 | if (junkit) { | |
926 | if (!no_modify) { | |
927 | dep->name[0] = '/'; | |
a2ceac1f | 928 | *dirty = 1; |
507f4e33 | 929 | do_warn(_("clearing entry\n")); |
2bd0ea18 | 930 | } else { |
507f4e33 | 931 | do_warn(_("would clear entry\n")); |
2bd0ea18 NS |
932 | } |
933 | } | |
934 | /* | |
935 | * Advance to the next entry. | |
936 | */ | |
271a654f | 937 | ptr += libxfs_dir2_data_entsize(mp, dep->namelen); |
2bd0ea18 NS |
938 | } |
939 | /* | |
940 | * Check the bestfree table. | |
941 | */ | |
942 | if (freeseen != 7 || badbest) { | |
5d1b7f0f CH |
943 | do_warn( |
944 | _("bad bestfree table in block %u in directory inode %" PRIu64 ": "), | |
945 | da_bno, ino); | |
2bd0ea18 | 946 | if (!no_modify) { |
507f4e33 | 947 | do_warn(_("repairing table\n")); |
d85595d0 | 948 | libxfs_dir2_data_freescan(mp, d, &i); |
a2ceac1f | 949 | *dirty = 1; |
2bd0ea18 | 950 | } else { |
507f4e33 | 951 | do_warn(_("would repair table\n")); |
2bd0ea18 NS |
952 | } |
953 | } | |
954 | return 0; | |
955 | } | |
956 | ||
957 | /* | |
958 | * Process a block-format directory. | |
959 | */ | |
960 | /* ARGSUSED */ | |
961 | static int | |
962 | process_block_dir2( | |
963 | xfs_mount_t *mp, | |
964 | xfs_ino_t ino, | |
965 | xfs_dinode_t *dip, | |
966 | int ino_discovery, | |
967 | int *dino_dirty, /* out - 1 if dinode buffer dirty */ | |
968 | char *dirname, /* directory pathname */ | |
969 | xfs_ino_t *parent, /* out - NULLFSINO if entry not exist */ | |
970 | blkmap_t *blkmap, | |
971 | int *dot, /* out - 1 if there is a dot, else 0 */ | |
972 | int *dotdot, /* out - 1 if there's a dotdot, else 0 */ | |
973 | int *repair) /* out - 1 if something was fixed */ | |
974 | { | |
a354abc8 | 975 | struct xfs_dir2_data_hdr *block; |
2bd0ea18 NS |
976 | xfs_dir2_leaf_entry_t *blp; |
977 | bmap_ext_t *bmp; | |
a2ceac1f | 978 | struct xfs_buf *bp; |
2bd0ea18 NS |
979 | xfs_dir2_block_tail_t *btp; |
980 | int nex; | |
981 | int rval; | |
1e77098c | 982 | bmap_ext_t lbmp; |
a2ceac1f | 983 | int dirty = 0; |
2bd0ea18 NS |
984 | |
985 | *repair = *dot = *dotdot = 0; | |
986 | *parent = NULLFSINO; | |
ff105f75 DC |
987 | nex = blkmap_getn(blkmap, mp->m_dir_geo->datablk, |
988 | mp->m_dir_geo->fsbcount, &bmp, &lbmp); | |
2bd0ea18 | 989 | if (nex == 0) { |
5d1b7f0f CH |
990 | do_warn( |
991 | _("block %u for directory inode %" PRIu64 " is missing\n"), | |
ff105f75 | 992 | mp->m_dir_geo->datablk, ino); |
2bd0ea18 NS |
993 | return 1; |
994 | } | |
75c8b434 | 995 | bp = da_read_buf(mp, nex, bmp, &xfs_dir3_block_buf_ops); |
1e77098c MV |
996 | if (bmp != &lbmp) |
997 | free(bmp); | |
2bd0ea18 | 998 | if (bp == NULL) { |
5d1b7f0f CH |
999 | do_warn( |
1000 | _("can't read block %u for directory inode %" PRIu64 "\n"), | |
ff105f75 | 1001 | mp->m_dir_geo->datablk, ino); |
2bd0ea18 NS |
1002 | return 1; |
1003 | } | |
be752639 DW |
1004 | if (bp->b_error == -EFSCORRUPTED) { |
1005 | do_warn( | |
1006 | _("corrupt directory block %u for inode %" PRIu64 "\n"), | |
1007 | mp->m_dir_geo->datablk, ino); | |
1008 | libxfs_buf_relse(bp); | |
1009 | return 1; | |
1010 | } | |
2bd0ea18 NS |
1011 | /* |
1012 | * Verify the block | |
1013 | */ | |
a2ceac1f | 1014 | block = bp->b_addr; |
a354abc8 DC |
1015 | if (!(be32_to_cpu(block->magic) == XFS_DIR2_BLOCK_MAGIC || |
1016 | be32_to_cpu(block->magic) == XFS_DIR3_BLOCK_MAGIC)) | |
5d1b7f0f CH |
1017 | do_warn( |
1018 | _("bad directory block magic # %#x in block %u for directory inode %" PRIu64 "\n"), | |
ff105f75 | 1019 | be32_to_cpu(block->magic), mp->m_dir_geo->datablk, ino); |
2bd0ea18 NS |
1020 | /* |
1021 | * process the data area | |
1022 | * this also checks & fixes the bestfree | |
1023 | */ | |
ff105f75 | 1024 | btp = xfs_dir2_block_tail_p(mp->m_dir_geo, block); |
5e656dbb | 1025 | blp = xfs_dir2_block_leaf_p(btp); |
2bd0ea18 NS |
1026 | /* |
1027 | * Don't let this go past the end of the block. | |
1028 | */ | |
1029 | if ((char *)blp > (char *)btp) | |
1030 | blp = (xfs_dir2_leaf_entry_t *)btp; | |
1031 | rval = process_dir2_data(mp, ino, dip, ino_discovery, dirname, parent, | |
ff105f75 | 1032 | bp, dot, dotdot, mp->m_dir_geo->datablk, (char *)blp, &dirty); |
575010d5 | 1033 | /* If block looks ok but CRC didn't match, make sure to recompute it. */ |
12b53197 | 1034 | if (!rval && bp->b_error == -EFSBADCRC) |
575010d5 | 1035 | dirty = 1; |
a2ceac1f | 1036 | if (dirty && !no_modify) { |
2bd0ea18 | 1037 | *repair = 1; |
f524ae04 | 1038 | libxfs_buf_mark_dirty(bp); |
18b4f688 | 1039 | libxfs_buf_relse(bp); |
2bd0ea18 | 1040 | } else |
e02ba985 | 1041 | libxfs_buf_relse(bp); |
2bd0ea18 NS |
1042 | return rval; |
1043 | } | |
1044 | ||
1045 | /* | |
1046 | * Validates leaf contents, node format directories only. | |
1047 | * magic number and sibling pointers checked by caller. | |
1048 | * Returns 0 if block is ok, 1 if the block is bad. | |
1049 | * Looking for: out of order hash values, bad stale counts. | |
1050 | */ | |
1051 | static int | |
1052 | process_leaf_block_dir2( | |
1053 | xfs_mount_t *mp, | |
1054 | xfs_dir2_leaf_t *leaf, | |
1055 | xfs_dablk_t da_bno, | |
1056 | xfs_ino_t ino, | |
1057 | xfs_dahash_t last_hashval, | |
1058 | xfs_dahash_t *next_hashval) | |
1059 | { | |
1060 | int i; | |
1061 | int stale; | |
65b80c98 | 1062 | struct xfs_dir2_leaf_entry *ents; |
658ac3e3 | 1063 | struct xfs_dir3_icleaf_hdr leafhdr; |
65b80c98 | 1064 | |
9db68faf | 1065 | libxfs_dir2_leaf_hdr_from_disk(mp, &leafhdr, leaf); |
a2279497 | 1066 | ents = leafhdr.ents; |
2bd0ea18 | 1067 | |
658ac3e3 | 1068 | for (i = stale = 0; i < leafhdr.count; i++) { |
ff105f75 | 1069 | if ((char *)&ents[i] >= (char *)leaf + mp->m_dir_geo->blksize) { |
5d1b7f0f CH |
1070 | do_warn( |
1071 | _("bad entry count in block %u of directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
1072 | da_bno, ino); |
1073 | return 1; | |
1074 | } | |
65b80c98 | 1075 | if (be32_to_cpu(ents[i].address) == XFS_DIR2_NULL_DATAPTR) |
2bd0ea18 | 1076 | stale++; |
65b80c98 | 1077 | else if (be32_to_cpu(ents[i].hashval) < last_hashval) { |
5d1b7f0f CH |
1078 | do_warn( |
1079 | _("bad hash ordering in block %u of directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
1080 | da_bno, ino); |
1081 | return 1; | |
1082 | } | |
65b80c98 | 1083 | *next_hashval = last_hashval = be32_to_cpu(ents[i].hashval); |
2bd0ea18 | 1084 | } |
658ac3e3 | 1085 | if (stale != leafhdr.stale) { |
5d1b7f0f CH |
1086 | do_warn( |
1087 | _("bad stale count in block %u of directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
1088 | da_bno, ino); |
1089 | return 1; | |
1090 | } | |
1091 | return 0; | |
1092 | } | |
1093 | ||
1094 | /* | |
1095 | * Returns 0 if the directory is ok, 1 if it has to be rebuilt. | |
1096 | */ | |
1097 | static int | |
1098 | process_leaf_level_dir2( | |
1099 | xfs_mount_t *mp, | |
360f4a2e | 1100 | da_bt_cursor_t *da_cursor, |
2bd0ea18 | 1101 | int *repair) |
dfc130f3 | 1102 | { |
2bd0ea18 | 1103 | bmap_ext_t *bmp; |
a2ceac1f | 1104 | struct xfs_buf *bp; |
2bd0ea18 NS |
1105 | int buf_dirty; |
1106 | xfs_dahash_t current_hashval; | |
1107 | xfs_dablk_t da_bno; | |
1108 | xfs_dahash_t greatest_hashval; | |
1109 | xfs_ino_t ino; | |
1110 | xfs_dir2_leaf_t *leaf; | |
1111 | int nex; | |
1112 | xfs_dablk_t prev_bno; | |
1e77098c | 1113 | bmap_ext_t lbmp; |
658ac3e3 | 1114 | struct xfs_dir3_icleaf_hdr leafhdr; |
2bd0ea18 NS |
1115 | |
1116 | da_bno = da_cursor->level[0].bno; | |
1117 | ino = da_cursor->ino; | |
1118 | prev_bno = 0; | |
1119 | bmp = NULL; | |
1120 | current_hashval = 0; | |
9234d416 | 1121 | greatest_hashval = 0; |
2bd0ea18 NS |
1122 | buf_dirty = 0; |
1123 | ||
1124 | do { | |
ff105f75 DC |
1125 | nex = blkmap_getn(da_cursor->blkmap, da_bno, |
1126 | mp->m_dir_geo->fsbcount, &bmp, &lbmp); | |
2bd0ea18 NS |
1127 | /* |
1128 | * Directory code uses 0 as the NULL block pointer since 0 | |
1129 | * is the root block and no directory block pointer can point | |
1130 | * to the root block of the btree. | |
1131 | */ | |
1132 | ASSERT(da_bno != 0); | |
1133 | ||
1134 | if (nex == 0) { | |
5d1b7f0f CH |
1135 | do_warn( |
1136 | _("can't map block %u for directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
1137 | da_bno, ino); |
1138 | goto error_out; | |
1139 | } | |
75c8b434 | 1140 | bp = da_read_buf(mp, nex, bmp, &xfs_dir3_leafn_buf_ops); |
1e77098c MV |
1141 | if (bmp != &lbmp) |
1142 | free(bmp); | |
2bd0ea18 NS |
1143 | bmp = NULL; |
1144 | if (bp == NULL) { | |
5d1b7f0f CH |
1145 | do_warn( |
1146 | _("can't read file block %u for directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
1147 | da_bno, ino); |
1148 | goto error_out; | |
1149 | } | |
be752639 DW |
1150 | if (bp->b_error == -EFSCORRUPTED) { |
1151 | do_warn( | |
1152 | _("corrupt directory leafn block %u for inode %" PRIu64 "\n"), | |
1153 | da_bno, ino); | |
1154 | libxfs_buf_relse(bp); | |
1155 | goto error_out; | |
1156 | } | |
a2ceac1f | 1157 | leaf = bp->b_addr; |
9db68faf | 1158 | libxfs_dir2_leaf_hdr_from_disk(mp, &leafhdr, leaf); |
2bd0ea18 NS |
1159 | /* |
1160 | * Check magic number for leaf directory btree block. | |
1161 | */ | |
658ac3e3 DC |
1162 | if (!(leafhdr.magic == XFS_DIR2_LEAFN_MAGIC || |
1163 | leafhdr.magic == XFS_DIR3_LEAFN_MAGIC)) { | |
5d1b7f0f CH |
1164 | do_warn( |
1165 | _("bad directory leaf magic # %#x for directory inode %" PRIu64 " block %u\n"), | |
658ac3e3 | 1166 | leafhdr.magic, ino, da_bno); |
e02ba985 | 1167 | libxfs_buf_relse(bp); |
2bd0ea18 NS |
1168 | goto error_out; |
1169 | } | |
1170 | buf_dirty = 0; | |
1171 | /* | |
1172 | * For each block, process the block, verify its path, | |
1173 | * then get next block. Update cursor values along the way. | |
1174 | */ | |
1175 | if (process_leaf_block_dir2(mp, leaf, da_bno, ino, | |
1176 | current_hashval, &greatest_hashval)) { | |
e02ba985 | 1177 | libxfs_buf_relse(bp); |
2bd0ea18 NS |
1178 | goto error_out; |
1179 | } | |
1180 | /* | |
1181 | * Index can be set to hdr.count so match the indices of the | |
1182 | * interior blocks -- which at the end of the block will point | |
1183 | * to 1 after the final real entry in the block. | |
1184 | */ | |
1185 | da_cursor->level[0].hashval = greatest_hashval; | |
1186 | da_cursor->level[0].bp = bp; | |
1187 | da_cursor->level[0].bno = da_bno; | |
658ac3e3 | 1188 | da_cursor->level[0].index = leafhdr.count; |
2bd0ea18 NS |
1189 | da_cursor->level[0].dirty = buf_dirty; |
1190 | ||
658ac3e3 | 1191 | if (leafhdr.back != prev_bno) { |
5d1b7f0f CH |
1192 | do_warn( |
1193 | _("bad sibling back pointer for block %u in directory inode %" PRIu64 "\n"), | |
2bd0ea18 | 1194 | da_bno, ino); |
e02ba985 | 1195 | libxfs_buf_relse(bp); |
2bd0ea18 NS |
1196 | goto error_out; |
1197 | } | |
1198 | prev_bno = da_bno; | |
658ac3e3 | 1199 | da_bno = leafhdr.forw; |
2bd0ea18 | 1200 | if (da_bno != 0) { |
360f4a2e | 1201 | if (verify_da_path(mp, da_cursor, 0, XFS_DATA_FORK)) { |
e02ba985 | 1202 | libxfs_buf_relse(bp); |
2bd0ea18 NS |
1203 | goto error_out; |
1204 | } | |
1205 | } | |
1206 | current_hashval = greatest_hashval; | |
575010d5 JK |
1207 | /* |
1208 | * If block looks ok but CRC didn't match, make sure to | |
1209 | * recompute it. | |
1210 | */ | |
12b53197 | 1211 | if (!no_modify && bp->b_error == -EFSBADCRC) |
575010d5 | 1212 | buf_dirty = 1; |
27527004 | 1213 | ASSERT(buf_dirty == 0 || (buf_dirty && !no_modify)); |
2bd0ea18 NS |
1214 | if (buf_dirty && !no_modify) { |
1215 | *repair = 1; | |
f524ae04 | 1216 | libxfs_buf_mark_dirty(bp); |
18b4f688 | 1217 | libxfs_buf_relse(bp); |
2bd0ea18 | 1218 | } else |
e02ba985 | 1219 | libxfs_buf_relse(bp); |
2bd0ea18 | 1220 | } while (da_bno != 0); |
5cd3b070 | 1221 | if (verify_final_da_path(mp, da_cursor, 0, XFS_DATA_FORK)) { |
2bd0ea18 NS |
1222 | /* |
1223 | * Verify the final path up (right-hand-side) if still ok. | |
1224 | */ | |
5d1b7f0f | 1225 | do_warn(_("bad hash path in directory %" PRIu64 "\n"), ino); |
2bd0ea18 NS |
1226 | goto error_out; |
1227 | } | |
1228 | /* | |
1229 | * Redundant but just for testing. | |
1230 | */ | |
360f4a2e | 1231 | release_da_cursor(mp, da_cursor, 0); |
2bd0ea18 NS |
1232 | return 0; |
1233 | ||
1234 | error_out: | |
1235 | /* | |
1236 | * Release all buffers holding interior btree blocks. | |
1237 | */ | |
360f4a2e | 1238 | err_release_da_cursor(mp, da_cursor, 0); |
1e77098c | 1239 | if (bmp && (bmp != &lbmp)) |
2bd0ea18 NS |
1240 | free(bmp); |
1241 | return 1; | |
1242 | } | |
1243 | ||
1244 | /* | |
1245 | * Return 1 if the directory's leaf/node space is corrupted and | |
1246 | * needs to be rebuilt, 0 if it's ok. | |
1247 | */ | |
1248 | static int | |
1249 | process_node_dir2( | |
1250 | xfs_mount_t *mp, | |
1251 | xfs_ino_t ino, | |
1252 | xfs_dinode_t *dip, | |
1253 | blkmap_t *blkmap, | |
1254 | int *repair) | |
1255 | { | |
1256 | xfs_dablk_t bno; | |
360f4a2e | 1257 | da_bt_cursor_t da_cursor; |
2bd0ea18 NS |
1258 | |
1259 | /* | |
1260 | * Try again -- traverse down left-side of tree until we hit the | |
1261 | * left-most leaf block setting up the btree cursor along the way. | |
1262 | * Then walk the leaf blocks left-to-right, calling a parent | |
1263 | * verification routine each time we traverse a block. | |
1264 | */ | |
dab9b8d6 | 1265 | memset(&da_cursor, 0, sizeof(da_cursor)); |
2bd0ea18 NS |
1266 | da_cursor.ino = ino; |
1267 | da_cursor.dip = dip; | |
1268 | da_cursor.blkmap = blkmap; | |
1269 | ||
1270 | /* | |
1271 | * Now process interior node. | |
1272 | */ | |
360f4a2e | 1273 | if (traverse_int_dablock(mp, &da_cursor, &bno, XFS_DATA_FORK) == 0) |
2bd0ea18 NS |
1274 | return 1; |
1275 | ||
1276 | /* | |
71a6af85 | 1277 | * Skip directories with a root marked XFS_DIR2_LEAFN_MAGIC |
3d2195e4 DW |
1278 | * |
1279 | * Be careful here: If any level of the da cursor was filled out then | |
1280 | * the directory has a da btree containing an invalid before pointer to | |
1281 | * dblock 0, and we should move on to rebuilding the directory. If no | |
1282 | * levels in the da cursor got filled out, then we just have a single | |
1283 | * leafn block and we're done. | |
2bd0ea18 NS |
1284 | */ |
1285 | if (bno == 0) { | |
3d2195e4 DW |
1286 | if (da_cursor.active > 0) { |
1287 | err_release_da_cursor(mp, &da_cursor, 0); | |
1288 | return 1; | |
1289 | } else { | |
1290 | release_da_cursor(mp, &da_cursor, 0); | |
1291 | return 0; | |
1292 | } | |
2bd0ea18 NS |
1293 | } else { |
1294 | /* | |
1295 | * Now pass cursor and bno into leaf-block processing routine. | |
dfc130f3 | 1296 | * The leaf dir level routine checks the interior paths up to |
2bd0ea18 NS |
1297 | * the root including the final right-most path. |
1298 | */ | |
1299 | return process_leaf_level_dir2(mp, &da_cursor, repair); | |
1300 | } | |
1301 | } | |
1302 | ||
1303 | /* | |
1304 | * Process leaf and node directories. | |
1305 | * Process the data blocks then, if it's a node directory, check | |
1306 | * the consistency of those blocks. | |
1307 | */ | |
1308 | static int | |
1309 | process_leaf_node_dir2( | |
1310 | xfs_mount_t *mp, | |
1311 | xfs_ino_t ino, | |
1312 | xfs_dinode_t *dip, | |
1313 | int ino_discovery, | |
1314 | char *dirname, /* directory pathname */ | |
1315 | xfs_ino_t *parent, /* out - NULLFSINO if entry not exist */ | |
1316 | blkmap_t *blkmap, | |
1317 | int *dot, /* out - 1 if there is a dot, else 0 */ | |
1318 | int *dotdot, /* out - 1 if there's a dotdot, else 0 */ | |
1319 | int *repair, /* out - 1 if something was fixed */ | |
1320 | int isnode) /* node directory not leaf */ | |
1321 | { | |
1322 | bmap_ext_t *bmp; | |
a2ceac1f | 1323 | struct xfs_buf *bp; |
a354abc8 | 1324 | struct xfs_dir2_data_hdr *data; |
5a35bf2c | 1325 | xfs_fileoff_t dbno; |
2bd0ea18 NS |
1326 | int good; |
1327 | int i; | |
5a35bf2c | 1328 | xfs_fileoff_t ndbno; |
2bd0ea18 NS |
1329 | int nex; |
1330 | int t; | |
1e77098c | 1331 | bmap_ext_t lbmp; |
a2ceac1f | 1332 | int dirty = 0; |
2bd0ea18 NS |
1333 | |
1334 | *repair = *dot = *dotdot = good = 0; | |
1335 | *parent = NULLFSINO; | |
5a35bf2c | 1336 | ndbno = NULLFILEOFF; |
ff105f75 DC |
1337 | while ((dbno = blkmap_next_off(blkmap, ndbno, &t)) < mp->m_dir_geo->leafblk) { |
1338 | nex = blkmap_getn(blkmap, dbno, mp->m_dir_geo->fsbcount, &bmp, &lbmp); | |
9fe1cc86 ES |
1339 | /* Advance through map to last dfs block in this dir block */ |
1340 | ndbno = dbno; | |
ff105f75 | 1341 | while (ndbno < dbno + mp->m_dir_geo->fsbcount - 1) { |
9fe1cc86 ES |
1342 | ndbno = blkmap_next_off(blkmap, ndbno, &t); |
1343 | } | |
2bd0ea18 | 1344 | if (nex == 0) { |
5d1b7f0f CH |
1345 | do_warn( |
1346 | _("block %" PRIu64 " for directory inode %" PRIu64 " is missing\n"), | |
2bd0ea18 NS |
1347 | dbno, ino); |
1348 | continue; | |
1349 | } | |
75c8b434 | 1350 | bp = da_read_buf(mp, nex, bmp, &xfs_dir3_data_buf_ops); |
1e77098c MV |
1351 | if (bmp != &lbmp) |
1352 | free(bmp); | |
2bd0ea18 | 1353 | if (bp == NULL) { |
5d1b7f0f CH |
1354 | do_warn( |
1355 | _("can't read block %" PRIu64 " for directory inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
1356 | dbno, ino); |
1357 | continue; | |
1358 | } | |
be752639 DW |
1359 | if (bp->b_error == -EFSCORRUPTED) { |
1360 | do_warn( | |
7aed36f9 | 1361 | _("corrupt directory data block %" PRIu64 " for inode %" PRIu64 "\n"), |
be752639 DW |
1362 | dbno, ino); |
1363 | libxfs_buf_relse(bp); | |
1364 | continue; | |
1365 | } | |
a2ceac1f | 1366 | data = bp->b_addr; |
a354abc8 DC |
1367 | if (!(be32_to_cpu(data->magic) == XFS_DIR2_DATA_MAGIC || |
1368 | be32_to_cpu(data->magic) == XFS_DIR3_DATA_MAGIC)) | |
5d1b7f0f CH |
1369 | do_warn( |
1370 | _("bad directory block magic # %#x in block %" PRIu64 " for directory inode %" PRIu64 "\n"), | |
a354abc8 | 1371 | be32_to_cpu(data->magic), dbno, ino); |
2bd0ea18 NS |
1372 | i = process_dir2_data(mp, ino, dip, ino_discovery, dirname, |
1373 | parent, bp, dot, dotdot, (xfs_dablk_t)dbno, | |
ff105f75 | 1374 | (char *)data + mp->m_dir_geo->blksize, &dirty); |
575010d5 | 1375 | if (i == 0) { |
2bd0ea18 | 1376 | good++; |
575010d5 | 1377 | /* Maybe just CRC is wrong. Make sure we correct it. */ |
12b53197 | 1378 | if (bp->b_error == -EFSBADCRC) |
575010d5 JK |
1379 | dirty = 1; |
1380 | } | |
a2ceac1f | 1381 | if (dirty && !no_modify) { |
2bd0ea18 | 1382 | *repair = 1; |
f524ae04 | 1383 | libxfs_buf_mark_dirty(bp); |
18b4f688 | 1384 | libxfs_buf_relse(bp); |
2bd0ea18 | 1385 | } else |
e02ba985 | 1386 | libxfs_buf_relse(bp); |
2bd0ea18 NS |
1387 | } |
1388 | if (good == 0) | |
1389 | return 1; | |
1390 | if (!isnode) | |
1391 | return 0; | |
1392 | if (dir2_is_badino(ino)) | |
1393 | return 0; | |
1394 | ||
1395 | if (process_node_dir2(mp, ino, dip, blkmap, repair)) | |
1396 | dir2_add_badlist(ino); | |
1397 | return 0; | |
1398 | ||
1399 | } | |
1400 | ||
1401 | /* | |
1402 | * Returns 1 if things are bad (directory needs to be junked) | |
1403 | * and 0 if things are ok. If ino_discovery is 1, add unknown | |
1404 | * inodes to uncertain inode list. | |
1405 | */ | |
1406 | int | |
1407 | process_dir2( | |
1408 | xfs_mount_t *mp, | |
1409 | xfs_ino_t ino, | |
1410 | xfs_dinode_t *dip, | |
1411 | int ino_discovery, | |
1412 | int *dino_dirty, | |
1413 | char *dirname, | |
1414 | xfs_ino_t *parent, | |
1415 | blkmap_t *blkmap) | |
1416 | { | |
1417 | int dot; | |
1418 | int dotdot; | |
5a35bf2c | 1419 | xfs_fileoff_t last; |
2bd0ea18 NS |
1420 | int repair; |
1421 | int res; | |
1422 | ||
1423 | *parent = NULLFSINO; | |
1424 | dot = dotdot = 0; | |
1425 | last = 0; | |
1426 | ||
1427 | /* | |
1428 | * branch off depending on the type of inode. This routine | |
1429 | * is only called ONCE so all the subordinate routines will | |
1430 | * fix '.' and junk '..' if they're bogus. | |
1431 | */ | |
1432 | if (blkmap) | |
1433 | last = blkmap_last_off(blkmap); | |
56b2de80 DC |
1434 | if (be64_to_cpu(dip->di_size) <= XFS_DFORK_DSIZE(dip, mp) && |
1435 | dip->di_format == XFS_DINODE_FMT_LOCAL) { | |
2bd0ea18 NS |
1436 | dot = dotdot = 1; |
1437 | res = process_sf_dir2(mp, ino, dip, ino_discovery, dino_dirty, | |
1438 | dirname, parent, &repair); | |
ff105f75 | 1439 | } else if (last == mp->m_dir_geo->fsbcount && |
56b2de80 DC |
1440 | (dip->di_format == XFS_DINODE_FMT_EXTENTS || |
1441 | dip->di_format == XFS_DINODE_FMT_BTREE)) { | |
2bd0ea18 NS |
1442 | res = process_block_dir2(mp, ino, dip, ino_discovery, |
1443 | dino_dirty, dirname, parent, blkmap, &dot, &dotdot, | |
1444 | &repair); | |
ff105f75 | 1445 | } else if (last >= mp->m_dir_geo->leafblk + mp->m_dir_geo->fsbcount && |
56b2de80 DC |
1446 | (dip->di_format == XFS_DINODE_FMT_EXTENTS || |
1447 | dip->di_format == XFS_DINODE_FMT_BTREE)) { | |
2bd0ea18 NS |
1448 | res = process_leaf_node_dir2(mp, ino, dip, ino_discovery, |
1449 | dirname, parent, blkmap, &dot, &dotdot, &repair, | |
ff105f75 | 1450 | last > mp->m_dir_geo->leafblk + mp->m_dir_geo->fsbcount); |
2bd0ea18 | 1451 | } else { |
5d1b7f0f | 1452 | do_warn(_("bad size/format for directory %" PRIu64 "\n"), ino); |
2bd0ea18 NS |
1453 | return 1; |
1454 | } | |
1455 | /* | |
1456 | * bad . entries in all directories will be fixed up in phase 6 | |
1457 | */ | |
1458 | if (dot == 0) { | |
5d1b7f0f | 1459 | do_warn(_("no . entry for directory %" PRIu64 "\n"), ino); |
2bd0ea18 NS |
1460 | } |
1461 | ||
1462 | /* | |
1463 | * shortform dirs always have a .. entry. .. for all longform | |
1464 | * directories will get fixed in phase 6. .. for other shortform | |
1465 | * dirs also get fixed there. .. for a shortform root was | |
1466 | * fixed in place since we know what it should be | |
1467 | */ | |
1468 | if (dotdot == 0 && ino != mp->m_sb.sb_rootino) { | |
5d1b7f0f | 1469 | do_warn(_("no .. entry for directory %" PRIu64 "\n"), ino); |
2bd0ea18 | 1470 | } else if (dotdot == 0 && ino == mp->m_sb.sb_rootino) { |
5d1b7f0f | 1471 | do_warn(_("no .. entry for root directory %" PRIu64 "\n"), ino); |
2bd0ea18 NS |
1472 | need_root_dotdot = 1; |
1473 | } | |
dfc130f3 | 1474 | |
27527004 NS |
1475 | ASSERT((ino != mp->m_sb.sb_rootino && ino != *parent) || |
1476 | (ino == mp->m_sb.sb_rootino && | |
1477 | (ino == *parent || need_root_dotdot == 1))); | |
2bd0ea18 NS |
1478 | |
1479 | return res; | |
1480 | } |