Blame SOURCES/xfsprogs-xfs_metadump-CVE-2012-2150.patch

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