diff mbox

[7/9] btrfs-progs: modify: Add support to corrupt specified mirror

Message ID 20170417032642.30770-8-quwenruo@cn.fujitsu.com (mailing list archive)
State New, archived
Headers show

Commit Message

Qu Wenruo April 17, 2017, 3:26 a.m. UTC
Add a subcommand, mirror, for btrfs-modify.

The mirror subcommand is used to modify specified mirror (P/Q included) of
a logical range.

Currently the mirror can be specified by --logical, --length and
--mirror.

Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
---
 Documentation/btrfs-modify.asciidoc |  28 +++-
 Makefile                            |   6 +-
 modify/main.c                       |   2 +
 modify/mirror.c                     | 282 ++++++++++++++++++++++++++++++++++++
 modify/modify_commands.h            |  25 ++++
 5 files changed, 339 insertions(+), 4 deletions(-)
 create mode 100644 modify/mirror.c
 create mode 100644 modify/modify_commands.h
diff mbox

Patch

diff --git a/Documentation/btrfs-modify.asciidoc b/Documentation/btrfs-modify.asciidoc
index 7d6e8e73..ae2ada65 100644
--- a/Documentation/btrfs-modify.asciidoc
+++ b/Documentation/btrfs-modify.asciidoc
@@ -16,11 +16,35 @@  experienced user to fix filesystem, or corrupt fs for test purpose.
 
 COMMANDS
 --------
-Nothing yet
+*mirror*::
+Modify specified mirror, including RAID56 P/Q stripe.
 
 OPTIONS
 -------
-Nothing yet
+*Mirror command options*::
+--logical <address>::::
+Specify the start logical address to modify. Must be aligned to sectorsize.
++
+Must be specified
+
+--length <length>::::
+Specify the length to modify. Must be aligned to sectorsize.
++
+Default value is the sectorsize of the filesystem, if not specified.
+
+--stripe <stripe number>::::
+Specify the stripe number to modify.
++
+0 for 1st data stripe. All profiles support stripe number 0. +
+1 for backup data stripe. Only mirror based profiles (DUP,RAID1,RAID10) support
+stripe number 1.
+P for RAID56 1st parity stripe. Only RAID5 and RAID6 support stripe number P.
+Q for RAID6 2nd parity stripe.
+
+EXIT STATUS
+-----------
+*btrfs-modify* returns a zero exit status if all its operations succeed.
+None zero is returned if any error happens.
 
 AVAILABILITY
 ------------
diff --git a/Makefile b/Makefile
index 633c4447..18ea6051 100644
--- a/Makefile
+++ b/Makefile
@@ -103,6 +103,7 @@  cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \
 	       cmds-property.o cmds-fi-usage.o cmds-inspect-dump-tree.o \
 	       cmds-inspect-dump-super.o cmds-inspect-tree-stats.o cmds-fi-du.o \
 	       mkfs/common.o
+modify_objects = modify/mirror.o
 libbtrfs_objects = send-stream.o send-utils.o kernel-lib/rbtree.o btrfs-list.o \
 		   kernel-lib/crc32c.o messages.o \
 		   uuid-tree.o utils-lib.o rbtree-utils.o
@@ -394,11 +395,11 @@  btrfs-image.static: image/main.static.o $(static_objects) $(static_libbtrfs_obje
 	@echo "    [LD]     $@"
 	$(Q)$(CC) $(STATIC_CFLAGS) -o $@ $^ $(STATIC_LDFLAGS) $(STATIC_LIBS) $(STATIC_LIBS_COMP)
 
-btrfs-modify: modify/main.o $(objects) $(libs_static)
+btrfs-modify: modify/main.o $(modify_objects) $(objects) $(libs_static)
 	@echo "    [LD]     $@"
 	$(Q)$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBS_COMP)
 
-btrfs-modify.static: modify/main.static.o $(static_objects) $(static_libbtrfs_objects)
+btrfs-modify.static: modify/main.static.o $(static_objects) $(static_libbtrfs_objects) $(modify_objects)
 	@echo "    [LD]     $@"
 	$(Q)$(CC) $(CFLAGS) -o $@ $^ $(STATIC_LDFLAGS) $(STATIC_LIBS) $(STATIC_LIBS_COMP)
 
