[Cluster-devel] cluster/gfs2/edit savemeta.c

rpeterso at sourceware.org rpeterso at sourceware.org
Tue Jun 26 01:53:19 UTC 2007


CVSROOT:	/cvs/cluster
Module name:	cluster
Branch: 	RHEL5
Changes by:	rpeterso at sourceware.org	2007-06-26 01:53:19

Added files:
	gfs2/edit      : savemeta.c 

Log message:
	Resolves: bz 245635: Bring gfs2_edit up to date

Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs2/edit/savemeta.c.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=NONE&r2=1.1.2.1

/cvs/cluster/cluster/gfs2/edit/savemeta.c,v  -->  standard output
revision 1.1.2.1
--- cluster/gfs2/edit/savemeta.c
+++ -	2007-06-26 01:53:19.678210000 +0000
@@ -0,0 +1,711 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <linux/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <curses.h>
+#include <term.h>
+#include <sys/ioctl.h>
+#include <limits.h>
+#include <linux_endian.h>
+#include <sys/time.h>
+#include <linux/gfs2_ondisk.h>
+
+#include "osi_list.h"
+#include "hexedit.h"
+#include "libgfs2.h"
+
+#define BUFSIZE (4096)
+#define DFT_SAVE_FILE "/tmp/gfsmeta"
+
+struct saved_metablock {
+	uint64_t blk;
+	uint16_t siglen; /* significant data length */
+	char buf[BUFSIZE];
+};
+
+struct saved_metablock *savedata;
+uint64_t last_fs_block, last_reported_block, blks_saved, total_out, pct;
+struct gfs2_block_list *blocklist = NULL;
+
+extern void read_superblock(void);
+uint64_t masterblock(const char *fn);
+
+static __inline__ int fs_is_jdata(struct gfs2_inode *ip)
+{
+        return ip->i_di.di_flags & GFS2_DIF_JDATA;
+}
+
+static struct metapath *find_metapath(struct gfs2_inode *ip, uint64_t block)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct metapath *mp;
+	uint64_t b = block;
+	unsigned int i;
+
+	zalloc(mp, sizeof(struct metapath));
+
+	for (i = ip->i_di.di_height; i--;)
+		mp->mp_list[i] = do_div(b, sdp->sd_inptrs);
+
+	return mp;
+}
+
+static __inline__ uint64_t *
+metapointer(struct gfs2_buffer_head *bh, unsigned int height,
+			struct metapath *mp)
+{
+	unsigned int head_size = (height > 0) ?
+		sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_dinode);
+
+	return ((uint64_t *)(bh->b_data + head_size)) + mp->mp_list[height];
+}
+
+static void lookup_block(struct gfs2_inode *ip,
+	     struct gfs2_buffer_head *bh, unsigned int height, struct metapath *mp,
+	     int create, int *new, uint64_t *block)
+{
+	uint64_t *ptr = metapointer(bh, height, mp);
+
+	if (*ptr) {
+		*block = be64_to_cpu(*ptr);
+		return;
+	}
+
+	*block = 0;
+
+	if (!create)
+		return;
+
+	if (height == ip->i_di.di_height - 1&&
+	    !(S_ISDIR(ip->i_di.di_mode)))
+		*block = data_alloc(ip);
+	else
+		*block = meta_alloc(ip);
+
+	*ptr = cpu_to_be64(*block);
+	ip->i_di.di_blocks++;
+
+	*new = 1;
+}
+
+void gfs1_block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
+		    uint64_t *dblock, uint32_t *extlen, int prealloc)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_buffer_head *bh;
+	struct metapath *mp;
+	int create = *new;
+	unsigned int bsize;
+	unsigned int height;
+	unsigned int end_of_metadata;
+	unsigned int x;
+	enum update_flags f;
+
+	f = not_updated;
+	*new = 0;
+	*dblock = 0;
+	if (extlen)
+		*extlen = 0;
+
+	if (!ip->i_di.di_height) { /* stuffed */
+		if (!lblock) {
+			*dblock = ip->i_di.di_num.no_addr;
+			if (extlen)
+				*extlen = 1;
+		}
+		return;
+	}
+
+	bsize = (fs_is_jdata(ip)) ? sdp->sd_jbsize : sdp->bsize;
+
+	height = calc_tree_height(ip, (lblock + 1) * bsize);
+	if (ip->i_di.di_height < height) {
+		if (!create)
+			return;
+
+		build_height(ip, height);
+	}
+
+	mp = find_metapath(ip, lblock);
+	end_of_metadata = ip->i_di.di_height - 1;
+
+	bh = bhold(ip->i_bh);
+
+	for (x = 0; x < end_of_metadata; x++) {
+		lookup_block(ip, bh, x, mp, create, new, dblock);
+		brelse(bh, not_updated);
+		if (!*dblock)
+			goto out;
+
+		if (*new) {
+			struct gfs2_meta_header mh;
+
+			bh = bget(sdp, *dblock);
+			mh.mh_magic = GFS2_MAGIC;
+			mh.mh_type = GFS2_METATYPE_IN;
+			mh.mh_format = GFS2_FORMAT_IN;
+			gfs2_meta_header_out(&mh, bh->b_data);
+			f = updated;
+		} else
+			bh = bread(sdp, *dblock);
+	}
+
+	if (!prealloc)
+		lookup_block(ip, bh, end_of_metadata, mp, create, new, dblock);
+
+	if (extlen && *dblock) {
+		*extlen = 1;
+
+		if (!*new) {
+			uint64_t tmp_dblock;
+			int tmp_new;
+			unsigned int nptrs;
+
+			nptrs = (end_of_metadata) ? sdp->sd_inptrs : sdp->sd_diptrs;
+
+			while (++mp->mp_list[end_of_metadata] < nptrs) {
+				lookup_block(ip, bh, end_of_metadata, mp, FALSE, &tmp_new,
+							 &tmp_dblock);
+
+				if (*dblock + *extlen != tmp_dblock)
+					break;
+
+				(*extlen)++;
+			}
+		}
+	}
+
+	brelse(bh, f);
+
+ out:
+	free(mp);
+}
+
+int gfs1_readi(struct gfs2_inode *ip, void *buf,
+	       uint64_t offset, unsigned int size)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_buffer_head *bh;
+	uint64_t lblock, dblock;
+	uint32_t extlen = 0;
+	unsigned int amount;
+	int not_new = 0;
+	int journaled = fs_is_jdata(ip);
+	int copied = 0;
+
+	if (offset >= ip->i_di.di_size)
+		return 0;
+
+	if ((offset + size) > ip->i_di.di_size)
+		size = ip->i_di.di_size - offset;
+
+	if (!size)
+		return 0;
+
+	if (journaled) {
+		lblock = offset / sdp->sd_jbsize;
+		offset %= sdp->sd_jbsize;
+	} else {
+		lblock = offset >> sdp->sd_sb.sb_bsize_shift;
+		offset &= sdp->sd_sb.sb_bsize - 1;
+	}
+
+	if (!ip->i_di.di_height) /* stuffed */
+		offset += sizeof(struct gfs2_dinode);
+	else if (journaled)
+		offset += sizeof(struct gfs2_meta_header);
+
+	while (copied < size) {
+		amount = size - copied;
+		if (amount > sdp->bsize - offset)
+			amount = sdp->bsize - offset;
+
+		if (!extlen)
+			gfs1_block_map(ip, lblock, &not_new, &dblock,
+				       &extlen, FALSE);
+
+		if (dblock) {
+			bh = bread(sdp, dblock);
+			dblock++;
+			extlen--;
+		} else
+			bh = NULL;
+
+
+		if (bh) {
+			memcpy(buf+copied, bh->b_data + offset, amount);
+			brelse(bh, not_updated);
+		} else
+			memset(buf+copied, 0, amount);
+		copied += amount;
+		lblock++;
+
+		offset = (journaled) ? sizeof(struct gfs2_meta_header) : 0;
+	}
+
+	return copied;
+}
+
+/**
+ * gfs1_rindex_read - read in the rg index file
+ *                  Stolen from libgfs2/super.c, but modified to handle gfs1.
+ * @sdp: the incore superblock pointer
+ * fd: optional file handle for rindex file (if meta_fs file system is mounted)
+ *     (if fd is <= zero, it will read from raw device)
+ * @count1: return count of the rgs.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int gfs1_rindex_read(struct gfs2_sbd *sdp, int fd, int *count1)
+{
+	unsigned int rg;
+	int error;
+	struct gfs2_rindex buf;
+	struct rgrp_list *rgd, *prev_rgd;
+	uint64_t prev_length;
+
+	*count1 = 0;
+	prev_rgd = NULL;
+	for (rg = 0; ; rg++) {
+		if (fd > 0)
+			error = read(fd, &buf, sizeof(struct gfs2_rindex));
+		else
+			error = gfs1_readi(sdp->md.riinode, (char *)&buf,
+					   (rg * sizeof(struct gfs2_rindex)),
+					   sizeof(struct gfs2_rindex));
+		if (!error)
+			break;
+		if (error != sizeof(struct gfs2_rindex))
+			return -1;
+
+		rgd = (struct rgrp_list *)malloc(sizeof(struct rgrp_list));
+		memset(rgd, 0, sizeof(struct rgrp_list));
+		osi_list_add_prev(&rgd->list, &sdp->rglist);
+
+		gfs2_rindex_in(&rgd->ri, (char *)&buf);
+
+		rgd->start = rgd->ri.ri_addr;
+		if (prev_rgd) {
+			prev_length = rgd->start - prev_rgd->start;
+			prev_rgd->length = prev_length;
+		}
+
+		if(gfs2_compute_bitstructs(sdp, rgd))
+			return -1;
+
+		(*count1)++;
+		prev_rgd = rgd;
+	}
+	if (prev_rgd)
+		prev_rgd->length = prev_length;
+	return 0;
+}
+
+/**
+ * gfs1_ri_update - attach rgrps to the super block
+ *                  Stolen from libgfs2/super.c, but modified to handle gfs1.
+ * @sdp:
+ *
+ * Given the rgrp index inode, link in all rgrps into the super block
+ * and be sure that they can be read.
+ *
+ * Returns: 0 on success, -1 on failure.
+ */
+int gfs1_ri_update(struct gfs2_sbd *sdp, int fd, int *rgcount)
+{
+	struct rgrp_list *rgd;
+	osi_list_t *tmp;
+	int count1 = 0, count2 = 0;
+	uint64_t errblock = 0;
+
+	if (gfs1_rindex_read(sdp, fd, &count1))
+	    goto fail;
+	for (tmp = sdp->rglist.next; tmp != &sdp->rglist; tmp = tmp->next) {
+		enum update_flags f;
+
+		f = not_updated;
+		rgd = osi_list_entry(tmp, struct rgrp_list, list);
+		errblock = gfs2_rgrp_read(sdp, rgd);
+		if (errblock)
+			return errblock;
+		else
+			gfs2_rgrp_relse(rgd, f);
+		count2++;
+	}
+
+	*rgcount = count1;
+	if (count1 != count2)
+		goto fail;
+
+	return 0;
+
+ fail:
+	gfs2_rgrp_free(&sdp->rglist, not_updated);
+	return -1;
+}
+
+
+/*
+ * get_gfs_struct_info - get block type and structure length
+ *
+ * @block_type - pointer to integer to hold the block type
+ * @struct_length - pointer to integet to hold the structure length
+ *
+ * returns: 0 if successful
+ *          -1 if this isn't gfs metadata.
+ */
+int get_gfs_struct_info(char *buf, int *block_type, int *struct_len)
+{
+	struct gfs2_meta_header mh;
+
+	*block_type = 0;
+	*struct_len = 0;
+
+	gfs2_meta_header_in(&mh, buf);
+	if (mh.mh_magic != GFS2_MAGIC)
+		return -1;
+
+	*block_type = mh.mh_type;
+
+	switch (mh.mh_type) {
+	case GFS2_METATYPE_SB:   /* 1 (superblock) */
+		*struct_len = sizeof(struct gfs_sb);
+		break;
+	case GFS2_METATYPE_RG:   /* 2 (rsrc grp hdr) */
+		*struct_len = bufsize; /*sizeof(struct gfs_rgrp);*/
+		break;
+	case GFS2_METATYPE_RB:   /* 3 (rsrc grp bitblk) */
+		*struct_len = bufsize;
+		break;
+	case GFS2_METATYPE_DI:   /* 4 (disk inode) */
+		*struct_len = bufsize; /*sizeof(struct gfs_dinode);*/
+		break;
+	case GFS2_METATYPE_IN:   /* 5 (indir inode blklst) */
+		*struct_len = bufsize; /*sizeof(struct gfs_indirect);*/
+		break;
+	case GFS2_METATYPE_LF:   /* 6 (leaf dinode blklst) */
+		*struct_len = bufsize; /*sizeof(struct gfs_leaf);*/
+		break;
+	case GFS2_METATYPE_JD:   /* 7 (journal data) */
+		*struct_len = sizeof(struct gfs2_meta_header);
+		break;
+	case GFS2_METATYPE_LH:   /* 8 (log header) */
+		*struct_len = sizeof(struct gfs2_log_header);
+		break;
+	case GFS2_METATYPE_LD:   /* 9 (log descriptor) */
+		*struct_len = sizeof(struct gfs2_log_descriptor);
+		break;
+	case GFS2_METATYPE_EA:   /* 10 (extended attr hdr) */
+		*struct_len = sizeof(struct gfs2_ea_header);
+		break;
+	case GFS2_METATYPE_ED:   /* 11 (extended attr data) */
+		*struct_len = 512;
+		break;
+	default:
+		*struct_len = bufsize;
+		break;
+	}
+	return 0;
+}
+
+/* Put out a warm, fuzzy message every second so the user     */
+/* doesn't think we hung.  (This may take a long time).       */
+/* We only check whether to report every one percent because  */
+/* checking every block kills performance.  We only report    */
+/* every second because we don't need 100 extra messages in   */
+/* logs made from verbose mode.                               */
+void warm_fuzzy_stuff(uint64_t block, int force)
+{
+        static struct timeval tv;
+        static uint32_t seconds = 0;
+        
+	last_reported_block = block;
+	gettimeofday(&tv, NULL);
+	if (!seconds)
+		seconds = tv.tv_sec;
+	if (force || tv.tv_sec - seconds) {
+		static uint64_t percent;
+
+		seconds = tv.tv_sec;
+		if (last_fs_block) {
+			percent = (block * 100) / last_fs_block;
+			printf("\r%" PRIu64 " blocks (%"
+			       PRIu64 "%%) processed, ", block, percent);
+			printf("%" PRIu64 " blocks (%" PRIu64 "MB) saved    ",
+			       blks_saved, total_out / (1024*1024));
+			fflush(stdout);
+		}
+	}
+}
+
+void save_block(int fd, int out_fd, uint64_t blk)
+{
+	int blktype, blklen, outsz;
+	uint16_t trailing0;
+	char *p;
+
+	warm_fuzzy_stuff(blk, FALSE);
+	memset(savedata, 0, sizeof(struct saved_metablock));
+	do_lseek(fd, blk * bufsize);
+	do_read(fd, savedata->buf, bufsize); /* read in the block */
+
+	if (get_gfs_struct_info(savedata->buf, &blktype, &blklen))
+		return; /* Not metadata, so skip it */
+	trailing0 = 0;
+	p = &savedata->buf[blklen - 1];
+	while (*p=='\0' && trailing0 < bufsize) {
+		trailing0++;
+		p--;
+	}
+	savedata->blk = cpu_to_be64(blk);
+	do_write(out_fd, &savedata->blk, sizeof(savedata->blk));
+	outsz = blklen - trailing0;
+	savedata->siglen = cpu_to_be16(outsz);
+	do_write(out_fd, &savedata->siglen, sizeof(savedata->siglen));
+	do_write(out_fd, savedata->buf, outsz);
+	total_out += sizeof(savedata->blk) + sizeof(savedata->siglen) + outsz;
+	blks_saved++;
+}
+
+void savemeta(const char *in_fn, const char *out_fn, int slow)
+{
+	int out_fd;
+	osi_list_t *tmp;
+	uint64_t blk;
+	uint64_t memreq;
+	int rgcount;
+
+	memset(&sbd, 0, sizeof(struct gfs2_sbd));
+	strcpy(sbd.device_name, in_fn);
+	sbd.bsize = GFS2_DEFAULT_BSIZE;
+	sbd.rgsize = -1;
+	sbd.jsize = GFS2_DEFAULT_JSIZE;
+	sbd.qcsize = GFS2_DEFAULT_QCSIZE;
+	sbd.md.journals = 1;
+	sbd.device_fd = open(in_fn, O_RDONLY);
+	if (sbd.device_fd < 0)
+		die("Can't open %s: %s\n", in_fn, strerror(errno));
+
+	if (!out_fn)
+		out_fn = DFT_SAVE_FILE;
+	out_fd = open(out_fn, O_RDWR | O_CREAT, 0644);
+	if (out_fd < 0)
+		die("Can't open %s: %s\n", out_fn, strerror(errno));
+
+	savedata = malloc(sizeof(struct saved_metablock));
+	if (!savedata)
+		die("Can't allocate memory for the operation.\n");
+
+	do_lseek(sbd.device_fd, 0);
+	blks_saved = total_out = last_reported_block = 0;
+	bufsize = BUFSIZE;
+	if (!slow) {
+		int i;
+
+		device_geometry(&sbd);
+		fix_device_geometry(&sbd);
+		osi_list_init(&sbd.rglist);
+		osi_list_init(&sbd.buf_list);
+		for(i = 0; i < BUF_HASH_SIZE; i++)
+			osi_list_init(&sbd.buf_hash[i]);
+		sbd.sd_sb.sb_bsize = GFS2_DEFAULT_BSIZE;
+		compute_constants(&sbd);
+		if(read_sb(&sbd) < 0)
+			slow = TRUE;
+		else
+			bufsize = sbd.bsize = sbd.sd_sb.sb_bsize;
+	}
+	last_fs_block = lseek(sbd.device_fd, 0, SEEK_END) / bufsize;
+	printf("There are %" PRIu64 " blocks of %" PRIu64 " bytes.\n\n",
+	       last_fs_block, bufsize);
+	if (!slow) {
+		if (gfs1)
+			sbd.md.riinode =
+				gfs2_load_inode(&sbd,
+						sbd1->sb_rindex_di.no_addr);
+		else {
+			sbd.master_dir =
+				gfs2_load_inode(&sbd,
+						sbd.sd_sb.sb_master_dir.no_addr);
+
+			slow = gfs2_lookupi(sbd.master_dir, "rindex", 6, 
+					    &sbd.md.riinode);
+		}
+	}
+	if (!slow) {
+		if (gfs1)
+			slow = gfs1_ri_update(&sbd, 0, &rgcount);
+		else
+			slow = ri_update(&sbd, 0, &rgcount);
+	}
+	if (!slow) {
+		blocklist = gfs2_block_list_create(last_fs_block + 1, &memreq);
+		if (!blocklist)
+			slow = TRUE;
+	}
+	if (!slow) {
+		save_block(sbd.device_fd, out_fd, 0x10 * (4096 / bufsize));
+		for (tmp = sbd.rglist.next; tmp != &sbd.rglist;
+		     tmp = tmp->next){
+			struct rgrp_list *rgd;
+			int i, first;
+
+			rgd = osi_list_entry(tmp, struct rgrp_list, list);
+			slow = gfs2_rgrp_read(&sbd, rgd);
+			if (slow)
+				continue;
+			log_debug("RG at %"PRIu64" is %u long\n",
+				  rgd->ri.ri_addr, rgd->ri.ri_length);
+			for (i = 0; i < rgd->ri.ri_length; i++) {
+				if(gfs2_block_set(blocklist,
+						  rgd->ri.ri_addr + i,
+						  gfs2_meta_other))
+					break;
+			}
+			first = 1;
+			/* Save off the rg and bitmaps */
+			for (blk = rgd->ri.ri_addr;
+			     blk < rgd->ri.ri_data0; blk++)
+				save_block(sbd.device_fd, out_fd, blk);
+			/* Save off the other metadata: inodes, etc. */
+			while (!gfs2_next_rg_meta(rgd, &blk, first)) {
+				save_block(sbd.device_fd, out_fd, blk);
+				first = 0;
+			}
+			gfs2_rgrp_relse(rgd, not_updated);
+		}
+	}
+	if (slow) {
+		for (blk = 0; blk < last_fs_block; blk++) {
+			save_block(sbd.device_fd, out_fd, blk);
+		}
+	}
+	if (blocklist)
+		gfs2_block_list_destroy(blocklist);
+	/* There may be a gap between end of file system and end of device */
+	/* so we tell the user that we've processed everything. */
+	blk = last_fs_block;
+	warm_fuzzy_stuff(blk, TRUE);
+	printf("\nMetadata saved to file %s.\n", out_fn);
+	free(savedata);
+	close(out_fd);
+	close(sbd.device_fd);
+	exit(0);
+}
+
+int restore_data(int fd, int in_fd)
+{
+	size_t rs;
+	uint64_t buf64, writes = 0;
+	uint16_t buf16;
+	int first = 1;
+	uint64_t max_fs_size;
+
+	do_lseek(fd, 0);
+	while (TRUE) {
+		memset(savedata, 0, sizeof(struct saved_metablock));
+		rs = read(in_fd, &buf64, sizeof(uint64_t));
+		if (!rs)
+			break;
+		if (rs != sizeof(uint64_t)) {
+			fprintf(stderr, "Error reading from file.\n");
+			return -1;
+		}
+		savedata->blk = be64_to_cpu(buf64);
+		if (savedata->blk >= max_fs_size) {
+			fprintf(stderr, "Error: File system is too small to "
+				"restore this metadata.\n");
+			fprintf(stderr, "File system is %" PRIu64 " blocks, ",
+				max_fs_size);
+			fprintf(stderr, "Restore block = %" PRIu64 "\n",
+				savedata->blk);
+			return -1;
+		}
+		rs = read(in_fd, &buf16, sizeof(uint16_t));
+		savedata->siglen = be16_to_cpu(buf16);
+		if (savedata->siglen > 0 &&
+		    savedata->siglen <= sizeof(savedata->buf)) {
+			do_read(in_fd, savedata->buf, savedata->siglen);
+			if (first) {
+				gfs2_sb_in(&sbd.sd_sb, savedata->buf);
+				if (check_sb(&sbd.sd_sb)) {
+					fprintf(stderr,"Error: Invalid superblock data.\n");
+					return -1;
+				}
+				bufsize = sbd.sd_sb.sb_bsize;
+				last_fs_block =
+					lseek(fd, 0, SEEK_END) / bufsize;
+				printf("There are %" PRIu64 " blocks of %" \
+				       PRIu64 "bytes in the destination file" \
+				       " system.\n\n", last_fs_block, bufsize);
+				first = 0;
+			}
+			do_lseek(fd, savedata->blk * bufsize);
+			do_write(fd, savedata->buf, bufsize);
+			writes++;
+		} else {
+			fprintf(stderr, "Bad record length: %d for #%"
+				PRIu64".\n", savedata->siglen, savedata->blk);
+			return -1;
+		}
+	}
+	printf("%" PRIu64 " blocks restored.\n", writes);
+	return 0;
+}
+
+void restoremeta(const char *in_fn, const char *out_device)
+{
+	int in_fd;
+
+	if (!in_fn)
+		die("No source file specified.  Format is: \ngfs2_edit "
+		    "restoremeta <file to restore> <dest file system>\n");
+	if (!out_device)
+		die("No destination file system specified.  Format is: \n"
+		    "gfs2_edit restoremeta <file to restore> <dest file "
+		    "system>\n");
+	in_fd = open(in_fn, O_RDONLY);
+	if (in_fd < 0)
+		die("Can't open source file %s: %s\n",
+		    in_fn, strerror(errno));
+
+	fd = open(out_device, O_RDWR);
+	if (fd < 0)
+		die("Can't open destination file system %s: %s\n",
+		    out_device, strerror(errno));
+
+	savedata = malloc(sizeof(struct saved_metablock));
+	if (!savedata)
+		die("Can't allocate memory for the restore operation.\n");
+
+	blks_saved = total_out = 0;
+	if (restore_data(fd, in_fd) == 0)
+		printf("File %s restore successful.\n", in_fn);
+	else
+		printf("File %s restore error.\n", in_fn);
+	free(savedata);
+	close(in_fd);
+	close(fd);
+
+	exit(0);
+}




More information about the Cluster-devel mailing list