All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/5] Btrfs-progs: setup framework to corrupt specific fields of an inode
@ 2013-09-09 20:41 Josef Bacik
  2013-09-09 20:41 ` [PATCH 2/5] Btrfs-progs: allow fsck to fix directory isize errors Josef Bacik
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Josef Bacik @ 2013-09-09 20:41 UTC (permalink / raw)
  To: linux-btrfs

A user reported a problem with his fs where he had a bogus isize on his
directory.  In order to make sure my patch for fsck fixes this properly I needed
to be able to corrupt an inode like this, which is what this patch is for.
Eventually I want to extend this to corrupt everything so we can integrate tests
into btrfs-progs to run btrfsck against to make sure we don't regress on fixing
things with btrfsck.  Thanks,

Signed-off-by: Josef Bacik <jbacik@fusionio.com>
---
 btrfs-corrupt-block.c |  125 ++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 118 insertions(+), 7 deletions(-)

diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c
index 9b72ad9..1a7b6ca 100644
--- a/btrfs-corrupt-block.c
+++ b/btrfs-corrupt-block.c
@@ -32,6 +32,8 @@
 #include "list.h"
 #include "version.h"
 
+#define FIELD_BUF_LEN 80
+
 struct extent_buffer *debug_corrupt_block(struct btrfs_root *root, u64 bytenr,
 				     u32 blocksize, int copy)
 {
@@ -96,6 +98,9 @@ static void print_usage(void)
 	fprintf(stderr, "\t-E The whole extent tree to be corrupted\n");
 	fprintf(stderr, "\t-u Given chunk item to be corrupted\n");
 	fprintf(stderr, "\t-U The whole chunk tree to be corrupted\n");
+	fprintf(stderr, "\t-i The inode item to corrupt (must also specify "
+		"the field to corrupt)\n");
+	fprintf(stderr, "\t-f The field in the item to corrupt\n");
 	exit(1);
 }
 
@@ -279,6 +284,83 @@ static void btrfs_corrupt_extent_tree(struct btrfs_trans_handle *trans,
 	}
 }
 
+enum btrfs_inode_field {
+	BTRFS_INODE_FIELD_ISIZE,
+	BTRFS_INODE_FIELD_BAD,
+};
+
+static enum btrfs_inode_field convert_field(char *field)
+{
+	if (!strncmp(field, "isize", FIELD_BUF_LEN))
+		return BTRFS_INODE_FIELD_ISIZE;
+	return BTRFS_INODE_FIELD_BAD;
+}
+
+static int corrupt_inode(struct btrfs_trans_handle *trans,
+			 struct btrfs_root *root, u64 inode, char *field)
+{
+	struct btrfs_inode_item *ei;
+	struct btrfs_path *path;
+	struct btrfs_key key;
+	enum btrfs_inode_field corrupt_field = convert_field(field);
+	u64 bogus;
+	u64 orig;
+	int ret;
+
+	if (corrupt_field == BTRFS_INODE_FIELD_BAD) {
+		fprintf(stderr, "Invalid field %s\n", field);
+		return -EINVAL;
+	}
+
+	key.objectid = inode;
+	key.type = BTRFS_INODE_ITEM_KEY;
+	key.offset = (u64)-1;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+	if (ret < 0)
+		goto out;
+	if (ret) {
+		if (!path->slots[0]) {
+			fprintf(stderr, "Couldn't find inode %Lu\n", inode);
+			ret = -ENOENT;
+			goto out;
+		}
+		path->slots[0]--;
+		ret = 0;
+	}
+
+	btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+	if (key.objectid != inode) {
+		fprintf(stderr, "Couldn't find inode %Lu\n", inode);
+		ret = -ENOENT;
+		goto out;
+	}
+
+	ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
+			    struct btrfs_inode_item);
+	switch (corrupt_field) {
+	case BTRFS_INODE_FIELD_ISIZE:
+		orig = btrfs_inode_size(path->nodes[0], ei);
+		do {
+			bogus = rand();
+		} while (bogus == orig);
+
+		btrfs_set_inode_size(path->nodes[0], ei, bogus);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	btrfs_mark_buffer_dirty(path->nodes[0]);
+out:
+	btrfs_free_path(path);
+	return ret;
+}
+
 static struct option long_options[] = {
 	/* { "byte-count", 1, NULL, 'b' }, */
 	{ "logical", 1, NULL, 'l' },
@@ -289,6 +371,8 @@ static struct option long_options[] = {
 	{ "keys", 0, NULL, 'k' },
 	{ "chunk-record", 0, NULL, 'u' },
 	{ "chunk-tree", 0, NULL, 'U' },
+	{ "inode", 1, NULL, 'i'},
+	{ "field", 1, NULL, 'f'},
 	{ 0, 0, 0, 0}
 };
 
@@ -449,12 +533,15 @@ int main(int ac, char **av)
 	int corrupt_block_keys = 0;
 	int chunk_rec = 0;
 	int chunk_tree = 0;
+	u64 inode = 0;
+	char field[FIELD_BUF_LEN];
 
+	field[0] = '\0';
 	srand(128);
 
 	while(1) {
 		int c;
-		c = getopt_long(ac, av, "l:c:b:eEkuU", long_options,
+		c = getopt_long(ac, av, "l:c:b:eEkuUi:f:", long_options,
 				&option_index);
 		if (c < 0)
 			break;
@@ -464,7 +551,7 @@ int main(int ac, char **av)
 				break;
 			case 'c':
 				copy = atoi(optarg);
-				if (copy == 0) {
+				if (copy <= 0) {
 					fprintf(stderr,
 						"invalid copy number\n");
 					print_usage();
@@ -492,6 +579,16 @@ int main(int ac, char **av)
 				break;
 			case 'U':
 				chunk_tree = 1;
+			case 'i':
+				inode = atoll(optarg);
+				if (inode == 0) {
+					fprintf(stderr,
+						"invalid inode number\n");
+					print_usage();
+				}
+				break;
+			case 'f':
+				strncpy(field, optarg, FIELD_BUF_LEN);
 				break;
 			default:
 				print_usage();
@@ -500,11 +597,6 @@ int main(int ac, char **av)
 	ac = ac - optind;
 	if (ac == 0)
 		print_usage();
-	if (logical == (u64)-1 && !(extent_tree || chunk_tree))
-		print_usage();
-	if (copy < 0)
-		print_usage();
-
 	dev = av[optind];
 
 	radix_tree_init();
@@ -517,6 +609,9 @@ int main(int ac, char **av)
 	}
 	if (extent_rec) {
 		struct btrfs_trans_handle *trans;
+
+		if (logical == (u64)-1)
+			print_usage();
 		trans = btrfs_start_transaction(root, 1);
 		ret = corrupt_extent (trans, root, logical, 0);
 		btrfs_commit_transaction(trans, root);
@@ -535,6 +630,8 @@ int main(int ac, char **av)
 		struct btrfs_path *path;
 		int del;
 
+		if (logical == (u64)-1)
+			print_usage();
 		del = rand() % 3;
 		path = btrfs_alloc_path();
 
@@ -560,6 +657,20 @@ int main(int ac, char **av)
 		btrfs_commit_transaction(trans, root);
 		goto out_close;
 	}
+	if (inode) {
+		struct btrfs_trans_handle *trans;
+
+		if (!strlen(field))
+			print_usage();
+		printf("corrupting inode\n");
+		trans = btrfs_start_transaction(root, 1);
+		ret = corrupt_inode(trans, root, inode, field);
+		btrfs_commit_transaction(trans, root);
+		goto out_close;
+	}
+
+	if (logical == (u64)-1)
+		print_usage();
 
 	if (bytes == 0)
 		bytes = root->sectorsize;
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/5] Btrfs-progs: allow fsck to fix directory isize errors
  2013-09-09 20:41 [PATCH 1/5] Btrfs-progs: setup framework to corrupt specific fields of an inode Josef Bacik
@ 2013-09-09 20:41 ` Josef Bacik
  2013-09-09 20:41 ` [PATCH 3/5] Btrfs-progs: make btrfsck fix backrefs that are broken Josef Bacik
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Josef Bacik @ 2013-09-09 20:41 UTC (permalink / raw)
  To: linux-btrfs

A user reported a problem where he was unable to rmdir an empty directory.  This
is because his isize was wrong.  This patch will fix this sort of corruption and
allow him to rmdir his directory.  Thanks

Signed-off-by: Josef Bacik <jbacik@fusionio.com>
---
 cmds-check.c |   86 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 80 insertions(+), 6 deletions(-)

diff --git a/cmds-check.c b/cmds-check.c
index df18c43..6cbd5a6 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -221,6 +221,8 @@ struct walk_control {
 	int root_level;
 };
 
+static void reset_cached_block_groups(struct btrfs_fs_info *fs_info);
+
 static u8 imode_to_type(u32 imode)
 {
 #define S_SHIFT 12
@@ -1344,8 +1346,64 @@ out:
 	return ret;
 }
 
+static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
+{
+	struct btrfs_trans_handle *trans;
+	struct btrfs_path *path;
+	struct btrfs_inode_item *ei;
+	struct btrfs_key key;
+	int ret;
+
+	/* So far we just fix dir isize wrong */
+	if (!(rec->errors & I_ERR_DIR_ISIZE_WRONG))
+		return 1;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	trans = btrfs_start_transaction(root, 1);
+	if (IS_ERR(trans)) {
+		btrfs_free_path(path);
+		return PTR_ERR(trans);
+	}
+
+	key.objectid = rec->ino;
+	key.type = BTRFS_INODE_ITEM_KEY;
+	key.offset = (u64)-1;
+
+	ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+	if (ret < 0)
+		goto out;
+	if (ret) {
+		if (!path->slots[0]) {
+			ret = -ENOENT;
+			goto out;
+		}
+		path->slots[0]--;
+		ret = 0;
+	}
+	btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+	if (key.objectid != rec->ino) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
+			    struct btrfs_inode_item);
+	btrfs_set_inode_size(path->nodes[0], ei, rec->found_size);
+	btrfs_mark_buffer_dirty(path->nodes[0]);
+	rec->errors &= ~I_ERR_DIR_ISIZE_WRONG;
+	printf("reset isize for dir %Lu root %Lu\n", rec->ino,
+	       root->root_key.objectid);
+out:
+	btrfs_commit_transaction(trans, root);
+	btrfs_free_path(path);
+	return ret;
+}
+
 static int check_inode_recs(struct btrfs_root *root,
-			    struct cache_tree *inode_cache)
+			    struct cache_tree *inode_cache, int repair)
 {
 	struct cache_extent *cache;
 	struct ptr_node *node;
@@ -1400,6 +1458,15 @@ static int check_inode_recs(struct btrfs_root *root,
 			}
 		}
 
+		if (repair) {
+			ret = try_repair_inode(root, rec);
+			if (ret == 0 && can_free_inode_rec(rec)) {
+				free_inode_rec(rec);
+				continue;
+			}
+			ret = 0;
+		}
+
 		error++;
 		if (!rec->found_inode_item)
 			rec->errors |= I_ERR_NO_INODE_ITEM;
@@ -1741,7 +1808,7 @@ static int process_root_ref(struct extent_buffer *eb, int slot,
 
 static int check_fs_root(struct btrfs_root *root,
 			 struct cache_tree *root_cache,
-			 struct walk_control *wc)
+			 struct walk_control *wc, int repair)
 {
 	int ret = 0;
 	int wret;
@@ -1811,7 +1878,7 @@ static int check_fs_root(struct btrfs_root *root,
 				root_node.current);
 	}
 
-	ret = check_inode_recs(root, &root_node.inode_cache);
+	ret = check_inode_recs(root, &root_node.inode_cache, repair);
 	return ret;
 }
 
@@ -1827,7 +1894,8 @@ static int fs_root_objectid(u64 objectid)
 }
 
 static int check_fs_roots(struct btrfs_root *root,
-			  struct cache_tree *root_cache)
+			  struct cache_tree *root_cache,
+			  int repair)
 {
 	struct btrfs_path path;
 	struct btrfs_key key;
@@ -1838,6 +1906,12 @@ static int check_fs_roots(struct btrfs_root *root,
 	int ret;
 	int err = 0;
 
+	/*
+	 * Just in case we made any changes to the extent tree that weren't
+	 * reflected into the free space cache yet.
+	 */
+	if (repair)
+		reset_cached_block_groups(root->fs_info);
 	memset(&wc, 0, sizeof(wc));
 	cache_tree_init(&wc.shared);
 	btrfs_init_path(&path);
@@ -1864,7 +1938,7 @@ static int check_fs_roots(struct btrfs_root *root,
 				err = 1;
 				goto next;
 			}
-			ret = check_fs_root(tmp_root, root_cache, &wc);
+			ret = check_fs_root(tmp_root, root_cache, &wc, repair);
 			if (ret)
 				err = 1;
 			btrfs_free_fs_root(tmp_root);
@@ -5874,7 +5948,7 @@ int cmd_check(int argc, char **argv)
 		goto out;
 
 	fprintf(stderr, "checking fs roots\n");
-	ret = check_fs_roots(root, &root_cache);
+	ret = check_fs_roots(root, &root_cache, repair);
 	if (ret)
 		goto out;
 
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/5] Btrfs-progs: make btrfsck fix backrefs that are broken
  2013-09-09 20:41 [PATCH 1/5] Btrfs-progs: setup framework to corrupt specific fields of an inode Josef Bacik
  2013-09-09 20:41 ` [PATCH 2/5] Btrfs-progs: allow fsck to fix directory isize errors Josef Bacik
