1017 lines
27 KiB
Diff
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.
|