public inbox for linux-xfs@vger.kernel.org
 help / color / mirror / Atom feed
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_ */

             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