
===========================================================================
xfsprogs/db/Makefile
===========================================================================

--- a/xfsprogs/db/Makefile	2007-02-13 17:29:33.000000000 +1100
+++ b/xfsprogs/db/Makefile	2007-02-13 11:24:54.820768218 +1100
@@ -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-02-13 17:29:33.000000000 +1100
+++ b/xfsprogs/db/command.c	2007-01-30 15:11:45.903970466 +1100
@@ -131,6 +131,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-02-13 17:29:33.000000000 +1100
+++ b/xfsprogs/db/init.c	2007-02-13 16:19:42.359432895 +1100
@@ -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	2007-02-13 17:29:33.000000000 +1100
+++ b/xfsprogs/db/metadump.c	2007-02-13 16:15:18.160445693 +1100
@@ -0,0 +1,1195 @@
+/*
+ * 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 <xfs/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"
+
+/* copy all metadata structures to/from a file */
+
+static int	metadump_f(int argc, char **argv);
+static int	metarestore_f(int argc, char **argv);
+static void	metadump_help(void);
+
+#define	XFS_MD_MAGIC		0x5846534d	/* 'XFSM' */
+
+/*
+ * metadump commands issue info/wornings/errors to standard error as
+ * metadump supports stdout as a destination (and from stdin for restore).
+ *
+ * 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] filename",
+		"dump metadata to a file", metadump_help };
+
+static const cmdinfo_t	metarestore_cmd =
+	{ "metarestore", NULL, metarestore_f, 0, -1, 0,
+		"[-g] filename",
+		"restore metadata from a file", NULL };
+
+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;
+
+static int		fd;	/* 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 int		show_progress = 0;
+static int		stop_on_read_error = 0;
+static int		wflag = 0;
+static int		progress_since_warning = 0;
+
+void
+metadump_init(void)
+{
+	add_command(&metadump_cmd);
+	if (expert_mode)
+		add_command(&metarestore_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"
+"   -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 = (fd == STDOUT_FILENO) ? 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 (write(fd, metablock, (cur_index + 1) << BBSHIFT) == -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 (wflag)
+		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 (wflag)
+			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 (wflag)
+			print_warning("invalid block number (%u) in bnobt "
+					"root in agf %u", root, agno);
+		return 1;
+	}
+	if (levels >= XFS_BTREE_MAXLEVELS) {
+		if (wflag)
+			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 (wflag)
+			print_warning("invalid block number (%u) in cntbt "
+					"root in agf %u", root, agno);
+		return 1;
+	}
+	if (levels >= XFS_BTREE_MAXLEVELS) {
+		if (wflag)
+			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);
+}
+
+/* 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;
+
+	if (btype == TYP_DATA)
+		return 1;
+
+	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 (!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 (wflag)
+				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 (wflag)
+			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 (wflag)
+				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_ino_t		ino,
+	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 (wflag)
+			print_warning("invalid level (%u) in inode %lld %s "
+					"root", level, (long long)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 (wflag)
+			print_warning("invalid numrecs (%u) in inode %lld %s "
+					"root", nrecs, (long long)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 (wflag)
+				print_warning("invalid block number (%u/%u) "
+						"in inode %llu %s root", ag,
+						bno, (long long)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_ino_t		ino,
+	xfs_dinode_t		*dip,
+	typnm_t			itype)
+{
+	switch (dip->di_core.di_format) {
+		case XFS_DINODE_FMT_EXTENTS:
+			return process_exinode(dip, itype);
+
+		case XFS_DINODE_FMT_BTREE:
+			return process_btinode(ino, 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;
+	xfs_ino_t		lino;
+	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;
+	lino = XFS_AGINO_TO_INO(mp, agno, agino);
+
+	/* copy appropriate data fork metadata */
+	switch (dip->di_core.di_mode & S_IFMT) {
+		case S_IFDIR:
+			success = process_inode_data(lino, dip, TYP_DIR2);
+			break;
+		case S_IFLNK:
+			success = process_inode_data(lino, dip, TYP_SYMLINK);
+			break;
+		default:
+			success = process_inode_data(lino, dip, TYP_DATA);
+	}
+
+	/* copy extended attributes if they exist */
+	if (success && dip->di_core.di_forkoff) {
+		switch (dip->di_core.di_aformat) {
+			case XFS_DINODE_FMT_EXTENTS:
+				success = process_exinode(dip, TYP_ATTR);
+				break;
+
+			case XFS_DINODE_FMT_BTREE:
+				success = process_btinode(lino, dip, TYP_ATTR);
+				break;
+		}
+	}
+
+	/* 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 (wflag)
+			print_warning("invalid block number (%u) in inobt "
+					"root in agi %u", root, agno);
+		return 1;
+	}
+	if (levels >= XFS_BTREE_MAXLEVELS) {
+		if (wflag)
+			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 (wflag)
+			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));
+
+	return process_inode_data(ino, 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;
+	wflag = 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, "egw")) != EOF) {
+		switch (c) {
+			case 'e':
+				stop_on_read_error = 1;
+				break;
+			case 'g':
+				show_progress = 1;
+				break;
+			case 'w':
+				wflag = 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);
+
+	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(STDOUT_FILENO)) {
+			print_warning("cannot write to a terminal");
+			free(metablock);
+			return 0;
+		}
+		fd = STDOUT_FILENO;
+	} else {
+		fd = open(argv[optind], O_WRONLY | O_CREAT | O_TRUNC, 0666);
+		if (fd == -1) {
+			print_warning("cannot create dump file");
+			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 (fd != STDOUT_FILENO)
+		close(fd);
+
+	/* cleanup iocur stack */
+	while (iocur_sp > start_iocur_sp)
+		pop_cur();
+
+	free(metablock);
+
+	if (progress_since_warning)
+		fputc('\n', (fd == STDOUT_FILENO) ? stderr : stdout);
+
+	return 0;
+}
+
+static void
+remount(
+	xfs_sb_t		*sb)
+{
+	libxfs_umount(mp);
+
+	if (!libxfs_mount(mp, sb, x.ddev, x.logdev, x.rtdev,
+			LIBXFS_MOUNT_ROOTINOS | LIBXFS_MOUNT_DEBUGGER)) {
+		if (!libxfs_mount(mp, sb, x.ddev, x.logdev, x.rtdev,
+				LIBXFS_MOUNT_DEBUGGER)) {
+			print_warning("cannot remount the filesystem");
+			exit(1);
+		}
+	}
+	blkbb = 1 << mp->m_blkbb_log;
+}
+
+static int
+perform_restore(void)
+{
+	xfs_metablock_t		tmb;
+	int			bsize;
+	xfs_sb_t		sb;
+	int			bbpb;
+	__int64_t		bbs_read;
+	int			success;
+
+	/*
+	 * read in first block (superblock 0), set "inprogress" flag for it,
+	 * read in the rest of the file, and if complete, clear SB 0's
+	 * "inprogress flag"
+	 */
+
+	if (read(fd, &tmb, sizeof(tmb)) == -1) {
+		print_warning("error reading from file: %s", strerror(errno));
+		return 0;
+	}
+
+	if (be32_to_cpu(tmb.mb_magic) != XFS_MD_MAGIC) {
+		print_warning("specified file is not a metadata dump");
+		return 0;
+	}
+
+	bsize = 1 << tmb.mb_blocklog;
+	metablock = (xfs_metablock_t *)calloc(bsize + 1, bsize);
+	if (metablock == NULL) {
+		print_warning("memory allocation failure");
+		return 0;
+	}
+	metablock->mb_count = be16_to_cpu(tmb.mb_count);
+	metablock->mb_blocklog = tmb.mb_blocklog;
+
+	num_indicies = (bsize - sizeof(xfs_metablock_t)) / sizeof(__be64);
+
+	if (metablock->mb_count == 0 || metablock->mb_count > num_indicies) {
+		print_warning("bad block count: %u", metablock->mb_count);
+		return 0;
+	}
+
+	block_index = (__be64 *)((char *)metablock + sizeof(xfs_metablock_t));
+	block_buffer = (char *)metablock + bsize;
+
+	if (read(fd, block_index, bsize - sizeof(tmb)) == -1) {
+		print_warning("error reading from file: %s", strerror(errno));
+		return 0;
+	}
+
+	if (be64_to_cpu(block_index[0]) != XFS_AG_DADDR(mp, 0, XFS_SB_DADDR)) {
+		print_warning("first block is not the primary superblock");
+		return 0;
+	}
+
+	if (read(fd, block_buffer,
+			metablock->mb_count << metablock->mb_blocklog) == -1) {
+		print_warning("error reading from file: %s", strerror(errno));
+		return 0;
+	}
+
+	libxfs_xlate_sb(block_buffer, &sb, 1, XFS_SB_ALL_BITS);
+
+	if (sb.sb_magicnum != XFS_SB_MAGIC) {
+		print_warning("bad magic number for primary superblock");
+		return 0;
+	}
+
+	/* purge all cached data before writing to device */
+	libxfs_bcache_purge();
+	libxfs_icache_purge();
+
+	((xfs_sb_t*)block_buffer)->sb_inprogress = 1;
+	cur_index = 1 << (sb.sb_sectlog - BBSHIFT);
+
+	if (write_bbs(XFS_SB_DADDR, cur_index, block_buffer, NULL)) {
+		print_warning("error writing primary superblock: %s",
+				strerror(errno));
+		return 0;
+	}
+
+	bbpb = 1 << (metablock->mb_blocklog - BBSHIFT);
+	bbs_read = 0;
+	success = 0;
+
+
+	/* write the rest of the file out */
+	for (;;) {
+		if (show_progress &&  (bbs_read & 2047) == 0)
+			print_progress("%lld MB read", bbs_read >> 11);
+
+		for (; cur_index < metablock->mb_count; cur_index++) {
+			if (write_bbs(be64_to_cpu(block_index[cur_index]),
+					bbpb, &block_buffer[cur_index <<
+							metablock->mb_blocklog],
+					NULL)) {
+				print_warning("error writing block %llu: %s",
+					be64_to_cpu(block_index[cur_index]),
+						strerror(errno));
+				remount(&sb);
+				return 0;
+			}
+		}
+		if (metablock->mb_count < num_indicies) {
+			success = 1;
+			break;
+		}
+
+		if (read(fd, metablock, bsize) == -1) {
+			print_warning("error reading from file: %s",
+					strerror(errno));
+			break;
+		}
+		if (metablock->mb_count == 0) {
+			success = 1;
+			break;
+		}
+
+		metablock->mb_count = be16_to_cpu(metablock->mb_count);
+
+		if (read(fd, block_buffer, metablock->mb_count <<
+				metablock->mb_blocklog) == -1) {
+			print_warning("error reading from file: %s",
+					strerror(errno));
+			break;
+		}
+
+		bbs_read += bbpb;
+
+		cur_index = 0;
+		if (seenint())
+			break;
+	}
+
+	if (progress_since_warning)
+		fputc('\n', (fd == STDOUT_FILENO) ? stderr : stdout);
+
+	if (success) {
+		memset(block_buffer, 0, sb.sb_sectsize);
+		sb.sb_inprogress = 0;
+		libxfs_xlate_sb(block_buffer, &sb, 0, XFS_SB_ALL_BITS);
+		if (write_bbs(XFS_SB_DADDR, 1 << (sb.sb_sectlog - BBSHIFT),
+				block_buffer, NULL)) {
+			print_warning("error writing primary superblock: %s",
+					strerror(errno));
+			success = 0;
+		}
+	}
+	remount(&sb);
+
+	return success;
+}
+
+static int
+metarestore_f(
+	int 		argc,
+	char 		**argv)
+{
+	int		c;
+
+	exitcode = 1;
+	show_progress = 0;
+
+	while ((c = getopt(argc, argv, "g")) != EOF) {
+		switch (c) {
+			case 'g':
+				show_progress = 1;
+				break;
+			default:
+				print_warning("bad option for metarestore "
+						"command");
+				return 0;
+		}
+	}
+
+	if (optind != argc - 1) {
+		print_warning("too few options for metarestore (no filename "
+				"given)");
+		return 0;
+	}
+
+	if (strcmp(argv[optind], "-") == 0) {
+		if (isatty(STDIN_FILENO)) {
+			print_warning("cannot read from a terminal");
+			return 0;
+		}
+		fd = STDIN_FILENO;
+	} else {
+		fd = open(argv[optind], O_RDONLY, 0666);
+		if (fd == -1) {
+			print_warning("cannot open dump file");
+			return 0;
+		}
+	}
+
+	metablock = NULL;
+
+	exitcode = !perform_restore();
+
+	if (metablock != NULL)
+		free(metablock);
+
+	if (fd != STDIN_FILENO)
+		close(fd);
+
+	return 0;
+}
+

===========================================================================
xfsprogs/db/metadump.h
===========================================================================

--- a/xfsprogs/db/metadump.h	2007-02-13 17:29:33.000000000 +1100
+++ b/xfsprogs/db/metadump.h	2007-01-30 15:14:58.453299539 +1100
@@ -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	2007-02-13 17:29:33.000000000 +1100
+++ b/xfsprogs/db/xfs_metadump.sh	2007-02-13 16:05:27.003359183 +1100
@@ -0,0 +1,37 @@
+#!/bin/sh -f
+#
+# Copyright (c) 2007 Silicon Graphics, Inc.  All Rights Reserved.
+#
+
+OPTS=" "
+DBOPTS=" "
+USAGE="Usage: xfs_metadump [-efgwV] [-l logdev] source target"
+
+while getopts "efgl:wV" c
+do
+	case $c in
+	e)	OPTS=$OPTS"-e ";;
+	g)	OPTS=$OPTS"-g ";;
+	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