@ 2013-09-09 20:41 ` Josef Bacik
  2013-09-09 20:41 ` [PATCH 4/5] Btrfs-progs: add ability to corrupt file extent disk bytenr Josef Bacik
  2013-09-09 20:41 ` [PATCH 5/5] Btrfs-progs: add make test framework Josef Bacik
  3 siblings, 0 replies; 5+ messages in thread
From: Josef Bacik @ 2013-09-09 20:41 UTC (permalink / raw)
  To: linux-btrfs

If you set an file extent item's disk_bytenr to something completely wrong we
won't be able to fix this if it is the only one who has a ref on the original
disk bytenr.  Our extent records know exactly who is supposed to point at them,
so if we have an extent record that has no backrefs we can go and try to lookup
the backrefs ourselves.  If these backrefs do not point to an extent record that
was actually found then we can be pretty sure this extent record is valid and
the backref is bogus.  Then the verify_backref code can do its thing and reset
the backref to point to the right extent record and we can all carry on.  This
fixes a user reported corruption.  Thanks,

Signed-off-by: Josef Bacik <jbacik@fusionio.com>
---
 cmds-check.c |  160 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 153 insertions(+), 7 deletions(-)

diff --git a/cmds-check.c b/cmds-check.c
index 6cbd5a6..f05c73e 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -56,6 +56,7 @@ struct extent_backref {
 	unsigned int found_extent_tree:1;
 	unsigned int full_backref:1;
 	unsigned int found_ref:1;
+	unsigned int broken:1;
 };
 
 struct data_backref {
@@ -4092,6 +4093,7 @@ struct extent_entry {
 	u64 bytenr;
 	u64 bytes;
 	int count;
+	int broken;
 	struct list_head list;
 };
 
@@ -4119,6 +4121,13 @@ static struct extent_entry *find_most_right_entry(struct list_head *entries)
 		}
 
 		/*
+		 * If there are as many broken entries as entries then we know
+		 * not to trust this particular entry.
+		 */
+		if (entry->broken == entry->count)
+			continue;
+
+		/*
 		 * If our current entry == best then we can't be sure our best
 		 * is really the best, so we need to keep searching.
 		 */
@@ -4129,7 +4138,7 @@ static struct extent_entry *find_most_right_entry(struct list_head *entries)
 		}
 
 		/* Prev == entry, not good enough, have to keep searching */
-		if (prev->count == entry->count)
+		if (!prev->broken && prev->count == entry->count)
 			continue;
 
 		if (!best)
@@ -4244,7 +4253,9 @@ static int repair_ref(struct btrfs_trans_handle *trans,
 		return -EINVAL;
 	}
 
-	if (dback->disk_bytenr > entry->bytenr) {
+	if (dback->node.broken && dback->disk_bytenr != entry->bytenr) {
+		btrfs_set_file_extent_disk_bytenr(leaf, fi, entry->bytenr);
+	} else if (dback->disk_bytenr > entry->bytenr) {
 		u64 off_diff, offset;
 
 		off_diff = dback->disk_bytenr - entry->bytenr;
@@ -4304,7 +4315,9 @@ static int verify_backrefs(struct btrfs_trans_handle *trans,
 	struct extent_entry *entry, *best = NULL;
 	LIST_HEAD(entries);
 	int nr_entries = 0;
+	int broken_entries = 0;
 	int ret = 0;
+	short mismatch = 0;
 
 	/*
 	 * Metadata is easy and the backrefs should always agree on bytenr and
@@ -4346,11 +4359,25 @@ static int verify_backrefs(struct btrfs_trans_handle *trans,
 			list_add_tail(&entry->list, &entries);
 			nr_entries++;
 		}
+
+		/*
+		 * If we only have on entry we may think the entries agree when
+		 * in reality they don't so we have to do some extra checking.
+		 */
+		if (dback->disk_bytenr != rec->start ||
+		    dback->bytes != rec->nr || back->broken)
+			mismatch = 1;
+
+		if (back->broken) {
+			entry->broken++;
+			broken_entries++;
+		}
+
 		entry->count++;
 	}
 
 	/* Yay all the backrefs agree, carry on good sir */
-	if (nr_entries <= 1)
+	if (nr_entries <= 1 && !mismatch)
 		goto out;
 
 	fprintf(stderr, "attempting to repair backref discrepency for bytenr "
@@ -4369,13 +4396,28 @@ static int verify_backrefs(struct btrfs_trans_handle *trans,
 	 */
 	if (!best) {
 		entry = find_entry(&entries, rec->start, rec->nr);
-		if (!entry) {
+		if (!entry && (!broken_entries || !rec->found_rec)) {
 			fprintf(stderr, "Backrefs don't agree with eachother "
 				"and extent record doesn't agree with anybody,"
 				" so we can't fix bytenr %Lu bytes %Lu\n",
 				rec->start, rec->nr);
 			ret = -EINVAL;
 			goto out;
+		} else if (!entry) {
+			/*
+			 * Ok our backrefs were broken, we'll assume this is the
+			 * correct value and add an entry for this range.
+			 */
+			entry = malloc(sizeof(struct extent_entry));
+			if (!entry) {
+				ret = -ENOMEM;
+				goto out;
+			}
+			memset(entry, 0, sizeof(*entry));
+			entry->bytenr = rec->start;
+			entry->bytes = rec->nr;
+			list_add_tail(&entry->list, &entries);
+			nr_entries++;
 		}
 		entry->count++;
 		best = find_most_right_entry(&entries);
@@ -4622,6 +4664,92 @@ out:
 	return ret ? ret : nr_del;
 }
 
+static int find_possible_backrefs(struct btrfs_trans_handle *trans,
+				  struct btrfs_fs_info *info,
+				  struct btrfs_path *path,
+				  struct cache_tree *extent_cache,
+				  struct extent_record *rec)
+{
+	struct btrfs_root *root;
+	struct extent_backref *back;
+	struct data_backref *dback;
+	struct cache_extent *cache;
+	struct btrfs_file_extent_item *fi;
+	struct btrfs_key key;
+	u64 bytenr, bytes;
+	int ret;
+
+	list_for_each_entry(back, &rec->backrefs, list) {
+		dback = (struct data_backref *)back;
+
+		/* We found this one, we don't need to do a lookup */
+		if (dback->found_ref)
+			continue;
+		/* Don't care about full backrefs (poor unloved backrefs) */
+		if (back->full_backref)
+			continue;
+		key.objectid = dback->root;
+		key.type = BTRFS_ROOT_ITEM_KEY;
+		key.offset = (u64)-1;
+
+		root = btrfs_read_fs_root(info, &key);
+
+		/* No root, definitely a bad ref, skip */
+		if (IS_ERR(root) && PTR_ERR(root) == -ENOENT)
+			continue;
+		/* Other err, exit */
+		if (IS_ERR(root))
+			return PTR_ERR(root);
+
+		key.objectid = dback->owner;
+		key.type = BTRFS_EXTENT_DATA_KEY;
+		key.offset = dback->offset;
+		ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+		if (ret) {
+			btrfs_release_path(path);
+			if (ret < 0)
+				return ret;
+			/* Didn't find it, we can carry on */
+			ret = 0;
+			continue;
+		}
+
+		fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+				    struct btrfs_file_extent_item);
+		bytenr = btrfs_file_extent_disk_bytenr(path->nodes[0], fi);
+		bytes = btrfs_file_extent_disk_num_bytes(path->nodes[0], fi);
+		btrfs_release_path(path);
+		cache = lookup_cache_extent(extent_cache, bytenr, 1);
+		if (cache) {
+			struct extent_record *tmp;
+			tmp = container_of(cache, struct extent_record, cache);
+
+			/*
+			 * If we found an extent record for the bytenr for this
+			 * particular backref then we can't add it to our
+			 * current extent record.  We only want to add backrefs
+			 * that don't have a corresponding extent item in the
+			 * extent tree since they likely belong to this record
+			 * and we need to fix it if it doesn't match bytenrs.
+			 */
+			if  (tmp->found_rec)
+				continue;
+		}
+
+		dback->found_ref += 1;
+		dback->disk_bytenr = bytenr;
+		dback->bytes = bytes;
+
+		/*
+		 * Set this so the verify backref code knows not to trust the
+		 * values in this backref.
+		 */
+		back->broken = 1;
+	}
+
+	return 0;
+}
+
 /*
  * when an incorrect extent item is found, this will delete
  * all of the existing entries for it and recreate them
@@ -4629,6 +4757,7 @@ out:
  */
 static int fixup_extent_refs(struct btrfs_trans_handle *trans,
 			     struct btrfs_fs_info *info,
+			     struct cache_tree *extent_cache,
 			     struct extent_record *rec)
 {
 	int ret;
@@ -4650,6 +4779,20 @@ static int fixup_extent_refs(struct btrfs_trans_handle *trans,
 	if (!path)
 		return -ENOMEM;
 
+	if (rec->refs != rec->extent_item_refs && !rec->metadata) {
+		/*
+		 * Sometimes the backrefs themselves are so broken they don't
+		 * get attached to any meaningful rec, so first go back and
+		 * check any of our backrefs that we couldn't find and throw
+		 * them into the list if we find the backref so that
+		 * verify_backrefs can figure out what to do.
+		 */
+		ret = find_possible_backrefs(trans, info, path, extent_cache,
+					     rec);
+		if (ret < 0)
+			goto out;
+	}
+
 	/* step one, make sure all of the backrefs agree */
 	ret = verify_backrefs(trans, info, path, rec);
 	if (ret < 0)
@@ -4959,7 +5102,8 @@ static int check_extent_refs(struct btrfs_trans_handle *trans,
 				(unsigned long long)rec->extent_item_refs,
 				(unsigned long long)rec->refs);
 			if (!fixed && repair) {
-				ret = fixup_extent_refs(trans, root->fs_info, rec);
+				ret = fixup_extent_refs(trans, root->fs_info,
+							extent_cache, rec);
 				if (ret)
 					goto repair_abort;
 				fixed = 1;
@@ -4973,7 +5117,8 @@ static int check_extent_refs(struct btrfs_trans_handle *trans,
 				(unsigned long long)rec->nr);
 
 			if (!fixed && repair) {
-				ret = fixup_extent_refs(trans, root->fs_info, rec);
+				ret = fixup_extent_refs(trans, root->fs_info,
+							extent_cache, rec);
 				if (ret)
 					goto repair_abort;
 				fixed = 1;
@@ -4986,7 +5131,8 @@ static int check_extent_refs(struct btrfs_trans_handle *trans,
 				(unsigned long long)rec->start,
 				(unsigned long long)rec->nr);
 			if (!fixed && repair) {
-				ret = fixup_extent_refs(trans, root->fs_info, rec);
+				ret = fixup_extent_refs(trans, root->fs_info,
+							extent_cache, rec);
 				if (ret)
 					goto repair_abort;
 				fixed = 1;
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 4/5] Btrfs-progs: add ability to corrupt file extent disk bytenr
  2013-09-09 20:41 [PATCH 1/5] Btrfs-progs: setup framework to corrupt specific fields of an inode Josef Bacik
  2013-09-09 20:41 ` [PATCH 2/5] Btrfs-progs: allow fsck to fix directory isize errors Josef Bacik
  2013-09-09 20:41 ` [PATCH 3/5] Btrfs-progs: make btrfsck fix backrefs that are broken Josef Bacik
@ 2013-09-09 20:41 ` Josef Bacik
  2013-09-09 20:41 ` [PATCH 5/5] Btrfs-progs: add make test framework Josef Bacik
  3 siblings, 0 replies; 5+ messages in thread
From: Josef Bacik @ 2013-09-09 20:41 UTC (permalink / raw)
  To: linux-btrfs

A user had a corrupt fs where one of his file extents pointed to a completely
bogus disk bytenr.  This patch allows us to corrupt a file system in a similar
way in order to test btrfsck.  Thanks,

Signed-off-by: Josef Bacik <jbacik@fusionio.com>
---
 btrfs-corrupt-block.c |  117 +++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 108 insertions(+), 9 deletions(-)

diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c
index 1a7b6ca..1f54b36 100644
--- a/btrfs-corrupt-block.c
+++ b/btrfs-corrupt-block.c
@@ -100,6 +100,8 @@ static void print_usage(void)
 	fprintf(stderr, "\t-U The whole chunk tree to be corrupted\n");
 	fprintf(stderr, "\t-i The inode item to corrupt (must also specify "
 		"the field to corrupt)\n");
+	fprintf(stderr, "\t-x The file extent item to corrupt (must also "
+		"specify -i for the inode and -f for the field to corrupt)\n");
 	fprintf(stderr, "\t-f The field in the item to corrupt\n");
 	exit(1);
 }
@@ -289,20 +291,41 @@ enum btrfs_inode_field {
 	BTRFS_INODE_FIELD_BAD,
 };
 
-static enum btrfs_inode_field convert_field(char *field)
+enum btrfs_file_extent_field {
+	BTRFS_FILE_EXTENT_DISK_BYTENR,
+	BTRFS_FILE_EXTENT_BAD,
+};
+
+static enum btrfs_inode_field convert_inode_field(char *field)
 {
 	if (!strncmp(field, "isize", FIELD_BUF_LEN))
 		return BTRFS_INODE_FIELD_ISIZE;
 	return BTRFS_INODE_FIELD_BAD;
 }
 
+static enum btrfs_file_extent_field convert_file_extent_field(char *field)
+{
+	if (!strncmp(field, "disk_bytenr", FIELD_BUF_LEN))
+		return BTRFS_FILE_EXTENT_DISK_BYTENR;
+	return BTRFS_FILE_EXTENT_BAD;
+}
+
+static u64 generate_u64(u64 orig)
+{
+	u64 ret;
+	do {
+		ret = rand();
+	} while (ret == orig);
+	return ret;
+}
+
 static int corrupt_inode(struct btrfs_trans_handle *trans,
 			 struct btrfs_root *root, u64 inode, char *field)
 {
 	struct btrfs_inode_item *ei;
 	struct btrfs_path *path;
 	struct btrfs_key key;
-	enum btrfs_inode_field corrupt_field = convert_field(field);
+	enum btrfs_inode_field corrupt_field = convert_inode_field(field);
 	u64 bogus;
 	u64 orig;
 	int ret;
@@ -345,10 +368,7 @@ static int corrupt_inode(struct btrfs_trans_handle *trans,
 	switch (corrupt_field) {
 	case BTRFS_INODE_FIELD_ISIZE:
 		orig = btrfs_inode_size(path->nodes[0], ei);
-		do {
-			bogus = rand();
-		} while (bogus == orig);
-
+		bogus = generate_u64(orig);
 		btrfs_set_inode_size(path->nodes[0], ei, bogus);
 		break;
 	default:
@@ -361,6 +381,60 @@ out:
 	return ret;
 }
 
+static int corrupt_file_extent(struct btrfs_trans_handle *trans,
+			       struct btrfs_root *root, u64 inode, u64 extent,
+			       char *field)
+{
+	struct btrfs_file_extent_item *fi;
+	struct btrfs_path *path;
+	struct btrfs_key key;
+	enum btrfs_file_extent_field corrupt_field;
+	u64 bogus;
+	u64 orig;
+	int ret = 0;
+
+	corrupt_field = convert_file_extent_field(field);
+	if (corrupt_field == BTRFS_FILE_EXTENT_BAD) {
+		fprintf(stderr, "Invalid field %s\n", field);
+		return -EINVAL;
+	}
+
+	key.objectid = inode;
+	key.type = BTRFS_EXTENT_DATA_KEY;
+	key.offset = extent;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+	if (ret < 0)
+		goto out;
+	if (ret) {
+		fprintf(stderr, "Couldn't find extent %llu for inode %llu\n",
+			extent, inode);
+		ret = -ENOENT;
+		goto out;
+	}
+
+	fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+			    struct btrfs_file_extent_item);
+	switch (corrupt_field) {
+	case BTRFS_FILE_EXTENT_DISK_BYTENR:
+		orig = btrfs_file_extent_disk_bytenr(path->nodes[0], fi);
+		bogus = generate_u64(orig);
+		btrfs_set_file_extent_disk_bytenr(path->nodes[0], fi, bogus);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	btrfs_mark_buffer_dirty(path->nodes[0]);
+out:
+	btrfs_free_path(path);
+	return ret;
+}
+
 static struct option long_options[] = {
 	/* { "byte-count", 1, NULL, 'b' }, */
 	{ "logical", 1, NULL, 'l' },
@@ -372,6 +446,7 @@ static struct option long_options[] = {
 	{ "chunk-record", 0, NULL, 'u' },
 	{ "chunk-tree", 0, NULL, 'U' },
 	{ "inode", 1, NULL, 'i'},
+	{ "file-extent", 1, NULL, 'x'},
 	{ "field", 1, NULL, 'f'},
 	{ 0, 0, 0, 0}
 };
@@ -534,6 +609,7 @@ int main(int ac, char **av)
 	int chunk_rec = 0;
 	int chunk_tree = 0;
 	u64 inode = 0;
+	u64 file_extent = (u64)-1;
 	char field[FIELD_BUF_LEN];
 
 	field[0] = '\0';
@@ -541,7 +617,7 @@ int main(int ac, char **av)
 
 	while(1) {
 		int c;
-		c = getopt_long(ac, av, "l:c:b:eEkuUi:f:", long_options,
+		c = getopt_long(ac, av, "l:c:b:eEkuUi:f:x:", long_options,
 				&option_index);
 		if (c < 0)
 			break;
@@ -590,6 +666,15 @@ int main(int ac, char **av)
 			case 'f':
 				strncpy(field, optarg, FIELD_BUF_LEN);
 				break;
+			case 'x':
+				errno = 0;
+				file_extent = atoll(optarg);
+				if (errno) {
+					fprintf(stderr, "error converting "
+						"%d\n", errno);
+					print_usage();
+				}
+				break;
 			default:
 				print_usage();
 		}
@@ -662,13 +747,27 @@ int main(int ac, char **av)
 
 		if (!strlen(field))
 			print_usage();
-		printf("corrupting inode\n");
+
 		trans = btrfs_start_transaction(root, 1);
-		ret = corrupt_inode(trans, root, inode, field);
+		if (file_extent == (u64)-1) {
+			printf("corrupting inode\n");
+			ret = corrupt_inode(trans, root, inode, field);
+		} else {
+			printf("corrupting file extent\n");
+			ret = corrupt_file_extent(trans, root, inode,
+						  file_extent, field);
+		}
 		btrfs_commit_transaction(trans, root);
 		goto out_close;
 	}
 
+	/*
+	 * If we made it here and we have extent set then we didn't specify
+	 * inode and we're screwed.
+	 */
+	if (file_extent != (u64)-1)
+		print_usage();
+
 	if (logical == (u64)-1)
 		print_usage();
 
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 5/5] Btrfs-progs: add make test framework
  2013-09-09 20:41 [PATCH 1/5] Btrfs-progs: setup framework to corrupt specific fields of an inode Josef Bacik
                   ` (2 preceding siblings ...)
  2013-09-09 20:41 ` [PATCH 4/5] Btrfs-progs: add ability to corrupt file extent disk bytenr Josef Bacik
@ 2013-09-09 20:41 ` Josef Bacik
  3 siblings, 0 replies; 5+ messages in thread
