diff --git a/xfsprogs-xfs_metadump-CVE-2012-2150.patch b/xfsprogs-xfs_metadump-CVE-2012-2150.patch new file mode 100644 index 0000000..ff59497 --- /dev/null +++ b/xfsprogs-xfs_metadump-CVE-2012-2150.patch @@ -0,0 +1,1016 @@ +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. diff --git a/xfsprogs.spec b/xfsprogs.spec index 2b4f03d..b100368 100644 --- a/xfsprogs.spec +++ b/xfsprogs.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing the XFS filesystem Name: xfsprogs Version: 3.2.2 -Release: 1%{?dist} +Release: 2%{?dist} # Licensing based on generic "GNU GENERAL PUBLIC LICENSE" # in source, with no mention of version. # doc/COPYING file specifies what is GPL and what is LGPL @@ -18,6 +18,8 @@ Provides: xfs-cmds Obsoletes: xfs-cmds <= %{version} Conflicts: xfsdump < 3.0.1 +Patch0: xfsprogs-xfs_metadump-CVE-2012-2150.patch + %description A set of commands to use the XFS filesystem, including mkfs.xfs. @@ -60,6 +62,7 @@ in building or running the xfstests QA suite. %prep %setup -q +%patch0 -p1 %build export tagname=CC @@ -175,6 +178,9 @@ rm -rf $RPM_BUILD_ROOT %{_includedir}/xfs/xfs_trans_space.h %changelog +* Thu Jul 30 2015 Eric Sandeen 3.2.2-2 +- Fix CVE-2012-2150 + * Thu Dec 04 2014 Eric Sandeen 3.2.2-1 - New upstream release