[Cluster-devel] [PATCH 2/3] gfs2-utils tests: Add helper program to damage rgrps

Andrew Price anprice at redhat.com
Tue Mar 29 11:26:23 UTC 2016


Add a small program named nukerg, based on Bob Peterson's program of the
same name, which can be used to scribble over resource groups and rindex
entries to test resource group repair in fsck.gfs2.

Signed-off-by: Andrew Price <anprice at redhat.com>
---
 .gitignore        |   1 +
 tests/Makefile.am |  13 ++
 tests/nukerg.c    | 348 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 362 insertions(+)
 create mode 100644 tests/nukerg.c

diff --git a/.gitignore b/.gitignore
index bc8236e..b615b87 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,6 +47,7 @@ gfs2/tune/tunegfs2
 test-driver
 tests/check_meta
 tests/check_rgrp
+tests/nukerg
 tests/testvol
 tests/testsuite.log
 tests/testsuite.dir
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5a37319..1f98cb4 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -11,6 +11,19 @@ DISTCLEANFILES = \
 
 CLEANFILES = testvol
 
+noinst_PROGRAMS = nukerg
+
+nukerg_SOURCES = nukerg.c
+nukerg_CPPFLAGS = \
+	-D_FILE_OFFSET_BITS=64 \
+	-D_LARGEFILE64_SOURCE \
+	-D_GNU_SOURCE
+nukerg_CFLAGS = \
+	-I$(top_srcdir)/gfs2/libgfs2 \
+	-I$(top_srcdir)/gfs2/include
+nukerg_LDADD = \
+	$(top_builddir)/gfs2/libgfs2/libgfs2.la
+
 if HAVE_CHECK
 UNIT_TESTS = \
 	check_meta \
