Skip to contentSkip to conversation

Various btrfsck updates

GIT PULL In review Josef Bacik 2 participants 11 messages Oct 31, 2014 – Nov 4, 2014 Btrfs file system

lore
Share & tools
lore
#0 lore raw

Please pull

https://github.com/josefbacik/btrfs-progs.git for-kdave

This is all the work from fixing a random IRC users broken file system. This also includes two patches that were not picked up previously for some reason, and includes 4 new test images. The test images may not make it to the list, but they are in the git tree (they are patch 02/11 and 11/11). Thanks,

Josef

#1 lore raw

PATCH 1/11 Btrfs-progs: add the ability to delete items

Somtetimes you just need to delete an item, add that functionality to btrfs-corrupt-block. Thanks,

---
 btrfs-corrupt-block.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 44 insertions(+), 1 deletion(-)
btrfs-corrupt-block.c +44 -1
index 171c81d..af9ae4d 100644
@@ -111,6 +111,7 @@ static void print_usage(void)
 	fprintf(stderr, "\t-I An item to corrupt (must also specify the field "
 		"to corrupt and a root+key for the item)\n");
 	fprintf(stderr, "\t-D Corrupt a dir item, must specify key and field\n");
+	fprintf(stderr, "\t-d Delete this item (must specify -K)\n");
 	exit(1);
 }
 
@@ -811,6 +812,39 @@ out:
 	return ret;
 }
 