From: Josef Bacik @ 2013-09-09 20:41 UTC (permalink / raw)
  To: linux-btrfs

We need to start adding some sanity tests to btrfs-progs to make sure we aren't
breaking things with our patches.  The most important of these tools is btrfsck.
This patch gets things started by adding a basic btrfsck test that makes sure we
can fix a corruption problem we know we can fix.  Thanks,

Signed-off-by: Josef Bacik <jbacik@fusionio.com>
---
 Makefile                                        |    7 +++++
 tests/fsck-tests.sh                             |   31 +++++++++++++++++++++++
 tests/fsck-tests/001-bad-file-extent-bytenr.img |  Bin 0 -> 4096 bytes
 3 files changed, 38 insertions(+), 0 deletions(-)
 create mode 100644 tests/fsck-tests.sh
 create mode 100644 tests/fsck-tests/001-bad-file-extent-bytenr.img

diff --git a/Makefile b/Makefile
index 3fade14..0e812cc 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,7 @@ libbtrfs_objects = send-stream.o send-utils.o rbtree.o btrfs-list.o crc32c.o \
 libbtrfs_headers = send-stream.h send-utils.h send.h rbtree.h btrfs-list.h \
 	       crc32c.h list.h kerncompat.h radix-tree.h extent-cache.h \
 	       extent_io.h ioctl.h ctree.h btrfsck.h
