Blame gfs2/edit/savemeta.c

Packit 6ef888
#include "clusterautoconfig.h"
Packit 6ef888
Packit 6ef888
#include <stdio.h>
Packit 6ef888
#include <stdlib.h>
Packit 6ef888
#include <ctype.h>
Packit 6ef888
#include <string.h>
Packit 6ef888
#include <stdint.h>
Packit 6ef888
#include <inttypes.h>
Packit 6ef888
#include <sys/types.h>
Packit 6ef888
#include <linux/types.h>
Packit 6ef888
#include <sys/stat.h>
Packit 6ef888
#include <fcntl.h>
Packit 6ef888
#include <unistd.h>
Packit 6ef888
#include <errno.h>
Packit 6ef888
#include <curses.h>
Packit 6ef888
#include <term.h>
Packit 6ef888
#include <sys/ioctl.h>
Packit 6ef888
#include <limits.h>
Packit 6ef888
#include <sys/time.h>
Packit 6ef888
#include <linux/gfs2_ondisk.h>
Packit 6ef888
#include <zlib.h>
Packit 6ef888
#include <time.h>
Packit 6ef888
Packit 6ef888
#include <logging.h>
Packit 6ef888
#include "osi_list.h"
Packit 6ef888
#include "gfs2hex.h"
Packit 6ef888
#include "hexedit.h"
Packit 6ef888
#include "libgfs2.h"
Packit 6ef888
Packit 6ef888
#define DFT_SAVE_FILE "/tmp/gfsmeta.XXXXXX"
Packit 6ef888
#define MAX_JOURNALS_SAVED 256
Packit 6ef888
Packit 6ef888
/* Header for the savemeta output file */
Packit 6ef888
struct savemeta_header {
Packit 6ef888
#define SAVEMETA_MAGIC (0x01171970)
Packit 6ef888
	uint32_t sh_magic;
Packit 6ef888
#define SAVEMETA_FORMAT (1)
Packit 6ef888
	uint32_t sh_format; /* In case we want to change the layout */
Packit 6ef888
	uint64_t sh_time; /* When savemeta was run */
Packit 6ef888
	uint64_t sh_fs_bytes; /* Size of the fs */
Packit 6ef888
	uint8_t __reserved[104];
Packit 6ef888
};
Packit 6ef888
Packit 6ef888
struct saved_metablock {
Packit 6ef888
	uint64_t blk;
Packit 6ef888
	uint16_t siglen; /* significant data length */
Packit 6ef888
/* This needs to be packed because old versions of gfs2_edit read and write the
Packit 6ef888
   individual fields separately, so the hole after siglen must be eradicated
Packit 6ef888
   before the struct reflects what's on disk. */
Packit 6ef888
} __attribute__((__packed__));
Packit 6ef888
Packit 6ef888
struct metafd {
Packit 6ef888
	int fd;
Packit 6ef888
	gzFile gzfd;
Packit 6ef888
	const char *filename;
Packit 6ef888
	int gziplevel;
Packit 6ef888
};
Packit 6ef888
Packit 6ef888
static uint64_t blks_saved;
Packit 6ef888
static uint64_t journal_blocks[MAX_JOURNALS_SAVED];
Packit 6ef888
static uint64_t gfs1_journal_size = 0; /* in blocks */
Packit 6ef888
static int journals_found = 0;
Packit 6ef888
int print_level = MSG_NOTICE;
Packit 6ef888
extern char *device;
Packit 6ef888
Packit 6ef888
static int block_is_a_journal(uint64_t blk)
Packit 6ef888
{
Packit 6ef888
	int j;
Packit 6ef888
Packit 6ef888
	for (j = 0; j < journals_found; j++)
Packit 6ef888
		if (blk == journal_blocks[j])
Packit 6ef888
			return TRUE;
Packit 6ef888
	return FALSE;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
struct osi_root per_node_tree;
Packit 6ef888
struct per_node_node {
Packit 6ef888
	struct osi_node node;
Packit 6ef888
	uint64_t block;
Packit 6ef888
};
Packit 6ef888
Packit 6ef888
static void destroy_per_node_lookup(void)
Packit 6ef888
{
Packit 6ef888
	struct osi_node *n;
Packit 6ef888
	struct per_node_node *pnp;
Packit 6ef888
Packit 6ef888
	while ((n = osi_first(&per_node_tree))) {
Packit 6ef888
		pnp = (struct per_node_node *)n;
Packit 6ef888
		osi_erase(n, &per_node_tree);
Packit 6ef888
		free(pnp);
Packit 6ef888
	}
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static int block_is_in_per_node(uint64_t blk)
Packit 6ef888
{
Packit 6ef888
	struct per_node_node *pnp = (struct per_node_node *)per_node_tree.osi_node;
Packit 6ef888
Packit 6ef888
	while (pnp) {
Packit 6ef888
		if (blk < pnp->block)
Packit 6ef888
			pnp = (struct per_node_node *)pnp->node.osi_left;
Packit 6ef888
		else if (blk > pnp->block)
Packit 6ef888
			pnp = (struct per_node_node *)pnp->node.osi_right;
Packit 6ef888
		else
Packit 6ef888
			return 1;
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	return 0;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static int insert_per_node_lookup(uint64_t blk)
Packit 6ef888
{
Packit 6ef888
	struct osi_node **newn = &per_node_tree.osi_node, *parent = NULL;
Packit 6ef888
	struct per_node_node *pnp;
Packit 6ef888
Packit 6ef888
	while (*newn) {
Packit 6ef888
		struct per_node_node *cur = (struct per_node_node *)*newn;
Packit 6ef888
Packit 6ef888
		parent = *newn;
Packit 6ef888
		if (blk < cur->block)
Packit 6ef888
			newn = &((*newn)->osi_left);
Packit 6ef888
		else if (blk > cur->block)
Packit 6ef888
			newn = &((*newn)->osi_right);
Packit 6ef888
		else
Packit 6ef888
			return 0;
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	pnp = calloc(1, sizeof(struct per_node_node));
Packit 6ef888
	if (pnp == NULL) {
Packit 6ef888
		perror("Failed to insert per_node lookup entry");
Packit 6ef888
		return 1;
Packit 6ef888
	}
Packit 6ef888
	pnp->block = blk;
Packit 6ef888
	osi_link_node(&pnp->node, parent, newn);
Packit 6ef888
	osi_insert_color(&pnp->node, &per_node_tree);
Packit 6ef888
	return 0;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static int init_per_node_lookup(void)
Packit 6ef888
{
Packit 6ef888
	int i;
Packit 6ef888
	struct gfs2_inode *per_node_di;
Packit 6ef888
Packit 6ef888
	if (sbd.gfs1)
Packit 6ef888
		return FALSE;
Packit 6ef888
Packit 6ef888
	per_node_di = lgfs2_inode_read(&sbd, masterblock("per_node"));
Packit 6ef888
	if (per_node_di == NULL) {
Packit 6ef888
		fprintf(stderr, "Failed to read per_node: %s\n", strerror(errno));
Packit 6ef888
		return 1;
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	do_dinode_extended(&per_node_di->i_di, per_node_di->i_bh);
Packit 6ef888
	inode_put(&per_node_di);
Packit 6ef888
Packit 6ef888
	for (i = 0; i < indirect_blocks; i++) {
Packit 6ef888
		int d;
Packit 6ef888
		for (d = 0; d < indirect->ii[i].dirents; d++) {
Packit 6ef888
			int ret = insert_per_node_lookup(indirect->ii[i].dirent[d].block);
Packit 6ef888
			if (ret != 0)
Packit 6ef888
				return ret;
Packit 6ef888
		}
Packit 6ef888
	}
Packit 6ef888
	return 0;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static int block_is_systemfile(uint64_t blk)
Packit 6ef888
{
Packit 6ef888
	return block_is_jindex(blk) || block_is_inum_file(blk) ||
Packit 6ef888
		block_is_statfs_file(blk) || block_is_quota_file(blk) ||
Packit 6ef888
		block_is_rindex(blk) || block_is_a_journal(blk) ||
Packit 6ef888
		block_is_per_node(blk) || block_is_in_per_node(blk);
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
/**
Packit 6ef888
 * anthropomorphize - make a uint64_t number more human
Packit 6ef888
 */
Packit 6ef888
static const char *anthropomorphize(unsigned long long inhuman_value)
Packit 6ef888
{
Packit 6ef888
	const char *symbols = " KMGTPE";
Packit 6ef888
	int i;
Packit 6ef888
	unsigned long long val = inhuman_value, remainder = 0;
Packit 6ef888
	static char out_val[32];
Packit 6ef888
Packit 6ef888
	memset(out_val, 0, sizeof(out_val));
Packit 6ef888
	for (i = 0; i < 6 && val > 1024; i++) {
Packit 6ef888
		remainder = val % 1024;
Packit 6ef888
		val /= 1024;
Packit 6ef888
	}
Packit 6ef888
	sprintf(out_val, "%llu.%llu%cB", val, remainder, symbols[i]);
Packit 6ef888
	return out_val;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static size_t di_save_len(struct gfs2_buffer_head *bh, uint64_t owner)
Packit 6ef888
{
Packit 6ef888
	struct gfs2_inode *inode;
Packit 6ef888
	struct gfs2_dinode *dn;
Packit 6ef888
	size_t len;
Packit 6ef888
Packit 6ef888
	if (sbd.gfs1)
Packit 6ef888
		inode = lgfs2_gfs_inode_get(&sbd, bh);
Packit 6ef888
	else
Packit 6ef888
		inode = lgfs2_inode_get(&sbd, bh);
Packit 6ef888
Packit 6ef888
	if (inode == NULL) {
Packit 6ef888
		fprintf(stderr, "Error reading inode at %"PRIu64": %s\n",
Packit 6ef888
		        bh->b_blocknr, strerror(errno));
Packit 6ef888
		return 0; /* Skip the block */
Packit 6ef888
	}
Packit 6ef888
	dn = &inode->i_di;
Packit 6ef888
	len = sizeof(struct gfs2_dinode);
Packit 6ef888
Packit 6ef888
	/* Do not save (user) data from the inode block unless they are
Packit 6ef888
	   indirect pointers, dirents, symlinks or fs internal data */
Packit 6ef888
	if (dn->di_height != 0 ||
Packit 6ef888
	    S_ISDIR(dn->di_mode) ||
Packit 6ef888
	    S_ISLNK(dn->di_mode) ||
Packit 6ef888
	    (sbd.gfs1 && dn->__pad1 == GFS_FILE_DIR) ||
Packit 6ef888
	    block_is_systemfile(owner))
Packit 6ef888
		len = sbd.bsize;
Packit 6ef888
Packit 6ef888
	inode_put(&inode;;
Packit 6ef888
	return len;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
/*
Packit 6ef888
 * get_gfs_struct_info - get block type and structure length
Packit 6ef888
 *
Packit 6ef888
 * @lbh - The block buffer to examine
Packit 6ef888
 * @owner - The block address of the parent structure
Packit 6ef888
 * @block_type - pointer to integer to hold the block type
Packit 6ef888
 * @gstruct_len - pointer to integer to hold the structure length
Packit 6ef888
 *
Packit 6ef888
 * returns: 0 if successful
Packit 6ef888
 *          -1 if this isn't gfs metadata.
Packit 6ef888
 */
Packit 6ef888
static int get_gfs_struct_info(struct gfs2_buffer_head *lbh, uint64_t owner,
Packit 6ef888
                               int *block_type, size_t *gstruct_len)
Packit 6ef888
{
Packit 6ef888
	struct gfs2_meta_header mh;
Packit 6ef888
Packit 6ef888
	if (block_type != NULL)
Packit 6ef888
		*block_type = 0;
Packit 6ef888
	*gstruct_len = sbd.bsize;
Packit 6ef888
Packit 6ef888
	gfs2_meta_header_in(&mh, lbh->b_data);
Packit 6ef888
	if (mh.mh_magic != GFS2_MAGIC)
Packit 6ef888
		return -1;
Packit 6ef888
Packit 6ef888
	if (block_type != NULL)
Packit 6ef888
		*block_type = mh.mh_type;
Packit 6ef888
Packit 6ef888
	switch (mh.mh_type) {
Packit 6ef888
	case GFS2_METATYPE_SB:   /* 1 (superblock) */
Packit 6ef888
		*gstruct_len = sizeof(struct gfs_sb);
Packit 6ef888
		break;
Packit 6ef888
	case GFS2_METATYPE_RG:   /* 2 (rsrc grp hdr) */
Packit 6ef888
		*gstruct_len = sbd.bsize; /*sizeof(struct gfs_rgrp);*/
Packit 6ef888
		break;
Packit 6ef888
	case GFS2_METATYPE_RB:   /* 3 (rsrc grp bitblk) */
Packit 6ef888
		*gstruct_len = sbd.bsize;
Packit 6ef888
		break;
Packit 6ef888
	case GFS2_METATYPE_DI:   /* 4 (disk inode) */
Packit 6ef888
		*gstruct_len = di_save_len(lbh, owner);
Packit 6ef888
		break;
Packit 6ef888
	case GFS2_METATYPE_IN:   /* 5 (indir inode blklst) */
Packit 6ef888
		*gstruct_len = sbd.bsize; /*sizeof(struct gfs_indirect);*/
Packit 6ef888
		break;
Packit 6ef888
	case GFS2_METATYPE_LF:   /* 6 (leaf dinode blklst) */
Packit 6ef888
		*gstruct_len = sbd.bsize; /*sizeof(struct gfs_leaf);*/
Packit 6ef888
		break;
Packit 6ef888
	case GFS2_METATYPE_JD:   /* 7 (journal data) */
Packit 6ef888
		*gstruct_len = sbd.bsize;
Packit 6ef888
		break;
Packit 6ef888
	case GFS2_METATYPE_LH:   /* 8 (log header) */
Packit 6ef888
		if (sbd.gfs1)
Packit 6ef888
			*gstruct_len = 512; /* gfs copies the log header
Packit 6ef888
					       twice and compares the copy,
Packit 6ef888
					       so we need to save all 512
Packit 6ef888
					       bytes of it. */
Packit 6ef888
		else
Packit 6ef888
			*gstruct_len = sizeof(struct gfs2_log_header);
Packit 6ef888
		break;
Packit 6ef888
	case GFS2_METATYPE_LD:   /* 9 (log descriptor) */
Packit 6ef888
		*gstruct_len = sbd.bsize;
Packit 6ef888
		break;
Packit 6ef888
	case GFS2_METATYPE_EA:   /* 10 (extended attr hdr) */
Packit 6ef888
		*gstruct_len = sbd.bsize;
Packit 6ef888
		break;
Packit 6ef888
	case GFS2_METATYPE_ED:   /* 11 (extended attr data) */
Packit 6ef888
		*gstruct_len = sbd.bsize;
Packit 6ef888
		break;
Packit 6ef888
	default:
Packit 6ef888
		*gstruct_len = sbd.bsize;
Packit 6ef888
		break;
Packit 6ef888
	}
Packit 6ef888
	return 0;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
/* Put out a warm, fuzzy message every second so the user     */
Packit 6ef888
/* doesn't think we hung.  (This may take a long time).       */
Packit 6ef888
/* We only check whether to report every one percent because  */
Packit 6ef888
/* checking every block kills performance.  We only report    */
Packit 6ef888
/* every second because we don't need 100 extra messages in   */
Packit 6ef888
/* logs made from verbose mode.                               */
Packit 6ef888
static void warm_fuzzy_stuff(uint64_t wfsblock, int force)
Packit 6ef888
{
Packit 6ef888
        static struct timeval tv;
Packit 6ef888
        static uint32_t seconds = 0;
Packit 6ef888
Packit 6ef888
	gettimeofday(&tv, NULL);
Packit 6ef888
	if (!seconds)
Packit 6ef888
		seconds = tv.tv_sec;
Packit 6ef888
	if (force || tv.tv_sec - seconds) {
Packit 6ef888
		static uint64_t percent;
Packit 6ef888
Packit 6ef888
		seconds = tv.tv_sec;
Packit 6ef888
		if (sbd.fssize) {
Packit 6ef888
			printf("\r");
Packit 6ef888
			percent = (wfsblock * 100) / sbd.fssize;
Packit 6ef888
			printf("%llu blocks processed, %llu saved (%llu%%)",
Packit 6ef888
			       (unsigned long long)wfsblock,
Packit 6ef888
			       (unsigned long long)blks_saved,
Packit 6ef888
			       (unsigned long long)percent);
Packit 6ef888
			if (force)
Packit 6ef888
				printf("\n");
Packit 6ef888
			fflush(stdout);
Packit 6ef888
		}
Packit 6ef888
	}
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
/**
Packit 6ef888
 * Open a file and prepare it for writing by savemeta()
Packit 6ef888
 * out_fn: the path to the file, which will be truncated if it exists
Packit 6ef888
 * gziplevel: 0   - do not compress the file,
Packit 6ef888
 *            1-9 - use gzip compression level 1-9
Packit 6ef888
 * Returns a struct metafd containing the opened file descriptor
Packit 6ef888
 */
Packit 6ef888
static struct metafd savemetaopen(char *out_fn, int gziplevel)
Packit 6ef888
{
Packit 6ef888
	struct metafd mfd = {-1, NULL, NULL, gziplevel};
Packit 6ef888
	char gzmode[3] = "w9";
Packit 6ef888
	char dft_fn[] = DFT_SAVE_FILE;
Packit 6ef888
	mode_t mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
Packit 6ef888
Packit 6ef888
	if (!out_fn) {
Packit 6ef888
		out_fn = dft_fn;
Packit 6ef888
		mfd.fd = mkstemp(out_fn);
Packit 6ef888
	} else {
Packit 6ef888
		mfd.fd = open(out_fn, O_RDWR | O_CREAT, 0644);
Packit 6ef888
	}
Packit 6ef888
	umask(mask);
Packit 6ef888
	mfd.filename = out_fn;
Packit 6ef888
Packit 6ef888
	if (mfd.fd < 0) {
Packit 6ef888
		fprintf(stderr, "Can't open %s: %s\n", out_fn, strerror(errno));
Packit 6ef888
		exit(1);
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	if (ftruncate(mfd.fd, 0)) {
Packit 6ef888
		fprintf(stderr, "Can't truncate %s: %s\n", out_fn, strerror(errno));
Packit 6ef888
		exit(1);
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	if (gziplevel > 0) {
Packit 6ef888
		gzmode[1] = '0' + gziplevel;
Packit 6ef888
		mfd.gzfd = gzdopen(mfd.fd, gzmode);
Packit 6ef888
		if (!mfd.gzfd) {
Packit 6ef888
			fprintf(stderr, "gzdopen error: %s\n", strerror(errno));
Packit 6ef888
			exit(1);
Packit 6ef888
		}
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	return mfd;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
/**
Packit 6ef888
 * Write nbyte bytes from buf to a file opened with savemetaopen()
Packit 6ef888
 * mfd: the file descriptor opened using savemetaopen()
Packit 6ef888
 * buf: the buffer to write data from
Packit 6ef888
 * nbyte: the number of bytes to write
Packit 6ef888
 * Returns the number of bytes written from buf or -1 on error
Packit 6ef888
 */
Packit 6ef888
static ssize_t savemetawrite(struct metafd *mfd, const void *buf, size_t nbyte)
Packit 6ef888
{
Packit 6ef888
	ssize_t ret;
Packit 6ef888
	int gzerr;
Packit 6ef888
	const char *gzerrmsg;
Packit 6ef888
Packit 6ef888
	if (mfd->gziplevel == 0) {
Packit 6ef888
		return write(mfd->fd, buf, nbyte);
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	ret = gzwrite(mfd->gzfd, buf, nbyte);
Packit 6ef888
	if (ret != nbyte) {
Packit 6ef888
		gzerrmsg = gzerror(mfd->gzfd, &gzerr);
Packit 6ef888
		if (gzerr != Z_ERRNO) {
Packit 6ef888
			fprintf(stderr, "Error: zlib: %s\n", gzerrmsg);
Packit 6ef888
		}
Packit 6ef888
	}
Packit 6ef888
	return ret;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
/**
Packit 6ef888
 * Closes a file descriptor previously opened using savemetaopen()
Packit 6ef888
 * mfd: the file descriptor previously opened using savemetaopen()
Packit 6ef888
 * Returns 0 on success or -1 on error
Packit 6ef888
 */
Packit 6ef888
static int savemetaclose(struct metafd *mfd)
Packit 6ef888
{
Packit 6ef888
	int gzret;
Packit 6ef888
	if (mfd->gziplevel > 0) {
Packit 6ef888
		gzret = gzclose(mfd->gzfd);
Packit 6ef888
		if (gzret == Z_STREAM_ERROR) {
Packit 6ef888
			fprintf(stderr, "gzclose: file is not valid\n");
Packit 6ef888
			return -1;
Packit 6ef888
		} else if (gzret == Z_ERRNO) {
Packit 6ef888
			return -1;
Packit 6ef888
		}
Packit 6ef888
	}
Packit 6ef888
	return close(mfd->fd);
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static int save_bh(struct metafd *mfd, struct gfs2_buffer_head *savebh, uint64_t owner, int *blktype)
Packit 6ef888
{
Packit 6ef888
	struct saved_metablock *savedata;
Packit 6ef888
	size_t blklen;
Packit 6ef888
	size_t outsz;
Packit 6ef888
Packit 6ef888
	/* If this isn't metadata and isn't a system file, we don't want it.
Packit 6ef888
	   Note that we're checking "owner" here rather than blk.  That's
Packit 6ef888
	   because we want to know if the source inode is a system inode
Packit 6ef888
	   not the block within the inode "blk". They may or may not
Packit 6ef888
	   be the same thing. */
Packit 6ef888
	if (get_gfs_struct_info(savebh, owner, blktype, &blklen) &&
Packit 6ef888
	    !block_is_systemfile(owner) && owner != 0)
Packit 6ef888
		return 0; /* Not metadata, and not system file, so skip it */
Packit 6ef888
Packit 6ef888
	/* No need to save trailing zeroes */
Packit 6ef888
	for (; blklen > 0 && savebh->b_data[blklen - 1] == '\0'; blklen--);
Packit 6ef888
Packit 6ef888
	if (blklen == 0) /* No significant data; skip. */
Packit 6ef888
		return 0;
Packit 6ef888
Packit 6ef888
	outsz = sizeof(*savedata) + blklen;
Packit 6ef888
	savedata = calloc(1, outsz);
Packit 6ef888
	if (savedata == NULL) {
Packit 6ef888
		perror("Failed to save block");
Packit 6ef888
		exit(1);
Packit 6ef888
	}
Packit 6ef888
	savedata->blk = cpu_to_be64(savebh->b_blocknr);
Packit 6ef888
	savedata->siglen = cpu_to_be16(blklen);
Packit 6ef888
	memcpy(savedata + 1, savebh->b_data, blklen);
Packit 6ef888
Packit 6ef888
	if (savemetawrite(mfd, savedata, outsz) != outsz) {
Packit 6ef888
		fprintf(stderr, "write error: %s from %s:%d: block %lld (0x%llx)\n",
Packit 6ef888
		        strerror(errno), __FUNCTION__, __LINE__,
Packit 6ef888
			(unsigned long long)savedata->blk,
Packit 6ef888
			(unsigned long long)savedata->blk);
Packit 6ef888
		free(savedata);
Packit 6ef888
		exit(-1);
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	blks_saved++;
Packit 6ef888
	free(savedata);
Packit 6ef888
	return 0;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static int save_block(int fd, struct metafd *mfd, uint64_t blk, uint64_t owner, int *blktype)
Packit 6ef888
{
Packit 6ef888
	struct gfs2_buffer_head *savebh;
Packit 6ef888
	int err;
Packit 6ef888
Packit 6ef888
	if (gfs2_check_range(&sbd, blk) && blk != LGFS2_SB_ADDR(&sbd)) {
Packit 6ef888
		fprintf(stderr, "\nWarning: bad block pointer '0x%llx' "
Packit 6ef888
			"ignored in block (block %llu (0x%llx))",
Packit 6ef888
			(unsigned long long)blk,
Packit 6ef888
			(unsigned long long)owner, (unsigned long long)owner);
Packit 6ef888
		return 0;
Packit 6ef888
	}
Packit 6ef888
	savebh = bread(&sbd, blk);
Packit 6ef888
	if (savebh == NULL)
Packit 6ef888
		return 1;
Packit 6ef888
	err = save_bh(mfd, savebh, owner, blktype);
Packit 6ef888
	brelse(savebh);
Packit 6ef888
	return err;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
/*
Packit 6ef888
 * save_ea_block - save off an extended attribute block
Packit 6ef888
 */
Packit 6ef888
static void save_ea_block(struct metafd *mfd, struct gfs2_buffer_head *metabh, uint64_t owner)
Packit 6ef888
{
Packit 6ef888
	int e;
Packit 6ef888
	struct gfs2_ea_header ea;
Packit 6ef888
Packit 6ef888
	for (e = sizeof(struct gfs2_meta_header); e < sbd.bsize; e += ea.ea_rec_len) {
Packit 6ef888
		uint64_t blk, *b;
Packit 6ef888
		int charoff, i;
Packit 6ef888
Packit 6ef888
		gfs2_ea_header_in(&ea, metabh->b_data + e);
Packit 6ef888
		for (i = 0; i < ea.ea_num_ptrs; i++) {
Packit 6ef888
			charoff = e + ea.ea_name_len +
Packit 6ef888
				sizeof(struct gfs2_ea_header) +
Packit 6ef888
				sizeof(uint64_t) - 1;
Packit 6ef888
			charoff /= sizeof(uint64_t);
Packit 6ef888
			b = (uint64_t *)(metabh->b_data);
Packit 6ef888
			b += charoff + i;
Packit 6ef888
			blk = be64_to_cpu(*b);
Packit 6ef888
			save_block(sbd.device_fd, mfd, blk, owner, NULL);
Packit 6ef888
		}
Packit 6ef888
		if (!ea.ea_rec_len)
Packit 6ef888
			break;
Packit 6ef888
	}
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
/*
Packit 6ef888
 * save_indirect_blocks - save all indirect blocks for the given buffer
Packit 6ef888
 */
Packit 6ef888
static void save_indirect_blocks(struct metafd *mfd, osi_list_t *cur_list,
Packit 6ef888
           struct gfs2_buffer_head *mybh, uint64_t owner, int height, int hgt)
Packit 6ef888
{
Packit 6ef888
	uint64_t old_block = 0, indir_block;
Packit 6ef888
	uint64_t *ptr;
Packit 6ef888
	int head_size, blktype;
Packit 6ef888
	struct gfs2_buffer_head *nbh;
Packit 6ef888
Packit 6ef888
	head_size = (hgt > 1 ?
Packit 6ef888
		     sizeof(struct gfs2_meta_header) :
Packit 6ef888
		     sizeof(struct gfs2_dinode));
Packit 6ef888
Packit 6ef888
	for (ptr = (uint64_t *)(mybh->b_data + head_size);
Packit 6ef888
	     (char *)ptr < (mybh->b_data + sbd.bsize); ptr++) {
Packit 6ef888
		if (!*ptr)
Packit 6ef888
			continue;
Packit 6ef888
		indir_block = be64_to_cpu(*ptr);
Packit 6ef888
		if (indir_block == old_block)
Packit 6ef888
			continue;
Packit 6ef888
		old_block = indir_block;
Packit 6ef888
		save_block(sbd.device_fd, mfd, indir_block, owner, &blktype);
Packit 6ef888
		if (blktype == GFS2_METATYPE_EA) {
Packit 6ef888
			nbh = bread(&sbd, indir_block);
Packit 6ef888
			save_ea_block(mfd, nbh, owner);
Packit 6ef888
			brelse(nbh);
Packit 6ef888
		}
Packit 6ef888
		if (height != hgt && /* If not at max height and */
Packit 6ef888
		    (!gfs2_check_range(&sbd, indir_block))) {
Packit 6ef888
			nbh = bread(&sbd, indir_block);
Packit 6ef888
			osi_list_add_prev(&nbh->b_altlist, cur_list);
Packit 6ef888
			/* The buffer_head needs to be queued ahead, so
Packit 6ef888
			   don't release it!
Packit 6ef888
			   brelse(nbh);*/
Packit 6ef888
		}
Packit 6ef888
	} /* for all data on the indirect block */
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static int save_leaf_chain(struct metafd *mfd, struct gfs2_sbd *sdp, uint64_t blk)
Packit 6ef888
{
Packit 6ef888
	struct gfs2_buffer_head *bh;
Packit 6ef888
	struct gfs2_leaf leaf;
Packit 6ef888
Packit 6ef888
	do {
Packit 6ef888
		if (gfs2_check_range(sdp, blk) != 0)
Packit 6ef888
			return 0;
Packit 6ef888
		bh = bread(sdp, blk);
Packit 6ef888
		if (bh == NULL) {
Packit 6ef888
			perror("Failed to read leaf block");
Packit 6ef888
			return 1;
Packit 6ef888
		}
Packit 6ef888
		warm_fuzzy_stuff(blk, FALSE);
Packit 6ef888
		if (gfs2_check_meta(bh, GFS2_METATYPE_LF) == 0) {
Packit 6ef888
			int ret = save_bh(mfd, bh, blk, NULL);
Packit 6ef888
			if (ret != 0) {
Packit 6ef888
				brelse(bh);
Packit 6ef888
				return ret;
Packit 6ef888
			}
Packit 6ef888
		}
Packit 6ef888
		gfs2_leaf_in(&leaf, bh->b_data);
Packit 6ef888
		brelse(bh);
Packit 6ef888
		blk = leaf.lf_next;
Packit 6ef888
	} while (leaf.lf_next != 0);
Packit 6ef888
Packit 6ef888
	return 0;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
/*
Packit 6ef888
 * save_inode_data - save off important data associated with an inode
Packit 6ef888
 *
Packit 6ef888
 * mfd - destination file descriptor
Packit 6ef888
 * iblk - block number of the inode to save the data for
Packit 6ef888
 *
Packit 6ef888
 * For user files, we don't want anything except all the indirect block
Packit 6ef888
 * pointers that reside on blocks on all but the highest height.
Packit 6ef888
 *
Packit 6ef888
 * For system files like statfs and inum, we want everything because they
Packit 6ef888
 * may contain important clues and no user data.
Packit 6ef888
 *
Packit 6ef888
 * For file system journals, the "data" is a mixture of metadata and
Packit 6ef888
 * journaled data.  We want all the metadata and none of the user data.
Packit 6ef888
 */
Packit 6ef888
static void save_inode_data(struct metafd *mfd, uint64_t iblk)
Packit 6ef888
{
Packit 6ef888
	uint32_t height;
Packit 6ef888
	struct gfs2_inode *inode;
Packit 6ef888
	osi_list_t metalist[GFS2_MAX_META_HEIGHT];
Packit 6ef888
	osi_list_t *prev_list, *cur_list, *tmp;
Packit 6ef888
	struct gfs2_buffer_head *metabh, *mybh;
Packit 6ef888
	int i;
Packit 6ef888
Packit 6ef888
	for (i = 0; i < GFS2_MAX_META_HEIGHT; i++)
Packit 6ef888
		osi_list_init(&metalist[i]);
Packit 6ef888
	metabh = bread(&sbd, iblk);
Packit 6ef888
	if (sbd.gfs1) {
Packit 6ef888
		inode = lgfs2_gfs_inode_get(&sbd, metabh);
Packit 6ef888
	} else {
Packit 6ef888
		inode = lgfs2_inode_get(&sbd, metabh);
Packit 6ef888
	}
Packit 6ef888
	if (inode == NULL) {
Packit 6ef888
		perror("Failed to read inode");
Packit 6ef888
		exit(-1);
Packit 6ef888
	}
Packit 6ef888
	height = inode->i_di.di_height;
Packit 6ef888
	/* If this is a user inode, we don't follow to the file height.
Packit 6ef888
	   We stop one level less.  That way we save off the indirect
Packit 6ef888
	   pointer blocks but not the actual file contents. The exception
Packit 6ef888
	   is directories, where the height represents the level at which
Packit 6ef888
	   the hash table exists, and we have to save the directory data. */
Packit 6ef888
	if (inode->i_di.di_flags & GFS2_DIF_EXHASH &&
Packit 6ef888
	    (S_ISDIR(inode->i_di.di_mode) ||
Packit 6ef888
	     (sbd.gfs1 && inode->i_di.__pad1 == GFS_FILE_DIR)))
Packit 6ef888
		height++;
Packit 6ef888
	else if (height && !(inode->i_di.di_flags & GFS2_DIF_SYSTEM) &&
Packit 6ef888
		 !block_is_systemfile(iblk) && !S_ISDIR(inode->i_di.di_mode))
Packit 6ef888
		height--;
Packit 6ef888
	osi_list_add(&metabh->b_altlist, &metalist[0]);
Packit 6ef888
        for (i = 1; i <= height; i++){
Packit 6ef888
		prev_list = &metalist[i - 1];
Packit 6ef888
		cur_list = &metalist[i];
Packit 6ef888
Packit 6ef888
		for (tmp = prev_list->next; tmp != prev_list; tmp = tmp->next){
Packit 6ef888
			mybh = osi_list_entry(tmp, struct gfs2_buffer_head,
Packit 6ef888
					      b_altlist);
Packit 6ef888
			warm_fuzzy_stuff(iblk, FALSE);
Packit 6ef888
			save_indirect_blocks(mfd, cur_list, mybh, iblk,
Packit 6ef888
					     height, i);
Packit 6ef888
		} /* for blocks at that height */
Packit 6ef888
	} /* for height */
Packit 6ef888
	/* free metalists */
Packit 6ef888
	for (i = 0; i < GFS2_MAX_META_HEIGHT; i++) {
Packit 6ef888
		cur_list = &metalist[i];
Packit 6ef888
		while (!osi_list_empty(cur_list)) {
Packit 6ef888
			mybh = osi_list_entry(cur_list->next,
Packit 6ef888
					    struct gfs2_buffer_head,
Packit 6ef888
					    b_altlist);
Packit 6ef888
			if (mybh == inode->i_bh)
Packit 6ef888
				osi_list_del(&mybh->b_altlist);
Packit 6ef888
			else
Packit 6ef888
				brelse(mybh);
Packit 6ef888
		}
Packit 6ef888
	}
Packit 6ef888
	/* Process directory exhash inodes */
Packit 6ef888
	if (S_ISDIR(inode->i_di.di_mode) &&
Packit 6ef888
	    inode->i_di.di_flags & GFS2_DIF_EXHASH) {
Packit 6ef888
		uint64_t  leaf_no, old_leaf = -1;
Packit 6ef888
		int li;
Packit 6ef888
Packit 6ef888
		for (li = 0; li < (1 << inode->i_di.di_depth); li++) {
Packit 6ef888
			if (lgfs2_get_leaf_ptr(inode, li, &leaf_no)) {
Packit 6ef888
				fprintf(stderr, "Could not read leaf index %d in dinode %"PRIu64"\n", li,
Packit 6ef888
				        (uint64_t)inode->i_di.di_num.no_addr);
Packit 6ef888
				exit(-1);
Packit 6ef888
			}
Packit 6ef888
			if (leaf_no != old_leaf && save_leaf_chain(mfd, &sbd, leaf_no) != 0)
Packit 6ef888
				exit(-1);
Packit 6ef888
			old_leaf = leaf_no;
Packit 6ef888
		}
Packit 6ef888
	}
Packit 6ef888
	if (inode->i_di.di_eattr) { /* if this inode has extended attributes */
Packit 6ef888
		struct gfs2_meta_header mh;
Packit 6ef888
		struct gfs2_buffer_head *lbh;
Packit 6ef888
Packit 6ef888
		lbh = bread(&sbd, inode->i_di.di_eattr);
Packit 6ef888
		save_block(sbd.device_fd, mfd, inode->i_di.di_eattr, iblk, NULL);
Packit 6ef888
		gfs2_meta_header_in(&mh, lbh->b_data);
Packit 6ef888
		if (mh.mh_magic == GFS2_MAGIC &&
Packit 6ef888
		    mh.mh_type == GFS2_METATYPE_EA)
Packit 6ef888
			save_ea_block(mfd, lbh, iblk);
Packit 6ef888
		else if (mh.mh_magic == GFS2_MAGIC &&
Packit 6ef888
			 mh.mh_type == GFS2_METATYPE_IN)
Packit 6ef888
			save_indirect_blocks(mfd, cur_list, lbh, iblk, 2, 2);
Packit 6ef888
		else {
Packit 6ef888
			if (mh.mh_magic == GFS2_MAGIC) /* if it's metadata */
Packit 6ef888
				save_block(sbd.device_fd, mfd, inode->i_di.di_eattr,
Packit 6ef888
				           iblk, NULL);
Packit 6ef888
			fprintf(stderr,
Packit 6ef888
				"\nWarning: corrupt extended "
Packit 6ef888
				"attribute at block %llu (0x%llx) "
Packit 6ef888
				"detected in inode %lld (0x%llx).\n",
Packit 6ef888
				(unsigned long long)inode->i_di.di_eattr,
Packit 6ef888
				(unsigned long long)inode->i_di.di_eattr,
Packit 6ef888
				(unsigned long long)iblk,
Packit 6ef888
				(unsigned long long)iblk);
Packit 6ef888
		}
Packit 6ef888
		brelse(lbh);
Packit 6ef888
	}
Packit 6ef888
	inode_put(&inode;;
Packit 6ef888
	brelse(metabh);
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static void get_journal_inode_blocks(void)
Packit 6ef888
{
Packit 6ef888
	int journal;
Packit 6ef888
Packit 6ef888
	journals_found = 0;
Packit 6ef888
	memset(journal_blocks, 0, sizeof(journal_blocks));
Packit 6ef888
	/* Save off all the journals--but only the metadata.
Packit 6ef888
	 * This is confusing so I'll explain.  The journals contain important 
Packit 6ef888
	 * metadata.  However, in gfs2 the journals are regular files within
Packit 6ef888
	 * the system directory.  Since they're regular files, the blocks
Packit 6ef888
	 * within the journals are considered data, not metadata.  Therefore,
Packit 6ef888
	 * they won't have been saved by the code above.  We want to dump
Packit 6ef888
	 * these blocks, but we have to be careful.  We only care about the
Packit 6ef888
	 * journal blocks that look like metadata, and we need to not save
Packit 6ef888
	 * journaled user data that may exist there as well. */
Packit 6ef888
	for (journal = 0; ; journal++) { /* while journals exist */
Packit 6ef888
		uint64_t jblock;
Packit 6ef888
		int amt;
Packit 6ef888
		struct gfs2_inode *j_inode = NULL;
Packit 6ef888
Packit 6ef888
		if (sbd.gfs1) {
Packit 6ef888
			struct gfs_jindex ji;
Packit 6ef888
			char jbuf[sizeof(struct gfs_jindex)];
Packit 6ef888
Packit 6ef888
			j_inode = lgfs2_gfs_inode_read(&sbd,
Packit 6ef888
						 sbd1->sb_jindex_di.no_addr);
Packit 6ef888
			if (j_inode == NULL) {
Packit 6ef888
				fprintf(stderr, "Error reading journal inode: %s\n", strerror(errno));
Packit 6ef888
				return;
Packit 6ef888
			}
Packit 6ef888
			amt = gfs2_readi(j_inode, (void *)&jbuf,
Packit 6ef888
					 journal * sizeof(struct gfs_jindex),
Packit 6ef888
					 sizeof(struct gfs_jindex));
Packit 6ef888
			inode_put(&j_inode);
Packit 6ef888
			if (!amt)
Packit 6ef888
				break;
Packit 6ef888
			gfs_jindex_in(&ji, jbuf);
Packit 6ef888
			jblock = ji.ji_addr;
Packit 6ef888
			gfs1_journal_size = (uint64_t)ji.ji_nsegment * 16;
Packit 6ef888
		} else {
Packit 6ef888
			if (journal > indirect->ii[0].dirents - 3)
Packit 6ef888
				break;
Packit 6ef888
			jblock = indirect->ii[0].dirent[journal + 2].block;
Packit 6ef888
		}
Packit 6ef888
		journal_blocks[journals_found++] = jblock;
Packit 6ef888
	}
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static void save_allocated(struct rgrp_tree *rgd, struct metafd *mfd)
Packit 6ef888
{
Packit 6ef888
	int blktype;
Packit 6ef888
	uint64_t blk = 0;
Packit 6ef888
	unsigned i, j, m;
Packit 6ef888
	uint64_t *ibuf = malloc(sbd.bsize * GFS2_NBBY * sizeof(uint64_t));
Packit 6ef888
Packit 6ef888
	for (i = 0; i < rgd->ri.ri_length; i++) {
Packit 6ef888
		m = lgfs2_bm_scan(rgd, i, ibuf, GFS2_BLKST_DINODE);
Packit 6ef888
Packit 6ef888
		for (j = 0; j < m; j++) {
Packit 6ef888
			blk = ibuf[j];
Packit 6ef888
			warm_fuzzy_stuff(blk, FALSE);
Packit 6ef888
			save_block(sbd.device_fd, mfd, blk, blk, &blktype);
Packit 6ef888
			if (blktype == GFS2_METATYPE_DI)
Packit 6ef888
				save_inode_data(mfd, blk);
Packit 6ef888
		}
Packit 6ef888
Packit 6ef888
		if (!sbd.gfs1)
Packit 6ef888
			continue;
Packit 6ef888
Packit 6ef888
		/* For gfs1, Save off the free/unlinked meta blocks too.
Packit 6ef888
		 * If we don't, we may run into metadata allocation issues. */
Packit 6ef888
		m = lgfs2_bm_scan(rgd, i, ibuf, GFS2_BLKST_UNLINKED);
Packit 6ef888
		for (j = 0; j < m; j++) {
Packit 6ef888
			save_block(sbd.device_fd, mfd, blk, blk, NULL);
Packit 6ef888
		}
Packit 6ef888
	}
Packit 6ef888
	free(ibuf);
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
/* We don't use gfs2_rgrp_read() here as it checks for metadata sanity and we
Packit 6ef888
   want to save rgrp headers even if they're corrupt. */
Packit 6ef888
static int rgrp_read(struct gfs2_sbd *sdp, struct rgrp_tree *rgd)
Packit 6ef888
{
Packit 6ef888
	unsigned x, length = rgd->ri.ri_length;
Packit 6ef888
	struct gfs2_buffer_head **bhs;
Packit 6ef888
Packit 6ef888
	if (length == 0 || gfs2_check_range(sdp, rgd->ri.ri_addr))
Packit 6ef888
		return -1;
Packit 6ef888
Packit 6ef888
	bhs = calloc(length, sizeof(struct gfs2_buffer_head *));
Packit 6ef888
	if (bhs == NULL)
Packit 6ef888
		return -1;
Packit 6ef888
Packit 6ef888
	if (breadm(sdp, bhs, length, rgd->ri.ri_addr)) {
Packit 6ef888
		free(bhs);
Packit 6ef888
		return -1;
Packit 6ef888
	}
Packit 6ef888
	for (x = 0; x < length; x++)
Packit 6ef888
		rgd->bits[x].bi_bh = bhs[x];
Packit 6ef888
Packit 6ef888
	if (sdp->gfs1)
Packit 6ef888
		gfs_rgrp_in((struct gfs_rgrp *)&rgd->rg, rgd->bits[0].bi_bh);
Packit 6ef888
	else
Packit 6ef888
		gfs2_rgrp_in(&rgd->rg, rgd->bits[0].bi_bh->b_data);
Packit 6ef888
	free(bhs);
Packit 6ef888
	return 0;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static void save_rgrp(struct metafd *mfd, struct rgrp_tree *rgd, int withcontents)
Packit 6ef888
{
Packit 6ef888
	uint64_t addr = rgd->ri.ri_addr;
Packit 6ef888
	uint32_t i;
Packit 6ef888
Packit 6ef888
	if (rgrp_read(&sbd, rgd))
Packit 6ef888
		return;
Packit 6ef888
	log_debug("RG at %"PRIu64" (0x%"PRIx64") is %u long\n",
Packit 6ef888
		  addr, addr, rgd->ri.ri_length);
Packit 6ef888
	/* Save the rg and bitmaps */
Packit 6ef888
	for (i = 0; i < rgd->ri.ri_length; i++) {
Packit 6ef888
		warm_fuzzy_stuff(rgd->ri.ri_addr + i, FALSE);
Packit 6ef888
		save_bh(mfd, rgd->bits[i].bi_bh, 0, NULL);
Packit 6ef888
	}
Packit 6ef888
	/* Save the other metadata: inodes, etc. if mode is not 'savergs' */
Packit 6ef888
	if (withcontents)
Packit 6ef888
		save_allocated(rgd, mfd);
Packit 6ef888
	gfs2_rgrp_relse(rgd);
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static int save_header(struct metafd *mfd, uint64_t fsbytes)
Packit 6ef888
{
Packit 6ef888
	struct savemeta_header smh = {
Packit 6ef888
		.sh_magic = cpu_to_be32(SAVEMETA_MAGIC),
Packit 6ef888
		.sh_format = cpu_to_be32(SAVEMETA_FORMAT),
Packit 6ef888
		.sh_time = cpu_to_be64(time(NULL)),
Packit 6ef888
		.sh_fs_bytes = cpu_to_be64(fsbytes)
Packit 6ef888
	};
Packit 6ef888
Packit 6ef888
	if (savemetawrite(mfd, (char *)(&smh), sizeof(smh)) != sizeof(smh))
Packit 6ef888
		return -1;
Packit 6ef888
	return 0;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static int read_header(gzFile gzin_fd, struct savemeta_header *smh)
Packit 6ef888
{
Packit 6ef888
	size_t rs;
Packit 6ef888
	struct savemeta_header smh_be = {0};
Packit 6ef888
Packit 6ef888
	gzseek(gzin_fd, 0, SEEK_SET);
Packit 6ef888
	rs = gzread(gzin_fd, &smh_be, sizeof(smh_be));
Packit 6ef888
	if (rs == -1) {
Packit 6ef888
		perror("Failed to read savemeta file header");
Packit 6ef888
		return -1;
Packit 6ef888
	}
Packit 6ef888
	if (rs != sizeof(smh_be))
Packit 6ef888
		return 1;
Packit 6ef888
Packit 6ef888
	smh->sh_magic = be32_to_cpu(smh_be.sh_magic);
Packit 6ef888
	smh->sh_format = be32_to_cpu(smh_be.sh_format);
Packit 6ef888
	smh->sh_time = be64_to_cpu(smh_be.sh_time);
Packit 6ef888
	smh->sh_fs_bytes = be64_to_cpu(smh_be.sh_fs_bytes);
Packit 6ef888
Packit 6ef888
	return 0;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static int check_header(struct savemeta_header *smh)
Packit 6ef888
{
Packit 6ef888
	if (smh->sh_magic != SAVEMETA_MAGIC || smh->sh_format > SAVEMETA_FORMAT)
Packit 6ef888
		return -1;
Packit 6ef888
	printf("Metadata saved at %s", ctime((time_t *)&smh->sh_time)); /* ctime() adds \n */
Packit 6ef888
	printf("File system size %s\n", anthropomorphize(smh->sh_fs_bytes));
Packit 6ef888
	return 0;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
void savemeta(char *out_fn, int saveoption, int gziplevel)
Packit 6ef888
{
Packit 6ef888
	uint64_t jindex_block;
Packit 6ef888
	struct gfs2_buffer_head *lbh;
Packit 6ef888
	struct metafd mfd;
Packit 6ef888
	struct osi_node *n;
Packit 6ef888
	int err = 0;
Packit 6ef888
Packit 6ef888
	sbd.md.journals = 1;
Packit 6ef888
Packit 6ef888
	mfd = savemetaopen(out_fn, gziplevel);
Packit 6ef888
Packit 6ef888
	blks_saved = 0;
Packit 6ef888
	if (sbd.gfs1)
Packit 6ef888
		sbd.bsize = sbd.sd_sb.sb_bsize;
Packit 6ef888
	printf("There are %llu blocks of %u bytes in the filesystem.\n",
Packit 6ef888
	                     (unsigned long long)sbd.fssize, sbd.bsize);
Packit 6ef888
	if (sbd.gfs1)
Packit 6ef888
		jindex_block = sbd1->sb_jindex_di.no_addr;
Packit 6ef888
	else
Packit 6ef888
		jindex_block = masterblock("jindex");
Packit 6ef888
	lbh = bread(&sbd, jindex_block);
Packit 6ef888
	gfs2_dinode_in(&di, lbh->b_data);
Packit 6ef888
	if (!sbd.gfs1)
Packit 6ef888
		do_dinode_extended(&di, lbh);
Packit 6ef888
	brelse(lbh);
Packit 6ef888
Packit 6ef888
	printf("Filesystem size: %s\n", anthropomorphize(sbd.fssize * sbd.bsize));
Packit 6ef888
	get_journal_inode_blocks();
Packit 6ef888
Packit 6ef888
	err = init_per_node_lookup();
Packit 6ef888
	if (err)
Packit 6ef888
		exit(1);
Packit 6ef888
Packit 6ef888
	/* Write the savemeta file header */
Packit 6ef888
	err = save_header(&mfd, sbd.fssize * sbd.bsize);
Packit 6ef888
	if (err) {
Packit 6ef888
		perror("Failed to write metadata file header");
Packit 6ef888
		exit(1);
Packit 6ef888
	}
Packit 6ef888
	/* Save off the superblock */
Packit 6ef888
	save_block(sbd.device_fd, &mfd, GFS2_SB_ADDR * GFS2_BASIC_BLOCK / sbd.bsize, 0, NULL);
Packit 6ef888
	/* If this is gfs1, save off the rindex because it's not
Packit 6ef888
	   part of the file system as it is in gfs2. */
Packit 6ef888
	if (sbd.gfs1) {
Packit 6ef888
		uint64_t blk;
Packit 6ef888
		int j;
Packit 6ef888
Packit 6ef888
		blk = sbd1->sb_rindex_di.no_addr;
Packit 6ef888
		save_block(sbd.device_fd, &mfd, blk, blk, NULL);
Packit 6ef888
		save_inode_data(&mfd, blk);
Packit 6ef888
		/* In GFS1, journals aren't part of the RG space */
Packit 6ef888
		for (j = 0; j < journals_found; j++) {
Packit 6ef888
			log_debug("Saving journal #%d\n", j + 1);
Packit 6ef888
			for (blk = journal_blocks[j];
Packit 6ef888
			     blk < journal_blocks[j] + gfs1_journal_size;
Packit 6ef888
			     blk++)
Packit 6ef888
				save_block(sbd.device_fd, &mfd, blk, blk, NULL);
Packit 6ef888
		}
Packit 6ef888
	}
Packit 6ef888
	/* Walk through the resource groups saving everything within */
Packit 6ef888
	for (n = osi_first(&sbd.rgtree); n; n = osi_next(n)) {
Packit 6ef888
		struct rgrp_tree *rgd;
Packit 6ef888
Packit 6ef888
		rgd = (struct rgrp_tree *)n;
Packit 6ef888
		save_rgrp(&mfd, rgd, (saveoption != 2));
Packit 6ef888
	}
Packit 6ef888
	/* Clean up */
Packit 6ef888
	/* There may be a gap between end of file system and end of device */
Packit 6ef888
	/* so we tell the user that we've processed everything. */
Packit 6ef888
	warm_fuzzy_stuff(sbd.fssize, TRUE);
Packit 6ef888
	printf("\nMetadata saved to file %s ", mfd.filename);
Packit 6ef888
	if (mfd.gziplevel) {
Packit 6ef888
		printf("(gzipped, level %d).\n", mfd.gziplevel);
Packit 6ef888
	} else {
Packit 6ef888
		printf("(uncompressed).\n");
Packit 6ef888
	}
Packit 6ef888
	savemetaclose(&mfd;;
Packit 6ef888
	close(sbd.device_fd);
Packit 6ef888
	destroy_per_node_lookup();
Packit 6ef888
	free(indirect);
Packit 6ef888
	gfs2_rgrp_free(&sbd.rgtree);
Packit 6ef888
	exit(0);
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static off_t restore_init(gzFile gzfd, struct savemeta_header *smh)
Packit 6ef888
{
Packit 6ef888
	int err;
Packit 6ef888
	unsigned i;
Packit 6ef888
	size_t rs;
Packit 6ef888
	char buf[256];
Packit 6ef888
	off_t startpos = 0;
Packit 6ef888
	struct gfs2_meta_header sbmh;
Packit 6ef888
Packit 6ef888
	err = read_header(gzfd, smh);
Packit 6ef888
	if (err < 0) {
Packit 6ef888
		exit(1);
Packit 6ef888
	} else if (check_header(smh) != 0) {
Packit 6ef888
		printf("No valid file header found. Falling back to old format...\n");
Packit 6ef888
	} else if (err == 0) {
Packit 6ef888
		startpos = sizeof(*smh);
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	gzseek(gzfd, startpos, SEEK_SET);
Packit 6ef888
	rs = gzread(gzfd, buf, sizeof(buf));
Packit 6ef888
	if (rs != sizeof(buf)) {
Packit 6ef888
		fprintf(stderr, "Error: File is too small.\n");
Packit 6ef888
		exit(1);
Packit 6ef888
	}
Packit 6ef888
	/* Scan for the beginning of the file body. Required to support old formats(?). */
Packit 6ef888
	for (i = 0; i < (256 - sizeof(struct saved_metablock) - sizeof(sbmh)); i++) {
Packit 6ef888
		off_t off = i + sizeof(struct saved_metablock);
Packit 6ef888
Packit 6ef888
		memcpy(&sbmh, &buf[off], sizeof(sbmh));
Packit 6ef888
		if (sbmh.mh_magic == cpu_to_be32(GFS2_MAGIC) &&
Packit 6ef888
		     sbmh.mh_type == cpu_to_be32(GFS2_METATYPE_SB))
Packit 6ef888
			break;
Packit 6ef888
	}
Packit 6ef888
	if (i == (sizeof(buf) - sizeof(struct saved_metablock) - sizeof(sbmh)))
Packit 6ef888
		i = 0;
Packit 6ef888
	return startpos + i; /* File offset of saved sb */
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
Packit 6ef888
static int restore_block(gzFile gzfd, struct saved_metablock *svb, char *buf, uint16_t maxlen)
Packit 6ef888
{
Packit 6ef888
	int gzerr;
Packit 6ef888
	int ret;
Packit 6ef888
	uint16_t checklen;
Packit 6ef888
	const char *errstr;
Packit 6ef888
Packit 6ef888
	ret = gzread(gzfd, svb, sizeof(*svb));
Packit 6ef888
	if (ret < sizeof(*svb)) {
Packit 6ef888
		goto gzread_err;
Packit 6ef888
	}
Packit 6ef888
	svb->blk = be64_to_cpu(svb->blk);
Packit 6ef888
	svb->siglen = be16_to_cpu(svb->siglen);
Packit 6ef888
Packit 6ef888
	if (sbd.fssize && svb->blk >= sbd.fssize) {
Packit 6ef888
		fprintf(stderr, "Error: File system is too small to restore this metadata.\n");
Packit 6ef888
		fprintf(stderr, "File system is %llu blocks. Restore block = %llu\n",
Packit 6ef888
		        (unsigned long long)sbd.fssize, (unsigned long long)svb->blk);
Packit 6ef888
		return -1;
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	if (maxlen)
Packit 6ef888
		checklen = maxlen;
Packit 6ef888
	else
Packit 6ef888
		checklen = sbd.bsize;
Packit 6ef888
Packit 6ef888
	if (checklen && svb->siglen > checklen) {
Packit 6ef888
		fprintf(stderr, "Bad record length: %u for block %"PRIu64" (0x%"PRIx64").\n",
Packit 6ef888
			svb->siglen, svb->blk, svb->blk);
Packit 6ef888
		return -1;
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	if (buf != NULL && maxlen != 0) {
Packit 6ef888
		ret = gzread(gzfd, buf, svb->siglen);
Packit 6ef888
		if (ret < svb->siglen) {
Packit 6ef888
			goto gzread_err;
Packit 6ef888
		}
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	return 0;
Packit 6ef888
Packit 6ef888
gzread_err:
Packit 6ef888
	if (gzeof(gzfd))
Packit 6ef888
		return 1;
Packit 6ef888
Packit 6ef888
	errstr = gzerror(gzfd, &gzerr);
Packit 6ef888
	if (gzerr == Z_ERRNO)
Packit 6ef888
		errstr = strerror(errno);
Packit 6ef888
	fprintf(stderr, "Failed to restore block: %s\n", errstr);
Packit 6ef888
	return -1;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static int restore_super(gzFile gzfd, off_t pos)
Packit 6ef888
{
Packit 6ef888
	int ret;
Packit 6ef888
	struct saved_metablock svb = {0};
Packit 6ef888
	char *buf;
Packit 6ef888
Packit 6ef888
	buf = calloc(1, sizeof(struct gfs2_sb));
Packit 6ef888
	if (buf == NULL) {
Packit 6ef888
		perror("Failed to restore super block");
Packit 6ef888
		exit(1);
Packit 6ef888
	}
Packit 6ef888
	gzseek(gzfd, pos, SEEK_SET);
Packit 6ef888
	ret = restore_block(gzfd, &svb, buf, sizeof(struct gfs2_sb));
Packit 6ef888
	if (ret == 1) {
Packit 6ef888
		fprintf(stderr, "Reached end of file while restoring superblock\n");
Packit 6ef888
		goto err;
Packit 6ef888
	} else if (ret != 0) {
Packit 6ef888
		goto err;
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	gfs2_sb_in(&sbd.sd_sb, buf);
Packit 6ef888
	sbd1 = (struct gfs_sb *)&sbd.sd_sb;
Packit 6ef888
	ret = check_sb(&sbd.sd_sb);
Packit 6ef888
	if (ret < 0) {
Packit 6ef888
		fprintf(stderr,"Error: Invalid superblock data.\n");
Packit 6ef888
		goto err;
Packit 6ef888
	}
Packit 6ef888
	if (ret == 1)
Packit 6ef888
		sbd.gfs1 = 1;
Packit 6ef888
	sbd.bsize = sbd.sd_sb.sb_bsize;
Packit 6ef888
	free(buf);
Packit 6ef888
	printf("Block size is %uB\n", sbd.bsize);
Packit 6ef888
	return 0;
Packit 6ef888
err:
Packit 6ef888
	free(buf);
Packit 6ef888
	return -1;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static int find_highest_block(gzFile gzfd, off_t pos, uint64_t fssize)
Packit 6ef888
{
Packit 6ef888
	int err = 0;
Packit 6ef888
	uint64_t highest = 0;
Packit 6ef888
	struct saved_metablock svb = {0};
Packit 6ef888
Packit 6ef888
	while (1) {
Packit 6ef888
		gzseek(gzfd, pos, SEEK_SET);
Packit 6ef888
		err = restore_block(gzfd, &svb, NULL, 0);
Packit 6ef888
		if (err == 1)
Packit 6ef888
			break;
Packit 6ef888
		if (err != 0)
Packit 6ef888
			return -1;
Packit 6ef888
Packit 6ef888
		if (svb.blk > highest)
Packit 6ef888
			highest = svb.blk;
Packit 6ef888
		pos += sizeof(svb) + svb.siglen;
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	if (fssize > 0) {
Packit 6ef888
		printf("Saved file system size is %"PRIu64" (0x%"PRIx64") blocks, %s\n",
Packit 6ef888
		       fssize, fssize, anthropomorphize(fssize * sbd.bsize));
Packit 6ef888
		sbd.fssize = fssize;
Packit 6ef888
	} else {
Packit 6ef888
		sbd.fssize = highest + 1;
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	printf("Highest saved block is %"PRIu64" (0x%"PRIx64")\n", highest, highest);
Packit 6ef888
	return 0;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static int restore_data(int fd, gzFile gzin_fd, off_t pos, int printonly)
Packit 6ef888
{
Packit 6ef888
	struct saved_metablock savedata = {0};
Packit 6ef888
	uint64_t writes = 0;
Packit 6ef888
	char *buf;
Packit 6ef888
Packit 6ef888
	buf = calloc(1, sbd.bsize);
Packit 6ef888
	if (buf == NULL) {
Packit 6ef888
		perror("Failed to restore data");
Packit 6ef888
		exit(1);
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	gzseek(gzin_fd, pos, SEEK_SET);
Packit 6ef888
	blks_saved = 0;
Packit 6ef888
	while (TRUE) {
Packit 6ef888
		int err;
Packit 6ef888
		err = restore_block(gzin_fd, &savedata, buf, sbd.bsize);
Packit 6ef888
		if (err == 1)
Packit 6ef888
			break;
Packit 6ef888
		if (err != 0) {
Packit 6ef888
			free(buf);
Packit 6ef888
			return -1;
Packit 6ef888
		}
Packit 6ef888
Packit 6ef888
		if (printonly) {
Packit 6ef888
			struct gfs2_buffer_head dummy_bh = {
Packit 6ef888
				.b_data = buf,
Packit 6ef888
				.b_blocknr = savedata.blk,
Packit 6ef888
			};
Packit 6ef888
			if (printonly > 1 && printonly == savedata.blk) {
Packit 6ef888
				display_block_type(&dummy_bh, TRUE);
Packit 6ef888
				display_gfs2(&dummy_bh);
Packit 6ef888
				break;
Packit 6ef888
			} else if (printonly == 1) {
Packit 6ef888
				print_gfs2("%"PRId64" (l=0x%x): ", blks_saved, savedata.siglen);
Packit 6ef888
				display_block_type(&dummy_bh, TRUE);
Packit 6ef888
			}
Packit 6ef888
		} else {
Packit 6ef888
			warm_fuzzy_stuff(savedata.blk, FALSE);
Packit 6ef888
			memset(buf + savedata.siglen, 0, sbd.bsize - savedata.siglen);
Packit 6ef888
			if (pwrite(fd, buf, sbd.bsize, savedata.blk * sbd.bsize) != sbd.bsize) {
Packit 6ef888
				fprintf(stderr, "write error: %s from %s:%d: block %lld (0x%llx)\n",
Packit 6ef888
					strerror(errno), __FUNCTION__, __LINE__,
Packit 6ef888
					(unsigned long long)savedata.blk,
Packit 6ef888
					(unsigned long long)savedata.blk);
Packit 6ef888
				free(buf);
Packit 6ef888
				return -1;
Packit 6ef888
			}
Packit 6ef888
			writes++;
Packit 6ef888
			if (writes % 1000 == 0)
Packit 6ef888
				fsync(fd);
Packit 6ef888
		}
Packit 6ef888
		blks_saved++;
Packit 6ef888
	}
Packit 6ef888
	if (!printonly)
Packit 6ef888
		warm_fuzzy_stuff(sbd.fssize, 1);
Packit 6ef888
	free(buf);
Packit 6ef888
	return 0;
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
static void complain(const char *complaint)
Packit 6ef888
{
Packit 6ef888
	fprintf(stderr, "%s\n", complaint);
Packit 6ef888
	die("Format is: \ngfs2_edit restoremeta <file to restore> "
Packit 6ef888
	    "<dest file system>\n");
Packit 6ef888
}
Packit 6ef888
Packit 6ef888
void restoremeta(const char *in_fn, const char *out_device, uint64_t printonly)
Packit 6ef888
{
Packit 6ef888
	int error;
Packit 6ef888
	gzFile gzfd;
Packit 6ef888
	off_t pos = 0;
Packit 6ef888
	struct savemeta_header smh = {0};
Packit 6ef888
Packit 6ef888
	termlines = 0;
Packit 6ef888
	if (!in_fn)
Packit 6ef888
		complain("No source file specified.");
Packit 6ef888
	if (!printonly && !out_device)
Packit 6ef888
		complain("No destination file system specified.");
Packit 6ef888
Packit 6ef888
	gzfd = gzopen(in_fn, "rb");
Packit 6ef888
	if (!gzfd)
Packit 6ef888
		die("Can't open source file %s: %s\n",
Packit 6ef888
		    in_fn, strerror(errno));
Packit 6ef888
Packit 6ef888
	if (!printonly) {
Packit 6ef888
		sbd.device_fd = open(out_device, O_RDWR);
Packit 6ef888
		if (sbd.device_fd < 0)
Packit 6ef888
			die("Can't open destination file system %s: %s\n",
Packit 6ef888
			    out_device, strerror(errno));
Packit 6ef888
	} else if (out_device) /* for printsavedmeta, the out_device is an
Packit 6ef888
				  optional block no */
Packit 6ef888
		printonly = check_keywords(out_device);
Packit 6ef888
Packit 6ef888
	pos = restore_init(gzfd, &smh;;
Packit 6ef888
	error = restore_super(gzfd, pos);
Packit 6ef888
	if (error)
Packit 6ef888
		exit(1);
Packit 6ef888
Packit 6ef888
	printf("This is gfs%c metadata.\n", sbd.gfs1 ? '1': '2');
Packit 6ef888
Packit 6ef888
	if (!printonly) {
Packit 6ef888
		uint64_t space = lseek(sbd.device_fd, 0, SEEK_END) / sbd.bsize;
Packit 6ef888
		printf("There are %"PRIu64" free blocks on the destination device.\n", space);
Packit 6ef888
	}
Packit 6ef888
Packit 6ef888
	error = find_highest_block(gzfd, pos, sbd.fssize);
Packit 6ef888
	if (error)
Packit 6ef888
		exit(1);
Packit 6ef888
Packit 6ef888
	error = restore_data(sbd.device_fd, gzfd, pos, printonly);
Packit 6ef888
	printf("File %s %s %s.\n", in_fn,
Packit 6ef888
	       (printonly ? "print" : "restore"),
Packit 6ef888
	       (error ? "error" : "successful"));
Packit 6ef888
Packit 6ef888
	gzclose(gzfd);
Packit 6ef888
	if (!printonly)
Packit 6ef888
		close(sbd.device_fd);
Packit 6ef888
	free(indirect);
Packit 6ef888
	exit(error);
Packit 6ef888
}