|
|
| version 1.1, 2007/06/05 04:03:18 | version 1.2, 2007/09/07 06:11:50 |
|---|---|
| Line 42 static void metadump_help(void); | Line 42 static void metadump_help(void); |
| static const cmdinfo_t metadump_cmd = | static const cmdinfo_t metadump_cmd = |
| { "metadump", NULL, metadump_f, 0, -1, 0, | { "metadump", NULL, metadump_f, 0, -1, 0, |
| "[-e] [-g] [-w] [-o] filename", | "[-e] [-g] [-m max_extent] [-w] [-o] filename", |
| "dump metadata to a file", metadump_help }; | "dump metadata to a file", metadump_help }; |
| static FILE *outf; /* metadump file */ | static FILE *outf; /* metadump file */ |
| Line 58 static xfs_ino_t cur_ino; | Line 58 static xfs_ino_t cur_ino; |
| static int show_progress = 0; | static int show_progress = 0; |
| static int stop_on_read_error = 0; | static int stop_on_read_error = 0; |
| static int max_extent_size = 20; | |
| static int dont_obfuscate = 0; | static int dont_obfuscate = 0; |
| static int show_warnings = 0; | static int show_warnings = 0; |
| static int progress_since_warning = 0; | static int progress_since_warning = 0; |
| Line 76 metadump_help(void) | Line 77 metadump_help(void) |
| " The 'metadump' command dumps the known metadata to a compact file suitable\n" | " The 'metadump' command dumps the known metadata to a compact file suitable\n" |
| " for compressing and sending to an XFS maintainer for corruption analysis \n" | " for compressing and sending to an XFS maintainer for corruption analysis \n" |
| " or xfs_repair failures.\n\n" | " or xfs_repair failures.\n\n" |
| " There are 3 options:\n" | " Options:\n" |
| " -e -- Ignore read errors and keep going\n" | " -e -- Ignore read errors and keep going\n" |
| " -g -- Display dump progress\n" | " -g -- Display dump progress\n" |
| " -m -- Specify max extent size in blocks to copy (default = 20 blocks)\n" | |
| " -o -- Don't obfuscate names and extended attributes\n" | " -o -- Don't obfuscate names and extended attributes\n" |
| " -w -- Show warnings of bad metadata information\n" | " -w -- Show warnings of bad metadata information\n" |
| "\n"); | "\n"); |
| Line 206 scan_btree( | Line 208 scan_btree( |
| static int | static int |
| valid_bno( | valid_bno( |
| xfs_agblock_t bno, | |
| xfs_agnumber_t agno, | xfs_agnumber_t agno, |
| xfs_agblock_t agbno, | xfs_agblock_t agbno) |
| typnm_t btype) | |
| { | { |
| if (bno > 0 && bno <= mp->m_sb.sb_agblocks) | if (agno < (mp->m_sb.sb_agcount - 1) && agbno > 0 && |
| agbno <= mp->m_sb.sb_agblocks) | |
| return 1; | |
| if (agno == (mp->m_sb.sb_agcount - 1) && agbno > 0 && | |
| agbno <= (mp->m_sb.sb_dblocks - | |
| (mp->m_sb.sb_agcount - 1) * mp->m_sb.sb_agblocks)) | |
| return 1; | return 1; |
| if (show_warnings) | |
| print_warning("invalid block number (%u) in %s block %u/%u", | |
| bno, typtab[btype].name, agno, agbno); | |
| return 0; | return 0; |
| } | } |
| static int | static int |
| scanfunc_freesp( | scanfunc_freesp( |
| xfs_btree_hdr_t *bthdr, | xfs_btree_hdr_t *bthdr, |
| Line 231 scanfunc_freesp( | Line 234 scanfunc_freesp( |
| { | { |
| xfs_alloc_ptr_t *pp; | xfs_alloc_ptr_t *pp; |
| int i; | int i; |
| int nrecs; | int numrecs; |
| if (level == 0) | if (level == 0) |
| return 1; | return 1; |
| nrecs = be16_to_cpu(bthdr->bb_numrecs); | numrecs = be16_to_cpu(bthdr->bb_numrecs); |
| if (nrecs > mp->m_alloc_mxr[1]) { | if (numrecs > mp->m_alloc_mxr[1]) { |
| if (show_warnings) | if (show_warnings) |
| print_warning("invalid nrecs (%u) in %s block %u/%u", | print_warning("invalid numrecs (%u) in %s block %u/%u", |
| nrecs, typtab[btype].name, agno, agbno); | numrecs, typtab[btype].name, agno, agbno); |
| return 1; | return 1; |
| } | } |
| pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_alloc, bthdr, 1, | pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_alloc, bthdr, 1, |
| mp->m_alloc_mxr[1]); | mp->m_alloc_mxr[1]); |
| for (i = 0; i < nrecs; i++) { | for (i = 0; i < numrecs; i++) { |
| if (!valid_bno(be32_to_cpu(pp[i]), agno, agbno, btype)) | if (!valid_bno(agno, be32_to_cpu(pp[i]))) { |
| if (show_warnings) | |
| print_warning("invalid block number (%u/%u) " | |
| "in %s block %u/%u", | |
| agno, be32_to_cpu(pp[i]), | |
| typtab[btype].name, agno, agbno); | |
| continue; | continue; |
| } | |
| if (!scan_btree(agno, be32_to_cpu(pp[i]), level, btype, arg, | if (!scan_btree(agno, be32_to_cpu(pp[i]), level, btype, arg, |
| scanfunc_freesp)) | scanfunc_freesp)) |
| return 0; | return 0; |
| Line 492 obfuscate_sf_dir( | Line 501 obfuscate_sf_dir( |
| if (ino_dir_size > XFS_DFORK_DSIZE(dip, mp)) { | if (ino_dir_size > XFS_DFORK_DSIZE(dip, mp)) { |
| ino_dir_size = XFS_DFORK_DSIZE(dip, mp); | ino_dir_size = XFS_DFORK_DSIZE(dip, mp); |
| if (show_warnings) | if (show_warnings) |
| print_warning("invalid size for dir inode %llu", | print_warning("invalid size in dir inode %llu", |
| (long long)cur_ino); | (long long)cur_ino); |
| } | } |
| Line 539 static void | Line 548 static void |
| obfuscate_sf_symlink( | obfuscate_sf_symlink( |
| xfs_dinode_t *dip) | xfs_dinode_t *dip) |
| { | { |
| int i; | int len; |
| len = dip->di_core.di_size; | |
| if (len > XFS_DFORK_DSIZE(dip, mp)) { | |
| if (show_warnings) | |
| print_warning("invalid size (%d) in symlink inode %llu", | |
| len, (long long)cur_ino); | |
| len = XFS_DFORK_DSIZE(dip, mp); | |
| } | |
| for (i = 0; i < dip->di_core.di_size; i++) | while (len > 0) |
| dip->di_u.di_symlink[i] = random() % 127 + 1; | dip->di_u.di_symlink[--len] = random() % 127 + 1; |
| } | } |
| static void | static void |
| Line 858 process_bmbt_reclist( | Line 875 process_bmbt_reclist( |
| typnm_t btype) | typnm_t btype) |
| { | { |
| int i; | int i; |
| xfs_dfiloff_t o; | xfs_dfiloff_t o, op; |
| xfs_dfsbno_t s; | xfs_dfsbno_t s; |
| xfs_dfilblks_t c; | xfs_dfilblks_t c, cp; |
| int f; | int f; |
| xfs_dfiloff_t last; | xfs_dfiloff_t last; |
| xfs_agnumber_t agno; | |
| xfs_agblock_t agbno; | |
| if (btype == TYP_DATA) | if (btype == TYP_DATA) |
| return 1; | return 1; |
| Line 873 process_bmbt_reclist( | Line 892 process_bmbt_reclist( |
| for (i = 0; i < numrecs; i++, rp++) { | for (i = 0; i < numrecs; i++, rp++) { |
| convert_extent(rp, &o, &s, &c, &f); | convert_extent(rp, &o, &s, &c, &f); |
| /* | |
| * ignore extents that are clearly bogus, and if a bogus | |
| * one is found, stop processing remaining extents | |
| */ | |
| if (i > 0 && op + cp > o) { | |
| if (show_warnings) | |
| print_warning("bmap extent %d in %s ino %llu " | |
| "starts at %llu, previous extent " | |
| "ended at %llu", i, | |
| typtab[btype].name, (long long)cur_ino, | |
| o, op + cp - 1); | |
| break; | |
| } | |
| if (c > max_extent_size) { | |
| /* | |
| * since we are only processing non-data extents, | |
| * large numbers of blocks in a metadata extent is | |
| * extremely rare and more than likely to be corrupt. | |
| */ | |
| if (show_warnings) | |
| print_warning("suspicious count %u in bmap " | |
| "extent %d in %s ino %llu", c, i, | |
| typtab[btype].name, (long long)cur_ino); | |
| break; | |
| } | |
| op = o; | |
| cp = c; | |
| agno = XFS_FSB_TO_AGNO(mp, s); | |
| agbno = XFS_FSB_TO_AGBNO(mp, s); | |
| if (!valid_bno(agno, agbno)) { | |
| if (show_warnings) | |
| print_warning("invalid block number %u/%u " | |
| "(%llu) in bmap extent %d in %s ino " | |
| "%llu", agno, agbno, s, i, | |
| typtab[btype].name, (long long)cur_ino); | |
| break; | |
| } | |
| if (!valid_bno(agno, agbno + c - 1)) { | |
| if (show_warnings) | |
| print_warning("bmap extent %i in %s inode %llu " | |
| "overflows AG (end is %u/%u)", i, | |
| typtab[btype].name, (long long)cur_ino, | |
| agno, agbno + c - 1); | |
| break; | |
| } | |
| push_cur(); | push_cur(); |
| set_cur(&typtab[btype], XFS_FSB_TO_DADDR(mp, s), c * blkbb, | set_cur(&typtab[btype], XFS_FSB_TO_DADDR(mp, s), c * blkbb, |
| DB_RING_IGN, NULL); | DB_RING_IGN, NULL); |
| if (iocur_top->data == NULL) { | if (iocur_top->data == NULL) { |
| print_warning("cannot read %s block %u/%u", | print_warning("cannot read %s block %u/%u (%llu)", |
| typtab[btype].name, | typtab[btype].name, agno, agbno, s); |
| XFS_FSB_TO_AGNO(mp, s), | |
| XFS_FSB_TO_AGBNO(mp, s)); | |
| if (stop_on_read_error) | if (stop_on_read_error) |
| return 0; | return 0; |
| } else { | } else { |
| Line 1054 process_exinode( | Line 1122 process_exinode( |
| typnm_t itype) | typnm_t itype) |
| { | { |
| int whichfork; | int whichfork; |
| xfs_extnum_t nex; | |
| whichfork = (itype == TYP_ATTR) ? XFS_ATTR_FORK : XFS_DATA_FORK; | whichfork = (itype == TYP_ATTR) ? XFS_ATTR_FORK : XFS_DATA_FORK; |
| return process_bmbt_reclist( | nex = XFS_DFORK_NEXTENTS_HOST(dip, whichfork); |
| (xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip, whichfork), | if (nex < 0 || nex > XFS_DFORK_SIZE_HOST(dip, mp, whichfork) / |
| XFS_DFORK_NEXTENTS_HOST(dip, whichfork), itype); | sizeof(xfs_bmbt_rec_t)) { |
| if (show_warnings) | |
| print_warning("bad number of extents %d in inode %lld", | |
| nex, (long long)cur_ino); | |
| return 1; | |
| } | |
| return process_bmbt_reclist((xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip, | |
| whichfork), nex, itype); | |
| } | } |
| static int | static int |
| Line 1108 process_inode( | Line 1185 process_inode( |
| success = 1; | success = 1; |
| cur_ino = XFS_AGINO_TO_INO(mp, agno, agino); | cur_ino = XFS_AGINO_TO_INO(mp, agno, agino); |
| /* copy appropriate data fork metadata */ | /* copy appropriate data fork metadata */ |
| switch (dip->di_core.di_mode & S_IFMT) { | switch (dip->di_core.di_mode & S_IFMT) { |
| case S_IFDIR: | case S_IFDIR: |
| Line 1118 process_inode( | Line 1194 process_inode( |
| case S_IFLNK: | case S_IFLNK: |
| success = process_inode_data(dip, TYP_SYMLINK); | success = process_inode_data(dip, TYP_SYMLINK); |
| break; | break; |
| default: | case S_IFREG: |
| success = process_inode_data(dip, TYP_DATA); | success = process_inode_data(dip, TYP_DATA); |
| break; | |
| default: ; | |
| } | } |
| clear_nametable(); | clear_nametable(); |
| /* copy extended attributes if they exist */ | /* copy extended attributes if they exist and forkoff is valid */ |
| if (success && dip->di_core.di_forkoff) { | if (success && XFS_CFORK_DSIZE(&dip->di_core, mp) < XFS_LITINO(mp)) { |
| attr_data.remote_val_count = 0; | attr_data.remote_val_count = 0; |
| switch (dip->di_core.di_aformat) { | switch (dip->di_core.di_aformat) { |
| case XFS_DINODE_FMT_LOCAL: | case XFS_DINODE_FMT_LOCAL: |
| Line 1165 copy_inode_chunk( | Line 1243 copy_inode_chunk( |
| agbno = XFS_AGINO_TO_AGBNO(mp, agino); | agbno = XFS_AGINO_TO_AGBNO(mp, agino); |
| off = XFS_INO_TO_OFFSET(mp, agino); | off = XFS_INO_TO_OFFSET(mp, agino); |
| if (agino == 0 || agino == NULLAGINO || !valid_bno(agno, agbno) || | |
| !valid_bno(agno, XFS_AGINO_TO_AGBNO(mp, | |
| agino + XFS_INODES_PER_CHUNK - 1))) { | |
| if (show_warnings) | |
| print_warning("bad inode number %llu (%u/%u)", | |
| XFS_AGINO_TO_INO(mp, agno, agino), agno, agino); | |
| return 1; | |
| } | |
| push_cur(); | push_cur(); |
| set_cur(&typtab[TYP_INODE], XFS_AGB_TO_DADDR(mp, agno, agbno), | set_cur(&typtab[TYP_INODE], XFS_AGB_TO_DADDR(mp, agno, agbno), |
| XFS_FSB_TO_BB(mp, XFS_IALLOC_BLOCKS(mp)), | XFS_FSB_TO_BB(mp, XFS_IALLOC_BLOCKS(mp)), |
| Line 1175 copy_inode_chunk( | Line 1262 copy_inode_chunk( |
| } | } |
| /* | /* |
| * check for basic assumptions about inode chunks, and if any | |
| * assumptions fail, don't process the inode chunk. | |
| */ | |
| if ((mp->m_sb.sb_inopblock <= XFS_INODES_PER_CHUNK && off != 0) || | |
| (mp->m_sb.sb_inopblock > XFS_INODES_PER_CHUNK && | |
| off % XFS_INODES_PER_CHUNK != 0) || | |
| (XFS_SB_VERSION_HASALIGN(&mp->m_sb) && | |
| agbno % mp->m_sb.sb_inoalignmt != 0)) { | |
| if (show_warnings) | |
| print_warning("badly aligned inode (start = %llu)", | |
| XFS_AGINO_TO_INO(mp, agno, agino)); | |
| goto skip_processing; | |
| } | |
| /* | |
| * scan through inodes and copy any btree extent lists, directory | * scan through inodes and copy any btree extent lists, directory |
| * contents and extended attributes. | * contents and extended attributes. |
| */ | */ |
| for (i = 0; i < XFS_INODES_PER_CHUNK; i++) { | for (i = 0; i < XFS_INODES_PER_CHUNK; i++) { |
| xfs_dinode_t *dip; | xfs_dinode_t *dip; |
| Line 1191 copy_inode_chunk( | Line 1293 copy_inode_chunk( |
| if (!process_inode(agno, agino + i, dip)) | if (!process_inode(agno, agino + i, dip)) |
| return 0; | return 0; |
| } | } |
| skip_processing: | |
| if (!write_buf(iocur_top)) | if (!write_buf(iocur_top)) |
| return 0; | return 0; |
| Line 1219 scanfunc_ino( | Line 1321 scanfunc_ino( |
| xfs_inobt_rec_t *rp; | xfs_inobt_rec_t *rp; |
| xfs_inobt_ptr_t *pp; | xfs_inobt_ptr_t *pp; |
| int i; | int i; |
| int numrecs; | |
| numrecs = be16_to_cpu(bthdr->bb_numrecs); | |
| if (level == 0) { | if (level == 0) { |
| if (numrecs > mp->m_inobt_mxr[0]) { | |
| if (show_warnings) | |
| print_warning("invalid numrecs %d in %s " | |
| "block %u/%u", numrecs, | |
| typtab[btype].name, agno, agbno); | |
| numrecs = mp->m_inobt_mxr[0]; | |
| } | |
| rp = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_inobt, | rp = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_inobt, |
| bthdr, 1, mp->m_inobt_mxr[0]); | bthdr, 1, mp->m_inobt_mxr[0]); |
| for (i = 0; i < be16_to_cpu(bthdr->bb_numrecs); i++, rp++) { | for (i = 0; i < numrecs; i++, rp++) { |
| if (!copy_inode_chunk(agno, rp)) | if (!copy_inode_chunk(agno, rp)) |
| return 0; | return 0; |
| } | } |
| } else { | return 1; |
| pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_inobt, | } |
| bthdr, 1, mp->m_inobt_mxr[1]); | |
| for (i = 0; i < be16_to_cpu(bthdr->bb_numrecs); i++) { | if (numrecs > mp->m_inobt_mxr[1]) { |
| if (!valid_bno(be32_to_cpu(pp[i]), agno, agbno, btype)) | if (show_warnings) |
| continue; | print_warning("invalid numrecs %d in %s block %u/%u", |
| if (!scan_btree(agno, be32_to_cpu(pp[i]), level, | numrecs, typtab[btype].name, agno, agbno); |
| btype, arg, scanfunc_ino)) | numrecs = mp->m_inobt_mxr[1]; |
| return 0; | } |
| pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_inobt, | |
| bthdr, 1, mp->m_inobt_mxr[1]); | |
| for (i = 0; i < numrecs; i++) { | |
| if (!valid_bno(agno, be32_to_cpu(pp[i]))) { | |
| if (show_warnings) | |
| print_warning("invalid block number (%u/%u) " | |
| "in %s block %u/%u", | |
| agno, be32_to_cpu(pp[i]), | |
| typtab[btype].name, agno, agbno); | |
| continue; | |
| } | } |
| if (!scan_btree(agno, be32_to_cpu(pp[i]), level, | |
| btype, arg, scanfunc_ino)) | |
| return 0; | |
| } | } |
| return 1; | return 1; |
| } | } |
| Line 1441 metadump_f( | Line 1567 metadump_f( |
| xfs_agnumber_t agno; | xfs_agnumber_t agno; |
| int c; | int c; |
| int start_iocur_sp; | int start_iocur_sp; |
| char *p; | |
| exitcode = 1; | exitcode = 1; |
| show_progress = 0; | show_progress = 0; |
| Line 1453 metadump_f( | Line 1580 metadump_f( |
| return 0; | return 0; |
| } | } |
| while ((c = getopt(argc, argv, "egow")) != EOF) { | while ((c = getopt(argc, argv, "egm:ow")) != EOF) { |
| switch (c) { | switch (c) { |
| case 'e': | case 'e': |
| stop_on_read_error = 1; | stop_on_read_error = 1; |
| Line 1461 metadump_f( | Line 1588 metadump_f( |
| case 'g': | case 'g': |
| show_progress = 1; | show_progress = 1; |
| break; | break; |
| case 'm': | |
| max_extent_size = (int)strtol(optarg, &p, 0); | |
| if (*p != '\0' || max_extent_size <= 0) { | |
| print_warning("bad max extent size %s", | |
| optarg); | |
| return 0; | |
| } | |
| break; | |
| case 'o': | case 'o': |
| dont_obfuscate = 1; | dont_obfuscate = 1; |
| break; | break; |