+static int delete_item(struct btrfs_root *root, struct btrfs_key *key)
+{
+	struct btrfs_trans_handle *trans;
+	struct btrfs_path *path;
+	int ret;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	trans = btrfs_start_transaction(root, 1);
+	if (IS_ERR(trans)) {
+		btrfs_free_path(path);
+		fprintf(stderr, "Couldn't start transaction %ld\n",
+			PTR_ERR(trans));
+		return PTR_ERR(trans);
+	}
+
+	ret = btrfs_search_slot(trans, root, key, path, -1, 1);
+	if (ret) {
+		if (ret > 0)
+			ret = -ENOENT;
+		fprintf(stderr, "Error searching to node %d\n", ret);
+		goto out;
+	}
+	ret = btrfs_del_item(trans, root, path);
+	btrfs_mark_buffer_dirty(path->nodes[0]);
+out:
+	btrfs_commit_transaction(trans, root);
+	btrfs_free_path(path);
+	return ret;
+}
+
 static struct option long_options[] = {
 	/* { "byte-count", 1, NULL, 'b' }, */
 	{ "logical", 1, NULL, 'l' },
@@ -828,6 +862,7 @@ static struct option long_options[] = {
 	{ "key", 1, NULL, 'K'},
 	{ "item", 0, NULL, 'I'},
 	{ "dir-item", 0, NULL, 'D'},
+	{ "delete", 0, NULL, 'd'},
 	{ 0, 0, 0, 0}
 };
 
@@ -993,6 +1028,7 @@ int main(int ac, char **av)
 	int chunk_tree = 0;
 	int corrupt_item = 0;
 	int corrupt_di = 0;
+	int delete = 0;
 	u64 metadata_block = 0;
 	u64 inode = 0;
 	u64 file_extent = (u64)-1;
@@ -1004,7 +1040,7 @@ int main(int ac, char **av)
 
 	while(1) {
 		int c;
-		c = getopt_long(ac, av, "l:c:b:eEkuUi:f:x:m:K:ID", long_options,
+		c = getopt_long(ac, av, "l:c:b:eEkuUi:f:x:m:K:IDd", long_options,
 				&option_index);
 		if (c < 0)
 			break;
@@ -1060,6 +1096,8 @@ int main(int ac, char **av)
 				break;
 			case 'I':
 				corrupt_item = 1;
+			case 'd':
+				delete = 1;
 				break;
 			default:
 				print_usage();
@@ -1167,6 +1205,11 @@ int main(int ac, char **av)
 		if (!key.objectid)
 			print_usage();
 		ret = corrupt_btrfs_item(root, &key, field);
+	}
+	if (delete) {
+		if (!key.objectid)
+			print_usage();
+		ret = delete_item(root, &key);
 		goto out_close;
 	}
 	if (key.objectid || key.offset || key.type) {
Signature · 1 line
1.8.3.1
#2 lore raw

PATCH 3/11 Btrfs-progs: allow fsck to take the tree bytenr

In reply to Josef Bacik

Sometimes we have a pretty corrupted fs but have an old tree bytenr that we could use, add the ability to specify the tree root bytenr. Thanks,

---
 cmds-check.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
cmds-check.c +9 -2
index 2a5f823..38f8d11 100644
@@ -7546,6 +7546,7 @@ static struct option long_options[] = {
 	{ "backup", 0, NULL, 0 },
 	{ "subvol-extents", no_argument, NULL, 'E' },
 	{ "qgroup-report", 0, NULL, 'Q' },
+	{ "tree-root", 1, NULL, 'r' },
 	{ NULL, 0, NULL, 0}
 };
 
@@ -7561,6 +7562,7 @@ const char * const cmd_check_usage[] = {
 	"--check-data-csum           verify checkums of data blocks",
 	"--qgroup-report             print a report on qgroup consistency",
 	"--subvol-extents            print subvolume extents and sharing state",
+	"--tree-root                 use the given bytenr for the tree root",
 	NULL
 };
 
@@ -7571,6 +7573,7 @@ int cmd_check(int argc, char **argv)
 	struct btrfs_fs_info *info;
 	u64 bytenr = 0;
 	u64 subvolid = 0;
+	u64 tree_root_bytenr = 0;
 	char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
 	int ret;
 	u64 num;
@@ -7581,7 +7584,7 @@ int cmd_check(int argc, char **argv)
 
 	while(1) {
 		int c;
-		c = getopt_long(argc, argv, "as:b", long_options,
+		c = getopt_long(argc, argv, "as:br:", long_options,
 				&option_index);
 		if (c < 0)
 			break;
@@ -7608,6 +7611,9 @@ int cmd_check(int argc, char **argv)
 			case 'E':
 				subvolid = arg_strtou64(optarg);
 				break;
+			case 'r':
+				tree_root_bytenr = arg_strtou64(optarg);
+				break;
 			case '?':
 			case 'h':
 				usage(cmd_check_usage);
@@ -7651,7 +7657,8 @@ int cmd_check(int argc, char **argv)
 	if (repair)
 		ctree_flags |= OPEN_CTREE_PARTIAL;
 
-	info = open_ctree_fs_info(argv[optind], bytenr, 0, ctree_flags);
+	info = open_ctree_fs_info(argv[optind], bytenr, tree_root_bytenr,
+				  ctree_flags);
 	if (!info) {
 		fprintf(stderr, "Couldn't open file system\n");
 		ret = -EIO;
Signature · 1 line
1.8.3.1
#3 lore raw

Re: [PATCH 03/11] Btrfs-progs: allow fsck to take the tree bytenr

Quoted 4 lines · Josef Bacik <jbacik <at> fb.com> writes:

Sometimes we have a pretty corrupted fs but have an old tree bytenr that we could use, add the ability to specify the tree root bytenr. Thanks,

Signed-off-by Josef Bacik <jbacik <at> fb.com>

Tested-by: Ansgar Hockmann-Stolle <ansgar.hockmann-stolle <at> uni-osnabrueck.de>

This patch fixed my case:
http://www.spinics.net/lists/linux-btrfs/msg38714.html
Thank you! And thank you, Qu Wenruo for the help!!

I tested all blocks that find-root gave - but only the one with generation = want + 1 gave some possible root items. The btrfsck with --tree-root <that block> finally fixed my file system. Hooray!

Ciao
Ansgar

#4 lore raw

PATCH 4/11 Btrfs-progs: don't fail on log tree opening with PARTIAL

In reply to Josef Bacik

We were failing to fsck a volume because we couldn't open the log tree, which is not helpful. Make us skip erroring out if we are using OPEN_CTREE_PARTIAL since it isn't a mandatory tree. Thanks,

---
 disk-io.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
disk-io.c +2 -1
index 77fc610..d2c18a8 100644
@@ -930,7 +930,8 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
 	ret = find_and_setup_log_root(root, fs_info, sb);
 	if (ret) {
 		printk("Couldn't setup log root tree\n");
-		return -EIO;
+		if (!(flags & OPEN_CTREE_PARTIAL))
+			return -EIO;
 	}
 
 	fs_info->generation = generation;
Signature · 1 line
1.8.3.1
#5 lore raw

PATCH 5/11 Btrfs-progs: spit out the broken file when ignoring errors

In reply to Josef Bacik

It's nice to ignore errors on restore, but spit out the filename so the user knows which files of his aren't going to look right. Thanks,

---
 cmds-restore.c | 4 ++++
 1 file changed, 4 insertions(+)
cmds-restore.c +4 -0
index 38a131e..b52d5c8 100644
@@ -840,6 +840,8 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 			ret = copy_file(root, fd, &location, path_name);
 			close(fd);
 			if (ret) {
+				fprintf(stderr, "Error copying data for %s\n",
+					path_name);
 				if (ignore_errors)
 					goto next;
 				btrfs_free_path(path);
@@ -917,6 +919,8 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 					 output_rootdir, dir, mreg);
 			free(dir);
 			if (ret) {
+				fprintf(stderr, "Error searching %s\n",
+					path_name);
 				if (ignore_errors)
 					goto next;
 				btrfs_free_path(path);
Signature · 1 line
1.8.3.1
#6 lore raw

PATCH 6/11 Btrfs-progs: make zero-log use partial open

In reply to Josef Bacik

Because seriously, we only want to kill the tree log. Thanks,

---
 btrfs-zero-log.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
btrfs-zero-log.c +1 -1
index 88998e9..46c705c 100644
@@ -61,7 +61,7 @@ int main(int ac, char **av)
 		goto out;
 	}
 
-	root = open_ctree(av[1], 0, OPEN_CTREE_WRITES);
+	root = open_ctree(av[1], 0, OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL);
 
 	if (root == NULL)
 		return 1;
Signature · 1 line
1.8.3.1
#7 lore raw

PATCH 7/11 Btrfs-progs: add a message to know zero log ran successfully

In reply to Josef Bacik

If there are errors when opening the fs because of PARTIAL we could think that the zero-log didn't actually work. Add a printf so we know that it was successfull. Thanks,

---
 btrfs-zero-log.c | 1 +
 1 file changed, 1 insertion(+)
btrfs-zero-log.c +1 -0
index 46c705c..4154175 100644
@@ -71,6 +71,7 @@ int main(int ac, char **av)
 	btrfs_set_super_log_root_level(root->fs_info->super_copy, 0);
 	btrfs_commit_transaction(trans, root);
 	close_ctree(root);
+	printf("Log root zero'ed\n");
 out:
 	return !!ret;
 }
Signature · 1 line
1.8.3.1
#8 lore raw

PATCH 8/11 Btrfs-progs: add ability to replace missing dir item/dir indexes

In reply to Josef Bacik

If we have everything except the dir item and dir index we can easily replace them, so add this ability to btrfsck. Thanks,

---
 cmds-check.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
cmds-check.c +27 -0
index 38f8d11..be75dcb 100644
@@ -1772,6 +1772,33 @@ static int repair_inode_backrefs(struct btrfs_root *root,
 			}
 		}
 
+		if (!delete && (!backref->found_dir_index &&
+				!backref->found_dir_item &&
+				backref->found_inode_ref)) {
+			struct btrfs_trans_handle *trans;
+			struct btrfs_key location;
+
+			location.objectid = rec->ino;
+			location.type = BTRFS_INODE_ITEM_KEY;
+			location.offset = 0;
+
+			trans = btrfs_start_transaction(root, 1);
+			if (IS_ERR(trans)) {
+				ret = PTR_ERR(trans);
+				break;
+			}
+			fprintf(stderr, "adding missing dir index/item pair "
+				"for inode %llu\n",
+				(unsigned long long)rec->ino);
+			ret = btrfs_insert_dir_item(trans, root, backref->name,
+						    backref->namelen,
+						    backref->dir, &location,
+						    imode_to_type(rec->imode),
+						    backref->index);
+			BUG_ON(ret);
+			btrfs_commit_transaction(trans, root);
+			repaired++;
+		}
 	}
 	return ret ? ret : repaired;
 }
Signature · 1 line
1.8.3.1
#9 lore raw

PATCH 9/11 Btrfs-progs: fix missing inode items

In reply to Josef Bacik

If we have all the other items but no inode item we can recreate it for the most part, with the exception of the permissions and ownership. Add this ability to btrfsck. Thanks,

---
 cmds-check.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)
cmds-check.c +76 -0
index be75dcb..a30db8d 100644
@@ -1727,6 +1727,61 @@ static int delete_dir_index(struct btrfs_root *root,
 	return ret;
 }
 
+static int create_inode_item(struct btrfs_root *root,
+			     struct inode_record *rec,
+			     struct inode_backref *backref, int root_dir)
+{
+	struct btrfs_trans_handle *trans;
+	struct btrfs_inode_item inode_item;
+	time_t now = time(NULL);
+	int ret;
+
+	trans = btrfs_start_transaction(root, 1);
+	if (IS_ERR(trans)) {
+		ret = PTR_ERR(trans);
+		return ret;
+	}
+
+	fprintf(stderr, "root %llu inode %llu recreating inode item, this may "
+		"be incomplete, please check permissions and content after "
+		"the fsck completes.\n", (unsigned long long)root->objectid,
+		(unsigned long long)rec->ino);
+
+	memset(&inode_item, 0, sizeof(inode_item));
+	btrfs_set_stack_inode_generation(&inode_item, trans->transid);
+	if (root_dir)
+		btrfs_set_stack_inode_nlink(&inode_item, 1);
+	else
+		btrfs_set_stack_inode_nlink(&inode_item, rec->found_link);
+	btrfs_set_stack_inode_nbytes(&inode_item, rec->found_size);
+	if (rec->found_dir_item) {
+		if (rec->found_file_extent)
+			fprintf(stderr, "root %llu inode %llu has both a dir "
+				"item and extents, unsure if it is a dir or a "
+				"regular file so setting it as a directory\n",
+				(unsigned long long)root->objectid,
+				(unsigned long long)rec->ino);
+		btrfs_set_stack_inode_mode(&inode_item, S_IFDIR | 0755);
+		btrfs_set_stack_inode_size(&inode_item, rec->found_size);
+	} else if (!rec->found_dir_item) {
+		btrfs_set_stack_inode_size(&inode_item, rec->extent_end);
+		btrfs_set_stack_inode_mode(&inode_item, S_IFREG | 0755);
+	}
+	btrfs_set_stack_timespec_sec(&inode_item.atime, now);
+	btrfs_set_stack_timespec_nsec(&inode_item.atime, 0);
+	btrfs_set_stack_timespec_sec(&inode_item.ctime, now);
+	btrfs_set_stack_timespec_nsec(&inode_item.ctime, 0);
+	btrfs_set_stack_timespec_sec(&inode_item.mtime, now);
+	btrfs_set_stack_timespec_nsec(&inode_item.mtime, 0);
+	btrfs_set_stack_timespec_sec(&inode_item.otime, 0);
+	btrfs_set_stack_timespec_nsec(&inode_item.otime, 0);
+
+	ret = btrfs_insert_inode(trans, root, rec->ino, &inode_item);
+	BUG_ON(ret);
+	btrfs_commit_transaction(trans, root);
+	return 0;
+}
+
 static int repair_inode_backrefs(struct btrfs_root *root,
 				 struct inode_record *rec,
 				 struct cache_tree *inode_cache,
@@ -1738,6 +1793,15 @@ static int repair_inode_backrefs(struct btrfs_root *root,
 	int repaired = 0;
 
 	list_for_each_entry_safe(backref, tmp, &rec->backrefs, list) {
+		if (!delete && rec->ino == root_dirid) {
+			if (!rec->found_inode_item) {
+				ret = create_inode_item(root, rec, backref, 1);
+				if (ret)
+					break;
+				repaired++;
+			}
+		}
+
 		/* Index 0 for root dir's are special, don't mess with it */
 		if (rec->ino == root_dirid && backref->index == 0)
 			continue;
@@ -1799,6 +1863,18 @@ static int repair_inode_backrefs(struct btrfs_root *root,
 			btrfs_commit_transaction(trans, root);
 			repaired++;
 		}
+
+		if (!delete && (backref->found_inode_ref &&
+				backref->found_dir_index &&
+				backref->found_dir_item &&
+				!(backref->errors & REF_ERR_INDEX_UNMATCH) &&
+				!rec->found_inode_item)) {
+			ret = create_inode_item(root, rec, backref, 0);
+			if (ret)
+				break;
+			repaired++;
+		}
+
 	}
 	return ret ? ret : repaired;
 }
Signature · 1 line
1.8.3.1
#10 lore raw

PATCH 10/11 Btrfs-progs: create missing root dirid

In reply to Josef Bacik

If we just don't have the root dirid stuff go ahead and re-create it, since it is easily recreated. Thanks,

---
 cmds-check.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
cmds-check.c +20 -0
index a30db8d..5fbf08f 100644
@@ -1987,6 +1987,26 @@ static int check_inode_recs(struct btrfs_root *root,
 			error++;
 		}
 	} else {
+		if (repair) {
+			struct btrfs_trans_handle *trans;
+
+			trans = btrfs_start_transaction(root, 1);
+			if (IS_ERR(trans)) {
+				err = PTR_ERR(trans);
+				return err;
+			}
+
+			fprintf(stderr, "root %llu missing its root dir, "
+				"recreating\n",
+				(unsigned long long)root->objectid);
+
+			ret = btrfs_make_root_dir(trans, root, root_dirid);
+			BUG_ON(ret);
+
+			btrfs_commit_transaction(trans, root);
+			return -EAGAIN;
+		}
+
 		fprintf(stderr, "root %llu root dir %llu not found\n",
 			(unsigned long long)root->root_key.objectid,
 			(unsigned long long)root_dirid);
Signature · 1 line
1.8.3.1