For functions btrfs_lookup_dir_index() and btrfs_lookup_dir_item(), they
will check if the dir item/index is valid before doing further check.

The behavior is pretty good for healthy fs, but calling them on
corrupted fs with incorrect dir item/index will also return NULL, making
repair code unable to reuse them.

This patch add a new parameter @verify to address the problem.
For normal operation, @verify should be set to true to keep the old
behavior.

While for some functions used in repair, like btrfs_unlink(), they can
set @verify to false, so repair code can locate corrupted dir index/item
and do what they should do (either repair or just delete).

Signed-off-by: Qu Wenruo <w...@suse.com>
---
 cmds-check.c   |  2 +-
 convert/main.c |  2 +-
 ctree.h        |  4 ++--
 dir-item.c     | 34 ++++++++++++++++++++++++++--------
 inode.c        | 14 +++++++-------
 5 files changed, 37 insertions(+), 19 deletions(-)

diff --git a/cmds-check.c b/cmds-check.c
index f302724dd840..59575506c83e 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -3074,7 +3074,7 @@ static int delete_dir_index(struct btrfs_root *root,
        btrfs_init_path(&path);
        di = btrfs_lookup_dir_index(trans, root, &path, backref->dir,
                                    backref->name, backref->namelen,
-                                   backref->index, -1);
+                                   backref->index, -1, false);
        if (IS_ERR(di)) {
                ret = PTR_ERR(di);
                btrfs_release_path(&path);
diff --git a/convert/main.c b/convert/main.c
index 89f9261172ca..102ba4fc5ae2 100644
--- a/convert/main.c
+++ b/convert/main.c
@@ -1580,7 +1580,7 @@ static int do_rollback(const char *devname)
        /* Search the image file */
        root_dir = btrfs_root_dirid(&image_root->root_item);
        dir = btrfs_lookup_dir_item(NULL, image_root, &path, root_dir,
-                       image_name, strlen(image_name), 0);
+                       image_name, strlen(image_name), 0, true);
 
        if (!dir || IS_ERR(dir)) {
                btrfs_release_path(&path);
diff --git a/ctree.h b/ctree.h
index ef422ea60031..02529f4cb021 100644
--- a/ctree.h
+++ b/ctree.h
@@ -2679,12 +2679,12 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct 
btrfs_trans_handle *trans,
                                             struct btrfs_root *root,
                                             struct btrfs_path *path, u64 dir,
                                             const char *name, int name_len,
-                                            int mod);
+                                            int mod, bool verify);
 struct btrfs_dir_item *btrfs_lookup_dir_index(struct btrfs_trans_handle *trans,
                                              struct btrfs_root *root,
                                              struct btrfs_path *path, u64 dir,
                                              const char *name, int name_len,
-                                             u64 index, int mod);
+                                             u64 index, int mod, bool verify);
 int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
                              struct btrfs_root *root,
                              struct btrfs_path *path,
diff --git a/dir-item.c b/dir-item.c
index 462546c0eaf4..31ad1eca29d5 100644
--- a/dir-item.c
+++ b/dir-item.c
@@ -24,7 +24,7 @@
 
 static struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root 
*root,
                              struct btrfs_path *path,
-                             const char *name, int name_len);
+                             const char *name, int name_len, bool verify);
 
 static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle
                                                   *trans,
@@ -43,7 +43,8 @@ static struct btrfs_dir_item *insert_with_overflow(struct 
btrfs_trans_handle
        ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size);
        if (ret == -EEXIST) {
                struct btrfs_dir_item *di;
-               di = btrfs_match_dir_item_name(root, path, name, name_len);
+               di = btrfs_match_dir_item_name(root, path, name, name_len,
+                                              true);
                if (di)
                        return ERR_PTR(-EEXIST);
                ret = btrfs_extend_item(root, path, data_size);
@@ -196,7 +197,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct 
btrfs_trans_handle *trans,
                                             struct btrfs_root *root,
                                             struct btrfs_path *path, u64 dir,
                                             const char *name, int name_len,
-                                            int mod)
+                                            int mod, bool verify)
 {
        int ret;
        struct btrfs_key key;
@@ -227,14 +228,31 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct 
btrfs_trans_handle *trans,
            found_key.offset != key.offset)
                return NULL;
 
-       return btrfs_match_dir_item_name(root, path, name, name_len);
+       return btrfs_match_dir_item_name(root, path, name, name_len, verify);
 }
 
