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 <l...@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
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to