]>
Commit | Line | Data |
---|---|---|
2bd0ea18 | 1 | /* |
da23017d NS |
2 | * Copyright (c) 2000-2005 Silicon Graphics, Inc. |
3 | * All Rights Reserved. | |
dfc130f3 | 4 | * |
da23017d NS |
5 | * This program is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU General Public License as | |
2bd0ea18 | 7 | * published by the Free Software Foundation. |
dfc130f3 | 8 | * |
da23017d NS |
9 | * This program is distributed in the hope that it would be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
dfc130f3 | 13 | * |
da23017d NS |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write the Free Software Foundation, | |
16 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
2bd0ea18 NS |
17 | */ |
18 | ||
19 | #include <libxfs.h> | |
20 | #include "avl.h" | |
21 | #include "globals.h" | |
22 | #include "agheader.h" | |
23 | #include "incore.h" | |
24 | #include "protos.h" | |
25 | #include "err_protos.h" | |
26 | #include "dir.h" | |
27 | #include "dir2.h" | |
28 | #include "dinode.h" | |
29 | #include "scan.h" | |
30 | #include "versions.h" | |
31 | #include "attr_repair.h" | |
32 | #include "bmap.h" | |
3b6ac903 | 33 | #include "threads.h" |
2bd0ea18 NS |
34 | |
35 | /* | |
36 | * inode clearing routines | |
37 | */ | |
38 | ||
39 | /* | |
40 | * return the offset into the inode where the attribute fork starts | |
41 | */ | |
42 | /* ARGSUSED */ | |
43 | int | |
44 | calc_attr_offset(xfs_mount_t *mp, xfs_dinode_t *dino) | |
45 | { | |
46 | xfs_dinode_core_t *dinoc = &dino->di_core; | |
47 | int offset = ((__psint_t) &dino->di_u) | |
48 | - (__psint_t)dino; | |
49 | ||
50 | /* | |
51 | * don't worry about alignment when calculating offset | |
52 | * because the data fork is already 8-byte aligned | |
53 | */ | |
54 | switch (dinoc->di_format) { | |
55 | case XFS_DINODE_FMT_DEV: | |
63899e27 | 56 | offset += sizeof(xfs_dev_t); |
2bd0ea18 NS |
57 | break; |
58 | case XFS_DINODE_FMT_LOCAL: | |
59 | offset += INT_GET(dinoc->di_size, ARCH_CONVERT); | |
60 | break; | |
61 | case XFS_DINODE_FMT_UUID: | |
62 | offset += sizeof(uuid_t); | |
63 | break; | |
64 | case XFS_DINODE_FMT_EXTENTS: | |
65 | offset += INT_GET(dinoc->di_nextents, ARCH_CONVERT) * sizeof(xfs_bmbt_rec_32_t); | |
66 | break; | |
67 | case XFS_DINODE_FMT_BTREE: | |
68 | offset += INT_GET(dino->di_u.di_bmbt.bb_numrecs, ARCH_CONVERT) * sizeof(xfs_bmbt_rec_32_t); | |
69 | break; | |
70 | default: | |
507f4e33 | 71 | do_error(_("Unknown inode format.\n")); |
2bd0ea18 NS |
72 | abort(); |
73 | break; | |
74 | } | |
75 | ||
76 | return(offset); | |
77 | } | |
78 | ||
79 | /* ARGSUSED */ | |
80 | int | |
81 | clear_dinode_attr(xfs_mount_t *mp, xfs_dinode_t *dino, xfs_ino_t ino_num) | |
82 | { | |
83 | xfs_dinode_core_t *dinoc = &dino->di_core; | |
84 | ||
85 | ASSERT(dinoc->di_forkoff != 0); | |
86 | ||
87 | if (!no_modify) | |
507f4e33 | 88 | fprintf(stderr, _("clearing inode %llu attributes\n"), |
5b64e00a | 89 | (unsigned long long)ino_num); |
2bd0ea18 | 90 | else |
507f4e33 | 91 | fprintf(stderr, _("would have cleared inode %llu attributes\n"), |
5b64e00a | 92 | (unsigned long long)ino_num); |
2bd0ea18 NS |
93 | |
94 | if (INT_GET(dinoc->di_anextents, ARCH_CONVERT) != 0) { | |
95 | if (no_modify) | |
96 | return(1); | |
46eca962 | 97 | dinoc->di_anextents = 0; |
2bd0ea18 NS |
98 | } |
99 | ||
100 | if (dinoc->di_aformat != XFS_DINODE_FMT_EXTENTS) { | |
101 | if (no_modify) | |
102 | return(1); | |
103 | dinoc->di_aformat = XFS_DINODE_FMT_EXTENTS; | |
104 | } | |
105 | ||
106 | /* get rid of the fork by clearing forkoff */ | |
107 | ||
108 | /* Originally, when the attr repair code was added, the fork was cleared | |
109 | * by turning it into shortform status. This meant clearing the | |
110 | * hdr.totsize/count fields and also changing aformat to LOCAL | |
111 | * (vs EXTENTS). Over various fixes, the aformat and forkoff have | |
112 | * been updated to not show an attribute fork at all, however. | |
113 | * It could be possible that resetting totsize/count are not needed, | |
dfc130f3 | 114 | * but just to be safe, leave it in for now. |
2bd0ea18 NS |
115 | */ |
116 | ||
117 | if (!no_modify) { | |
118 | xfs_attr_shortform_t *asf = (xfs_attr_shortform_t *) | |
46eca962 | 119 | XFS_DFORK_APTR(dino); |
2bd0ea18 NS |
120 | INT_SET(asf->hdr.totsize, ARCH_CONVERT, |
121 | sizeof(xfs_attr_sf_hdr_t)); | |
122 | INT_SET(asf->hdr.count, ARCH_CONVERT, 0); | |
123 | dinoc->di_forkoff = 0; /* got to do this after asf is set */ | |
124 | } | |
125 | ||
126 | /* | |
127 | * always returns 1 since the fork gets zapped | |
128 | */ | |
129 | return(1); | |
130 | } | |
131 | ||
132 | /* ARGSUSED */ | |
133 | int | |
134 | clear_dinode_core(xfs_dinode_core_t *dinoc, xfs_ino_t ino_num) | |
135 | { | |
136 | int dirty = 0; | |
137 | ||
138 | if (INT_GET(dinoc->di_magic, ARCH_CONVERT) != XFS_DINODE_MAGIC) { | |
139 | dirty = 1; | |
140 | ||
141 | if (no_modify) | |
142 | return(1); | |
143 | ||
144 | INT_SET(dinoc->di_magic, ARCH_CONVERT, XFS_DINODE_MAGIC); | |
145 | } | |
146 | ||
147 | if (!XFS_DINODE_GOOD_VERSION(dinoc->di_version) || | |
148 | (!fs_inode_nlink && dinoc->di_version > XFS_DINODE_VERSION_1)) { | |
149 | dirty = 1; | |
150 | ||
151 | if (no_modify) | |
152 | return(1); | |
153 | ||
154 | dinoc->di_version = (fs_inode_nlink) ? XFS_DINODE_VERSION_2 | |
155 | : XFS_DINODE_VERSION_1; | |
156 | } | |
157 | ||
158 | if (INT_GET(dinoc->di_mode, ARCH_CONVERT) != 0) { | |
159 | dirty = 1; | |
160 | ||
161 | if (no_modify) | |
162 | return(1); | |
163 | ||
46eca962 | 164 | dinoc->di_mode = 0; |
2bd0ea18 NS |
165 | } |
166 | ||
167 | if (INT_GET(dinoc->di_flags, ARCH_CONVERT) != 0) { | |
168 | dirty = 1; | |
169 | ||
170 | if (no_modify) | |
171 | return(1); | |
172 | ||
46eca962 | 173 | dinoc->di_flags = 0; |
2bd0ea18 NS |
174 | } |
175 | ||
176 | if (INT_GET(dinoc->di_dmevmask, ARCH_CONVERT) != 0) { | |
177 | dirty = 1; | |
178 | ||
179 | if (no_modify) | |
180 | return(1); | |
181 | ||
46eca962 | 182 | dinoc->di_dmevmask = 0; |
2bd0ea18 NS |
183 | } |
184 | ||
185 | if (dinoc->di_forkoff != 0) { | |
186 | dirty = 1; | |
187 | ||
188 | if (no_modify) | |
189 | return(1); | |
190 | ||
191 | dinoc->di_forkoff = 0; | |
192 | } | |
193 | ||
194 | if (dinoc->di_format != XFS_DINODE_FMT_EXTENTS) { | |
195 | dirty = 1; | |
196 | ||
197 | if (no_modify) | |
198 | return(1); | |
199 | ||
200 | dinoc->di_format = XFS_DINODE_FMT_EXTENTS; | |
201 | } | |
202 | ||
203 | if (dinoc->di_aformat != XFS_DINODE_FMT_EXTENTS) { | |
204 | dirty = 1; | |
205 | ||
206 | if (no_modify) | |
207 | return(1); | |
208 | ||
209 | dinoc->di_aformat = XFS_DINODE_FMT_EXTENTS; | |
210 | } | |
211 | ||
212 | if (INT_GET(dinoc->di_size, ARCH_CONVERT) != 0) { | |
213 | dirty = 1; | |
214 | ||
215 | if (no_modify) | |
216 | return(1); | |
217 | ||
46eca962 | 218 | dinoc->di_size = 0; |
2bd0ea18 NS |
219 | } |
220 | ||
221 | if (INT_GET(dinoc->di_nblocks, ARCH_CONVERT) != 0) { | |
222 | dirty = 1; | |
223 | ||
224 | if (no_modify) | |
225 | return(1); | |
226 | ||
46eca962 | 227 | dinoc->di_nblocks = 0; |
2bd0ea18 NS |
228 | } |
229 | ||
230 | if (INT_GET(dinoc->di_onlink, ARCH_CONVERT) != 0) { | |
231 | dirty = 1; | |
232 | ||
233 | if (no_modify) | |
234 | return(1); | |
235 | ||
46eca962 | 236 | dinoc->di_onlink = 0; |
2bd0ea18 NS |
237 | } |
238 | ||
239 | if (INT_GET(dinoc->di_nextents, ARCH_CONVERT) != 0) { | |
240 | dirty = 1; | |
241 | ||
242 | if (no_modify) | |
243 | return(1); | |
244 | ||
46eca962 | 245 | dinoc->di_nextents = 0; |
2bd0ea18 NS |
246 | } |
247 | ||
248 | if (INT_GET(dinoc->di_anextents, ARCH_CONVERT) != 0) { | |
249 | dirty = 1; | |
250 | ||
251 | if (no_modify) | |
252 | return(1); | |
253 | ||
46eca962 | 254 | dinoc->di_anextents = 0; |
2bd0ea18 NS |
255 | } |
256 | ||
257 | if (dinoc->di_version > XFS_DINODE_VERSION_1 && | |
258 | INT_GET(dinoc->di_nlink, ARCH_CONVERT) != 0) { | |
259 | dirty = 1; | |
260 | ||
261 | if (no_modify) | |
262 | return(1); | |
263 | ||
46eca962 | 264 | dinoc->di_nlink = 0; |
2bd0ea18 NS |
265 | } |
266 | ||
267 | return(dirty); | |
268 | } | |
269 | ||
270 | /* ARGSUSED */ | |
271 | int | |
272 | clear_dinode_unlinked(xfs_mount_t *mp, xfs_dinode_t *dino) | |
273 | { | |
274 | ||
275 | if (dino->di_next_unlinked != NULLAGINO) { | |
276 | if (!no_modify) | |
277 | dino->di_next_unlinked = NULLAGINO; | |
278 | return(1); | |
279 | } | |
280 | ||
281 | return(0); | |
282 | } | |
283 | ||
284 | /* | |
285 | * this clears the unlinked list too so it should not be called | |
286 | * until after the agi unlinked lists are walked in phase 3. | |
287 | * returns > zero if the inode has been altered while being cleared | |
288 | */ | |
289 | int | |
290 | clear_dinode(xfs_mount_t *mp, xfs_dinode_t *dino, xfs_ino_t ino_num) | |
291 | { | |
292 | int dirty; | |
293 | ||
294 | dirty = clear_dinode_core(&dino->di_core, ino_num); | |
295 | dirty += clear_dinode_unlinked(mp, dino); | |
296 | ||
297 | /* and clear the forks */ | |
298 | ||
299 | if (dirty && !no_modify) | |
300 | bzero(&dino->di_u, XFS_LITINO(mp)); | |
301 | ||
302 | return(dirty); | |
303 | } | |
304 | ||
305 | ||
306 | /* | |
307 | * misc. inode-related utility routines | |
308 | */ | |
309 | ||
1e77098c MV |
310 | /* |
311 | * verify_ag_bno is heavily used. In the common case, it | |
312 | * performs just two number of compares | |
313 | */ | |
314 | static __inline int | |
315 | verify_ag_bno(xfs_sb_t *sbp, | |
316 | xfs_agnumber_t agno, | |
317 | xfs_agblock_t agbno) | |
318 | { | |
99a86c52 | 319 | if (agno < (sbp->sb_agcount - 1)) { |
1e77098c MV |
320 | if (agbno >= sbp->sb_agblocks) { |
321 | return 1; /* bad */ | |
322 | } | |
323 | return 0; /* good */ | |
324 | } | |
99a86c52 | 325 | if (agno == (sbp->sb_agcount - 1)) { |
1e77098c MV |
326 | if (agbno >= |
327 | (sbp->sb_dblocks - | |
328 | (sbp->sb_agcount-1) * | |
329 | sbp->sb_agblocks)) { | |
330 | return 1; /* bad */ | |
331 | } | |
332 | return 0; /* good */ | |
333 | } | |
334 | return 1; /* bad */ | |
335 | } | |
336 | ||
2bd0ea18 NS |
337 | /* |
338 | * returns 0 if inode number is valid, 1 if bogus | |
339 | */ | |
340 | int | |
341 | verify_inum(xfs_mount_t *mp, | |
342 | xfs_ino_t ino) | |
343 | { | |
344 | xfs_agnumber_t agno; | |
345 | xfs_agino_t agino; | |
346 | xfs_agblock_t agbno; | |
347 | xfs_sb_t *sbp = &mp->m_sb;; | |
348 | ||
349 | /* range check ag #, ag block. range-checking offset is pointless */ | |
350 | ||
351 | agno = XFS_INO_TO_AGNO(mp, ino); | |
352 | agino = XFS_INO_TO_AGINO(mp, ino); | |
353 | agbno = XFS_AGINO_TO_AGBNO(mp, agino); | |
1e77098c MV |
354 | if (agbno == 0) |
355 | return 1; | |
2bd0ea18 NS |
356 | |
357 | if (ino == 0 || ino == NULLFSINO) | |
358 | return(1); | |
359 | ||
360 | if (ino != XFS_AGINO_TO_INO(mp, agno, agino)) | |
361 | return(1); | |
362 | ||
1e77098c | 363 | return verify_ag_bno(sbp, agno, agbno); |
2bd0ea18 NS |
364 | } |
365 | ||
366 | /* | |
367 | * have a separate routine to ensure that we don't accidentally | |
368 | * lose illegally set bits in the agino by turning it into an FSINO | |
369 | * to feed to the above routine | |
370 | */ | |
371 | int | |
372 | verify_aginum(xfs_mount_t *mp, | |
373 | xfs_agnumber_t agno, | |
374 | xfs_agino_t agino) | |
375 | { | |
376 | xfs_agblock_t agbno; | |
377 | xfs_sb_t *sbp = &mp->m_sb;; | |
378 | ||
379 | /* range check ag #, ag block. range-checking offset is pointless */ | |
380 | ||
381 | if (agino == 0 || agino == NULLAGINO) | |
382 | return(1); | |
383 | ||
384 | /* | |
385 | * agino's can't be too close to NULLAGINO because the min blocksize | |
386 | * is 9 bits and at most 1 bit of that gets used for the inode offset | |
387 | * so if the agino gets shifted by the # of offset bits and compared | |
388 | * to the legal agbno values, a bogus agino will be too large. there | |
389 | * will be extra bits set at the top that shouldn't be set. | |
390 | */ | |
391 | agbno = XFS_AGINO_TO_AGBNO(mp, agino); | |
1e77098c MV |
392 | if (agbno == 0) |
393 | return 1; | |
2bd0ea18 | 394 | |
1e77098c | 395 | return verify_ag_bno(sbp, agno, agbno); |
2bd0ea18 NS |
396 | } |
397 | ||
398 | /* | |
399 | * return 1 if block number is good, 0 if out of range | |
400 | */ | |
401 | int | |
402 | verify_dfsbno(xfs_mount_t *mp, | |
403 | xfs_dfsbno_t fsbno) | |
404 | { | |
405 | xfs_agnumber_t agno; | |
406 | xfs_agblock_t agbno; | |
407 | xfs_sb_t *sbp = &mp->m_sb;; | |
408 | ||
409 | /* range check ag #, ag block. range-checking offset is pointless */ | |
410 | ||
411 | agno = XFS_FSB_TO_AGNO(mp, fsbno); | |
412 | agbno = XFS_FSB_TO_AGBNO(mp, fsbno); | |
413 | ||
1e77098c MV |
414 | return verify_ag_bno(sbp, agno, agbno) == 0; |
415 | } | |
2bd0ea18 | 416 | |
1e77098c MV |
417 | #define XR_DFSBNORANGE_VALID 0 |
418 | #define XR_DFSBNORANGE_BADSTART 1 | |
419 | #define XR_DFSBNORANGE_BADEND 2 | |
420 | #define XR_DFSBNORANGE_OVERFLOW 3 | |
421 | ||
422 | static __inline int | |
423 | verify_dfsbno_range(xfs_mount_t *mp, | |
424 | xfs_dfsbno_t fsbno, | |
425 | xfs_dfilblks_t count) | |
426 | { | |
427 | xfs_agnumber_t agno; | |
428 | xfs_agblock_t agbno; | |
429 | xfs_sb_t *sbp = &mp->m_sb;; | |
430 | ||
431 | /* the start and end blocks better be in the same allocation group */ | |
432 | agno = XFS_FSB_TO_AGNO(mp, fsbno); | |
433 | if (agno != XFS_FSB_TO_AGNO(mp, fsbno + count - 1)) { | |
434 | return XR_DFSBNORANGE_OVERFLOW; | |
435 | } | |
436 | ||
437 | agbno = XFS_FSB_TO_AGBNO(mp, fsbno); | |
438 | if (verify_ag_bno(sbp, agno, agbno)) { | |
439 | return XR_DFSBNORANGE_BADSTART; | |
440 | } | |
441 | ||
442 | agbno = XFS_FSB_TO_AGBNO(mp, fsbno + count - 1); | |
443 | if (verify_ag_bno(sbp, agno, agbno)) { | |
444 | return XR_DFSBNORANGE_BADEND; | |
445 | } | |
446 | ||
447 | return (XR_DFSBNORANGE_VALID); | |
2bd0ea18 NS |
448 | } |
449 | ||
450 | int | |
451 | verify_agbno(xfs_mount_t *mp, | |
452 | xfs_agnumber_t agno, | |
453 | xfs_agblock_t agbno) | |
454 | { | |
455 | xfs_sb_t *sbp = &mp->m_sb;; | |
456 | ||
457 | /* range check ag #, ag block. range-checking offset is pointless */ | |
1e77098c | 458 | return verify_ag_bno(sbp, agno, agbno) == 0; |
2bd0ea18 NS |
459 | } |
460 | ||
461 | void | |
462 | convert_extent( | |
463 | xfs_bmbt_rec_32_t *rp, | |
464 | xfs_dfiloff_t *op, /* starting offset (blockno in file) */ | |
465 | xfs_dfsbno_t *sp, /* starting block (fs blockno) */ | |
466 | xfs_dfilblks_t *cp, /* blockcount */ | |
467 | int *fp) /* extent flag */ | |
468 | { | |
469 | xfs_bmbt_irec_t irec, *s = &irec; | |
93cc2301 NS |
470 | xfs_bmbt_rec_t rpcopy, *p = &rpcopy; |
471 | ||
472 | memcpy(&rpcopy, rp, sizeof(rpcopy)); | |
2bd0ea18 | 473 | /* Just use the extent parsing routine from the kernel */ |
f9e56f43 | 474 | libxfs_bmbt_disk_get_all(p, s); |
2bd0ea18 NS |
475 | |
476 | if (fs_has_extflgbit) { | |
477 | if (s->br_state == XFS_EXT_UNWRITTEN) { | |
478 | *fp = 1; | |
479 | } else { | |
480 | *fp = 0; | |
481 | } | |
482 | } else { | |
483 | *fp = 0; | |
484 | } | |
485 | *op = s->br_startoff; | |
486 | *sp = s->br_startblock; | |
487 | *cp = s->br_blockcount; | |
488 | } | |
489 | ||
490 | /* | |
491 | * return address of block fblock if it's within the range described | |
492 | * by the extent list. Otherwise, returns a null address. | |
493 | */ | |
494 | /* ARGSUSED */ | |
495 | xfs_dfsbno_t | |
496 | get_bmbt_reclist( | |
497 | xfs_mount_t *mp, | |
498 | xfs_bmbt_rec_32_t *rp, | |
499 | int numrecs, | |
500 | xfs_dfiloff_t fblock) | |
501 | { | |
502 | int i; | |
503 | xfs_dfilblks_t cnt; | |
504 | xfs_dfiloff_t off_bno; | |
505 | xfs_dfsbno_t start; | |
506 | int flag; | |
507 | ||
508 | for (i = 0; i < numrecs; i++, rp++) { | |
509 | convert_extent(rp, &off_bno, &start, &cnt, &flag); | |
510 | if (off_bno >= fblock && off_bno + cnt < fblock) | |
511 | return(start + fblock - off_bno); | |
512 | } | |
513 | ||
514 | return(NULLDFSBNO); | |
515 | } | |
516 | ||
3b6ac903 MV |
517 | /* |
518 | * process_bmbt_reclist_int is the most compute intensive | |
519 | * function in repair. The following macros reduce the | |
520 | * the large number of lock/unlock steps it would otherwise | |
521 | * call. | |
522 | */ | |
523 | #define PROCESS_BMBT_DECL(type, var) type var | |
524 | ||
525 | #define PROCESS_BMBT_LOCK(agno) \ | |
526 | if (do_parallel && (agno != locked_agno)) { \ | |
527 | if (locked_agno != -1) /* release old ag lock */ \ | |
528 | PREPAIR_RW_UNLOCK_NOTEST(&per_ag_lock[locked_agno]); \ | |
529 | PREPAIR_RW_WRITE_LOCK_NOTEST(&per_ag_lock[agno]); \ | |
530 | locked_agno = agno; \ | |
531 | } | |
532 | ||
533 | #define PROCESS_BMBT_UNLOCK_RETURN(val) \ | |
534 | do { \ | |
535 | if (locked_agno != -1) \ | |
536 | PREPAIR_RW_UNLOCK_NOTEST(&per_ag_lock[locked_agno]); \ | |
537 | return (val); \ | |
538 | } while (0) | |
539 | ||
2bd0ea18 NS |
540 | /* |
541 | * return 1 if inode should be cleared, 0 otherwise | |
542 | * if check_dups should be set to 1, that implies that | |
543 | * the primary purpose of this call is to see if the | |
544 | * file overlaps with any duplicate extents (in the | |
545 | * duplicate extent list). | |
546 | */ | |
547 | /* ARGSUSED */ | |
548 | int | |
549 | process_bmbt_reclist_int( | |
550 | xfs_mount_t *mp, | |
551 | xfs_bmbt_rec_32_t *rp, | |
552 | int numrecs, | |
553 | int type, | |
554 | xfs_ino_t ino, | |
555 | xfs_drfsbno_t *tot, | |
556 | blkmap_t **blkmapp, | |
557 | xfs_dfiloff_t *first_key, | |
558 | xfs_dfiloff_t *last_key, | |
559 | int check_dups, | |
560 | int whichfork) | |
561 | { | |
562 | xfs_dfsbno_t b; | |
563 | xfs_drtbno_t ext; | |
564 | xfs_dfilblks_t c; /* count */ | |
565 | xfs_dfilblks_t cp = 0; /* prev count */ | |
566 | xfs_dfsbno_t s; /* start */ | |
567 | xfs_dfsbno_t sp = 0; /* prev start */ | |
568 | xfs_dfiloff_t o = 0; /* offset */ | |
569 | xfs_dfiloff_t op = 0; /* prev offset */ | |
570 | char *ftype; | |
571 | char *forkname; | |
572 | int i; | |
573 | int state; | |
574 | int flag; /* extent flag */ | |
c1281304 | 575 | int pwe; /* partially-written extent */ |
1e77098c MV |
576 | xfs_dfsbno_t e; |
577 | xfs_agnumber_t agno; | |
578 | xfs_agblock_t agbno; | |
3b6ac903 MV |
579 | PROCESS_BMBT_DECL |
580 | (xfs_agnumber_t, locked_agno=-1); | |
2bd0ea18 NS |
581 | |
582 | if (whichfork == XFS_DATA_FORK) | |
507f4e33 | 583 | forkname = _("data"); |
2bd0ea18 | 584 | else |
507f4e33 | 585 | forkname = _("attr"); |
2bd0ea18 NS |
586 | |
587 | if (type == XR_INO_RTDATA) | |
507f4e33 | 588 | ftype = _("real-time"); |
2bd0ea18 | 589 | else |
507f4e33 | 590 | ftype = _("regular"); |
2bd0ea18 NS |
591 | |
592 | for (i = 0; i < numrecs; i++, rp++) { | |
593 | convert_extent(rp, &o, &s, &c, &flag); | |
594 | if (i == 0) | |
595 | *last_key = *first_key = o; | |
596 | else | |
597 | *last_key = o; | |
598 | if (i > 0 && op + cp > o) { | |
599 | do_warn( | |
507f4e33 NS |
600 | _("bmap rec out of order, inode %llu entry %d " |
601 | "[o s c] [%llu %llu %llu], %d [%llu %llu %llu]\n"), | |
2bd0ea18 | 602 | ino, i, o, s, c, i-1, op, sp, cp); |
3b6ac903 | 603 | PROCESS_BMBT_UNLOCK_RETURN(1); |
2bd0ea18 NS |
604 | } |
605 | op = o; | |
606 | cp = c; | |
607 | sp = s; | |
608 | ||
609 | /* | |
610 | * check numeric validity of the extent | |
611 | */ | |
612 | if (c == 0) { | |
613 | do_warn( | |
507f4e33 | 614 | _("zero length extent (off = %llu, fsbno = %llu) in ino %llu\n"), |
2bd0ea18 | 615 | o, s, ino); |
3b6ac903 | 616 | PROCESS_BMBT_UNLOCK_RETURN(1); |
2bd0ea18 NS |
617 | } |
618 | if (type == XR_INO_RTDATA) { | |
619 | if (s >= mp->m_sb.sb_rblocks) { | |
620 | do_warn( | |
507f4e33 | 621 | _("inode %llu - bad rt extent start block number %llu, offset %llu\n"), |
2bd0ea18 | 622 | ino, s, o); |
3b6ac903 | 623 | PROCESS_BMBT_UNLOCK_RETURN(1); |
2bd0ea18 NS |
624 | } |
625 | if (s + c - 1 >= mp->m_sb.sb_rblocks) { | |
626 | do_warn( | |
507f4e33 | 627 | _("inode %llu - bad rt extent last block number %llu, offset %llu\n"), |
2bd0ea18 | 628 | ino, s + c - 1, o); |
3b6ac903 | 629 | PROCESS_BMBT_UNLOCK_RETURN(1); |
2bd0ea18 NS |
630 | } |
631 | if (s + c - 1 < s) { | |
632 | do_warn( | |
507f4e33 NS |
633 | _("inode %llu - bad rt extent overflows - start %llu, end %llu, " |
634 | "offset %llu\n"), | |
2bd0ea18 | 635 | ino, s, s + c - 1, o); |
3b6ac903 | 636 | PROCESS_BMBT_UNLOCK_RETURN(1); |
2bd0ea18 NS |
637 | } |
638 | } else { | |
1e77098c MV |
639 | switch (verify_dfsbno_range(mp, s, c)) { |
640 | case XR_DFSBNORANGE_VALID: | |
641 | break; | |
642 | case XR_DFSBNORANGE_BADSTART: | |
2bd0ea18 | 643 | do_warn( |
507f4e33 | 644 | _("inode %llu - bad extent starting block number %llu, offset %llu\n"), |
2bd0ea18 | 645 | ino, s, o); |
3b6ac903 | 646 | PROCESS_BMBT_UNLOCK_RETURN(1); |
1e77098c | 647 | case XR_DFSBNORANGE_BADEND: |
2bd0ea18 | 648 | do_warn( |
507f4e33 | 649 | _("inode %llu - bad extent last block number %llu, offset %llu\n"), |
2bd0ea18 | 650 | ino, s + c - 1, o); |
3b6ac903 | 651 | PROCESS_BMBT_UNLOCK_RETURN(1); |
1e77098c | 652 | case XR_DFSBNORANGE_OVERFLOW: |
2bd0ea18 | 653 | do_warn( |
1e77098c | 654 | |
507f4e33 NS |
655 | _("inode %llu - bad extent overflows - start %llu, end %llu, " |
656 | "offset %llu\n"), | |
2bd0ea18 | 657 | ino, s, s + c - 1, o); |
3b6ac903 | 658 | PROCESS_BMBT_UNLOCK_RETURN(1); |
2bd0ea18 NS |
659 | } |
660 | if (o >= fs_max_file_offset) { | |
661 | do_warn( | |
507f4e33 NS |
662 | _("inode %llu - extent offset too large - start %llu, count %llu, " |
663 | "offset %llu\n"), | |
2bd0ea18 | 664 | ino, s, c, o); |
3b6ac903 | 665 | PROCESS_BMBT_UNLOCK_RETURN(1); |
2bd0ea18 NS |
666 | } |
667 | } | |
668 | ||
669 | /* | |
670 | * realtime file data fork | |
671 | */ | |
672 | if (type == XR_INO_RTDATA && whichfork == XFS_DATA_FORK) { | |
673 | /* | |
674 | * XXX - verify that the blocks listed in the record | |
675 | * are multiples of an extent | |
676 | */ | |
218c4496 NS |
677 | if (XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb) == 0 |
678 | && (s % mp->m_sb.sb_rextsize != 0 || | |
679 | c % mp->m_sb.sb_rextsize != 0)) { | |
2bd0ea18 | 680 | do_warn( |
507f4e33 | 681 | _("malformed rt inode extent [%llu %llu] (fs rtext size = %u)\n"), |
2bd0ea18 | 682 | s, c, mp->m_sb.sb_rextsize); |
3b6ac903 | 683 | PROCESS_BMBT_UNLOCK_RETURN(1); |
2bd0ea18 NS |
684 | } |
685 | ||
686 | /* | |
687 | * XXX - set the appropriate number of extents | |
688 | */ | |
689 | for (b = s; b < s + c; b += mp->m_sb.sb_rextsize) { | |
690 | ext = (xfs_drtbno_t) b / mp->m_sb.sb_rextsize; | |
c1281304 GO |
691 | if (XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb) && |
692 | flag && (b % mp->m_sb.sb_rextsize != 0)) { | |
693 | pwe = 1; | |
694 | } else { | |
695 | pwe = 0; | |
696 | } | |
2bd0ea18 NS |
697 | |
698 | if (check_dups == 1) { | |
c1281304 GO |
699 | if (search_rt_dup_extent(mp, ext) && |
700 | !pwe) { | |
2bd0ea18 | 701 | do_warn( |
507f4e33 NS |
702 | _("data fork in rt ino %llu claims dup rt extent, off - %llu, " |
703 | "start - %llu, count %llu\n"), | |
2bd0ea18 | 704 | ino, o, s, c); |
3b6ac903 | 705 | PROCESS_BMBT_UNLOCK_RETURN(1); |
2bd0ea18 NS |
706 | } |
707 | continue; | |
708 | } | |
709 | ||
710 | state = get_rtbno_state(mp, ext); | |
711 | ||
712 | switch (state) { | |
713 | case XR_E_FREE: | |
714 | /* XXX - turn this back on after we | |
715 | run process_rtbitmap() in phase2 | |
716 | do_warn( | |
507f4e33 | 717 | _("%s fork in rt ino %llu claims free rt block %llu\n"), |
2bd0ea18 NS |
718 | forkname, ino, ext); |
719 | */ | |
720 | /* fall through ... */ | |
721 | case XR_E_UNKNOWN: | |
722 | set_rtbno_state(mp, ext, XR_E_INUSE); | |
723 | break; | |
724 | case XR_E_BAD_STATE: | |
725 | do_error( | |
507f4e33 | 726 | _("bad state in rt block map %llu\n"), ext); |
2bd0ea18 NS |
727 | abort(); |
728 | break; | |
729 | case XR_E_FS_MAP: | |
730 | case XR_E_INO: | |
731 | case XR_E_INUSE_FS: | |
732 | do_error( | |
507f4e33 | 733 | _("%s fork in rt inode %llu found metadata block %llu in %s bmap\n"), |
2bd0ea18 NS |
734 | forkname, ino, ext, ftype); |
735 | case XR_E_INUSE: | |
c1281304 GO |
736 | if (pwe) |
737 | break; | |
2bd0ea18 NS |
738 | case XR_E_MULT: |
739 | set_rtbno_state(mp, ext, XR_E_MULT); | |
740 | do_warn( | |
507f4e33 | 741 | _("%s fork in rt inode %llu claims used rt block %llu\n"), |
2bd0ea18 | 742 | forkname, ino, ext); |
3b6ac903 | 743 | PROCESS_BMBT_UNLOCK_RETURN(1); |
2bd0ea18 NS |
744 | case XR_E_FREE1: |
745 | default: | |
746 | do_error( | |
507f4e33 | 747 | _("illegal state %d in %s block map %llu\n"), |
2bd0ea18 NS |
748 | state, ftype, b); |
749 | } | |
750 | } | |
751 | ||
752 | /* | |
753 | * bump up the block counter | |
754 | */ | |
755 | *tot += c; | |
756 | ||
757 | /* | |
758 | * skip rest of loop processing since that's | |
759 | * all for regular file forks and attr forks | |
760 | */ | |
761 | continue; | |
762 | } | |
763 | ||
dfc130f3 | 764 | |
2bd0ea18 NS |
765 | /* |
766 | * regular file data fork or attribute fork | |
767 | */ | |
768 | if (blkmapp && *blkmapp) | |
769 | blkmap_set_ext(blkmapp, o, s, c); | |
1e77098c MV |
770 | /* |
771 | * Profiling shows that the following loop takes the | |
772 | * most time in all of xfs_repair. | |
773 | */ | |
774 | agno = XFS_FSB_TO_AGNO(mp, s); | |
775 | agbno = XFS_FSB_TO_AGBNO(mp, s); | |
776 | e = s + c; | |
3b6ac903 | 777 | PROCESS_BMBT_LOCK(agno); |
1e77098c | 778 | for (b = s; b < e; b++, agbno++) { |
2bd0ea18 NS |
779 | if (check_dups == 1) { |
780 | /* | |
781 | * if we're just checking the bmap for dups, | |
782 | * return if we find one, otherwise, continue | |
783 | * checking each entry without setting the | |
784 | * block bitmap | |
785 | */ | |
1e77098c | 786 | if (search_dup_extent(mp, agno, agbno)) { |
2bd0ea18 | 787 | do_warn( |
507f4e33 NS |
788 | _("%s fork in ino %llu claims dup extent, off - %llu, " |
789 | "start - %llu, cnt %llu\n"), | |
2bd0ea18 | 790 | forkname, ino, o, s, c); |
3b6ac903 | 791 | PROCESS_BMBT_UNLOCK_RETURN(1); |
2bd0ea18 NS |
792 | } |
793 | continue; | |
794 | } | |
795 | ||
dfc130f3 | 796 | /* FIX FOR BUG 653709 -- EKN |
2bd0ea18 | 797 | * realtime attribute fork, should be valid block number |
dfc130f3 | 798 | * in regular data space, not realtime partion. |
2bd0ea18 | 799 | */ |
dfc130f3 | 800 | if (type == XR_INO_RTDATA && whichfork == XFS_ATTR_FORK) { |
1e77098c | 801 | if (mp->m_sb.sb_agcount < agno) |
3b6ac903 | 802 | PROCESS_BMBT_UNLOCK_RETURN(1); |
dfc130f3 RC |
803 | } |
804 | ||
1e77098c MV |
805 | /* Process in chunks of 16 (XR_BB_UNIT/XR_BB) |
806 | * for common XR_E_UNKNOWN to XR_E_INUSE transition | |
807 | */ | |
808 | if (((agbno & XR_BB_MASK) == 0) && ((s + c - b) >= (XR_BB_UNIT/XR_BB))) { | |
809 | if (ba_bmap[agno][agbno>>XR_BB] == XR_E_UNKNOWN_LL) { | |
810 | ba_bmap[agno][agbno>>XR_BB] = XR_E_INUSE_LL; | |
811 | agbno += (XR_BB_UNIT/XR_BB) - 1; | |
812 | b += (XR_BB_UNIT/XR_BB) - 1; | |
813 | continue; | |
814 | } | |
815 | ||
816 | } | |
817 | ||
818 | state = get_agbno_state(mp, agno, agbno); | |
2bd0ea18 NS |
819 | switch (state) { |
820 | case XR_E_FREE: | |
821 | case XR_E_FREE1: | |
822 | do_warn( | |
507f4e33 | 823 | _("%s fork in ino %llu claims free block %llu\n"), |
2bd0ea18 NS |
824 | forkname, ino, (__uint64_t) b); |
825 | /* fall through ... */ | |
826 | case XR_E_UNKNOWN: | |
1e77098c | 827 | set_agbno_state(mp, agno, agbno, XR_E_INUSE); |
2bd0ea18 NS |
828 | break; |
829 | case XR_E_BAD_STATE: | |
507f4e33 | 830 | do_error(_("bad state in block map %llu\n"), b); |
2bd0ea18 NS |
831 | abort(); |
832 | break; | |
833 | case XR_E_FS_MAP: | |
834 | case XR_E_INO: | |
835 | case XR_E_INUSE_FS: | |
836 | do_warn( | |
507f4e33 | 837 | _("%s fork in inode %llu claims metadata block %llu\n"), |
2bd0ea18 | 838 | forkname, ino, (__uint64_t) b); |
3b6ac903 | 839 | PROCESS_BMBT_UNLOCK_RETURN(1); |
2bd0ea18 NS |
840 | case XR_E_INUSE: |
841 | case XR_E_MULT: | |
1e77098c | 842 | set_agbno_state(mp, agno, agbno, XR_E_MULT); |
2bd0ea18 | 843 | do_warn( |
507f4e33 | 844 | _("%s fork in %s inode %llu claims used block %llu\n"), |
2bd0ea18 | 845 | forkname, ftype, ino, (__uint64_t) b); |
3b6ac903 | 846 | PROCESS_BMBT_UNLOCK_RETURN(1); |
2bd0ea18 | 847 | default: |
507f4e33 NS |
848 | do_error( |
849 | _("illegal state %d in block map %llu\n"), | |
2bd0ea18 NS |
850 | state, b); |
851 | abort(); | |
852 | } | |
853 | } | |
854 | *tot += c; | |
855 | } | |
856 | ||
3b6ac903 | 857 | PROCESS_BMBT_UNLOCK_RETURN(0); |
2bd0ea18 NS |
858 | } |
859 | ||
860 | /* | |
861 | * return 1 if inode should be cleared, 0 otherwise, sets block bitmap | |
862 | * as a side-effect | |
863 | */ | |
864 | int | |
865 | process_bmbt_reclist( | |
866 | xfs_mount_t *mp, | |
867 | xfs_bmbt_rec_32_t *rp, | |
868 | int numrecs, | |
869 | int type, | |
870 | xfs_ino_t ino, | |
871 | xfs_drfsbno_t *tot, | |
872 | blkmap_t **blkmapp, | |
873 | xfs_dfiloff_t *first_key, | |
874 | xfs_dfiloff_t *last_key, | |
875 | int whichfork) | |
876 | { | |
877 | return(process_bmbt_reclist_int(mp, rp, numrecs, type, ino, tot, | |
878 | blkmapp, first_key, last_key, 0, | |
879 | whichfork)); | |
880 | } | |
881 | ||
882 | /* | |
883 | * return 1 if inode should be cleared, 0 otherwise, does not set | |
884 | * block bitmap | |
885 | */ | |
886 | int | |
887 | scan_bmbt_reclist( | |
888 | xfs_mount_t *mp, | |
889 | xfs_bmbt_rec_32_t *rp, | |
890 | int numrecs, | |
891 | int type, | |
892 | xfs_ino_t ino, | |
893 | xfs_drfsbno_t *tot, | |
894 | int whichfork) | |
895 | { | |
896 | xfs_dfiloff_t first_key = 0; | |
897 | xfs_dfiloff_t last_key = 0; | |
898 | ||
899 | return(process_bmbt_reclist_int(mp, rp, numrecs, type, ino, tot, | |
900 | NULL, &first_key, &last_key, 1, | |
901 | whichfork)); | |
902 | } | |
903 | ||
904 | /* | |
905 | * these two are meant for routines that read and work with inodes | |
906 | * one at a time where the inodes may be in any order (like walking | |
907 | * the unlinked lists to look for inodes). the caller is responsible | |
908 | * for writing/releasing the buffer. | |
909 | */ | |
910 | xfs_buf_t * | |
911 | get_agino_buf(xfs_mount_t *mp, | |
912 | xfs_agnumber_t agno, | |
913 | xfs_agino_t agino, | |
914 | xfs_dinode_t **dipp) | |
915 | { | |
916 | ino_tree_node_t *irec; | |
917 | xfs_buf_t *bp; | |
918 | int size; | |
919 | ||
920 | if ((irec = find_inode_rec(agno, agino)) == NULL) | |
921 | return(NULL); | |
dfc130f3 | 922 | |
2bd0ea18 NS |
923 | size = XFS_FSB_TO_BB(mp, MAX(1, XFS_INODES_PER_CHUNK/inodes_per_block)); |
924 | bp = libxfs_readbuf(mp->m_dev, XFS_AGB_TO_DADDR(mp, agno, | |
925 | XFS_AGINO_TO_AGBNO(mp, irec->ino_startnum)), size, 0); | |
926 | if (!bp) { | |
507f4e33 | 927 | do_warn(_("cannot read inode (%u/%u), disk block %lld\n"), |
2bd0ea18 NS |
928 | agno, irec->ino_startnum, |
929 | XFS_AGB_TO_DADDR(mp, agno, | |
930 | XFS_AGINO_TO_AGBNO(mp, irec->ino_startnum))); | |
931 | return(NULL); | |
932 | } | |
933 | ||
934 | *dipp = XFS_MAKE_IPTR(mp, bp, agino - | |
935 | XFS_OFFBNO_TO_AGINO(mp, XFS_AGINO_TO_AGBNO(mp, | |
936 | irec->ino_startnum), | |
937 | 0)); | |
938 | ||
939 | return(bp); | |
940 | } | |
941 | ||
942 | /* | |
943 | * these next routines return the filesystem blockno of the | |
944 | * block containing the block "bno" in the file whose bmap | |
945 | * tree (or extent list) is rooted by "rootblock". | |
946 | * | |
947 | * the next routines are utility routines for the third | |
948 | * routine, get_bmapi(). | |
949 | */ | |
950 | /* ARGSUSED */ | |
951 | xfs_dfsbno_t | |
952 | getfunc_extlist(xfs_mount_t *mp, | |
953 | xfs_ino_t ino, | |
954 | xfs_dinode_t *dip, | |
955 | xfs_dfiloff_t bno, | |
956 | int whichfork) | |
957 | { | |
958 | xfs_dfiloff_t fbno; | |
959 | xfs_dfilblks_t bcnt; | |
960 | xfs_dfsbno_t fsbno; | |
961 | xfs_dfsbno_t final_fsbno = NULLDFSBNO; | |
962 | xfs_bmbt_rec_32_t *rootblock = (xfs_bmbt_rec_32_t *) | |
46eca962 NS |
963 | XFS_DFORK_PTR(dip, whichfork); |
964 | xfs_extnum_t nextents = XFS_DFORK_NEXTENTS(dip, whichfork); | |
2bd0ea18 NS |
965 | int i; |
966 | int flag; | |
967 | ||
968 | for (i = 0; i < nextents; i++) { | |
969 | convert_extent(rootblock + i, &fbno, &fsbno, &bcnt, &flag); | |
970 | ||
971 | if (fbno <= bno && bno < fbno + bcnt) { | |
972 | final_fsbno = bno - fbno + fsbno; | |
973 | break; | |
974 | } | |
975 | } | |
976 | ||
977 | return(final_fsbno); | |
978 | } | |
979 | ||
980 | xfs_dfsbno_t | |
981 | getfunc_btree(xfs_mount_t *mp, | |
982 | xfs_ino_t ino, | |
983 | xfs_dinode_t *dip, | |
984 | xfs_dfiloff_t bno, | |
985 | int whichfork) | |
986 | { | |
987 | int i; | |
1e77098c | 988 | #ifdef DEBUG |
2bd0ea18 | 989 | int prev_level; |
1e77098c | 990 | #endif |
2bd0ea18 NS |
991 | int flag; |
992 | int found; | |
993 | xfs_bmbt_rec_32_t *rec; | |
994 | xfs_bmbt_ptr_t *pp; | |
995 | xfs_bmbt_key_t *key; | |
996 | xfs_bmdr_key_t *rkey; | |
997 | xfs_bmdr_ptr_t *rp; | |
998 | xfs_dfiloff_t fbno; | |
999 | xfs_dfsbno_t fsbno; | |
1000 | xfs_dfilblks_t bcnt; | |
1001 | xfs_buf_t *bp; | |
1002 | xfs_dfsbno_t final_fsbno = NULLDFSBNO; | |
1003 | xfs_bmbt_block_t *block; | |
1004 | xfs_bmdr_block_t *rootblock = (xfs_bmdr_block_t *) | |
46eca962 | 1005 | XFS_DFORK_PTR(dip, whichfork); |
2bd0ea18 NS |
1006 | |
1007 | ASSERT(rootblock->bb_level != 0); | |
1008 | /* | |
1009 | * deal with root block, it's got a slightly different | |
1010 | * header structure than interior nodes. We know that | |
1011 | * a btree should have at least 2 levels otherwise it | |
1012 | * would be an extent list. | |
1013 | */ | |
1014 | rkey = XFS_BTREE_KEY_ADDR( | |
46eca962 | 1015 | XFS_DFORK_SIZE(dip, mp, whichfork), |
2bd0ea18 | 1016 | xfs_bmdr, rootblock, 1, |
46eca962 NS |
1017 | XFS_BTREE_BLOCK_MAXRECS(XFS_DFORK_SIZE(dip, |
1018 | mp, whichfork), | |
2bd0ea18 NS |
1019 | xfs_bmdr, 1)); |
1020 | rp = XFS_BTREE_PTR_ADDR( | |
46eca962 | 1021 | XFS_DFORK_SIZE(dip, mp, whichfork), |
2bd0ea18 | 1022 | xfs_bmdr, rootblock, 1, |
46eca962 NS |
1023 | XFS_BTREE_BLOCK_MAXRECS(XFS_DFORK_SIZE(dip, |
1024 | mp, whichfork), | |
2bd0ea18 NS |
1025 | xfs_bmdr, 1)); |
1026 | for (found = -1, i = 0; i < rootblock->bb_numrecs - 1; i++) { | |
1027 | if (rkey[i].br_startoff <= bno | |
1028 | && bno < rkey[i+1].br_startoff) { | |
1029 | found = i; | |
1030 | break; | |
1031 | } | |
1032 | } | |
1033 | if (i == rootblock->bb_numrecs - 1 && bno >= rkey[i].br_startoff) | |
1034 | found = i; | |
1035 | ||
1036 | ASSERT(found != -1); | |
1037 | ||
1038 | fsbno = INT_GET(rp[found], ARCH_CONVERT); | |
1039 | ||
1040 | ASSERT(verify_dfsbno(mp, fsbno)); | |
1041 | ||
1042 | bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno), | |
1043 | XFS_FSB_TO_BB(mp, 1), 0); | |
1044 | if (!bp) { | |
507f4e33 | 1045 | do_error(_("cannot read bmap block %llu\n"), fsbno); |
2bd0ea18 NS |
1046 | return(NULLDFSBNO); |
1047 | } | |
1048 | block = XFS_BUF_TO_BMBT_BLOCK(bp); | |
1049 | ||
1050 | /* | |
1051 | * ok, now traverse any interior btree nodes | |
1052 | */ | |
1e77098c MV |
1053 | #ifdef DEBUG |
1054 | prev_level = INT_GET(block->bb_level, ARCH_CONVERT); | |
1055 | #endif | |
2bd0ea18 NS |
1056 | |
1057 | while (INT_GET(block->bb_level, ARCH_CONVERT) > 0) { | |
1e77098c | 1058 | #ifdef DEBUG |
2bd0ea18 NS |
1059 | ASSERT(INT_GET(block->bb_level, ARCH_CONVERT) < prev_level); |
1060 | ||
1061 | prev_level = INT_GET(block->bb_level, ARCH_CONVERT); | |
1e77098c | 1062 | #endif |
2bd0ea18 NS |
1063 | |
1064 | if (INT_GET(block->bb_numrecs, ARCH_CONVERT) > | |
1065 | mp->m_bmap_dmxr[1]) { | |
507f4e33 NS |
1066 | do_warn(_("# of bmap records in inode %llu exceeds max " |
1067 | "(%u, max - %u)\n"), | |
2bd0ea18 NS |
1068 | ino, INT_GET(block->bb_numrecs, ARCH_CONVERT), |
1069 | mp->m_bmap_dmxr[1]); | |
1070 | libxfs_putbuf(bp); | |
1071 | return(NULLDFSBNO); | |
1072 | } | |
1073 | if (verbose && INT_GET(block->bb_numrecs, ARCH_CONVERT) < | |
1074 | mp->m_bmap_dmnr[1]) { | |
507f4e33 NS |
1075 | do_warn(_("- # of bmap records in inode %llu less than " |
1076 | "minimum (%u, min - %u), proceeding ...\n"), | |
2bd0ea18 NS |
1077 | ino, INT_GET(block->bb_numrecs, ARCH_CONVERT), |
1078 | mp->m_bmap_dmnr[1]); | |
1079 | } | |
1080 | key = XFS_BTREE_KEY_ADDR(mp->m_sb.sb_blocksize, | |
1081 | xfs_bmbt, block, 1, mp->m_bmap_dmxr[1]); | |
1082 | pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, | |
1083 | xfs_bmbt, block, 1, mp->m_bmap_dmxr[1]); | |
1084 | for ( found = -1, i = 0; | |
1085 | i < INT_GET(block->bb_numrecs, ARCH_CONVERT) - 1; | |
1086 | i++) { | |
1087 | if (INT_GET(key[i].br_startoff, ARCH_CONVERT) <= bno && | |
1088 | bno < INT_GET(key[i+1].br_startoff, ARCH_CONVERT)) { | |
1089 | found = i; | |
1090 | break; | |
1091 | } | |
1092 | } | |
1093 | if (i == INT_GET(block->bb_numrecs, ARCH_CONVERT) - 1 && | |
1094 | bno >= INT_GET(key[i].br_startoff, ARCH_CONVERT)) | |
1095 | found = i; | |
1096 | ||
1097 | ASSERT(found != -1); | |
1098 | fsbno = INT_GET(pp[found], ARCH_CONVERT); | |
1099 | ||
1100 | ASSERT(verify_dfsbno(mp, fsbno)); | |
1101 | ||
1102 | /* | |
1103 | * release current btree block and read in the | |
1104 | * next btree block to be traversed | |
1105 | */ | |
1106 | libxfs_putbuf(bp); | |
1107 | bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno), | |
1108 | XFS_FSB_TO_BB(mp, 1), 0); | |
1109 | if (!bp) { | |
507f4e33 | 1110 | do_error(_("cannot read bmap block %llu\n"), fsbno); |
2bd0ea18 NS |
1111 | return(NULLDFSBNO); |
1112 | } | |
1113 | block = XFS_BUF_TO_BMBT_BLOCK(bp); | |
1114 | } | |
1115 | ||
1116 | /* | |
1117 | * current block must be a leaf block | |
1118 | */ | |
1119 | ASSERT(INT_GET(block->bb_level, ARCH_CONVERT) == 0); | |
1120 | if (INT_GET(block->bb_numrecs, ARCH_CONVERT) > mp->m_bmap_dmxr[0]) { | |
507f4e33 NS |
1121 | do_warn(_("# of bmap records in inode %llu greater than " |
1122 | "maximum (%u, max - %u)\n"), | |
2bd0ea18 NS |
1123 | ino, INT_GET(block->bb_numrecs, ARCH_CONVERT), |
1124 | mp->m_bmap_dmxr[0]); | |
1125 | libxfs_putbuf(bp); | |
1126 | return(NULLDFSBNO); | |
1127 | } | |
1128 | if (verbose && INT_GET(block->bb_numrecs, ARCH_CONVERT) < | |
1129 | mp->m_bmap_dmnr[0]) | |
507f4e33 NS |
1130 | do_warn(_("- # of bmap records in inode %llu less than minimum " |
1131 | "(%u, min - %u), continuing...\n"), | |
2bd0ea18 NS |
1132 | ino, INT_GET(block->bb_numrecs, ARCH_CONVERT), |
1133 | mp->m_bmap_dmnr[0]); | |
1134 | ||
1135 | rec = (xfs_bmbt_rec_32_t *)XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, | |
1136 | xfs_bmbt, block, 1, mp->m_bmap_dmxr[0]); | |
1137 | for (i = 0; i < INT_GET(block->bb_numrecs, ARCH_CONVERT); i++) { | |
1138 | convert_extent(rec + i, &fbno, &fsbno, &bcnt, &flag); | |
1139 | ||
1140 | if (fbno <= bno && bno < fbno + bcnt) { | |
1141 | final_fsbno = bno - fbno + fsbno; | |
1142 | break; | |
1143 | } | |
1144 | } | |
1145 | libxfs_putbuf(bp); | |
1146 | ||
1147 | if (final_fsbno == NULLDFSBNO) | |
507f4e33 | 1148 | do_warn(_("could not map block %llu\n"), bno); |
2bd0ea18 NS |
1149 | |
1150 | return(final_fsbno); | |
1151 | } | |
1152 | ||
1153 | /* | |
1154 | * this could be smarter. maybe we should have an open inode | |
1155 | * routine that would get the inode buffer and return back | |
1156 | * an inode handle. I'm betting for the moment that this | |
1157 | * is used only by the directory and attribute checking code | |
1158 | * and that the avl tree find and buffer cache search are | |
1159 | * relatively cheap. If they're too expensive, we'll just | |
1160 | * have to fix this and add an inode handle to the da btree | |
1161 | * cursor. | |
1162 | * | |
1163 | * caller is responsible for checking doubly referenced blocks | |
1164 | * and references to holes | |
1165 | */ | |
1166 | xfs_dfsbno_t | |
1167 | get_bmapi(xfs_mount_t *mp, xfs_dinode_t *dino_p, | |
1168 | xfs_ino_t ino_num, xfs_dfiloff_t bno, int whichfork) | |
1169 | { | |
1170 | xfs_dfsbno_t fsbno; | |
1171 | ||
46eca962 | 1172 | switch (XFS_DFORK_FORMAT(dino_p, whichfork)) { |
2bd0ea18 NS |
1173 | case XFS_DINODE_FMT_EXTENTS: |
1174 | fsbno = getfunc_extlist(mp, ino_num, dino_p, bno, whichfork); | |
1175 | break; | |
1176 | case XFS_DINODE_FMT_BTREE: | |
dfc130f3 | 1177 | fsbno = getfunc_btree(mp, ino_num, dino_p, bno, whichfork); |
2bd0ea18 NS |
1178 | break; |
1179 | case XFS_DINODE_FMT_LOCAL: | |
507f4e33 NS |
1180 | do_error(_("get_bmapi() called for local inode %llu\n"), |
1181 | ino_num); | |
2bd0ea18 NS |
1182 | fsbno = NULLDFSBNO; |
1183 | break; | |
1184 | default: | |
1185 | /* | |
1186 | * shouldn't happen | |
1187 | */ | |
507f4e33 | 1188 | do_error(_("bad inode format for inode %llu\n"), ino_num); |
2bd0ea18 NS |
1189 | fsbno = NULLDFSBNO; |
1190 | } | |
1191 | ||
1192 | return(fsbno); | |
1193 | } | |
1194 | ||
1195 | /* | |
1196 | * higher level inode processing stuff starts here: | |
1197 | * first, one utility routine for each type of inode | |
1198 | */ | |
1199 | ||
1200 | /* | |
1201 | * return 1 if inode should be cleared, 0 otherwise | |
1202 | */ | |
1203 | /* ARGSUSED */ | |
1204 | int | |
1205 | process_btinode( | |
1206 | xfs_mount_t *mp, | |
1207 | xfs_agnumber_t agno, | |
1208 | xfs_agino_t ino, | |
1209 | xfs_dinode_t *dip, | |
1210 | int type, | |
1211 | int *dirty, | |
1212 | xfs_drfsbno_t *tot, | |
1213 | __uint64_t *nex, | |
1214 | blkmap_t **blkmapp, | |
1215 | int whichfork, | |
1216 | int check_dups) | |
1217 | { | |
1218 | xfs_bmdr_block_t *dib; | |
1219 | xfs_dfiloff_t last_key; | |
1220 | xfs_dfiloff_t first_key = 0; | |
1221 | xfs_ino_t lino; | |
1222 | xfs_bmbt_ptr_t *pp; | |
1223 | xfs_bmbt_key_t *pkey; | |
1224 | char *forkname; | |
1225 | int i; | |
1226 | bmap_cursor_t cursor; | |
1227 | ||
46eca962 | 1228 | dib = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork); |
2bd0ea18 NS |
1229 | lino = XFS_AGINO_TO_INO(mp, agno, ino); |
1230 | *tot = 0; | |
1231 | *nex = 0; | |
1232 | ||
1233 | if (whichfork == XFS_DATA_FORK) | |
507f4e33 | 1234 | forkname = _("data"); |
2bd0ea18 | 1235 | else |
507f4e33 | 1236 | forkname = _("attr"); |
2bd0ea18 NS |
1237 | |
1238 | if (INT_GET(dib->bb_level, ARCH_CONVERT) == 0) { | |
1239 | /* | |
1240 | * This should never happen since a btree inode | |
1241 | * has to have at least one other block in the | |
1242 | * bmap in addition to the root block in the | |
1243 | * inode's data fork. | |
1244 | * | |
1245 | * XXX - if we were going to fix up the inode, | |
1246 | * we'd try to treat the fork as an interior | |
1247 | * node and see if we could get an accurate | |
1248 | * level value from one of the blocks pointed | |
1249 | * to by the pointers in the fork. For now | |
1250 | * though, we just bail (and blow out the inode). | |
1251 | */ | |
507f4e33 | 1252 | do_warn(_("bad level 0 in inode %llu bmap btree root block\n"), |
2bd0ea18 NS |
1253 | XFS_AGINO_TO_INO(mp, agno, ino)); |
1254 | return(1); | |
1255 | } | |
1256 | /* | |
1257 | * use bmdr/dfork_dsize since the root block is in the data fork | |
1258 | */ | |
1259 | init_bm_cursor(&cursor, INT_GET(dib->bb_level, ARCH_CONVERT) + 1); | |
1260 | ||
1261 | if (XFS_BMDR_SPACE_CALC(INT_GET(dib->bb_numrecs, ARCH_CONVERT)) > | |
1262 | ((whichfork == XFS_DATA_FORK) ? | |
46eca962 NS |
1263 | XFS_DFORK_DSIZE(dip, mp) : |
1264 | XFS_DFORK_ASIZE(dip, mp))) { | |
2bd0ea18 | 1265 | do_warn( |
507f4e33 NS |
1266 | _("indicated size of %s btree root (%d bytes) greater than space in " |
1267 | "inode %llu %s fork\n"), | |
1268 | forkname, XFS_BMDR_SPACE_CALC(INT_GET(dib->bb_numrecs, | |
1269 | ARCH_CONVERT)), | |
2bd0ea18 NS |
1270 | lino, forkname); |
1271 | return(1); | |
1272 | } | |
1273 | ||
507f4e33 | 1274 | pp = XFS_BTREE_PTR_ADDR( |
46eca962 | 1275 | XFS_DFORK_SIZE(dip, mp, whichfork), |
2bd0ea18 | 1276 | xfs_bmdr, dib, 1, |
507f4e33 | 1277 | XFS_BTREE_BLOCK_MAXRECS( |
46eca962 | 1278 | XFS_DFORK_SIZE(dip, mp, whichfork), |
2bd0ea18 | 1279 | xfs_bmdr, 0)); |
507f4e33 | 1280 | pkey = XFS_BTREE_KEY_ADDR( |
46eca962 | 1281 | XFS_DFORK_SIZE(dip, mp, whichfork), |
2bd0ea18 | 1282 | xfs_bmdr, dib, 1, |
507f4e33 | 1283 | XFS_BTREE_BLOCK_MAXRECS( |
46eca962 | 1284 | XFS_DFORK_SIZE(dip, mp, whichfork), |
2bd0ea18 NS |
1285 | xfs_bmdr, 0)); |
1286 | ||
1287 | last_key = NULLDFILOFF; | |
1288 | ||
1289 | for (i = 0; i < INT_GET(dib->bb_numrecs, ARCH_CONVERT); i++) { | |
1290 | /* | |
1291 | * XXX - if we were going to do more to fix up the inode | |
1292 | * btree, we'd do it right here. For now, if there's a | |
1293 | * problem, we'll bail out and presumably clear the inode. | |
1294 | */ | |
1295 | if (!verify_dfsbno(mp, INT_GET(pp[i], ARCH_CONVERT))) { | |
507f4e33 | 1296 | do_warn(_("bad bmap btree ptr 0x%llx in ino %llu\n"), |
2bd0ea18 NS |
1297 | INT_GET(pp[i], ARCH_CONVERT), lino); |
1298 | return(1); | |
1299 | } | |
1300 | ||
1301 | if (scan_lbtree((xfs_dfsbno_t)INT_GET(pp[i], ARCH_CONVERT), INT_GET(dib->bb_level, ARCH_CONVERT), | |
1302 | scanfunc_bmap, type, whichfork, | |
1303 | lino, tot, nex, blkmapp, &cursor, | |
1304 | 1, check_dups)) | |
1305 | return(1); | |
1306 | /* | |
1307 | * fix key (offset) mismatches between the keys in root | |
1308 | * block records and the first key of each child block. | |
1309 | * fixes cases where entries have been shifted between | |
1310 | * blocks but the parent hasn't been updated | |
1311 | */ | |
1312 | if (check_dups == 0 && | |
507f4e33 NS |
1313 | cursor.level[INT_GET(dib->bb_level, |
1314 | ARCH_CONVERT)-1].first_key != | |
1315 | INT_GET(pkey[i].br_startoff, ARCH_CONVERT)) { | |
2bd0ea18 NS |
1316 | if (!no_modify) { |
1317 | do_warn( | |
507f4e33 NS |
1318 | _("correcting key in bmbt root (was %llu, now %llu) in inode " |
1319 | "%llu %s fork\n"), | |
1320 | INT_GET(pkey[i].br_startoff, | |
1321 | ARCH_CONVERT), | |
1322 | cursor.level[INT_GET(dib->bb_level, | |
1323 | ARCH_CONVERT)-1].first_key, | |
2bd0ea18 NS |
1324 | XFS_AGINO_TO_INO(mp, agno, ino), |
1325 | forkname); | |
1326 | *dirty = 1; | |
507f4e33 NS |
1327 | INT_SET(pkey[i].br_startoff, ARCH_CONVERT, |
1328 | cursor.level[INT_GET(dib->bb_level, | |
1329 | ARCH_CONVERT)-1].first_key); | |
2bd0ea18 NS |
1330 | } else { |
1331 | do_warn( | |
507f4e33 NS |
1332 | _("bad key in bmbt root (is %llu, would reset to %llu) in inode " |
1333 | "%llu %s fork\n"), | |
1334 | INT_GET(pkey[i].br_startoff, | |
1335 | ARCH_CONVERT), | |
1336 | cursor.level[INT_GET(dib->bb_level, | |
1337 | ARCH_CONVERT)-1].first_key, | |
2bd0ea18 NS |
1338 | XFS_AGINO_TO_INO(mp, agno, ino), |
1339 | forkname); | |
1340 | } | |
1341 | } | |
1342 | /* | |
1343 | * make sure that keys are in ascending order. blow out | |
1344 | * inode if the ordering doesn't hold | |
1345 | */ | |
1346 | if (check_dups == 0) { | |
1347 | if (last_key != NULLDFILOFF && last_key >= | |
507f4e33 NS |
1348 | cursor.level[INT_GET(dib->bb_level, |
1349 | ARCH_CONVERT)-1].first_key) { | |
2bd0ea18 | 1350 | do_warn( |
507f4e33 | 1351 | _("out of order bmbt root key %llu in inode %llu %s fork\n"), |
2bd0ea18 NS |
1352 | first_key, |
1353 | XFS_AGINO_TO_INO(mp, agno, ino), | |
1354 | forkname); | |
1355 | return(1); | |
1356 | } | |
507f4e33 NS |
1357 | last_key = cursor.level[INT_GET(dib->bb_level, |
1358 | ARCH_CONVERT)-1].first_key; | |
2bd0ea18 NS |
1359 | } |
1360 | } | |
1361 | /* | |
1362 | * Check that the last child block's forward sibling pointer | |
1363 | * is NULL. | |
1364 | */ | |
1365 | if (check_dups == 0 && | |
1366 | cursor.level[0].right_fsbno != NULLDFSBNO) { | |
1367 | do_warn( | |
507f4e33 | 1368 | _("bad fwd (right) sibling pointer (saw %llu should be NULLDFSBNO)\n"), |
2bd0ea18 NS |
1369 | cursor.level[0].right_fsbno); |
1370 | do_warn( | |
507f4e33 | 1371 | _("\tin inode %u (%s fork) bmap btree block %llu\n"), |
2bd0ea18 NS |
1372 | XFS_AGINO_TO_INO(mp, agno, ino), forkname, |
1373 | cursor.level[0].fsbno); | |
1374 | return(1); | |
1375 | } | |
dfc130f3 | 1376 | |
2bd0ea18 NS |
1377 | return(0); |
1378 | } | |
1379 | ||
1380 | /* | |
1381 | * return 1 if inode should be cleared, 0 otherwise | |
1382 | */ | |
1383 | /* ARGSUSED */ | |
1384 | int | |
1385 | process_exinode( | |
1386 | xfs_mount_t *mp, | |
1387 | xfs_agnumber_t agno, | |
1388 | xfs_agino_t ino, | |
1389 | xfs_dinode_t *dip, | |
1390 | int type, | |
1391 | int *dirty, | |
1392 | xfs_drfsbno_t *tot, | |
1393 | __uint64_t *nex, | |
1394 | blkmap_t **blkmapp, | |
1395 | int whichfork, | |
1396 | int check_dups) | |
1397 | { | |
1398 | xfs_ino_t lino; | |
1399 | xfs_bmbt_rec_32_t *rp; | |
1400 | xfs_dfiloff_t first_key; | |
1401 | xfs_dfiloff_t last_key; | |
1402 | ||
1403 | lino = XFS_AGINO_TO_INO(mp, agno, ino); | |
46eca962 | 1404 | rp = (xfs_bmbt_rec_32_t *)XFS_DFORK_PTR(dip, whichfork); |
2bd0ea18 | 1405 | *tot = 0; |
46eca962 | 1406 | *nex = XFS_DFORK_NEXTENTS(dip, whichfork); |
2bd0ea18 NS |
1407 | /* |
1408 | * XXX - if we were going to fix up the btree record, | |
1409 | * we'd do it right here. For now, if there's a problem, | |
1410 | * we'll bail out and presumably clear the inode. | |
1411 | */ | |
1412 | if (check_dups == 0) | |
1413 | return(process_bmbt_reclist(mp, rp, *nex, type, lino, | |
1414 | tot, blkmapp, &first_key, &last_key, | |
1415 | whichfork)); | |
1416 | else | |
1417 | return(scan_bmbt_reclist(mp, rp, *nex, type, lino, tot, | |
1418 | whichfork)); | |
1419 | } | |
1420 | ||
1421 | /* | |
1422 | * return 1 if inode should be cleared, 0 otherwise | |
1423 | */ | |
1424 | /* ARGSUSED */ | |
1425 | int | |
1426 | process_lclinode( | |
1427 | xfs_mount_t *mp, | |
1428 | xfs_agnumber_t agno, | |
1429 | xfs_agino_t ino, | |
1430 | xfs_dinode_t *dip, | |
1431 | int type, | |
1432 | int *dirty, | |
1433 | xfs_drfsbno_t *tot, | |
1434 | __uint64_t *nex, | |
1435 | blkmap_t **blkmapp, | |
1436 | int whichfork, | |
1437 | int check_dups) | |
1438 | { | |
1439 | xfs_attr_shortform_t *asf; | |
1440 | xfs_dinode_core_t *dic; | |
1441 | xfs_ino_t lino; | |
1442 | ||
1443 | *tot = 0; | |
1444 | *nex = 0; /* local inodes have 0 extents */ | |
1445 | ||
1446 | dic = &dip->di_core; | |
1447 | lino = XFS_AGINO_TO_INO(mp, agno, ino); | |
1448 | if (whichfork == XFS_DATA_FORK && | |
507f4e33 | 1449 | INT_GET(dic->di_size, ARCH_CONVERT) > |
46eca962 | 1450 | XFS_DFORK_DSIZE(dip, mp)) { |
2bd0ea18 | 1451 | do_warn( |
507f4e33 NS |
1452 | _("local inode %llu data fork is too large (size = %lld, max = %d)\n"), |
1453 | lino, INT_GET(dic->di_size, ARCH_CONVERT), | |
46eca962 | 1454 | XFS_DFORK_DSIZE(dip, mp)); |
2bd0ea18 NS |
1455 | return(1); |
1456 | } else if (whichfork == XFS_ATTR_FORK) { | |
507f4e33 | 1457 | asf = (xfs_attr_shortform_t *) |
46eca962 | 1458 | XFS_DFORK_APTR(dip); |
507f4e33 | 1459 | if (INT_GET(asf->hdr.totsize, ARCH_CONVERT) > |
46eca962 | 1460 | XFS_DFORK_ASIZE(dip, mp)) { |
2bd0ea18 | 1461 | do_warn( |
507f4e33 NS |
1462 | _("local inode %llu attr fork too large (size %d, max = %d)\n"), |
1463 | lino, INT_GET(asf->hdr.totsize, ARCH_CONVERT), | |
46eca962 | 1464 | XFS_DFORK_ASIZE(dip, mp)); |
2bd0ea18 NS |
1465 | return(1); |
1466 | } | |
507f4e33 NS |
1467 | if (INT_GET(asf->hdr.totsize, ARCH_CONVERT) < |
1468 | sizeof(xfs_attr_sf_hdr_t)) { | |
2bd0ea18 | 1469 | do_warn( |
507f4e33 NS |
1470 | _("local inode %llu attr too small (size = %d, min size = %d)\n"), |
1471 | lino, INT_GET(asf->hdr.totsize, ARCH_CONVERT), | |
1472 | sizeof(xfs_attr_sf_hdr_t)); | |
2bd0ea18 NS |
1473 | return(1); |
1474 | } | |
1475 | } | |
1476 | ||
1477 | return(0); | |
1478 | } | |
1479 | ||
1480 | int | |
1481 | process_symlink_extlist(xfs_mount_t *mp, xfs_ino_t lino, xfs_dinode_t *dino) | |
1482 | { | |
1483 | xfs_dfsbno_t start; /* start */ | |
1484 | xfs_dfilblks_t cnt; /* count */ | |
1485 | xfs_dfiloff_t offset; /* offset */ | |
1486 | xfs_dfiloff_t expected_offset; | |
1487 | xfs_bmbt_rec_32_t *rp; | |
1488 | int numrecs; | |
1489 | int i; | |
1490 | int max_blocks; | |
1491 | int whichfork = XFS_DATA_FORK; | |
1492 | int flag; | |
1493 | ||
507f4e33 | 1494 | if (INT_GET(dino->di_core.di_size, ARCH_CONVERT) <= |
46eca962 | 1495 | XFS_DFORK_SIZE(dino, mp, whichfork)) { |
2bd0ea18 NS |
1496 | if (dino->di_core.di_format == XFS_DINODE_FMT_LOCAL) { |
1497 | return(0); | |
1498 | } else { | |
1499 | do_warn( | |
507f4e33 | 1500 | _("mismatch between format (%d) and size (%lld) in symlink ino %llu\n"), |
2bd0ea18 NS |
1501 | dino->di_core.di_format, |
1502 | INT_GET(dino->di_core.di_size, ARCH_CONVERT), | |
1503 | lino); | |
1504 | return(1); | |
1505 | } | |
1506 | } else if (dino->di_core.di_format == XFS_DINODE_FMT_LOCAL) { | |
1507 | do_warn( | |
507f4e33 | 1508 | _("mismatch between format (%d) and size (%lld) in symlink inode %llu\n"), |
2bd0ea18 NS |
1509 | dino->di_core.di_format, |
1510 | INT_GET(dino->di_core.di_size, ARCH_CONVERT), | |
1511 | lino); | |
1512 | return(1); | |
1513 | } | |
1514 | ||
46eca962 NS |
1515 | rp = (xfs_bmbt_rec_32_t *)XFS_DFORK_PTR(dino, whichfork); |
1516 | numrecs = XFS_DFORK_NEXTENTS(dino, whichfork); | |
2bd0ea18 NS |
1517 | |
1518 | /* | |
1519 | * the max # of extents in a symlink inode is equal to the | |
dfc130f3 | 1520 | * number of max # of blocks required to store the symlink |
2bd0ea18 NS |
1521 | */ |
1522 | if (numrecs > max_symlink_blocks) { | |
1523 | do_warn( | |
507f4e33 | 1524 | _("bad number of extents (%d) in symlink %llu data fork\n"), |
2bd0ea18 NS |
1525 | numrecs, lino); |
1526 | return(1); | |
1527 | } | |
1528 | ||
1529 | max_blocks = max_symlink_blocks; | |
1530 | expected_offset = 0; | |
1531 | ||
1532 | for (i = 0; numrecs > 0; i++, numrecs--) { | |
1533 | convert_extent(rp, &offset, &start, &cnt, &flag); | |
1534 | ||
1535 | if (offset != expected_offset) { | |
1536 | do_warn( | |
507f4e33 | 1537 | _("bad extent #%d offset (%llu) in symlink %llu data fork\n"), |
2bd0ea18 NS |
1538 | i, offset, lino); |
1539 | return(1); | |
1540 | } | |
1541 | if (cnt == 0 || cnt > max_blocks) { | |
1542 | do_warn( | |
507f4e33 | 1543 | _("bad extent #%d count (%llu) in symlink %llu data fork\n"), |
2bd0ea18 NS |
1544 | i, cnt, lino); |
1545 | return(1); | |
1546 | } | |
1547 | ||
1548 | max_blocks -= cnt; | |
1549 | expected_offset += cnt; | |
1550 | } | |
1551 | ||
1552 | return(0); | |
1553 | } | |
1554 | ||
1555 | /* | |
1556 | * takes a name and length and returns 1 if the name contains | |
1557 | * a \0, returns 0 otherwise | |
1558 | */ | |
1559 | int | |
1560 | null_check(char *name, int length) | |
1561 | { | |
1562 | int i; | |
1563 | ||
1564 | ASSERT(length < MAXPATHLEN); | |
1565 | ||
1566 | for (i = 0; i < length; i++, name++) { | |
1567 | if (*name == '\0') | |
1568 | return(1); | |
1569 | } | |
1570 | ||
1571 | return(0); | |
1572 | } | |
1573 | ||
1574 | /* | |
1575 | * like usual, returns 0 if everything's ok and 1 if something's | |
1576 | * bogus | |
1577 | */ | |
1578 | int | |
1579 | process_symlink(xfs_mount_t *mp, xfs_ino_t lino, xfs_dinode_t *dino, | |
1580 | blkmap_t *blkmap) | |
1581 | { | |
1582 | xfs_dfsbno_t fsbno; | |
1583 | xfs_dinode_core_t *dinoc = &dino->di_core; | |
1584 | xfs_buf_t *bp = NULL; | |
1585 | char *symlink, *cptr, *buf_data; | |
1586 | int i, size, amountdone; | |
1587 | char data[MAXPATHLEN]; | |
1588 | ||
1589 | /* | |
1590 | * check size against kernel symlink limits. we know | |
1591 | * size is consistent with inode storage format -- e.g. | |
1592 | * the inode is structurally ok so we don't have to check | |
1593 | * for that | |
1594 | */ | |
1595 | if (INT_GET(dinoc->di_size, ARCH_CONVERT) >= MAXPATHLEN) { | |
507f4e33 | 1596 | do_warn(_("symlink in inode %llu too long (%lld chars)\n"), |
2bd0ea18 NS |
1597 | lino, INT_GET(dinoc->di_size, ARCH_CONVERT)); |
1598 | return(1); | |
1599 | } | |
1600 | ||
1601 | /* | |
1602 | * have to check symlink component by component. | |
1603 | * get symlink contents into data area | |
1604 | */ | |
1605 | symlink = &data[0]; | |
1606 | if (INT_GET(dinoc->di_size, ARCH_CONVERT) | |
46eca962 | 1607 | <= XFS_DFORK_DSIZE(dino, mp)) { |
2bd0ea18 NS |
1608 | /* |
1609 | * local symlink, just copy the symlink out of the | |
1610 | * inode into the data area | |
1611 | */ | |
46eca962 | 1612 | bcopy((char *)XFS_DFORK_DPTR(dino), |
2bd0ea18 NS |
1613 | symlink, INT_GET(dinoc->di_size, ARCH_CONVERT)); |
1614 | } else { | |
1615 | /* | |
1616 | * stored in a meta-data file, have to bmap one block | |
1617 | * at a time and copy the symlink into the data area | |
1618 | */ | |
1619 | i = size = amountdone = 0; | |
1620 | cptr = symlink; | |
1621 | ||
1622 | while (amountdone < INT_GET(dinoc->di_size, ARCH_CONVERT)) { | |
1623 | fsbno = blkmap_get(blkmap, i); | |
1624 | if (fsbno != NULLDFSBNO) | |
1625 | bp = libxfs_readbuf(mp->m_dev, | |
1626 | XFS_FSB_TO_DADDR(mp, fsbno), | |
1627 | XFS_FSB_TO_BB(mp, 1), 0); | |
1628 | if (!bp || fsbno == NULLDFSBNO) { | |
507f4e33 NS |
1629 | do_warn( |
1630 | _("cannot read inode %llu, file block %d, disk block %llu\n"), | |
1631 | lino, i, fsbno); | |
2bd0ea18 NS |
1632 | return(1); |
1633 | } | |
1634 | ||
1635 | buf_data = (char *)XFS_BUF_PTR(bp); | |
1636 | size = MIN(INT_GET(dinoc->di_size, ARCH_CONVERT) | |
1637 | - amountdone, (int)XFS_FSB_TO_BB(mp, 1)*BBSIZE); | |
1638 | bcopy(buf_data, cptr, size); | |
1639 | cptr += size; | |
1640 | amountdone += size; | |
1641 | i++; | |
1642 | libxfs_putbuf(bp); | |
1643 | } | |
1644 | } | |
1645 | data[INT_GET(dinoc->di_size, ARCH_CONVERT)] = '\0'; | |
1646 | ||
1647 | /* | |
1648 | * check for nulls | |
1649 | */ | |
1650 | if (null_check(symlink, (int) INT_GET(dinoc->di_size, ARCH_CONVERT))) { | |
507f4e33 NS |
1651 | do_warn( |
1652 | _("found illegal null character in symlink inode %llu\n"), | |
2bd0ea18 NS |
1653 | lino); |
1654 | return(1); | |
1655 | } | |
1656 | ||
1657 | /* | |
1658 | * check for any component being too long | |
1659 | */ | |
1660 | if (INT_GET(dinoc->di_size, ARCH_CONVERT) >= MAXNAMELEN) { | |
1661 | cptr = strchr(symlink, '/'); | |
1662 | ||
1663 | while (cptr != NULL) { | |
1664 | if (cptr - symlink >= MAXNAMELEN) { | |
1665 | do_warn( | |
507f4e33 | 1666 | _("component of symlink in inode %llu too long\n"), |
2bd0ea18 NS |
1667 | lino); |
1668 | return(1); | |
1669 | } | |
1670 | symlink = cptr + 1; | |
1671 | cptr = strchr(symlink, '/'); | |
1672 | } | |
1673 | ||
1674 | if (strlen(symlink) >= MAXNAMELEN) { | |
507f4e33 NS |
1675 | do_warn( |
1676 | _("component of symlink in inode %llu too long\n"), | |
2bd0ea18 NS |
1677 | lino); |
1678 | return(1); | |
1679 | } | |
1680 | } | |
1681 | ||
1682 | return(0); | |
1683 | } | |
1684 | ||
1685 | /* | |
1686 | * called to process the set of misc inode special inode types | |
1687 | * that have no associated data storage (fifos, pipes, devices, etc.). | |
1688 | */ | |
1689 | /* ARGSUSED */ | |
1690 | int | |
1691 | process_misc_ino_types(xfs_mount_t *mp, | |
1692 | xfs_dinode_t *dino, | |
1693 | xfs_ino_t lino, | |
1694 | int type) | |
1695 | { | |
1696 | /* | |
1697 | * disallow mountpoint inodes until such time as the | |
1698 | * kernel actually allows them to be created (will | |
1699 | * probably require a superblock version rev, sigh). | |
1700 | */ | |
1701 | if (type == XR_INO_MOUNTPOINT) { | |
507f4e33 | 1702 | do_warn(_("inode %llu has bad inode type (IFMNT)\n"), lino); |
2bd0ea18 NS |
1703 | return(1); |
1704 | } | |
1705 | ||
1706 | /* | |
1707 | * must also have a zero size | |
1708 | */ | |
1709 | if (INT_GET(dino->di_core.di_size, ARCH_CONVERT) != 0) { | |
1710 | switch (type) { | |
1711 | case XR_INO_CHRDEV: | |
507f4e33 NS |
1712 | do_warn(_("size of character device inode %llu != 0 " |
1713 | "(%lld bytes)\n"), lino, | |
2bd0ea18 NS |
1714 | INT_GET(dino->di_core.di_size, ARCH_CONVERT)); |
1715 | break; | |
1716 | case XR_INO_BLKDEV: | |
507f4e33 NS |
1717 | do_warn(_("size of block device inode %llu != 0 " |
1718 | "(%lld bytes)\n"), lino, | |
2bd0ea18 NS |
1719 | INT_GET(dino->di_core.di_size, ARCH_CONVERT)); |
1720 | break; | |
1721 | case XR_INO_SOCK: | |
507f4e33 NS |
1722 | do_warn(_("size of socket inode %llu != 0 " |
1723 | "(%lld bytes)\n"), lino, | |
2bd0ea18 NS |
1724 | INT_GET(dino->di_core.di_size, ARCH_CONVERT)); |
1725 | break; | |
1726 | case XR_INO_FIFO: | |
507f4e33 NS |
1727 | do_warn(_("size of fifo inode %llu != 0 " |
1728 | "(%lld bytes)\n"), lino, | |
2bd0ea18 NS |
1729 | INT_GET(dino->di_core.di_size, ARCH_CONVERT)); |
1730 | break; | |
1731 | default: | |
507f4e33 NS |
1732 | do_warn(_("Internal error - process_misc_ino_types, " |
1733 | "illegal type %d\n"), type); | |
2bd0ea18 NS |
1734 | abort(); |
1735 | } | |
1736 | ||
1737 | return(1); | |
1738 | } | |
1739 | ||
1740 | return(0); | |
1741 | } | |
1742 | ||
1e77098c | 1743 | static __inline int |
2bd0ea18 NS |
1744 | process_misc_ino_types_blocks(xfs_drfsbno_t totblocks, xfs_ino_t lino, int type) |
1745 | { | |
1746 | /* | |
1747 | * you can not enforce all misc types have zero data fork blocks | |
1748 | * by checking dino->di_core.di_nblocks because atotblocks (attribute | |
1749 | * blocks) are part of nblocks. We must check this later when atotblocks | |
dfc130f3 | 1750 | * has been calculated or by doing a simple check that anExtents == 0. |
2bd0ea18 NS |
1751 | * We must also guarantee that totblocks is 0. Thus nblocks checking |
1752 | * will be done later in process_dinode_int for misc types. | |
1753 | */ | |
1754 | ||
1755 | if (totblocks != 0) { | |
1756 | switch (type) { | |
1757 | case XR_INO_CHRDEV: | |
1758 | do_warn( | |
507f4e33 | 1759 | _("size of character device inode %llu != 0 (%llu blocks)\n"), |
2bd0ea18 NS |
1760 | lino, totblocks); |
1761 | break; | |
1762 | case XR_INO_BLKDEV: | |
1763 | do_warn( | |
507f4e33 | 1764 | _("size of block device inode %llu != 0 (%llu blocks)\n"), |
2bd0ea18 NS |
1765 | lino, totblocks); |
1766 | break; | |
1767 | case XR_INO_SOCK: | |
1768 | do_warn( | |
507f4e33 | 1769 | _("size of socket inode %llu != 0 (%llu blocks)\n"), |
2bd0ea18 NS |
1770 | lino, totblocks); |
1771 | break; | |
1772 | case XR_INO_FIFO: | |
1773 | do_warn( | |
507f4e33 | 1774 | _("size of fifo inode %llu != 0 (%llu blocks)\n"), |
2bd0ea18 NS |
1775 | lino, totblocks); |
1776 | break; | |
1777 | default: | |
1778 | return(0); | |
1779 | } | |
1780 | return(1); | |
1781 | } | |
1782 | return (0); | |
1783 | } | |
1784 | ||
1785 | /* | |
1786 | * returns 0 if the inode is ok, 1 if the inode is corrupt | |
1787 | * check_dups can be set to 1 *only* when called by the | |
1788 | * first pass of the duplicate block checking of phase 4. | |
1789 | * *dirty is set > 0 if the dinode has been altered and | |
1790 | * needs to be written out. | |
1791 | * | |
1792 | * for detailed, info, look at process_dinode() comments. | |
1793 | */ | |
1794 | /* ARGSUSED */ | |
1795 | int | |
1796 | process_dinode_int(xfs_mount_t *mp, | |
1797 | xfs_dinode_t *dino, | |
1798 | xfs_agnumber_t agno, | |
1799 | xfs_agino_t ino, | |
1800 | int was_free, /* 1 if inode is currently free */ | |
1801 | int *dirty, /* out == > 0 if inode is now dirty */ | |
1802 | int *cleared, /* out == 1 if inode was cleared */ | |
1803 | int *used, /* out == 1 if inode is in use */ | |
1804 | int verify_mode, /* 1 == verify but don't modify inode */ | |
1805 | int uncertain, /* 1 == inode is uncertain */ | |
1806 | int ino_discovery, /* 1 == check dirs for unknown inodes */ | |
1807 | int check_dups, /* 1 == check if inode claims | |
1808 | * duplicate blocks */ | |
1809 | int extra_attr_check, /* 1 == do attribute format and value checks */ | |
1810 | int *isa_dir, /* out == 1 if inode is a directory */ | |
1811 | xfs_ino_t *parent) /* out -- parent if ino is a dir */ | |
1812 | { | |
1813 | xfs_drfsbno_t totblocks = 0; | |
1814 | xfs_drfsbno_t atotblocks = 0; | |
1815 | xfs_dinode_core_t *dinoc; | |
1816 | char *rstring; | |
1817 | int type; | |
1818 | int rtype; | |
1819 | int do_rt; | |
1820 | int err; | |
1821 | int retval = 0; | |
1822 | __uint64_t nextents; | |
1823 | __uint64_t anextents; | |
1824 | xfs_ino_t lino; | |
1825 | const int is_free = 0; | |
1826 | const int is_used = 1; | |
1827 | int repair = 0; | |
1828 | blkmap_t *ablkmap = NULL; | |
1829 | blkmap_t *dblkmap = NULL; | |
1830 | static char okfmts[] = { | |
1831 | 0, /* free inode */ | |
1832 | 1 << XFS_DINODE_FMT_DEV, /* FIFO */ | |
1833 | 1 << XFS_DINODE_FMT_DEV, /* CHR */ | |
1834 | 0, /* type 3 unused */ | |
1835 | (1 << XFS_DINODE_FMT_LOCAL) | | |
1836 | (1 << XFS_DINODE_FMT_EXTENTS) | | |
1837 | (1 << XFS_DINODE_FMT_BTREE), /* DIR */ | |
1838 | 0, /* type 5 unused */ | |
1839 | 1 << XFS_DINODE_FMT_DEV, /* BLK */ | |
1840 | 0, /* type 7 unused */ | |
1841 | (1 << XFS_DINODE_FMT_EXTENTS) | | |
1842 | (1 << XFS_DINODE_FMT_BTREE), /* REG */ | |
1843 | 0, /* type 9 unused */ | |
1844 | (1 << XFS_DINODE_FMT_LOCAL) | | |
1845 | (1 << XFS_DINODE_FMT_EXTENTS), /* LNK */ | |
1846 | 0, /* type 11 unused */ | |
1847 | 1 << XFS_DINODE_FMT_DEV, /* SOCK */ | |
1848 | 0, /* type 13 unused */ | |
1849 | 1 << XFS_DINODE_FMT_UUID, /* MNT */ | |
1850 | 0 /* type 15 unused */ | |
1851 | }; | |
1852 | ||
1853 | retval = 0; | |
1854 | totblocks = atotblocks = 0; | |
1855 | *dirty = *isa_dir = *cleared = 0; | |
1856 | *used = is_used; | |
1857 | type = rtype = XR_INO_UNKNOWN; | |
1858 | rstring = NULL; | |
1859 | do_rt = 0; | |
1860 | ||
1861 | dinoc = &dino->di_core; | |
1862 | lino = XFS_AGINO_TO_INO(mp, agno, ino); | |
1863 | ||
1864 | /* | |
1865 | * if in verify mode, don't modify the inode. | |
1866 | * | |
1867 | * if correcting, reset stuff that has known values | |
1868 | * | |
1869 | * if in uncertain mode, be silent on errors since we're | |
1870 | * trying to find out if these are inodes as opposed | |
1871 | * to assuming that they are. Just return the appropriate | |
1872 | * return code in that case. | |
1873 | */ | |
1874 | ||
1875 | if (INT_GET(dinoc->di_magic, ARCH_CONVERT) != XFS_DINODE_MAGIC) { | |
1876 | retval++; | |
1877 | if (!verify_mode) { | |
507f4e33 | 1878 | do_warn(_("bad magic number 0x%x on inode %llu, "), |
2bd0ea18 NS |
1879 | INT_GET(dinoc->di_magic, ARCH_CONVERT), lino); |
1880 | if (!no_modify) { | |
507f4e33 | 1881 | do_warn(_("resetting magic number\n")); |
2bd0ea18 | 1882 | *dirty = 1; |
507f4e33 NS |
1883 | INT_SET(dinoc->di_magic, ARCH_CONVERT, |
1884 | XFS_DINODE_MAGIC); | |
2bd0ea18 | 1885 | } else { |
507f4e33 | 1886 | do_warn(_("would reset magic number\n")); |
2bd0ea18 NS |
1887 | } |
1888 | } else if (!uncertain) { | |
507f4e33 | 1889 | do_warn(_("bad magic number 0x%x on inode %llu\n"), |
2bd0ea18 NS |
1890 | INT_GET(dinoc->di_magic, ARCH_CONVERT), lino); |
1891 | } | |
1892 | } | |
1893 | ||
1894 | if (!XFS_DINODE_GOOD_VERSION(dinoc->di_version) || | |
1895 | (!fs_inode_nlink && dinoc->di_version > XFS_DINODE_VERSION_1)) { | |
1896 | retval++; | |
1897 | if (!verify_mode) { | |
507f4e33 | 1898 | do_warn(_("bad version number 0x%x on inode %llu, "), |
2bd0ea18 NS |
1899 | dinoc->di_version, lino); |
1900 | if (!no_modify) { | |
507f4e33 | 1901 | do_warn(_("resetting version number\n")); |
2bd0ea18 NS |
1902 | *dirty = 1; |
1903 | dinoc->di_version = (fs_inode_nlink) ? | |
1904 | XFS_DINODE_VERSION_2 : | |
1905 | XFS_DINODE_VERSION_1; | |
1906 | } else { | |
507f4e33 | 1907 | do_warn(_("would reset version number\n")); |
2bd0ea18 NS |
1908 | } |
1909 | } else if (!uncertain) { | |
507f4e33 | 1910 | do_warn(_("bad version number 0x%x on inode %llu\n"), |
2bd0ea18 NS |
1911 | dinoc->di_version, lino); |
1912 | } | |
1913 | } | |
1914 | ||
1915 | /* | |
1916 | * blow out of here if the inode size is < 0 | |
1917 | */ | |
1918 | if (INT_GET(dinoc->di_size, ARCH_CONVERT) < 0) { | |
1919 | retval++; | |
1920 | if (!verify_mode) { | |
507f4e33 | 1921 | do_warn(_("bad (negative) size %lld on inode %llu\n"), |
2bd0ea18 NS |
1922 | INT_GET(dinoc->di_size, ARCH_CONVERT), lino); |
1923 | if (!no_modify) { | |
1924 | *dirty += clear_dinode(mp, dino, lino); | |
1925 | *cleared = 1; | |
1926 | } else { | |
1927 | *dirty = 1; | |
1928 | *cleared = 1; | |
1929 | } | |
1930 | *used = is_free; | |
1931 | } else if (!uncertain) { | |
507f4e33 | 1932 | do_warn(_("bad (negative) size %lld on inode %llu\n"), |
2bd0ea18 NS |
1933 | INT_GET(dinoc->di_size, ARCH_CONVERT), lino); |
1934 | } | |
1935 | ||
1936 | return(1); | |
1937 | } | |
1938 | ||
1939 | /* | |
1940 | * was_free value is not meaningful if we're in verify mode | |
1941 | */ | |
1942 | if (!verify_mode && INT_GET(dinoc->di_mode, ARCH_CONVERT) == 0 && was_free == 1) { | |
1943 | /* | |
1944 | * easy case, inode free -- inode and map agree, clear | |
1945 | * it just in case to ensure that format, etc. are | |
1946 | * set correctly | |
1947 | */ | |
1948 | if (!no_modify) { | |
1949 | err = clear_dinode(mp, dino, lino); | |
1950 | if (err) { | |
1951 | *dirty = 1; | |
1952 | *cleared = 1; | |
1953 | } | |
1954 | } | |
1955 | *used = is_free; | |
1956 | return(0); | |
1957 | } else if (!verify_mode && INT_GET(dinoc->di_mode, ARCH_CONVERT) == 0 && was_free == 0) { | |
1958 | /* | |
1959 | * the inode looks free but the map says it's in use. | |
1960 | * clear the inode just to be safe and mark the inode | |
1961 | * free. | |
1962 | */ | |
507f4e33 | 1963 | do_warn(_("imap claims a free inode %llu is in use, "), lino); |
2bd0ea18 NS |
1964 | |
1965 | if (!no_modify) { | |
507f4e33 | 1966 | do_warn(_("correcting imap and clearing inode\n")); |
2bd0ea18 NS |
1967 | |
1968 | err = clear_dinode(mp, dino, lino); | |
1969 | if (err) { | |
1970 | retval++; | |
1971 | *dirty = 1; | |
1972 | *cleared = 1; | |
1973 | } | |
1974 | } else { | |
507f4e33 | 1975 | do_warn(_("would correct imap and clear inode\n")); |
2bd0ea18 NS |
1976 | |
1977 | *dirty = 1; | |
1978 | *cleared = 1; | |
1979 | } | |
1980 | ||
1981 | *used = is_free; | |
1982 | ||
1983 | return(retval > 0 ? 1 : 0); | |
1984 | } | |
1985 | ||
1986 | /* | |
1987 | * because of the lack of any write ordering guarantee, it's | |
1988 | * possible that the core got updated but the forks didn't. | |
1989 | * so rather than be ambitious (and probably incorrect), | |
dfc130f3 | 1990 | * if there's an inconsistency, we get conservative and |
2bd0ea18 NS |
1991 | * just pitch the file. blow off checking formats of |
1992 | * free inodes since technically any format is legal | |
1993 | * as we reset the inode when we re-use it. | |
1994 | */ | |
1995 | if (INT_GET(dinoc->di_mode, ARCH_CONVERT) != 0 && | |
322f2a29 | 1996 | ((((INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT) >> 12) > 15) || |
1e77098c | 1997 | (uchar_t) dinoc->di_format > XFS_DINODE_FMT_UUID || |
322f2a29 | 1998 | (!(okfmts[(INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT) >> 12] & |
2bd0ea18 NS |
1999 | (1 << dinoc->di_format))))) { |
2000 | /* bad inode format */ | |
2001 | retval++; | |
2002 | if (!uncertain) | |
507f4e33 | 2003 | do_warn(_("bad inode format in inode %llu\n"), lino); |
2bd0ea18 NS |
2004 | if (!verify_mode) { |
2005 | if (!no_modify) { | |
2006 | *dirty += clear_dinode(mp, dino, lino); | |
2007 | ASSERT(*dirty > 0); | |
2008 | } | |
2009 | } | |
2010 | *cleared = 1; | |
2011 | *used = is_free; | |
2012 | ||
2013 | return(retval > 0 ? 1 : 0); | |
2014 | } | |
2015 | ||
2016 | if (verify_mode) | |
2017 | return(retval > 0 ? 1 : 0); | |
2018 | ||
2019 | /* | |
2020 | * clear the next unlinked field if necessary on a good | |
2021 | * inode only during phase 4 -- when checking for inodes | |
2022 | * referencing duplicate blocks. then it's safe because | |
2023 | * we've done the inode discovery and have found all the inodes | |
2024 | * we're going to find. check_dups is set to 1 only during | |
2025 | * phase 4. Ugly. | |
2026 | */ | |
2027 | if (check_dups && !no_modify) | |
2028 | *dirty += clear_dinode_unlinked(mp, dino); | |
2029 | ||
2030 | /* set type and map type info */ | |
2031 | ||
322f2a29 SL |
2032 | switch (INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT) { |
2033 | case S_IFDIR: | |
2bd0ea18 NS |
2034 | type = XR_INO_DIR; |
2035 | *isa_dir = 1; | |
2036 | break; | |
322f2a29 | 2037 | case S_IFREG: |
2bd0ea18 NS |
2038 | if (INT_GET(dinoc->di_flags, ARCH_CONVERT) & XFS_DIFLAG_REALTIME) |
2039 | type = XR_INO_RTDATA; | |
2040 | else if (lino == mp->m_sb.sb_rbmino) | |
2041 | type = XR_INO_RTBITMAP; | |
2042 | else if (lino == mp->m_sb.sb_rsumino) | |
2043 | type = XR_INO_RTSUM; | |
2044 | else | |
2045 | type = XR_INO_DATA; | |
2046 | break; | |
322f2a29 | 2047 | case S_IFLNK: |
2bd0ea18 NS |
2048 | type = XR_INO_SYMLINK; |
2049 | break; | |
322f2a29 | 2050 | case S_IFCHR: |
2bd0ea18 NS |
2051 | type = XR_INO_CHRDEV; |
2052 | break; | |
322f2a29 | 2053 | case S_IFBLK: |
2bd0ea18 NS |
2054 | type = XR_INO_BLKDEV; |
2055 | break; | |
322f2a29 | 2056 | case S_IFSOCK: |
2bd0ea18 NS |
2057 | type = XR_INO_SOCK; |
2058 | break; | |
322f2a29 | 2059 | case S_IFIFO: |
2bd0ea18 NS |
2060 | type = XR_INO_FIFO; |
2061 | break; | |
2bd0ea18 | 2062 | default: |
c426c8b7 BN |
2063 | retval++; |
2064 | if (!verify_mode) { | |
2065 | do_warn(_("bad inode type %#o inode %llu\n"), | |
2066 | (int) (INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT), lino); | |
2067 | if (!no_modify) | |
2068 | *dirty += clear_dinode(mp, dino, lino); | |
2069 | else | |
2070 | *dirty = 1; | |
2071 | *cleared = 1; | |
2072 | *used = is_free; | |
2073 | } else if (!uncertain) { | |
2074 | do_warn(_("bad inode type %#o inode %llu\n"), | |
2075 | (int) (INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT), lino); | |
2076 | } | |
2077 | return 1; | |
2bd0ea18 NS |
2078 | } |
2079 | ||
2080 | /* | |
2081 | * type checks for root, realtime inodes, and quota inodes | |
2082 | */ | |
2083 | if (lino == mp->m_sb.sb_rootino && type != XR_INO_DIR) { | |
507f4e33 | 2084 | do_warn(_("bad inode type for root inode %llu, "), lino); |
2bd0ea18 NS |
2085 | type = XR_INO_DIR; |
2086 | ||
2087 | if (!no_modify) { | |
507f4e33 NS |
2088 | do_warn(_("resetting to directory\n")); |
2089 | INT_MOD_EXPR(dinoc->di_mode, ARCH_CONVERT, | |
322f2a29 | 2090 | &= ~(INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT)); |
507f4e33 | 2091 | INT_MOD_EXPR(dinoc->di_mode, ARCH_CONVERT, |
322f2a29 | 2092 | |= INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFDIR); |
2bd0ea18 | 2093 | } else { |
507f4e33 | 2094 | do_warn(_("would reset to directory\n")); |
2bd0ea18 NS |
2095 | } |
2096 | } else if (lino == mp->m_sb.sb_rsumino) { | |
2097 | do_rt = 1; | |
507f4e33 | 2098 | rstring = _("summary"); |
2bd0ea18 NS |
2099 | rtype = XR_INO_RTSUM; |
2100 | } else if (lino == mp->m_sb.sb_rbmino) { | |
2101 | do_rt = 1; | |
507f4e33 | 2102 | rstring = _("bitmap"); |
2bd0ea18 NS |
2103 | rtype = XR_INO_RTBITMAP; |
2104 | } else if (lino == mp->m_sb.sb_uquotino) { | |
2105 | if (type != XR_INO_DATA) { | |
507f4e33 | 2106 | do_warn(_("user quota inode has bad type 0x%x\n"), |
322f2a29 | 2107 | INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT); |
2bd0ea18 NS |
2108 | |
2109 | if (!no_modify) { | |
2110 | *dirty += clear_dinode(mp, dino, lino); | |
2111 | ASSERT(*dirty > 0); | |
2112 | } | |
2113 | ||
2114 | *cleared = 1; | |
2115 | *used = is_free; | |
2116 | *isa_dir = 0; | |
2117 | ||
2118 | mp->m_sb.sb_uquotino = NULLFSINO; | |
2119 | ||
2120 | return(1); | |
2121 | } | |
b36eef04 | 2122 | } else if (lino == mp->m_sb.sb_gquotino) { |
2bd0ea18 | 2123 | if (type != XR_INO_DATA) { |
507f4e33 | 2124 | do_warn(_("group quota inode has bad type 0x%x\n"), |
322f2a29 | 2125 | INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT); |
2bd0ea18 NS |
2126 | |
2127 | if (!no_modify) { | |
2128 | *dirty += clear_dinode(mp, dino, lino); | |
2129 | ASSERT(*dirty > 0); | |
2130 | } | |
2131 | ||
2132 | *cleared = 1; | |
2133 | *used = is_free; | |
2134 | *isa_dir = 0; | |
2135 | ||
b36eef04 | 2136 | mp->m_sb.sb_gquotino = NULLFSINO; |
2bd0ea18 NS |
2137 | |
2138 | return(1); | |
2139 | } | |
2140 | } | |
2141 | ||
2142 | if (do_rt && type != rtype) { | |
2143 | type = XR_INO_DATA; | |
2144 | ||
507f4e33 | 2145 | do_warn(_("bad inode type for realtime %s inode %llu, "), |
2bd0ea18 NS |
2146 | rstring, lino); |
2147 | ||
2148 | if (!no_modify) { | |
507f4e33 NS |
2149 | do_warn(_("resetting to regular file\n")); |
2150 | INT_MOD_EXPR(dinoc->di_mode, ARCH_CONVERT, | |
322f2a29 | 2151 | &= ~(INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFMT)); |
507f4e33 | 2152 | INT_MOD_EXPR(dinoc->di_mode, ARCH_CONVERT, |
322f2a29 | 2153 | |= INT_GET(dinoc->di_mode, ARCH_CONVERT) & S_IFREG); |
2bd0ea18 | 2154 | } else { |
507f4e33 | 2155 | do_warn(_("would reset to regular file\n")); |
2bd0ea18 NS |
2156 | } |
2157 | } | |
2158 | ||
2159 | /* | |
ae541a2b NS |
2160 | * only regular files with REALTIME or EXTSIZE flags set can have |
2161 | * extsize set, or directories with EXTSZINHERIT. | |
2bd0ea18 | 2162 | */ |
ae541a2b NS |
2163 | if (INT_GET(dinoc->di_extsize, ARCH_CONVERT) != 0) { |
2164 | if ((type == XR_INO_RTDATA) || | |
2165 | (type == XR_INO_DIR && | |
2166 | (INT_GET(dinoc->di_flags, ARCH_CONVERT) & | |
2167 | XFS_DIFLAG_EXTSZINHERIT)) || | |
2168 | (type == XR_INO_DATA && | |
2169 | (INT_GET(dinoc->di_flags, ARCH_CONVERT) & | |
2170 | XFS_DIFLAG_EXTSIZE))) { | |
2171 | /* s'okay */ ; | |
2172 | } else { | |
2173 | do_warn( | |
2174 | _("bad non-zero extent size %u for non-realtime/extsize inode %llu, "), | |
2175 | INT_GET(dinoc->di_extsize, ARCH_CONVERT), lino); | |
2bd0ea18 | 2176 | |
ae541a2b NS |
2177 | if (!no_modify) { |
2178 | do_warn(_("resetting to zero\n")); | |
2179 | dinoc->di_extsize = 0; | |
2180 | *dirty = 1; | |
2181 | } else { | |
2182 | do_warn(_("would reset to zero\n")); | |
2183 | } | |
2bd0ea18 NS |
2184 | } |
2185 | } | |
2186 | ||
2187 | /* | |
2188 | * for realtime inodes, check sizes to see that | |
2189 | * they are consistent with the # of realtime blocks. | |
2190 | * also, verify that they contain only one extent and | |
2191 | * are extent format files. If anything's wrong, clear | |
2192 | * the inode -- we'll recreate it in phase 6. | |
2193 | */ | |
184cb918 ES |
2194 | if (do_rt && |
2195 | ((lino == mp->m_sb.sb_rbmino && | |
2196 | INT_GET(dinoc->di_size, ARCH_CONVERT) | |
2197 | != mp->m_sb.sb_rbmblocks * mp->m_sb.sb_blocksize) || | |
2198 | (lino == mp->m_sb.sb_rsumino && | |
2199 | INT_GET(dinoc->di_size, ARCH_CONVERT) != mp->m_rsumsize))) { | |
2200 | ||
507f4e33 | 2201 | do_warn(_("bad size %llu for realtime %s inode %llu\n"), |
2bd0ea18 NS |
2202 | INT_GET(dinoc->di_size, ARCH_CONVERT), rstring, lino); |
2203 | ||
2204 | if (!no_modify) { | |
2205 | *dirty += clear_dinode(mp, dino, lino); | |
2206 | ASSERT(*dirty > 0); | |
2207 | } | |
2208 | ||
2209 | *cleared = 1; | |
2210 | *used = is_free; | |
2211 | *isa_dir = 0; | |
2212 | ||
2213 | return(1); | |
2214 | } | |
2215 | ||
2216 | if (do_rt && mp->m_sb.sb_rblocks == 0 && INT_GET(dinoc->di_nextents, ARCH_CONVERT) != 0) { | |
507f4e33 | 2217 | do_warn(_("bad # of extents (%u) for realtime %s inode %llu\n"), |
2bd0ea18 NS |
2218 | INT_GET(dinoc->di_nextents, ARCH_CONVERT), rstring, lino); |
2219 | ||
2220 | if (!no_modify) { | |
2221 | *dirty += clear_dinode(mp, dino, lino); | |
2222 | ASSERT(*dirty > 0); | |
2223 | } | |
2224 | ||
2225 | *cleared = 1; | |
2226 | *used = is_free; | |
2227 | *isa_dir = 0; | |
2228 | ||
2229 | return(1); | |
2230 | } | |
2231 | ||
2232 | /* | |
2233 | * Setup nextents and anextents for blkmap_alloc calls. | |
2234 | */ | |
2235 | nextents = INT_GET(dinoc->di_nextents, ARCH_CONVERT); | |
2236 | if (nextents > INT_GET(dinoc->di_nblocks, ARCH_CONVERT) || nextents > XFS_MAX_INCORE_EXTENTS) | |
2237 | nextents = 1; | |
2238 | anextents = INT_GET(dinoc->di_anextents, ARCH_CONVERT); | |
2239 | if (anextents > INT_GET(dinoc->di_nblocks, ARCH_CONVERT) || anextents > XFS_MAX_INCORE_EXTENTS) | |
2240 | anextents = 1; | |
2241 | ||
2242 | /* | |
2243 | * general size/consistency checks: | |
2244 | * | |
2245 | * if the size <= size of the data fork, directories must be | |
2246 | * local inodes unlike regular files which would be extent inodes. | |
2247 | * all the other mentioned types have to have a zero size value. | |
2248 | * | |
2249 | * if the size and format don't match, get out now rather than | |
2250 | * risk trying to process a non-existent extents or btree | |
2251 | * type data fork. | |
2252 | */ | |
2253 | switch (type) { | |
2254 | case XR_INO_DIR: | |
507f4e33 | 2255 | if (INT_GET(dinoc->di_size, ARCH_CONVERT) <= |
46eca962 | 2256 | XFS_DFORK_DSIZE(dino, mp) && |
507f4e33 | 2257 | (dinoc->di_format != XFS_DINODE_FMT_LOCAL)) { |
2bd0ea18 | 2258 | do_warn( |
507f4e33 | 2259 | _("mismatch between format (%d) and size (%lld) in directory ino %llu\n"), |
2bd0ea18 NS |
2260 | dinoc->di_format, |
2261 | INT_GET(dinoc->di_size, ARCH_CONVERT), | |
2262 | lino); | |
2263 | ||
2264 | if (!no_modify) { | |
2265 | *dirty += clear_dinode(mp, | |
2266 | dino, lino); | |
2267 | ASSERT(*dirty > 0); | |
2268 | } | |
2269 | ||
2270 | *cleared = 1; | |
2271 | *used = is_free; | |
2272 | *isa_dir = 0; | |
2273 | ||
2274 | return(1); | |
2275 | } | |
2276 | if (dinoc->di_format != XFS_DINODE_FMT_LOCAL) | |
2277 | dblkmap = blkmap_alloc(nextents); | |
2278 | break; | |
2279 | case XR_INO_SYMLINK: | |
2280 | if (process_symlink_extlist(mp, lino, dino)) { | |
507f4e33 | 2281 | do_warn(_("bad data fork in symlink %llu\n"), lino); |
2bd0ea18 NS |
2282 | |
2283 | if (!no_modify) { | |
2284 | *dirty += clear_dinode(mp, | |
2285 | dino, lino); | |
2286 | ASSERT(*dirty > 0); | |
2287 | } | |
2288 | ||
2289 | *cleared = 1; | |
2290 | *used = is_free; | |
2291 | *isa_dir = 0; | |
2292 | ||
2293 | return(1); | |
2294 | } | |
2295 | if (dinoc->di_format != XFS_DINODE_FMT_LOCAL) | |
2296 | dblkmap = blkmap_alloc(nextents); | |
2297 | break; | |
2298 | case XR_INO_CHRDEV: /* fall through to FIFO case ... */ | |
2299 | case XR_INO_BLKDEV: /* fall through to FIFO case ... */ | |
2300 | case XR_INO_SOCK: /* fall through to FIFO case ... */ | |
2301 | case XR_INO_MOUNTPOINT: /* fall through to FIFO case ... */ | |
2302 | case XR_INO_FIFO: | |
2303 | if (process_misc_ino_types(mp, dino, lino, type)) { | |
2304 | if (!no_modify) { | |
2305 | *dirty += clear_dinode(mp, dino, lino); | |
2306 | ASSERT(*dirty > 0); | |
2307 | } | |
2308 | ||
2309 | *cleared = 1; | |
2310 | *used = is_free; | |
2311 | *isa_dir = 0; | |
2312 | ||
2313 | return(1); | |
2314 | } | |
2315 | break; | |
2316 | case XR_INO_RTDATA: | |
2317 | /* | |
2318 | * if we have no realtime blocks, any inode claiming | |
2319 | * to be a real-time file is bogus | |
2320 | */ | |
2321 | if (mp->m_sb.sb_rblocks == 0) { | |
2322 | do_warn( | |
507f4e33 | 2323 | _("found inode %llu claiming to be a real-time file\n"), |
2bd0ea18 NS |
2324 | lino); |
2325 | ||
2326 | if (!no_modify) { | |
2327 | *dirty += clear_dinode(mp, dino, lino); | |
2328 | ASSERT(*dirty > 0); | |
2329 | } | |
2330 | ||
2331 | *cleared = 1; | |
2332 | *used = is_free; | |
2333 | *isa_dir = 0; | |
2334 | ||
2335 | return(1); | |
2336 | } | |
2337 | break; | |
2338 | case XR_INO_RTBITMAP: | |
507f4e33 NS |
2339 | if (INT_GET(dinoc->di_size, ARCH_CONVERT) != |
2340 | (__int64_t)mp->m_sb.sb_rbmblocks * mp->m_sb.sb_blocksize) { | |
2bd0ea18 | 2341 | do_warn( |
507f4e33 | 2342 | _("realtime bitmap inode %llu has bad size %lld (should be %lld)\n"), |
2bd0ea18 NS |
2343 | lino, INT_GET(dinoc->di_size, ARCH_CONVERT), |
2344 | (__int64_t) mp->m_sb.sb_rbmblocks * | |
2345 | mp->m_sb.sb_blocksize); | |
2346 | ||
2347 | if (!no_modify) { | |
2348 | *dirty += clear_dinode(mp, dino, lino); | |
2349 | ASSERT(*dirty > 0); | |
2350 | } | |
2351 | ||
2352 | *cleared = 1; | |
2353 | *used = is_free; | |
2354 | *isa_dir = 0; | |
2355 | ||
2356 | return(1); | |
2357 | } | |
2358 | dblkmap = blkmap_alloc(nextents); | |
2359 | break; | |
2360 | case XR_INO_RTSUM: | |
2361 | if (INT_GET(dinoc->di_size, ARCH_CONVERT) != mp->m_rsumsize) { | |
2362 | do_warn( | |
507f4e33 NS |
2363 | _("realtime summary inode %llu has bad size %lld (should be %d)\n"), |
2364 | lino, INT_GET(dinoc->di_size, ARCH_CONVERT), | |
2365 | mp->m_rsumsize); | |
2bd0ea18 NS |
2366 | |
2367 | if (!no_modify) { | |
2368 | *dirty += clear_dinode(mp, dino, lino); | |
2369 | ASSERT(*dirty > 0); | |
2370 | } | |
2371 | ||
2372 | *cleared = 1; | |
2373 | *used = is_free; | |
2374 | *isa_dir = 0; | |
2375 | ||
2376 | return(1); | |
2377 | } | |
2378 | dblkmap = blkmap_alloc(nextents); | |
2379 | break; | |
2380 | default: | |
2381 | break; | |
2382 | } | |
2383 | ||
2384 | /* | |
2385 | * check for illegal values of forkoff | |
2386 | */ | |
2387 | err = 0; | |
2388 | if (dinoc->di_forkoff != 0) { | |
2389 | switch (dinoc->di_format) { | |
2390 | case XFS_DINODE_FMT_DEV: | |
2391 | if (dinoc->di_forkoff != | |
63899e27 | 2392 | (roundup(sizeof(xfs_dev_t), 8) >> 3)) { |
2bd0ea18 | 2393 | do_warn( |
507f4e33 | 2394 | _("bad attr fork offset %d in dev inode %llu, should be %d\n"), |
2bd0ea18 NS |
2395 | (int) dinoc->di_forkoff, |
2396 | lino, | |
63899e27 | 2397 | (int) (roundup(sizeof(xfs_dev_t), 8) >> 3)); |
2bd0ea18 NS |
2398 | err = 1; |
2399 | } | |
2400 | break; | |
2401 | case XFS_DINODE_FMT_UUID: | |
2402 | if (dinoc->di_forkoff != | |
2403 | (roundup(sizeof(uuid_t), 8) >> 3)) { | |
2404 | do_warn( | |
507f4e33 | 2405 | _("bad attr fork offset %d in uuid inode %llu, should be %d\n"), |
2bd0ea18 NS |
2406 | (int) dinoc->di_forkoff, |
2407 | lino, | |
2408 | (int)(roundup(sizeof(uuid_t), 8) >> 3)); | |
2409 | err = 1; | |
2410 | } | |
2411 | break; | |
2412 | case XFS_DINODE_FMT_LOCAL: /* fall through ... */ | |
2413 | case XFS_DINODE_FMT_EXTENTS: /* fall through ... */ | |
ca86e759 NS |
2414 | case XFS_DINODE_FMT_BTREE: { |
2415 | if (dinoc->di_forkoff >= (XFS_LITINO(mp) >> 3)) { | |
2bd0ea18 | 2416 | do_warn( |
ca86e759 | 2417 | _("bad attr fork offset %d in inode %llu, max=%d\n"), |
2bd0ea18 | 2418 | (int) dinoc->di_forkoff, |
ca86e759 | 2419 | lino, XFS_LITINO(mp) >> 3); |
2bd0ea18 NS |
2420 | err = 1; |
2421 | } | |
2422 | break; | |
ca86e759 | 2423 | } |
2bd0ea18 | 2424 | default: |
507f4e33 | 2425 | do_error(_("unexpected inode format %d\n"), |
2bd0ea18 NS |
2426 | (int) dinoc->di_format); |
2427 | break; | |
2428 | } | |
2429 | } | |
2430 | ||
2431 | if (err) { | |
2432 | if (!no_modify) { | |
2433 | *dirty += clear_dinode(mp, dino, lino); | |
2434 | ASSERT(*dirty > 0); | |
2435 | } | |
2436 | ||
2437 | *cleared = 1; | |
2438 | *used = is_free; | |
2439 | *isa_dir = 0; | |
2440 | blkmap_free(dblkmap); | |
2441 | return(1); | |
2442 | } | |
2443 | ||
2444 | /* | |
2445 | * check data fork -- if it's bad, clear the inode | |
2446 | */ | |
2447 | nextents = 0; | |
2448 | switch (dinoc->di_format) { | |
2449 | case XFS_DINODE_FMT_LOCAL: | |
2450 | err = process_lclinode(mp, agno, ino, dino, type, | |
2451 | dirty, &totblocks, &nextents, &dblkmap, | |
2452 | XFS_DATA_FORK, check_dups); | |
2453 | break; | |
2454 | case XFS_DINODE_FMT_EXTENTS: | |
2455 | err = process_exinode(mp, agno, ino, dino, type, | |
2456 | dirty, &totblocks, &nextents, &dblkmap, | |
2457 | XFS_DATA_FORK, check_dups); | |
2458 | break; | |
2459 | case XFS_DINODE_FMT_BTREE: | |
2460 | err = process_btinode(mp, agno, ino, dino, type, | |
2461 | dirty, &totblocks, &nextents, &dblkmap, | |
2462 | XFS_DATA_FORK, check_dups); | |
2463 | break; | |
2464 | case XFS_DINODE_FMT_DEV: /* fall through */ | |
2465 | case XFS_DINODE_FMT_UUID: | |
2466 | err = 0; | |
2467 | break; | |
2468 | default: | |
507f4e33 NS |
2469 | do_error(_("unknown format %d, ino %llu (mode = %d)\n"), |
2470 | dinoc->di_format, lino, | |
2471 | INT_GET(dinoc->di_mode, ARCH_CONVERT)); | |
2bd0ea18 NS |
2472 | } |
2473 | ||
2474 | if (err) { | |
2475 | /* | |
2476 | * problem in the data fork, clear out the inode | |
2477 | * and get out | |
2478 | */ | |
507f4e33 | 2479 | do_warn(_("bad data fork in inode %llu\n"), lino); |
2bd0ea18 NS |
2480 | |
2481 | if (!no_modify) { | |
2482 | *dirty += clear_dinode(mp, dino, lino); | |
2483 | ASSERT(*dirty > 0); | |
2484 | } | |
2485 | ||
2486 | *cleared = 1; | |
2487 | *used = is_free; | |
2488 | *isa_dir = 0; | |
2489 | blkmap_free(dblkmap); | |
2bd0ea18 NS |
2490 | return(1); |
2491 | } | |
2492 | ||
2493 | if (check_dups) { | |
2494 | /* | |
2495 | * if check_dups was non-zero, we have to | |
2496 | * re-process data fork to set bitmap since the | |
2497 | * bitmap wasn't set the first time through | |
2498 | */ | |
2499 | switch (dinoc->di_format) { | |
2500 | case XFS_DINODE_FMT_LOCAL: | |
2501 | err = process_lclinode(mp, agno, ino, dino, type, | |
2502 | dirty, &totblocks, &nextents, &dblkmap, | |
2503 | XFS_DATA_FORK, 0); | |
2504 | break; | |
2505 | case XFS_DINODE_FMT_EXTENTS: | |
2506 | err = process_exinode(mp, agno, ino, dino, type, | |
2507 | dirty, &totblocks, &nextents, &dblkmap, | |
2508 | XFS_DATA_FORK, 0); | |
2509 | break; | |
2510 | case XFS_DINODE_FMT_BTREE: | |
2511 | err = process_btinode(mp, agno, ino, dino, type, | |
2512 | dirty, &totblocks, &nextents, &dblkmap, | |
2513 | XFS_DATA_FORK, 0); | |
2514 | break; | |
2515 | case XFS_DINODE_FMT_DEV: /* fall through */ | |
2516 | case XFS_DINODE_FMT_UUID: | |
2517 | err = 0; | |
2518 | break; | |
2519 | default: | |
507f4e33 NS |
2520 | do_error(_("unknown format %d, ino %llu (mode = %d)\n"), |
2521 | dinoc->di_format, lino, | |
2522 | INT_GET(dinoc->di_mode, ARCH_CONVERT)); | |
2bd0ea18 NS |
2523 | } |
2524 | ||
2525 | if (no_modify && err != 0) { | |
2526 | *cleared = 1; | |
2527 | *used = is_free; | |
2528 | *isa_dir = 0; | |
2529 | blkmap_free(dblkmap); | |
2bd0ea18 NS |
2530 | return(1); |
2531 | } | |
2532 | ||
2533 | ASSERT(err == 0); | |
2534 | } | |
2535 | ||
2536 | /* | |
2537 | * check attribute fork if necessary. attributes are | |
2538 | * always stored in the regular filesystem. | |
2539 | */ | |
2540 | ||
46eca962 | 2541 | if (!XFS_DFORK_Q(dino) && |
507f4e33 NS |
2542 | dinoc->di_aformat != XFS_DINODE_FMT_EXTENTS) { |
2543 | do_warn(_("bad attribute format %d in inode %llu, "), | |
2bd0ea18 NS |
2544 | dinoc->di_aformat, lino); |
2545 | if (!no_modify) { | |
507f4e33 | 2546 | do_warn(_("resetting value\n")); |
2bd0ea18 NS |
2547 | dinoc->di_aformat = XFS_DINODE_FMT_EXTENTS; |
2548 | *dirty = 1; | |
2549 | } else | |
507f4e33 | 2550 | do_warn(_("would reset value\n")); |
2bd0ea18 | 2551 | anextents = 0; |
46eca962 | 2552 | } else if (XFS_DFORK_Q(dino)) { |
2bd0ea18 NS |
2553 | switch (dinoc->di_aformat) { |
2554 | case XFS_DINODE_FMT_LOCAL: | |
2555 | anextents = 0; | |
2556 | err = process_lclinode(mp, agno, ino, dino, | |
2557 | type, dirty, &atotblocks, &anextents, &ablkmap, | |
2558 | XFS_ATTR_FORK, check_dups); | |
2559 | break; | |
2560 | case XFS_DINODE_FMT_EXTENTS: | |
2561 | ablkmap = blkmap_alloc(anextents); | |
2562 | anextents = 0; | |
2563 | err = process_exinode(mp, agno, ino, dino, | |
2564 | type, dirty, &atotblocks, &anextents, &ablkmap, | |
2565 | XFS_ATTR_FORK, check_dups); | |
2566 | break; | |
2567 | case XFS_DINODE_FMT_BTREE: | |
2568 | ablkmap = blkmap_alloc(anextents); | |
2569 | anextents = 0; | |
2570 | err = process_btinode(mp, agno, ino, dino, | |
2571 | type, dirty, &atotblocks, &anextents, &ablkmap, | |
2572 | XFS_ATTR_FORK, check_dups); | |
2573 | break; | |
2574 | default: | |
2575 | anextents = 0; | |
507f4e33 | 2576 | do_warn(_("illegal attribute format %d, ino %llu\n"), |
2bd0ea18 NS |
2577 | dinoc->di_aformat, lino); |
2578 | err = 1; | |
2579 | break; | |
2580 | } | |
2581 | ||
2582 | if (err) { | |
2583 | /* | |
2584 | * clear the attribute fork if necessary. we can't | |
2585 | * clear the inode because we've already put the | |
2586 | * inode space info into the blockmap. | |
2587 | * | |
2588 | * XXX - put the inode onto the "move it" list and | |
2589 | * log the the attribute scrubbing | |
2590 | */ | |
507f4e33 | 2591 | do_warn(_("bad attribute fork in inode %llu"), lino); |
2bd0ea18 NS |
2592 | |
2593 | if (!no_modify) { | |
2594 | if (delete_attr_ok) { | |
507f4e33 | 2595 | do_warn(_(", clearing attr fork\n")); |
2bd0ea18 NS |
2596 | *dirty += clear_dinode_attr(mp, |
2597 | dino, lino); | |
2598 | } else { | |
2599 | do_warn("\n"); | |
2600 | *dirty += clear_dinode(mp, | |
2601 | dino, lino); | |
2602 | } | |
2603 | ASSERT(*dirty > 0); | |
2604 | } else { | |
507f4e33 | 2605 | do_warn(_(", would clear attr fork\n")); |
2bd0ea18 NS |
2606 | } |
2607 | ||
2608 | atotblocks = 0; | |
2609 | anextents = 0; | |
2610 | ||
2611 | if (delete_attr_ok) { | |
2612 | if (!no_modify) | |
2613 | dinoc->di_aformat = XFS_DINODE_FMT_LOCAL; | |
2614 | } else { | |
2615 | *cleared = 1; | |
2616 | *used = is_free; | |
2617 | *isa_dir = 0; | |
2618 | blkmap_free(dblkmap); | |
2619 | blkmap_free(ablkmap); | |
2620 | } | |
2621 | return(1); | |
dfc130f3 | 2622 | |
2bd0ea18 NS |
2623 | } else if (check_dups) { |
2624 | switch (dinoc->di_aformat) { | |
2625 | case XFS_DINODE_FMT_LOCAL: | |
2626 | err = process_lclinode(mp, agno, ino, dino, | |
2627 | type, dirty, &atotblocks, &anextents, | |
2628 | &ablkmap, XFS_ATTR_FORK, 0); | |
2629 | break; | |
2630 | case XFS_DINODE_FMT_EXTENTS: | |
2631 | err = process_exinode(mp, agno, ino, dino, | |
2632 | type, dirty, &atotblocks, &anextents, | |
2633 | &ablkmap, XFS_ATTR_FORK, 0); | |
2634 | break; | |
2635 | case XFS_DINODE_FMT_BTREE: | |
2636 | err = process_btinode(mp, agno, ino, dino, | |
2637 | type, dirty, &atotblocks, &anextents, | |
2638 | &ablkmap, XFS_ATTR_FORK, 0); | |
2639 | break; | |
2640 | default: | |
507f4e33 NS |
2641 | do_error( |
2642 | _("illegal attribute fmt %d, ino %llu\n"), | |
2643 | dinoc->di_aformat, lino); | |
2bd0ea18 NS |
2644 | } |
2645 | ||
2646 | if (no_modify && err != 0) { | |
2647 | *cleared = 1; | |
2648 | *used = is_free; | |
2649 | *isa_dir = 0; | |
2650 | blkmap_free(dblkmap); | |
2651 | blkmap_free(ablkmap); | |
2bd0ea18 NS |
2652 | return(1); |
2653 | } | |
2654 | ||
2655 | ASSERT(err == 0); | |
2656 | } | |
2657 | ||
2658 | /* | |
2659 | * do attribute semantic-based consistency checks now | |
2660 | */ | |
2661 | ||
2662 | /* get this only in phase 3, not in both phase 3 and 4 */ | |
2663 | if (extra_attr_check) { | |
2664 | if ((err = process_attributes(mp, lino, dino, ablkmap, | |
2665 | &repair))) { | |
507f4e33 NS |
2666 | do_warn( |
2667 | _("problem with attribute contents in inode %llu\n"), lino); | |
2bd0ea18 NS |
2668 | if(!repair) { |
2669 | /* clear attributes if not done already */ | |
2670 | if (!no_modify) { | |
2671 | *dirty += clear_dinode_attr( | |
2672 | mp, dino, lino); | |
2673 | dinoc->di_aformat = | |
2674 | XFS_DINODE_FMT_LOCAL; | |
2675 | } else { | |
507f4e33 NS |
2676 | do_warn( |
2677 | _("would clear attr fork\n")); | |
2bd0ea18 NS |
2678 | } |
2679 | atotblocks = 0; | |
dfc130f3 | 2680 | anextents = 0; |
2bd0ea18 NS |
2681 | } |
2682 | else { | |
2683 | *dirty = 1; /* it's been repaired */ | |
2684 | } | |
2685 | } | |
2686 | } | |
2687 | blkmap_free(ablkmap); | |
2688 | ||
2689 | } else | |
2690 | anextents = 0; | |
2691 | ||
dfc130f3 RC |
2692 | /* |
2693 | * enforce totblocks is 0 for misc types | |
2bd0ea18 NS |
2694 | */ |
2695 | if (process_misc_ino_types_blocks(totblocks, lino, type)) { | |
2696 | if (!no_modify) { | |
2697 | *dirty += clear_dinode(mp, dino, lino); | |
2698 | ASSERT(*dirty > 0); | |
2699 | } | |
2700 | *cleared = 1; | |
2701 | *used = is_free; | |
2702 | *isa_dir = 0; | |
2703 | blkmap_free(dblkmap); | |
2bd0ea18 NS |
2704 | return(1); |
2705 | } | |
2706 | ||
2707 | /* | |
2708 | * correct space counters if required | |
2709 | */ | |
2710 | if (totblocks + atotblocks != INT_GET(dinoc->di_nblocks, ARCH_CONVERT)) { | |
2711 | if (!no_modify) { | |
507f4e33 NS |
2712 | do_warn( |
2713 | _("correcting nblocks for inode %llu, was %llu - counted %llu\n"), | |
2bd0ea18 NS |
2714 | lino, INT_GET(dinoc->di_nblocks, ARCH_CONVERT), |
2715 | totblocks + atotblocks); | |
2716 | *dirty = 1; | |
2717 | INT_SET(dinoc->di_nblocks, ARCH_CONVERT, totblocks + atotblocks); | |
2718 | } else { | |
507f4e33 NS |
2719 | do_warn( |
2720 | _("bad nblocks %llu for inode %llu, would reset to %llu\n"), | |
2bd0ea18 NS |
2721 | INT_GET(dinoc->di_nblocks, ARCH_CONVERT), lino, |
2722 | totblocks + atotblocks); | |
2723 | } | |
2724 | } | |
2725 | ||
2726 | if (nextents > MAXEXTNUM) { | |
507f4e33 | 2727 | do_warn(_("too many data fork extents (%llu) in inode %llu\n"), |
2bd0ea18 NS |
2728 | nextents, lino); |
2729 | ||
2730 | if (!no_modify) { | |
2731 | *dirty += clear_dinode(mp, dino, lino); | |
2732 | ASSERT(*dirty > 0); | |
2733 | } | |
2734 | *cleared = 1; | |
2735 | *used = is_free; | |
2736 | *isa_dir = 0; | |
2737 | blkmap_free(dblkmap); | |
2738 | ||
2739 | return(1); | |
2740 | } | |
2741 | if (nextents != INT_GET(dinoc->di_nextents, ARCH_CONVERT)) { | |
2742 | if (!no_modify) { | |
507f4e33 NS |
2743 | do_warn( |
2744 | _("correcting nextents for inode %llu, was %d - counted %llu\n"), | |
2745 | lino, INT_GET(dinoc->di_nextents, ARCH_CONVERT), | |
2746 | nextents); | |
2bd0ea18 | 2747 | *dirty = 1; |
507f4e33 NS |
2748 | INT_SET(dinoc->di_nextents, ARCH_CONVERT, |
2749 | (xfs_extnum_t) nextents); | |
2bd0ea18 NS |
2750 | } else { |
2751 | do_warn( | |
507f4e33 NS |
2752 | _("bad nextents %d for inode %llu, would reset to %llu\n"), |
2753 | INT_GET(dinoc->di_nextents, ARCH_CONVERT), | |
2754 | lino, nextents); | |
2bd0ea18 NS |
2755 | } |
2756 | } | |
2757 | ||
2758 | if (anextents > MAXAEXTNUM) { | |
507f4e33 | 2759 | do_warn(_("too many attr fork extents (%llu) in inode %llu\n"), |
2bd0ea18 NS |
2760 | anextents, lino); |
2761 | ||
2762 | if (!no_modify) { | |
2763 | *dirty += clear_dinode(mp, dino, lino); | |
2764 | ASSERT(*dirty > 0); | |
2765 | } | |
2766 | *cleared = 1; | |
2767 | *used = is_free; | |
2768 | *isa_dir = 0; | |
2769 | blkmap_free(dblkmap); | |
2bd0ea18 NS |
2770 | return(1); |
2771 | } | |
2772 | if (anextents != INT_GET(dinoc->di_anextents, ARCH_CONVERT)) { | |
2773 | if (!no_modify) { | |
507f4e33 NS |
2774 | do_warn( |
2775 | _("correcting anextents for inode %llu, was %d - counted %llu\n"), | |
2776 | lino, | |
2777 | INT_GET(dinoc->di_anextents, ARCH_CONVERT), | |
2778 | anextents); | |
2bd0ea18 | 2779 | *dirty = 1; |
507f4e33 NS |
2780 | INT_SET(dinoc->di_anextents, ARCH_CONVERT, |
2781 | (xfs_aextnum_t) anextents); | |
2bd0ea18 NS |
2782 | } else { |
2783 | do_warn( | |
507f4e33 NS |
2784 | _("bad anextents %d for inode %llu, would reset to %llu\n"), |
2785 | INT_GET(dinoc->di_anextents, ARCH_CONVERT), | |
2786 | lino, anextents); | |
2bd0ea18 NS |
2787 | } |
2788 | } | |
2789 | ||
2790 | /* | |
2791 | * do any semantic type-based checking here | |
2792 | */ | |
2793 | switch (type) { | |
2794 | case XR_INO_DIR: | |
2795 | if (XFS_SB_VERSION_HASDIRV2(&mp->m_sb)) | |
2796 | err = process_dir2(mp, lino, dino, ino_discovery, | |
2797 | dirty, "", parent, dblkmap); | |
2798 | else | |
2799 | err = process_dir(mp, lino, dino, ino_discovery, | |
2800 | dirty, "", parent, dblkmap); | |
2801 | if (err) | |
2802 | do_warn( | |
507f4e33 | 2803 | _("problem with directory contents in inode %llu\n"), |
2bd0ea18 NS |
2804 | lino); |
2805 | break; | |
2806 | case XR_INO_RTBITMAP: | |
2807 | /* process_rtbitmap XXX */ | |
2808 | err = 0; | |
2809 | break; | |
2810 | case XR_INO_RTSUM: | |
2811 | /* process_rtsummary XXX */ | |
2812 | err = 0; | |
2813 | break; | |
2814 | case XR_INO_SYMLINK: | |
2815 | if ((err = process_symlink(mp, lino, dino, dblkmap))) | |
507f4e33 | 2816 | do_warn(_("problem with symbolic link in inode %llu\n"), |
2bd0ea18 NS |
2817 | lino); |
2818 | break; | |
2819 | case XR_INO_DATA: /* fall through to FIFO case ... */ | |
2820 | case XR_INO_RTDATA: /* fall through to FIFO case ... */ | |
2821 | case XR_INO_CHRDEV: /* fall through to FIFO case ... */ | |
2822 | case XR_INO_BLKDEV: /* fall through to FIFO case ... */ | |
2823 | case XR_INO_SOCK: /* fall through to FIFO case ... */ | |
2824 | case XR_INO_FIFO: | |
2825 | err = 0; | |
2826 | break; | |
2827 | default: | |
507f4e33 | 2828 | printf(_("Unexpected inode type\n")); |
2bd0ea18 NS |
2829 | abort(); |
2830 | } | |
2831 | ||
1e77098c MV |
2832 | if (dblkmap) |
2833 | blkmap_free(dblkmap); | |
2bd0ea18 NS |
2834 | |
2835 | if (err) { | |
2836 | /* | |
2837 | * problem in the inode type-specific semantic | |
2838 | * checking, clear out the inode and get out | |
2839 | */ | |
2840 | if (!no_modify) { | |
2841 | *dirty += clear_dinode(mp, dino, lino); | |
2842 | ASSERT(*dirty > 0); | |
2843 | } | |
2844 | *cleared = 1; | |
2845 | *used = is_free; | |
2846 | *isa_dir = 0; | |
2847 | ||
2848 | return(1); | |
2849 | } | |
2850 | ||
2851 | /* | |
2852 | * check nlinks feature, if it's a version 1 inode, | |
2853 | * just leave nlinks alone. even if it's set wrong, | |
2854 | * it'll be reset when read in. | |
2855 | */ | |
2856 | if (dinoc->di_version > XFS_DINODE_VERSION_1 && !fs_inode_nlink) { | |
2857 | /* | |
2858 | * do we have a fs/inode version mismatch with a valid | |
2859 | * version 2 inode here that has to stay version 2 or | |
2860 | * lose links? | |
2861 | */ | |
2862 | if (INT_GET(dinoc->di_nlink, ARCH_CONVERT) > XFS_MAXLINK_1) { | |
2863 | /* | |
2864 | * yes. are nlink inodes allowed? | |
2865 | */ | |
2866 | if (fs_inode_nlink_allowed) { | |
2867 | /* | |
2868 | * yes, update status variable which will | |
2869 | * cause sb to be updated later. | |
2870 | */ | |
2871 | fs_inode_nlink = 1; | |
2872 | do_warn( | |
507f4e33 | 2873 | _("version 2 inode %llu claims > %u links, "), |
2bd0ea18 NS |
2874 | lino, XFS_MAXLINK_1); |
2875 | if (!no_modify) { | |
2876 | do_warn( | |
507f4e33 | 2877 | _("updating superblock version number\n")); |
2bd0ea18 NS |
2878 | } else { |
2879 | do_warn( | |
507f4e33 | 2880 | _("would update superblock version number\n")); |
2bd0ea18 NS |
2881 | } |
2882 | } else { | |
2883 | /* | |
2884 | * no, have to convert back to onlinks | |
2885 | * even if we lose some links | |
2886 | */ | |
2887 | do_warn( | |
507f4e33 | 2888 | _("WARNING: version 2 inode %llu claims > %u links, "), |
2bd0ea18 NS |
2889 | lino, XFS_MAXLINK_1); |
2890 | if (!no_modify) { | |
2891 | do_warn( | |
507f4e33 NS |
2892 | _("converting back to version 1,\n\tthis may destroy %d links\n"), |
2893 | INT_GET(dinoc->di_nlink, | |
2894 | ARCH_CONVERT) | |
2bd0ea18 NS |
2895 | - XFS_MAXLINK_1); |
2896 | ||
2897 | dinoc->di_version = | |
2898 | XFS_DINODE_VERSION_1; | |
507f4e33 NS |
2899 | INT_SET(dinoc->di_nlink, ARCH_CONVERT, |
2900 | XFS_MAXLINK_1); | |
2901 | INT_SET(dinoc->di_onlink, ARCH_CONVERT, | |
2902 | XFS_MAXLINK_1); | |
2bd0ea18 NS |
2903 | |
2904 | *dirty = 1; | |
2905 | } else { | |
2906 | do_warn( | |
507f4e33 NS |
2907 | _("would convert back to version 1,\n\tthis might destroy %d links\n"), |
2908 | INT_GET(dinoc->di_nlink, | |
2909 | ARCH_CONVERT) | |
2bd0ea18 NS |
2910 | - XFS_MAXLINK_1); |
2911 | } | |
2912 | } | |
2913 | } else { | |
2914 | /* | |
2915 | * do we have a v2 inode that we could convert back | |
2916 | * to v1 without losing any links? if we do and | |
2917 | * we have a mismatch between superblock bits and the | |
2918 | * version bit, alter the version bit in this case. | |
2919 | * | |
2920 | * the case where we lost links was handled above. | |
2921 | */ | |
507f4e33 | 2922 | do_warn(_("found version 2 inode %llu, "), lino); |
2bd0ea18 | 2923 | if (!no_modify) { |
507f4e33 | 2924 | do_warn(_("converting back to version 1\n")); |
2bd0ea18 NS |
2925 | |
2926 | dinoc->di_version = | |
2927 | XFS_DINODE_VERSION_1; | |
507f4e33 NS |
2928 | INT_SET(dinoc->di_onlink, ARCH_CONVERT, |
2929 | INT_GET(dinoc->di_nlink, ARCH_CONVERT)); | |
2bd0ea18 NS |
2930 | |
2931 | *dirty = 1; | |
2932 | } else { | |
507f4e33 | 2933 | do_warn(_("would convert back to version 1\n")); |
2bd0ea18 NS |
2934 | } |
2935 | } | |
2936 | } | |
2937 | ||
2938 | /* | |
2939 | * ok, if it's still a version 2 inode, it's going | |
2940 | * to stay a version 2 inode. it should have a zero | |
2941 | * onlink field, so clear it. | |
2942 | */ | |
2943 | if (dinoc->di_version > XFS_DINODE_VERSION_1 && | |
507f4e33 NS |
2944 | INT_GET(dinoc->di_onlink, ARCH_CONVERT) > 0 && |
2945 | fs_inode_nlink > 0) { | |
2bd0ea18 NS |
2946 | if (!no_modify) { |
2947 | do_warn( | |
507f4e33 | 2948 | _("clearing obsolete nlink field in version 2 inode %llu, was %d, now 0\n"), |
2bd0ea18 | 2949 | lino, INT_GET(dinoc->di_onlink, ARCH_CONVERT)); |
46eca962 | 2950 | dinoc->di_onlink = 0; |
2bd0ea18 NS |
2951 | *dirty = 1; |
2952 | } else { | |
2953 | do_warn( | |
507f4e33 | 2954 | _("would clear obsolete nlink field in version 2 inode %llu, currently %d\n"), |
2bd0ea18 NS |
2955 | lino, INT_GET(dinoc->di_onlink, ARCH_CONVERT)); |
2956 | *dirty = 1; | |
2957 | } | |
2958 | } | |
2959 | ||
2960 | return(retval > 0 ? 1 : 0); | |
2961 | } | |
2962 | ||
2963 | /* | |
2964 | * returns 1 if inode is used, 0 if free. | |
2965 | * performs any necessary salvaging actions. | |
2966 | * note that we leave the generation count alone | |
2967 | * because nothing we could set it to would be | |
2968 | * guaranteed to be correct so the best guess for | |
2969 | * the correct value is just to leave it alone. | |
2970 | * | |
2971 | * The trick is detecting empty files. For those, | |
2972 | * the core and the forks should all be in the "empty" | |
2973 | * or zero-length state -- a zero or possibly minimum length | |
2974 | * (in the case of dirs) extent list -- although inline directories | |
2975 | * and symlinks might be handled differently. So it should be | |
2976 | * possible to sanity check them against each other. | |
2977 | * | |
2978 | * If the forks are an empty extent list though, then forget it. | |
2979 | * The file is toast anyway since we can't recover its storage. | |
2980 | * | |
2981 | * Parameters: | |
2982 | * Ins: | |
2983 | * mp -- mount structure | |
2984 | * dino -- pointer to on-disk inode structure | |
2985 | * agno/ino -- inode numbers | |
2986 | * free -- whether the map thinks the inode is free (1 == free) | |
2987 | * ino_discovery -- whether we should examine directory | |
2988 | * contents to discover new inodes | |
2989 | * check_dups -- whether we should check to see if the | |
2990 | * inode references duplicate blocks | |
2991 | * if so, we compare the inode's claimed | |
2992 | * blocks against the contents of the | |
2993 | * duplicate extent list but we don't | |
2994 | * set the bitmap. If not, we set the | |
2995 | * bitmap and try and detect multiply | |
2996 | * claimed blocks using the bitmap. | |
2997 | * Outs: | |
2998 | * dirty -- whether we changed the inode (1 == yes) | |
2999 | * cleared -- whether we cleared the inode (1 == yes). In | |
3000 | * no modify mode, if we would have cleared it | |
3001 | * used -- 1 if the inode is used, 0 if free. In no modify | |
3002 | * mode, whether the inode should be used or free | |
3003 | * isa_dir -- 1 if the inode is a directory, 0 if not. In | |
3004 | * no modify mode, if the inode would be a dir or not. | |
3005 | * | |
3006 | * Return value -- 0 if the inode is good, 1 if it is/was corrupt | |
3007 | */ | |
3008 | ||
3009 | int | |
3010 | process_dinode(xfs_mount_t *mp, | |
3011 | xfs_dinode_t *dino, | |
3012 | xfs_agnumber_t agno, | |
3013 | xfs_agino_t ino, | |
3014 | int was_free, | |
3015 | int *dirty, | |
3016 | int *cleared, | |
3017 | int *used, | |
3018 | int ino_discovery, | |
3019 | int check_dups, | |
3020 | int extra_attr_check, | |
3021 | int *isa_dir, | |
3022 | xfs_ino_t *parent) | |
3023 | { | |
3024 | const int verify_mode = 0; | |
3025 | const int uncertain = 0; | |
3026 | ||
3027 | #ifdef XR_INODE_TRACE | |
3028 | fprintf(stderr, "processing inode %d/%d\n", agno, ino); | |
3029 | #endif | |
3030 | return(process_dinode_int(mp, dino, agno, ino, was_free, dirty, | |
3031 | cleared, used, verify_mode, uncertain, | |
3032 | ino_discovery, check_dups, extra_attr_check, | |
3033 | isa_dir, parent)); | |
3034 | } | |
3035 | ||
3036 | /* | |
3037 | * a more cursory check, check inode core, *DON'T* check forks | |
3038 | * this basically just verifies whether the inode is an inode | |
3039 | * and whether or not it has been totally trashed. returns 0 | |
3040 | * if the inode passes the cursory sanity check, 1 otherwise. | |
3041 | */ | |
3042 | int | |
3043 | verify_dinode(xfs_mount_t *mp, | |
3044 | xfs_dinode_t *dino, | |
3045 | xfs_agnumber_t agno, | |
3046 | xfs_agino_t ino) | |
3047 | { | |
3048 | xfs_ino_t parent; | |
3049 | int cleared = 0; | |
3050 | int used = 0; | |
3051 | int dirty = 0; | |
3052 | int isa_dir = 0; | |
3053 | const int verify_mode = 1; | |
3054 | const int check_dups = 0; | |
3055 | const int ino_discovery = 0; | |
3056 | const int uncertain = 0; | |
3057 | ||
3058 | return(process_dinode_int(mp, dino, agno, ino, 0, &dirty, | |
3059 | &cleared, &used, verify_mode, | |
3060 | uncertain, ino_discovery, check_dups, | |
3061 | 0, &isa_dir, &parent)); | |
3062 | } | |
3063 | ||
3064 | /* | |
3065 | * like above only for inode on the uncertain list. it sets | |
3066 | * the uncertain flag which makes process_dinode_int quieter. | |
3067 | * returns 0 if the inode passes the cursory sanity check, 1 otherwise. | |
3068 | */ | |
3069 | int | |
3070 | verify_uncertain_dinode(xfs_mount_t *mp, | |
3071 | xfs_dinode_t *dino, | |
3072 | xfs_agnumber_t agno, | |
3073 | xfs_agino_t ino) | |
3074 | { | |
3075 | xfs_ino_t parent; | |
3076 | int cleared = 0; | |
3077 | int used = 0; | |
3078 | int dirty = 0; | |
3079 | int isa_dir = 0; | |
3080 | const int verify_mode = 1; | |
3081 | const int check_dups = 0; | |
3082 | const int ino_discovery = 0; | |
3083 | const int uncertain = 1; | |
3084 | ||
3085 | return(process_dinode_int(mp, dino, agno, ino, 0, &dirty, | |
3086 | &cleared, &used, verify_mode, | |
3087 | uncertain, ino_discovery, check_dups, | |
3088 | 0, &isa_dir, &parent)); | |
3089 | } |