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