Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 558540c1 authored by Jan Schmidt's avatar Jan Schmidt
Browse files

btrfs scrub: print paths of corrupted files



While scrubbing, we may encounter various errors. Previously, a logical
address was printed to the log only. Now, all paths belonging to that
address are resolved and printed separately. That should work for hardlinks
as well as reflinks.

Signed-off-by: default avatarJan Schmidt <list.btrfs@jan-o-sch.net>
parent 13db62b7
Loading
Loading
Loading
Loading
+163 −6
Original line number Diff line number Diff line
@@ -17,10 +17,12 @@
 */

#include <linux/blkdev.h>
#include <linux/ratelimit.h>
#include "ctree.h"
#include "volumes.h"
#include "disk-io.h"
#include "ordered-data.h"
#include "backref.h"

/*
 * This is only the first step towards a full-features scrub. It reads all
@@ -100,6 +102,19 @@ struct scrub_dev {
	spinlock_t		stat_lock;
};

struct scrub_warning {
	struct btrfs_path	*path;
	u64			extent_item_size;
	char			*scratch_buf;
	char			*msg_buf;
	const char		*errstr;
	sector_t		sector;
	u64			logical;
	struct btrfs_device	*dev;
	int			msg_bufsize;
	int			scratch_bufsize;
};

static void scrub_free_csums(struct scrub_dev *sdev)
{
	while (!list_empty(&sdev->csum_list)) {
@@ -195,6 +210,143 @@ struct scrub_dev *scrub_setup_dev(struct btrfs_device *dev)
	return ERR_PTR(-ENOMEM);
}

static int scrub_print_warning_inode(u64 inum, u64 offset, u64 root, void *ctx)
{
	u64 isize;
	u32 nlink;
	int ret;
	int i;
	struct extent_buffer *eb;
	struct btrfs_inode_item *inode_item;
	struct scrub_warning *swarn = ctx;
	struct btrfs_fs_info *fs_info = swarn->dev->dev_root->fs_info;
	struct inode_fs_paths *ipath = NULL;
	struct btrfs_root *local_root;
	struct btrfs_key root_key;

	root_key.objectid = root;
	root_key.type = BTRFS_ROOT_ITEM_KEY;
	root_key.offset = (u64)-1;
	local_root = btrfs_read_fs_root_no_name(fs_info, &root_key);
	if (IS_ERR(local_root)) {
		ret = PTR_ERR(local_root);
		goto err;
	}

	ret = inode_item_info(inum, 0, local_root, swarn->path);
	if (ret) {
		btrfs_release_path(swarn->path);
		goto err;
	}

	eb = swarn->path->nodes[0];
	inode_item = btrfs_item_ptr(eb, swarn->path->slots[0],
					struct btrfs_inode_item);
	isize = btrfs_inode_size(eb, inode_item);
	nlink = btrfs_inode_nlink(eb, inode_item);
	btrfs_release_path(swarn->path);

	ipath = init_ipath(4096, local_root, swarn->path);
	ret = paths_from_inode(inum, ipath);

	if (ret < 0)
		goto err;

	/*
	 * we deliberately ignore the bit ipath might have been too small to
	 * hold all of the paths here
	 */
	for (i = 0; i < ipath->fspath->elem_cnt; ++i)
		printk(KERN_WARNING "btrfs: %s at logical %llu on dev "
			"%s, sector %llu, root %llu, inode %llu, offset %llu, "
			"length %llu, links %u (path: %s)\n", swarn->errstr,
			swarn->logical, swarn->dev->name,
			(unsigned long long)swarn->sector, root, inum, offset,
			min(isize - offset, (u64)PAGE_SIZE), nlink,
			ipath->fspath->str[i]);

	free_ipath(ipath);
	return 0;

err:
	printk(KERN_WARNING "btrfs: %s at logical %llu on dev "
		"%s, sector %llu, root %llu, inode %llu, offset %llu: path "
		"resolving failed with ret=%d\n", swarn->errstr,
		swarn->logical, swarn->dev->name,
		(unsigned long long)swarn->sector, root, inum, offset, ret);

	free_ipath(ipath);
	return 0;
}

