xfsprogs/xfsprogs-xfs_metadump-CVE-2...

1017 lines
27 KiB
Diff

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 <libxfs.h>
+#include <libxlog.h>
#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.