* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <libxfs.h>
+#include "libxfs.h"
+#include "libxlog.h"
#include "bmap.h"
#include "command.h"
#include "metadump.h"
#include "init.h"
#include "sig.h"
#include "xfs_metadump.h"
+#include "fprint.h"
+#include "faddr.h"
+#include "field.h"
+#include "dir2.h"
#define DEFAULT_MAX_EXT_SIZE 1000
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 */
static __be64 *block_index;
static char *block_buffer;
-static int num_indicies;
+static int num_indices;
static int cur_index;
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;
" 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"
* A complete dump file will have a "zero" entry in the last index block,
* even if the dump is exactly aligned, the last index will be full of
* zeros. If the last index entry is non-zero, the dump is incomplete.
- * Correspondingly, the last chunk will have a count < num_indicies.
+ * Correspondingly, the last chunk will have a count < num_indices.
+ *
+ * Return 0 for success, -1 for failure.
*/
static int
metablock->mb_count = cpu_to_be16(cur_index);
if (fwrite(metablock, (cur_index + 1) << BBSHIFT, 1, outf) != 1) {
print_warning("error writing to file: %s", strerror(errno));
- return 0;
+ return -errno;
}
- memset(block_index, 0, num_indicies * sizeof(__be64));
+ memset(block_index, 0, num_indices * sizeof(__be64));
cur_index = 0;
- return 1;
+ return 0;
}
+/*
+ * Return 0 for success, -errno for failure.
+ */
static int
-write_buf(
- iocur_t *buf)
+write_buf_segment(
+ char *data,
+ __int64_t off,
+ int len)
{
- char *data;
- __int64_t off;
int i;
+ int ret;
- for (i = 0, off = buf->bb, data = buf->data;
- i < buf->blen;
- i++, off++, data += BBSIZE) {
+ for (i = 0; i < len; i++, off++, data += BBSIZE) {
block_index[cur_index] = cpu_to_be64(off);
memcpy(&block_buffer[cur_index << BBSHIFT], data, BBSIZE);
- if (++cur_index == num_indicies) {
- if (!write_index())
- return 0;
+ if (++cur_index == num_indices) {
+ ret = write_index();
+ if (ret)
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+/*
+ * we want to preserve the state of the metadata in the dump - whether it is
+ * intact or corrupt, so even if the buffer has a verifier attached to it we
+ * don't want to run it prior to writing the buffer to the metadump image.
+ *
+ * The only reason for running the verifier is to recalculate the CRCs on a
+ * buffer that has been obfuscated. i.e. a buffer than metadump modified itself.
+ * In this case, we only run the verifier if the buffer was not corrupt to begin
+ * with so that we don't accidentally correct buffers with CRC or errors in them
+ * when we are obfuscating them.
+ */
+static int
+write_buf(
+ iocur_t *buf)
+{
+ struct xfs_buf *bp = buf->bp;
+ int i;
+ int ret;
+
+ /*
+ * Run the write verifier to recalculate the buffer CRCs and check
+ * metadump didn't introduce a new corruption. Warn if the verifier
+ * failed, but still continue to dump it into the output file.
+ */
+ if (buf->need_crc && bp && bp->b_ops && !bp->b_error) {
+ bp->b_ops->verify_write(bp);
+ if (bp->b_error) {
+ print_warning(
+ "obfuscation corrupted block at %s bno 0x%llx/0x%x",
+ bp->b_ops->name,
+ (long long)bp->b_bn, bp->b_bcount);
+ }
+ }
+
+ /* handle discontiguous buffers */
+ if (!buf->bbmap) {
+ ret = write_buf_segment(buf->data, buf->bb, buf->blen);
+ if (ret)
+ return ret;
+ } else {
+ int len = 0;
+ for (i = 0; i < buf->bbmap->nmaps; i++) {
+ ret = write_buf_segment(buf->data + BBTOB(len),
+ buf->bbmap->b[i].bm_bn,
+ buf->bbmap->b[i].bm_len);
+ if (ret)
+ return ret;
+ len += buf->bbmap->b[i].bm_len;
}
}
- return !seenint();
+ return seenint() ? -EINTR : 0;
}
+/*
+ * We could be processing a corrupt block, so we can't trust any of
+ * the offsets or lengths to be within the buffer range. Hence check
+ * carefully!
+ */
+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;
+ char *zp1, *zp2;
+ char *key_end;
+
+ nrecs = be16_to_cpu(block->bb_numrecs);
+ if (nrecs < 0)
+ return;
+
+ switch (btype) {
+ case TYP_BMAPBTA:
+ case TYP_BMAPBTD:
+ if (nrecs > mp->m_bmap_dmxr[1])
+ return;
+
+ bkp = XFS_BMBT_KEY_ADDR(mp, block, 1);
+ bpp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[1]);
+ zp1 = (char *)&bkp[nrecs];
+ zp2 = (char *)&bpp[nrecs];
+ key_end = (char *)bpp;
+ break;
+ case TYP_INOBT:
+ case TYP_FINOBT:
+ if (nrecs > mp->m_inobt_mxr[1])
+ return;
+
+ ikp = XFS_INOBT_KEY_ADDR(mp, block, 1);
+ ipp = XFS_INOBT_PTR_ADDR(mp, block, 1, mp->m_inobt_mxr[1]);
+ zp1 = (char *)&ikp[nrecs];
+ zp2 = (char *)&ipp[nrecs];
+ key_end = (char *)ipp;
+ break;
+ case TYP_BNOBT:
+ case TYP_CNTBT:
+ if (nrecs > mp->m_alloc_mxr[1])
+ return;
+
+ akp = XFS_ALLOC_KEY_ADDR(mp, block, 1);
+ app = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]);
+ zp1 = (char *)&akp[nrecs];
+ zp2 = (char *)&app[nrecs];
+ key_end = (char *)app;
+ break;
+ default:
+ return;
+ }
+
+
+ /* Zero from end of keys to beginning of pointers */
+ memset(zp1, 0, key_end - zp1);
+
+ /* Zero from end of pointers to end of block */
+ memset(zp2, 0, (char *)block + mp->m_sb.sb_blocksize - zp2);
+}
+
+/*
+ * We could be processing a corrupt block, so we can't trust any of
+ * the offsets or lengths to be within the buffer range. Hence check
+ * carefully!
+ */
+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;
+ char *zp;
+
+ nrecs = be16_to_cpu(block->bb_numrecs);
+ if (nrecs < 0)
+ return;
+
+ switch (btype) {
+ case TYP_BMAPBTA:
+ case TYP_BMAPBTD:
+ if (nrecs > mp->m_bmap_dmxr[0])
+ return;
+
+ brp = XFS_BMBT_REC_ADDR(mp, block, 1);
+ zp = (char *)&brp[nrecs];
+ break;
+ case TYP_INOBT:
+ case TYP_FINOBT:
+ if (nrecs > mp->m_inobt_mxr[0])
+ return;
+
+ irp = XFS_INOBT_REC_ADDR(mp, block, 1);
+ zp = (char *)&irp[nrecs];
+ break;
+ case TYP_BNOBT:
+ case TYP_CNTBT:
+ if (nrecs > mp->m_alloc_mxr[0])
+ return;
+
+ arp = XFS_ALLOC_REC_ADDR(mp, block, 1);
+ zp = (char *)&arp[nrecs];
+ break;
+ default:
+ return;
+ }
+
+ /* Zero from end of records to end of block */
+ memset(zp, 0, (char *)block + mp->m_sb.sb_blocksize - zp);
+}
+
+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(
rval = !stop_on_read_error;
goto pop_out;
}
- if (!write_buf(iocur_top))
+
+ if (zero_stale_data) {
+ zero_btree_block(iocur_top->data, btype);
+ iocur_top->need_crc = 1;
+ }
+
+ if (write_buf(iocur_top))
goto pop_out;
if (!(*func)(iocur_top->data, agno, agbno, level - 1, btype, arg))
return 1;
if (agno == (mp->m_sb.sb_agcount - 1) && agbno > 0 &&
agbno <= (mp->m_sb.sb_dblocks -
- (xfs_drfsbno_t)(mp->m_sb.sb_agcount - 1) *
+ (xfs_rfsblock_t)(mp->m_sb.sb_agcount - 1) *
mp->m_sb.sb_agblocks))
return 1;
return scan_btree(agno, root, levels, TYP_CNTBT, agf, scanfunc_freesp);
}
+static int
+scanfunc_rmapbt(
+ struct xfs_btree_block *block,
+ xfs_agnumber_t agno,
+ xfs_agblock_t agbno,
+ int level,
+ typnm_t btype,
+ void *arg)
+{
+ xfs_rmap_ptr_t *pp;
+ int i;
+ int numrecs;
+
+ if (level == 0)
+ return 1;
+
+ numrecs = be16_to_cpu(block->bb_numrecs);
+ if (numrecs > mp->m_rmap_mxr[1]) {
+ if (show_warnings)
+ print_warning("invalid numrecs (%u) in %s block %u/%u",
+ numrecs, typtab[btype].name, agno, agbno);
+ return 1;
+ }
+
+ pp = XFS_RMAP_PTR_ADDR(block, 1, mp->m_rmap_mxr[1]);
+ for (i = 0; i < numrecs; i++) {
+ if (!valid_bno(agno, be32_to_cpu(pp[i]))) {
+ if (show_warnings)
+ print_warning("invalid block number (%u/%u) "
+ "in %s block %u/%u",
+ agno, be32_to_cpu(pp[i]),
+ typtab[btype].name, agno, agbno);
+ continue;
+ }
+ if (!scan_btree(agno, be32_to_cpu(pp[i]), level, btype, arg,
+ scanfunc_rmapbt))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+copy_rmap_btree(
+ xfs_agnumber_t agno,
+ struct xfs_agf *agf)
+{
+ xfs_agblock_t root;
+ int levels;
+
+ if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
+ return 1;
+
+ root = be32_to_cpu(agf->agf_roots[XFS_BTNUM_RMAP]);
+ levels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]);
+
+ /* validate root and levels before processing the tree */
+ if (root == 0 || root > mp->m_sb.sb_agblocks) {
+ if (show_warnings)
+ print_warning("invalid block number (%u) in rmapbt "
+ "root in agf %u", root, agno);
+ return 1;
+ }
+ if (levels >= XFS_BTREE_MAXLEVELS) {
+ if (show_warnings)
+ print_warning("invalid level (%u) in rmapbt root "
+ "in agf %u", levels, agno);
+ return 1;
+ }
+
+ return scan_btree(agno, root, levels, TYP_RMAPBT, agf, scanfunc_rmapbt);
+}
+
/* filename and extended attribute obfuscation routines */
struct name_ent {
struct name_ent *next;
xfs_dahash_t hash;
int namelen;
- uchar_t name[1];
+ unsigned char name[1];
};
#define NAME_TABLE_SIZE 4096
* return a pointer to its entry, otherwise return a null pointer.
*/
static struct name_ent *
-nametable_find(xfs_dahash_t hash, int namelen, uchar_t *name)
+nametable_find(xfs_dahash_t hash, int namelen, unsigned char *name)
{
struct name_ent *ent;
* name's new entry, or a null pointer if an error occurs.
*/
static struct name_ent *
-nametable_add(xfs_dahash_t hash, int namelen, uchar_t *name)
+nametable_add(xfs_dahash_t hash, int namelen, unsigned char *name)
{
struct name_ent *ent;
#define is_invalid_char(c) ((c) == '/' || (c) == '\0')
#define rol32(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
-static inline uchar_t
+static inline unsigned char
random_filename_char(void)
{
- static uchar_t filename_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ static unsigned char filename_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-_";
struct xfs_mount *mp,
xfs_ino_t dir_ino,
size_t name_len,
- uchar_t *name)
+ unsigned char *name)
{
return dir_ino == mp->m_sb.sb_rootino &&
name_len == ORPHANAGE_LEN &&
in_lost_found(
xfs_ino_t ino,
int namelen,
- uchar_t *name)
+ unsigned char *name)
{
static xfs_ino_t orphanage_ino = 0;
char s[24]; /* 21 is enough (64 bits in decimal) */
obfuscate_name(
xfs_dahash_t hash,
size_t name_len,
- uchar_t *name)
+ unsigned char *name)
{
- uchar_t *newp = name;
+ unsigned char *newp = name;
int i;
xfs_dahash_t new_hash = 0;
- uchar_t *first;
- uchar_t high_bit;
+ unsigned char *first;
+ unsigned char high_bit;
int shift;
/*
static int
flip_bit(
size_t name_len,
- uchar_t *name,
+ unsigned char *name,
uint32_t bitseq)
{
int index;
size_t offset;
- uchar_t *p0, *p1;
- uchar_t m0, m1;
+ unsigned char *p0, *p1;
+ unsigned char m0, m1;
struct {
int byte; /* Offset from start within name */
- uchar_t bit; /* Bit within that byte */
+ unsigned char bit; /* Bit within that byte */
} bit_to_flip[][2] = { /* Sorted by second entry's byte */
{ { 0, 0 }, { 1, 7 } }, /* Each row defines a pair */
{ { 1, 0 }, { 2, 7 } }, /* of bytes and a bit within */
static int
find_alternate(
size_t name_len,
- uchar_t *name,
+ unsigned char *name,
uint32_t seq)
{
uint32_t bitseq = 0;
* are already in the table.
*/
static int
-handle_duplicate_name(xfs_dahash_t hash, size_t name_len, uchar_t *name)
+handle_duplicate_name(xfs_dahash_t hash, size_t name_len, unsigned char *name)
{
- uchar_t new_name[name_len + 1];
+ unsigned char new_name[name_len + 1];
uint32_t seq = 1;
if (!nametable_find(hash, name_len, name))
generate_obfuscated_name(
xfs_ino_t ino,
int namelen,
- uchar_t *name)
+ unsigned char *name)
{
xfs_dahash_t hash;
}
static void
-obfuscate_sf_dir(
+process_sf_dir(
xfs_dinode_t *dip)
{
- xfs_dir2_sf_t *sfp;
+ struct xfs_dir2_sf_hdr *sfp;
xfs_dir2_sf_entry_t *sfep;
__uint64_t ino_dir_size;
int i;
- sfp = (xfs_dir2_sf_t *)XFS_DFORK_DPTR(dip);
+ sfp = (struct xfs_dir2_sf_hdr *)XFS_DFORK_DPTR(dip);
ino_dir_size = be64_to_cpu(dip->di_size);
if (ino_dir_size > XFS_DFORK_DSIZE(dip, mp)) {
ino_dir_size = XFS_DFORK_DSIZE(dip, mp);
}
sfep = xfs_dir2_sf_firstentry(sfp);
- for (i = 0; (i < sfp->hdr.count) &&
+ for (i = 0; (i < sfp->count) &&
((char *)sfep - (char *)sfp < ino_dir_size); i++) {
/*
if (show_warnings)
print_warning("zero length entry in dir inode "
"%llu", (long long)cur_ino);
- if (i != sfp->hdr.count - 1)
+ if (i != sfp->count - 1)
break;
namelen = ino_dir_size - ((char *)&sfep->name[0] -
(char *)sfp);
} else if ((char *)sfep - (char *)sfp +
- xfs_dir2_sf_entsize_byentry(sfp, sfep) >
+ M_DIROPS(mp)->sf_entsize(sfp, sfep->namelen) >
ino_dir_size) {
if (show_warnings)
print_warning("entry length in dir inode %llu "
"overflows space", (long long)cur_ino);
- if (i != sfp->hdr.count - 1)
+ if (i != sfp->count - 1)
break;
namelen = ino_dir_size - ((char *)&sfep->name[0] -
(char *)sfp);
}
- generate_obfuscated_name(xfs_dir2_sf_get_inumber(sfp,
- xfs_dir2_sf_inumberp(sfep)), namelen,
- &sfep->name[0]);
+ if (obfuscate)
+ generate_obfuscated_name(
+ M_DIROPS(mp)->sf_get_ino(sfp, sfep),
+ namelen, &sfep->name[0]);
sfep = (xfs_dir2_sf_entry_t *)((char *)sfep +
- xfs_dir2_sf_entsize_byname(sfp, namelen));
+ M_DIROPS(mp)->sf_entsize(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);
}
+/*
+ * The pathname may not be null terminated. It may be terminated by the end of
+ * a buffer or inode literal area, and the start of the next region contains
+ * unknown data. Therefore, when we get to the last component of the symlink, we
+ * cannot assume that strlen() will give us the right result. Hence we need to
+ * track the remaining pathname length and use that instead.
+ */
static void
-obfuscate_sf_symlink(
+obfuscate_path_components(
+ char *buf,
+ __uint64_t len)
+{
+ unsigned char *comp = (unsigned char *)buf;
+ unsigned char *end = comp + len;
+ xfs_dahash_t hash;
+
+ while (comp < end) {
+ char *slash;
+ int namelen;
+
+ /* find slash at end of this component */
+ slash = strchr((char *)comp, '/');
+ if (!slash) {
+ /* last (or single) component */
+ namelen = strnlen((char *)comp, len);
+ hash = libxfs_da_hashname(comp, namelen);
+ obfuscate_name(hash, namelen, comp);
+ break;
+ }
+ namelen = slash - (char *)comp;
+ /* handle leading or consecutive slashes */
+ if (!namelen) {
+ comp++;
+ len--;
+ continue;
+ }
+ hash = libxfs_da_hashname(comp, namelen);
+ obfuscate_name(hash, namelen, comp);
+ comp += namelen + 1;
+ len -= namelen + 1;
+ }
+}
+
+static void
+process_sf_symlink(
xfs_dinode_t *dip)
{
__uint64_t len;
}
buf = (char *)XFS_DFORK_DPTR(dip);
- while (len > 0)
- buf[--len] = random() % 127 + 1;
+ 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;
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));
}
-}
-
-/*
- * dir_data structure is used to track multi-fsblock dir2 blocks between extent
- * processing calls.
- */
-static struct dir_data_s {
- int end_of_data;
- int block_index;
- int offset_to_entry;
- int bad_block;
-} dir_data;
+ /* 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_blocks(
- char *block,
- xfs_dfiloff_t offset,
- xfs_dfilblks_t count,
- int is_block_format)
+process_dir_data_block(
+ char *block,
+ xfs_fileoff_t offset,
+ int is_block_format)
{
/*
* we have to rely on the fileoffset and signature of the block to
* for multi-fsblock dir blocks, if a name crosses an extent boundary,
* ignore it and continue.
*/
- int c;
- int dir_offset;
- char *ptr;
- char *endptr;
-
- if (is_block_format && count != mp->m_dirblkfsbs)
- return; /* too complex to handle this rare case */
-
- for (c = 0, endptr = block; c < count; c++) {
-
- if (dir_data.block_index == 0) {
- int wantmagic;
-
- if (offset % mp->m_dirblkfsbs != 0)
- return; /* corrupted, leave it alone */
+ int dir_offset;
+ char *ptr;
+ char *endptr;
+ int end_of_data;
+ int wantmagic;
+ struct xfs_dir2_data_hdr *datahdr;
+
+ datahdr = (struct xfs_dir2_data_hdr *)block;
+
+ if (is_block_format) {
+ xfs_dir2_leaf_entry_t *blp;
+ xfs_dir2_block_tail_t *btp;
+
+ btp = xfs_dir2_block_tail_p(mp->m_dir_geo, datahdr);
+ blp = xfs_dir2_block_leaf_p(btp);
+ if ((char *)blp > (char *)btp)
+ blp = (xfs_dir2_leaf_entry_t *)btp;
+
+ end_of_data = (char *)blp - block;
+ if (xfs_sb_version_hascrc(&mp->m_sb))
+ wantmagic = XFS_DIR3_BLOCK_MAGIC;
+ else
+ wantmagic = XFS_DIR2_BLOCK_MAGIC;
+ } else { /* leaf/node format */
+ end_of_data = mp->m_dir_geo->fsbcount << mp->m_sb.sb_blocklog;
+ if (xfs_sb_version_hascrc(&mp->m_sb))
+ wantmagic = XFS_DIR3_DATA_MAGIC;
+ else
+ wantmagic = XFS_DIR2_DATA_MAGIC;
+ }
- dir_data.bad_block = 0;
+ if (be32_to_cpu(datahdr->magic) != wantmagic) {
+ if (show_warnings)
+ print_warning(
+ "invalid magic in dir inode %llu block %ld",
+ (long long)cur_ino, (long)offset);
+ return;
+ }
- if (is_block_format) {
- xfs_dir2_leaf_entry_t *blp;
- xfs_dir2_block_tail_t *btp;
+ dir_offset = M_DIROPS(mp)->data_entry_offset;
+ ptr = block + dir_offset;
+ endptr = block + mp->m_dir_geo->blksize;
- btp = xfs_dir2_block_tail_p(mp,
- (xfs_dir2_block_t *)block);
- blp = xfs_dir2_block_leaf_p(btp);
- if ((char *)blp > (char *)btp)
- blp = (xfs_dir2_leaf_entry_t *)btp;
+ while (ptr < endptr && dir_offset < end_of_data) {
+ xfs_dir2_data_entry_t *dep;
+ xfs_dir2_data_unused_t *dup;
+ int length;
- dir_data.end_of_data = (char *)blp - block;
- wantmagic = XFS_DIR2_BLOCK_MAGIC;
- } else { /* leaf/node format */
- dir_data.end_of_data = mp->m_dirblkfsbs <<
- mp->m_sb.sb_blocklog;
- wantmagic = XFS_DIR2_DATA_MAGIC;
- }
- dir_data.offset_to_entry = offsetof(xfs_dir2_data_t, u);
+ dup = (xfs_dir2_data_unused_t *)ptr;
- if (be32_to_cpu(((xfs_dir2_data_hdr_t*)block)->magic) !=
- wantmagic) {
+ if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
+ int length = be16_to_cpu(dup->length);
+ if (dir_offset + length > end_of_data ||
+ !length || (length & (XFS_DIR2_DATA_ALIGN - 1))) {
if (show_warnings)
- print_warning("invalid magic in dir "
- "inode %llu block %ld",
- (long long)cur_ino,
- (long)offset);
- dir_data.bad_block = 1;
+ print_warning(
+ "invalid length for dir free space in inode %llu",
+ (long long)cur_ino);
+ return;
}
- }
- dir_data.block_index++;
- if (dir_data.block_index == mp->m_dirblkfsbs)
- dir_data.block_index = 0;
-
- if (dir_data.bad_block)
- continue;
+ if (be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)) !=
+ dir_offset)
+ 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);
- dir_offset = (dir_data.block_index << mp->m_sb.sb_blocklog) +
- dir_data.offset_to_entry;
-
- ptr = endptr + dir_data.offset_to_entry;
- endptr += mp->m_sb.sb_blocksize;
-
- while (ptr < endptr && dir_offset < dir_data.end_of_data) {
- xfs_dir2_data_entry_t *dep;
- xfs_dir2_data_unused_t *dup;
- int length;
-
- dup = (xfs_dir2_data_unused_t *)ptr;
-
- if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
- int length = be16_to_cpu(dup->length);
- if (dir_offset + length > dir_data.end_of_data ||
- length == 0 || (length &
- (XFS_DIR2_DATA_ALIGN - 1))) {
- if (show_warnings)
- print_warning("invalid length "
- "for dir free space in "
- "inode %llu",
- (long long)cur_ino);
- dir_data.bad_block = 1;
- break;
- }
- if (be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)) !=
- dir_offset) {
- dir_data.bad_block = 1;
- break;
+ if (zlen > 0) {
+ memset(&dup->tag, 0, zlen);
+ iocur_top->need_crc = 1;
}
- dir_offset += length;
- ptr += length;
- if (dir_offset >= dir_data.end_of_data ||
- ptr >= endptr)
- break;
}
+ if (dir_offset >= end_of_data || ptr >= endptr)
+ return;
+ }
- dep = (xfs_dir2_data_entry_t *)ptr;
- length = xfs_dir2_data_entsize(dep->namelen);
+ dep = (xfs_dir2_data_entry_t *)ptr;
+ length = M_DIROPS(mp)->data_entsize(dep->namelen);
- if (dir_offset + length > dir_data.end_of_data ||
- ptr + length > endptr) {
- if (show_warnings)
- print_warning("invalid length for "
- "dir entry name in inode %llu",
- (long long)cur_ino);
- break;
- }
- if (be16_to_cpu(*xfs_dir2_data_entry_tag_p(dep)) !=
- dir_offset) {
- dir_data.bad_block = 1;
- break;
- }
+ if (dir_offset + length > end_of_data ||
+ ptr + length > endptr) {
+ if (show_warnings)
+ print_warning(
+ "invalid length for dir entry name in inode %llu",
+ (long long)cur_ino);
+ return;
+ }
+ if (be16_to_cpu(*M_DIROPS(mp)->data_entry_tag_p(dep)) !=
+ dir_offset)
+ return;
+
+ if (obfuscate)
generate_obfuscated_name(be64_to_cpu(dep->inumber),
- dep->namelen, &dep->name[0]);
- dir_offset += length;
- ptr += length;
+ 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 *)M_DIROPS(mp)->data_entry_tag_p(dep) -
+ (char *)&dep->name[dep->namelen] - 1;
+ if (zlen > 0) {
+ memset(&dep->name[dep->namelen] + 1, 0, zlen);
+ iocur_top->need_crc = 1;
+ }
}
- dir_data.offset_to_entry = dir_offset &
- (mp->m_sb.sb_blocksize - 1);
}
}
static void
-obfuscate_symlink_blocks(
- char *block,
- xfs_dfilblks_t count)
+process_symlink_block(
+ char *block)
{
- int i;
-
- count <<= mp->m_sb.sb_blocklog;
- for (i = 0; i < count; i++)
- block[i] = random() % 127 + 1;
+ 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
attr_data.remote_vals[attr_data.remote_val_count] = blockidx;
attr_data.remote_val_count++;
blockidx++;
- length -= XFS_LBSIZE(mp);
+ length -= mp->m_sb.sb_blocksize;
+ }
+
+ if (attr_data.remote_val_count >= MAX_REMOTE_VALS) {
+ print_warning(
+"Overflowed attr obfuscation array. No longer obfuscating remote attrs.");
}
}
+/* Handle remote and leaf attributes */
static void
-obfuscate_attr_blocks(
- char *block,
- xfs_dfiloff_t offset,
- xfs_dfilblks_t count)
+process_attr_block(
+ char *block,
+ xfs_fileoff_t offset)
{
- xfs_attr_leafblock_t *leaf;
- int c;
- 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;
+
+ /* 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++) {
+ 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;
+ }
- for (c = 0; c < count; c++, offset++, block += XFS_LBSIZE(mp)) {
+ /* Ok, it's a leaf - get header; accounts for crc & non-crc */
+ xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &hdr, leaf);
- leaf = (xfs_attr_leafblock_t *)block;
+ nentries = hdr.count;
+ if (nentries * sizeof(xfs_attr_leaf_entry_t) +
+ 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;
+ }
- if (be16_to_cpu(leaf->hdr.info.magic) != XFS_ATTR_LEAF_MAGIC) {
- for (i = 0; i < attr_data.remote_val_count; i++) {
- if (attr_data.remote_vals[i] == offset)
- memset(block, 0, XFS_LBSIZE(mp));
- }
- continue;
- }
+ 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);
- nentries = be16_to_cpu(leaf->hdr.count);
- if (nentries * sizeof(xfs_attr_leaf_entry_t) +
- sizeof(xfs_attr_leaf_hdr_t) > XFS_LBSIZE(mp)) {
+ if (be16_to_cpu(entry->nameidx) > mp->m_sb.sb_blocksize) {
if (show_warnings)
- print_warning("invalid attr count in inode %llu",
+ print_warning(
+ "invalid attr nameidx in inode %llu",
(long long)cur_ino);
- continue;
+ break;
}
-
- for (i = 0, entry = &leaf->entries[0]; i < nentries;
- i++, entry++) {
- if (be16_to_cpu(entry->nameidx) > XFS_LBSIZE(mp)) {
+ if (entry->flags & XFS_ATTR_LOCAL) {
+ local = xfs_attr3_leaf_name_local(leaf, i);
+ if (local->namelen == 0) {
if (show_warnings)
- print_warning("invalid attr nameidx "
- "in inode %llu",
- (long long)cur_ino);
+ print_warning(
+ "zero length for attr name in inode %llu",
+ (long long)cur_ino);
break;
}
- if (entry->flags & XFS_ATTR_LOCAL) {
- local = xfs_attr_leaf_name_local(leaf, i);
- if (local->namelen == 0) {
- if (show_warnings)
- print_warning("zero length for "
- "attr name in inode %llu",
- (long long)cur_ino);
- break;
- }
+ if (obfuscate) {
generate_obfuscated_name(0, local->namelen,
&local->nameval[0]);
- memset(&local->nameval[local->namelen], 0,
+ memset(&local->nameval[local->namelen], 'v',
be16_to_cpu(local->valuelen));
- } else {
- remote = xfs_attr_leaf_name_remote(leaf, i);
- if (remote->namelen == 0 ||
- remote->valueblk == 0) {
- if (show_warnings)
- print_warning("invalid attr "
- "entry in inode %llu",
- (long long)cur_ino);
- break;
- }
+ }
+ /* 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) {
+ if (show_warnings)
+ print_warning(
+ "invalid attr entry in inode %llu",
+ (long long)cur_ino);
+ break;
+ }
+ if (obfuscate) {
generate_obfuscated_name(0, remote->namelen,
- &remote->name[0]);
+ &remote->name[0]);
add_remote_vals(be32_to_cpu(remote->valueblk),
- be32_to_cpu(remote->valuelen));
+ 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]);
+ }
}
-/* inode copy routines */
+/* Processes symlinks, attrs, directories ... */
+static int
+process_single_fsb_objects(
+ xfs_fileoff_t o,
+ xfs_fsblock_t s,
+ xfs_filblks_t c,
+ typnm_t btype,
+ xfs_fileoff_t last)
+{
+ char *dp;
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < c; i++) {
+ push_cur();
+ set_cur(&typtab[btype], XFS_FSB_TO_DADDR(mp, s), blkbb,
+ DB_RING_IGN, NULL);
+
+ if (!iocur_top->data) {
+ xfs_agnumber_t agno = XFS_FSB_TO_AGNO(mp, s);
+ xfs_agblock_t agbno = XFS_FSB_TO_AGBNO(mp, s);
+
+ print_warning("cannot read %s block %u/%u (%llu)",
+ typtab[btype].name, agno, agbno, s);
+ if (stop_on_read_error)
+ ret = -EIO;
+ goto out_pop;
+
+ }
+
+ 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;
+
+ M_DIROPS(mp)->node_hdr_from_disk(&hdr, node);
+ used = M_DIROPS(mp)->node_hdr_size;
+
+ 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_dir_geo->leafblk)
+ break;
+
+ process_dir_data_block(dp, o,
+ last == mp->m_dir_geo->fsbcount);
+ iocur_top->need_crc = 1;
+ break;
+ case TYP_SYMLINK:
+ process_symlink_block(dp);
+ iocur_top->need_crc = 1;
+ break;
+ case TYP_ATTR:
+ process_attr_block(dp, o);
+ iocur_top->need_crc = 1;
+ break;
+ default:
+ break;
+ }
+
+write:
+ ret = write_buf(iocur_top);
+out_pop:
+ pop_cur();
+ if (ret)
+ break;
+ o++;
+ s++;
+ }
+
+ return ret;
+}
+
+/*
+ * Static map to aggregate multiple extents into a single directory block.
+ */
+static struct bbmap mfsb_map;
+static int mfsb_length;
+
+static int
+process_multi_fsb_objects(
+ xfs_fileoff_t o,
+ xfs_fsblock_t s,
+ xfs_filblks_t c,
+ typnm_t btype,
+ xfs_fileoff_t last)
+{
+ int ret = 0;
+
+ switch (btype) {
+ case TYP_DIR2:
+ break;
+ default:
+ print_warning("bad type for multi-fsb object %d", btype);
+ return -EINVAL;
+ }
+
+ while (c > 0) {
+ unsigned int bm_len;
+
+ if (mfsb_length + c >= mp->m_dir_geo->fsbcount) {
+ bm_len = mp->m_dir_geo->fsbcount - mfsb_length;
+ mfsb_length = 0;
+ } else {
+ mfsb_length += c;
+ bm_len = c;
+ }
+
+ mfsb_map.b[mfsb_map.nmaps].bm_bn = XFS_FSB_TO_DADDR(mp, s);
+ mfsb_map.b[mfsb_map.nmaps].bm_len = XFS_FSB_TO_BB(mp, bm_len);
+ mfsb_map.nmaps++;
+
+ if (mfsb_length == 0) {
+ push_cur();
+ set_cur(&typtab[btype], 0, 0, DB_RING_IGN, &mfsb_map);
+ if (!iocur_top->data) {
+ xfs_agnumber_t agno = XFS_FSB_TO_AGNO(mp, s);
+ xfs_agblock_t agbno = XFS_FSB_TO_AGBNO(mp, s);
+
+ print_warning("cannot read %s block %u/%u (%llu)",
+ typtab[btype].name, agno, agbno, s);
+ if (stop_on_read_error)
+ ret = -1;
+ goto out_pop;
+
+ }
+
+ if ((!obfuscate && !zero_stale_data) ||
+ o >= mp->m_dir_geo->leafblk) {
+ ret = write_buf(iocur_top);
+ goto out_pop;
+ }
+
+ process_dir_data_block(iocur_top->data, o,
+ last == mp->m_dir_geo->fsbcount);
+ iocur_top->need_crc = 1;
+ ret = write_buf(iocur_top);
+out_pop:
+ pop_cur();
+ mfsb_map.nmaps = 0;
+ if (ret)
+ break;
+ }
+ c -= bm_len;
+ s += bm_len;
+ }
+
+ return ret;
+}
+
+/* inode copy routines */
static int
process_bmbt_reclist(
xfs_bmbt_rec_t *rp,
typnm_t btype)
{
int i;
- xfs_dfiloff_t o, op = NULLDFILOFF;
- xfs_dfsbno_t s;
- xfs_dfilblks_t c, cp = NULLDFILOFF;
+ xfs_fileoff_t o, op = NULLFILEOFF;
+ xfs_fsblock_t s;
+ xfs_filblks_t c, cp = NULLFILEOFF;
int f;
- xfs_dfiloff_t last;
+ xfs_fileoff_t last;
xfs_agnumber_t agno;
xfs_agblock_t agbno;
+ int error;
if (btype == TYP_DATA)
return 1;
break;
}
- push_cur();
- set_cur(&typtab[btype], XFS_FSB_TO_DADDR(mp, s), c * blkbb,
- DB_RING_IGN, NULL);
- if (iocur_top->data == NULL) {
- print_warning("cannot read %s block %u/%u (%llu)",
- typtab[btype].name, agno, agbno, s);
- if (stop_on_read_error) {
- pop_cur();
- return 0;
- }
+ /* multi-extent blocks require special handling */
+ if (btype != TYP_DIR2 || mp->m_dir_geo->fsbcount == 1) {
+ error = process_single_fsb_objects(o, s, c, btype, last);
} else {
- if (!dont_obfuscate)
- switch (btype) {
- case TYP_DIR2:
- if (o < mp->m_dirleafblk)
- obfuscate_dir_data_blocks(
- iocur_top->data, o, c,
- last == mp->m_dirblkfsbs);
- break;
-
- case TYP_SYMLINK:
- obfuscate_symlink_blocks(
- iocur_top->data, c);
- break;
-
- case TYP_ATTR:
- obfuscate_attr_blocks(iocur_top->data,
- o, c);
- break;
-
- default: ;
- }
- if (!write_buf(iocur_top)) {
- pop_cur();
- return 0;
- }
+ error = process_multi_fsb_objects(o, s, c, btype, last);
}
- pop_cur();
+ if (error)
+ return 0;
}
return 1;
xfs_agnumber_t ag;
xfs_agblock_t bno;
- ag = XFS_FSB_TO_AGNO(mp, be64_to_cpu(pp[i]));
- bno = XFS_FSB_TO_AGBNO(mp, be64_to_cpu(pp[i]));
+ ag = XFS_FSB_TO_AGNO(mp, get_unaligned_be64(&pp[i]));
+ bno = XFS_FSB_TO_AGBNO(mp, get_unaligned_be64(&pp[i]));
if (bno == 0 || bno > mp->m_sb.sb_agblocks ||
ag > mp->m_sb.sb_agcount) {
nrecs, itype);
}
- maxrecs = xfs_bmdr_maxrecs(mp, XFS_DFORK_SIZE(dip, mp, whichfork), 0);
+ maxrecs = libxfs_bmdr_maxrecs(XFS_DFORK_SIZE(dip, mp, whichfork), 0);
if (nrecs > maxrecs) {
if (show_warnings)
print_warning("invalid numrecs (%u) in inode %lld %s "
xfs_agnumber_t ag;
xfs_agblock_t bno;
- ag = XFS_FSB_TO_AGNO(mp, be64_to_cpu(pp[i]));
- bno = XFS_FSB_TO_AGBNO(mp, be64_to_cpu(pp[i]));
+ ag = XFS_FSB_TO_AGNO(mp, get_unaligned_be64(&pp[i]));
+ bno = XFS_FSB_TO_AGBNO(mp, get_unaligned_be64(&pp[i]));
if (bno == 0 || bno > mp->m_sb.sb_agblocks ||
ag > mp->m_sb.sb_agcount) {
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);
}
{
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: ;
return 1;
}
+/*
+ * when we process the inode, we may change the data in the data and/or
+ * attribute fork if they are in short form and we are obfuscating names.
+ * In this case we need to recalculate the CRC of the inode, but we should
+ * only do that if the CRC in the inode is good to begin with. If the crc
+ * is not ok, we just leave it alone.
+ */
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 */
+ bool need_new_crc = false;
success = 1;
cur_ino = XFS_AGINO_TO_INO(mp, agno, agino);
+ /* we only care about crc recalculation if we will modify the inode. */
+ if (obfuscate || zero_stale_data) {
+ crc_was_ok = libxfs_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:
- memset(&dir_data, 0, sizeof(dir_data));
success = process_inode_data(dip, TYP_DIR2);
+ if (dip->di_format == XFS_DINODE_FMT_LOCAL)
+ need_new_crc = 1;
break;
case S_IFLNK:
success = process_inode_data(dip, TYP_SYMLINK);
+ if (dip->di_format == XFS_DINODE_FMT_LOCAL)
+ need_new_crc = 1;
break;
case S_IFREG:
success = process_inode_data(dip, TYP_DATA);
nametable_clear();
/* copy extended attributes if they exist and forkoff is valid */
- if (success && XFS_DFORK_DSIZE(dip, mp) < XFS_LITINO(mp)) {
+ if (success &&
+ XFS_DFORK_DSIZE(dip, mp) < XFS_LITINO(mp, dip->di_version)) {
attr_data.remote_val_count = 0;
switch (dip->di_aformat) {
case XFS_DINODE_FMT_LOCAL:
- if (!dont_obfuscate)
- obfuscate_sf_attr(dip);
+ need_new_crc = 1;
+ if (obfuscate || zero_stale_data)
+ process_sf_attr(dip);
break;
case XFS_DINODE_FMT_EXTENTS:
}
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)
+ libxfs_dinode_calc_crc(mp, dip);
return success;
}
xfs_agino_t agino;
int off;
xfs_agblock_t agbno;
+ xfs_agblock_t end_agbno;
int i;
int rval = 0;
+ int blks_per_buf;
+ int inodes_per_buf;
+ int ioff;
agino = be32_to_cpu(rp->ir_startino);
agbno = XFS_AGINO_TO_AGBNO(mp, agino);
+ end_agbno = agbno + mp->m_ialloc_blks;
off = XFS_INO_TO_OFFSET(mp, agino);
+ /*
+ * If the fs supports sparse inode records, we must process inodes a
+ * cluster at a time because that is the sparse allocation granularity.
+ * Otherwise, we risk CRC corruption errors on reads of inode chunks.
+ *
+ * Also make sure that that we don't process more than the single record
+ * we've been passed (large block sizes can hold multiple inode chunks).
+ */
+ if (xfs_sb_version_hassparseinodes(&mp->m_sb))
+ blks_per_buf = xfs_icluster_size_fsb(mp);
+ else
+ blks_per_buf = mp->m_ialloc_blks;
+ inodes_per_buf = min(blks_per_buf << mp->m_sb.sb_inopblog,
+ XFS_INODES_PER_CHUNK);
+
+ /*
+ * Sanity check that we only process a single buffer if ir_startino has
+ * a buffer offset. A non-zero offset implies that the entire chunk lies
+ * within a block.
+ */
+ if (off && inodes_per_buf != XFS_INODES_PER_CHUNK) {
+ print_warning("bad starting inode offset %d", off);
+ return 0;
+ }
+
if (agino == 0 || agino == NULLAGINO || !valid_bno(agno, agbno) ||
!valid_bno(agno, XFS_AGINO_TO_AGBNO(mp,
agino + XFS_INODES_PER_CHUNK - 1))) {
return 1;
}
- push_cur();
- set_cur(&typtab[TYP_INODE], XFS_AGB_TO_DADDR(mp, agno, agbno),
- XFS_FSB_TO_BB(mp, XFS_IALLOC_BLOCKS(mp)),
- DB_RING_IGN, NULL);
- if (iocur_top->data == NULL) {
- print_warning("cannot read inode block %u/%u", agno, agbno);
- rval = !stop_on_read_error;
- goto pop_out;
- }
-
/*
* check for basic assumptions about inode chunks, and if any
* assumptions fail, don't process the inode chunk.
*/
-
if ((mp->m_sb.sb_inopblock <= XFS_INODES_PER_CHUNK && off != 0) ||
(mp->m_sb.sb_inopblock > XFS_INODES_PER_CHUNK &&
off % XFS_INODES_PER_CHUNK != 0) ||
(xfs_sb_version_hasalign(&mp->m_sb) &&
+ mp->m_sb.sb_inoalignmt != 0 &&
agbno % mp->m_sb.sb_inoalignmt != 0)) {
if (show_warnings)
print_warning("badly aligned inode (start = %llu)",
XFS_AGINO_TO_INO(mp, agno, agino));
- goto skip_processing;
+ return 1;
}
- /*
- * scan through inodes and copy any btree extent lists, directory
- * contents and extended attributes.
- */
- for (i = 0; i < XFS_INODES_PER_CHUNK; i++) {
- xfs_dinode_t *dip;
+ push_cur();
+ ioff = 0;
+ while (agbno < end_agbno && ioff < XFS_INODES_PER_CHUNK) {
+ if (xfs_inobt_is_sparse_disk(rp, ioff))
+ goto next_bp;
- if (XFS_INOBT_IS_FREE_DISK(rp, i))
- continue;
+ set_cur(&typtab[TYP_INODE], XFS_AGB_TO_DADDR(mp, agno, agbno),
+ XFS_FSB_TO_BB(mp, blks_per_buf), DB_RING_IGN, NULL);
+ if (iocur_top->data == NULL) {
+ print_warning("cannot read inode block %u/%u",
+ agno, agbno);
+ rval = !stop_on_read_error;
+ goto pop_out;
+ }
+
+ for (i = 0; i < inodes_per_buf; i++) {
+ xfs_dinode_t *dip;
+
+ dip = (xfs_dinode_t *)((char *)iocur_top->data +
+ ((off + i) << mp->m_sb.sb_inodelog));
- dip = (xfs_dinode_t *)((char *)iocur_top->data +
- ((off + i) << mp->m_sb.sb_inodelog));
+ /* process_inode handles free inodes, too */
+ if (!process_inode(agno, agino + ioff + i, dip,
+ XFS_INOBT_IS_FREE_DISK(rp, i)))
+ goto pop_out;
- if (!process_inode(agno, agino + i, dip))
+ inodes_copied++;
+ }
+
+ if (write_buf(iocur_top))
goto pop_out;
- }
-skip_processing:
- if (!write_buf(iocur_top))
- goto pop_out;
- inodes_copied += XFS_INODES_PER_CHUNK;
+next_bp:
+ agbno += blks_per_buf;
+ ioff += inodes_per_buf;
+ }
if (show_progress)
print_progress("Copied %u of %u inodes (%u of %u AGs)",
xfs_inobt_ptr_t *pp;
int i;
int numrecs;
+ int finobt = *(int *) arg;
numrecs = be16_to_cpu(block->bb_numrecs);
typtab[btype].name, agno, agbno);
numrecs = mp->m_inobt_mxr[0];
}
+
+ /*
+ * Only copy the btree blocks for the finobt. The inobt scan
+ * copies the inode chunks.
+ */
+ if (finobt)
+ return 1;
+
rp = XFS_INOBT_REC_ADDR(mp, block, 1);
for (i = 0; i < numrecs; i++, rp++) {
if (!copy_inode_chunk(agno, rp))
{
xfs_agblock_t root;
int levels;
+ int finobt = 0;
root = be32_to_cpu(agi->agi_root);
levels = be32_to_cpu(agi->agi_level);
return 1;
}
- return scan_btree(agno, root, levels, TYP_INOBT, agi, scanfunc_ino);
+ if (!scan_btree(agno, root, levels, TYP_INOBT, &finobt, scanfunc_ino))
+ return 0;
+
+ if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
+ root = be32_to_cpu(agi->agi_free_root);
+ levels = be32_to_cpu(agi->agi_free_level);
+
+ finobt = 1;
+ if (!scan_btree(agno, root, levels, TYP_INOBT, &finobt,
+ scanfunc_ino))
+ return 0;
+ }
+
+ return 1;
}
static int
if (stop_on_read_error)
goto pop_out;
} else {
- if (!write_buf(iocur_top))
+ /* 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;
}
if (stop_on_read_error)
goto pop_out;
} else {
- if (!write_buf(iocur_top))
+ if (write_buf(iocur_top))
goto pop_out;
}
if (stop_on_read_error)
goto pop_out;
} else {
- if (!write_buf(iocur_top))
+ if (write_buf(iocur_top))
goto pop_out;
}
if (stop_on_read_error)
goto pop_out;
} else {
- if (!write_buf(iocur_top))
+ 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;
}
goto pop_out;
if (!copy_free_cnt_btree(agno, agf))
goto pop_out;
+ if (!copy_rmap_btree(agno, agf))
+ goto pop_out;
}
/* copy inode btrees and the inodes and their associated metadata */
int offset;
int rval = 0;
- if (ino == 0)
+ if (ino == 0 || ino == NULLFSINO)
return 1;
agno = XFS_INO_TO_AGNO(mp, ino);
if (!copy_ino(mp->m_sb.sb_uquotino, TYP_DQBLK))
return 0;
- return copy_ino(mp->m_sb.sb_gquotino, TYP_DQBLK);
+ if (!copy_ino(mp->m_sb.sb_gquotino, TYP_DQBLK))
+ return 0;
+
+ return copy_ino(mp->m_sb.sb_pquotino, TYP_DQBLK);
}
static int
copy_log(void)
{
+ struct xlog log;
+ int dirty;
+ xfs_daddr_t logstart;
+ int logblocks;
+ int logversion;
+ int cycle = XLOG_INIT_CYCLE;
+
if (show_progress)
print_progress("Copying log");
print_warning("cannot read log");
return !stop_on_read_error;
}
- return write_buf(iocur_top);
+
+ /* If not obfuscating or zeroing, just copy the log as it is */
+ if (!obfuscate && !zero_stale_data)
+ goto done;
+
+ dirty = xlog_is_dirty(mp, &log, &x, 0);
+
+ switch (dirty) {
+ case 0:
+ /* clear out a clean log */
+ if (show_progress)
+ print_progress("Zeroing clean log");
+
+ logstart = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart);
+ logblocks = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks);
+ logversion = xfs_sb_version_haslogv2(&mp->m_sb) ? 2 : 1;
+ if (xfs_sb_version_hascrc(&mp->m_sb))
+ cycle = log.l_curr_cycle + 1;
+
+ libxfs_log_clear(NULL, iocur_top->data, logstart, logblocks,
+ &mp->m_sb.sb_uuid, logversion,
+ mp->m_sb.sb_logsunit, XLOG_FMT, cycle, true);
+ 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);
}
static int
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;
}
break;
case 'o':
- dont_obfuscate = 1;
+ obfuscate = 0;
break;
case 'w':
show_warnings = 1;
block_index = (__be64 *)((char *)metablock + sizeof(xfs_metablock_t));
block_buffer = (char *)metablock + BBSIZE;
- num_indicies = (BBSIZE - sizeof(xfs_metablock_t)) / sizeof(__be64);
+ num_indices = (BBSIZE - sizeof(xfs_metablock_t)) / sizeof(__be64);
+
+ /*
+ * A metadump block can hold at most num_indices of BBSIZE sectors;
+ * do not try to dump a filesystem with a sector size which does not
+ * fit within num_indices (i.e. within a single metablock).
+ */
+ if (mp->m_sb.sb_sectsize > num_indices * BBSIZE) {
+ print_warning("Cannot dump filesystem with sector size %u",
+ mp->m_sb.sb_sectsize);
+ free(metablock);
+ return 0;
+ }
+
cur_index = 0;
start_iocur_sp = iocur_sp;
/* write the remaining index */
if (!exitcode)
- exitcode = !write_index();
+ exitcode = write_index() < 0;
if (progress_since_warning)
fputc('\n', (outf == stdout) ? stderr : stdout);