static void scrub_print_warning(const char *errstr, struct scrub_bio *sbio,
				int ix)
{
	struct btrfs_device *dev = sbio->sdev->dev;
	struct btrfs_fs_info *fs_info = dev->dev_root->fs_info;
	struct btrfs_path *path;
	struct btrfs_key found_key;
	struct extent_buffer *eb;
	struct btrfs_extent_item *ei;
	struct scrub_warning swarn;
	u32 item_size;
	int ret;
	u64 ref_root;
	u8 ref_level;
	unsigned long ptr = 0;
	const int bufsize = 4096;
	u64 extent_offset;

	path = btrfs_alloc_path();

	swarn.scratch_buf = kmalloc(bufsize, GFP_NOFS);
	swarn.msg_buf = kmalloc(bufsize, GFP_NOFS);
	swarn.sector = (sbio->physical + ix * PAGE_SIZE) >> 9;
	swarn.logical = sbio->logical + ix * PAGE_SIZE;
	swarn.errstr = errstr;
	swarn.dev = dev;
	swarn.msg_bufsize = bufsize;
	swarn.scratch_bufsize = bufsize;

	if (!path || !swarn.scratch_buf || !swarn.msg_buf)
		goto out;

	ret = extent_from_logical(fs_info, swarn.logical, path, &found_key);
	if (ret < 0)
		goto out;

	extent_offset = swarn.logical - found_key.objectid;
	swarn.extent_item_size = found_key.offset;

	eb = path->nodes[0];
	ei = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_item);
	item_size = btrfs_item_size_nr(eb, path->slots[0]);

	if (ret & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
		do {
			ret = tree_backref_for_extent(&ptr, eb, ei, item_size,
							&ref_root, &ref_level);
			printk(KERN_WARNING "%s at logical %llu on dev %s, "
				"sector %llu: metadata %s (level %d) in tree "
				"%llu\n", errstr, swarn.logical, dev->name,
				(unsigned long long)swarn.sector,
				ref_level ? "node" : "leaf",
				ret < 0 ? -1 : ref_level,
				ret < 0 ? -1 : ref_root);
		} while (ret != 1);
	} else {
		swarn.path = path;
		iterate_extent_inodes(fs_info, path, found_key.objectid,
					extent_offset,
					scrub_print_warning_inode, &swarn);
	}

out:
	btrfs_free_path(path);
	kfree(swarn.scratch_buf);
	kfree(swarn.msg_buf);
}

/*
 * scrub_recheck_error gets called when either verification of the page
 * failed or the bio failed to read, e.g. with EIO. In the latter case,
@@ -205,6 +357,8 @@ static int scrub_recheck_error(struct scrub_bio *sbio, int ix)
{
	struct scrub_dev *sdev = sbio->sdev;
	u64 sector = (sbio->physical + ix * PAGE_SIZE) >> 9;
	static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
					DEFAULT_RATELIMIT_BURST);

	if (sbio->err) {
		if (scrub_fixup_io(READ, sbio->sdev->dev->bdev, sector,
@@ -212,6 +366,11 @@ static int scrub_recheck_error(struct scrub_bio *sbio, int ix)
			if (scrub_fixup_check(sbio, ix) == 0)
				return 0;
		}
		if (__ratelimit(&_rs))
			scrub_print_warning("i/o error", sbio, ix);
	} else {
		if (__ratelimit(&_rs))
			scrub_print_warning("checksum error", sbio, ix);
	}

	spin_lock(&sdev->stat_lock);
@@ -326,8 +485,7 @@ static void scrub_fixup(struct scrub_bio *sbio, int ix)
	++sdev->stat.corrected_errors;
	spin_unlock(&sdev->stat_lock);

	if (printk_ratelimit())
		printk(KERN_ERR "btrfs: fixed up at %llu\n",
	printk_ratelimited(KERN_ERR "btrfs: fixed up error at logical %llu\n",
			       (unsigned long long)logical);
	return;

@@ -337,9 +495,8 @@ static void scrub_fixup(struct scrub_bio *sbio, int ix)
	++sdev->stat.uncorrectable_errors;
	spin_unlock(&sdev->stat_lock);

	if (printk_ratelimit())
		printk(KERN_ERR "btrfs: unable to fixup at %llu\n",
			 (unsigned long long)logical);
	printk_ratelimited(KERN_ERR "btrfs: unable to fixup (regular) error at "
				"logical %llu\n", (unsigned long long)logical);
}

static int scrub_fixup_io(int rw, struct block_device *bdev, sector_t sector,