+/*
+ * Lookup dir index
+ * 
+ * @dir:       dir inode number
+ * @name:      the filename we're looking for
+ * @name_len:  name length
+ * @index:     dir index
+ *
+ * Normally (@root, @dir, @index) should be enough to locate a dir index in a
+ * *healthy* fs.
+ * But with @name and @name_len, we can even handle corrupted fs with
+ * duplicated DIR_INDEX.
+ *
+ * @mod:       if we're going to modify the dir_index, needs @trans
+ * @verify:    if we need to verify the dir_item before search
+ *             useful for check to delet corrupted dir index.
+ */
 struct btrfs_dir_item *btrfs_lookup_dir_index(struct btrfs_trans_handle *trans,
                                              struct btrfs_root *root,
                                              struct btrfs_path *path, u64 dir,
                                              const char *name, int name_len,
-                                             u64 index, int mod)
+                                             u64 index, int mod, bool verify)
 {
        int ret;
        struct btrfs_key key;
@@ -251,7 +269,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_index(struct 
btrfs_trans_handle *trans,
        if (ret > 0)
                return ERR_PTR(-ENOENT);
 
-       return btrfs_match_dir_item_name(root, path, name, name_len);
+       return btrfs_match_dir_item_name(root, path, name, name_len, verify);
 }
 
 /*
@@ -323,7 +341,7 @@ static int verify_dir_item(struct btrfs_root *root,
 
 static struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root 
*root,
                              struct btrfs_path *path,
-                             const char *name, int name_len)
+                             const char *name, int name_len, bool verify)
 {
        struct btrfs_dir_item *dir_item;
        unsigned long name_ptr;
@@ -335,7 +353,7 @@ static struct btrfs_dir_item 
*btrfs_match_dir_item_name(struct btrfs_root *root,
        leaf = path->nodes[0];
        dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
        total_len = btrfs_item_size_nr(leaf, path->slots[0]);
-       if (verify_dir_item(root, leaf, dir_item))
+       if (verify && verify_dir_item(root, leaf, dir_item))
                return NULL;
 
        while(cur < total_len) {
diff --git a/inode.c b/inode.c
index 2398bca4a109..7b9a40062bb3 100644
--- a/inode.c
+++ b/inode.c
@@ -123,7 +123,7 @@ int check_dir_conflict(struct btrfs_root *root, char *name, 
int namelen,
 
        /* Name conflicting? */
        dir_item = btrfs_lookup_dir_item(NULL, root, path, dir, name,
-                                        namelen, 0);
+                                        namelen, 0, true);
        if (IS_ERR(dir_item)) {
                ret = PTR_ERR(dir_item);
                goto out;
@@ -136,7 +136,7 @@ int check_dir_conflict(struct btrfs_root *root, char *name, 
int namelen,
 
        /* Index conflicting? */
        dir_item = btrfs_lookup_dir_index(NULL, root, path, dir, name,
-                                         namelen, index, 0);
+                                         namelen, index, 0, true);
        if (IS_ERR(dir_item) && PTR_ERR(dir_item) == -ENOENT)
                dir_item = NULL;
        if (IS_ERR(dir_item)) {
@@ -301,7 +301,7 @@ int btrfs_unlink(struct btrfs_trans_handle *trans, struct 
btrfs_root *root,
        btrfs_release_path(path);
 
        dir_item = btrfs_lookup_dir_item(NULL, root, path, parent_ino,
-                                        name, namelen, 0);
+                                        name, namelen, 0, false);
        if (IS_ERR(dir_item)) {
                ret = PTR_ERR(dir_item);
                goto out;
@@ -311,7 +311,7 @@ int btrfs_unlink(struct btrfs_trans_handle *trans, struct 
btrfs_root *root,
        btrfs_release_path(path);
 
        dir_item = btrfs_lookup_dir_index(NULL, root, path, parent_ino,
-                                         name, namelen, index, 0);
+                                         name, namelen, index, 0, false);
        /*
         * Since lookup_dir_index() will return -ENOENT when not found,
         * we need to do extra check.
@@ -370,7 +370,7 @@ int btrfs_unlink(struct btrfs_trans_handle *trans, struct 
btrfs_root *root,
        if (del_dir_index) {
                dir_item = btrfs_lookup_dir_index(trans, root, path,
                                                  parent_ino, name, namelen,
-                                                 index, -1);
+                                                 index, -1, false);
                if (IS_ERR(dir_item)) {
                        ret = PTR_ERR(dir_item);
                        goto out;
@@ -403,7 +403,7 @@ int btrfs_unlink(struct btrfs_trans_handle *trans, struct 
btrfs_root *root,
 
        if (del_dir_item) {
                dir_item = btrfs_lookup_dir_item(trans, root, path, parent_ino,
-                                                name, namelen, -1);
+                                                name, namelen, -1, false);
                if (IS_ERR(dir_item)) {
                        ret = PTR_ERR(dir_item);
                        goto out;
@@ -532,7 +532,7 @@ int btrfs_mkdir(struct btrfs_trans_handle *trans, struct 
btrfs_root *root,
                ret_ino = *ino;
 
        dir_item = btrfs_lookup_dir_item(NULL, root, path, parent_ino,
-                                        name, namelen, 0);
+                                        name, namelen, 0, true);
        if (IS_ERR(dir_item)) {
                ret = PTR_ERR(dir_item);
                goto out;
-- 
2.15.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