+TESTS = fsck-tests.sh
 
 INSTALL = install
 prefix ?= /usr/local
@@ -123,6 +124,12 @@ $(BUILDDIRS):
 	@echo "Making all in $(patsubst build-%,%,$@)"
 	$(Q)$(MAKE) $(MAKEOPTS) -C $(patsubst build-%,%,$@)
 
+test:
+	$(Q)for t in $(TESTS); do \
+		echo "     [TEST]    $$t"; \
+		bash tests/$$t || exit 1; \
+	done
+
 #
 # NOTE: For static compiles, you need to have all the required libs
 # 	static equivalent available
diff --git a/tests/fsck-tests.sh b/tests/fsck-tests.sh
new file mode 100644
index 0000000..c1490bf
--- /dev/null
+++ b/tests/fsck-tests.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# loop through all of our bad images and make sure fsck repairs them properly
+#
+# It's GPL, same as everything else in this tree.
+#
+
+here=`pwd`
+
+_fail()
+{
+	echo "$*" | tee -a fsck-tests-results.txt
+	exit 1
+}
+
+rm -f fsck-tests-results.txt
+
+for i in $(find $here/tests/fsck-tests -name '*.img')
+do
+	echo "testing image $i" >> fsck-tests-results.txt
+	$here/btrfs-image -r $i test.img >> fsck-tests-results.txt 2>&1 \
+		|| _fail "restore failed"
+	$here/btrfsck test.img >> fsck-test-results.txt 2>&1
+	[ $? -eq 0 ] && _fail "btrfsck should have detected corruption"
+
+	$here/btrfsck --repair test.img >> fsck-test-results.txt 2>&1 || \
+		_fail "btrfsck should have repaired the image"
+
+	$here/btrfsck test.img >> fsck-test-results.txt 2>&1 || \
+		_fail "btrfsck did not correct corruption"
+done
diff --git a/tests/fsck-tests/001-bad-file-extent-bytenr.img b/tests/fsck-tests/001-bad-file-extent-bytenr.img
new file mode 100644
index 0000000000000000000000000000000000000000..d2a05bb8f83d7f1bdc4be1441d3d8d35a89e142e
GIT binary patch
literal 4096
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

literal 0
HcmV?d00001

-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2013-09-09 20:42 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-09-09 20:41 [PATCH 1/5] Btrfs-progs: setup framework to corrupt specific fields of an inode Josef Bacik
2013-09-09 20:41 ` [PATCH 2/5] Btrfs-progs: allow fsck to fix directory isize errors Josef Bacik
2013-09-09 20:41 ` [PATCH 3/5] Btrfs-progs: make btrfsck fix backrefs that are broken Josef Bacik
2013-09-09 20:41 ` [PATCH 4/5] Btrfs-progs: add ability to corrupt file extent disk bytenr Josef Bacik
2013-09-09 20:41 ` [PATCH 5/5] Btrfs-progs: add make test framework Josef Bacik

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.