diff --git a/db/metadump.c b/db/metadump.c index a599571..238f864 100644 --- a/db/metadump.c +++ b/db/metadump.c @@ -17,6 +17,7 @@ */ #include +#include #include "bmap.h" #include "command.h" #include "metadump.h" @@ -56,7 +57,7 @@ static void metadump_help(void); static const cmdinfo_t metadump_cmd = { "metadump", NULL, metadump_f, 0, -1, 0, - N_("[-e] [-g] [-m max_extent] [-w] [-o] filename"), + N_("[-a] [-e] [-g] [-m max_extent] [-w] [-o] filename"), N_("dump metadata to a file"), metadump_help }; static FILE *outf; /* metadump file */ @@ -73,7 +74,8 @@ static xfs_ino_t cur_ino; static int show_progress = 0; static int stop_on_read_error = 0; static int max_extent_size = DEFAULT_MAX_EXT_SIZE; -static int dont_obfuscate = 0; +static int obfuscate = 1; +static int zero_stale_data = 1; static int show_warnings = 0; static int progress_since_warning = 0; @@ -92,6 +94,7 @@ metadump_help(void) " for compressing and sending to an XFS maintainer for corruption analysis \n" " or xfs_repair failures.\n\n" " Options:\n" +" -a -- Copy full metadata blocks without zeroing unused space\n" " -e -- Ignore read errors and keep going\n" " -g -- Display dump progress\n" " -m -- Specify max extent size in blocks to copy (default = %d blocks)\n" @@ -242,6 +245,124 @@ write_buf( return seenint() ? -EINTR : 0; } +static void +zero_btree_node( + struct xfs_btree_block *block, + typnm_t btype) +{ + int nrecs; + xfs_bmbt_ptr_t *bpp; + xfs_bmbt_key_t *bkp; + xfs_inobt_ptr_t *ipp; + xfs_inobt_key_t *ikp; + xfs_alloc_ptr_t *app; + xfs_alloc_key_t *akp; + void *zp1, *zp2; + int zlen1, zlen2; + + nrecs = be16_to_cpu(block->bb_numrecs); + + switch (btype) { + case TYP_BMAPBTA: + case TYP_BMAPBTD: + bkp = XFS_BMBT_KEY_ADDR(mp, block, 1); + bpp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[1]); + zp1 = &bkp[nrecs]; + zlen1 = (char *)&bpp[0] - (char *)&bkp[nrecs]; + zp2 = &bpp[nrecs]; + zlen2 = (char *)block + mp->m_sb.sb_blocksize - + (char *)&bpp[nrecs]; + break; + case TYP_INOBT: + case TYP_FINOBT: + ikp = XFS_INOBT_KEY_ADDR(mp, block, 1); + ipp = XFS_INOBT_PTR_ADDR(mp, block, 1, mp->m_inobt_mxr[1]); + zp1 = &ikp[nrecs]; + zlen1 = (char *)&ipp[0] - (char *)&ikp[nrecs]; + zp2 = &ipp[nrecs]; + zlen2 = (char *)block + mp->m_sb.sb_blocksize - + (char *)&ipp[nrecs]; + break; + case TYP_BNOBT: + case TYP_CNTBT: + akp = XFS_ALLOC_KEY_ADDR(mp, block, 1); + app = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]); + zp1 = &akp[nrecs]; + zlen1 = (char *)&app[0] - (char *)&akp[nrecs]; + zp2 = &app[nrecs]; + zlen2 = (char *)block + mp->m_sb.sb_blocksize - + (char *)&app[nrecs]; + break; + default: + zp1 = NULL; + break; + } + + if (zp1 && zp2) { + /* Zero from end of keys to beginning of pointers */ + memset(zp1, 0, zlen1); + /* Zero from end of pointers to end of block */ + memset(zp2, 0, zlen2); + } +} + +static void +zero_btree_leaf( + struct xfs_btree_block *block, + typnm_t btype) +{ + int nrecs; + struct xfs_bmbt_rec *brp; + struct xfs_inobt_rec *irp; + struct xfs_alloc_rec *arp; + void *zp; + int zlen; + + nrecs = be16_to_cpu(block->bb_numrecs); + + switch (btype) { + case TYP_BMAPBTA: + case TYP_BMAPBTD: + brp = XFS_BMBT_REC_ADDR(mp, block, 1); + zp = &brp[nrecs]; + zlen = (char *)block + mp->m_sb.sb_blocksize - (char *)&brp[nrecs]; + break; + case TYP_INOBT: + case TYP_FINOBT: + irp = XFS_INOBT_REC_ADDR(mp, block, 1); + zp = &irp[nrecs]; + zlen = (char *)block + mp->m_sb.sb_blocksize - (char *)&irp[nrecs]; + break; + case TYP_BNOBT: + case TYP_CNTBT: + arp = XFS_ALLOC_REC_ADDR(mp, block, 1); + zp = &arp[nrecs]; + zlen = (char *)block + mp->m_sb.sb_blocksize - (char *)&arp[nrecs]; + break; + default: + zp = NULL; + break; + } + + /* Zero from end of records to end of block */ + if (zp && zlen < mp->m_sb.sb_blocksize) + memset(zp, 0, zlen); +} + +static void +zero_btree_block( + struct xfs_btree_block *block, + typnm_t btype) +{ + int level; + + level = be16_to_cpu(block->bb_level); + + if (level > 0) + zero_btree_node(block, btype); + else + zero_btree_leaf(block, btype); +} static int scan_btree( @@ -268,6 +389,12 @@ scan_btree( rval = !stop_on_read_error; goto pop_out; } + + if (zero_stale_data) { + zero_btree_block(iocur_top->data, btype); + iocur_top->need_crc = 1; + } + if (write_buf(iocur_top)) goto pop_out; @@ -960,7 +1087,7 @@ generate_obfuscated_name( } static void -obfuscate_sf_dir( +process_sf_dir( xfs_dinode_t *dip) { struct xfs_dir2_sf_hdr *sfp; @@ -1007,12 +1134,18 @@ obfuscate_sf_dir( (char *)sfp); } - generate_obfuscated_name(xfs_dir3_sfe_get_ino(mp, sfp, sfep), + if (obfuscate) + generate_obfuscated_name( + xfs_dir3_sfe_get_ino(mp, sfp, sfep), namelen, &sfep->name[0]); sfep = (xfs_dir2_sf_entry_t *)((char *)sfep + xfs_dir3_sf_entsize(mp, sfp, namelen)); } + + /* zero stale data in rest of space in data fork, if any */ + if (zero_stale_data && (ino_dir_size < XFS_DFORK_DSIZE(dip, mp))) + memset(sfep, 0, XFS_DFORK_DSIZE(dip, mp) - ino_dir_size); } /* @@ -1059,7 +1192,7 @@ obfuscate_path_components( } static void -obfuscate_sf_symlink( +process_sf_symlink( xfs_dinode_t *dip) { __uint64_t len; @@ -1074,16 +1207,21 @@ obfuscate_sf_symlink( } buf = (char *)XFS_DFORK_DPTR(dip); - obfuscate_path_components(buf, len); + if (obfuscate) + obfuscate_path_components(buf, len); + + /* zero stale data in rest of space in data fork, if any */ + if (zero_stale_data && len < XFS_DFORK_DSIZE(dip, mp)) + memset(&buf[len], 0, XFS_DFORK_DSIZE(dip, mp) - len); } static void -obfuscate_sf_attr( +process_sf_attr( xfs_dinode_t *dip) { /* - * with extended attributes, obfuscate the names and zero the actual - * values. + * with extended attributes, obfuscate the names and fill the actual + * values with 'v' (to see a valid string length, as opposed to NULLs) */ xfs_attr_shortform_t *asfp; @@ -1122,16 +1260,24 @@ obfuscate_sf_attr( break; } - generate_obfuscated_name(0, asfep->namelen, &asfep->nameval[0]); - memset(&asfep->nameval[asfep->namelen], 0, asfep->valuelen); + if (obfuscate) { + generate_obfuscated_name(0, asfep->namelen, + &asfep->nameval[0]); + memset(&asfep->nameval[asfep->namelen], 'v', + asfep->valuelen); + } asfep = (xfs_attr_sf_entry_t *)((char *)asfep + XFS_ATTR_SF_ENTSIZE(asfep)); } + + /* zero stale data in rest of space in attr fork, if any */ + if (zero_stale_data && (ino_attr_size < XFS_DFORK_ASIZE(dip, mp))) + memset(asfep, 0, XFS_DFORK_ASIZE(dip, mp) - ino_attr_size); } static void -obfuscate_dir_data_block( +process_dir_data_block( char *block, xfs_dfiloff_t offset, int is_block_format) @@ -1151,9 +1297,6 @@ obfuscate_dir_data_block( datahdr = (struct xfs_dir2_data_hdr *)block; - if (offset % mp->m_dirblkfsbs != 0) - return; /* corrupted, leave it alone */ - if (is_block_format) { xfs_dir2_leaf_entry_t *blp; xfs_dir2_block_tail_t *btp; @@ -1186,7 +1329,7 @@ obfuscate_dir_data_block( dir_offset = xfs_dir3_data_entry_offset(datahdr); ptr = block + dir_offset; - endptr = block + mp->m_sb.sb_blocksize; + endptr = block + mp->m_dirblksize; while (ptr < endptr && dir_offset < end_of_data) { xfs_dir2_data_entry_t *dep; @@ -1210,6 +1353,20 @@ obfuscate_dir_data_block( return; dir_offset += length; ptr += length; + /* + * Zero the unused space up to the tag - the tag is + * actually at a variable offset, so zeroing &dup->tag + * is zeroing the free space in between + */ + if (zero_stale_data) { + int zlen = length - + sizeof(xfs_dir2_data_unused_t); + + if (zlen > 0) { + memset(&dup->tag, 0, zlen); + iocur_top->need_crc = 1; + } + } if (dir_offset >= end_of_data || ptr >= endptr) return; } @@ -1228,19 +1385,48 @@ obfuscate_dir_data_block( if (be16_to_cpu(*xfs_dir3_data_entry_tag_p(mp, dep)) != dir_offset) return; - generate_obfuscated_name(be64_to_cpu(dep->inumber), + + if (obfuscate) + generate_obfuscated_name(be64_to_cpu(dep->inumber), dep->namelen, &dep->name[0]); dir_offset += length; ptr += length; + /* Zero the unused space after name, up to the tag */ + if (zero_stale_data) { + /* 1 byte for ftype; don't bother with conditional */ + int zlen = + (char *)xfs_dir3_data_entry_tag_p(mp, dep) - + (char *)&dep->name[dep->namelen] - 1; + if (zlen > 0) { + memset(&dep->name[dep->namelen] + 1, 0, zlen); + iocur_top->need_crc = 1; + } + } } } static void -obfuscate_symlink_block( +process_symlink_block( char *block) { - /* XXX: need to handle CRC headers */ - obfuscate_path_components(block, mp->m_sb.sb_blocksize); + char *link = block; + + if (xfs_sb_version_hascrc(&(mp)->m_sb)) + link += sizeof(struct xfs_dsymlink_hdr); + + if (obfuscate) + obfuscate_path_components(link, XFS_SYMLINK_BUF_SPACE(mp, + mp->m_sb.sb_blocksize)); + if (zero_stale_data) { + size_t linklen, zlen; + + linklen = strlen(link); + zlen = mp->m_sb.sb_blocksize - linklen; + if (xfs_sb_version_hascrc(&mp->m_sb)) + zlen -= sizeof(struct xfs_dsymlink_hdr); + if (zlen < mp->m_sb.sb_blocksize) + memset(link + linklen, 0, zlen); + } } #define MAX_REMOTE_VALS 4095 @@ -1268,39 +1454,61 @@ add_remote_vals( } } +/* Handle remote and leaf attributes */ static void -obfuscate_attr_block( - char *block, - xfs_dfiloff_t offset) +process_attr_block( + char *block, + xfs_fileoff_t offset) { - xfs_attr_leafblock_t *leaf; - int i; - int nentries; - xfs_attr_leaf_entry_t *entry; - xfs_attr_leaf_name_local_t *local; - xfs_attr_leaf_name_remote_t *remote; + struct xfs_attr_leafblock *leaf; + struct xfs_attr3_icleaf_hdr hdr; + int i; + int nentries; + xfs_attr_leaf_entry_t *entry; + xfs_attr_leaf_name_local_t *local; + xfs_attr_leaf_name_remote_t *remote; + __uint32_t bs = mp->m_sb.sb_blocksize; + char *first_name; + leaf = (xfs_attr_leafblock_t *)block; - if (be16_to_cpu(leaf->hdr.info.magic) != XFS_ATTR_LEAF_MAGIC) { + /* Remote attributes - attr3 has XFS_ATTR3_RMT_MAGIC, attr has none */ + if ((be16_to_cpu(leaf->hdr.info.magic) != XFS_ATTR_LEAF_MAGIC) && + (be16_to_cpu(leaf->hdr.info.magic) != XFS_ATTR3_LEAF_MAGIC)) { for (i = 0; i < attr_data.remote_val_count; i++) { - /* XXX: need to handle CRC headers */ - if (attr_data.remote_vals[i] == offset) - memset(block, 0, XFS_LBSIZE(mp)); + if (obfuscate && attr_data.remote_vals[i] == offset) + /* Macros to handle both attr and attr3 */ + memset(block + + (bs - XFS_ATTR3_RMT_BUF_SPACE(mp, bs)), + 'v', XFS_ATTR3_RMT_BUF_SPACE(mp, bs)); } return; } - nentries = be16_to_cpu(leaf->hdr.count); + /* Ok, it's a leaf - get header; accounts for crc & non-crc */ + xfs_attr3_leaf_hdr_from_disk(&hdr, leaf); + + nentries = hdr.count; if (nentries * sizeof(xfs_attr_leaf_entry_t) + - sizeof(xfs_attr_leaf_hdr_t) > XFS_LBSIZE(mp)) { + xfs_attr3_leaf_hdr_size(leaf) > + XFS_ATTR3_RMT_BUF_SPACE(mp, bs)) { if (show_warnings) print_warning("invalid attr count in inode %llu", (long long)cur_ino); return; } - for (i = 0, entry = &leaf->entries[0]; i < nentries; i++, entry++) { + entry = xfs_attr3_leaf_entryp(leaf); + /* We will move this as we parse */ + first_name = NULL; + for (i = 0; i < nentries; i++, entry++) { + int nlen, vlen, zlen; + + /* Grows up; if this name is topmost, move first_name */ + if (!first_name || xfs_attr3_leaf_name(leaf, i) < first_name) + first_name = xfs_attr3_leaf_name(leaf, i); + if (be16_to_cpu(entry->nameidx) > XFS_LBSIZE(mp)) { if (show_warnings) print_warning( @@ -1317,10 +1525,20 @@ obfuscate_attr_block( (long long)cur_ino); break; } - generate_obfuscated_name(0, local->namelen, - &local->nameval[0]); - memset(&local->nameval[local->namelen], 0, - be16_to_cpu(local->valuelen)); + if (obfuscate) { + generate_obfuscated_name(0, local->namelen, + &local->nameval[0]); + memset(&local->nameval[local->namelen], 'v', + be16_to_cpu(local->valuelen)); + } + /* zero from end of nameval[] to next name start */ + nlen = local->namelen; + vlen = be16_to_cpu(local->valuelen); + zlen = xfs_attr_leaf_entsize_local(nlen, vlen) - + (sizeof(xfs_attr_leaf_name_local_t) - 1 + + nlen + vlen); + if (zero_stale_data) + memset(&local->nameval[nlen + vlen], 0, zlen); } else { remote = xfs_attr3_leaf_name_remote(leaf, i); if (remote->namelen == 0 || remote->valueblk == 0) { @@ -1330,14 +1548,33 @@ obfuscate_attr_block( (long long)cur_ino); break; } - generate_obfuscated_name(0, remote->namelen, - &remote->name[0]); - add_remote_vals(be32_to_cpu(remote->valueblk), - be32_to_cpu(remote->valuelen)); + if (obfuscate) { + generate_obfuscated_name(0, remote->namelen, + &remote->name[0]); + add_remote_vals(be32_to_cpu(remote->valueblk), + be32_to_cpu(remote->valuelen)); + } + /* zero from end of name[] to next name start */ + nlen = remote->namelen; + zlen = xfs_attr_leaf_entsize_remote(nlen) - + (sizeof(xfs_attr_leaf_name_remote_t) - 1 + + nlen); + if (zero_stale_data) + memset(&remote->name[nlen], 0, zlen); } } + + /* Zero from end of entries array to the first name/val */ + if (zero_stale_data) { + struct xfs_attr_leaf_entry *entries; + + entries = xfs_attr3_leaf_entryp(leaf); + memset(&entries[nentries], 0, + first_name - (char *)&entries[nentries]); + } } +/* Processes symlinks, attrs, directories ... */ static int process_single_fsb_objects( xfs_dfiloff_t o, @@ -1367,25 +1604,50 @@ process_single_fsb_objects( } - if (dont_obfuscate) + if (!obfuscate && !zero_stale_data) goto write; + /* Zero unused part of interior nodes */ + if (zero_stale_data) { + xfs_da_intnode_t *node = iocur_top->data; + int magic = be16_to_cpu(node->hdr.info.magic); + + if (magic == XFS_DA_NODE_MAGIC || + magic == XFS_DA3_NODE_MAGIC) { + struct xfs_da3_icnode_hdr hdr; + int used; + + xfs_da3_node_hdr_from_disk(&hdr, node); + used = xfs_da3_node_hdr_size(node); + + used += hdr.count + * sizeof(struct xfs_da_node_entry); + + if (used < mp->m_sb.sb_blocksize) { + memset((char *)node + used, 0, + mp->m_sb.sb_blocksize - used); + iocur_top->need_crc = 1; + } + } + } + + /* Handle leaf nodes */ dp = iocur_top->data; switch (btype) { case TYP_DIR2: if (o >= mp->m_dirleafblk) break; - obfuscate_dir_data_block(dp, o, + process_dir_data_block(dp, o, last == mp->m_dirblkfsbs); iocur_top->need_crc = 1; break; case TYP_SYMLINK: - obfuscate_symlink_block(dp); + process_symlink_block(dp); iocur_top->need_crc = 1; break; case TYP_ATTR: - obfuscate_attr_block(dp, o); + process_attr_block(dp, o); iocur_top->need_crc = 1; break; default: @@ -1459,13 +1721,14 @@ process_multi_fsb_objects( } - if (dont_obfuscate || o >= mp->m_dirleafblk) { + if ((!obfuscate && !zero_stale_data) || + o >= mp->m_dirleafblk) { ret = write_buf(iocur_top); goto out_pop; } - obfuscate_dir_data_block(iocur_top->data, o, - last == mp->m_dirblkfsbs); + process_dir_data_block(iocur_top->data, o, + last == mp->m_dirblkfsbs); iocur_top->need_crc = 1; ret = write_buf(iocur_top); out_pop: @@ -1700,19 +1963,26 @@ process_exinode( typnm_t itype) { int whichfork; + int used; xfs_extnum_t nex; whichfork = (itype == TYP_ATTR) ? XFS_ATTR_FORK : XFS_DATA_FORK; nex = XFS_DFORK_NEXTENTS(dip, whichfork); - if (nex < 0 || nex > XFS_DFORK_SIZE(dip, mp, whichfork) / - sizeof(xfs_bmbt_rec_t)) { + used = nex * sizeof(xfs_bmbt_rec_t); + if (nex < 0 || used > XFS_DFORK_SIZE(dip, mp, whichfork)) { if (show_warnings) print_warning("bad number of extents %d in inode %lld", nex, (long long)cur_ino); return 1; } + /* Zero unused data fork past used extents */ + if (zero_stale_data && (used < XFS_DFORK_SIZE(dip, mp, whichfork))) + memset(XFS_DFORK_PTR(dip, whichfork) + used, 0, + XFS_DFORK_SIZE(dip, mp, whichfork) - used); + + return process_bmbt_reclist((xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip, whichfork), nex, itype); } @@ -1724,14 +1994,14 @@ process_inode_data( { switch (dip->di_format) { case XFS_DINODE_FMT_LOCAL: - if (!dont_obfuscate) + if (obfuscate || zero_stale_data) switch (itype) { case TYP_DIR2: - obfuscate_sf_dir(dip); + process_sf_dir(dip); break; case TYP_SYMLINK: - obfuscate_sf_symlink(dip); + process_sf_symlink(dip); break; default: ; @@ -1758,7 +2028,8 @@ static int process_inode( xfs_agnumber_t agno, xfs_agino_t agino, - xfs_dinode_t *dip) + xfs_dinode_t *dip, + bool free_inode) { int success; bool crc_was_ok = false; /* no recalc by default */ @@ -1767,13 +2038,22 @@ process_inode( success = 1; cur_ino = XFS_AGINO_TO_INO(mp, agno, agino); - /* we only care about crc recalculation if we are obfuscating names. */ - if (!dont_obfuscate) { + /* we only care about crc recalculation if we will modify the inode. */ + if (obfuscate || zero_stale_data) { crc_was_ok = xfs_verify_cksum((char *)dip, mp->m_sb.sb_inodesize, offsetof(struct xfs_dinode, di_crc)); } + if (free_inode) { + if (zero_stale_data) { + /* Zero all of the inode literal area */ + memset(XFS_DFORK_DPTR(dip), 0, + XFS_LITINO(mp, dip->di_version)); + } + goto done; + } + /* copy appropriate data fork metadata */ switch (be16_to_cpu(dip->di_mode) & S_IFMT) { case S_IFDIR: @@ -1800,8 +2080,8 @@ process_inode( switch (dip->di_aformat) { case XFS_DINODE_FMT_LOCAL: need_new_crc = 1; - if (!dont_obfuscate) - obfuscate_sf_attr(dip); + if (obfuscate || zero_stale_data) + process_sf_attr(dip); break; case XFS_DINODE_FMT_EXTENTS: @@ -1815,6 +2095,11 @@ process_inode( nametable_clear(); } +done: + /* Heavy handed but low cost; just do it as a catch-all. */ + if (zero_stale_data) + need_new_crc = 1; + if (crc_was_ok && need_new_crc) xfs_dinode_calc_crc(mp, dip); return success; @@ -1880,13 +2165,12 @@ copy_inode_chunk( for (i = 0; i < XFS_INODES_PER_CHUNK; i++) { xfs_dinode_t *dip; - if (XFS_INOBT_IS_FREE_DISK(rp, i)) - continue; - dip = (xfs_dinode_t *)((char *)iocur_top->data + ((off + i) << mp->m_sb.sb_inodelog)); - if (!process_inode(agno, agino + i, dip)) + /* process_inode handles free inodes, too */ + if (!process_inode(agno, agino + i, dip, + XFS_INOBT_IS_FREE_DISK(rp, i))) goto pop_out; } skip_processing: @@ -2031,6 +2315,13 @@ scan_ag( if (stop_on_read_error) goto pop_out; } else { + /* Replace any filesystem label with "L's" */ + if (obfuscate) { + struct xfs_sb *sb = iocur_top->data; + memset(sb->sb_fname, 'L', + min(strlen(sb->sb_fname), sizeof(sb->sb_fname))); + iocur_top->need_crc = 1; + } if (write_buf(iocur_top)) goto pop_out; } @@ -2075,6 +2366,23 @@ scan_ag( if (stop_on_read_error) goto pop_out; } else { + if (agf && zero_stale_data) { + /* Zero out unused bits of agfl */ + int i; + __be32 *agfl_bno; + + agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, iocur_top->bp); + i = be32_to_cpu(agf->agf_fllast); + + for (;;) { + if (++i == XFS_AGFL_SIZE(mp)) + i = 0; + if (i == be32_to_cpu(agf->agf_flfirst)) + break; + agfl_bno[i] = cpu_to_be32(NULLAGBLOCK); + } + iocur_top->need_crc = 1; + } if (write_buf(iocur_top)) goto pop_out; } @@ -2169,6 +2477,8 @@ copy_sb_inodes(void) static int copy_log(void) { + int dirty; + if (show_progress) print_progress("Copying log"); @@ -2180,6 +2490,34 @@ copy_log(void) print_warning("cannot read log"); return !stop_on_read_error; } + + /* If not obfuscating or zeroing, just copy the log as it is */ + if (!obfuscate && !zero_stale_data) + goto done; + + dirty = xlog_is_dirty(mp, &x, 0); + + switch (dirty) { + case 0: + /* clear out a clean log */ + if (show_progress) + print_progress("Zeroing clean log"); + memset(iocur_top->data, 0, + mp->m_sb.sb_logblocks * mp->m_sb.sb_blocksize); + break; + case 1: + /* keep the dirty log */ + print_warning( +_("Filesystem log is dirty; image will contain unobfuscated metadata in log.")); + break; + case -1: + /* log detection error */ + print_warning( +_("Could not discern log; image will contain unobfuscated metadata in log.")); + break; + } + +done: return !write_buf(iocur_top); } @@ -2204,8 +2542,11 @@ metadump_f( return 0; } - while ((c = getopt(argc, argv, "egm:ow")) != EOF) { + while ((c = getopt(argc, argv, "aegm:ow")) != EOF) { switch (c) { + case 'a': + zero_stale_data = 0; + break; case 'e': stop_on_read_error = 1; break; @@ -2221,7 +2562,7 @@ metadump_f( } break; case 'o': - dont_obfuscate = 1; + obfuscate = 0; break; case 'w': show_warnings = 1; diff --git a/db/sb.c b/db/sb.c index 974abd2..f0c2145 100644 --- a/db/sb.c +++ b/db/sb.c @@ -225,8 +225,7 @@ int xlog_recover_do_trans(struct xlog *log, xlog_recover_t *t, int p) int sb_logcheck(void) { - struct xlog log; - xfs_daddr_t head_blk, tail_blk; + int dirty; if (mp->m_sb.sb_logstart) { if (x.logdev && x.logdev != x.ddev) { @@ -242,26 +241,13 @@ sb_logcheck(void) } } - memset(&log, 0, sizeof(log)); libxfs_buftarg_init(mp, x.ddev, x.logdev, x.rtdev); - x.logBBsize = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks); - x.logBBstart = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart); - x.lbsize = BBSIZE; - if (xfs_sb_version_hassector(&mp->m_sb)) - x.lbsize <<= (mp->m_sb.sb_logsectlog - BBSHIFT); - - log.l_dev = mp->m_logdev_targp; - log.l_logsize = BBTOB(log.l_logBBsize); - log.l_logBBsize = x.logBBsize; - log.l_logBBstart = x.logBBstart; - log.l_sectBBsize = BTOBB(x.lbsize); - log.l_mp = mp; - - if (xlog_find_tail(&log, &head_blk, &tail_blk)) { + + dirty = xlog_is_dirty(mp, &x, 0); + if (dirty == -1) { dbprintf(_("ERROR: cannot find log head/tail, run xfs_repair\n")); return 0; - } - if (head_blk != tail_blk) { + } else if (dirty == 1) { dbprintf(_( "ERROR: The filesystem has valuable metadata changes in a log which needs to\n" "be replayed. Mount the filesystem to replay the log, and unmount it before\n" @@ -271,6 +257,7 @@ sb_logcheck(void) "of the filesystem before doing this.\n"), progname); return 0; } + /* Log is clean */ return 1; } diff --git a/db/type.c b/db/type.c index b29f2a4..0aa3137 100644 --- a/db/type.c +++ b/db/type.c @@ -70,6 +70,7 @@ static const typ_t __typtab[] = { { TYP_SB, "sb", handle_struct, sb_hfld, NULL }, { TYP_SYMLINK, "symlink", handle_string, NULL, NULL }, { TYP_TEXT, "text", handle_text, NULL, NULL }, + { TYP_FINOBT, "finobt", handle_struct, inobt_hfld, NULL }, { TYP_NONE, NULL } }; @@ -104,6 +105,8 @@ static const typ_t __typtab_crc[] = { { TYP_SYMLINK, "symlink", handle_struct, symlink_crc_hfld, &xfs_symlink_buf_ops }, { TYP_TEXT, "text", handle_text, NULL, NULL }, + { TYP_FINOBT, "finobt", handle_struct, inobt_crc_hfld, + &xfs_inobt_buf_ops }, { TYP_NONE, NULL } }; diff --git a/db/type.h b/db/type.h index 3bb26f1..e8d8df7 100644 --- a/db/type.h +++ b/db/type.h @@ -27,7 +27,7 @@ typedef enum typnm TYP_BMAPBTD, TYP_BNOBT, TYP_CNTBT, TYP_DATA, TYP_DIR2, TYP_DQBLK, TYP_INOBT, TYP_INODATA, TYP_INODE, TYP_LOG, TYP_RTBITMAP, TYP_RTSUMMARY, TYP_SB, TYP_SYMLINK, - TYP_TEXT, TYP_NONE + TYP_TEXT, TYP_FINOBT, TYP_NONE } typnm_t; #define DB_WRITE 1 diff --git a/db/xfs_metadump.sh b/db/xfs_metadump.sh index a95d5a5..836d3ae 100755 --- a/db/xfs_metadump.sh +++ b/db/xfs_metadump.sh @@ -5,11 +5,12 @@ OPTS=" " DBOPTS=" " -USAGE="Usage: xfs_metadump [-efFogwV] [-m max_extents] [-l logdev] source target" +USAGE="Usage: xfs_metadump [-aefFogwV] [-m max_extents] [-l logdev] source target" -while getopts "efgl:m:owFV" c +while getopts "aefgl:m:owFV" c do case $c in + a) OPTS=$OPTS"-a ";; e) OPTS=$OPTS"-e ";; g) OPTS=$OPTS"-g ";; m) OPTS=$OPTS"-m "$OPTARG" ";; diff --git a/include/libxlog.h b/include/libxlog.h index a61e437..d2a33c1 100644 --- a/include/libxlog.h +++ b/include/libxlog.h @@ -78,6 +78,8 @@ extern int print_record_header; /* libxfs parameters */ extern libxfs_init_t x; + +extern int xlog_is_dirty(xfs_mount_t *mp, libxfs_init_t *x, int verbose); extern struct xfs_buf *xlog_get_bp(struct xlog *, int); extern void xlog_put_bp(struct xfs_buf *); extern int xlog_bread(struct xlog *log, xfs_daddr_t blk_no, int nbblks, diff --git a/libxlog/util.c b/libxlog/util.c index 949b79d..edb23a8 100644 --- a/libxlog/util.c +++ b/libxlog/util.c @@ -23,6 +23,62 @@ int print_skip_uuid; int print_record_header; libxfs_init_t x; +/* + * Return 1 for dirty, 0 for clean, -1 for errors + */ +int +xlog_is_dirty( + xfs_mount_t *mp, + libxfs_init_t *x, + int verbose) +{ + int error; + struct xlog log; + xfs_daddr_t head_blk, tail_blk; + + memset(&log, 0, sizeof(log)); + + /* We (re-)init members of libxfs_init_t here? really? */ + x->logBBsize = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks); + x->logBBstart = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart); + x->lbsize = BBSIZE; + if (xfs_sb_version_hassector(&mp->m_sb)) + x->lbsize <<= (mp->m_sb.sb_logsectlog - BBSHIFT); + + log.l_dev = mp->m_logdev_targp; + log.l_logBBsize = x->logBBsize; + log.l_logBBstart = x->logBBstart; + log.l_sectBBsize = BTOBB(x->lbsize); + log.l_mp = mp; + if (xfs_sb_version_hassector(&mp->m_sb)) { + log.l_sectbb_log = mp->m_sb.sb_logsectlog - BBSHIFT; + ASSERT(log.l_sectbb_log <= mp->m_sectbb_log); + /* for larger sector sizes, must have v2 or external log */ + ASSERT(log.l_sectbb_log == 0 || + log.l_logBBstart == 0 || + xfs_sb_version_haslogv2(&mp->m_sb)); + ASSERT(mp->m_sb.sb_logsectlog >= BBSHIFT); + } + log.l_sectbb_mask = (1 << log.l_sectbb_log) - 1; + + if ((error = xlog_find_tail(&log, &head_blk, &tail_blk))) { + xlog_warn(_("%s: cannot find log head/tail " + "(xlog_find_tail=%d)\n"), + __func__, error); + return -1; + } + + if (verbose) + xlog_warn( + _("%s: head block %" PRId64 " tail block %" PRId64 "\n"), + __func__, head_blk, tail_blk); + + if (head_blk != tail_blk) + return 1; + + return 0; +} + static int header_check_uuid(xfs_mount_t *mp, xlog_rec_header_t *head) { diff --git a/man/man8/xfs_metadump.8 b/man/man8/xfs_metadump.8 index 077fff5..81a17a9 100644 --- a/man/man8/xfs_metadump.8 +++ b/man/man8/xfs_metadump.8 @@ -4,7 +4,7 @@ xfs_metadump \- copy XFS filesystem metadata to a file .SH SYNOPSIS .B xfs_metadump [ -.B \-efFgow +.B \-aefFgow ] [ .B \-m .I max_extents @@ -74,6 +74,14 @@ tool. .PP .SH OPTIONS .TP +.B \-a +Copies entire metadata blocks. Normally, +.B xfs_metadump +will zero any stale +bytes interspersed with in-use metadata. Use this option to copy full metadata +blocks, to provide more debugging information for a corrupted filesystem. Note +that the extra data will be unobfuscated. +.TP .B \-e Stops the dump on a read error. Normally, it will ignore read errors and copy all the metadata that is accessible.