All of lore.kernel.org
 help / color / mirror / Atom feed
From: Li Zefan <lizf@cn.fujitsu.com>
To: "linux-btrfs@vger.kernel.org" <linux-btrfs@vger.kernel.org>
Subject: [PATCH] Btrfs: initial online fsck support
Date: Thu, 30 Jun 2011 17:33:55 +0800	[thread overview]
Message-ID: <4E0C4303.1080406@cn.fujitsu.com> (raw)

This is an initial version of online fsck. What it does is:

- check the dir item and dir index pointing to a file.
- check the structure of extents of a file.

As furthur work, we should consider:

- fix but not only check the structure of a file.
- verify the extent allocation tree on the fly.
...

Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
---
 fs/btrfs/ioctl.c |  258 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/btrfs/ioctl.h |   15 +++
 2 files changed, 273 insertions(+), 0 deletions(-)

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index b793d11..c06f542 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2834,6 +2834,262 @@ static long btrfs_ioctl_scrub_progress(struct btrfs_root *root,
 	return ret;
 }
 
+static long check_file_extents(u64 *errors, struct inode *inode,
+			       struct btrfs_root *root)
+{
+	struct btrfs_path *path;
+	struct btrfs_key prev_key;
+	struct btrfs_key key;
+	struct extent_buffer *leaf;
+	int slot;
+	int ret;
+	int sector = root->sectorsize;
+	u64 err = 0;
+	u64 prev_num_bytes = 0;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	key.objectid = btrfs_ino(inode);
+	key.type = BTRFS_EXTENT_DATA_KEY;
+	key.offset = 0;
+
+	mutex_lock(&inode->i_mutex);
+
+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+	if (ret < 0)
+		goto out;
+	if (ret > 0) {
+		if (inode->i_size)
+			err |= BTRFS_FSCK_NO_FILE_EXTENT;
+		ret = 0;
+		goto out;
+	}
+
+	if (!inode->i_size) {
+		err |= BTRFS_FSCK_BAD_FILE_EXTENT;
+		goto out;
+	}
+
+	while (1) {
+		struct btrfs_file_extent_item *fi;
+		u64 ram_bytes;
+		u64 offset;
+		u64 num_bytes;
+		u64 disk_bytenr;
+		u64 disk_num_bytes;
+		u32 inline_size;
+		u8 compress;
+		u8 type;
+
+		leaf = path->nodes[0];
+		slot = path->slots[0];
+		if (slot >= btrfs_header_nritems(leaf)) {
+			ret = btrfs_next_leaf(root, path);
+			if (ret < 0)
+				goto out;
+			else if (ret > 0)
+				break;
+			continue;
+		}
+
+		btrfs_item_key_to_cpu(leaf, &key, slot);
+
+		if (key.objectid != btrfs_ino(inode) ||
+		    key.type != BTRFS_EXTENT_DATA_KEY)
+			break;
+
+		fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
+
+		type = btrfs_file_extent_type(leaf, fi);
+		ram_bytes = btrfs_file_extent_ram_bytes(leaf, fi);
+		compress = btrfs_file_extent_compression(leaf, fi);
+
+		if (compress >= BTRFS_COMPRESS_LAST) {
+			err |= BTRFS_FSCK_BAD_FILE_EXTENT;
+			goto out;
+		}
+
+		switch (type) {
+		case BTRFS_FILE_EXTENT_INLINE:
+			inline_size = btrfs_file_extent_inline_item_len(leaf,
+						btrfs_item_nr(leaf, slot));
+			if (inline_size == 0) {
+				err |= BTRFS_FSCK_BAD_FILE_EXTENT;
+				goto out;
+			}
+			num_bytes = 0;
+			break;
+		case BTRFS_FILE_EXTENT_REG:
+		case BTRFS_FILE_EXTENT_PREALLOC:
+			offset = btrfs_file_extent_offset(leaf, fi);
+			num_bytes = btrfs_file_extent_num_bytes(leaf, fi);
+			disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
+			disk_num_bytes = btrfs_file_extent_disk_num_bytes(leaf,
+									  fi);
+
+			if (num_bytes == 0 || !IS_ALIGNED(num_bytes, sector)) {
+				err |= BTRFS_FSCK_BAD_FILE_EXTENT;
+				goto out;
+			}
+
+			if (type == BTRFS_FILE_EXTENT_PREALLOC &&
+			    (compress ||
+			     btrfs_file_extent_encryption(leaf, fi) ||
+			     btrfs_file_extent_other_encoding(leaf, fi))) {
+				err |= BTRFS_FSCK_BAD_FILE_EXTENT;
+				goto out;
+			}
+
+			if (disk_bytenr && offset + num_bytes > disk_bytenr) {
+				err |= BTRFS_FSCK_BAD_FILE_EXTENT;
+				goto out;
+			}
+
+			if (key.offset != 0 &&
+			    key.offset != prev_key.offset + prev_num_bytes) {
+				err |= BTRFS_FSCK_BAD_FILE_EXTENT;
+				goto out;
+			}
+
+			break;
+		default:
+			err |= BTRFS_FSCK_BAD_FILE_EXTENT;
+			goto out;
+		}
+
+		memcpy(&prev_key, &key, sizeof(key));
+		prev_num_bytes = num_bytes;
+
+		path->slots[0]++;
+	}
+
+	ret = 0;
+out:
+	*errors |= err;
+	mutex_unlock(&inode->i_mutex);
+	btrfs_free_path(path);
+
+	return ret;
+}
+
+static bool check_dir_item(struct inode *inode, struct extent_buffer *leaf,
+			   struct btrfs_dir_item *di)
+{
+	struct btrfs_key location;
+	u8 type;
+
+	type = btrfs_dir_type(leaf, di);
+	if (type >= BTRFS_FT_MAX || type == BTRFS_FT_XATTR)
+		return false;
+	else if (S_ISDIR(inode->i_mode) && type != BTRFS_FT_DIR)
+		return false;
+
+	btrfs_dir_item_key_to_cpu(leaf, di, &location);
+	if (memcmp(&location, &BTRFS_I(inode)->location, sizeof(location)))
+		return false;
+
+	return true;
+}
+
+static int check_dir_items(u64 *errors, struct dentry *dentry,
+			   struct btrfs_root *root)
+
+{
+	struct inode *inode = dentry->d_inode;
+	struct inode *dir = dentry->d_parent->d_inode;
+	struct btrfs_inode_ref *iref;
+	struct btrfs_dir_item *di;
+	struct btrfs_path *path;
+	int ret = 0;
+	u64 index;
+	u64 err = 0;
+	u64 ino = btrfs_ino(inode);
+	u64 dir_ino = btrfs_ino(dir);
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	iref = btrfs_lookup_inode_ref(NULL, root, path, dentry->d_name.name,
+				      dentry->d_name.len, ino, dir_ino, 0);
+	if (!iref) {
+		err |= BTRFS_FSCK_NO_INODE_REF;
+		goto out;
+	} else if (IS_ERR(iref)) {
+		ret = PTR_ERR(iref);
+		goto out;
+	}
+
+	index = btrfs_inode_ref_index(path->nodes[0], iref);
+	btrfs_release_path(path);
+
+	di = btrfs_lookup_dir_item(NULL, root, path, dir_ino,
+				   dentry->d_name.name, dentry->d_name.len, 0);
+	if (!di) {
+		err |= BTRFS_FSCK_NO_DIR_ITEM;
+		goto check_dir_index;
+	} else if (IS_ERR(di)) {
+		ret = PTR_ERR(di);
+		goto out;
+	}
+
+	if (!check_dir_item(inode, path->nodes[0], di))
+		err |= BTRFS_FSCK_BAD_DIR_ITEM;
+	btrfs_release_path(path);
+
+check_dir_index:
+	di = btrfs_lookup_dir_index_item(NULL, root, path, dir_ino, index,
+					 dentry->d_name.name,
+					 dentry->d_name.len, 0);
+	if (!di) {
+		err |= BTRFS_FSCK_NO_DIR_INDEX;
+		goto out;
+	} else if (IS_ERR(di)) {
+		ret = PTR_ERR(di);
+		goto out;
+	}
+
+	if (!check_dir_item(inode, path->nodes[0], di))
+		err |= BTRFS_FSCK_BAD_DIR_INDEX;
+out:
+	*errors |= err;
+	btrfs_free_path(path);
+	return ret;
+}
+
+static long btrfs_ioctl_online_fsck(struct file *file, void __user *argp)
+{
+	struct btrfs_ioctl_online_fsck_args *args;
+	struct dentry *dentry = fdentry(file);
+	struct inode *inode = dentry->d_inode;
+	struct btrfs_root *root = BTRFS_I(inode)->root;
+	int ret;
+
+	args = kzalloc(sizeof(*args), GFP_KERNEL);
+	if (!args)
+		return -ENOMEM;
+
+	if (copy_from_user(args, argp, sizeof(*args)))
+		return -EFAULT;
+	args->errors = 0;
+
+	ret = check_dir_items(&args->errors, dentry, root);
+	if (ret)
+		return ret;
+
+	if (S_ISDIR(inode->i_mode))
+		goto out;
+
+	ret = check_file_extents(&args->errors, inode, root);
+out:
+	if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))
+		return -EFAULT;
+
+	return ret;
+}
+
 long btrfs_ioctl(struct file *file, unsigned int
 		cmd, unsigned long arg)
 {
@@ -2906,6 +3162,8 @@ long btrfs_ioctl(struct file *file, unsigned int
 		return btrfs_ioctl_scrub_cancel(root, argp);
 	case BTRFS_IOC_SCRUB_PROGRESS:
 		return btrfs_ioctl_scrub_progress(root, argp);
+	case BTRFS_IOC_ONLINE_FSCK:
+		return btrfs_ioctl_online_fsck(file, argp);
 	}
 
 	return -ENOTTY;
diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h
index ad1ea78..85f5c95 100644
--- a/fs/btrfs/ioctl.h
+++ b/fs/btrfs/ioctl.h
@@ -193,6 +193,19 @@ struct btrfs_ioctl_space_args {
 	struct btrfs_ioctl_space_info spaces[0];
 };
 
+#define BTRFS_FSCK_NO_INODE_REF		(1 << 0)
+#define BTRFS_FSCK_NO_DIR_ITEM		(1 << 1)
+#define BTRFS_FSCK_BAD_DIR_ITEM		(1 << 2)
+#define BTRFS_FSCK_NO_DIR_INDEX		(1 << 3)
+#define BTRFS_FSCK_BAD_DIR_INDEX	(1 << 4)
+#define BTRFS_FSCK_NO_FILE_EXTENT	(1 << 5)
+#define BTRFS_FSCK_BAD_FILE_EXTENT	(1 << 6)
+
+struct btrfs_ioctl_online_fsck_args {
+	__u64 errors;
+	__u64 unused[1024 - sizeof(__u64)];
+};
+
 #define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
 				   struct btrfs_ioctl_vol_args)
 #define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
@@ -248,4 +261,6 @@ struct btrfs_ioctl_space_args {
 				 struct btrfs_ioctl_dev_info_args)
 #define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \
 			       struct btrfs_ioctl_fs_info_args)
+#define BTRFS_IOC_ONLINE_FSCK _IOWR(BTRFS_IOCTL_MAGIC, 32, \
+				    struct btrfs_ioctl_online_fsck_args)
 #endif
-- 
1.7.3.1

             reply	other threads:[~2011-06-30  9:33 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-06-30  9:33 Li Zefan [this message]
2011-07-01 17:48 ` [PATCH] Btrfs: initial online fsck support Andi Kleen
2011-07-02 11:47   ` Hubert Kario
2011-07-02 17:04     ` Andi Kleen
2011-07-02 19:06       ` Hubert Kario
2011-07-20 15:06       ` Chris Mason

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4E0C4303.1080406@cn.fujitsu.com \
    --to=lizf@cn.fujitsu.com \
    --cc=linux-btrfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.