]>
Commit | Line | Data |
---|---|---|
2bd0ea18 | 1 | /* |
cc08d43e | 2 | * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. |
2bd0ea18 NS |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms of version 2 of the GNU General Public License as | |
6 | * published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it would be useful, but | |
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
11 | * | |
12 | * Further, this software is distributed without any warranty that it is | |
13 | * free of the rightful claim of any third person regarding infringement | |
14 | * or the like. Any license provided herein, whether implied or | |
15 | * otherwise, applies only to this software file. Patent licenses, if | |
16 | * any, provided herein do not apply to combinations of this program with | |
17 | * other software, or any other product whatsoever. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License along | |
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | |
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | |
22 | * | |
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | |
24 | * Mountain View, CA 94043, or: | |
25 | * | |
26 | * http://www.sgi.com | |
27 | * | |
28 | * For further information regarding this notice, see: | |
29 | * | |
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | |
31 | */ | |
32 | ||
33 | #include <libxfs.h> | |
34 | #include "avl.h" | |
35 | #include "globals.h" | |
36 | #include "agheader.h" | |
37 | #include "incore.h" | |
38 | #include "protos.h" | |
39 | #include "err_protos.h" | |
40 | #include "dinode.h" | |
41 | #include "dir.h" | |
42 | #include "bmap.h" | |
43 | ||
44 | #if XFS_DIR_LEAF_MAPSIZE >= XFS_ATTR_LEAF_MAPSIZE | |
45 | #define XR_DA_LEAF_MAPSIZE XFS_DIR_LEAF_MAPSIZE | |
46 | #else | |
47 | #define XR_DA_LEAF_MAPSIZE XFS_ATTR_LEAF_MAPSIZE | |
48 | #endif | |
49 | ||
50 | ||
51 | ||
52 | typedef struct da_hole_map { | |
53 | int lost_holes; | |
54 | int num_holes; | |
55 | struct { | |
56 | int base; | |
57 | int size; | |
58 | } hentries[XR_DA_LEAF_MAPSIZE]; | |
59 | } da_hole_map_t; | |
60 | ||
61 | /* | |
62 | * takes a name and length (name need not be null-terminated) | |
63 | * and returns 1 if the name contains a '/' or a \0, returns 0 | |
64 | * otherwise | |
65 | */ | |
66 | int | |
67 | namecheck(char *name, int length) | |
68 | { | |
69 | char *c; | |
70 | int i; | |
71 | ||
72 | ASSERT(length < MAXNAMELEN); | |
73 | ||
74 | for (c = name, i = 0; i < length; i++, c++) { | |
75 | if (*c == '/' || *c == '\0') | |
76 | return(1); | |
77 | } | |
78 | ||
79 | return(0); | |
80 | } | |
81 | ||
82 | /* | |
83 | * this routine performs inode discovery and tries to fix things | |
84 | * in place. available redundancy -- inode data size should match | |
85 | * used directory space in inode. returns number of valid directory | |
86 | * entries. a non-zero return value means the directory is bogus | |
87 | * and should be blasted. | |
88 | */ | |
89 | /* ARGSUSED */ | |
90 | int | |
91 | process_shortform_dir( | |
92 | xfs_mount_t *mp, | |
93 | xfs_ino_t ino, | |
94 | xfs_dinode_t *dip, | |
95 | int ino_discovery, | |
96 | int *dino_dirty, /* out - 1 if dinode buffer dirty? */ | |
97 | xfs_ino_t *parent, /* out - NULLFSINO if entry doesn't exist */ | |
98 | char *dirname, /* directory pathname */ | |
99 | int *repair) /* out - 1 if dir was fixed up */ | |
100 | { | |
101 | xfs_dir_shortform_t *sf; | |
102 | xfs_dir_sf_entry_t *sf_entry, *next_sfe, *tmp_sfe; | |
103 | xfs_ino_t lino; | |
104 | int max_size; | |
105 | __int64_t ino_dir_size; | |
106 | int num_entries; | |
107 | int ino_off; | |
108 | int namelen; | |
109 | int i; | |
110 | int junkit; | |
111 | int tmp_len; | |
112 | int tmp_elen; | |
113 | int bad_sfnamelen; | |
114 | ino_tree_node_t *irec_p; | |
115 | char name[MAXNAMELEN + 1]; | |
116 | ||
117 | #ifdef XR_DIR_TRACE | |
118 | fprintf(stderr, "process_shortform_dir - inode %llu\n", ino); | |
119 | #endif | |
120 | ||
121 | sf = &dip->di_u.di_dirsf; | |
122 | ||
123 | max_size = XFS_DFORK_DSIZE_ARCH(dip, mp, ARCH_CONVERT); | |
124 | num_entries = INT_GET(sf->hdr.count, ARCH_CONVERT); | |
125 | ino_dir_size = INT_GET(dip->di_core.di_size, ARCH_CONVERT); | |
126 | *repair = 0; | |
127 | ||
128 | ASSERT(ino_dir_size <= max_size); | |
129 | ||
130 | /* | |
131 | * check for bad entry count | |
132 | */ | |
133 | if (num_entries * sizeof(xfs_dir_sf_entry_t) + sizeof(xfs_dir_sf_hdr_t) | |
134 | > max_size || num_entries == 0) | |
135 | num_entries = 0xFF; | |
136 | ||
137 | /* | |
138 | * run through entries, stop at first bad entry, don't need | |
139 | * to check for .. since that's encoded in its own field | |
140 | */ | |
141 | sf_entry = next_sfe = &sf->list[0]; | |
142 | for (i = 0; i < num_entries && ino_dir_size > | |
143 | (__psint_t)next_sfe - (__psint_t)sf; i++) { | |
144 | tmp_sfe = NULL; | |
145 | sf_entry = next_sfe; | |
146 | junkit = 0; | |
147 | bad_sfnamelen = 0; | |
148 | XFS_DIR_SF_GET_DIRINO_ARCH(&sf_entry->inumber, &lino, ARCH_CONVERT); | |
149 | ||
150 | /* | |
151 | * if entry points to self, junk it since only '.' or '..' | |
152 | * should do that and shortform dirs don't contain either | |
153 | * entry. if inode number is invalid, trash entry. | |
154 | * if entry points to special inodes, trash it. | |
155 | * if inode is unknown but number is valid, | |
156 | * add it to the list of uncertain inodes. don't | |
157 | * have to worry about an entry pointing to a | |
158 | * deleted lost+found inode because the entry was | |
159 | * deleted at the same time that the inode was cleared. | |
160 | */ | |
161 | if (lino == ino) { | |
162 | junkit = 1; | |
163 | } else if (verify_inum(mp, lino)) { | |
164 | /* | |
165 | * junk the entry, mark lino as NULL since it's bad | |
166 | */ | |
167 | do_warn("invalid inode number %llu in directory %llu\n", | |
168 | lino, ino); | |
169 | lino = NULLFSINO; | |
170 | junkit = 1; | |
171 | } else if (lino == mp->m_sb.sb_rbmino) { | |
172 | do_warn( | |
173 | "entry in shorform dir %llu references realtime bitmap inode %llu\n", | |
174 | ino, lino); | |
175 | junkit = 1; | |
176 | } else if (lino == mp->m_sb.sb_rsumino) { | |
177 | do_warn( | |
178 | "entry in shorform dir %llu references realtime summary inode %llu\n", | |
179 | ino, lino); | |
180 | junkit = 1; | |
181 | } else if (lino == mp->m_sb.sb_uquotino) { | |
182 | do_warn( | |
183 | "entry in shorform dir %llu references user quota inode %llu\n", | |
184 | ino, lino); | |
185 | junkit = 1; | |
b36eef04 | 186 | } else if (lino == mp->m_sb.sb_gquotino) { |
2bd0ea18 | 187 | do_warn( |
b36eef04 | 188 | "entry in shorform dir %llu references group quota inode %llu\n", |
2bd0ea18 NS |
189 | ino, lino); |
190 | junkit = 1; | |
191 | } else if ((irec_p = find_inode_rec(XFS_INO_TO_AGNO(mp, lino), | |
192 | XFS_INO_TO_AGINO(mp, lino))) != NULL) { | |
193 | /* | |
194 | * if inode is marked free and we're in inode | |
195 | * discovery mode, leave the entry alone for now. | |
196 | * if the inode turns out to be used, we'll figure | |
197 | * that out when we scan it. If the inode really | |
198 | * is free, we'll hit this code again in phase 4 | |
199 | * after we've finished inode discovery and blow | |
200 | * out the entry then. | |
201 | */ | |
202 | ino_off = XFS_INO_TO_AGINO(mp, lino) - | |
203 | irec_p->ino_startnum; | |
204 | ASSERT(is_inode_confirmed(irec_p, ino_off)); | |
205 | ||
206 | if (!ino_discovery && is_inode_free(irec_p, ino_off)) { | |
207 | do_warn( | |
208 | "entry references free inode %llu in shortform directory %llu\n", | |
209 | lino, ino); | |
210 | junkit = 1; | |
211 | } | |
212 | } else if (ino_discovery) { | |
213 | /* | |
214 | * put the inode on the uncertain list. we'll | |
215 | * pull the inode off the list and check it later. | |
216 | * if the inode turns out be bogus, we'll delete | |
217 | * this entry in phase 6. | |
218 | */ | |
219 | add_inode_uncertain(mp, lino, 0); | |
220 | } else { | |
221 | /* | |
222 | * blow the entry out. we know about all | |
223 | * undiscovered entries now (past inode discovery | |
224 | * phase) so this is clearly a bogus entry. | |
225 | */ | |
226 | do_warn( | |
227 | "entry references non-existent inode %llu in shortform dir %llu\n", | |
228 | lino, ino); | |
229 | junkit = 1; | |
230 | } | |
231 | ||
232 | namelen = sf_entry->namelen; | |
233 | ||
234 | if (namelen == 0) { | |
235 | /* | |
236 | * if we're really lucky, this is | |
237 | * the last entry in which case we | |
238 | * can use the dir size to set the | |
239 | * namelen value. otherwise, forget | |
240 | * it because we're not going to be | |
241 | * able to find the next entry. | |
242 | */ | |
243 | bad_sfnamelen = 1; | |
244 | ||
245 | if (i == num_entries - 1) { | |
246 | namelen = ino_dir_size - | |
247 | ((__psint_t) &sf_entry->name[0] - | |
248 | (__psint_t) sf); | |
249 | if (!no_modify) { | |
250 | do_warn( | |
251 | "zero length entry in shortform dir %llu, resetting to %d\n", | |
252 | ino, namelen); | |
253 | sf_entry->namelen = namelen; | |
254 | } else { | |
255 | do_warn( | |
256 | "zero length entry in shortform dir %llu, would set to %d\n", | |
257 | ino, namelen); | |
258 | } | |
259 | } else { | |
260 | do_warn( | |
261 | "zero length entry in shortform dir %llu", | |
262 | ino); | |
263 | if (!no_modify) | |
264 | do_warn(", junking %d entries\n", | |
265 | num_entries - i); | |
266 | else | |
267 | do_warn(", would junk %d entries\n", | |
268 | num_entries - i); | |
269 | /* | |
270 | * don't process the rest of the directory, | |
271 | * break out of processing looop | |
272 | */ | |
273 | break; | |
274 | } | |
275 | } else if ((__psint_t) sf_entry - (__psint_t) sf + | |
276 | + XFS_DIR_SF_ENTSIZE_BYENTRY(sf_entry) | |
277 | > ino_dir_size) { | |
278 | bad_sfnamelen = 1; | |
279 | ||
280 | if (i == num_entries - 1) { | |
281 | namelen = ino_dir_size - | |
282 | ((__psint_t) &sf_entry->name[0] - | |
283 | (__psint_t) sf); | |
284 | do_warn( | |
285 | "size of last entry overflows space left in in shortform dir %llu, ", | |
286 | ino); | |
287 | if (!no_modify) { | |
288 | do_warn("resetting to %d\n", | |
289 | namelen); | |
290 | sf_entry->namelen = namelen; | |
291 | *dino_dirty = 1; | |
292 | } else { | |
293 | do_warn("would reset to %d\n", | |
294 | namelen); | |
295 | } | |
296 | } else { | |
297 | do_warn( | |
298 | "size of entry #%d overflows space left in in shortform dir %llu\n", | |
299 | i, ino); | |
300 | if (!no_modify) { | |
301 | if (i == num_entries - 1) | |
302 | do_warn("junking entry #%d\n", | |
303 | i); | |
304 | else | |
305 | do_warn( | |
306 | "junking %d entries\n", | |
307 | num_entries - i); | |
308 | } else { | |
309 | if (i == num_entries - 1) | |
310 | do_warn( | |
311 | "would junk entry #%d\n", | |
312 | i); | |
313 | else | |
314 | do_warn( | |
315 | "would junk %d entries\n", | |
316 | num_entries - i); | |
317 | } | |
318 | ||
319 | break; | |
320 | } | |
321 | } | |
322 | ||
323 | /* | |
324 | * check for illegal chars in name. | |
325 | * no need to check for bad length because | |
326 | * the length value is stored in a byte | |
327 | * so it can't be too big, it can only wrap | |
328 | */ | |
329 | if (namecheck((char *)&sf_entry->name[0], namelen)) { | |
330 | /* | |
331 | * junk entry | |
332 | */ | |
333 | do_warn( | |
334 | "entry contains illegal character in shortform dir %llu\n", | |
335 | ino); | |
336 | junkit = 1; | |
337 | } | |
338 | ||
339 | /* | |
340 | * junk the entry by copying up the rest of the | |
341 | * fork over the current entry and decrementing | |
342 | * the entry count. if we're in no_modify mode, | |
343 | * just issue the warning instead. then continue | |
344 | * the loop with the next_sfe pointer set to the | |
345 | * correct place in the fork and other counters | |
346 | * properly set to reflect the deletion if it | |
347 | * happened. | |
348 | */ | |
349 | if (junkit) { | |
350 | bcopy(sf_entry->name, name, namelen); | |
351 | name[namelen] = '\0'; | |
352 | ||
353 | if (!no_modify) { | |
354 | tmp_elen = XFS_DIR_SF_ENTSIZE_BYENTRY(sf_entry); | |
355 | INT_MOD(dip->di_core.di_size, ARCH_CONVERT, -(tmp_elen)); | |
356 | ino_dir_size -= tmp_elen; | |
357 | ||
358 | tmp_sfe = (xfs_dir_sf_entry_t *) | |
359 | ((__psint_t) sf_entry + tmp_elen); | |
360 | tmp_len = max_size - ((__psint_t) tmp_sfe | |
361 | - (__psint_t) sf); | |
362 | ||
363 | memmove(sf_entry, tmp_sfe, tmp_len); | |
364 | ||
365 | INT_MOD(sf->hdr.count, ARCH_CONVERT, -1); | |
366 | num_entries--; | |
367 | bzero((void *) ((__psint_t) sf_entry + tmp_len), | |
368 | tmp_elen); | |
369 | ||
370 | /* | |
371 | * reset the tmp value to the current | |
372 | * pointer so we'll process the entry | |
373 | * we just moved up | |
374 | */ | |
375 | tmp_sfe = sf_entry; | |
376 | ||
377 | /* | |
378 | * WARNING: drop the index i by one | |
379 | * so it matches the decremented count | |
380 | * for accurate comparisons later | |
381 | */ | |
382 | i--; | |
383 | ||
384 | *dino_dirty = 1; | |
385 | *repair = 1; | |
386 | ||
387 | do_warn( | |
388 | "junking entry \"%s\" in directory inode %llu\n", | |
389 | name, ino); | |
390 | } else { | |
391 | do_warn( | |
392 | "would have junked entry \"%s\" in directory inode %llu\n", | |
393 | name, ino); | |
394 | } | |
395 | } | |
396 | ||
397 | /* | |
398 | * go onto next entry unless we've just junked an | |
399 | * entry in which the current entry pointer points | |
400 | * to an unprocessed entry. have to take into zero-len | |
401 | * entries into account in no modify mode since we | |
402 | * calculate size based on next_sfe. | |
403 | */ | |
404 | next_sfe = (tmp_sfe == NULL) | |
405 | ? (xfs_dir_sf_entry_t *) ((__psint_t) sf_entry | |
406 | + ((!bad_sfnamelen) | |
407 | ? XFS_DIR_SF_ENTSIZE_BYENTRY(sf_entry) | |
408 | : sizeof(xfs_dir_sf_entry_t) - 1 | |
409 | + namelen)) | |
410 | : tmp_sfe; | |
411 | } | |
412 | ||
413 | /* sync up sizes and entry counts */ | |
414 | ||
415 | if (INT_GET(sf->hdr.count, ARCH_CONVERT) != i) { | |
416 | if (no_modify) { | |
417 | do_warn("would have corrected entry count in directory %llu from %d to %d\n", | |
418 | ino, INT_GET(sf->hdr.count, ARCH_CONVERT), i); | |
419 | } else { | |
420 | do_warn("corrected entry count in directory %llu, was %d, now %d\n", | |
421 | ino, INT_GET(sf->hdr.count, ARCH_CONVERT), i); | |
422 | INT_SET(sf->hdr.count, ARCH_CONVERT, i); | |
423 | *dino_dirty = 1; | |
424 | *repair = 1; | |
425 | } | |
426 | } | |
427 | ||
428 | if ((__psint_t) next_sfe - (__psint_t) sf != ino_dir_size) { | |
429 | if (no_modify) { | |
430 | do_warn( | |
431 | "would have corrected directory %llu size from %lld to %lld\n", | |
432 | ino, (__int64_t) ino_dir_size, | |
433 | (__int64_t)((__psint_t) next_sfe - (__psint_t) sf)); | |
434 | } else { | |
435 | do_warn( | |
436 | "corrected directory %llu size, was %lld, now %lld\n", | |
437 | ino, (__int64_t) ino_dir_size, | |
438 | (__int64_t)((__psint_t) next_sfe - (__psint_t) sf)); | |
439 | ||
440 | INT_SET(dip->di_core.di_size, ARCH_CONVERT, (xfs_fsize_t) | |
441 | ((__psint_t) next_sfe - (__psint_t) sf)); | |
442 | *dino_dirty = 1; | |
443 | *repair = 1; | |
444 | } | |
445 | } | |
446 | /* | |
447 | * check parent (..) entry | |
448 | */ | |
449 | XFS_DIR_SF_GET_DIRINO_ARCH(&sf->hdr.parent, parent, ARCH_CONVERT); | |
450 | ||
451 | /* | |
452 | * if parent entry is bogus, null it out. we'll fix it later . | |
453 | */ | |
454 | if (verify_inum(mp, *parent)) { | |
455 | *parent = NULLFSINO; | |
456 | ||
457 | do_warn( | |
458 | "bogus .. inode number (%llu) in directory inode %llu,", | |
459 | *parent, ino); | |
460 | if (!no_modify) { | |
461 | do_warn("clearing inode number\n"); | |
462 | ||
463 | XFS_DIR_SF_PUT_DIRINO_ARCH(parent, &sf->hdr.parent, ARCH_CONVERT); | |
464 | *dino_dirty = 1; | |
465 | *repair = 1; | |
466 | } else { | |
467 | do_warn("would clear inode number\n"); | |
468 | } | |
469 | } else if (ino == mp->m_sb.sb_rootino && ino != *parent) { | |
470 | /* | |
471 | * root directories must have .. == . | |
472 | */ | |
473 | if (!no_modify) { | |
474 | do_warn( | |
475 | "corrected root directory %llu .. entry, was %llu, now %llu\n", | |
476 | ino, *parent, ino); | |
477 | *parent = ino; | |
478 | XFS_DIR_SF_PUT_DIRINO_ARCH(parent, &sf->hdr.parent, ARCH_CONVERT); | |
479 | *dino_dirty = 1; | |
480 | *repair = 1; | |
481 | } else { | |
482 | do_warn( | |
483 | "would have corrected root directory %llu .. entry from %llu to %llu\n", | |
484 | ino, *parent, ino); | |
485 | } | |
486 | } else if (ino == *parent && ino != mp->m_sb.sb_rootino) { | |
487 | /* | |
488 | * likewise, non-root directories can't have .. pointing | |
489 | * to . | |
490 | */ | |
491 | *parent = NULLFSINO; | |
492 | do_warn("bad .. entry in dir ino %llu, points to self,", | |
493 | ino); | |
494 | if (!no_modify) { | |
495 | do_warn(" clearing inode number\n"); | |
496 | ||
497 | XFS_DIR_SF_PUT_DIRINO_ARCH(parent, &sf->hdr.parent, ARCH_CONVERT); | |
498 | *dino_dirty = 1; | |
499 | *repair = 1; | |
500 | } else { | |
501 | do_warn(" would clear inode number\n"); | |
502 | } | |
503 | } | |
504 | ||
505 | return(0); | |
506 | } | |
507 | ||
508 | /* | |
509 | * freespace map for directory leaf blocks (1 bit per byte) | |
510 | * 1 == used, 0 == free | |
511 | */ | |
512 | static da_freemap_t dir_freemap[DA_BMAP_SIZE]; | |
513 | ||
514 | #if 0 | |
515 | unsigned char * | |
516 | alloc_da_freemap(xfs_mount_t *mp) | |
517 | { | |
518 | unsigned char *freemap; | |
519 | ||
520 | if ((freemap = malloc(mp->m_sb.sb_blocksize)) == NULL) | |
521 | return(NULL); | |
522 | ||
523 | bzero(freemap, mp->m_sb.sb_blocksize/NBBY); | |
524 | ||
525 | return(freemap); | |
526 | } | |
527 | #endif | |
528 | ||
529 | void | |
530 | init_da_freemap(da_freemap_t *dir_freemap) | |
531 | { | |
532 | bzero(dir_freemap, sizeof(da_freemap_t) * DA_BMAP_SIZE); | |
533 | } | |
534 | ||
535 | /* | |
536 | * sets directory freemap, returns 1 if there is a conflict | |
537 | * returns 0 if everything's good. the range [start, stop) is set. | |
538 | * right now, we just use the static array since only one directory | |
539 | * block will be processed at once even though the interface allows | |
540 | * you to pass in arbitrary da_freemap_t array's. | |
541 | * | |
542 | * Within a char, the lowest bit of the char represents the byte with | |
543 | * the smallest address | |
544 | */ | |
545 | int | |
546 | set_da_freemap(xfs_mount_t *mp, da_freemap_t *map, int start, int stop) | |
547 | { | |
548 | const da_freemap_t mask = 0x1; | |
549 | int i; | |
550 | ||
551 | if (start > stop) { | |
552 | /* | |
553 | * allow == relation since [x, x) claims 1 byte | |
554 | */ | |
555 | do_warn("bad range claimed [%d, %d) in da block\n", | |
556 | start, stop); | |
557 | return(1); | |
558 | } | |
559 | ||
560 | if (stop > mp->m_sb.sb_blocksize) { | |
561 | do_warn( | |
562 | "byte range end [%d %d) in da block larger than blocksize %d\n", | |
563 | start, stop, mp->m_sb.sb_blocksize); | |
564 | return(1); | |
565 | } | |
566 | ||
567 | for (i = start; i < stop; i ++) { | |
568 | if (map[i / NBBY] & (mask << i % NBBY)) { | |
569 | do_warn("multiply claimed byte %d in da block\n", i); | |
570 | return(1); | |
571 | } | |
572 | map[i / NBBY] |= (mask << i % NBBY); | |
573 | } | |
574 | ||
575 | return(0); | |
576 | } | |
577 | ||
578 | /* | |
579 | * returns 0 if holemap is consistent with reality (as expressed by | |
580 | * the da_freemap_t). returns 1 if there's a conflict. | |
581 | */ | |
582 | int | |
583 | verify_da_freemap(xfs_mount_t *mp, da_freemap_t *map, da_hole_map_t *holes, | |
584 | xfs_ino_t ino, xfs_dablk_t da_bno) | |
585 | { | |
586 | int i, j, start, len; | |
587 | const da_freemap_t mask = 0x1; | |
588 | ||
589 | for (i = 0; i < XFS_DIR_LEAF_MAPSIZE; i++) { | |
590 | if (holes->hentries[i].size == 0) | |
591 | continue; | |
592 | ||
593 | start = holes->hentries[i].base; | |
594 | len = holes->hentries[i].size; | |
595 | ||
596 | if (start >= mp->m_sb.sb_blocksize || | |
597 | start + len > mp->m_sb.sb_blocksize) { | |
598 | do_warn( | |
599 | "hole (start %d, len %d) out of range, block %d, dir ino %llu\n", | |
600 | start, len, da_bno, ino); | |
601 | return(1); | |
602 | } | |
603 | ||
604 | for (j = start; j < start + len; j++) { | |
605 | if ((map[j / NBBY] & (mask << (j % NBBY))) != 0) { | |
606 | /* | |
607 | * bad news -- hole claims a used byte is free | |
608 | */ | |
609 | do_warn( | |
610 | "hole claims used byte %d, block %d, dir ino %llu\n", | |
611 | j, da_bno, ino); | |
612 | return(1); | |
613 | } | |
614 | } | |
615 | } | |
616 | ||
617 | return(0); | |
618 | } | |
619 | ||
620 | void | |
621 | process_da_freemap(xfs_mount_t *mp, da_freemap_t *map, da_hole_map_t *holes) | |
622 | { | |
623 | int i, j, in_hole, start, length, smallest, num_holes; | |
624 | const da_freemap_t mask = 0x1; | |
625 | ||
626 | num_holes = in_hole = start = length = 0; | |
627 | ||
628 | for (i = 0; i < mp->m_sb.sb_blocksize; i++) { | |
629 | if ((map[i / NBBY] & (mask << (i % NBBY))) == 0) { | |
630 | /* | |
631 | * byte is free (unused) | |
632 | */ | |
633 | if (in_hole == 1) | |
634 | continue; | |
635 | /* | |
636 | * start of a new hole | |
637 | */ | |
638 | in_hole = 1; | |
639 | start = i; | |
640 | } else { | |
641 | /* | |
642 | * byte is used | |
643 | */ | |
644 | if (in_hole == 0) | |
645 | continue; | |
646 | /* | |
647 | * end of a hole | |
648 | */ | |
649 | in_hole = 0; | |
650 | /* | |
651 | * if the hole disappears, throw it away | |
652 | */ | |
653 | length = i - start; | |
654 | ||
655 | if (length <= 0) | |
656 | continue; | |
657 | ||
658 | num_holes++; | |
659 | ||
660 | for (smallest = j = 0; j < XR_DA_LEAF_MAPSIZE; j++) { | |
661 | if (holes->hentries[j].size < | |
662 | holes->hentries[smallest].size) | |
663 | smallest = j; | |
664 | ||
665 | } | |
666 | if (length > holes->hentries[smallest].size) { | |
667 | holes->hentries[smallest].base = start; | |
668 | holes->hentries[smallest].size = length; | |
669 | } | |
670 | } | |
671 | } | |
672 | ||
673 | /* | |
674 | * see if we have a big hole at the end | |
675 | */ | |
676 | if (in_hole == 1) { | |
677 | /* | |
678 | * duplicate of hole placement code above | |
679 | */ | |
680 | length = i - start; | |
681 | ||
682 | if (length > 0) { | |
683 | num_holes++; | |
684 | ||
685 | for (smallest = j = 0; j < XR_DA_LEAF_MAPSIZE; j++) { | |
686 | if (holes->hentries[j].size < | |
687 | holes->hentries[smallest].size) | |
688 | smallest = j; | |
689 | ||
690 | } | |
691 | if (length > holes->hentries[smallest].size) { | |
692 | holes->hentries[smallest].base = start; | |
693 | holes->hentries[smallest].size = length; | |
694 | } | |
695 | } | |
696 | } | |
697 | ||
698 | holes->lost_holes = MAX(num_holes - XR_DA_LEAF_MAPSIZE, 0); | |
699 | holes->num_holes = num_holes; | |
700 | ||
701 | return; | |
702 | } | |
703 | ||
704 | /* | |
705 | * returns 1 if the hole info doesn't match, 0 if it does | |
706 | */ | |
707 | /* ARGSUSED */ | |
708 | int | |
709 | compare_da_freemaps(xfs_mount_t *mp, da_hole_map_t *holemap, | |
710 | da_hole_map_t *block_hmap, int entries, | |
711 | xfs_ino_t ino, xfs_dablk_t da_bno) | |
712 | { | |
713 | int i, k, res, found; | |
714 | ||
715 | res = 0; | |
716 | ||
717 | /* | |
718 | * we chop holemap->lost_holes down to being two-valued | |
719 | * value (1 or 0) for the test because the filesystem | |
720 | * value is two-valued | |
721 | */ | |
722 | if ((holemap->lost_holes > 0 ? 1 : 0) != block_hmap->lost_holes) { | |
723 | if (verbose) { | |
724 | do_warn( | |
725 | "- derived hole value %d, saw %d, block %d, dir ino %llu\n", | |
726 | holemap->lost_holes, block_hmap->lost_holes, | |
727 | da_bno, ino); | |
728 | res = 1; | |
729 | } else | |
730 | return(1); | |
731 | } | |
732 | ||
733 | for (i = 0; i < entries; i++) { | |
734 | for (found = k = 0; k < entries; k++) { | |
735 | if (holemap->hentries[i].base == | |
736 | block_hmap->hentries[k].base | |
737 | && holemap->hentries[i].size == | |
738 | block_hmap->hentries[k].size) | |
739 | found = 1; | |
740 | } | |
741 | if (!found) { | |
742 | if (verbose) { | |
743 | do_warn( | |
744 | "- derived hole (base %d, size %d) in block %d, dir inode %llu not found\n", | |
745 | holemap->hentries[i].base, | |
746 | holemap->hentries[i].size, | |
747 | da_bno, ino); | |
748 | res = 1; | |
749 | } else | |
750 | return(1); | |
751 | } | |
752 | } | |
753 | ||
754 | return(res); | |
755 | } | |
756 | ||
757 | #if 0 | |
758 | void | |
759 | test(xfs_mount_t *mp) | |
760 | { | |
761 | int i = 0; | |
762 | da_hole_map_t holemap; | |
763 | ||
764 | init_da_freemap(dir_freemap); | |
765 | bzero(&holemap, sizeof(da_hole_map_t)); | |
766 | ||
767 | set_da_freemap(mp, dir_freemap, 0, 50); | |
768 | set_da_freemap(mp, dir_freemap, 100, 126); | |
769 | set_da_freemap(mp, dir_freemap, 126, 129); | |
770 | set_da_freemap(mp, dir_freemap, 130, 131); | |
771 | set_da_freemap(mp, dir_freemap, 150, 160); | |
772 | process_da_freemap(mp, dir_freemap, &holemap); | |
773 | ||
774 | return; | |
775 | } | |
776 | #endif | |
777 | ||
778 | ||
779 | /* | |
780 | * walk tree from root to the left-most leaf block reading in | |
781 | * blocks and setting up cursor. passes back file block number of the | |
782 | * left-most leaf block if successful (bno). returns 1 if successful, | |
783 | * 0 if unsuccessful. | |
784 | */ | |
785 | int | |
786 | traverse_int_dablock(xfs_mount_t *mp, | |
787 | da_bt_cursor_t *da_cursor, | |
788 | xfs_dablk_t *rbno, | |
789 | int whichfork) | |
790 | { | |
791 | xfs_dablk_t bno; | |
792 | int i; | |
793 | xfs_da_intnode_t *node; | |
794 | xfs_dfsbno_t fsbno; | |
795 | xfs_buf_t *bp; | |
796 | ||
797 | /* | |
798 | * traverse down left-side of tree until we hit the | |
799 | * left-most leaf block setting up the btree cursor along | |
800 | * the way. | |
801 | */ | |
802 | bno = 0; | |
803 | i = -1; | |
804 | node = NULL; | |
805 | da_cursor->active = 0; | |
806 | ||
807 | do { | |
808 | /* | |
809 | * read in each block along the way and set up cursor | |
810 | */ | |
811 | fsbno = blkmap_get(da_cursor->blkmap, bno); | |
812 | ||
813 | if (fsbno == NULLDFSBNO) | |
814 | goto error_out; | |
815 | ||
816 | bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno), | |
817 | XFS_FSB_TO_BB(mp, 1), 0); | |
818 | if (!bp) { | |
819 | if (whichfork == XFS_DATA_FORK) | |
820 | do_warn("can't read block %u (fsbno %llu) for " | |
821 | "directory inode %llu\n", | |
822 | bno, fsbno, da_cursor->ino); | |
823 | else | |
824 | do_warn("can't read block %u (fsbno %llu) for " | |
825 | "attrbute fork of inode %llu\n", | |
826 | bno, fsbno, da_cursor->ino); | |
827 | goto error_out; | |
828 | } | |
829 | ||
830 | node = (xfs_da_intnode_t *)XFS_BUF_PTR(bp); | |
831 | ||
832 | if (INT_GET(node->hdr.info.magic, ARCH_CONVERT) != XFS_DA_NODE_MAGIC) { | |
833 | do_warn("bad dir/attr magic number in inode %llu, file " | |
834 | "bno = %u, fsbno = %llu\n", da_cursor->ino, bno, fsbno); | |
835 | libxfs_putbuf(bp); | |
836 | goto error_out; | |
837 | } | |
838 | if (INT_GET(node->hdr.count, ARCH_CONVERT) > XFS_DA_NODE_ENTRIES(mp)) { | |
839 | do_warn("bad record count in inode %llu, count = %d, max = %d\n", | |
840 | da_cursor->ino, INT_GET(node->hdr.count, ARCH_CONVERT), | |
841 | XFS_DA_NODE_ENTRIES(mp)); | |
842 | libxfs_putbuf(bp); | |
843 | goto error_out; | |
844 | } | |
845 | ||
846 | /* | |
847 | * maintain level counter | |
848 | */ | |
849 | if (i == -1) | |
850 | i = da_cursor->active = INT_GET(node->hdr.level, ARCH_CONVERT); | |
851 | else { | |
852 | if (INT_GET(node->hdr.level, ARCH_CONVERT) == i - 1) { | |
853 | i--; | |
854 | } else { | |
855 | if (whichfork == XFS_DATA_FORK) | |
856 | do_warn("bad directory btree for directory " | |
857 | "inode %llu\n", da_cursor->ino); | |
858 | else | |
859 | do_warn("bad attribute fork btree for " | |
860 | "inode %llu\n", da_cursor->ino); | |
861 | libxfs_putbuf(bp); | |
862 | goto error_out; | |
863 | } | |
864 | } | |
865 | ||
866 | da_cursor->level[i].hashval = | |
867 | INT_GET(node->btree[0].hashval, ARCH_CONVERT); | |
868 | da_cursor->level[i].bp = bp; | |
869 | da_cursor->level[i].bno = bno; | |
870 | da_cursor->level[i].index = 0; | |
871 | #ifdef XR_DIR_TRACE | |
872 | da_cursor->level[i].n = XFS_BUF_TO_DA_INTNODE(bp); | |
873 | #endif | |
874 | ||
875 | /* | |
876 | * set up new bno for next level down | |
877 | */ | |
878 | bno = INT_GET(node->btree[0].before, ARCH_CONVERT); | |
879 | } while(node != NULL && i > 1); | |
880 | ||
881 | /* | |
882 | * now return block number and get out | |
883 | */ | |
884 | *rbno = da_cursor->level[0].bno = bno; | |
885 | return(1); | |
886 | ||
887 | error_out: | |
888 | while (i > 1 && i <= da_cursor->active) { | |
889 | libxfs_putbuf(da_cursor->level[i].bp); | |
890 | i++; | |
891 | } | |
892 | ||
893 | return(0); | |
894 | } | |
895 | ||
896 | /* | |
897 | * blow out buffer for this level and all the rest above as well | |
898 | * if error == 0, we are not expecting to encounter any unreleased | |
899 | * buffers (e.g. if we do, it's a mistake). if error == 1, we're | |
900 | * in an error-handling case so unreleased buffers may exist. | |
901 | */ | |
902 | void | |
903 | release_da_cursor_int(xfs_mount_t *mp, | |
904 | da_bt_cursor_t *cursor, | |
905 | int prev_level, | |
906 | int error) | |
907 | { | |
908 | int level = prev_level + 1; | |
909 | ||
910 | if (cursor->level[level].bp != NULL) { | |
911 | if (!error) { | |
912 | do_warn("release_da_cursor_int got unexpected non-null bp, " | |
913 | "dabno = %u\n", cursor->level[level].bno); | |
914 | } | |
915 | ASSERT(error != 0); | |
916 | ||
917 | libxfs_putbuf(cursor->level[level].bp); | |
918 | cursor->level[level].bp = NULL; | |
919 | } | |
920 | ||
921 | if (level < cursor->active) | |
922 | release_da_cursor_int(mp, cursor, level, error); | |
923 | ||
924 | return; | |
925 | } | |
926 | ||
927 | void | |
928 | release_da_cursor(xfs_mount_t *mp, | |
929 | da_bt_cursor_t *cursor, | |
930 | int prev_level) | |
931 | { | |
932 | release_da_cursor_int(mp, cursor, prev_level, 0); | |
933 | } | |
934 | ||
935 | void | |
936 | err_release_da_cursor(xfs_mount_t *mp, | |
937 | da_bt_cursor_t *cursor, | |
938 | int prev_level) | |
939 | { | |
940 | release_da_cursor_int(mp, cursor, prev_level, 1); | |
941 | } | |
942 | ||
943 | /* | |
944 | * like traverse_int_dablock only it does far less checking | |
945 | * and doesn't maintain the cursor. Just gets you to the | |
946 | * leftmost block in the directory. returns the fsbno | |
947 | * of that block if successful, NULLDFSBNO if not. | |
948 | */ | |
949 | xfs_dfsbno_t | |
950 | get_first_dblock_fsbno(xfs_mount_t *mp, | |
951 | xfs_ino_t ino, | |
952 | xfs_dinode_t *dino) | |
953 | { | |
954 | xfs_dablk_t bno; | |
955 | int i; | |
956 | xfs_da_intnode_t *node; | |
957 | xfs_dfsbno_t fsbno; | |
958 | xfs_buf_t *bp; | |
959 | ||
960 | /* | |
961 | * traverse down left-side of tree until we hit the | |
962 | * left-most leaf block setting up the btree cursor along | |
963 | * the way. | |
964 | */ | |
965 | bno = 0; | |
966 | i = -1; | |
967 | node = NULL; | |
968 | ||
969 | fsbno = get_bmapi(mp, dino, ino, bno, XFS_DATA_FORK); | |
970 | ||
971 | if (fsbno == NULLDFSBNO) { | |
972 | do_warn("bmap of block #%u of inode %llu failed\n", | |
973 | bno, ino); | |
974 | return(fsbno); | |
975 | } | |
976 | ||
977 | if (INT_GET(dino->di_core.di_size, ARCH_CONVERT) <= XFS_LBSIZE(mp)) | |
978 | return(fsbno); | |
979 | ||
980 | do { | |
981 | /* | |
982 | * walk down left side of btree, release buffers as you | |
983 | * go. if the root block is a leaf (single-level btree), | |
984 | * just return it. | |
985 | * | |
986 | */ | |
987 | ||
988 | bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno), | |
989 | XFS_FSB_TO_BB(mp, 1), 0); | |
990 | if (!bp) { | |
991 | do_warn("can't read block %u (fsbno %llu) for directory " | |
992 | "inode %llu\n", bno, fsbno, ino); | |
993 | return(NULLDFSBNO); | |
994 | } | |
995 | ||
996 | node = (xfs_da_intnode_t *)XFS_BUF_PTR(bp); | |
997 | ||
998 | if (INT_GET(node->hdr.info.magic, ARCH_CONVERT) != XFS_DA_NODE_MAGIC) { | |
999 | do_warn("bad dir/attr magic number in inode %llu, file " | |
1000 | "bno = %u, fsbno = %llu\n", ino, bno, fsbno); | |
1001 | libxfs_putbuf(bp); | |
1002 | return(NULLDFSBNO); | |
1003 | } | |
1004 | ||
1005 | if (i == -1) | |
1006 | i = INT_GET(node->hdr.level, ARCH_CONVERT); | |
1007 | bno = INT_GET(node->btree[0].before, ARCH_CONVERT); | |
1008 | ||
1009 | libxfs_putbuf(bp); | |
1010 | ||
1011 | fsbno = get_bmapi(mp, dino, ino, bno, XFS_DATA_FORK); | |
1012 | ||
1013 | if (fsbno == NULLDFSBNO) { | |
1014 | do_warn("bmap of block #%u of inode %llu failed\n", bno, ino); | |
1015 | return(NULLDFSBNO); | |
1016 | } | |
1017 | ||
1018 | i--; | |
1019 | } while(i > 0); | |
1020 | ||
1021 | return(fsbno); | |
1022 | } | |
1023 | ||
1024 | /* | |
1025 | * make sure that all entries in all blocks along the right side of | |
1026 | * of the tree are used and hashval's are consistent. level is the | |
1027 | * level of the descendent block. returns 0 if good (even if it had | |
1028 | * to be fixed up), and 1 if bad. The right edge of the tree is | |
1029 | * technically a block boundary. this routine should be used then | |
1030 | * instead of verify_da_path(). | |
1031 | */ | |
1032 | int | |
1033 | verify_final_da_path(xfs_mount_t *mp, | |
1034 | da_bt_cursor_t *cursor, | |
1035 | const int p_level) | |
1036 | { | |
1037 | xfs_da_intnode_t *node; | |
948ce18a | 1038 | xfs_dahash_t hashval; |
2bd0ea18 NS |
1039 | int bad = 0; |
1040 | int entry; | |
1041 | int this_level = p_level + 1; | |
1042 | ||
1043 | #ifdef XR_DIR_TRACE | |
1044 | fprintf(stderr, "in verify_final_da_path, this_level = %d\n", | |
1045 | this_level); | |
1046 | #endif | |
1047 | /* | |
1048 | * the index should point to the next "unprocessed" entry | |
1049 | * in the block which should be the final (rightmost) entry | |
1050 | */ | |
1051 | entry = cursor->level[this_level].index; | |
1052 | node = (xfs_da_intnode_t *)XFS_BUF_PTR(cursor->level[this_level].bp); | |
1053 | /* | |
1054 | * check internal block consistency on this level -- ensure | |
1055 | * that all entries are used, encountered and expected hashvals | |
1056 | * match, etc. | |
1057 | */ | |
1058 | if (entry != INT_GET(node->hdr.count, ARCH_CONVERT) - 1) { | |
1059 | do_warn("directory/attribute block used/count inconsistency - %d/%hu\n", | |
1060 | entry, INT_GET(node->hdr.count, ARCH_CONVERT)); | |
1061 | bad++; | |
1062 | } | |
1063 | /* | |
1064 | * hash values monotonically increasing ??? | |
1065 | */ | |
1066 | if (cursor->level[this_level].hashval >= | |
1067 | INT_GET(node->btree[entry].hashval, ARCH_CONVERT)) { | |
1068 | do_warn("directory/attribute block hashvalue inconsistency, " | |
1069 | "expected > %u / saw %u\n", cursor->level[this_level].hashval, | |
1070 | INT_GET(node->btree[entry].hashval, ARCH_CONVERT)); | |
1071 | bad++; | |
1072 | } | |
1073 | if (INT_GET(node->hdr.info.forw, ARCH_CONVERT) != 0) { | |
1074 | do_warn("bad directory/attribute forward block pointer, expected 0, " | |
1075 | "saw %u\n", INT_GET(node->hdr.info.forw, ARCH_CONVERT)); | |
1076 | bad++; | |
1077 | } | |
1078 | if (bad) { | |
1079 | do_warn("bad directory block in dir ino %llu\n", cursor->ino); | |
1080 | return(1); | |
1081 | } | |
1082 | /* | |
1083 | * keep track of greatest block # -- that gets | |
1084 | * us the length of the directory | |
1085 | */ | |
1086 | if (cursor->level[this_level].bno > cursor->greatest_bno) | |
1087 | cursor->greatest_bno = cursor->level[this_level].bno; | |
1088 | ||
1089 | /* | |
1090 | * ok, now check descendant block number against this level | |
1091 | */ | |
1092 | if (cursor->level[p_level].bno != | |
1093 | INT_GET(node->btree[entry].before, ARCH_CONVERT)) { | |
1094 | #ifdef XR_DIR_TRACE | |
1095 | fprintf(stderr, "bad directory btree pointer, child bno should be %d, " | |
1096 | "block bno is %d, hashval is %u\n", | |
1097 | INT_GET(node->btree[entry].before, ARCH_CONVERT), | |
1098 | cursor->level[p_level].bno, | |
1099 | cursor->level[p_level].hashval); | |
1100 | fprintf(stderr, "verify_final_da_path returns 1 (bad) #1a\n"); | |
1101 | #endif | |
1102 | return(1); | |
1103 | } | |
1104 | ||
1105 | if (cursor->level[p_level].hashval != | |
1106 | INT_GET(node->btree[entry].hashval, ARCH_CONVERT)) { | |
1107 | if (!no_modify) { | |
1108 | do_warn("correcting bad hashval in non-leaf dir/attr block\n"); | |
1109 | do_warn("\tin (level %d) in inode %llu.\n", | |
1110 | this_level, cursor->ino); | |
1111 | INT_SET(node->btree[entry].hashval, ARCH_CONVERT, | |
1112 | cursor->level[p_level].hashval); | |
1113 | cursor->level[this_level].dirty++; | |
1114 | } else { | |
1115 | do_warn("would correct bad hashval in non-leaf dir/attr " | |
1116 | "block\n\tin (level %d) in inode %llu.\n", | |
1117 | this_level, cursor->ino); | |
1118 | } | |
1119 | } | |
1120 | ||
948ce18a NS |
1121 | /* |
1122 | * Note: squirrel hashval away _before_ releasing the | |
1123 | * buffer, preventing a use-after-free problem. | |
1124 | */ | |
1125 | hashval = INT_GET(node->btree[entry].hashval, ARCH_CONVERT); | |
1126 | ||
2bd0ea18 NS |
1127 | /* |
1128 | * release/write buffer | |
1129 | */ | |
1130 | ASSERT(cursor->level[this_level].dirty == 0 || | |
27527004 | 1131 | (cursor->level[this_level].dirty && !no_modify)); |
2bd0ea18 NS |
1132 | |
1133 | if (cursor->level[this_level].dirty && !no_modify) | |
1134 | libxfs_writebuf(cursor->level[this_level].bp, 0); | |
1135 | else | |
1136 | libxfs_putbuf(cursor->level[this_level].bp); | |
1137 | ||
1138 | cursor->level[this_level].bp = NULL; | |
1139 | ||
1140 | /* | |
1141 | * bail out if this is the root block (top of tree) | |
1142 | */ | |
1143 | if (this_level >= cursor->active) { | |
1144 | #ifdef XR_DIR_TRACE | |
1145 | fprintf(stderr, "verify_final_da_path returns 0 (ok)\n"); | |
1146 | #endif | |
1147 | return(0); | |
1148 | } | |
1149 | /* | |
948ce18a | 1150 | * set hashvalue to correctly reflect the now-validated |
2bd0ea18 NS |
1151 | * last entry in this block and continue upwards validation |
1152 | */ | |
948ce18a | 1153 | cursor->level[this_level].hashval = hashval; |
2bd0ea18 NS |
1154 | return(verify_final_da_path(mp, cursor, this_level)); |
1155 | } | |
1156 | ||
1157 | /* | |
1158 | * Verifies the path from a descendant block up to the root. | |
1159 | * Should be called when the descendant level traversal hits | |
1160 | * a block boundary before crossing the boundary (reading in a new | |
1161 | * block). | |
1162 | * | |
1163 | * the directory/attr btrees work differently to the other fs btrees. | |
1164 | * each interior block contains records that are <hashval, bno> | |
1165 | * pairs. The bno is a file bno, not a filesystem bno. The last | |
1166 | * hashvalue in the block <bno> will be <hashval>. BUT unlike | |
1167 | * the freespace btrees, the *last* value in each block gets | |
1168 | * propagated up the tree instead of the first value in each block. | |
1169 | * that is, the interior records point to child blocks and the *greatest* | |
1170 | * hash value contained by the child block is the one the block above | |
1171 | * uses as the key for the child block. | |
1172 | * | |
1173 | * level is the level of the descendent block. returns 0 if good, | |
1174 | * and 1 if bad. The descendant block may be a leaf block. | |
1175 | * | |
1176 | * the invariant here is that the values in the cursor for the | |
1177 | * levels beneath this level (this_level) and the cursor index | |
1178 | * for this level *must* be valid. | |
1179 | * | |
1180 | * that is, the hashval/bno info is accurate for all | |
1181 | * DESCENDANTS and match what the node[index] information | |
1182 | * for the current index in the cursor for this level. | |
1183 | * | |
1184 | * the index values in the cursor for the descendant level | |
1185 | * are allowed to be off by one as they will reflect the | |
1186 | * next entry at those levels to be processed. | |
1187 | * | |
1188 | * the hashvalue for the current level can't be set until | |
1189 | * we hit the last entry in the block so, it's garbage | |
1190 | * until set by this routine. | |
1191 | * | |
1192 | * bno and bp for the current block/level are always valid | |
1193 | * since they have to be set so we can get a buffer for the | |
1194 | * block. | |
1195 | */ | |
1196 | int | |
1197 | verify_da_path(xfs_mount_t *mp, | |
1198 | da_bt_cursor_t *cursor, | |
1199 | const int p_level) | |
1200 | { | |
1201 | xfs_da_intnode_t *node; | |
1202 | xfs_da_intnode_t *newnode; | |
1203 | xfs_dfsbno_t fsbno; | |
1204 | xfs_dablk_t dabno; | |
1205 | xfs_buf_t *bp; | |
1206 | int bad; | |
1207 | int entry; | |
1208 | int this_level = p_level + 1; | |
1209 | ||
1210 | /* | |
1211 | * index is currently set to point to the entry that | |
1212 | * should be processed now in this level. | |
1213 | */ | |
1214 | entry = cursor->level[this_level].index; | |
1215 | node = (xfs_da_intnode_t *)XFS_BUF_PTR(cursor->level[this_level].bp); | |
1216 | ||
1217 | /* | |
1218 | * if this block is out of entries, validate this | |
1219 | * block and move on to the next block. | |
1220 | * and update cursor value for said level | |
1221 | */ | |
1222 | if (entry >= INT_GET(node->hdr.count, ARCH_CONVERT)) { | |
1223 | /* | |
1224 | * update the hash value for this level before | |
1225 | * validating it. bno value should be ok since | |
1226 | * it was set when the block was first read in. | |
1227 | */ | |
1228 | cursor->level[this_level].hashval = | |
1229 | INT_GET(node->btree[entry - 1].hashval, ARCH_CONVERT); | |
1230 | ||
1231 | /* | |
1232 | * keep track of greatest block # -- that gets | |
1233 | * us the length of the directory | |
1234 | */ | |
1235 | if (cursor->level[this_level].bno > cursor->greatest_bno) | |
1236 | cursor->greatest_bno = cursor->level[this_level].bno; | |
1237 | ||
1238 | /* | |
1239 | * validate the path for the current used-up block | |
1240 | * before we trash it | |
1241 | */ | |
1242 | if (verify_da_path(mp, cursor, this_level)) | |
1243 | return(1); | |
1244 | /* | |
1245 | * ok, now get the next buffer and check sibling pointers | |
1246 | */ | |
1247 | dabno = INT_GET(node->hdr.info.forw, ARCH_CONVERT); | |
1248 | ASSERT(dabno != 0); | |
1249 | fsbno = blkmap_get(cursor->blkmap, dabno); | |
1250 | ||
1251 | if (fsbno == NULLDFSBNO) { | |
1252 | do_warn("can't get map info for block %u of directory " | |
1253 | "inode %llu\n", dabno, cursor->ino); | |
1254 | return(1); | |
1255 | } | |
1256 | ||
1257 | bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno), | |
1258 | XFS_FSB_TO_BB(mp, 1), 0); | |
1259 | if (!bp) { | |
1260 | do_warn("can't read block %u (%llu) for directory inode %llu\n", | |
1261 | dabno, fsbno, cursor->ino); | |
1262 | return(1); | |
1263 | } | |
1264 | ||
1265 | newnode = (xfs_da_intnode_t *)XFS_BUF_PTR(bp); | |
1266 | /* | |
1267 | * verify magic number and back pointer, sanity-check | |
1268 | * entry count, verify level | |
1269 | */ | |
1270 | bad = 0; | |
1271 | if (INT_GET(newnode->hdr.info.magic, ARCH_CONVERT) != XFS_DA_NODE_MAGIC) { | |
1272 | do_warn("bad magic number %x in block %u (%llu) for directory " | |
1273 | "inode %llu\n", | |
1274 | INT_GET(newnode->hdr.info.magic, ARCH_CONVERT), | |
1275 | dabno, fsbno, cursor->ino); | |
1276 | bad++; | |
1277 | } | |
1278 | if (INT_GET(newnode->hdr.info.back, ARCH_CONVERT) != | |
1279 | cursor->level[this_level].bno) { | |
1280 | do_warn("bad back pointer in block %u (%llu) for directory " | |
1281 | "inode %llu\n", dabno, fsbno, cursor->ino); | |
1282 | bad++; | |
1283 | } | |
1284 | if (INT_GET(newnode->hdr.count, ARCH_CONVERT) > | |
1285 | XFS_DA_NODE_ENTRIES(mp)) { | |
1286 | do_warn("entry count %d too large in block %u (%llu) for " | |
1287 | "directory inode %llu\n", | |
1288 | INT_GET(newnode->hdr.count, ARCH_CONVERT), | |
1289 | dabno, fsbno, cursor->ino); | |
1290 | bad++; | |
1291 | } | |
1292 | if (INT_GET(newnode->hdr.level, ARCH_CONVERT) != this_level) { | |
1293 | do_warn("bad level %d in block %u (%llu) for directory inode " | |
1294 | "%llu\n", INT_GET(newnode->hdr.level, ARCH_CONVERT), | |
1295 | dabno, fsbno, cursor->ino); | |
1296 | bad++; | |
1297 | } | |
1298 | if (bad) { | |
1299 | #ifdef XR_DIR_TRACE | |
1300 | fprintf(stderr, "verify_da_path returns 1 (bad) #4\n"); | |
1301 | #endif | |
1302 | libxfs_putbuf(bp); | |
1303 | return(1); | |
1304 | } | |
1305 | /* | |
1306 | * update cursor, write out the *current* level if | |
1307 | * required. don't write out the descendant level | |
1308 | */ | |
1309 | ASSERT(cursor->level[this_level].dirty == 0 || | |
27527004 | 1310 | (cursor->level[this_level].dirty && !no_modify)); |
2bd0ea18 NS |
1311 | |
1312 | if (cursor->level[this_level].dirty && !no_modify) | |
1313 | libxfs_writebuf(cursor->level[this_level].bp, 0); | |
1314 | else | |
1315 | libxfs_putbuf(cursor->level[this_level].bp); | |
1316 | cursor->level[this_level].bp = bp; | |
1317 | cursor->level[this_level].dirty = 0; | |
1318 | cursor->level[this_level].bno = dabno; | |
1319 | cursor->level[this_level].hashval = | |
1320 | INT_GET(newnode->btree[0].hashval, ARCH_CONVERT); | |
1321 | #ifdef XR_DIR_TRACE | |
1322 | cursor->level[this_level].n = newnode; | |
1323 | #endif | |
1324 | node = newnode; | |
1325 | ||
1326 | entry = cursor->level[this_level].index = 0; | |
1327 | } | |
1328 | /* | |
1329 | * ditto for block numbers | |
1330 | */ | |
1331 | if (cursor->level[p_level].bno != | |
1332 | INT_GET(node->btree[entry].before, ARCH_CONVERT)) { | |
1333 | #ifdef XR_DIR_TRACE | |
1334 | fprintf(stderr, "bad directory btree pointer, child bno should be %d, " | |
1335 | "block bno is %d, hashval is %u\n", | |
1336 | INT_GET(node->btree[entry].before, ARCH_CONVERT), | |
1337 | cursor->level[p_level].bno, | |
1338 | cursor->level[p_level].hashval); | |
1339 | fprintf(stderr, "verify_da_path returns 1 (bad) #1a\n"); | |
1340 | #endif | |
1341 | return(1); | |
1342 | } | |
1343 | /* | |
1344 | * ok, now validate last hashvalue in the descendant | |
1345 | * block against the hashval in the current entry | |
1346 | */ | |
1347 | if (cursor->level[p_level].hashval != | |
1348 | INT_GET(node->btree[entry].hashval, ARCH_CONVERT)) { | |
1349 | if (!no_modify) { | |
1350 | do_warn("correcting bad hashval in interior dir/attr block\n"); | |
1351 | do_warn("\tin (level %d) in inode %llu.\n", | |
1352 | this_level, cursor->ino); | |
1353 | INT_SET(node->btree[entry].hashval, ARCH_CONVERT, | |
1354 | cursor->level[p_level].hashval); | |
1355 | cursor->level[this_level].dirty++; | |
1356 | } else { | |
1357 | do_warn("would correct bad hashval in interior dir/attr " | |
1358 | "block\n\tin (level %d) in inode %llu.\n", | |
1359 | this_level, cursor->ino); | |
1360 | } | |
1361 | } | |
1362 | /* | |
1363 | * increment index for this level to point to next entry | |
1364 | * (which should point to the next descendant block) | |
1365 | */ | |
1366 | cursor->level[this_level].index++; | |
1367 | #ifdef XR_DIR_TRACE | |
1368 | fprintf(stderr, "verify_da_path returns 0 (ok)\n"); | |
1369 | #endif | |
1370 | return(0); | |
1371 | } | |
1372 | ||
1373 | #if 0 | |
1374 | /* | |
1375 | * handles junking directory leaf block entries that have zero lengths | |
1376 | * buf_dirty is an in/out, set to 1 if the leaf was modified. | |
1377 | * we do NOT initialize it to zero if nothing happened because it | |
1378 | * may be already set by the caller. Assumes that the block | |
1379 | * has been compacted before calling this routine. | |
1380 | */ | |
1381 | void | |
1382 | junk_zerolen_dir_leaf_entries( | |
1383 | xfs_mount_t *mp, | |
1384 | xfs_dir_leafblock_t *leaf, | |
1385 | xfs_ino_t ino, | |
1386 | int *buf_dirty) | |
1387 | { | |
1388 | xfs_dir_leaf_entry_t *entry; | |
1389 | xfs_dir_leaf_name_t *namest; | |
1390 | xfs_dir_leaf_hdr_t *hdr; | |
1391 | xfs_dir_leaf_map_t *map; | |
1392 | xfs_ino_t tmp_ino; | |
1393 | int bytes; | |
1394 | int tmp_bytes; | |
1395 | int current_hole = 0; | |
1396 | int i; | |
1397 | int j; | |
1398 | int tmp; | |
1399 | int start; | |
1400 | int before; | |
1401 | int after; | |
1402 | int smallest; | |
1403 | int tablesize; | |
1404 | ||
1405 | entry = &leaf->entries[0]; | |
1406 | hdr = &leaf->hdr; | |
1407 | ||
1408 | /* | |
1409 | * we can convert the entries to one character entries | |
1410 | * as long as we have space. Once we run out, then | |
1411 | * we have to delete really delete (copy over) an entry. | |
1412 | * however, that frees up some space that we could use ... | |
1413 | * | |
1414 | * so the idea is, we'll use up space from all the holes, | |
1415 | * potentially leaving each hole too small to do any good. | |
1416 | * then if need to, we'll delete entries and use that space | |
1417 | * up from the top-most byte down. that may leave a 4th hole | |
1418 | * but we can represent that by correctly setting the value | |
1419 | * of firstused. that leaves any hole between the end of | |
1420 | * the entry list and firstused so it doesn't have to be | |
1421 | * recorded in the hole map. | |
1422 | */ | |
1423 | ||
1424 | for (bytes = i = 0; i < INT_GET(hdr->count, ARCH_CONVERT); entry++, i++) { | |
1425 | /* | |
1426 | * skip over entries that are good or already converted | |
1427 | */ | |
1428 | if (entry->namelen != 0) | |
1429 | continue; | |
1430 | ||
1431 | *buf_dirty = 1; | |
1432 | #if 0 | |
1433 | /* | |
1434 | * try and use up existing holes first until they get | |
1435 | * too small, then set bytes to the # of bytes between | |
1436 | * the current heap beginning and the last used byte | |
1437 | * in the entry table. | |
1438 | */ | |
1439 | if (bytes < sizeof(xfs_dir_leaf_name_t) && | |
1440 | current_hole < XFS_DIR_LEAF_MAPSIZE) { | |
1441 | /* | |
1442 | * skip over holes that are too small | |
1443 | */ | |
1444 | while (current_hole < XFS_DIR_LEAF_MAPSIZE && | |
1445 | INT_GET(hdr->freemap[current_hole].size, ARCH_CONVERT) < | |
1446 | sizeof(xfs_dir_leaf_name_t)) { | |
1447 | current_hole++; | |
1448 | } | |
1449 | ||
1450 | if (current_hole < XFS_DIR_LEAF_MAPSIZE) | |
1451 | bytes = INT_GET(hdr->freemap[current_hole].size, ARCH_CONVERT); | |
1452 | else | |
1453 | bytes = (int) INT_GET(hdr->firstused, ARCH_CONVERT) - | |
1454 | ((__psint_t) &leaf->entries[INT_GET(hdr->count, ARCH_CONVERT)] - | |
1455 | (__psint_t) leaf); | |
1456 | } | |
1457 | #endif | |
1458 | current_hole = 0; | |
1459 | ||
1460 | for (map = &hdr->freemap[0]; | |
1461 | current_hole < XFS_DIR_LEAF_MAPSIZE && | |
1462 | INT_GET(map->size, ARCH_CONVERT) < sizeof(xfs_dir_leaf_name_t); | |
1463 | map++) { | |
1464 | current_hole++; | |
1465 | } | |
1466 | ||
1467 | /* | |
1468 | * if we can use an existing hole, do it. otherwise, | |
1469 | * delete entries until the deletions create a big enough | |
1470 | * hole to convert another entry. then use up those bytes | |
1471 | * bytes until you run low. then delete entries again ... | |
1472 | */ | |
1473 | if (current_hole < XFS_DIR_LEAF_MAPSIZE) { | |
1474 | ASSERT(sizeof(xfs_dir_leaf_name_t) <= bytes); | |
1475 | ||
1476 | do_warn("marking bad entry in directory inode %llu\n", | |
1477 | ino); | |
1478 | ||
1479 | entry->namelen = 1; | |
1480 | INT_SET(entry->nameidx, ARCH_CONVERT, INT_GET(hdr->freemap[current_hole].base, ARCH_CONVERT) + | |
1481 | bytes - sizeof(xfs_dir_leaf_name_t)); | |
1482 | ||
1483 | namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT)); | |
1484 | tmp_ino = NULLFSINO; | |
1485 | XFS_DIR_SF_PUT_DIRINO_ARCH(&tmp_ino, &namest->inumber, ARCH_CONVERT); | |
1486 | namest->name[0] = '/'; | |
1487 | ||
1488 | if (INT_GET(entry->nameidx, ARCH_CONVERT) < INT_GET(hdr->firstused, ARCH_CONVERT)) | |
1489 | INT_SET(hdr->firstused, ARCH_CONVERT, INT_GET(entry->nameidx, ARCH_CONVERT)); | |
1490 | INT_MOD(hdr->freemap[current_hole].size, ARCH_CONVERT, -(sizeof(xfs_dir_leaf_name_t))); | |
1491 | INT_MOD(hdr->namebytes, ARCH_CONVERT, +1); | |
1492 | } else { | |
1493 | /* | |
1494 | * delete the table entry and try and account for the | |
1495 | * space in the holemap. don't have to update namebytes | |
1496 | * or firstused since we're not actually deleting any | |
1497 | * bytes from the heap. following code swiped from | |
1498 | * xfs_dir_leaf_remove() in xfs_dir_leaf.c | |
1499 | */ | |
1500 | INT_MOD(hdr->count, ARCH_CONVERT, -1); | |
1501 | do_warn( | |
1502 | "deleting zero length entry in directory inode %llu\n", | |
1503 | ino); | |
1504 | /* | |
1505 | * overwrite the bad entry unless it's the | |
1506 | * last entry in the list (highly unlikely). | |
1507 | * zero out the free'd bytes. | |
1508 | */ | |
1509 | if (INT_GET(hdr->count, ARCH_CONVERT) - i > 0) { | |
1510 | memmove(entry, entry + 1, (INT_GET(hdr->count, ARCH_CONVERT) - i) * | |
1511 | sizeof(xfs_dir_leaf_entry_t)); | |
1512 | } | |
1513 | bzero((void *) ((__psint_t) entry + | |
1514 | (INT_GET(leaf->hdr.count, ARCH_CONVERT) - i - 1) * | |
1515 | sizeof(xfs_dir_leaf_entry_t)), | |
1516 | sizeof(xfs_dir_leaf_entry_t)); | |
1517 | ||
1518 | start = (__psint_t) &leaf->entries[INT_GET(hdr->count, ARCH_CONVERT)] - | |
1519 | (__psint_t) &leaf; | |
1520 | tablesize = sizeof(xfs_dir_leaf_entry_t) * | |
1521 | (INT_GET(hdr->count, ARCH_CONVERT) + 1) + sizeof(xfs_dir_leaf_hdr_t); | |
1522 | map = &hdr->freemap[0]; | |
1523 | tmp = INT_GET(map->size, ARCH_CONVERT); | |
1524 | before = after = -1; | |
1525 | smallest = XFS_DIR_LEAF_MAPSIZE - 1; | |
1526 | for (j = 0; j < XFS_DIR_LEAF_MAPSIZE; map++, j++) { | |
1527 | ASSERT(INT_GET(map->base, ARCH_CONVERT) < XFS_LBSIZE(mp)); | |
1528 | ASSERT(INT_GET(map->size, ARCH_CONVERT) < XFS_LBSIZE(mp)); | |
1529 | if (INT_GET(map->base, ARCH_CONVERT) == tablesize) { | |
1530 | INT_MOD(map->base, ARCH_CONVERT, -(sizeof(xfs_dir_leaf_entry_t))); | |
1531 | INT_MOD(map->size, ARCH_CONVERT, sizeof(xfs_dir_leaf_entry_t)); | |
1532 | } | |
1533 | ||
1534 | if ((INT_GET(map->base, ARCH_CONVERT) + INT_GET(map->size, ARCH_CONVERT)) == start) { | |
1535 | before = j; | |
1536 | } else if (INT_GET(map->base, ARCH_CONVERT) == start + | |
1537 | sizeof(xfs_dir_leaf_entry_t)) { | |
1538 | after = j; | |
1539 | } else if (INT_GET(map->size, ARCH_CONVERT) < tmp) { | |
1540 | tmp = INT_GET(map->size, ARCH_CONVERT); | |
1541 | smallest = j; | |
1542 | } | |
1543 | } | |
1544 | ||
1545 | /* | |
1546 | * Coalesce adjacent freemap regions, | |
1547 | * or replace the smallest region. | |
1548 | */ | |
1549 | if ((before >= 0) || (after >= 0)) { | |
1550 | if ((before >= 0) && (after >= 0)) { | |
1551 | map = &hdr->freemap[before]; | |
1552 | INT_MOD(map->size, ARCH_CONVERT, sizeof(xfs_dir_leaf_entry_t)); | |
1553 | INT_MOD(map->size, ARCH_CONVERT, INT_GET(hdr->freemap[after].size, ARCH_CONVERT)); | |
1554 | INT_ZERO(hdr->freemap[after].base, ARCH_CONVERT); | |
1555 | INT_ZERO(hdr->freemap[after].size, ARCH_CONVERT); | |
1556 | } else if (before >= 0) { | |
1557 | map = &hdr->freemap[before]; | |
1558 | INT_MOD(map->size, ARCH_CONVERT, sizeof(xfs_dir_leaf_entry_t)); | |
1559 | } else { | |
1560 | map = &hdr->freemap[after]; | |
1561 | INT_SET(map->base, ARCH_CONVERT, start); | |
1562 | INT_MOD(map->size, ARCH_CONVERT, sizeof(xfs_dir_leaf_entry_t)); | |
1563 | } | |
1564 | } else { | |
1565 | /* | |
1566 | * Replace smallest region | |
1567 | * (if it is smaller than free'd entry) | |
1568 | */ | |
1569 | map = &hdr->freemap[smallest]; | |
1570 | if (INT_GET(map->size, ARCH_CONVERT) < sizeof(xfs_dir_leaf_entry_t)) { | |
1571 | INT_SET(map->base, ARCH_CONVERT, start); | |
1572 | INT_SET(map->size, ARCH_CONVERT, sizeof(xfs_dir_leaf_entry_t)); | |
1573 | } | |
1574 | /* | |
1575 | * mark as needing compaction | |
1576 | */ | |
1577 | hdr->holes = 1; | |
1578 | } | |
1579 | #if 0 | |
1580 | /* | |
1581 | * do we have to delete stuff or is there | |
1582 | * room for deletions? | |
1583 | */ | |
1584 | ASSERT(current_hole == XFS_DIR_LEAF_MAPSIZE); | |
1585 | ||
1586 | /* | |
1587 | * here, bytes == number of unused bytes from | |
1588 | * end of list to top (beginning) of heap | |
1589 | * (firstused). It's ok to leave extra | |
1590 | * unused bytes in that region because they | |
1591 | * wind up before firstused (which we reset | |
1592 | * appropriately | |
1593 | */ | |
1594 | if (bytes < sizeof(xfs_dir_leaf_name_t)) { | |
1595 | /* | |
1596 | * have to delete an entry because | |
1597 | * we have no room to convert it to | |
1598 | * a bad entry | |
1599 | */ | |
1600 | do_warn( | |
1601 | "deleting entry in directory inode %llu\n", | |
1602 | ino); | |
1603 | /* | |
1604 | * overwrite the bad entry unless it's the | |
1605 | * last entry in the list (highly unlikely). | |
1606 | */ | |
1607 | if (INT_GET(leaf->hdr.count, ARCH_CONVERT) - i - 1> 0) { | |
1608 | memmove(entry, entry + 1, | |
1609 | (INT_GET(leaf->hdr.count, ARCH_CONVERT) - i - 1) * | |
1610 | sizeof(xfs_dir_leaf_entry_t)); | |
1611 | } | |
1612 | bzero((void *) ((__psint_t) entry + | |
1613 | (INT_GET(leaf->hdr.count, ARCH_CONVERT) - i - 1) * | |
1614 | sizeof(xfs_dir_leaf_entry_t)), | |
1615 | sizeof(xfs_dir_leaf_entry_t)); | |
1616 | ||
1617 | /* | |
1618 | * bump up free byte count, drop other | |
1619 | * index vars since the table just | |
1620 | * shrank by one entry and we don't | |
1621 | * want to miss any as we walk the table | |
1622 | */ | |
1623 | bytes += sizeof(xfs_dir_leaf_entry_t); | |
1624 | INT_MOD(leaf->hdr.count, ARCH_CONVERT, -1); | |
1625 | entry--; | |
1626 | i--; | |
1627 | } else { | |
1628 | /* | |
1629 | * convert entry using the bytes in between | |
1630 | * the end of the entry table and the heap | |
1631 | */ | |
1632 | entry->namelen = 1; | |
1633 | INT_MOD(leaf->hdr.firstused, ARCH_CONVERT, -(sizeof(xfs_dir_leaf_name_t))); | |
1634 | INT_SET(entry->nameidx, ARCH_CONVERT, INT_GET(leaf->hdr.firstused, ARCH_CONVERT)); | |
1635 | ||
1636 | namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, | |
1637 | INT_GET(entry->nameidx, ARCH_CONVERT)); | |
1638 | tmp_ino = NULLFSINO; | |
1639 | XFS_DIR_SF_PUT_DIRINO_ARCH(&tmp_ino, | |
1640 | &namest->inumber, ARCH_CONVERT); | |
1641 | namest->name[0] = '/'; | |
1642 | ||
1643 | bytes -= sizeof(xfs_dir_leaf_entry_t); | |
1644 | } | |
1645 | #endif | |
1646 | } | |
1647 | } | |
1648 | ||
1649 | return; | |
1650 | } | |
1651 | #endif | |
1652 | ||
1653 | static char dirbuf[64 * 1024]; | |
1654 | ||
1655 | /* | |
1656 | * called by both node dir and leaf dir processing routines | |
1657 | * validates all contents *but* the sibling pointers (forw/back) | |
1658 | * and the magic number. | |
1659 | * | |
1660 | * returns 0 if the directory is ok or has been brought to the | |
1661 | * stage that it can be fixed up later (in phase 6), | |
1662 | * 1 if it has to be junked. | |
1663 | * | |
1664 | * Right now we fix a lot of things (TBD == to be deleted). | |
1665 | * | |
1666 | * incorrect . entries - inode # is corrected | |
1667 | * entries with mismatched hashvalue/name strings - hashvalue reset | |
1668 | * entries whose hashvalues are out-of-order - entry marked TBD | |
1669 | * .. entries with invalid inode numbers - entry marked TBD | |
1670 | * entries with invalid inode numbers - entry marked TBD | |
1671 | * multiple . entries - all but the first entry are marked TBD | |
1672 | * zero-length entries - entry is deleted | |
1673 | * entries with an out-of-bounds name index ptr - entry is deleted | |
1674 | * | |
1675 | * entries marked TBD have the first character of the name (which | |
1676 | * lives in the heap) have the first character in the name set | |
1677 | * to '/' -- an illegal value. | |
1678 | * | |
1679 | * entries deleted right here are deleted by blowing away the entry | |
1680 | * (but leaving the heap untouched). any space that was used | |
1681 | * by the deleted entry will be reclaimed by the block freespace | |
1682 | * (da_freemap) processing code. | |
1683 | * | |
1684 | * if two entries claim the same space in the heap (say, due to | |
1685 | * bad entry name index pointers), we lose the directory. We could | |
1686 | * try harder to fix this but it'll do for now. | |
1687 | */ | |
1688 | /* ARGSUSED */ | |
1689 | int | |
1690 | process_leaf_dir_block( | |
1691 | xfs_mount_t *mp, | |
1692 | xfs_dir_leafblock_t *leaf, | |
1693 | xfs_dablk_t da_bno, | |
1694 | xfs_ino_t ino, | |
1695 | xfs_dahash_t last_hashval, /* last hashval encountered */ | |
1696 | int ino_discovery, | |
1697 | blkmap_t *blkmap, | |
1698 | int *dot, | |
1699 | int *dotdot, | |
1700 | xfs_ino_t *parent, | |
1701 | int *buf_dirty, /* is buffer dirty? */ | |
1702 | xfs_dahash_t *next_hashval) /* greatest hashval in block */ | |
1703 | { | |
1704 | xfs_ino_t lino; | |
1705 | xfs_dir_leaf_entry_t *entry; | |
1706 | xfs_dir_leaf_entry_t *s_entry; | |
1707 | xfs_dir_leaf_entry_t *d_entry; | |
1708 | xfs_dir_leafblock_t *new_leaf; | |
1709 | char *first_byte; | |
1710 | xfs_dir_leaf_name_t *namest; | |
1711 | ino_tree_node_t *irec_p; | |
1712 | int num_entries; | |
1713 | xfs_dahash_t hashval; | |
1714 | int i; | |
1715 | int nm_illegal; | |
1716 | int bytes; | |
1717 | int start; | |
1718 | int stop; | |
1719 | int res = 0; | |
1720 | int ino_off; | |
1721 | int first_used; | |
1722 | int bytes_used; | |
1723 | int reset_holes; | |
1724 | int zero_len_entries; | |
1725 | char fname[MAXNAMELEN + 1]; | |
1726 | da_hole_map_t holemap; | |
1727 | da_hole_map_t bholemap; | |
1728 | #if 0 | |
1729 | unsigned char *dir_freemap; | |
1730 | #endif | |
1731 | ||
1732 | #ifdef XR_DIR_TRACE | |
1733 | fprintf(stderr, "\tprocess_leaf_dir_block - ino %llu\n", ino); | |
1734 | #endif | |
1735 | ||
1736 | /* | |
1737 | * clear static dir block freespace bitmap | |
1738 | */ | |
1739 | init_da_freemap(dir_freemap); | |
1740 | ||
1741 | #if 0 | |
1742 | /* | |
1743 | * XXX - alternatively, do this for parallel usage. | |
1744 | * set up block freespace map. head part of dir leaf block | |
1745 | * including all entries are packed so we can use sizeof | |
1746 | * and not worry about alignment. | |
1747 | */ | |
1748 | ||
1749 | if ((dir_freemap = alloc_da_freemap(mp)) == NULL) { | |
1750 | do_error("couldn't allocate directory block freemap\n"); | |
1751 | abort(); | |
1752 | } | |
1753 | #endif | |
1754 | ||
1755 | *buf_dirty = 0; | |
1756 | first_used = mp->m_sb.sb_blocksize; | |
1757 | zero_len_entries = 0; | |
1758 | bytes_used = 0; | |
1759 | ||
1760 | i = stop = sizeof(xfs_dir_leaf_hdr_t); | |
1761 | if (set_da_freemap(mp, dir_freemap, 0, stop)) { | |
1762 | do_warn( | |
1763 | "directory block header conflicts with used space in directory inode %llu\n", | |
1764 | ino); | |
1765 | return(1); | |
1766 | } | |
1767 | ||
1768 | /* | |
1769 | * verify structure: monotonically increasing hash value for | |
1770 | * all leaf entries, indexes for all entries must be within | |
1771 | * this fs block (trivially true for 64K blocks). also track | |
1772 | * used space so we can check the freespace map. check for | |
1773 | * zero-length entries. for now, if anything's wrong, we | |
1774 | * junk the directory and we'll pick up no-longer referenced | |
1775 | * inodes on a later pass. | |
1776 | */ | |
1777 | for (i = 0, entry = &leaf->entries[0]; | |
1778 | i < INT_GET(leaf->hdr.count, ARCH_CONVERT); | |
1779 | i++, entry++) { | |
1780 | /* | |
1781 | * check that the name index isn't out of bounds | |
1782 | * if it is, delete the entry since we can't | |
1783 | * grab the inode #. | |
1784 | */ | |
1785 | if (INT_GET(entry->nameidx, ARCH_CONVERT) >= mp->m_sb.sb_blocksize) { | |
1786 | if (!no_modify) { | |
1787 | *buf_dirty = 1; | |
1788 | ||
1789 | if (INT_GET(leaf->hdr.count, ARCH_CONVERT) > 1) { | |
1790 | do_warn( | |
1791 | "nameidx %d for entry #%d, bno %d, ino %llu > fs blocksize, deleting entry\n", | |
1792 | INT_GET(entry->nameidx, ARCH_CONVERT), i, da_bno, ino); | |
1793 | ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT) > i); | |
1794 | ||
1795 | bytes = (INT_GET(leaf->hdr.count, ARCH_CONVERT) - i) * | |
1796 | sizeof(xfs_dir_leaf_entry_t); | |
1797 | ||
1798 | /* | |
1799 | * compress table unless we're | |
1800 | * only dealing with 1 entry | |
1801 | * (the last one) in which case | |
1802 | * just zero it. | |
1803 | */ | |
1804 | if (bytes > | |
1805 | sizeof(xfs_dir_leaf_entry_t)) { | |
1806 | memmove(entry, entry + 1, | |
1807 | bytes); | |
1808 | bzero((void *) | |
1809 | ((__psint_t) entry + bytes), | |
1810 | sizeof(xfs_dir_leaf_entry_t)); | |
1811 | } else { | |
1812 | bzero(entry, | |
1813 | sizeof(xfs_dir_leaf_entry_t)); | |
1814 | } | |
1815 | ||
1816 | /* | |
1817 | * sync vars to match smaller table. | |
1818 | * don't have to worry about freespace | |
1819 | * map since we haven't set it for | |
1820 | * this entry yet. | |
1821 | */ | |
1822 | INT_MOD(leaf->hdr.count, ARCH_CONVERT, -1); | |
1823 | i--; | |
1824 | entry--; | |
1825 | } else { | |
1826 | do_warn( | |
1827 | "nameidx %d, entry #%d, bno %d, ino %llu > fs blocksize, marking entry bad\n", | |
1828 | INT_GET(entry->nameidx, ARCH_CONVERT), i, da_bno, ino); | |
1829 | INT_SET(entry->nameidx, ARCH_CONVERT, mp->m_sb.sb_blocksize - | |
1830 | sizeof(xfs_dir_leaf_name_t)); | |
1831 | namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, | |
1832 | INT_GET(entry->nameidx, ARCH_CONVERT)); | |
1833 | lino = NULLFSINO; | |
1834 | XFS_DIR_SF_PUT_DIRINO_ARCH(&lino, | |
1835 | &namest->inumber, ARCH_CONVERT); | |
1836 | namest->name[0] = '/'; | |
1837 | } | |
1838 | } else { | |
1839 | do_warn( | |
1840 | "nameidx %d, entry #%d, bno %d, ino %llu > fs blocksize, would delete entry\n", | |
1841 | INT_GET(entry->nameidx, ARCH_CONVERT), i, da_bno, ino); | |
1842 | } | |
1843 | continue; | |
1844 | } | |
1845 | /* | |
1846 | * inode processing -- make sure the inode | |
1847 | * is in our tree or we add it to the uncertain | |
1848 | * list if the inode # is valid. if namelen is 0, | |
1849 | * we can still try for the inode as long as nameidx | |
1850 | * is ok. | |
1851 | */ | |
1852 | namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT)); | |
1853 | XFS_DIR_SF_GET_DIRINO_ARCH(&namest->inumber, &lino, ARCH_CONVERT); | |
1854 | ||
1855 | /* | |
1856 | * we may have to blow out an entry because of bad | |
1857 | * inode numbers. do NOT touch the name until after | |
1858 | * we've computed the hashvalue and done a namecheck() | |
1859 | * on the name. | |
1860 | */ | |
1861 | if (!ino_discovery && lino == NULLFSINO) { | |
1862 | /* | |
1863 | * don't do a damned thing. We already | |
1864 | * found this (or did it ourselves) during | |
1865 | * phase 3. | |
1866 | */ | |
1867 | } else if (verify_inum(mp, lino)) { | |
1868 | /* | |
1869 | * bad inode number. clear the inode | |
1870 | * number and the entry will get removed | |
1871 | * later. We don't trash the directory | |
1872 | * since it's still structurally intact. | |
1873 | */ | |
1874 | do_warn( | |
1875 | "invalid ino number %llu in dir ino %llu, entry #%d, bno %d\n", | |
1876 | lino, ino, i, da_bno); | |
1877 | if (!no_modify) { | |
1878 | do_warn( | |
1879 | "\tclearing ino number in entry %d...\n", i); | |
1880 | ||
1881 | lino = NULLFSINO; | |
1882 | XFS_DIR_SF_PUT_DIRINO_ARCH(&lino, &namest->inumber, ARCH_CONVERT); | |
1883 | *buf_dirty = 1; | |
1884 | } else { | |
1885 | do_warn( | |
1886 | "\twould clear ino number in entry %d...\n", i); | |
1887 | } | |
1888 | } else if (lino == mp->m_sb.sb_rbmino) { | |
1889 | do_warn( | |
1890 | "entry #%d, bno %d in directory %llu references realtime bitmap inode %llu\n", | |
1891 | i, da_bno, ino, lino); | |
1892 | if (!no_modify) { | |
1893 | do_warn( | |
1894 | "\tclearing ino number in entry %d...\n", i); | |
1895 | ||
1896 | lino = NULLFSINO; | |
1897 | XFS_DIR_SF_PUT_DIRINO_ARCH(&lino, &namest->inumber, ARCH_CONVERT); | |
1898 | *buf_dirty = 1; | |
1899 | } else { | |
1900 | do_warn( | |
1901 | "\twould clear ino number in entry %d...\n", i); | |
1902 | } | |
1903 | } else if (lino == mp->m_sb.sb_rsumino) { | |
1904 | do_warn( | |
1905 | "entry #%d, bno %d in directory %llu references realtime summary inode %llu\n", | |
1906 | i, da_bno, ino, lino); | |
1907 | if (!no_modify) { | |
1908 | do_warn( | |
1909 | "\tclearing ino number in entry %d...\n", i); | |
1910 | ||
1911 | lino = NULLFSINO; | |
1912 | XFS_DIR_SF_PUT_DIRINO_ARCH(&lino, &namest->inumber, ARCH_CONVERT); | |
1913 | *buf_dirty = 1; | |
1914 | } else { | |
1915 | do_warn( | |
1916 | "\twould clear ino number in entry %d...\n", i); | |
1917 | } | |
1918 | } else if (lino == mp->m_sb.sb_uquotino) { | |
1919 | do_warn( | |
1920 | "entry #%d, bno %d in directory %llu references user quota inode %llu\n", | |
1921 | i, da_bno, ino, lino); | |
1922 | if (!no_modify) { | |
1923 | do_warn( | |
1924 | "\tclearing ino number in entry %d...\n", i); | |
1925 | ||
1926 | lino = NULLFSINO; | |
1927 | XFS_DIR_SF_PUT_DIRINO_ARCH(&lino, &namest->inumber, ARCH_CONVERT); | |
1928 | *buf_dirty = 1; | |
1929 | } else { | |
1930 | do_warn( | |
1931 | "\twould clear ino number in entry %d...\n", i); | |
1932 | } | |
b36eef04 | 1933 | } else if (lino == mp->m_sb.sb_gquotino) { |
2bd0ea18 | 1934 | do_warn( |
b36eef04 | 1935 | "entry #%d, bno %d in directory %llu references group quota inode %llu\n", |
2bd0ea18 NS |
1936 | i, da_bno, ino, lino); |
1937 | if (!no_modify) { | |
1938 | do_warn( | |
1939 | "\tclearing ino number in entry %d...\n", i); | |
1940 | ||
1941 | lino = NULLFSINO; | |
1942 | XFS_DIR_SF_PUT_DIRINO_ARCH(&lino, &namest->inumber, ARCH_CONVERT); | |
1943 | *buf_dirty = 1; | |
1944 | } else { | |
1945 | do_warn( | |
1946 | "\twould clear ino number in entry %d...\n", i); | |
1947 | } | |
1948 | } else if (lino == old_orphanage_ino) { | |
1949 | /* | |
1950 | * do nothing, silently ignore it, entry has | |
1951 | * already been marked TBD since old_orphanage_ino | |
1952 | * is set non-zero. | |
1953 | */ | |
1954 | } else if ((irec_p = find_inode_rec( | |
1955 | XFS_INO_TO_AGNO(mp, lino), | |
1956 | XFS_INO_TO_AGINO(mp, lino))) != NULL) { | |
1957 | /* | |
1958 | * inode recs should have only confirmed | |
1959 | * inodes in them | |
1960 | */ | |
1961 | ino_off = XFS_INO_TO_AGINO(mp, lino) - | |
1962 | irec_p->ino_startnum; | |
1963 | ASSERT(is_inode_confirmed(irec_p, ino_off)); | |
1964 | /* | |
1965 | * if inode is marked free and we're in inode | |
1966 | * discovery mode, leave the entry alone for now. | |
1967 | * if the inode turns out to be used, we'll figure | |
1968 | * that out when we scan it. If the inode really | |
1969 | * is free, we'll hit this code again in phase 4 | |
1970 | * after we've finished inode discovery and blow | |
1971 | * out the entry then. | |
1972 | */ | |
1973 | if (!ino_discovery && is_inode_free(irec_p, ino_off)) { | |
1974 | if (!no_modify) { | |
1975 | do_warn( | |
1976 | "entry references free inode %llu in directory %llu, will clear entry\n", | |
1977 | lino, ino); | |
1978 | lino = NULLFSINO; | |
1979 | XFS_DIR_SF_PUT_DIRINO_ARCH(&lino, | |
1980 | &namest->inumber, ARCH_CONVERT); | |
1981 | *buf_dirty = 1; | |
1982 | } else { | |
1983 | do_warn( | |
1984 | "entry references free inode %llu in directory %llu, would clear entry\n", | |
1985 | lino, ino); | |
1986 | } | |
1987 | } | |
1988 | } else if (ino_discovery) { | |
1989 | add_inode_uncertain(mp, lino, 0); | |
1990 | } else { | |
1991 | do_warn( | |
1992 | "bad ino number %llu in dir ino %llu, entry #%d, bno %d\n", | |
1993 | lino, ino, i, da_bno); | |
1994 | if (!no_modify) { | |
1995 | do_warn("clearing inode number...\n"); | |
1996 | lino = NULLFSINO; | |
1997 | XFS_DIR_SF_PUT_DIRINO_ARCH(&lino, &namest->inumber, ARCH_CONVERT); | |
1998 | *buf_dirty = 1; | |
1999 | } else { | |
2000 | do_warn("would clear inode number...\n"); | |
2001 | } | |
2002 | } | |
2003 | /* | |
2004 | * if we have a zero-length entry, trash it. | |
2005 | * we may lose the inode (chunk) if we don't | |
2006 | * finish the repair successfully and the inode | |
2007 | * isn't mentioned anywhere else (like in the inode | |
2008 | * tree) but the alternative is to risk losing the | |
2009 | * entire directory by trying to use the next byte | |
2010 | * to turn the entry into a 1-char entry. That's | |
2011 | * probably a safe bet but if it didn't work, we'd | |
2012 | * lose the entire directory the way we currently do | |
2013 | * things. (Maybe we should change that later :-). | |
2014 | */ | |
2015 | if (entry->namelen == 0) { | |
2016 | *buf_dirty = 1; | |
2017 | ||
2018 | if (INT_GET(leaf->hdr.count, ARCH_CONVERT) > 1) { | |
2019 | do_warn( | |
2020 | "entry #%d, dir inode %llu, has zero-len name, deleting entry\n", | |
2021 | i, ino); | |
2022 | ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT) > i); | |
2023 | ||
2024 | bytes = (INT_GET(leaf->hdr.count, ARCH_CONVERT) - i) * | |
2025 | sizeof(xfs_dir_leaf_entry_t); | |
2026 | ||
2027 | /* | |
2028 | * compress table unless we're | |
2029 | * only dealing with 1 entry | |
2030 | * (the last one) in which case | |
2031 | * just zero it. | |
2032 | */ | |
2033 | if (bytes > sizeof(xfs_dir_leaf_entry_t)) { | |
2034 | memmove(entry, entry + 1, | |
2035 | bytes); | |
2036 | bzero((void *) | |
2037 | ((__psint_t) entry + bytes), | |
2038 | sizeof(xfs_dir_leaf_entry_t)); | |
2039 | } else { | |
2040 | bzero(entry, | |
2041 | sizeof(xfs_dir_leaf_entry_t)); | |
2042 | } | |
2043 | ||
2044 | /* | |
2045 | * sync vars to match smaller table. | |
2046 | * don't have to worry about freespace | |
2047 | * map since we haven't set it for | |
2048 | * this entry yet. | |
2049 | */ | |
2050 | INT_MOD(leaf->hdr.count, ARCH_CONVERT, -1); | |
2051 | i--; | |
2052 | entry--; | |
2053 | } else { | |
2054 | /* | |
2055 | * if it's the only entry, preserve the | |
2056 | * inode number for now | |
2057 | */ | |
2058 | do_warn( | |
2059 | "entry #%d, dir inode %llu, has zero-len name, marking entry bad\n", | |
2060 | i, ino); | |
2061 | INT_SET(entry->nameidx, ARCH_CONVERT, mp->m_sb.sb_blocksize - | |
2062 | sizeof(xfs_dir_leaf_name_t)); | |
2063 | namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, | |
2064 | INT_GET(entry->nameidx, ARCH_CONVERT)); | |
2065 | XFS_DIR_SF_PUT_DIRINO_ARCH(&lino, &namest->inumber, ARCH_CONVERT); | |
2066 | namest->name[0] = '/'; | |
2067 | } | |
2068 | } else if (INT_GET(entry->nameidx, ARCH_CONVERT) + entry->namelen > XFS_LBSIZE(mp)) { | |
2069 | do_warn( | |
2070 | "bad size, entry #%d in dir inode %llu, block %u -- entry overflows block\n", | |
2071 | i, ino, da_bno); | |
2072 | ||
2073 | return(1); | |
2074 | } | |
2075 | ||
2076 | start = (__psint_t)&leaf->entries[i] - (__psint_t)leaf;; | |
2077 | stop = start + sizeof(xfs_dir_leaf_entry_t); | |
2078 | ||
2079 | if (set_da_freemap(mp, dir_freemap, start, stop)) { | |
2080 | do_warn( | |
2081 | "dir entry slot %d in block %u conflicts with used space in dir inode %llu\n", | |
2082 | i, da_bno, ino); | |
2083 | return(1); | |
2084 | } | |
2085 | ||
2086 | /* | |
2087 | * check if the name is legal. if so, then | |
2088 | * check that the name and hashvalues match. | |
2089 | * | |
2090 | * if the name is illegal, we don't check the | |
2091 | * hashvalue computed from it. we just make | |
2092 | * sure that the hashvalue in the entry is | |
2093 | * monotonically increasing wrt to the previous | |
2094 | * entry. | |
2095 | * | |
2096 | * Note that we do NOT have to check the length | |
2097 | * because the length is stored in a one-byte | |
2098 | * unsigned int which max's out at MAXNAMELEN | |
2099 | * making it impossible for the stored length | |
2100 | * value to be out of range. | |
2101 | */ | |
2102 | bcopy(namest->name, fname, entry->namelen); | |
2103 | fname[entry->namelen] = '\0'; | |
2104 | hashval = libxfs_da_hashname(fname, entry->namelen); | |
2105 | ||
2106 | /* | |
2107 | * only complain about illegal names in phase 3 (when | |
2108 | * inode discovery is turned on). Otherwise, we'd complain | |
2109 | * a lot during phase 4. If the name is illegal, leave | |
2110 | * the hash value in that entry alone. | |
2111 | */ | |
2112 | nm_illegal = namecheck(fname, entry->namelen); | |
2113 | ||
2114 | if (ino_discovery && nm_illegal) { | |
2115 | /* | |
2116 | * junk the entry, illegal name | |
2117 | */ | |
2118 | if (!no_modify) { | |
2119 | do_warn( | |
2120 | "illegal name \"%s\" in directory inode %llu, entry will be cleared\n", | |
2121 | fname, ino); | |
2122 | namest->name[0] = '/'; | |
2123 | *buf_dirty = 1; | |
2124 | } else { | |
2125 | do_warn( | |
2126 | "illegal name \"%s\" in directory inode %llu, entry would be cleared\n", | |
2127 | fname, ino); | |
2128 | } | |
2129 | } else if (!nm_illegal && INT_GET(entry->hashval, ARCH_CONVERT) != hashval) { | |
2130 | /* | |
2131 | * try resetting the hashvalue to the correct | |
2132 | * value for the string, if the string has been | |
2133 | * corrupted, too, that will get picked up next | |
2134 | */ | |
2135 | do_warn("\tmismatched hash value for entry \"%s\"\n", | |
2136 | fname); | |
2137 | if (!no_modify) { | |
2138 | do_warn( | |
2139 | "\t\tin directory inode %llu. resetting hash value.\n", | |
2140 | ino); | |
2141 | INT_SET(entry->hashval, ARCH_CONVERT, hashval); | |
2142 | *buf_dirty = 1; | |
2143 | } else { | |
2144 | do_warn( | |
2145 | "\t\tin directory inode %llu. would reset hash value.\n", | |
2146 | ino); | |
2147 | } | |
2148 | } | |
2149 | ||
2150 | /* | |
2151 | * now we can mark entries with NULLFSINO's bad | |
2152 | */ | |
2153 | if (!no_modify && lino == NULLFSINO) { | |
2154 | namest->name[0] = '/'; | |
2155 | *buf_dirty = 1; | |
2156 | } | |
2157 | ||
2158 | /* | |
2159 | * regardless of whether the entry has or hasn't been | |
2160 | * marked for deletion, the hash value ordering must | |
2161 | * be maintained. | |
2162 | */ | |
2163 | if (INT_GET(entry->hashval, ARCH_CONVERT) < last_hashval) { | |
2164 | /* | |
2165 | * blow out the entry -- set hashval to sane value | |
2166 | * and set the first character in the string to | |
2167 | * the illegal value '/'. Reset the hash value | |
2168 | * to the last hashvalue so that verify_da_path | |
2169 | * will fix up the interior pointers correctly. | |
2170 | * the entry will be deleted later (by routines | |
2171 | * that need only the entry #). We keep the | |
2172 | * inode number in the entry so we can attach | |
2173 | * the inode to the orphanage later. | |
2174 | */ | |
2175 | do_warn("\tbad hash ordering for entry \"%s\"\n", | |
2176 | fname); | |
2177 | if (!no_modify) { | |
2178 | do_warn( | |
2179 | "\t\tin directory inode %llu. will clear entry\n", | |
2180 | ino); | |
2181 | INT_SET(entry->hashval, ARCH_CONVERT, last_hashval); | |
2182 | namest->name[0] = '/'; | |
2183 | *buf_dirty = 1; | |
2184 | } else { | |
2185 | do_warn( | |
2186 | "\t\tin directory inode %llu. would clear entry\n", | |
2187 | ino); | |
2188 | } | |
2189 | } | |
2190 | ||
2191 | *next_hashval = last_hashval = INT_GET(entry->hashval, ARCH_CONVERT); | |
2192 | ||
2193 | /* | |
2194 | * if heap data conflicts with something, | |
2195 | * blow it out and skip the rest of the loop | |
2196 | */ | |
2197 | if (set_da_freemap(mp, dir_freemap, INT_GET(entry->nameidx, ARCH_CONVERT), | |
2198 | INT_GET(entry->nameidx, ARCH_CONVERT) + sizeof(xfs_dir_leaf_name_t) + | |
2199 | entry->namelen - 1)) { | |
2200 | do_warn( | |
2201 | "name \"%s\" (block %u, slot %d) conflicts with used space in dir inode %llu\n", | |
2202 | fname, da_bno, i, ino); | |
2203 | if (!no_modify) { | |
2204 | entry->namelen = 0; | |
2205 | *buf_dirty = 1; | |
2206 | ||
2207 | do_warn( | |
2208 | "will clear entry \"%s\" (#%d) in directory inode %llu\n", | |
2209 | fname, i, ino); | |
2210 | } else { | |
2211 | do_warn( | |
2212 | "would clear entry \"%s\" (#%d)in directory inode %llu\n", | |
2213 | fname, i, ino); | |
2214 | } | |
2215 | continue; | |
2216 | } | |
2217 | ||
2218 | /* | |
2219 | * keep track of heap stats (first byte used, total bytes used) | |
2220 | */ | |
2221 | if (INT_GET(entry->nameidx, ARCH_CONVERT) < first_used) | |
2222 | first_used = INT_GET(entry->nameidx, ARCH_CONVERT); | |
2223 | bytes_used += entry->namelen; | |
2224 | ||
2225 | /* | |
2226 | * special . or .. entry processing | |
2227 | */ | |
2228 | if (entry->namelen == 2 && namest->name[0] == '.' && | |
2229 | namest->name[1] == '.') { | |
2230 | /* | |
2231 | * the '..' case | |
2232 | */ | |
2233 | if (!*dotdot) { | |
2234 | (*dotdot)++; | |
2235 | *parent = lino; | |
2236 | #ifdef XR_DIR_TRACE | |
2237 | fprintf(stderr, "process_leaf_dir_block found .. entry (parent) = %llu\n", lino); | |
2238 | #endif | |
2239 | /* | |
2240 | * what if .. == .? legal only in | |
2241 | * the root inode. blow out entry | |
2242 | * and set parent to NULLFSINO otherwise. | |
2243 | */ | |
2244 | if (ino == lino && | |
2245 | ino != mp->m_sb.sb_rootino) { | |
2246 | *parent = NULLFSINO; | |
2247 | do_warn( | |
2248 | "bad .. entry in dir ino %llu, points to self", | |
2249 | ino); | |
2250 | if (!no_modify) { | |
2251 | do_warn("will clear entry\n"); | |
2252 | ||
2253 | namest->name[0] = '/'; | |
2254 | *buf_dirty = 1; | |
2255 | } else { | |
2256 | do_warn("would clear entry\n"); | |
2257 | } | |
2258 | } else if (ino != lino && | |
2259 | ino == mp->m_sb.sb_rootino) { | |
2260 | /* | |
2261 | * we have to make sure that . == .. | |
2262 | * in the root inode | |
2263 | */ | |
2264 | if (!no_modify) { | |
2265 | do_warn( | |
2266 | "correcting .. entry in root inode %llu, was %llu\n", | |
2267 | ino, *parent); | |
2268 | XFS_DIR_SF_PUT_DIRINO_ARCH( | |
2269 | &ino, | |
2270 | &namest->inumber, ARCH_CONVERT); | |
2271 | *buf_dirty = 1; | |
2272 | } else { | |
2273 | do_warn( | |
2274 | "bad .. entry (%llu) in root inode %llu should be %llu\n", | |
2275 | *parent, | |
2276 | ino, ino); | |
2277 | } | |
2278 | } | |
2279 | } else { | |
2280 | /* | |
2281 | * can't fix the directory unless we know | |
2282 | * which .. entry is the right one. Both | |
2283 | * have valid inode numbers, match the hash | |
2284 | * value and the hash values are ordered | |
2285 | * properly or we wouldn't be here. So | |
2286 | * since both seem equally valid, trash | |
2287 | * this one. | |
2288 | */ | |
2289 | if (!no_modify) { | |
2290 | do_warn( | |
2291 | "multiple .. entries in directory inode %llu, will clear second entry\n", | |
2292 | ino); | |
2293 | namest->name[0] = '/'; | |
2294 | *buf_dirty = 1; | |
2295 | } else { | |
2296 | do_warn( | |
2297 | "multiple .. entries in directory inode %llu, would clear second entry\n", | |
2298 | ino); | |
2299 | } | |
2300 | } | |
2301 | } else if (entry->namelen == 1 && namest->name[0] == '.') { | |
2302 | /* | |
2303 | * the '.' case | |
2304 | */ | |
2305 | if (!*dot) { | |
2306 | (*dot)++; | |
2307 | if (lino != ino) { | |
2308 | if (!no_modify) { | |
2309 | do_warn( | |
2310 | ". in directory inode %llu has wrong value (%llu), fixing entry...\n", | |
2311 | ino, lino); | |
2312 | XFS_DIR_SF_PUT_DIRINO_ARCH(&ino, | |
2313 | &namest->inumber, ARCH_CONVERT); | |
2314 | *buf_dirty = 1; | |
2315 | } else { | |
2316 | do_warn( | |
2317 | ". in directory inode %llu has wrong value (%llu)\n", | |
2318 | ino, lino); | |
2319 | } | |
2320 | } | |
2321 | } else { | |
2322 | do_warn( | |
2323 | "multiple . entries in directory inode %llu\n", | |
2324 | ino); | |
2325 | /* | |
2326 | * mark entry as to be junked. | |
2327 | */ | |
2328 | if (!no_modify) { | |
2329 | do_warn( | |
2330 | "will clear one . entry in directory inode %llu\n", | |
2331 | ino); | |
2332 | namest->name[0] = '/'; | |
2333 | *buf_dirty = 1; | |
2334 | } else { | |
2335 | do_warn( | |
2336 | "would clear one . entry in directory inode %llu\n", | |
2337 | ino); | |
2338 | } | |
2339 | } | |
2340 | } else { | |
2341 | /* | |
2342 | * all the rest -- make sure only . references self | |
2343 | */ | |
2344 | if (lino == ino) { | |
2345 | do_warn( | |
2346 | "entry \"%s\" in directory inode %llu points to self, ", | |
2347 | fname, ino); | |
2348 | if (!no_modify) { | |
2349 | do_warn("will clear entry\n"); | |
2350 | namest->name[0] = '/'; | |
2351 | *buf_dirty = 1; | |
2352 | } else { | |
2353 | do_warn("would clear entry\n"); | |
2354 | } | |
2355 | } | |
2356 | } | |
2357 | } | |
2358 | ||
2359 | /* | |
2360 | * compare top of heap values and reset as required. if the | |
2361 | * holes flag is set, don't reset first_used unless it's | |
2362 | * pointing to used bytes. we're being conservative here | |
2363 | * since the block will get compacted anyhow by the kernel. | |
2364 | */ | |
27527004 | 2365 | if ((leaf->hdr.holes == 0 && first_used != INT_GET(leaf->hdr.firstused, ARCH_CONVERT)) || |
2bd0ea18 NS |
2366 | INT_GET(leaf->hdr.firstused, ARCH_CONVERT) > first_used) { |
2367 | if (!no_modify) { | |
2368 | if (verbose) | |
2369 | do_warn( | |
2370 | "- resetting first used heap value from %d to %d in block %u of dir ino %llu\n", | |
2371 | (int) INT_GET(leaf->hdr.firstused, ARCH_CONVERT), first_used, | |
2372 | da_bno, ino); | |
2373 | INT_SET(leaf->hdr.firstused, ARCH_CONVERT, first_used); | |
2374 | *buf_dirty = 1; | |
2375 | } else { | |
2376 | if (verbose) | |
2377 | do_warn( | |
2378 | "- would reset first used value from %d to %d in block %u of dir ino %llu\n", | |
2379 | (int) INT_GET(leaf->hdr.firstused, ARCH_CONVERT), first_used, | |
2380 | da_bno, ino); | |
2381 | } | |
2382 | } | |
2383 | ||
2384 | if (bytes_used != INT_GET(leaf->hdr.namebytes, ARCH_CONVERT)) { | |
2385 | if (!no_modify) { | |
2386 | if (verbose) | |
2387 | do_warn( | |
2388 | "- resetting namebytes cnt from %d to %d in block %u of dir inode %llu\n", | |
2389 | (int) INT_GET(leaf->hdr.namebytes, ARCH_CONVERT), bytes_used, | |
2390 | da_bno, ino); | |
2391 | INT_SET(leaf->hdr.namebytes, ARCH_CONVERT, bytes_used); | |
2392 | *buf_dirty = 1; | |
2393 | } else { | |
2394 | if (verbose) | |
2395 | do_warn( | |
2396 | "- would reset namebytes cnt from %d to %d in block %u of dir inode %llu\n", | |
2397 | (int) INT_GET(leaf->hdr.namebytes, ARCH_CONVERT), bytes_used, | |
2398 | da_bno, ino); | |
2399 | } | |
2400 | } | |
2401 | ||
2402 | /* | |
2403 | * If the hole flag is not set, then we know that there can | |
2404 | * be no lost holes. If the hole flag is set, then it's ok | |
2405 | * if the on-disk holemap doesn't describe everything as long | |
2406 | * as what it does describe doesn't conflict with reality. | |
2407 | */ | |
2408 | ||
2409 | reset_holes = 0; | |
2410 | ||
2411 | bholemap.lost_holes = leaf->hdr.holes; | |
2412 | for (i = 0; i < XFS_DIR_LEAF_MAPSIZE; i++) { | |
2413 | bholemap.hentries[i].base = INT_GET(leaf->hdr.freemap[i].base, ARCH_CONVERT); | |
2414 | bholemap.hentries[i].size = INT_GET(leaf->hdr.freemap[i].size, ARCH_CONVERT); | |
2415 | } | |
2416 | ||
2417 | /* | |
2418 | * Ok, now set up our own freespace list | |
2419 | * (XFS_DIR_LEAF_MAPSIZE (3) * biggest regions) | |
2420 | * and see if they match what's in the block | |
2421 | */ | |
2422 | bzero(&holemap, sizeof(da_hole_map_t)); | |
2423 | process_da_freemap(mp, dir_freemap, &holemap); | |
2424 | ||
2425 | if (zero_len_entries) { | |
2426 | reset_holes = 1; | |
2427 | } else if (leaf->hdr.holes == 0) { | |
2428 | if (holemap.lost_holes > 0) { | |
2429 | if (verbose) | |
2430 | do_warn( | |
2431 | "- found unexpected lost holes in block %u, dir inode %llu\n", | |
2432 | da_bno, ino); | |
2433 | ||
2434 | reset_holes = 1; | |
2435 | } else if (compare_da_freemaps(mp, &holemap, &bholemap, | |
2436 | XFS_DIR_LEAF_MAPSIZE, ino, da_bno)) { | |
2437 | if (verbose) | |
2438 | do_warn( | |
2439 | "- hole info non-optimal in block %u, dir inode %llu\n", | |
2440 | da_bno, ino); | |
2441 | reset_holes = 1; | |
2442 | } | |
2443 | } else if (verify_da_freemap(mp, dir_freemap, &holemap, ino, da_bno)) { | |
2444 | if (verbose) | |
2445 | do_warn( | |
2446 | "- hole info incorrect in block %u, dir inode %llu\n", | |
2447 | da_bno, ino); | |
2448 | reset_holes = 1; | |
2449 | } | |
2450 | ||
2451 | if (reset_holes) { | |
2452 | /* | |
2453 | * have to reset block hole info | |
2454 | */ | |
2455 | if (verbose) { | |
2456 | do_warn( | |
2457 | "- existing hole info for block %d, dir inode %llu (base, size) - \n", | |
2458 | da_bno, ino); | |
2459 | do_warn("- \t"); | |
2460 | for (i = 0; i < XFS_DIR_LEAF_MAPSIZE; i++) { | |
2461 | do_warn( | |
2462 | "- (%d, %d) ", bholemap.hentries[i].base, | |
2463 | bholemap.hentries[i].size); | |
2464 | } | |
2465 | do_warn("- holes flag = %d\n", bholemap.lost_holes); | |
2466 | } | |
2467 | ||
2468 | if (!no_modify) { | |
2469 | if (verbose) | |
2470 | do_warn( | |
2471 | "- compacting block %u in dir inode %llu\n", | |
2472 | da_bno, ino); | |
2473 | ||
2474 | new_leaf = (xfs_dir_leafblock_t *) &dirbuf[0]; | |
2475 | ||
2476 | /* | |
2477 | * copy leaf block header | |
2478 | */ | |
2479 | bcopy(&leaf->hdr, &new_leaf->hdr, | |
2480 | sizeof(xfs_dir_leaf_hdr_t)); | |
2481 | ||
2482 | /* | |
2483 | * reset count in case we have some zero length entries | |
2484 | * that are being junked | |
2485 | */ | |
2486 | num_entries = 0; | |
2487 | first_used = XFS_LBSIZE(mp); | |
2488 | first_byte = (char *) new_leaf | |
2489 | + (__psint_t) XFS_LBSIZE(mp); | |
2490 | ||
2491 | /* | |
2492 | * copy entry table and pack names starting from the end | |
2493 | * of the block | |
2494 | */ | |
2495 | for (i = 0, s_entry = &leaf->entries[0], | |
2496 | d_entry = &new_leaf->entries[0]; | |
2497 | i < INT_GET(leaf->hdr.count, ARCH_CONVERT); | |
2498 | i++, s_entry++) { | |
2499 | /* | |
2500 | * skip zero-length entries | |
2501 | */ | |
2502 | if (s_entry->namelen == 0) | |
2503 | continue; | |
2504 | ||
2505 | bytes = sizeof(xfs_dir_leaf_name_t) | |
2506 | + s_entry->namelen - 1; | |
2507 | ||
2508 | if ((__psint_t) first_byte - bytes < | |
2509 | sizeof(xfs_dir_leaf_entry_t) | |
2510 | + (__psint_t) d_entry) { | |
2511 | do_warn( | |
2512 | "not enough space in block %u of dir inode %llu for all entries\n", | |
2513 | da_bno, ino); | |
2514 | break; | |
2515 | } | |
2516 | ||
2517 | first_used -= bytes; | |
2518 | first_byte -= bytes; | |
2519 | ||
2520 | INT_SET(d_entry->nameidx, ARCH_CONVERT, first_used); | |
2521 | INT_SET(d_entry->hashval, ARCH_CONVERT, INT_GET(s_entry->hashval, ARCH_CONVERT)); | |
2522 | d_entry->namelen = s_entry->namelen; | |
2523 | d_entry->pad2 = 0; | |
2524 | ||
2525 | bcopy((char *) leaf + INT_GET(s_entry->nameidx, ARCH_CONVERT), | |
2526 | first_byte, bytes); | |
2527 | ||
2528 | num_entries++; | |
2529 | d_entry++; | |
2530 | } | |
2531 | ||
2532 | ASSERT((char *) first_byte >= (char *) d_entry); | |
2533 | ASSERT(first_used <= XFS_LBSIZE(mp)); | |
2534 | ||
2535 | /* | |
2536 | * zero space between end of table and top of heap | |
2537 | */ | |
2538 | bzero(d_entry, (__psint_t) first_byte | |
2539 | - (__psint_t) d_entry); | |
2540 | ||
2541 | /* | |
2542 | * reset header info | |
2543 | */ | |
2544 | if (num_entries != INT_GET(new_leaf->hdr.count, ARCH_CONVERT)) | |
2545 | INT_SET(new_leaf->hdr.count, ARCH_CONVERT, num_entries); | |
2546 | ||
2547 | INT_SET(new_leaf->hdr.firstused, ARCH_CONVERT, first_used); | |
2548 | new_leaf->hdr.holes = 0; | |
2549 | new_leaf->hdr.pad1 = 0; | |
2550 | ||
2551 | INT_SET(new_leaf->hdr.freemap[0].base, ARCH_CONVERT, (__psint_t) d_entry | |
2552 | - (__psint_t) new_leaf); | |
2553 | INT_SET(new_leaf->hdr.freemap[0].size, ARCH_CONVERT, (__psint_t) first_byte | |
2554 | - (__psint_t) d_entry); | |
2555 | ||
2556 | ASSERT(INT_GET(new_leaf->hdr.freemap[0].base, ARCH_CONVERT) < first_used); | |
2557 | ASSERT(INT_GET(new_leaf->hdr.freemap[0].base, ARCH_CONVERT) == | |
2558 | (__psint_t) (&new_leaf->entries[0]) | |
2559 | - (__psint_t) new_leaf | |
2560 | + i * sizeof(xfs_dir_leaf_entry_t)); | |
2561 | ASSERT(INT_GET(new_leaf->hdr.freemap[0].base, ARCH_CONVERT) < XFS_LBSIZE(mp)); | |
2562 | ASSERT(INT_GET(new_leaf->hdr.freemap[0].size, ARCH_CONVERT) < XFS_LBSIZE(mp)); | |
2563 | ASSERT(INT_GET(new_leaf->hdr.freemap[0].base, ARCH_CONVERT) + | |
2564 | INT_GET(new_leaf->hdr.freemap[0].size, ARCH_CONVERT) == first_used); | |
2565 | ||
2566 | INT_ZERO(new_leaf->hdr.freemap[1].base, ARCH_CONVERT); | |
2567 | INT_ZERO(new_leaf->hdr.freemap[1].size, ARCH_CONVERT); | |
2568 | INT_ZERO(new_leaf->hdr.freemap[2].base, ARCH_CONVERT); | |
2569 | INT_ZERO(new_leaf->hdr.freemap[2].size, ARCH_CONVERT); | |
2570 | ||
2571 | /* | |
2572 | * final step, copy block back | |
2573 | */ | |
2574 | bcopy(new_leaf, leaf, mp->m_sb.sb_blocksize); | |
2575 | ||
2576 | *buf_dirty = 1; | |
2577 | } else { | |
2578 | if (verbose) | |
2579 | do_warn( | |
2580 | "- would compact block %u in dir inode %llu\n", | |
2581 | da_bno, ino); | |
2582 | } | |
2583 | } | |
2584 | #if 0 | |
2585 | if (!no_modify) { | |
2586 | /* | |
2587 | * now take care of deleting or marking the entries with | |
2588 | * zero-length namelen's | |
2589 | */ | |
2590 | junk_zerolen_dir_leaf_entries(mp, leaf, ino, buf_dirty); | |
2591 | } | |
2592 | #endif | |
2593 | #ifdef XR_DIR_TRACE | |
2594 | fprintf(stderr, "process_leaf_dir_block returns %d\n", res); | |
2595 | #endif | |
2596 | return((res > 0) ? 1 : 0); | |
2597 | } | |
2598 | ||
2599 | /* | |
2600 | * returns 0 if the directory is ok, 1 if it has to be junked. | |
2601 | */ | |
2602 | int | |
2603 | process_leaf_dir_level(xfs_mount_t *mp, | |
2604 | da_bt_cursor_t *da_cursor, | |
2605 | int ino_discovery, | |
2606 | int *repair, | |
2607 | int *dot, | |
2608 | int *dotdot, | |
2609 | xfs_ino_t *parent) | |
2610 | { | |
2611 | xfs_dir_leafblock_t *leaf; | |
2612 | xfs_buf_t *bp; | |
2613 | xfs_ino_t ino; | |
2614 | xfs_dfsbno_t dev_bno; | |
2615 | xfs_dablk_t da_bno; | |
2616 | xfs_dablk_t prev_bno; | |
2617 | int res = 0; | |
2618 | int buf_dirty = 0; | |
2619 | xfs_daddr_t bd_addr; | |
2620 | xfs_dahash_t current_hashval = 0; | |
2621 | xfs_dahash_t greatest_hashval; | |
2622 | ||
2623 | #ifdef XR_DIR_TRACE | |
2624 | fprintf(stderr, "process_leaf_dir_level - ino %llu\n", da_cursor->ino); | |
2625 | #endif | |
2626 | *repair = 0; | |
2627 | da_bno = da_cursor->level[0].bno; | |
2628 | ino = da_cursor->ino; | |
2629 | prev_bno = 0; | |
2630 | ||
2631 | do { | |
2632 | dev_bno = blkmap_get(da_cursor->blkmap, da_bno); | |
2633 | /* | |
2634 | * directory code uses 0 as the NULL block pointer | |
2635 | * since 0 is the root block and no directory block | |
2636 | * pointer can point to the root block of the btree | |
2637 | */ | |
2638 | ASSERT(da_bno != 0); | |
2639 | ||
2640 | if (dev_bno == NULLDFSBNO) { | |
2641 | do_warn("can't map block %u for directory inode %llu\n", | |
2642 | da_bno, ino); | |
2643 | goto error_out; | |
2644 | } | |
2645 | ||
2646 | bd_addr = (xfs_daddr_t)XFS_FSB_TO_DADDR(mp, dev_bno); | |
2647 | ||
2648 | bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, dev_bno), | |
2649 | XFS_FSB_TO_BB(mp, 1), 0); | |
2650 | if (!bp) { | |
2651 | do_warn("can't read file block %u (fsbno %llu, daddr %lld) " | |
2652 | "for directory inode %llu\n", | |
2653 | da_bno, dev_bno, (__int64_t) bd_addr, ino); | |
2654 | goto error_out; | |
2655 | } | |
2656 | ||
2657 | leaf = (xfs_dir_leafblock_t *)XFS_BUF_PTR(bp); | |
2658 | ||
2659 | /* | |
2660 | * check magic number for leaf directory btree block | |
2661 | */ | |
2662 | if (INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) != XFS_DIR_LEAF_MAGIC) { | |
2663 | do_warn("bad directory leaf magic # %#x for dir ino %llu\n", | |
2664 | INT_GET(leaf->hdr.info.magic, ARCH_CONVERT), ino); | |
2665 | libxfs_putbuf(bp); | |
2666 | goto error_out; | |
2667 | } | |
2668 | /* | |
2669 | * keep track of greatest block # -- that gets | |
2670 | * us the length of the directory | |
2671 | */ | |
2672 | if (da_bno > da_cursor->greatest_bno) | |
2673 | da_cursor->greatest_bno = da_bno; | |
2674 | ||
2675 | buf_dirty = 0; | |
2676 | /* | |
2677 | * for each block, process the block, verify it's path, | |
2678 | * then get next block. update cursor values along the way | |
2679 | */ | |
2680 | if (process_leaf_dir_block(mp, leaf, da_bno, ino, | |
2681 | current_hashval, ino_discovery, | |
2682 | da_cursor->blkmap, dot, dotdot, parent, | |
2683 | &buf_dirty, &greatest_hashval)) { | |
2684 | libxfs_putbuf(bp); | |
2685 | goto error_out; | |
2686 | } | |
2687 | ||
2688 | /* | |
2689 | * index can be set to hdr.count so match the | |
2690 | * indexes of the interior blocks -- which at the | |
2691 | * end of the block will point to 1 after the final | |
2692 | * real entry in the block | |
2693 | */ | |
2694 | da_cursor->level[0].hashval = greatest_hashval; | |
2695 | da_cursor->level[0].bp = bp; | |
2696 | da_cursor->level[0].bno = da_bno; | |
2697 | da_cursor->level[0].index = INT_GET(leaf->hdr.count, ARCH_CONVERT); | |
2698 | da_cursor->level[0].dirty = buf_dirty; | |
2699 | ||
2700 | if (INT_GET(leaf->hdr.info.back, ARCH_CONVERT) != prev_bno) { | |
2701 | do_warn("bad sibling back pointer for directory block %u " | |
2702 | "in directory inode %llu\n", da_bno, ino); | |
2703 | libxfs_putbuf(bp); | |
2704 | goto error_out; | |
2705 | } | |
2706 | ||
2707 | prev_bno = da_bno; | |
2708 | da_bno = INT_GET(leaf->hdr.info.forw, ARCH_CONVERT); | |
2709 | ||
2710 | if (da_bno != 0) | |
2711 | if (verify_da_path(mp, da_cursor, 0)) { | |
2712 | libxfs_putbuf(bp); | |
2713 | goto error_out; | |
2714 | } | |
2715 | ||
2716 | current_hashval = greatest_hashval; | |
2717 | ||
27527004 | 2718 | ASSERT(buf_dirty == 0 || (buf_dirty && !no_modify)); |
2bd0ea18 NS |
2719 | |
2720 | if (buf_dirty && !no_modify) { | |
2721 | *repair = 1; | |
2722 | libxfs_writebuf(bp, 0); | |
2723 | } | |
2724 | else | |
2725 | libxfs_putbuf(bp); | |
2726 | } while (da_bno != 0 && res == 0); | |
2727 | ||
2728 | if (verify_final_da_path(mp, da_cursor, 0)) { | |
2729 | /* | |
2730 | * verify the final path up (right-hand-side) if still ok | |
2731 | */ | |
2732 | do_warn("bad hash path in directory %llu\n", da_cursor->ino); | |
2733 | goto error_out; | |
2734 | } | |
2735 | ||
2736 | #ifdef XR_DIR_TRACE | |
2737 | fprintf(stderr, "process_leaf_dir_level returns %d (%s)\n", | |
2738 | res, ((res) ? "bad" : "ok")); | |
2739 | #endif | |
2740 | /* | |
2741 | * redundant but just for testing | |
2742 | */ | |
2743 | release_da_cursor(mp, da_cursor, 0); | |
2744 | ||
2745 | return(res); | |
2746 | ||
2747 | error_out: | |
2748 | /* | |
2749 | * release all buffers holding interior btree blocks | |
2750 | */ | |
2751 | err_release_da_cursor(mp, da_cursor, 0); | |
2752 | ||
2753 | return(1); | |
2754 | } | |
2755 | ||
2756 | /* | |
2757 | * a node directory is a true btree directory -- where the directory | |
2758 | * has gotten big enough that it is represented as a non-trivial (e.g. | |
2759 | * has more than just a root block) btree. | |
2760 | * | |
2761 | * Note that if we run into any problems, we trash the | |
2762 | * directory. Even if it's the root directory, | |
2763 | * we'll be able to traverse all the disconnected | |
2764 | * subtrees later (phase 6). | |
2765 | * | |
2766 | * one day, if we actually fix things, we'll set repair to 1 to | |
2767 | * indicate that we have or that we should. | |
2768 | * | |
2769 | * dirname can be set to NULL if the name is unknown (or to | |
2770 | * the string representation of the inode) | |
2771 | * | |
2772 | * returns 0 if things are ok, 1 if bad (directory needs to be junked) | |
2773 | */ | |
2774 | /* ARGSUSED */ | |
2775 | int | |
2776 | process_node_dir( | |
2777 | xfs_mount_t *mp, | |
2778 | xfs_ino_t ino, | |
2779 | xfs_dinode_t *dip, | |
2780 | int ino_discovery, | |
2781 | blkmap_t *blkmap, | |
2782 | int *dot, | |
2783 | int *dotdot, | |
2784 | xfs_ino_t *parent, /* out - parent ino # or NULLFSINO */ | |
2785 | char *dirname, | |
2786 | int *repair) | |
2787 | { | |
2788 | xfs_dablk_t bno; | |
2789 | int error = 0; | |
2790 | da_bt_cursor_t da_cursor; | |
2791 | ||
2792 | #ifdef XR_DIR_TRACE | |
2793 | fprintf(stderr, "process_node_dir - ino %llu\n", ino); | |
2794 | #endif | |
2795 | *repair = *dot = *dotdot = 0; | |
2796 | *parent = NULLFSINO; | |
2797 | ||
2798 | /* | |
2799 | * try again -- traverse down left-side of tree until we hit | |
2800 | * the left-most leaf block setting up the btree cursor along | |
2801 | * the way. Then walk the leaf blocks left-to-right, calling | |
2802 | * a parent-verification routine each time we traverse a block. | |
2803 | */ | |
2804 | bzero(&da_cursor, sizeof(da_bt_cursor_t)); | |
2805 | ||
2806 | da_cursor.active = 0; | |
2807 | da_cursor.type = 0; | |
2808 | da_cursor.ino = ino; | |
2809 | da_cursor.dip = dip; | |
2810 | da_cursor.greatest_bno = 0; | |
2811 | da_cursor.blkmap = blkmap; | |
2812 | ||
2813 | /* | |
2814 | * now process interior node | |
2815 | */ | |
2816 | ||
2817 | error = traverse_int_dablock(mp, &da_cursor, &bno, XFS_DATA_FORK); | |
2818 | ||
2819 | if (error == 0) | |
2820 | return(1); | |
2821 | ||
2822 | /* | |
2823 | * now pass cursor and bno into leaf-block processing routine | |
2824 | * the leaf dir level routine checks the interior paths | |
2825 | * up to the root including the final right-most path. | |
2826 | */ | |
2827 | ||
2828 | error = process_leaf_dir_level(mp, &da_cursor, ino_discovery, | |
2829 | repair, dot, dotdot, parent); | |
2830 | ||
2831 | if (error) | |
2832 | return(1); | |
2833 | ||
2834 | /* | |
2835 | * sanity check inode size | |
2836 | */ | |
2837 | if (INT_GET(dip->di_core.di_size, ARCH_CONVERT) < | |
2838 | (da_cursor.greatest_bno + 1) * mp->m_sb.sb_blocksize) { | |
2839 | if ((xfs_fsize_t) (da_cursor.greatest_bno | |
2840 | * mp->m_sb.sb_blocksize) > UINT_MAX) { | |
2841 | do_warn( | |
2842 | "out of range internal directory block numbers (inode %llu)\n", | |
2843 | ino); | |
2844 | return(1); | |
2845 | } | |
2846 | ||
2847 | do_warn( | |
2848 | "setting directory inode (%llu) size to %llu bytes, was %lld bytes\n", | |
2849 | ino, | |
2850 | (xfs_dfiloff_t) (da_cursor.greatest_bno + 1) | |
2851 | * mp->m_sb.sb_blocksize, | |
2852 | INT_GET(dip->di_core.di_size, ARCH_CONVERT)); | |
2853 | ||
2854 | INT_SET(dip->di_core.di_size, ARCH_CONVERT, (xfs_fsize_t) | |
2855 | (da_cursor.greatest_bno + 1) * mp->m_sb.sb_blocksize); | |
2856 | } | |
2857 | return(0); | |
2858 | } | |
2859 | ||
2860 | /* | |
2861 | * a leaf directory is one where the directory is too big for | |
2862 | * the inode data fork but is small enough to fit into one | |
2863 | * directory btree block (filesystem block) outside the inode | |
2864 | * | |
2865 | * returns NULLFSINO if the directory is cannot be salvaged | |
2866 | * and the .. ino if things are ok (even if the directory had | |
2867 | * to be altered to make it ok). | |
2868 | * | |
2869 | * dirname can be set to NULL if the name is unknown (or to | |
2870 | * the string representation of the inode) | |
2871 | * | |
2872 | * returns 0 if things are ok, 1 if bad (directory needs to be junked) | |
2873 | */ | |
2874 | /* ARGSUSED */ | |
2875 | int | |
2876 | process_leaf_dir( | |
2877 | xfs_mount_t *mp, | |
2878 | xfs_ino_t ino, | |
2879 | xfs_dinode_t *dip, | |
2880 | int ino_discovery, | |
2881 | int *dino_dirty, | |
2882 | blkmap_t *blkmap, | |
2883 | int *dot, /* out - 1 if there is a dot, else 0 */ | |
2884 | int *dotdot, /* out - 1 if there's a dotdot, else 0 */ | |
2885 | xfs_ino_t *parent, /* out - parent ino # or NULLFSINO */ | |
2886 | char *dirname, /* in - directory pathname */ | |
2887 | int *repair) /* out - 1 if something was fixed */ | |
2888 | { | |
2889 | xfs_dir_leafblock_t *leaf; | |
2890 | xfs_dahash_t next_hashval; | |
2891 | xfs_dfsbno_t bno; | |
2892 | xfs_buf_t *bp; | |
2893 | int buf_dirty = 0; | |
2894 | ||
2895 | #ifdef XR_DIR_TRACE | |
2896 | fprintf(stderr, "process_leaf_dir - ino %llu\n", ino); | |
2897 | #endif | |
2898 | *repair = *dot = *dotdot = 0; | |
2899 | *parent = NULLFSINO; | |
2900 | ||
2901 | bno = blkmap_get(blkmap, 0); | |
2902 | if (bno == NULLDFSBNO) { | |
2903 | do_warn("block 0 for directory inode %llu is missing\n", ino); | |
2904 | return(1); | |
2905 | } | |
2906 | bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, bno), | |
2907 | XFS_FSB_TO_BB(mp, 1), 0); | |
2908 | if (!bp) { | |
2909 | do_warn("can't read block 0 for directory inode %llu\n", ino); | |
2910 | return(1); | |
2911 | } | |
2912 | /* | |
2913 | * verify leaf block | |
2914 | */ | |
2915 | leaf = (xfs_dir_leafblock_t *)XFS_BUF_PTR(bp); | |
2916 | ||
2917 | /* | |
2918 | * check magic number for leaf directory btree block | |
2919 | */ | |
2920 | if (INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) != XFS_DIR_LEAF_MAGIC) { | |
2921 | do_warn("bad directory leaf magic # %#x for dir ino %llu\n", | |
2922 | INT_GET(leaf->hdr.info.magic, ARCH_CONVERT), ino); | |
2923 | libxfs_putbuf(bp); | |
2924 | return(1); | |
2925 | } | |
2926 | ||
2927 | if (process_leaf_dir_block(mp, leaf, 0, ino, 0, ino_discovery, blkmap, | |
2928 | dot, dotdot, parent, &buf_dirty, &next_hashval)) { | |
2929 | /* | |
2930 | * the block is bad. lose the directory. | |
2931 | * XXX - later, we should try and just lose | |
2932 | * the block without losing the entire directory | |
2933 | */ | |
27527004 | 2934 | ASSERT(*dotdot == 0 || (*dotdot == 1 && *parent != NULLFSINO)); |
2bd0ea18 NS |
2935 | libxfs_putbuf(bp); |
2936 | return(1); | |
2937 | } | |
2938 | ||
2939 | /* | |
2940 | * check sibling pointers in leaf block (above doesn't do it) | |
2941 | */ | |
2942 | if (INT_GET(leaf->hdr.info.forw, ARCH_CONVERT) != 0 || | |
2943 | INT_GET(leaf->hdr.info.back, ARCH_CONVERT) != 0) { | |
2944 | if (!no_modify) { | |
2945 | do_warn("clearing forw/back pointers for directory inode " | |
2946 | "%llu\n", ino); | |
2947 | buf_dirty = 1; | |
2948 | INT_ZERO(leaf->hdr.info.forw, ARCH_CONVERT); | |
2949 | INT_ZERO(leaf->hdr.info.back, ARCH_CONVERT); | |
2950 | } else { | |
2951 | do_warn("would clear forw/back pointers for directory inode " | |
2952 | "%llu\n", ino); | |
2953 | } | |
2954 | } | |
2955 | ||
27527004 | 2956 | ASSERT(buf_dirty == 0 || (buf_dirty && !no_modify)); |
2bd0ea18 NS |
2957 | |
2958 | if (buf_dirty && !no_modify) | |
2959 | libxfs_writebuf(bp, 0); | |
2960 | else | |
2961 | libxfs_putbuf(bp); | |
2962 | ||
2963 | return(0); | |
2964 | } | |
2965 | ||
2966 | /* | |
2967 | * returns 1 if things are bad (directory needs to be junked) | |
2968 | * and 0 if things are ok. If ino_discovery is 1, add unknown | |
2969 | * inodes to uncertain inode list. | |
2970 | */ | |
2971 | int | |
2972 | process_dir( | |
2973 | xfs_mount_t *mp, | |
2974 | xfs_ino_t ino, | |
2975 | xfs_dinode_t *dip, | |
2976 | int ino_discovery, | |
2977 | int *dino_dirty, | |
2978 | char *dirname, | |
2979 | xfs_ino_t *parent, | |
2980 | blkmap_t *blkmap) | |
2981 | { | |
2982 | int dot; | |
2983 | int dotdot; | |
2984 | int repair = 0; | |
2985 | int res = 0; | |
2986 | ||
2987 | *parent = NULLFSINO; | |
2988 | dot = dotdot = 0; | |
2989 | ||
2990 | /* | |
2991 | * branch off depending on the type of inode. This routine | |
2992 | * is only called ONCE so all the subordinate routines will | |
2993 | * fix '.' and junk '..' if they're bogus. | |
2994 | */ | |
2995 | if (INT_GET(dip->di_core.di_size, ARCH_CONVERT) <= XFS_DFORK_DSIZE_ARCH(dip, mp, ARCH_CONVERT)) { | |
2996 | dot = 1; | |
2997 | dotdot = 1; | |
2998 | if (process_shortform_dir(mp, ino, dip, ino_discovery, | |
2999 | dino_dirty, parent, dirname, &repair)) { | |
3000 | res = 1; | |
3001 | } | |
3002 | } else if (INT_GET(dip->di_core.di_size, ARCH_CONVERT) <= XFS_LBSIZE(mp)) { | |
3003 | if (process_leaf_dir(mp, ino, dip, ino_discovery, | |
3004 | dino_dirty, blkmap, &dot, &dotdot, | |
3005 | parent, dirname, &repair)) { | |
3006 | res = 1; | |
3007 | } | |
3008 | } else { | |
3009 | if (process_node_dir(mp, ino, dip, ino_discovery, | |
3010 | blkmap, &dot, &dotdot, | |
3011 | parent, dirname, &repair)) { | |
3012 | res = 1; | |
3013 | } | |
3014 | } | |
3015 | /* | |
3016 | * bad . entries in all directories will be fixed up in phase 6 | |
3017 | */ | |
3018 | if (dot == 0) { | |
3019 | do_warn("no . entry for directory %llu\n", ino); | |
3020 | } | |
3021 | ||
3022 | /* | |
3023 | * shortform dirs always have a .. entry. .. for all longform | |
3024 | * directories will get fixed in phase 6. .. for other shortform | |
3025 | * dirs also get fixed there. .. for a shortform root was | |
3026 | * fixed in place since we know what it should be | |
3027 | */ | |
3028 | if (dotdot == 0 && ino != mp->m_sb.sb_rootino) { | |
3029 | do_warn("no .. entry for directory %llu\n", ino); | |
3030 | } else if (dotdot == 0 && ino == mp->m_sb.sb_rootino) { | |
3031 | do_warn("no .. entry for root directory %llu\n", ino); | |
3032 | need_root_dotdot = 1; | |
3033 | } | |
3034 | ||
3035 | #ifdef XR_DIR_TRACE | |
3036 | fprintf(stderr, "(process_dir), parent of %llu is %llu\n", ino, parent); | |
3037 | #endif | |
3038 | return(res); | |
3039 | } |