btrfs-progs: lowmem check: Fix false alert about file extent interrupt
[platform/upstream/btrfs-progs.git] / cmds-check.c
index a55d00d..23adc03 100644 (file)
@@ -40,8 +40,9 @@
 #include "qgroup-verify.h"
 #include "rbtree-utils.h"
 #include "backref.h"
-#include "ulist.h"
+#include "kernel-shared/ulist.h"
 #include "hash.h"
+#include "help.h"
 
 enum task_position {
        TASK_EXTENTS,
@@ -1477,8 +1478,7 @@ out:
        return has_parent ? 0 : 2;
 }
 
-static int process_dir_item(struct btrfs_root *root,
-                           struct extent_buffer *eb,
+static int process_dir_item(struct extent_buffer *eb,
                            int slot, struct btrfs_key *key,
                            struct shared_node *active_node)
 {
@@ -1512,13 +1512,19 @@ static int process_dir_item(struct btrfs_root *root,
                filetype = btrfs_dir_type(eb, di);
 
                rec->found_size += name_len;
-               if (name_len <= BTRFS_NAME_LEN) {
+               if (cur + sizeof(*di) + name_len > total ||
+                   name_len > BTRFS_NAME_LEN) {
+                       error = REF_ERR_NAME_TOO_LONG;
+
+                       if (cur + sizeof(*di) > total)
+                               break;
+                       len = min_t(u32, total - cur - sizeof(*di),
+                                   BTRFS_NAME_LEN);
+               } else {
                        len = name_len;
                        error = 0;
-               } else {
-                       len = BTRFS_NAME_LEN;
-                       error = REF_ERR_NAME_TOO_LONG;
                }
+
                read_extent_buffer(eb, namebuf, (unsigned long)(di + 1), len);
 
                if (location.type == BTRFS_INODE_ITEM_KEY) {
@@ -1569,13 +1575,22 @@ static int process_inode_ref(struct extent_buffer *eb,
        while (cur < total) {
                name_len = btrfs_inode_ref_name_len(eb, ref);
                index = btrfs_inode_ref_index(eb, ref);
-               if (name_len <= BTRFS_NAME_LEN) {
+
+               /* inode_ref + namelen should not cross item boundary */
+               if (cur + sizeof(*ref) + name_len > total ||
+                   name_len > BTRFS_NAME_LEN) {
+                       if (total < cur + sizeof(*ref))
+                               break;
+
+                       /* Still try to read out the remaining part */
+                       len = min_t(u32, total - cur - sizeof(*ref),
+                                   BTRFS_NAME_LEN);
+                       error = REF_ERR_NAME_TOO_LONG;
+               } else {
                        len = name_len;
                        error = 0;
-               } else {
-                       len = BTRFS_NAME_LEN;
-                       error = REF_ERR_NAME_TOO_LONG;
                }
+
                read_extent_buffer(eb, namebuf, (unsigned long)(ref + 1), len);
                add_inode_backref(inode_cache, key->objectid, key->offset,
                                  index, namebuf, len, 0, key->type, error);
@@ -1835,7 +1850,7 @@ static int process_one_leaf(struct btrfs_root *root, struct extent_buffer *eb,
                switch (key.type) {
                case BTRFS_DIR_ITEM_KEY:
                case BTRFS_DIR_INDEX_KEY:
-                       ret = process_dir_item(root, eb, i, &key, active_node);
+                       ret = process_dir_item(eb, i, &key, active_node);
                        break;
                case BTRFS_INODE_REF_KEY:
                        ret = process_inode_ref(eb, i, &key, active_node);
@@ -1868,6 +1883,11 @@ static int update_nodes_refs(struct btrfs_root *root, u64 bytenr,
 static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
                            unsigned int ext_ref);
 
+/*
+ * Returns >0  Found error, not fatal, should continue
+ * Returns <0  Fatal error, must exit the whole check
+ * Returns 0   No errors found
+ */
 static int process_one_leaf_v2(struct btrfs_root *root, struct btrfs_path *path,
                               struct node_refs *nrefs, int *level, int ext_ref)
 {
@@ -1937,13 +1957,8 @@ again:
        }
 out:
        err &= ~LAST_ITEM;
-       /*
-        * Convert any error bitmap to -EIO, as we should avoid
-        * mixing positive and negative return value to represent
-        * error
-        */
        if (err && !ret)
-               ret = -EIO;
+               ret = err;
        return ret;
 }
 
@@ -1983,8 +1998,7 @@ static void reada_walk_down(struct btrfs_root *root,
  * which makes leaf owner check not so strong, key check should be
  * sufficient enough for that case.
  */
-static int check_child_node(struct btrfs_root *root,
-                           struct extent_buffer *parent, int slot,
+static int check_child_node(struct extent_buffer *parent, int slot,
                            struct extent_buffer *child)
 {
        struct btrfs_key parent_key;
@@ -2184,8 +2198,9 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
                        }
                }
 
-               ret = check_child_node(root, cur, path->slots[*level], next);
+               ret = check_child_node(cur, path->slots[*level], next);
                if (ret) {
+                       free_extent_buffer(next);
                        err = ret;
                        goto out;
                }
@@ -2213,6 +2228,11 @@ out:
 static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
                            unsigned int ext_ref);
 
+/*
+ * Returns >0  Found error, should continue
+ * Returns <0  Fatal error, must exit the whole check
+ * Returns 0   No errors found
+ */
 static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
                             int *level, struct node_refs *nrefs, int ext_ref)
 {
@@ -2292,7 +2312,7 @@ static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
                        }
                }
 
-               ret = check_child_node(root, cur, path->slots[*level], next);
+               ret = check_child_node(cur, path->slots[*level], next);
                if (ret < 0) 
                        break;
 
@@ -2534,8 +2554,6 @@ static int add_missing_dir_index(struct btrfs_root *root,
 }
 
 static int delete_dir_index(struct btrfs_root *root,
-                           struct cache_tree *inode_cache,
-                           struct inode_record *rec,
                            struct inode_backref *backref)
 {
        struct btrfs_trans_handle *trans;
@@ -2577,7 +2595,7 @@ static int delete_dir_index(struct btrfs_root *root,
 
 static int create_inode_item(struct btrfs_root *root,
                             struct inode_record *rec,
-                            struct inode_backref *backref, int root_dir)
+                            int root_dir)
 {
        struct btrfs_trans_handle *trans;
        struct btrfs_inode_item inode_item;
@@ -2643,7 +2661,7 @@ static int repair_inode_backrefs(struct btrfs_root *root,
        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);
+                               ret = create_inode_item(root, rec, 1);
                                if (ret)
                                        break;
                                repaired++;
@@ -2658,12 +2676,13 @@ static int repair_inode_backrefs(struct btrfs_root *root,
                    ((backref->found_dir_index && !backref->found_inode_ref) ||
                     (backref->found_dir_index && backref->found_inode_ref &&
                      (backref->errors & REF_ERR_INDEX_UNMATCH)))) {
-                       ret = delete_dir_index(root, inode_cache, rec, backref);
+                       ret = delete_dir_index(root, backref);
                        if (ret)
                                break;
                        repaired++;
                        list_del(&backref->list);
                        free(backref);
+                       continue;
                }
 
                if (!delete && !backref->found_dir_index &&
@@ -2674,12 +2693,12 @@ static int repair_inode_backrefs(struct btrfs_root *root,
                                break;
                        repaired++;
                        if (backref->found_dir_item &&
-                           backref->found_dir_index &&
                            backref->found_dir_index) {
                                if (!backref->errors &&
                                    backref->found_inode_ref) {
                                        list_del(&backref->list);
                                        free(backref);
+                                       continue;
                                }
                        }
                }
@@ -2729,7 +2748,7 @@ static int repair_inode_backrefs(struct btrfs_root *root,
                                backref->found_dir_item &&
                                !(backref->errors & REF_ERR_INDEX_UNMATCH) &&
                                !rec->found_inode_item)) {
-                       ret = create_inode_item(root, rec, backref, 0);
+                       ret = create_inode_item(root, rec, 0);
                        if (ret)
                                break;
                        repaired++;
@@ -3825,8 +3844,7 @@ static int repair_btree(struct btrfs_root *root,
                                             path.slots[level]);
 
                /* Remove the ptr */
-               ret = btrfs_del_ptr(trans, root, &path, level,
-                                   path.slots[level]);
+               ret = btrfs_del_ptr(root, &path, level, path.slots[level]);
                if (ret < 0)
                        goto out;
                /*
@@ -4223,16 +4241,22 @@ static int find_dir_item(struct btrfs_root *root, struct btrfs_key *ref_key,
                if (imode_to_type(mode) != filetype)
                        goto next;
 
-               if (name_len <= BTRFS_NAME_LEN) {
-                       len = name_len;
-               } else {
-                       len = BTRFS_NAME_LEN;
+               if (cur + sizeof(*di) + name_len > total ||
+                   name_len > BTRFS_NAME_LEN) {
                        warning("root %llu %s[%llu %llu] name too long %u, trimmed",
-                       root->objectid,
-                       key->type == BTRFS_DIR_ITEM_KEY ?
-                       "DIR_ITEM" : "DIR_INDEX",
-                       key->objectid, key->offset, name_len);
+                               root->objectid,
+                               key->type == BTRFS_DIR_ITEM_KEY ?
+                               "DIR_ITEM" : "DIR_INDEX",
+                               key->objectid, key->offset, name_len);
+
+                       if (cur + sizeof(*di) > total)
+                               break;
+                       len = min_t(u32, total - cur - sizeof(*di),
+                                   BTRFS_NAME_LEN);
+               } else {
+                       len = name_len;
                }
+
                read_extent_buffer(node, namebuf, (unsigned long)(di + 1), len);
                if (len != namelen || strncmp(namebuf, name, len))
                        goto next;
@@ -4293,12 +4317,16 @@ next:
 
        index = btrfs_inode_ref_index(node, ref);
        name_len = btrfs_inode_ref_name_len(node, ref);
-       if (name_len <= BTRFS_NAME_LEN) {
-               len = name_len;
-       } else {
-               len = BTRFS_NAME_LEN;
+       if (cur + sizeof(*ref) + name_len > total ||
+           name_len > BTRFS_NAME_LEN) {
                warning("root %llu INODE_REF[%llu %llu] name too long",
                        root->objectid, ref_key->objectid, ref_key->offset);
+
+               if (total < cur + sizeof(*ref))
+                       goto out;
+               len = min_t(u32, total - cur - sizeof(*ref), BTRFS_NAME_LEN);
+       } else {
+               len = name_len;
        }
 
        read_extent_buffer(node, namebuf, (unsigned long)(ref + 1), len);
@@ -4331,6 +4359,7 @@ next:
        if (cur < total)
                goto next;
 
+out:
        return err;
 }
 
@@ -4468,16 +4497,22 @@ static int find_inode_ref(struct btrfs_root *root, struct btrfs_key *key,
                if (index != (u64)-1 && index != ref_index)
                        goto next_ref;
 
-               if (ref_namelen <= BTRFS_NAME_LEN) {
-                       len = ref_namelen;
-               } else {
-                       len = BTRFS_NAME_LEN;
+               if (cur + sizeof(*ref) + ref_namelen > total ||
+                   ref_namelen > BTRFS_NAME_LEN) {
                        warning("root %llu INODE %s[%llu %llu] name too long",
                                root->objectid,
                                key->type == BTRFS_INODE_REF_KEY ?
                                        "REF" : "EXTREF",
                                key->objectid, key->offset);
+
+                       if (cur + sizeof(*ref) > total)
+                               break;
+                       len = min_t(u32, total - cur - sizeof(*ref),
+                                   BTRFS_NAME_LEN);
+               } else {
+                       len = ref_namelen;
                }
+
                read_extent_buffer(node, ref_namebuf, (unsigned long)(ref + 1),
                                   len);
 
@@ -4609,15 +4644,20 @@ static int check_dir_item(struct btrfs_root *root, struct btrfs_key *key,
                              key->objectid, key->offset, data_len);
 
                name_len = btrfs_dir_name_len(node, di);
-               if (name_len <= BTRFS_NAME_LEN) {
-                       len = name_len;
-               } else {
-                       len = BTRFS_NAME_LEN;
+               if (cur + sizeof(*di) + name_len > total ||
+                   name_len > BTRFS_NAME_LEN) {
                        warning("root %llu %s[%llu %llu] name too long",
                                root->objectid,
                                key->type == BTRFS_DIR_ITEM_KEY ?
                                "DIR_ITEM" : "DIR_INDEX",
                                key->objectid, key->offset);
+
+                       if (cur + sizeof(*di) > total)
+                               break;
+                       len = min_t(u32, total - cur - sizeof(*di),
+                                   BTRFS_NAME_LEN);
+               } else {
+                       len = name_len;
                }
                (*size) += name_len;
 
@@ -4703,25 +4743,41 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
        u64 disk_bytenr;
        u64 disk_num_bytes;
        u64 extent_num_bytes;
-       u64 found;
+       u64 extent_offset;
+       u64 csum_found;         /* In byte size, sectorsize aligned */
+       u64 search_start;       /* Logical range start we search for csum */
+       u64 search_len;         /* Logical range len we search for csum */
        unsigned int extent_type;
        unsigned int is_hole;
+       int compressed = 0;
        int ret;
        int err = 0;
 
        fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item);
 
+       /* Check inline extent */
        extent_type = btrfs_file_extent_type(node, fi);
-       /* Skip if file extent is inline */
        if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
                struct btrfs_item *e = btrfs_item_nr(slot);
                u32 item_inline_len;
 
                item_inline_len = btrfs_file_extent_inline_item_len(node, e);
                extent_num_bytes = btrfs_file_extent_inline_len(node, slot, fi);
-               if (extent_num_bytes == 0 ||
-                   extent_num_bytes != item_inline_len)
+               compressed = btrfs_file_extent_compression(node, fi);
+               if (extent_num_bytes == 0) {
+                       error(
+               "root %llu EXTENT_DATA[%llu %llu] has empty inline extent",
+                               root->objectid, fkey->objectid, fkey->offset);
                        err |= FILE_EXTENT_ERROR;
+               }
+               if (!compressed && extent_num_bytes != item_inline_len) {
+                       error(
+               "root %llu EXTENT_DATA[%llu %llu] wrong inline size, have: %llu, expected: %u",
+                               root->objectid, fkey->objectid, fkey->offset,
+                               extent_num_bytes, item_inline_len);
+                       err |= FILE_EXTENT_ERROR;
+               }
+               *end += extent_num_bytes;
                *size += extent_num_bytes;
                return err;
        }
@@ -4739,32 +4795,49 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
        disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi);
        disk_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi);
        extent_num_bytes = btrfs_file_extent_num_bytes(node, fi);
+       extent_offset = btrfs_file_extent_offset(node, fi);
+       compressed = btrfs_file_extent_compression(node, fi);
        is_hole = (disk_bytenr == 0) && (disk_num_bytes == 0);
 
-       /* Check EXTENT_DATA datasum */
-       ret = count_csum_range(root, disk_bytenr, disk_num_bytes, &found);
-       if (found > 0 && nodatasum) {
+       /*
+        * Check EXTENT_DATA csum
+        *
+        * For plain (uncompressed) extent, we should only check the range
+        * we're referring to, as it's possible that part of prealloc extent
+        * has been written, and has csum:
+        *
+        * |<--- Original large preallocated extent A ---->|
+        * |<- Prealloc File Extent ->|<- Regular Extent ->|
+        *      No csum                         Has csum
+        *
+        * For compressed extent, we should check the whole range.
+        */
+       if (!compressed) {
+               search_start = disk_bytenr + extent_offset;
+               search_len = extent_num_bytes;
+       } else {
+               search_start = disk_bytenr;
+               search_len = disk_num_bytes;
+       }
+       ret = count_csum_range(root, search_start, search_len, &csum_found);
+       if (csum_found > 0 && nodatasum) {
                err |= ODD_CSUM_ITEM;
                error("root %llu EXTENT_DATA[%llu %llu] nodatasum shouldn't have datasum",
                      root->objectid, fkey->objectid, fkey->offset);
        } else if (extent_type == BTRFS_FILE_EXTENT_REG && !nodatasum &&
-                  !is_hole &&
-                  (ret < 0 || found == 0 || found < disk_num_bytes)) {
+                  !is_hole && (ret < 0 || csum_found < search_len)) {
                err |= CSUM_ITEM_MISSING;
-               error("root %llu EXTENT_DATA[%llu %llu] datasum missing",
-                     root->objectid, fkey->objectid, fkey->offset);
-       } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC && found > 0) {
+               error("root %llu EXTENT_DATA[%llu %llu] csum missing, have: %llu, expected: %llu",
+                     root->objectid, fkey->objectid, fkey->offset,
+                     csum_found, search_len);
+       } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC && csum_found > 0) {
                err |= ODD_CSUM_ITEM;
-               error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have datasum",
-                     root->objectid, fkey->objectid, fkey->offset);
+               error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have csum, but has: %llu",
+                     root->objectid, fkey->objectid, fkey->offset, csum_found);
        }
 
        /* Check EXTENT_DATA hole */
-       if (no_holes && is_hole) {
-               err |= FILE_EXTENT_ERROR;
-               error("root %llu EXTENT_DATA[%llu %llu] shouldn't be hole",
-                     root->objectid, fkey->objectid, fkey->offset);
-       } else if (!no_holes && *end != fkey->offset) {
+       if (!no_holes && *end != fkey->offset) {
                err |= FILE_EXTENT_ERROR;
                error("root %llu EXTENT_DATA[%llu %llu] interrupt",
                      root->objectid, fkey->objectid, fkey->offset);
@@ -4947,17 +5020,26 @@ static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref)
        int err = 0;
        int ret;
 
-       btrfs_init_path(&path);
        key.objectid = BTRFS_FIRST_FREE_OBJECTID;
        key.type = BTRFS_INODE_ITEM_KEY;
        key.offset = 0;
 
+       /* For root being dropped, we don't need to check first inode */
+       if (btrfs_root_refs(&root->root_item) == 0 &&
+           btrfs_disk_key_objectid(&root->root_item.drop_progress) >=
+           key.objectid)
+               return 0;
+
+       btrfs_init_path(&path);
+
        ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
        if (ret < 0)
                goto out;
        if (ret > 0) {
                ret = 0;
                err |= INODE_ITEM_MISSING;
+               error("first inode item of root %llu is missing",
+                     root->objectid);
        }
 
        err |= check_inode_item(root, &path, ext_ref);
@@ -4983,8 +5065,9 @@ static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref)
        struct btrfs_path path;
        struct node_refs nrefs;
        struct btrfs_root_item *root_item = &root->root_item;
-       int ret, wret;
+       int ret;
        int level;
+       int err = 0;
 
        /*
         * We need to manually check the first inode item(256)
@@ -5018,17 +5101,21 @@ static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref)
        }
 
        while (1) {
-               wret = walk_down_tree_v2(root, &path, &level, &nrefs, ext_ref);
-               if (wret < 0)
-                       ret = wret;
-               if (wret != 0)
+               ret = walk_down_tree_v2(root, &path, &level, &nrefs, ext_ref);
+               err |= !!ret;
+
+               /* if ret is negative, walk shall stop */
+               if (ret < 0) {
+                       ret = err;
                        break;
+               }
 
-               wret = walk_up_tree_v2(root, &path, &level);
-               if (wret < 0)
-                       ret = wret;
-               if (wret != 0)
+               ret = walk_up_tree_v2(root, &path, &level);
+               if (ret != 0) {
+                       /* Normal exit, reset ret to err */
+                       ret = err;
                        break;
+               }
        }
 
 out:
@@ -5338,8 +5425,7 @@ static int free_all_extent_backrefs(struct extent_record *rec)
        return 0;
 }
 
-static void free_extent_record_cache(struct btrfs_fs_info *fs_info,
-                                    struct cache_tree *extent_cache)
+static void free_extent_record_cache(struct cache_tree *extent_cache)
 {
        struct cache_extent *cache;
        struct extent_record *rec;
@@ -5538,9 +5624,7 @@ static int swap_values(struct btrfs_root *root, struct btrfs_path *path,
        return 0;
 }
 
-static int fix_key_order(struct btrfs_trans_handle *trans,
-                        struct btrfs_root *root,
-                        struct btrfs_path *path)
+static int fix_key_order(struct btrfs_root *root, struct btrfs_path *path)
 {
        struct extent_buffer *buf;
        struct btrfs_key k1, k2;
@@ -5568,8 +5652,7 @@ static int fix_key_order(struct btrfs_trans_handle *trans,
        return ret;
 }
 
-static int delete_bogus_item(struct btrfs_trans_handle *trans,
-                            struct btrfs_root *root,
+static int delete_bogus_item(struct btrfs_root *root,
                             struct btrfs_path *path,
                             struct extent_buffer *buf, int slot)
 {
@@ -5604,9 +5687,7 @@ static int delete_bogus_item(struct btrfs_trans_handle *trans,
        return 0;
 }
 
-static int fix_item_offset(struct btrfs_trans_handle *trans,
-                          struct btrfs_root *root,
-                          struct btrfs_path *path)
+static int fix_item_offset(struct btrfs_root *root, struct btrfs_path *path)
 {
        struct extent_buffer *buf;
        int i;
@@ -5623,8 +5704,7 @@ again:
                    BTRFS_LEAF_DATA_SIZE(root)) {
                        if (btrfs_item_end_nr(buf, i) >
                            BTRFS_LEAF_DATA_SIZE(root)) {
-                               ret = delete_bogus_item(trans, root, path,
-                                                       buf, i);
+                               ret = delete_bogus_item(root, path, buf, i);
                                if (!ret)
                                        goto again;
                                fprintf(stderr, "item is off the end of the "
@@ -5638,8 +5718,7 @@ again:
                           btrfs_item_offset_nr(buf, i - 1)) {
                        if (btrfs_item_end_nr(buf, i) >
                            btrfs_item_offset_nr(buf, i - 1)) {
-                               ret = delete_bogus_item(trans, root, path,
-                                                       buf, i);
+                               ret = delete_bogus_item(root, path, buf, i);
                                if (!ret)
                                        goto again;
                                fprintf(stderr, "items overlap, can't fix\n");
@@ -5731,9 +5810,9 @@ static int try_to_fix_bad_block(struct btrfs_root *root,
                        break;
                }
                if (status == BTRFS_TREE_BLOCK_BAD_KEY_ORDER)
-                       ret = fix_key_order(trans, search_root, &path);
+                       ret = fix_key_order(search_root, &path);
                else if (status == BTRFS_TREE_BLOCK_INVALID_OFFSETS)
-                       ret = fix_item_offset(trans, search_root, &path);
+                       ret = fix_item_offset(search_root, &path);
                if (ret) {
                        btrfs_commit_transaction(trans, search_root);
                        break;
@@ -5986,6 +6065,7 @@ static int add_extent_rec_nolookup(struct cache_tree *extent_cache,
        struct extent_record *rec;
        int ret = 0;
 
+       BUG_ON(tmpl->max_size == 0);
        rec = malloc(sizeof(*rec));
        if (!rec)
                return -ENOMEM;
@@ -6149,6 +6229,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
                tmpl.start = bytenr;
                tmpl.nr = 1;
                tmpl.metadata = 1;
+               tmpl.max_size = 1;
 
                ret = add_extent_rec_nolookup(extent_cache, &tmpl);
                if (ret)
@@ -6789,14 +6870,16 @@ static int process_extent_item(struct btrfs_root *root,
                        ret = add_tree_backref(extent_cache, key.objectid,
                                        0, offset, 0);
                        if (ret < 0)
-                               error("add_tree_backref failed: %s",
+                               error(
+                       "add_tree_backref failed (extent items tree block): %s",
                                      strerror(-ret));
                        break;
                case BTRFS_SHARED_BLOCK_REF_KEY:
                        ret = add_tree_backref(extent_cache, key.objectid,
                                        offset, 0, 0);
                        if (ret < 0)
-                               error("add_tree_backref failed: %s",
+                               error(
+                       "add_tree_backref failed (extent items shared block): %s",
                                      strerror(-ret));
                        break;
                case BTRFS_EXTENT_DATA_REF_KEY:
@@ -7112,7 +7195,7 @@ again:
                        csum = ~(u32)0;
                        tmp = offset + data_checked;
 
-                       csum = btrfs_csum_data(NULL, (char *)data + tmp,
+                       csum = btrfs_csum_data((char *)data + tmp,
                                               csum, root->sectorsize);
                        btrfs_csum_final(csum, (u8 *)&csum);
 
@@ -7399,8 +7482,7 @@ static int is_dropped_key(struct btrfs_key *key,
  * assumption and simply indicate that we _think_ that the FULL BACKREF needs to
  * be set or not and then we can check later once we've gathered all the refs.
  */
-static int calc_extent_flag(struct btrfs_root *root,
-                          struct cache_tree *extent_cache,
+static int calc_extent_flag(struct cache_tree *extent_cache,
                           struct extent_buffer *buf,
                           struct root_item_record *ri,
                           u64 *flags)
@@ -7594,7 +7676,7 @@ static int run_next_block(struct btrfs_root *root,
                                       btrfs_header_level(buf), 1, NULL,
                                       &flags);
                if (ret < 0) {
-                       ret = calc_extent_flag(root, extent_cache, buf, ri, &flags);
+                       ret = calc_extent_flag(extent_cache, buf, ri, &flags);
                        if (ret < 0) {
                                fprintf(stderr, "Couldn't calc extent flags\n");
                                flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
@@ -7602,7 +7684,7 @@ static int run_next_block(struct btrfs_root *root,
                }
        } else {
                flags = 0;
-               ret = calc_extent_flag(root, extent_cache, buf, ri, &flags);
+               ret = calc_extent_flag(extent_cache, buf, ri, &flags);
                if (ret < 0) {
                        fprintf(stderr, "Couldn't calc extent flags\n");
                        flags |= BTRFS_BLOCK_FLAG_FULL_BACKREF;
@@ -7711,7 +7793,8 @@ static int run_next_block(struct btrfs_root *root,
                                ret = add_tree_backref(extent_cache,
                                                key.objectid, 0, key.offset, 0);
                                if (ret < 0)
-                                       error("add_tree_backref failed: %s",
+                                       error(
+                               "add_tree_backref failed (leaf tree block): %s",
                                              strerror(-ret));
                                continue;
                        }
@@ -7719,7 +7802,8 @@ static int run_next_block(struct btrfs_root *root,
                                ret = add_tree_backref(extent_cache,
                                                key.objectid, key.offset, 0, 0);
                                if (ret < 0)
-                                       error("add_tree_backref failed: %s",
+                                       error(
+                               "add_tree_backref failed (leaf shared block): %s",
                                              strerror(-ret));
                                continue;
                        }
@@ -7824,7 +7908,8 @@ static int run_next_block(struct btrfs_root *root,
                        ret = add_tree_backref(extent_cache, ptr, parent,
                                        owner, 1);
                        if (ret < 0) {
-                               error("add_tree_backref failed: %s",
+                               error(
+                               "add_tree_backref failed (non-leaf block): %s",
                                      strerror(-ret));
                                continue;
                        }
@@ -7961,7 +8046,7 @@ out:
 static int delete_extent_records(struct btrfs_trans_handle *trans,
                                 struct btrfs_root *root,
                                 struct btrfs_path *path,
-                                u64 bytenr, u64 new_len)
+                                u64 bytenr)
 {
        struct btrfs_key key;
        struct btrfs_key found_key;
@@ -8571,8 +8656,7 @@ out:
        return ret;
 }
 
-static int process_duplicates(struct btrfs_root *root,
-                             struct cache_tree *extent_cache,
+static int process_duplicates(struct cache_tree *extent_cache,
                              struct extent_record *rec)
 {
        struct extent_record *good, *tmp;
@@ -8967,7 +9051,7 @@ static int fixup_extent_refs(struct btrfs_fs_info *info,
 
        /* step two, delete all the existing records */
        ret = delete_extent_records(trans, info->extent_root, &path,
-                                   rec->start, rec->max_size);
+                                   rec->start);
 
        if (ret < 0)
                goto out;
@@ -9137,7 +9221,7 @@ again:
 
 del_ptr:
        printk("deleting pointer to block %Lu\n", corrupt->cache.start);
-       ret = btrfs_del_ptr(trans, info->extent_root, &path, level, slot);
+       ret = btrfs_del_ptr(info->extent_root, &path, level, slot);
 
 out:
        btrfs_release_path(&path);
@@ -9179,8 +9263,7 @@ static void reset_cached_block_groups(struct btrfs_fs_info *fs_info)
                                            &start, &end, EXTENT_DIRTY);
                if (ret)
                        break;
-               clear_extent_dirty(&fs_info->free_space_cache, start, end,
-                                  GFP_NOFS);
+               clear_extent_dirty(&fs_info->free_space_cache, start, end);
        }
 
        start = 0;
@@ -9214,8 +9297,7 @@ static int check_extent_refs(struct btrfs_root *root,
                        rec = container_of(cache, struct extent_record, cache);
                        set_extent_dirty(root->fs_info->excluded_extents,
                                         rec->start,
-                                        rec->start + rec->max_size - 1,
-                                        GFP_NOFS);
+                                        rec->start + rec->max_size - 1);
                        cache = next_cache_extent(cache);
                }
 
@@ -9224,8 +9306,7 @@ static int check_extent_refs(struct btrfs_root *root,
                while(cache) {
                        set_extent_dirty(root->fs_info->excluded_extents,
                                         cache->start,
-                                        cache->start + cache->size - 1,
-                                        GFP_NOFS);
+                                        cache->start + cache->size - 1);
                        cache = next_cache_extent(cache);
                }
                prune_corrupt_blocks(root->fs_info);
@@ -9250,7 +9331,7 @@ static int check_extent_refs(struct btrfs_root *root,
                 * process_duplicates() will return 0, otherwise it will return
                 * 1 and we
                 */
-               if (process_duplicates(root, extent_cache, rec))
+               if (process_duplicates(extent_cache, rec))
                        continue;
                ret = delete_duplicate_records(root, rec);
                if (ret < 0)
@@ -9351,8 +9432,7 @@ static int check_extent_refs(struct btrfs_root *root,
                if (!init_extent_tree && repair && (!cur_err || fix))
                        clear_extent_dirty(root->fs_info->excluded_extents,
                                           rec->start,
-                                          rec->start + rec->max_size - 1,
-                                          GFP_NOFS);
+                                          rec->start + rec->max_size - 1);
                free(rec);
        }
 repair_abort:
@@ -9935,6 +10015,8 @@ out:
        free_extent_cache_tree(&pending);
        free_extent_cache_tree(&reada);
        free_extent_cache_tree(&nodes);
+       free_root_item_list(&normal_trees);
+       free_root_item_list(&dropping_trees);
        return ret;
 loop:
        free_corrupt_blocks_tree(root->fs_info->corrupt_blocks);
@@ -9946,7 +10028,7 @@ loop:
        free_block_group_tree(&block_group_cache);
        free_device_cache_tree(&dev_cache);
        free_device_extent_tree(&dev_extent_cache);
-       free_extent_record_cache(root->fs_info, &extent_cache);
+       free_extent_record_cache(&extent_cache);
        free_root_item_list(&normal_trees);
        free_root_item_list(&dropping_trees);
        extent_io_tree_cleanup(&excluded_extents);
@@ -10192,10 +10274,8 @@ static int check_extent_data_item(struct btrfs_root *root,
        dbref_key.offset = btrfs_file_extent_disk_num_bytes(eb, fi);
 
        ret = btrfs_search_slot(NULL, extent_root, &dbref_key, &path, 0, 0);
-       if (ret) {
-               err |= BACKREF_MISSING;
-               goto error;
-       }
+       if (ret)
+               goto out;
 
        leaf = path.nodes[0];
        slot = path.slots[0];
@@ -10236,11 +10316,10 @@ static int check_extent_data_item(struct btrfs_root *root,
                ptr += btrfs_extent_inline_ref_size(type);
        }
 
-       /* Didn't found inlined data backref, try EXTENT_DATA_REF_KEY */
        if (!found_dbackref) {
                btrfs_release_path(&path);
 
-               btrfs_init_path(&path);
+               /* Didn't find inlined data backref, try EXTENT_DATA_REF_KEY */
                dbref_key.objectid = btrfs_file_extent_disk_bytenr(eb, fi);
                dbref_key.type = BTRFS_EXTENT_DATA_REF_KEY;
                dbref_key.offset = hash_extent_data_ref(root->objectid,
@@ -10248,13 +10327,32 @@ static int check_extent_data_item(struct btrfs_root *root,
 
                ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
                                        &dbref_key, &path, 0, 0);
-               if (!ret)
+               if (!ret) {
+                       found_dbackref = 1;
+                       goto out;
+               }
+
+               btrfs_release_path(&path);
+
+               /*
+                * Neither inlined nor EXTENT_DATA_REF found, try
+                * SHARED_DATA_REF as last chance.
+                */
+               dbref_key.objectid = disk_bytenr;
+               dbref_key.type = BTRFS_SHARED_DATA_REF_KEY;
+               dbref_key.offset = eb->start;
+
+               ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
+                                       &dbref_key, &path, 0, 0);
+               if (!ret) {
                        found_dbackref = 1;
+                       goto out;
+               }
        }
 
+out:
        if (!found_dbackref)
                err |= BACKREF_MISSING;
-error:
        btrfs_release_path(&path);
        if (err & BACKREF_MISSING) {
                error("data extent[%llu %llu] backref lost",
@@ -10584,6 +10682,8 @@ static int check_extent_data_backref(struct btrfs_fs_info *fs_info,
                leaf = path.nodes[0];
                slot = path.slots[0];
 
+               if (slot >= btrfs_header_nritems(leaf))
+                       goto next;
                btrfs_item_key_to_cpu(leaf, &key, slot);
                if (key.objectid != objectid || key.type != BTRFS_EXTENT_DATA_KEY)
                        break;
@@ -10599,6 +10699,7 @@ static int check_extent_data_backref(struct btrfs_fs_info *fs_info,
                    offset)
                        found_count++;
 
+next:
                ret = btrfs_next_item(root, &path);
                if (ret)
                        break;
@@ -10726,13 +10827,20 @@ static int check_extent_item(struct btrfs_fs_info *fs_info,
        }
        end = (unsigned long)ei + item_size;
 
-       if (ptr >= end) {
+next:
+       /* Reached extent item end normally */
+       if (ptr == end)
+               goto out;
+
+       /* Beyond extent item end, wrong item size */
+       if (ptr > end) {
                err |= ITEM_SIZE_MISMATCH;
+               error("extent item at bytenr %llu slot %d has wrong size",
+                       eb->start, slot);
                goto out;
        }
 
        /* Now check every backref in this extent item */
-next:
        iref = (struct btrfs_extent_inline_ref *)ptr;
        type = btrfs_extent_inline_ref_type(eb, iref);
        offset = btrfs_extent_inline_ref_offset(eb, iref);
@@ -10769,8 +10877,7 @@ next:
        }
 
        ptr += btrfs_extent_inline_ref_size(type);
-       if (ptr < end)
-               goto next;
+       goto next;
 
 out:
        return err;
@@ -10871,8 +10978,10 @@ static int check_dev_item(struct btrfs_fs_info *fs_info,
 
        /* Iterate dev_extents to calculate the used space of a device */
        while (1) {
-               btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+               if (path.slots[0] >= btrfs_header_nritems(path.nodes[0]))
+                       goto next;
 
+               btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
                if (key.objectid > dev_id)
                        break;
                if (key.type != BTRFS_DEV_EXTENT_KEY || key.objectid != dev_id)
@@ -10969,6 +11078,11 @@ static int check_block_group_item(struct btrfs_fs_info *fs_info,
        /* Iterate extent tree to account used space */
        while (1) {
                leaf = path.nodes[0];
+
+               /* Search slot can point to the last item beyond leaf nritems */
+               if (path.slots[0] >= btrfs_header_nritems(leaf))
+                       goto next;
+
                btrfs_item_key_to_cpu(leaf, &extent_key, path.slots[0]);
                if (extent_key.objectid >= bg_key.objectid + bg_key.offset)
                        break;
@@ -11669,8 +11783,7 @@ static int reset_block_groups(struct btrfs_fs_info *fs_info)
                                      key.objectid, key.offset,
                                      btrfs_chunk_length(leaf, chunk));
                set_extent_dirty(&fs_info->free_space_cache, key.offset,
-                                key.offset + btrfs_chunk_length(leaf, chunk),
-                                GFP_NOFS);
+                                key.offset + btrfs_chunk_length(leaf, chunk));
                path.slots[0]++;
        }
        start = 0;
@@ -12333,8 +12446,7 @@ out:
        return ret;
 }
 
-static int maybe_repair_root_item(struct btrfs_fs_info *info,
-                                 struct btrfs_path *path,
+static int maybe_repair_root_item(struct btrfs_path *path,
                                  const struct btrfs_key *root_key,
                                  const int read_only_mode)
 {
@@ -12489,8 +12601,7 @@ again:
                if (found_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
                        goto next;
 
-               ret = maybe_repair_root_item(info, &path, &found_key,
-                                            trans ? 0 : 1);
+               ret = maybe_repair_root_item(&path, &found_key, trans ? 0 : 1);
                if (ret < 0)
                        goto out;
                if (ret) {
@@ -12916,8 +13027,10 @@ int cmd_check(int argc, char **argv)
 
        ret = repair_root_items(info);
        err |= !!ret;
-       if (ret < 0)
+       if (ret < 0) {
+               error("failed to repair root items: %s", strerror(-ret));
                goto close_out;
+       }
        if (repair) {
                fprintf(stderr, "Fixed %d roots.\n", ret);
                ret = 0;
@@ -12940,8 +13053,13 @@ int cmd_check(int argc, char **argv)
        }
        ret = check_space_cache(root);
        err |= !!ret;
-       if (ret)
+       if (ret) {
+               if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE))
+                       error("errors found in free space tree");
+               else
+                       error("errors found in free space cache");
                goto out;
+       }
 
        /*
         * We used to have to have these hole extents in between our real
@@ -12957,22 +13075,28 @@ int cmd_check(int argc, char **argv)
        else
                ret = check_fs_roots(root, &root_cache);
        err |= !!ret;
-       if (ret)
+       if (ret) {
+               error("errors found in fs roots");
                goto out;
+       }
 
        fprintf(stderr, "checking csums\n");
        ret = check_csums(root);
        err |= !!ret;
-       if (ret)
+       if (ret) {
+               error("errors found in csum tree");
                goto out;
+       }
 
        fprintf(stderr, "checking root refs\n");
        /* For low memory mode, check_fs_roots_v2 handles root refs */
        if (check_mode != CHECK_MODE_LOWMEM) {
                ret = check_root_refs(root, &root_cache);
                err |= !!ret;
-               if (ret)
+               if (ret) {
+                       error("errors found in root refs");
                        goto out;
+               }
        }
 
        while (repair && !list_empty(&root->fs_info->recow_ebs)) {
@@ -12983,8 +13107,10 @@ int cmd_check(int argc, char **argv)
                list_del_init(&eb->recow);
                ret = recow_extent_buffer(root, eb);
                err |= !!ret;
-               if (ret)
+               if (ret) {
+                       error("fails to fix transid errors");
                        break;
+               }
        }
 
        while (!list_empty(&delete_items)) {
@@ -13003,13 +13129,17 @@ int cmd_check(int argc, char **argv)
                fprintf(stderr, "checking quota groups\n");
                ret = qgroup_verify_all(info);
                err |= !!ret;
-               if (ret)
+               if (ret) {
+                       error("failed to check quota groups");
                        goto out;
+               }
                report_qgroups(0);
                ret = repair_qgroups(info, &qgroups_repaired);
                err |= !!ret;
-               if (err)
+               if (err) {
+                       error("failed to repair quota groups");
                        goto out;
+               }
                ret = 0;
        }
 
@@ -13030,8 +13160,12 @@ out:
                       "backup data and re-format the FS. *\n\n");
                err |= 1;
        }
-       printf("found %llu bytes used err is %d\n",
-              (unsigned long long)bytes_used, ret);
+       printf("found %llu bytes used, ",
+              (unsigned long long)bytes_used);
+       if (err)
+               printf("error(s) found\n");
+       else
+               printf("no error found\n");
        printf("total csum bytes: %llu\n",(unsigned long long)total_csum_bytes);
        printf("total tree bytes: %llu\n",
               (unsigned long long)total_btree_bytes);