From: "Barry Naujok" <bnaujok@sgi.com>
To: "xfs@oss.sgi.com" <xfs@oss.sgi.com>, xfs-dev <xfs-dev@sgi.com>
Subject: [PATCH 1/3] XFS metadump utility
Date: Mon, 28 May 2007 15:22:52 +1000 [thread overview]
Message-ID: <op.ts0ukea43jf8g2@pc-bnaujok.melbourne.sgi.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 716 bytes --]
Back in February, I posted a patch to xfs_db to capture metadata from a
filesystem into a file
http://oss.sgi.com/archives/xfs/2007-02/msg00072.html .
I have now updated it with the following changes:
- obfuscates directory names and attribute names
- zeros attribute values
- better support of stdin/stdout for redirection.
- separated the restore tool to it's own binary.
This was required as making it part of xfs_db
required an already valid filesystem to overwrite
with the restore operation. It's also a very
small compact piece of code.
- now has man pages.
Part 1 contains the changes for metadump.
Part 2 contains the changes for the restore.
Part 3 contains the man pages.
[-- Attachment #2: xfs_metadump.patch --]
[-- Type: application/octet-stream, Size: 43775 bytes --]
===========================================================================
xfsprogs/db/Makefile
===========================================================================
--- a/xfsprogs/db/Makefile 2007-05-28 15:09:59.000000000 +1000
+++ b/xfsprogs/db/Makefile 2007-05-17 14:41:18.780756729 +1000
@@ -11,11 +11,11 @@ HFILES = addr.h agf.h agfl.h agi.h attr.
bmapbt.h bmroot.h bnobt.h check.h cntbt.h command.h convert.h \
dbread.h debug.h dir.h dir2.h dir2sf.h dirshort.h dquot.h echo.h \
faddr.h field.h flist.h fprint.h frag.h freesp.h hash.h help.h \
- init.h inobt.h inode.h input.h io.h malloc.h output.h \
+ init.h inobt.h inode.h input.h io.h malloc.h metadump.h output.h \
print.h quit.h sb.h sig.h strvec.h text.h type.h write.h \
attrset.h
CFILES = $(HFILES:.h=.c)
-LSRCFILES = xfs_admin.sh xfs_check.sh xfs_ncheck.sh
+LSRCFILES = xfs_admin.sh xfs_check.sh xfs_ncheck.sh xfs_metadump.sh
LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBUUID) $(LIBRT)
LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG)
LLDFLAGS += -static
@@ -40,4 +40,5 @@ install: default
$(INSTALL) -m 755 xfs_admin.sh $(PKG_BIN_DIR)/xfs_admin
$(INSTALL) -m 755 xfs_check.sh $(PKG_BIN_DIR)/xfs_check
$(INSTALL) -m 755 xfs_ncheck.sh $(PKG_BIN_DIR)/xfs_ncheck
+ $(INSTALL) -m 755 xfs_metadump.sh $(PKG_BIN_DIR)/xfs_metadump
install-dev:
===========================================================================
xfsprogs/db/command.c
===========================================================================
--- a/xfsprogs/db/command.c 2007-05-28 15:09:59.000000000 +1000
+++ b/xfsprogs/db/command.c 2007-05-18 12:30:31.876457599 +1000
@@ -40,6 +40,7 @@
#include "inode.h"
#include "input.h"
#include "io.h"
+#include "metadump.h"
#include "output.h"
#include "print.h"
#include "quit.h"
@@ -131,6 +132,7 @@ init_commands(void)
inode_init();
input_init();
io_init();
+ metadump_init();
output_init();
print_init();
quit_init();
===========================================================================
xfsprogs/db/init.c
===========================================================================
--- a/xfsprogs/db/init.c 2007-05-28 15:09:59.000000000 +1000
+++ b/xfsprogs/db/init.c 2007-05-17 14:41:19.064719317 +1000
@@ -107,8 +107,8 @@ init(
}
if (read_bbs(XFS_SB_DADDR, 1, &bufp, NULL)) {
- dbprintf(_("%s: %s is invalid (cannot read first 512 bytes)\n"),
- progname, fsdevice);
+ fprintf(stderr, _("%s: %s is invalid (cannot read first 512 "
+ "bytes)\n"), progname, fsdevice);
exit(1);
}
@@ -118,7 +118,7 @@ init(
sbp = &xmount.m_sb;
if (sbp->sb_magicnum != XFS_SB_MAGIC) {
- dbprintf(_("%s: unexpected XFS SB magic number 0x%08x\n"),
+ fprintf(stderr, _("%s: unexpected XFS SB magic number 0x%08x\n"),
progname, sbp->sb_magicnum);
}
@@ -128,8 +128,8 @@ init(
mp = libxfs_mount(&xmount, sbp, x.ddev, x.logdev, x.rtdev,
LIBXFS_MOUNT_DEBUGGER);
if (!mp) {
- dbprintf(_("%s: device %s unusable (not an XFS filesystem?)\n"),
- progname, fsdevice);
+ fprintf(stderr, _("%s: device %s unusable (not an XFS "
+ "filesystem?)\n"), progname, fsdevice);
exit(1);
}
}
===========================================================================
xfsprogs/db/metadump.c
===========================================================================
--- a/xfsprogs/db/metadump.c 2006-06-17 00:58:24.000000000 +1000
+++ b/xfsprogs/db/metadump.c 2007-05-25 16:16:04.783219829 +1000
@@ -0,0 +1,1554 @@
+/*
+ * Copyright (c) 2007 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <libxfs.h>
+#include "bmap.h"
+#include "command.h"
+#include "metadump.h"
+#include "io.h"
+#include "output.h"
+#include "type.h"
+#include "init.h"
+#include "sig.h"
+#include "xfs_metadump.h"
+
+/* copy all metadata structures to/from a file */
+
+static int metadump_f(int argc, char **argv);
+static void metadump_help(void);
+
+/*
+ * metadump commands issue info/wornings/errors to standard error as
+ * metadump supports stdout as a destination.
+ *
+ * All static functions return zero on failure, while the public functions
+ * return zero on success.
+ */
+
+static const cmdinfo_t metadump_cmd =
+ { "metadump", NULL, metadump_f, 0, -1, 0,
+ "[-e] [-g] [-w] [-o] filename",
+ "dump metadata to a file", metadump_help };
+
+static FILE *outf; /* metadump file */
+
+static xfs_metablock_t *metablock; /* header + index + buffers */
+static __be64 *block_index;
+static char *block_buffer;
+
+static int num_indicies;
+static int cur_index;
+
+static xfs_ino_t cur_ino;
+
+static int show_progress = 0;
+static int stop_on_read_error = 0;
+static int dont_obfuscate = 0;
+static int show_warnings = 0;
+static int progress_since_warning = 0;
+
+void
+metadump_init(void)
+{
+ add_command(&metadump_cmd);
+}
+
+static void
+metadump_help(void)
+{
+ dbprintf(
+"\n"
+" The 'metadump' command dumps the known metadata to a compact file suitable\n"
+" for compressing and sending to an XFS maintainer for corruption analysis \n"
+" or xfs_repair failures.\n\n"
+" There are 3 options:\n"
+" -e -- Ignore read errors and keep going\n"
+" -g -- Display dump progress\n"
+" -o -- Don't obfuscate names and extended attributes\n"
+" -w -- Show warnings of bad metadata information\n"
+"\n");
+}
+
+static void
+print_warning(const char *fmt, ...)
+{
+ char buf[200];
+ va_list ap;
+
+ if (seenint())
+ return;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ buf[sizeof(buf)-1] = '\0';
+
+ fprintf(stderr, "%s%s: %s\n", progress_since_warning ? "\n" : "",
+ progname, buf);
+ progress_since_warning = 0;
+}
+
+static void
+print_progress(const char *fmt, ...)
+{
+ char buf[60];
+ va_list ap;
+ FILE *f;
+
+ if (seenint())
+ return;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ buf[sizeof(buf)-1] = '\0';
+
+ f = (outf == stdout) ? stderr : stdout;
+ fprintf(f, "\r%-59s", buf);
+ fflush(f);
+ progress_since_warning = 1;
+}
+
+/*
+ * 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.
+ */
+
+static int
+write_index(void)
+{
+ /*
+ * write index block and following data blocks (streaming)
+ */
+ 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;
+ }
+
+ memset(block_index, 0, num_indicies * sizeof(__be64));
+ cur_index = 0;
+ return 1;
+}
+
+static int
+write_buf(
+ iocur_t *buf)
+{
+ char *data;
+ __int64_t off;
+ int i;
+
+ for (i = 0, off = buf->bb, data = buf->data;
+ i < buf->blen;
+ 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;
+ }
+ }
+ return !seenint();
+}
+
+
+static int
+scan_btree(
+ xfs_agnumber_t agno,
+ xfs_agblock_t agbno,
+ int level,
+ typnm_t btype,
+ void *arg,
+ int (*func)(xfs_btree_hdr_t *bthdr,
+ xfs_agnumber_t agno,
+ xfs_agblock_t agbno,
+ int level,
+ typnm_t btype,
+ void *arg))
+{
+ push_cur();
+ set_cur(&typtab[btype], XFS_AGB_TO_DADDR(mp, agno, agbno), blkbb,
+ DB_RING_IGN, NULL);
+ if (iocur_top->data == NULL) {
+ print_warning("cannot read %s block %u/%u", typtab[btype].name,
+ agno, agbno);
+ return !stop_on_read_error;
+ }
+ if (!write_buf(iocur_top))
+ return 0;
+
+ if (!(*func)(iocur_top->data, agno, agbno, level - 1, btype, arg))
+ return 0;
+
+ pop_cur();
+ return 1;
+}
+
+/* free space tree copy routines */
+
+static int
+valid_bno(
+ xfs_agblock_t bno,
+ xfs_agnumber_t agno,
+ xfs_agblock_t agbno,
+ typnm_t btype)
+{
+ if (bno > 0 && bno <= mp->m_sb.sb_agblocks)
+ return 1;
+
+ if (show_warnings)
+ print_warning("invalid block number (%u) in %s block %u/%u",
+ bno, typtab[btype].name, agno, agbno);
+ return 0;
+}
+
+static int
+scanfunc_freesp(
+ xfs_btree_hdr_t *bthdr,
+ xfs_agnumber_t agno,
+ xfs_agblock_t agbno,
+ int level,
+ typnm_t btype,
+ void *arg)
+{
+ xfs_alloc_ptr_t *pp;
+ int i;
+ int nrecs;
+
+ if (level == 0)
+ return 1;
+
+ nrecs = be16_to_cpu(bthdr->bb_numrecs);
+ if (nrecs > mp->m_alloc_mxr[1]) {
+ if (show_warnings)
+ print_warning("invalid nrecs (%u) in %s block %u/%u",
+ nrecs, typtab[btype].name, agno, agbno);
+ return 1;
+ }
+
+ pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_alloc, bthdr, 1,
+ mp->m_alloc_mxr[1]);
+ for (i = 0; i < nrecs; i++) {
+ if (!valid_bno(be32_to_cpu(pp[i]), agno, agbno, btype))
+ continue;
+ if (!scan_btree(agno, be32_to_cpu(pp[i]), level, btype, arg,
+ scanfunc_freesp))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+copy_free_bno_btree(
+ xfs_agnumber_t agno,
+ xfs_agf_t *agf)
+{
+ xfs_agblock_t root;
+ int levels;
+
+ root = be32_to_cpu(agf->agf_roots[XFS_BTNUM_BNO]);
+ levels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]);
+
+ /* 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 bnobt "
+ "root in agf %u", root, agno);
+ return 1;
+ }
+ if (levels >= XFS_BTREE_MAXLEVELS) {
+ if (show_warnings)
+ print_warning("invalid level (%u) in bnobt root "
+ "in agf %u", levels, agno);
+ return 1;
+ }
+
+ return scan_btree(agno, root, levels, TYP_BNOBT, agf, scanfunc_freesp);
+}
+
+static int
+copy_free_cnt_btree(
+ xfs_agnumber_t agno,
+ xfs_agf_t *agf)
+{
+ xfs_agblock_t root;
+ int levels;
+
+ root = be32_to_cpu(agf->agf_roots[XFS_BTNUM_CNT]);
+ levels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]);
+
+ /* 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 cntbt "
+ "root in agf %u", root, agno);
+ return 1;
+ }
+ if (levels >= XFS_BTREE_MAXLEVELS) {
+ if (show_warnings)
+ print_warning("invalid level (%u) in cntbt root "
+ "in agf %u", levels, agno);
+ return 1;
+ }
+
+ return scan_btree(agno, root, levels, TYP_CNTBT, agf, scanfunc_freesp);
+}
+
+/* filename and extended attribute obfuscation routines */
+
+typedef struct name_ent {
+ struct name_ent *next;
+ xfs_dahash_t hash;
+ int namelen;
+ uchar_t name[1];
+} name_ent_t;
+
+#define NAME_TABLE_SIZE 4096
+
+static name_ent_t **nametable;
+
+static int
+create_nametable(void)
+{
+ nametable = calloc(NAME_TABLE_SIZE, sizeof(name_ent_t));
+ return nametable != NULL;
+}
+
+static void
+clear_nametable(void)
+{
+ int i;
+ name_ent_t *p;
+
+ for (i = 0; i < NAME_TABLE_SIZE; i++) {
+ while (nametable[i]) {
+ p = nametable[i];
+ nametable[i] = p->next;
+ free(p);
+ }
+ }
+}
+
+
+#define is_invalid_char(c) ((c) == '/' || (c) == '\0')
+#define rol32(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
+
+static inline uchar_t
+random_filename_char(void)
+{
+ uchar_t c;
+
+ do {
+ c = random() % 127 + 1;
+ } while (c == '/');
+ return c;
+}
+
+static int
+is_special_dirent(
+ xfs_ino_t ino,
+ int namelen,
+ uchar_t *name)
+{
+ static xfs_ino_t orphanage_ino = 0;
+ char s[32];
+ int slen;
+
+ /*
+ * due to the XFS name hashing algorithm, we cannot obfuscate
+ * names with 4 chars or less.
+ */
+ if (namelen <= 4)
+ return 1;
+
+ if (ino == 0)
+ return 0;
+
+ /*
+ * don't obfuscate lost+found nor any inodes within lost+found with
+ * the inode number
+ */
+ if (cur_ino == mp->m_sb.sb_rootino && namelen == 10 &&
+ memcmp(name, "lost+found", 10) == 0) {
+ orphanage_ino = ino;
+ return 1;
+ }
+ if (cur_ino != orphanage_ino)
+ return 0;
+
+ slen = sprintf(s, "%lld", (long long)ino);
+ return (slen == namelen && memcmp(name, s, namelen) == 0);
+}
+
+static void
+generate_obfuscated_name(
+ xfs_ino_t ino,
+ int namelen,
+ uchar_t *name)
+{
+ xfs_dahash_t hash;
+ name_ent_t *p;
+ int i;
+ int dup;
+ xfs_dahash_t newhash;
+ uchar_t newname[namelen];
+
+ if (is_special_dirent(ino, namelen, name))
+ return;
+
+ hash = libxfs_da_hashname(name, namelen);
+
+ /* create a random name with the same hash value */
+
+ do {
+ dup = 0;
+ newname[0] = '/';
+
+ for (;;) {
+ /* if the first char is a "/", preserve it */
+ i = (name[0] == '/');
+
+ for (newhash = 0; i < namelen - 5; i++) {
+ newname[i] = random_filename_char();
+ newhash = newname[i] ^ rol32(newhash, 7);
+ }
+ newhash = rol32(newhash, 3) ^ hash;
+ if (name[0] != '/' || namelen > 5) {
+ newname[namelen - 5] = (newhash >> 28) |
+ (random_filename_char() & 0xf0);
+ if (is_invalid_char(newname[namelen - 5]))
+ continue;
+ }
+ newname[namelen - 4] = (newhash >> 21) & 0x7f;
+ if (is_invalid_char(newname[namelen - 4]))
+ continue;
+ newname[namelen - 3] = (newhash >> 14) & 0x7f;
+ if (is_invalid_char(newname[namelen - 3]))
+ continue;
+ newname[namelen - 2] = (newhash >> 7) & 0x7f;
+ if (is_invalid_char(newname[namelen - 2]))
+ continue;
+ newname[namelen - 1] = ((newhash >> 0) ^
+ (newname[namelen - 5] >> 4)) & 0x7f;
+ if (is_invalid_char(newname[namelen - 1]))
+ continue;
+ break;
+ }
+
+ ASSERT(libxfs_da_hashname(newname, namelen) == hash);
+
+ for (p = nametable[hash % NAME_TABLE_SIZE]; p; p = p->next) {
+ if (p->hash == hash && p->namelen == namelen &&
+ memcmp(p->name, newname, namelen) == 0){
+ dup = 1;
+ break;
+ }
+ }
+ } while (dup);
+
+ memcpy(name, newname, namelen);
+
+ p = malloc(sizeof(name_ent_t) + namelen);
+ if (p == NULL)
+ return;
+
+ p->next = nametable[hash % NAME_TABLE_SIZE];
+ p->hash = hash;
+ p->namelen = namelen;
+ memcpy(p->name, name, namelen);
+
+ nametable[hash % NAME_TABLE_SIZE] = p;
+}
+
+static void
+obfuscate_sf_dir(
+ xfs_dinode_t *dip)
+{
+ xfs_dir2_sf_t *sfp;
+ xfs_dir2_sf_entry_t *sfep;
+ int ino_dir_size;
+ int i;
+
+ sfp = &dip->di_u.di_dir2sf;
+ ino_dir_size = dip->di_core.di_size;
+ if (ino_dir_size > XFS_DFORK_DSIZE(dip, mp)) {
+ ino_dir_size = XFS_DFORK_DSIZE(dip, mp);
+ if (show_warnings)
+ print_warning("invalid size for dir inode %llu",
+ (long long)cur_ino);
+ }
+
+ sfep = XFS_DIR2_SF_FIRSTENTRY(sfp);
+ for (i = 0; (i < sfp->hdr.count) &&
+ ((char *)sfep - (char *)sfp < ino_dir_size); i++) {
+
+ /*
+ * first check for bad name lengths. If they are bad, we
+ * have limitations to how much can be obfuscated.
+ */
+ int namelen = sfep->namelen;
+
+ if (namelen == 0) {
+ if (show_warnings)
+ print_warning("zero length entry in dir inode "
+ "%llu", (long long)cur_ino);
+ if (i != sfp->hdr.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) >
+ 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)
+ 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]);
+
+ sfep = (xfs_dir2_sf_entry_t *)((char *)sfep +
+ XFS_DIR2_SF_ENTSIZE_BYNAME(sfp, namelen));
+ }
+}
+
+static void
+obfuscate_sf_symlink(
+ xfs_dinode_t *dip)
+{
+ int i;
+
+ for (i = 0; i < dip->di_core.di_size; i++)
+ dip->di_u.di_symlink[i] = random() % 127 + 1;
+}
+
+static void
+obfuscate_sf_attr(
+ xfs_dinode_t *dip)
+{
+ /*
+ * with extended attributes, obfuscate the names and zero the actual
+ * values.
+ */
+
+ xfs_attr_shortform_t *asfp;
+ xfs_attr_sf_entry_t *asfep;
+ int ino_attr_size;
+ int i;
+
+ asfp = (xfs_attr_shortform_t *)XFS_DFORK_APTR(dip);
+ if (asfp->hdr.count == 0)
+ return;
+
+ ino_attr_size = be16_to_cpu(asfp->hdr.totsize);
+ if (ino_attr_size > XFS_DFORK_ASIZE(dip, mp)) {
+ ino_attr_size = XFS_DFORK_ASIZE(dip, mp);
+ if (show_warnings)
+ print_warning("invalid attr size in inode %llu",
+ (long long)cur_ino);
+ }
+
+ asfep = &asfp->list[0];
+ for (i = 0; (i < asfp->hdr.count) &&
+ ((char *)asfep - (char *)asfp < ino_attr_size); i++) {
+
+ int namelen = asfep->namelen;
+
+ if (namelen == 0) {
+ if (show_warnings)
+ print_warning("zero length attr entry in inode "
+ "%llu", (long long)cur_ino);
+ break;
+ } else if ((char *)asfep - (char *)asfp +
+ XFS_ATTR_SF_ENTSIZE(asfep) > ino_attr_size) {
+ if (show_warnings)
+ print_warning("attr entry length in inode %llu "
+ "overflows space", (long long)cur_ino);
+ break;
+ }
+
+ generate_obfuscated_name(0, asfep->namelen, &asfep->nameval[0]);
+ memset(&asfep->nameval[asfep->namelen], 0, 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;
+
+static void
+obfuscate_dir_data_blocks(
+ char *block,
+ xfs_dfiloff_t offset,
+ xfs_dfilblks_t count,
+ int is_block_format)
+{
+ /*
+ * we have to rely on the fileoffset and signature of the block to
+ * handle it's contents. If it's invalid, leave it alone.
+ * 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 */
+
+ dir_data.bad_block = 0;
+
+ if (is_block_format) {
+ xfs_dir2_leaf_entry_t *blp;
+ xfs_dir2_block_tail_t *btp;
+
+ 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;
+
+ 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);
+
+ if (be32_to_cpu(((xfs_dir2_data_hdr_t*)block)->magic) !=
+ wantmagic) {
+ if (show_warnings)
+ print_warning("invalid magic in dir "
+ "inode %llu block %ld",
+ (long long)cur_ino,
+ (long)offset);
+ dir_data.bad_block = 1;
+ }
+ }
+ dir_data.block_index++;
+ if (dir_data.block_index == mp->m_dirblkfsbs)
+ dir_data.block_index = 0;
+
+ if (dir_data.bad_block)
+ continue;
+
+ 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;
+ }
+ dir_offset += length;
+ ptr += length;
+ if (dir_offset >= dir_data.end_of_data ||
+ ptr >= endptr)
+ break;
+ }
+
+ dep = (xfs_dir2_data_entry_t *)ptr;
+ length = XFS_DIR2_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;
+ }
+ generate_obfuscated_name(be64_to_cpu(dep->inumber),
+ dep->namelen, &dep->name[0]);
+ dir_offset += length;
+ ptr += length;
+ }
+ dir_data.offset_to_entry = dir_offset &
+ (mp->m_sb.sb_blocksize - 1);
+ }
+}
+
+static void
+obfuscate_symlink_blocks(
+ char *block,
+ xfs_dfilblks_t count)
+{
+ int i;
+
+ count <<= mp->m_sb.sb_blocklog;
+ for (i = 0; i < count; i++)
+ block[i] = random() % 127 + 1;
+}
+
+#define MAX_REMOTE_VALS 4095
+
+static struct attr_data_s {
+ int remote_val_count;
+ xfs_dablk_t remote_vals[MAX_REMOTE_VALS];
+} attr_data;
+
+static inline void
+add_remote_vals(
+ xfs_dablk_t blockidx,
+ int length)
+{
+ while (length > 0 && attr_data.remote_val_count < MAX_REMOTE_VALS) {
+ attr_data.remote_vals[attr_data.remote_val_count] = blockidx;
+ attr_data.remote_val_count++;
+ blockidx++;
+ length -= XFS_LBSIZE(mp);
+ }
+}
+
+static void
+obfuscate_attr_blocks(
+ char *block,
+ xfs_dfiloff_t offset,
+ xfs_dfilblks_t count)
+{
+ 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;
+
+ for (c = 0; c < count; c++, offset++, block += XFS_LBSIZE(mp)) {
+
+ leaf = (xfs_attr_leafblock_t *)block;
+
+ 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;
+ }
+
+ 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 (show_warnings)
+ print_warning("invalid attr count in inode %llu",
+ (long long)cur_ino);
+ continue;
+ }
+
+ for (i = 0, entry = &leaf->entries[0]; i < nentries;
+ i++, entry++) {
+ if (be16_to_cpu(entry->nameidx) > XFS_LBSIZE(mp)) {
+ if (show_warnings)
+ print_warning("invalid attr nameidx "
+ "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;
+ }
+ generate_obfuscated_name(0, local->namelen,
+ &local->nameval[0]);
+ memset(&local->nameval[local->namelen], 0,
+ 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;
+ }
+ generate_obfuscated_name(0, remote->namelen,
+ &remote->name[0]);
+ add_remote_vals(be32_to_cpu(remote->valueblk),
+ be32_to_cpu(remote->valuelen));
+ }
+ }
+ }
+}
+
+/* inode copy routines */
+
+static int
+process_bmbt_reclist(
+ xfs_bmbt_rec_t *rp,
+ int numrecs,
+ typnm_t btype)
+{
+ int i;
+ xfs_dfiloff_t o;
+ xfs_dfsbno_t s;
+ xfs_dfilblks_t c;
+ int f;
+ xfs_dfiloff_t last;
+
+ if (btype == TYP_DATA)
+ return 1;
+
+ convert_extent(&rp[numrecs - 1], &o, &s, &c, &f);
+ last = o + c;
+
+ for (i = 0; i < numrecs; i++, rp++) {
+ convert_extent(rp, &o, &s, &c, &f);
+
+ 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",
+ typtab[btype].name,
+ XFS_FSB_TO_AGNO(mp, s),
+ XFS_FSB_TO_AGBNO(mp, s));
+ if (stop_on_read_error)
+ return 0;
+ } 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))
+ return 0;
+ }
+ pop_cur();
+ }
+
+ return 1;
+}
+
+static int
+scanfunc_bmap(
+ xfs_btree_hdr_t *bthdr,
+ xfs_agnumber_t agno,
+ xfs_agblock_t agbno,
+ int level,
+ typnm_t btype,
+ void *arg) /* ptr to itype */
+{
+ int i;
+ xfs_bmbt_ptr_t *pp;
+ xfs_bmbt_rec_t *rp;
+ int nrecs;
+
+ nrecs = be16_to_cpu(bthdr->bb_numrecs);
+
+ if (level == 0) {
+ if (nrecs > mp->m_bmap_dmxr[0]) {
+ if (show_warnings)
+ print_warning("invalid numrecs (%u) in %s "
+ "block %u/%u", nrecs,
+ typtab[btype].name, agno, agbno);
+ return 1;
+ }
+ rp = XFS_BTREE_REC_ADDR(mp->m_sb.sqb_blocksize, xfs_bmbt, bthdr,
+ 1, mp->m_bmap_dmxr[0]);
+
+ return process_bmbt_reclist(rp, nrecs, *(typnm_t*)arg);
+ }
+
+ if (nrecs > mp->m_bmap_dmxr[1]) {
+ if (show_warnings)
+ print_warning("invalid numrecs (%u) in %s block %u/%u",
+ nrecs, typtab[btype].name, agno, agbno);
+ return 1;
+ }
+ pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt, bthdr, 1,
+ mp->m_bmap_dmxr[1]);
+ for (i = 0; i < nrecs; i++) {
+ 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]));
+
+ if (bno == 0 || bno > mp->m_sb.sb_agblocks ||
+ ag > mp->m_sb.sb_agcount) {
+ if (show_warnings)
+ print_warning("invalid block number (%u/%u) "
+ "in %s block %u/%u", ag, bno,
+ typtab[btype].name, agno, agbno);
+ continue;
+ }
+
+ if (!scan_btree(ag, bno, level, btype, arg, scanfunc_bmap))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+process_btinode(
+ xfs_dinode_t *dip,
+ typnm_t itype)
+{
+ xfs_bmdr_block_t *dib;
+ int i;
+ xfs_bmbt_ptr_t *pp;
+ xfs_bmbt_rec_t *rp;
+ int level;
+ int nrecs;
+ int maxrecs;
+ int whichfork;
+ typnm_t btype;
+
+ whichfork = (itype == TYP_ATTR) ? XFS_ATTR_FORK : XFS_DATA_FORK;
+ btype = (itype == TYP_ATTR) ? TYP_BMAPBTA : TYP_BMAPBTD;
+
+ dib = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork);
+ level = be16_to_cpu(dib->bb_level);
+ nrecs = be16_to_cpu(dib->bb_numrecs);
+
+ if (level > XFS_BM_MAXLEVELS(mp, whichfork)) {
+ if (show_warnings)
+ print_warning("invalid level (%u) in inode %lld %s "
+ "root", level, (long long)cur_ino,
+ typtab[btype].name);
+ return 1;
+ }
+
+ if (level == 0) {
+ rp = XFS_BTREE_REC_ADDR(XFS_DFORK_SIZE(dip, mp, whichfork),
+ xfs_bmdr, dib, 1, XFS_BTREE_BLOCK_MAXRECS(
+ XFS_DFORK_SIZE(dip, mp, whichfork),
+ xfs_bmdr, 1));
+
+ return process_bmbt_reclist(rp, nrecs, itype);
+ }
+
+ maxrecs = XFS_BTREE_BLOCK_MAXRECS(XFS_DFORK_SIZE(dip, mp, whichfork),
+ xfs_bmdr, 0);
+ if (nrecs > maxrecs) {
+ if (show_warnings)
+ print_warning("invalid numrecs (%u) in inode %lld %s "
+ "root", nrecs, (long long)cur_ino,
+ typtab[btype].name);
+ return 1;
+ }
+
+ pp = XFS_BTREE_PTR_ADDR(XFS_DFORK_SIZE(dip, mp, whichfork), xfs_bmdr,
+ dib, 1, maxrecs);
+ for (i = 0; i < nrecs; i++) {
+ 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]));
+
+ if (bno == 0 || bno > mp->m_sb.sb_agblocks ||
+ ag > mp->m_sb.sb_agcount) {
+ if (show_warnings)
+ print_warning("invalid block number (%u/%u) "
+ "in inode %llu %s root", ag,
+ bno, (long long)cur_ino,
+ typtab[btype].name);
+ continue;
+ }
+
+ if (!scan_btree(ag, bno, level, btype, &itype, scanfunc_bmap))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+process_exinode(
+ xfs_dinode_t *dip,
+ typnm_t itype)
+{
+ int whichfork;
+
+ whichfork = (itype == TYP_ATTR) ? XFS_ATTR_FORK : XFS_DATA_FORK;
+
+ return process_bmbt_reclist(
+ (xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip, whichfork),
+ XFS_DFORK_NEXTENTS_HOST(dip, whichfork), itype);
+}
+
+static int
+process_inode_data(
+ xfs_dinode_t *dip,
+ typnm_t itype)
+{
+ switch (dip->di_core.di_format) {
+ case XFS_DINODE_FMT_LOCAL:
+ if (!dont_obfuscate)
+ switch (itype) {
+ case TYP_DIR2:
+ obfuscate_sf_dir(dip);
+ break;
+
+ case TYP_SYMLINK:
+ obfuscate_sf_symlink(dip);
+ break;
+
+ default: ;
+ }
+ break;
+
+ case XFS_DINODE_FMT_EXTENTS:
+ return process_exinode(dip, itype);
+
+ case XFS_DINODE_FMT_BTREE:
+ return process_btinode(dip, itype);
+ }
+ return 1;
+}
+
+static int
+process_inode(
+ xfs_agnumber_t agno,
+ xfs_agino_t agino,
+ xfs_dinode_t *dip)
+{
+ xfs_dinode_core_t odic;
+ int success;
+
+ /* convert the core */
+ memcpy(&odic, &dip->di_core, sizeof(xfs_dinode_core_t));
+ libxfs_xlate_dinode_core((xfs_caddr_t)&odic, &dip->di_core, 1);
+
+ success = 1;
+ cur_ino = XFS_AGINO_TO_INO(mp, agno, agino);
+
+
+ /* copy appropriate data fork metadata */
+ switch (dip->di_core.di_mode & S_IFMT) {
+ case S_IFDIR:
+ memset(&dir_data, 0, sizeof(dir_data));
+ success = process_inode_data(dip, TYP_DIR2);
+ break;
+ case S_IFLNK:
+ success = process_inode_data(dip, TYP_SYMLINK);
+ break;
+ default:
+ success = process_inode_data(dip, TYP_DATA);
+ }
+ clear_nametable();
+
+ /* copy extended attributes if they exist */
+ if (success && dip->di_core.di_forkoff) {
+ attr_data.remote_val_count = 0;
+ switch (dip->di_core.di_aformat) {
+ case XFS_DINODE_FMT_LOCAL:
+ if (!dont_obfuscate)
+ obfuscate_sf_attr(dip);
+ break;
+
+ case XFS_DINODE_FMT_EXTENTS:
+ success = process_exinode(dip, TYP_ATTR);
+ break;
+
+ case XFS_DINODE_FMT_BTREE:
+ success = process_btinode(dip, TYP_ATTR);
+ break;
+ }
+ clear_nametable();
+ }
+
+ /* restore the core back to it's original endianess */
+ memcpy(&dip->di_core, &odic, sizeof(xfs_dinode_core_t));
+
+ return success;
+}
+
+static __uint32_t inodes_copied = 0;
+
+static int
+copy_inode_chunk(
+ xfs_agnumber_t agno,
+ xfs_inobt_rec_t *rp)
+{
+ xfs_agino_t agino;
+ int off;
+ xfs_agblock_t agbno;
+ int i;
+
+ agino = be32_to_cpu(rp->ir_startino);
+ agbno = XFS_AGINO_TO_AGBNO(mp, agino);
+ off = XFS_INO_TO_OFFSET(mp, agino);
+
+ 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);
+ return !stop_on_read_error;
+ }
+
+ /*
+ * 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;
+
+ 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))
+ return 0;
+ }
+
+ if (!write_buf(iocur_top))
+ return 0;
+
+ inodes_copied += XFS_INODES_PER_CHUNK;
+
+ if (show_progress)
+ print_progress("Copied %u of %u inodes (%u of %u AGs)",
+ inodes_copied, mp->m_sb.sb_icount, agno,
+ mp->m_sb.sb_agcount);
+
+ pop_cur();
+
+ return 1;
+}
+
+static int
+scanfunc_ino(
+ xfs_btree_hdr_t *bthdr,
+ xfs_agnumber_t agno,
+ xfs_agblock_t agbno,
+ int level,
+ typnm_t btype,
+ void *arg)
+{
+ xfs_inobt_rec_t *rp;
+ xfs_inobt_ptr_t *pp;
+ int i;
+
+ if (level == 0) {
+ rp = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_inobt,
+ bthdr, 1, mp->m_inobt_mxr[0]);
+ for (i = 0; i < be16_to_cpu(bthdr->bb_numrecs); i++, rp++) {
+ if (!copy_inode_chunk(agno, rp))
+ return 0;
+ }
+ } else {
+ pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_inobt,
+ bthdr, 1, mp->m_inobt_mxr[1]);
+ for (i = 0; i < be16_to_cpu(bthdr->bb_numrecs); i++) {
+ if (!valid_bno(be32_to_cpu(pp[i]), agno, agbno, btype))
+ continue;
+ if (!scan_btree(agno, be32_to_cpu(pp[i]), level,
+ btype, arg, scanfunc_ino))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+copy_inodes(
+ xfs_agnumber_t agno,
+ xfs_agi_t *agi)
+{
+ xfs_agblock_t root;
+ int levels;
+
+ root = be32_to_cpu(agi->agi_root);
+ levels = be32_to_cpu(agi->agi_level);
+
+ /* 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 inobt "
+ "root in agi %u", root, agno);
+ return 1;
+ }
+ if (levels >= XFS_BTREE_MAXLEVELS) {
+ if (show_warnings)
+ print_warning("invalid level (%u) in inobt root "
+ "in agi %u", levels, agno);
+ return 1;
+ }
+
+ return scan_btree(agno, root, levels, TYP_INOBT, agi, scanfunc_ino);
+}
+
+static int
+scan_ag(
+ xfs_agnumber_t agno)
+{
+ xfs_agf_t *agf;
+ xfs_agi_t *agi;
+
+ /* copy the superblock of the AG */
+ push_cur();
+ set_cur(&typtab[TYP_SB], XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
+ XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
+ if (!iocur_top->data) {
+ print_warning("cannot read superblock for ag %u", agno);
+ if (stop_on_read_error)
+ return 0;
+ } else {
+ if (!write_buf(iocur_top))
+ return 0;
+ }
+
+ /* copy the AG free space btree root */
+ push_cur();
+ set_cur(&typtab[TYP_AGF], XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
+ agf = iocur_top->data;
+ if (iocur_top->data == NULL) {
+ print_warning("cannot read agf block for ag %u", agno);
+ if (stop_on_read_error)
+ return 0;
+ } else {
+ if (!write_buf(iocur_top))
+ return 0;
+ }
+
+ /* copy the AG inode btree root */
+ push_cur();
+ set_cur(&typtab[TYP_AGI], XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
+ agi = iocur_top->data;
+ if (iocur_top->data == NULL) {
+ print_warning("cannot read agi block for ag %u", agno);
+ if (stop_on_read_error)
+ return 0;
+ } else {
+ if (!write_buf(iocur_top))
+ return 0;
+ }
+
+ /* copy the AG free list header */
+ push_cur();
+ set_cur(&typtab[TYP_AGFL], XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
+ if (iocur_top->data == NULL) {
+ print_warning("cannot read agfl block for ag %u", agno);
+ if (stop_on_read_error)
+ return 0;
+ } else {
+ if (!write_buf(iocur_top))
+ return 0;
+ }
+ pop_cur();
+
+ /* copy AG free space btrees */
+ if (agf) {
+ if (show_progress)
+ print_progress("Copying free space trees of AG %u",
+ agno);
+ if (!copy_free_bno_btree(agno, agf))
+ return 0;
+ if (!copy_free_cnt_btree(agno, agf))
+ return 0;
+ }
+
+ /* copy inode btrees and the inodes and their associated metadata */
+ if (agi) {
+ if (!copy_inodes(agno, agi))
+ return 0;
+ }
+
+ pop_cur();
+ pop_cur();
+ pop_cur();
+
+ return 1;
+}
+
+static int
+copy_ino(
+ xfs_ino_t ino,
+ typnm_t itype)
+{
+ xfs_agnumber_t agno;
+ xfs_agblock_t agbno;
+ xfs_agino_t agino;
+ xfs_dinode_t *dip;
+ xfs_dinode_core_t tdic;
+ int offset;
+
+ if (ino == 0)
+ return 1;
+
+ agno = XFS_INO_TO_AGNO(mp, ino);
+ agino = XFS_INO_TO_AGINO(mp, ino);
+ agbno = XFS_AGINO_TO_AGBNO(mp, agino);
+ offset = XFS_AGINO_TO_OFFSET(mp, agino);
+
+ if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks ||
+ offset >= mp->m_sb.sb_inopblock) {
+ if (show_warnings)
+ print_warning("invalid %s inode number (%lld)",
+ typtab[itype].name, (long long)ino);
+ return 1;
+ }
+
+ push_cur();
+ set_cur(&typtab[TYP_INODE], XFS_AGB_TO_DADDR(mp, agno, agbno),
+ blkbb, DB_RING_IGN, NULL);
+ if (iocur_top->data == NULL) {
+ print_warning("cannot read %s inode %lld",
+ typtab[itype].name, (long long)ino);
+ return !stop_on_read_error;
+ }
+ off_cur(offset << mp->m_sb.sb_inodelog, mp->m_sb.sb_inodesize);
+
+ dip = iocur_top->data;
+ libxfs_xlate_dinode_core((xfs_caddr_t)&dip->di_core, &tdic, 1);
+ memcpy(&dip->di_core, &tdic, sizeof(xfs_dinode_core_t));
+
+ cur_ino = ino;
+ return process_inode_data(dip, itype);
+}
+
+
+static int
+copy_sb_inodes(void)
+{
+ if (!copy_ino(mp->m_sb.sb_rbmino, TYP_RTBITMAP))
+ return 0;
+
+ if (!copy_ino(mp->m_sb.sb_rsumino, TYP_RTSUMMARY))
+ return 0;
+
+ if (!copy_ino(mp->m_sb.sb_uquotino, TYP_DQBLK))
+ return 0;
+
+ return copy_ino(mp->m_sb.sb_gquotino, TYP_DQBLK);
+}
+
+static int
+copy_log(void)
+{
+ if (show_progress)
+ print_progress("Copying log");
+
+ push_cur();
+ set_cur(&typtab[TYP_LOG], XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart),
+ mp->m_sb.sb_logblocks * blkbb, DB_RING_IGN, NULL);
+ if (iocur_top->data == NULL) {
+ print_warning("cannot read log");
+ return !stop_on_read_error;
+ }
+ return write_buf(iocur_top);
+}
+
+static int
+metadump_f(
+ int argc,
+ char **argv)
+{
+ xfs_agnumber_t agno;
+ int c;
+ int start_iocur_sp;
+
+ exitcode = 1;
+ show_progress = 0;
+ show_warnings = 0;
+ stop_on_read_error = 0;
+
+ if (mp->m_sb.sb_magicnum != XFS_SB_MAGIC) {
+ print_warning("bad superblock magic number %x, giving up",
+ mp->m_sb.sb_magicnum);
+ return 0;
+ }
+
+ while ((c = getopt(argc, argv, "egow")) != EOF) {
+ switch (c) {
+ case 'e':
+ stop_on_read_error = 1;
+ break;
+ case 'g':
+ show_progress = 1;
+ break;
+ case 'o':
+ dont_obfuscate = 1;
+ break;
+ case 'w':
+ show_warnings = 1;
+ break;
+ default:
+ print_warning("bad option for metadump command");
+ return 0;
+ }
+ }
+
+ if (optind != argc - 1) {
+ print_warning("too few options for metadump (no filename given)");
+ return 0;
+ }
+
+ metablock = (xfs_metablock_t *)calloc(BBSIZE + 1, BBSIZE);
+ if (metablock == NULL) {
+ print_warning("memory allocation failure");
+ return 0;
+ }
+ metablock->mb_blocklog = BBSHIFT;
+ metablock->mb_magic = cpu_to_be32(XFS_MD_MAGIC);
+
+ if (!create_nametable()) {
+ print_warning("memory allocation failure");
+ free(metablock);
+ return 0;
+ }
+
+ block_index = (__be64 *)((char *)metablock + sizeof(xfs_metablock_t));
+ block_buffer = (char *)metablock + BBSIZE;
+ num_indicies = (BBSIZE - sizeof(xfs_metablock_t)) / sizeof(__be64);
+ cur_index = 0;
+ start_iocur_sp = iocur_sp;
+
+ if (strcmp(argv[optind], "-") == 0) {
+ if (isatty(fileno(stdout))) {
+ print_warning("cannot write to a terminal");
+ free(nametable);
+ free(metablock);
+ return 0;
+ }
+ outf = stdout;
+ } else {
+ outf = fopen(argv[optind], "wb");
+ if (outf == NULL) {
+ print_warning("cannot create dump file");
+ free(nametable);
+ free(metablock);
+ return 0;
+ }
+ }
+
+ exitcode = 0;
+
+ for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+ if (!scan_ag(agno)) {
+ exitcode = 1;
+ break;
+ }
+ }
+
+ /* copy realtime and quota inode contents */
+ if (!exitcode)
+ exitcode = !copy_sb_inodes();
+
+ /* copy log if it's internal */
+ if ((mp->m_sb.sb_logstart != 0) && !exitcode)
+ exitcode = !copy_log();
+
+ /* write the remaining index */
+ if (!exitcode)
+ exitcode = !write_index();
+
+ if (progress_since_warning)
+ fputc('\n', (outf == stdout) ? stderr : stdout);
+
+ if (outf != stdout)
+ fclose(outf);
+
+ /* cleanup iocur stack */
+ while (iocur_sp > start_iocur_sp)
+ pop_cur();
+
+ free(nametable);
+ free(metablock);
+
+ return 0;
+}
===========================================================================
xfsprogs/db/metadump.h
===========================================================================
--- a/xfsprogs/db/metadump.h 2006-06-17 00:58:24.000000000 +1000
+++ b/xfsprogs/db/metadump.h 2007-05-18 12:07:57.314023778 +1000
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2007 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+extern void metadump_init(void);
===========================================================================
xfsprogs/db/xfs_metadump.sh
===========================================================================
--- a/xfsprogs/db/xfs_metadump.sh 2006-06-17 00:58:24.000000000 +1000
+++ b/xfsprogs/db/xfs_metadump.sh 2007-05-28 14:59:12.817850921 +1000
@@ -0,0 +1,38 @@
+#!/bin/sh -f
+#
+# Copyright (c) 2007 Silicon Graphics, Inc. All Rights Reserved.
+#
+
+OPTS=" "
+DBOPTS=" "
+USAGE="Usage: xfs_metadump [-efogwV] [-l logdev] source target"
+
+while getopts "efgl:owV" c
+do
+ case $c in
+ e) OPTS=$OPTS"-e ";;
+ g) OPTS=$OPTS"-g ";;
+ o) OPTS=$OPTS"-o ";;
+ w) OPTS=$OPTS"-w ";;
+ f) DBOPTS=$DBOPTS" -f";;
+ l) DBOPTS=$DBOPTS" -l "$OPTARG" ";;
+ V) xfs_db -p xfs_metadump -V
+ status=$?
+ exit $status
+ ;;
+ \?) echo $USAGE 1>&2
+ exit 2
+ ;;
+ esac
+done
+set -- extra $@
+shift $OPTIND
+case $# in
+ 2) xfs_db$DBOPTS -i -p xfs_metadump -c "metadump$OPTS $2" $1
+ status=$?
+ ;;
+ *) echo $USAGE 1>&2
+ exit 2
+ ;;
+esac
+exit $status
===========================================================================
xfsprogs/include/xfs_metadump.h
===========================================================================
--- a/xfsprogs/include/xfs_metadump.h 2006-06-17 00:58:24.000000000 +1000
+++ b/xfsprogs/include/xfs_metadump.h 2007-05-18 12:31:00.840635783 +1000
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2007 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _XFS_METADUMP_H_
+#define _XFS_METADUMP_H_
+
+#define XFS_MD_MAGIC 0x5846534d /* 'XFSM' */
+
+typedef struct xfs_metablock {
+ __be32 mb_magic;
+ __be16 mb_count;
+ __uint8_t mb_blocklog;
+ __uint8_t mb_reserved;
+ /* followed by an array of xfs_daddr_t */
+} xfs_metablock_t;
+
+#endif /* _XFS_METADUMP_H_ */
next reply other threads:[~2007-05-28 5:19 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-05-28 5:22 Barry Naujok [this message]
2007-06-04 14:52 ` [PATCH 1/3] XFS metadump utility Christoph Hellwig
2007-06-05 0:47 ` Barry Naujok
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=op.ts0ukea43jf8g2@pc-bnaujok.melbourne.sgi.com \
--to=bnaujok@sgi.com \
--cc=xfs-dev@sgi.com \
--cc=xfs@oss.sgi.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox