gfs2_edit: Add compression to savemeta and restoremeta
This patch adds the ability to output gzip-compressed data with savemeta and
makes it the default behaviour. It also adds a -z <0-9> option to allow the
level of compression to be controlled, 0 meaning no compression and 9 being the
default. restoremeta can now restore from a gzip-compressed or raw metadata
file without any extra options.
The file opening, closing and writing code from savemeta has been moved into
savemeta{open,close,write} functions to abstract away compressed and
non-compressed file output.
");
+ fprintf(stderr,"
Format is: gfs2_edit [-c 1] [-V] [-x] [-h] [identify] [-z <0-9>] [-p structures|blocks][blocktype][blockalloc [val]][blockbits][blockrg][find sb|rg|rb|di|in|lf|jd|lh|ld|ea|ed|lb|13|qc][field <f>[val]] /dev/device
");
fprintf(stderr,"If only the device is specified, it enters into hexedit mode.
");
fprintf(stderr,"identify - prints out only the block type, not the details.
");
fprintf(stderr,"printsavedmeta - prints out the saved metadata blocks from a savemeta file.
");
@@ -3348,6 +3349,8 @@ static void usage(void)
fprintf(stderr,"-p <b> find sb|rg|rb|di|in|lf|jd|lh|ld|ea|ed|lb|"
"13|qc - find block of given type after block <b>
");
fprintf(stderr," <b> specifies the starting block for search
");
+ fprintf(stderr,"-z 1 use gzip compression level 1 for savemeta (default 9)
");
+ fprintf(stderr,"-z 0 do not use compression
");
fprintf(stderr,"-s specifies a starting block such as root, rindex, quota, inum.
");
fprintf(stderr,"-x print in hexmode.
");
fprintf(stderr,"-h prints this help.
");
@@ -3374,10 +3377,33 @@ static void usage(void)
fprintf(stderr," gfs2_edit -p quota find di /dev/x/y
");
fprintf(stderr," To set the Resource Group flags for rg #7 to 3.
");
fprintf(stderr," gfs2_edit rgflags 7 3 /dev/sdc2
");
- fprintf(stderr," To save off all metadata for /dev/vg/lv:
");
- fprintf(stderr," gfs2_edit savemeta /dev/vg/lv /tmp/metasave
");
+ fprintf(stderr," To save off all metadata for /dev/vg/lv without compression:
");
+ fprintf(stderr," gfs2_edit savemeta -z 0 /dev/vg/lv /tmp/metasave
");
}/* usage */
+/**
+ * getgziplevel - Process the -z parameter to savemeta operations
+ * argv - argv
+ * i - a pointer to the argv index at which to begin processing
+ * The index pointed to by i will be incremented past the -z option if found
+ */
+static void getgziplevel(char *argv[], int *i)
+{
+ char *endptr;
+ (*i)++;
+ if (!strcasecmp(argv[*i], "-z")) {
+ (*i)++;
+ errno = 0;
+ gziplevel = strtol(argv[*i], &endptr, 10);
+ if (errno || endptr == argv[*i] || gziplevel < 0 || gziplevel > 9) {
+ fprintf(stderr, "Compression level out of range: %s
", argv[*i]);
+ exit(-1);
+ }
+ } else {
+ (*i)--;
+ }
+}
+
/* ------------------------------------------------------------------------ */
/* parameterpass1 - pre-processing for command-line parameters */
/* ------------------------------------------------------------------------ */
@@ -3571,13 +3597,16 @@ static void process_parameters(int argc, char *argv[], int pass)
exit(EXIT_SUCCESS);
}
}
- else if (!strcasecmp(argv[i], "savemeta"))
- savemeta(argv[i+2], 0);
- else if (!strcasecmp(argv[i], "savemetaslow"))
- savemeta(argv[i+2], 1);
- else if (!strcasecmp(argv[i], "savergs"))
- savemeta(argv[i+2], 2);
- else if (isdigit(argv[i][0])) { /* decimal addr */
+ else if (!strcasecmp(argv[i], "savemeta")) {
+ getgziplevel(argv, &i);
+ savemeta(argv[i+2], 0, gziplevel);
+ } else if (!strcasecmp(argv[i], "savemetaslow")) {
+ getgziplevel(argv, &i);
+ savemeta(argv[i+2], 1, gziplevel);
+ } else if (!strcasecmp(argv[i], "savergs")) {
+ getgziplevel(argv, &i);
+ savemeta(argv[i+2], 2, gziplevel);
+ } else if (isdigit(argv[i][0])) { /* decimal addr */
sscanf(argv[i], "%"SCNd64, &temp_blk);
push_block(temp_blk);
} else {
diff --git a/gfs2/edit/hexedit.h b/gfs2/edit/hexedit.h
index 81637bc..8732c27 100644
--- a/gfs2/edit/hexedit.h
+++ b/gfs2/edit/hexedit.h
@@ -365,7 +365,7 @@ extern void gfs_log_header_in(struct gfs_log_header *head,
struct gfs2_buffer_head *bh);
extern void gfs_log_header_print(struct gfs_log_header *lh);
extern void gfs_dinode_in(struct gfs_dinode *di, struct gfs2_buffer_head *bh);
-extern void savemeta(char *out_fn, int saveoption);
+extern void savemeta(char *out_fn, int saveoption, int gziplevel);
extern void restoremeta(const char *in_fn, const char *out_device,
uint64_t printblocksonly);
extern int display(int identify_only);
diff --git a/gfs2/edit/savemeta.c b/gfs2/edit/savemeta.c
index 1ca5b42..c868edd 100644
--- a/gfs2/edit/savemeta.c
+++ b/gfs2/edit/savemeta.c
@@ -29,6 +29,7 @@
#include <linux_endian.h>
#include <sys/time.h>
#include <linux/gfs2_ondisk.h>
+#include <zlib.h>
-void savemeta(char *out_fn, int saveoption)
+void savemeta(char *out_fn, int saveoption, int gziplevel)
{
- int out_fd;
int slow;
osi_list_t *tmp;
int rgcount;
uint64_t jindex_block;
struct gfs2_buffer_head *lbh;
struct rgrp_list *last_rgd, *prev_rgd;
+ struct metafd mfd;
slow = (saveoption == 1);
sbd.md.journals = 1;
- if (!out_fn) {
- out_fn = strdup(DFT_SAVE_FILE);
- if (!out_fn)
- die("Can't allocate memory for the operation.
");
- out_fd = mkstemp(out_fn);
- } else
- out_fd = open(out_fn, O_RDWR | O_CREAT, 0644);
-
- if (out_fd < 0)
- die("Can't open %s: %s
", out_fn, strerror(errno));
-
- if (ftruncate(out_fd, 0))
- die("Can't truncate %s: %s
", out_fn, strerror(errno));
+ mfd = savemetaopen(out_fn, gziplevel);
+
savedata = malloc(sizeof(struct saved_metablock));
if (!savedata)
die("Can't allocate memory for the operation.
");
@@ -628,15 +738,15 @@ void savemeta(char *out_fn, int saveoption)
get_journal_inode_blocks();
if (!slow) {
/* Save off the superblock */
- save_block(sbd.device_fd, out_fd, 0x10 * (4096 / sbd.bsize));
+ save_block(sbd.device_fd, &mfd, 0x10 * (4096 / sbd.bsize));
/* If this is gfs1, save off the rindex because it's not
part of the file system as it is in gfs2. */
if (gfs1) {
int j;
block = sbd1->sb_rindex_di.no_addr;
- save_block(sbd.device_fd, out_fd, block);
- save_inode_data(out_fd);
+ save_block(sbd.device_fd, &mfd, block);
+ save_inode_data(&mfd);
/* In GFS1, journals aren't part of the RG space */
for (j = 0; j < journals_found; j++) {
log_debug("Saving journal #%d
", j + 1);
@@ -644,7 +754,7 @@ void savemeta(char *out_fn, int saveoption)
block < journal_blocks[j] +
gfs1_journal_size;
block++)
- save_block(sbd.device_fd, out_fd, block);
+ save_block(sbd.device_fd, &mfd, block);
}
}
/* Walk through the resource groups saving everything within */
@@ -666,7 +776,7 @@ void savemeta(char *out_fn, int saveoption)
for (block = rgd->ri.ri_addr;
block < rgd->ri.ri_data0; block++) {
warm_fuzzy_stuff(block, FALSE);
- save_block(sbd.device_fd, out_fd, block);
+ save_block(sbd.device_fd, &mfd, block);
}
/* Save off the other metadata: inodes, etc. */
if (saveoption != 2) {
@@ -675,9 +785,9 @@ void savemeta(char *out_fn, int saveoption)
while (!gfs2_next_rg_meta(rgd, &block, first)){
warm_fuzzy_stuff(block, FALSE);
blktype = save_block(sbd.device_fd,
- out_fd, block);
+ &mfd, block);
if (blktype == GFS2_METATYPE_DI)
- save_inode_data(out_fd);
+ save_inode_data(&mfd);
first = 0;
}
/* Save off the free/unlinked meta blocks too.
@@ -686,7 +796,7 @@ void savemeta(char *out_fn, int saveoption)
while (!next_rg_freemeta(&sbd, rgd, &block,
first)) {
blktype = save_block(sbd.device_fd,
- out_fd, block);
+ &mfd, block);
first = 0;
}
}
@@ -695,7 +805,7 @@ void savemeta(char *out_fn, int saveoption)
}
if (slow) {
for (block = 0; block < last_fs_block; block++) {
- save_block(sbd.device_fd, out_fd, block);
+ save_block(sbd.device_fd, &mfd, block);
}
}
/* Clean up */
@@ -703,28 +813,33 @@ void savemeta(char *out_fn, int saveoption)
/* so we tell the user that we've processed everything. */
block = last_fs_block;
warm_fuzzy_stuff(block, TRUE);
- printf("
Metadata saved to file %s.
", out_fn);
+ printf("
Metadata saved to file %s ", mfd.filename);
+ if (mfd.gziplevel) {
+ printf("(gzipped, level %d).
", mfd.gziplevel);
+ } else {
+ printf("(uncompressed).
");
+ }
free(savedata);
- close(out_fd);
+ savemetaclose(&mfd);
close(sbd.device_fd);
exit(0);
}
-static int restore_data(int fd, int in_fd, int printblocksonly,
+static int restore_data(int fd, gzFile *gzin_fd, int printblocksonly,
int find_highblk)
{
size_t rs;
uint64_t buf64, writes = 0, highest_valid_block = 0;
uint16_t buf16;
- int first = 1, pos;
+ int first = 1, pos, gzerr;
char rdbuf[256];
char gfs_superblock_id[8] = {0x01, 0x16, 0x19, 0x70,
0x00, 0x00, 0x00, 0x01};
if (!printblocksonly)
do_lseek(fd, 0);
- do_lseek(in_fd, 0);
- rs = read(in_fd, rdbuf, sizeof(rdbuf));
+ gzseek(gzin_fd, 0, SEEK_SET);
+ rs = gzread(gzin_fd, rdbuf, sizeof(rdbuf));
if (rs != sizeof(rdbuf)) {
fprintf(stderr, "Error: File is too small.
");
return -1;
@@ -738,14 +853,20 @@ static int restore_data(int fd, int in_fd, int printblocksonly,
}
if (pos == sizeof(rdbuf) - sizeof(uint64_t) - sizeof(uint16_t))
pos = 0;
- do_lseek(in_fd, pos);
+ if (gzseek(gzin_fd, pos, SEEK_SET) != pos) {
+ fprintf(stderr, "bad seek: %s from %s:%d: "
+ "offset %lld (0x%llx)
", strerror(errno),
+ __FUNCTION__, __LINE__, (unsigned long long)pos,
+ (unsigned long long)pos);
+ exit(-1);
+ }
blks_saved = total_out = 0;
last_fs_block = 0;
while (TRUE) {
struct gfs2_buffer_head dummy_bh;
memset(savedata, 0, sizeof(struct saved_metablock));
- rs = read(in_fd, &buf64, sizeof(uint64_t));
+ rs = gzread(gzin_fd, &buf64, sizeof(uint64_t));
if (!rs)
break;
if (rs != sizeof(uint64_t)) {
@@ -764,7 +885,15 @@ static int restore_data(int fd, int in_fd, int printblocksonly,
savedata->blk);
return -1;
}
- rs = read(in_fd, &buf16, sizeof(uint16_t));
+ if (gzread(gzin_fd, &buf16, sizeof(uint16_t)) !=
+ sizeof(uint16_t)) {
+ fprintf(stderr, "read error: %s from %s:%d: "
+ "block %lld (0x%llx)
",
+ gzerror(gzin_fd, &gzerr), __FUNCTION__, __LINE__,
+ (unsigned long long)savedata->blk,
+ (unsigned long long)savedata->blk);
+ exit(-1);
+ }
savedata->siglen = be16_to_cpu(buf16);
if (savedata->siglen > sizeof(savedata->buf)) {
fprintf(stderr, "
Bad record length: %d for block #%"
@@ -773,11 +902,11 @@ static int restore_data(int fd, int in_fd, int printblocksonly,
return -1;
}
if (savedata->siglen &&
- read(in_fd, savedata->buf, savedata->siglen) !=
+ gzread(gzin_fd, savedata->buf, savedata->siglen) !=
savedata->siglen) {
fprintf(stderr, "read error: %s from %s:%d: "
"block %lld (0x%llx)
",
- strerror(errno), __FUNCTION__, __LINE__,
+ gzerror(gzin_fd, &gzerr), __FUNCTION__, __LINE__,
(unsigned long long)savedata->blk,
(unsigned long long)savedata->blk);
exit(-1);
@@ -883,15 +1012,16 @@ static void complain(const char *complaint)
void restoremeta(const char *in_fn, const char *out_device,
uint64_t printblocksonly)
{
- int in_fd, error;
+ int error;
+ gzFile gzfd;
termlines = 0;
if (!in_fn)
complain("No source file specified.");
if (!printblocksonly && !out_device)
complain("No destination file system specified.");
- in_fd = open(in_fn, O_RDONLY);
- if (in_fd < 0)
+ gzfd = gzopen(in_fn, "rb");
+ if (!gzfd)
die("Can't open source file %s: %s
",
in_fn, strerror(errno));
@@ -908,13 +1038,13 @@ void restoremeta(const char *in_fn, const char *out_device,
die("Can't allocate memory for the restore operation.
");
diff --git a/gfs2/man/gfs2_edit.8 b/gfs2/man/gfs2_edit.8
index d133956..cade7c8 100644
--- a/gfs2/man/gfs2_edit.8
+++ b/gfs2/man/gfs2_edit.8
@@ -140,7 +140,9 @@ Print program version information only.
.TP
fB-xfP
Print in hex mode.
-
+.TP
+fB-z <0-9>fP
+Compress metadata with gzip compression level 1 to 9 (default 9). 0 means no compression at all.
.TP
fBrgfP fI<rg>fR fI<device>fR
Print the contents of Resource Group fI<rg>fR on fI<device>fR.
@@ -171,35 +173,35 @@ that may be contained in the files. This option works quickly by
using the system bitmap blocks in the resource groups to determine the
location of all the metadata. If there is corruption
in the bitmaps, resource groups or rindex file, this method may fail and
-you may need to use the savemetaslow option.
-The destination file is not compressed. You may want to compress it
-with a program such as bzip2 before sending it for analysis.
+you may need to use the savemetaslow option. The destination file is
+compressed using gzip unless -z 0 is specified.
.TP
fBsavemetaslowfP fI<device>fR fI<filename>fR
Save off GFS2 metadata, as with the savemeta option, examining every
block in the file system for metadata. This option is less prone to failure
due to file system corruption than the savemeta option, but it is
-extremely slow.
+extremely slow. The destination file is compressed using gzip unless
+-z 0 is specified.
.TP
fBsavergsfP fI<device>fR fI<filename>fR
Save off only the GFS2 resource group metadata for the file system on the
-specified device to a file given by <filename>.
+specified device to a file given by <filename>. The destination file is
+compressed using gzip unless -z 0 is specified.
.TP
fBrestoremetafP fI<filename>fR fI<dest device>fR
-Take a file created with the savemeta option and restores its
-contents on top of the specified destination device. fBWARNINGfP:
-When you use this option, the file system and all data on the
-destination device is destroyed. Since only metadata (but no data)
-is restored, every file in the resulting file system is likely to be
-corrupt. The ONLY purpose of this option is to examine and debug file
-system problems by restoring and examining the state of the saved metadata.
-If the destination file system is the same size or larger than the source
-file system where the metadata was saved, the resulting file system
-will be the same size as the source. If the destination device is
-smaller than the source file system, gfs2_edit will restore as much as
-it can, then quit, leaving you with a file system that probably will not
-mount, but from which you might still be able to figure out what is
-wrong with the source file system.
+Take a compressed or uncompressed file created with the savemeta option and
+restores its contents on top of the specified destination device.
+fBWARNINGfP: When you use this option, the file system and all data on the
+destination device is destroyed. Since only metadata (but no data) is
+restored, every file in the resulting file system is likely to be corrupt. The
+ONLY purpose of this option is to examine and debug file system problems by
+restoring and examining the state of the saved metadata. If the destination
+file system is the same size or larger than the source file system where the
+metadata was saved, the resulting file system will be the same size as the
+source. If the destination device is smaller than the source file system,
+gfs2_edit will restore as much as it can, then quit, leaving you with a file
+system that probably will not mount, but from which you might still be able to
+figure out what is wrong with the source file system.
.SH INTERACTIVE MODE
If you specify a device on the gfs2_edit command line and you specify
--
1.7.4.1