@@ -514,6 +515,7 @@  clean: $(CLEANDIRS)
 		image/*.o image/*.o.d \
 		convert/*.o convert/*.o.d \
 		mkfs/*.o mkfs/*.o.d \
+		modify/*.o modify/*.o.d \
 	      dir-test ioctl-test quick-test library-test library-test-static \
 	      btrfs.static mkfs.btrfs.static fssum \
 	      $(check_defs) \
diff --git a/modify/main.c b/modify/main.c
index 107e2e7e..eca93fc2 100644
--- a/modify/main.c
+++ b/modify/main.c
@@ -33,6 +33,7 @@ 
 #include "help.h"
 #include "commands.h"
 #include "crc32c.h"
+#include "modify/modify_commands.h"
 
 const char * const modify_group_usage[] = {
 	"btrfs-modify <command> <dest_options> <device>",
@@ -53,6 +54,7 @@  static const char modify_group_info[] =
 
 static const struct cmd_group modify_cmd_group = {
 	modify_group_usage, modify_group_info, {
+		{ "mirror", modify_mirror, modify_mirror_usage, NULL, 0 },
 		NULL_CMD_STRUCT
 	},
 };
diff --git a/modify/mirror.c b/modify/mirror.c
new file mode 100644
index 00000000..f17dc9a7
--- /dev/null
+++ b/modify/mirror.c
@@ -0,0 +1,282 @@ 
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <strings.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <getopt.h>
+#include <unistd.h>
+#include "utils.h"
+#include "ctree.h"
+#include "kerncompat.h"
+#include "help.h"
+#include "disk-io.h"
+#include "volumes.h"
+#include "modify/modify_commands.h"
+
+const char * const modify_mirror_usage[] = {
+	"btrfs-modify mirror <options> <device>",
+	"Modify specified mirror/parity of a filesystem(unmounted).",
+	"<options> are used to specify the destination.",
+	"See 'btrfs-modify'(8) for supported options",
+	NULL
+};
+
+#define STRIPE_UNINITILIZED	(-1)
+#define STRIPE_P		(-2)
+#define STRIPE_Q		(-3)
+
+static char write_buf[BTRFS_STRIPE_LEN] = { 0 };
+
+static int strtostripe(const char *str)
+{
+	u32 tmp;
+
+	if (!strncasecmp(str, "p", strlen("p") + 1))
+		return STRIPE_P;
+	if (!strncasecmp(str, "q", strlen("q") + 1))
+		return STRIPE_Q;
+	tmp = arg_strtou32(str);
+	return (int)tmp;
+}
+
+static int write_range_fd(int fd, u64 offset, u64 len)
+{
+	u64 cur = offset;
+	int ret;
+
+	while (cur < offset + len) {
+		u64 write_len = min(offset + len - cur, (u64)BTRFS_STRIPE_LEN);
+		ret = pwrite(fd, write_buf, write_len, cur);
+		if (ret < 0)
+			return -errno;
+		cur += ret;
+	}
+	return 0;
+}
+
+static int corrupt_mapped_range(struct btrfs_fs_info *fs_info,
+				struct btrfs_map_block *map, u64 logical,
+				u64 len, int stripe_num)
+{
+	u64 mirror_profiles = BTRFS_BLOCK_GROUP_RAID1 |
+			      BTRFS_BLOCK_GROUP_RAID10 |
+			      BTRFS_BLOCK_GROUP_DUP;
+	u64 parity_profiles = BTRFS_BLOCK_GROUP_RAID5 |
+			      BTRFS_BLOCK_GROUP_RAID6;
+	int i;
+	int ret;
+
+	/* Check stripe_num with map->profiles */
+	if (!(map->type & mirror_profiles) && stripe_num > 0) {
+		error("logical range [%llu, %llu) doesn't have extra mirror",
+			map->start, map->length);
+		return -EINVAL;
+	}
+	if (stripe_num == STRIPE_P && !(map->type & parity_profiles)) {
+		error("logical range [%llu, %llu) doesn't have P stripe",
+			map->start, map->length);
+		return -EINVAL;
+	}
+	if (stripe_num == STRIPE_Q && !(map->type & BTRFS_BLOCK_GROUP_RAID6)) {
+		error("logical range [%llu, %llu) doesn't have Q stripe",
+			map->start, map->length);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < map->num_stripes; i++) {
+		struct btrfs_map_stripe *stripe = &map->stripes[i];
+
+		u64 corrupt_logical = 0;
+		u64 corrupt_phy;
+		u64 corrupt_len;
+
+		if (stripe_num == STRIPE_P || stripe_num == STRIPE_Q) {
+			u64 dest_logical;
+
+			if (stripe_num == STRIPE_P)
+				dest_logical = BTRFS_RAID5_P_STRIPE;
+			else
+				dest_logical = BTRFS_RAID6_Q_STRIPE;
+			if (stripe->logical != dest_logical)
+				continue;
+
+			/* For P/Q, corrupt the whole stripe */
+			corrupt_phy = stripe->physical;
+			corrupt_len = stripe->length;
+		} else  {
+			/* Skip unrelated mirror stripe */
+			if (map->type & mirror_profiles && i % 2 != stripe_num)
+				continue;
+
+			corrupt_logical = max(stripe->logical, logical);
+			corrupt_phy = corrupt_logical - stripe->logical +
+					stripe->physical;
+			corrupt_len = min(stripe->logical + stripe->length,
+					logical + len) - corrupt_logical;
+		}
+		ret = write_range_fd(stripe->dev->fd, corrupt_phy,
+				     corrupt_len);
+		if (ret < 0) {
+			if (stripe_num == STRIPE_P || stripe_num == STRIPE_Q)
+				error(
+			"failded to write %s stripe for full stripe [%llu, %llu): %s",
+					(stripe_num == STRIPE_P ? "P" : "Q"),
+					map->start, map->start + map->length,
+					strerror(-ret));
+			else
+				error(
+			"failed to write data for logical range [%llu, %llu): %s",
+					corrupt_logical,
+					corrupt_logical + corrupt_len,
+					strerror(-ret));
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static int modify_logical(struct btrfs_fs_info *fs_info, u64 logical, u64 len,
+			  int stripe)
+{
+	u32 sectorsize = fs_info->tree_root->sectorsize;
+	u64 cur;
+	int ret;
+
+	if (!IS_ALIGNED(logical, sectorsize)) {
+		error("logical address %llu is not aligned to sectorsize %d",
+			logical, sectorsize);
+		return -EINVAL;
+	}
+	if (!IS_ALIGNED(len, sectorsize)) {
+		error("length %llu is not aligned to sectorsize %d",
+			len, sectorsize);
+		return -EINVAL;
+	}
+	/* Current btrfs only support 1 mirror */
+	if (stripe > 1) {
+		error("btrfs only supports 1 mirror, stripe number %d is invalid",
+			stripe);
+		return -EINVAL;
+	}
+
+	cur = logical;
+
+	while (cur < logical + len) {
+		struct btrfs_map_block *map;
+
+		ret = __btrfs_map_block_v2(fs_info, WRITE, cur,
+					   logical + len - cur, &map);
+		if (ret < 0) {
+			error("failed to map logical range [%llu, %llu): %s",
+				cur, logical + len, strerror(-ret));
+			return ret;
+		}
+		ret = corrupt_mapped_range(fs_info, map, cur,
+					   logical + len - cur, stripe);
+		if (ret < 0) {
+			error("failed to modify on-disk data for range [%llu, %llu): %s",
+				cur, logical + len, strerror(-ret));
+			free(map);
+			return ret;
+		}
+		cur = map->start + map->length;
+	}
+	return 0;
+}
+
+int modify_mirror(int argc, char **argv)
+{
+	struct btrfs_fs_info *fs_info;
+	char *device;
+	u64 length = (u64)-1;
+	u64 logical = (u64)-1;
+	int stripe = STRIPE_UNINITILIZED;
+	int ret;
+
+	while (1) {
+		int c;
+		enum { GETOPT_VAL_LOGICAL = 257, GETOPT_VAL_LENGTH,
+			GETOPT_VAL_STRIPE };
+		static const struct option long_options[] = {
+			{ "logical", required_argument, NULL,
+				GETOPT_VAL_LOGICAL },
+			{ "length", required_argument, NULL,
+				GETOPT_VAL_LENGTH },
+			{ "stripe", required_argument, NULL, GETOPT_VAL_STRIPE }
+		};
+
+		c = getopt_long(argc, argv, "", long_options, NULL);
+		if (c < 0)
+			break;
+		switch (c) {
+		case GETOPT_VAL_LOGICAL:
+			logical = arg_strtou64(optarg);
+			break;
+		case GETOPT_VAL_LENGTH:
+			length = arg_strtou64(optarg);
+			break;
+		case GETOPT_VAL_STRIPE:
+			stripe = strtostripe(optarg);
+			break;
+		case '?':
+		case 'h':
+			usage(modify_mirror_usage);
+		}
+	}
+	if (check_argc_exact(argc - optind, 1))
+		usage(modify_mirror_usage);
+	device = argv[optind];
+	
+	ret = check_mounted(device);
+	if (ret < 0) {
+		error("could not check mount status for device %s: %s",
+			device, strerror(-ret));
+		return ret;
+	}
+	if (ret > 0) {
+		error("%s is currently mounted, aborting", device);
+		return -EINVAL;
+	}
+	if (logical == (u64)-1) {
+		error("--logical must be specified");
+		return -EINVAL;
+	}
+	if (stripe == STRIPE_UNINITILIZED) {
+		printf("--stripe not specified, fallback to 0 (1st stripe)\n");
+		stripe = 0;
+	}
+
+	fs_info = open_ctree_fs_info(device, 0, 0, 0, OPEN_CTREE_WRITES);
+	if (!fs_info) {
+		error("failed to open btrfs on device %s\n", device);
+		return -EIO;
+	}
+	if (length == (u64)-1) {
+		printf("--length not specified, fallback to sectorsize (%d)\n",
+			fs_info->tree_root->sectorsize);
+		length = fs_info->tree_root->sectorsize;
+	}
+	ret = modify_logical(fs_info, logical, length, stripe);
+	if (ret < 0)
+		error("failed to modify btrfs: %s", strerror(-ret));
+	else
+		printf("Succeeded in modifying specified mirror\n");
+
+	close_ctree(fs_info->tree_root);
+	return ret;
+}
diff --git a/modify/modify_commands.h b/modify/modify_commands.h
new file mode 100644
index 00000000..0d3e188f
--- /dev/null
+++ b/modify/modify_commands.h
@@ -0,0 +1,25 @@ 
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef __BTRFS_MODIFY_COMMANDS_H__
+#define __BTRFS_MODIFY_COMMANDS_H__
+
+#include "commands.h"
+
+extern const char * const modify_mirror_usage[];
+int modify_mirror(int argc, char **argv);
+
+#endif