diff --git a/tests/nukerg.c b/tests/nukerg.c
new file mode 100644
index 0000000..f2018fb
--- /dev/null
+++ b/tests/nukerg.c
@@ -0,0 +1,348 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include <libgfs2.h>
+
+const char *prog_name = "nukerg";
+
+static void usage(void)
+{
+	printf("%s zeroes a list of gfs2 resource groups or rindex entries.\n", prog_name);
+	printf("\n");
+	printf("Usage:\n");
+	printf("    %s -r <num_list>|-i <num_list> /dev/your/device\n", prog_name);
+	printf("\n");
+	printf("      -r: Destroy resource groups\n");
+	printf("      -i: Destroy resource group index (rindex) entries\n");
+	printf("\n");
+	printf("num_list: A comma- or space-separated list of resource group or rindex\n");
+	printf("          numbers (0-indexed) to be destroyed. Use '*' for all.\n");
+	printf("\n");
+	printf("Both -r and -i can be specified. Resource groups will necessarily\n");
+	printf("be destroyed before rindex entries.\n");
+}
+
+#define RGNUMS_SIZE (256)
+
+struct opts {
+	const char *device;
+	unsigned rgnums[RGNUMS_SIZE];
+	size_t rgnum_count;
+	unsigned rinums[RGNUMS_SIZE];
+	size_t rinum_count;
+
+	unsigned got_help:1;
+	unsigned got_device:1;
+	unsigned got_rgnums:1;
+	unsigned got_rinums:1;
+};
+
+static int parse_uint(char *str, unsigned *uint)
+{
+	long long tmpll;
+	char *endptr;
+
+	if (str == NULL || *str == '\0')
+		return 1;
+
+	errno = 0;
+	tmpll = strtoll(str, &endptr, 10);
+	if (errno || tmpll < 0 || tmpll > UINT_MAX || *endptr != '\0')
+		return 1;
+
+	*uint = (unsigned)tmpll;
+	return 0;
+}
+
+#define ALL_RGS ((unsigned)-1)
+
+static size_t parse_uint_list(char *str, unsigned *list, char **tokp, size_t nleft)
+{
+	const char *delim = " ,";
+	size_t i;
+
+	for (i = 0, *tokp = strsep(&str, delim); *tokp != NULL;
+	       i++, *tokp = strsep(&str, delim))
+	{
+		int ret;
+
+		if (i >= nleft)
+			return (size_t)-1; /* List would overflow */
+
+		/* Allow * to denote "all" */
+		if (strcmp(*tokp, "*") == 0) {
+			list[0] = ALL_RGS;
+			return 1;
+		}
+		ret = parse_uint(*tokp, &list[i]);
+		if (ret != 0)
+			return 0; /* Invalid token, *tokp points to it */
+	}
+	return i; /* Will be 0 if str is NULL or empty (*tokp is NULL in that case) */
+}
+
+static int parse_ri_list(char *str, struct opts *opts)
+{
+	unsigned *rinums = opts->rinums + opts->rinum_count;
+	size_t nleft = RGNUMS_SIZE - opts->rinum_count;
+	char *errtok = NULL;
+	size_t n;
+
+	n = parse_uint_list(str, rinums, &errtok, nleft);
+	if (n == 0) {
+		if (errtok == NULL)
+			fprintf(stderr, "No rindex entries given\n");
+		else
+			fprintf(stderr, "Invalid rindex entry: '%s'\n", errtok);
+		return 1;
+	}
+	else if (n == (size_t)-1) {
+		fprintf(stderr, "Too many rindex entries\n");
+		return 1;
+	}
+	opts->rinum_count += n;
+	opts->got_rinums = 1;
+	return 0;
+}
+
+static int parse_rg_list(char *str, struct opts *opts)
+{
+	unsigned *rgnums = opts->rgnums + opts->rgnum_count;
+	size_t nleft = RGNUMS_SIZE - opts->rgnum_count;
+	char *errtok = NULL;
+	size_t n;
+
+	n = parse_uint_list(optarg, rgnums, &errtok, nleft);
+	if (n == 0) {
+		if (errtok == NULL)
+			fprintf(stderr, "No resource groups given\n");
+		else
+			fprintf(stderr, "Invalid resource group number: '%s'\n", errtok);
+		return 1;
+	} else if (n == (size_t)-1) {
+		fprintf(stderr, "Too many resource group numbers\n");
+		return 1;
+	}
+	opts->rgnum_count += n;
+	opts->got_rgnums = 1;
+	return 0;
+}
+
+static int opts_get(int argc, char *argv[], struct opts *opts)
+{
+	int c;
+
+	memset(opts, 0, sizeof(*opts));
+
+	while (1) {
+		c = getopt(argc, argv, "-hi:r:");
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			opts->got_help = 1;
+			usage();
+			return 0;
+		case 'i':
+			if (parse_ri_list(optarg, opts))
+				return 1;
+			break;
+		case 'r':
+			if (parse_rg_list(optarg, opts))
+				return 1;
+			break;
+		case 1:
+			if (opts->got_device) {
+				fprintf(stderr, "More than one device specified. ");
+				fprintf(stderr, "Try -h for help.\n");
+				return 1;
+			}
+			opts->device = optarg;
+			opts->got_device = 1;
+			break;
+		case '?':
+		default:
+			usage();
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int nuke_rgs(struct gfs2_sbd *sdp, lgfs2_rgrps_t rgs, unsigned *rgnums, size_t count)
+{
+	struct gfs2_rgrp blankrg;
+	lgfs2_rgrp_t rg;
+	unsigned i;
+
+	memset(&blankrg, 0, sizeof(blankrg));
+
+	for (rg = lgfs2_rgrp_first(rgs), i = 0; rg; rg = lgfs2_rgrp_next(rg), i++) {
+		const struct gfs2_rindex *ri = lgfs2_rgrp_index(rg);
+		unsigned j;
+
+		for (j = 0; j < count; j++) {
+			uint64_t addr = ri->ri_addr;
+			off_t off = addr * sdp->bsize;
+			ssize_t bytes;
+
+			if (i != rgnums[j] && rgnums[j] != ALL_RGS)
+				continue;
+
+			printf("Nuking rg #%u at block 0x%"PRIx64"\n", i, addr);
+
+			bytes = pwrite(sdp->device_fd, &blankrg, sizeof(blankrg), off);
+			if (bytes != sizeof(blankrg)) {
+				fprintf(stderr, "Write failed (%u bytes): %s",
+				             (unsigned)bytes, strerror(errno));
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+static int nuke_ris(struct gfs2_sbd *sdp, lgfs2_rgrps_t rgs, unsigned *rinums, size_t count)
+{
+	struct gfs2_rindex blankri;
+	lgfs2_rgrp_t rg;
+	unsigned i;
+
+	memset(&blankri, 0, sizeof(blankri));
+
+	for (rg = lgfs2_rgrp_first(rgs), i = 0; rg; rg = lgfs2_rgrp_next(rg), i++) {
+		unsigned j;
+
+		for (j = 0; j < count; j++) {
+			int bytes = 0;
+			uint64_t off;
+
+			if (i != rinums[j] && rinums[j] != ALL_RGS)
+				continue;
+
+			printf("Nuking rindex entry %u.\n", i);
+
+			off = i * sizeof(struct gfs2_rindex);
+			bytes = gfs2_writei(sdp->md.riinode, &blankri, off,
+						sizeof(struct gfs2_rindex));
+			if (bytes != sizeof(struct gfs2_rindex)) {
+				fprintf(stderr, "Write failed (%d bytes): %s",
+						       bytes, strerror(errno));
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+static lgfs2_rgrps_t read_rindex(struct gfs2_sbd *sdp)
+{
+	lgfs2_rgrps_t rgs;
+	unsigned rgcount;
+	unsigned i;
+
+	gfs2_lookupi(sdp->master_dir, "rindex", 6, &sdp->md.riinode);
+	if (sdp->md.riinode == NULL) {
+		perror("Failed to look up rindex");
+		return NULL;
+	}
+	rgs = lgfs2_rgrps_init(sdp, 0, 0);
+	if (rgs == NULL) {
+		fprintf(stderr, "Failed to initialize resource group set: %s\n",
+		                                                strerror(errno));
+		return NULL;
+	}
+	rgcount = sdp->md.riinode->i_di.di_size / sizeof(struct gfs2_rindex);
+	for (i = 0; i < rgcount; i++) {
+		const struct gfs2_rindex *ri;
+
+		ri = lgfs2_rindex_read_one(sdp->md.riinode, rgs, i);
+		if (ri == NULL) {
+			fprintf(stderr, "Failed to read rindex entry %u: %s\n",
+			                                    i, strerror(errno));
+			return NULL;
+		}
+	}
+	printf("%u rindex entries found.\n", i);
+	return rgs;
+}
+
+static int fill_super_block(struct gfs2_sbd *sdp)
+{
+	sdp->sd_sb.sb_bsize = GFS2_BASIC_BLOCK;
+	sdp->bsize = sdp->sd_sb.sb_bsize;
+
+	if (compute_constants(sdp) != 0) {
+		fprintf(stderr, "Failed to compute file system constants.\n");
+		return 1;
+	}
+	if (read_sb(sdp) != 0) {
+		perror("Failed to read superblock\n");
+		return 1;
+	}
+	sdp->master_dir = lgfs2_inode_read(sdp, sdp->sd_sb.sb_master_dir.no_addr);
+	if (sdp->master_dir == NULL) {
+		fprintf(stderr, "Failed to read master directory inode.\n");
+		return 1;
+	}
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	struct gfs2_sbd sbd;
+	lgfs2_rgrps_t rgs;
+	struct opts opts;
+	int ret;
+
+	memset(&sbd, 0, sizeof(sbd));
+
+	ret = opts_get(argc, argv, &opts);
+	if (ret != 0 || opts.got_help)
+		exit(ret);
+
+	if (!opts.got_device) {
+		fprintf(stderr, "No device specified.\n");
+		usage();
+		exit(1);
+	}
+	if (!opts.got_rgnums && !opts.got_rinums) {
+		fprintf(stderr, "No resource groups or rindex entries specified.\n");
+		usage();
+		exit(1);
+	}
+	if ((sbd.device_fd = open(opts.device, O_RDWR)) < 0) {
+		perror(opts.device);
+		exit(1);
+	}
+	if (fill_super_block(&sbd) != 0)
+		exit(1);
+
+	rgs = read_rindex(&sbd);
+	if (rgs == NULL)
+		exit(1);
+
+	if (opts.got_rgnums && nuke_rgs(&sbd, rgs, opts.rgnums, opts.rgnum_count) != 0)
+		exit(1);
+
+	if (opts.got_rinums && nuke_ris(&sbd, rgs, opts.rinums, opts.rinum_count) != 0)
+		exit(1);
+
+	inode_put(&sbd.md.riinode);
+	inode_put(&sbd.master_dir);
+	lgfs2_rgrps_free(&rgs);
+	fsync(sbd.device_fd);
+	close(sbd.device_fd);
+	exit(0);
+}
+
+/* This function is for libgfs2's sake. */
+void print_it(const char *label, const char *fmt, const char *fmt2, ...) {}
-- 
2.4.3




More information about the Cluster-devel mailing list