]> rtime.felk.cvut.cz Git - linux-imx.git/blobdiff - fs/btrfs/backref.c
Btrfs: fix backref walking when we hit a compressed extent
[linux-imx.git] / fs / btrfs / backref.c
index 290e347b6db3f925f414fd9be4e6ea394da6f887..30dbe1c618577bcfd7c7f47f3403845d35b7ee91 100644 (file)
@@ -36,16 +36,23 @@ static int check_extent_in_eb(struct btrfs_key *key, struct extent_buffer *eb,
                                u64 extent_item_pos,
                                struct extent_inode_elem **eie)
 {
-       u64 data_offset;
-       u64 data_len;
+       u64 offset = 0;
        struct extent_inode_elem *e;
 
-       data_offset = btrfs_file_extent_offset(eb, fi);
-       data_len = btrfs_file_extent_num_bytes(eb, fi);
+       if (!btrfs_file_extent_compression(eb, fi) &&
+           !btrfs_file_extent_encryption(eb, fi) &&
+           !btrfs_file_extent_other_encoding(eb, fi)) {
+               u64 data_offset;
+               u64 data_len;
 
-       if (extent_item_pos < data_offset ||
-           extent_item_pos >= data_offset + data_len)
-               return 1;
+               data_offset = btrfs_file_extent_offset(eb, fi);
+               data_len = btrfs_file_extent_num_bytes(eb, fi);
+
+               if (extent_item_pos < data_offset ||
+                   extent_item_pos >= data_offset + data_len)
+                       return 1;
+               offset = extent_item_pos - data_offset;
+       }
 
        e = kmalloc(sizeof(*e), GFP_NOFS);
        if (!e)
@@ -53,7 +60,7 @@ static int check_extent_in_eb(struct btrfs_key *key, struct extent_buffer *eb,
 
        e->next = *eie;
        e->inum = key->objectid;
-       e->offset = key->offset + (extent_item_pos - data_offset);
+       e->offset = key->offset + offset;
        *eie = e;
 
        return 0;
@@ -255,13 +262,11 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
  * to a logical address
  */
 static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
-                                       int search_commit_root,
-                                       u64 time_seq,
-                                       struct __prelim_ref *ref,
-                                       struct ulist *parents,
-                                       const u64 *extent_item_pos)
+                                 struct btrfs_path *path, u64 time_seq,
+                                 struct __prelim_ref *ref,
+                                 struct ulist *parents,
+                                 const u64 *extent_item_pos)
 {
-       struct btrfs_path *path;
        struct btrfs_root *root;
        struct btrfs_key root_key;
        struct extent_buffer *eb;
@@ -269,11 +274,6 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
        int root_level;
        int level = ref->level;
 
-       path = btrfs_alloc_path();
-       if (!path)
-               return -ENOMEM;
-       path->search_commit_root = !!search_commit_root;
-
        root_key.objectid = ref->root_id;
        root_key.type = BTRFS_ROOT_ITEM_KEY;
        root_key.offset = (u64)-1;
@@ -314,7 +314,8 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
                                time_seq, ref->wanted_disk_byte,
                                extent_item_pos);
 out:
-       btrfs_free_path(path);
+       path->lowest_level = 0;
+       btrfs_release_path(path);
        return ret;
 }
 
@@ -322,7 +323,7 @@ out:
  * resolve all indirect backrefs from the list
  */
 static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
-                                  int search_commit_root, u64 time_seq,
+                                  struct btrfs_path *path, u64 time_seq,
                                   struct list_head *head,
                                   const u64 *extent_item_pos)
 {
@@ -349,9 +350,8 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
                        continue;
                if (ref->count == 0)
                        continue;
-               err = __resolve_indirect_ref(fs_info, search_commit_root,
-                                            time_seq, ref, parents,
-                                            extent_item_pos);
+               err = __resolve_indirect_ref(fs_info, path, time_seq, ref,
+                                            parents, extent_item_pos);
                if (err == -ENOMEM)
                        goto out;
                if (err)
@@ -604,6 +604,7 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info,
        int slot;
        struct extent_buffer *leaf;
        struct btrfs_key key;
+       struct btrfs_key found_key;
        unsigned long ptr;
        unsigned long end;
        struct btrfs_extent_item *ei;
@@ -621,17 +622,21 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info,
 
        ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
        flags = btrfs_extent_flags(leaf, ei);
+       btrfs_item_key_to_cpu(leaf, &found_key, slot);
 
        ptr = (unsigned long)(ei + 1);
        end = (unsigned long)ei + item_size;
 
-       if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
+       if (found_key.type == BTRFS_EXTENT_ITEM_KEY &&
+           flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
                struct btrfs_tree_block_info *info;
 
                info = (struct btrfs_tree_block_info *)ptr;
                *info_level = btrfs_tree_block_level(leaf, info);
                ptr += sizeof(struct btrfs_tree_block_info);
                BUG_ON(ptr > end);
+       } else if (found_key.type == BTRFS_METADATA_ITEM_KEY) {
+               *info_level = found_key.offset;
        } else {
                BUG_ON(!(flags & BTRFS_EXTENT_FLAG_DATA));
        }
@@ -795,7 +800,6 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
        struct btrfs_delayed_ref_head *head;
        int info_level = 0;
        int ret;
-       int search_commit_root = (trans == BTRFS_BACKREF_SEARCH_COMMIT_ROOT);
        struct list_head prefs_delayed;
        struct list_head prefs;
        struct __prelim_ref *ref;
@@ -804,13 +808,17 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
        INIT_LIST_HEAD(&prefs_delayed);
 
        key.objectid = bytenr;
-       key.type = BTRFS_EXTENT_ITEM_KEY;
        key.offset = (u64)-1;
+       if (btrfs_fs_incompat(fs_info, SKINNY_METADATA))
+               key.type = BTRFS_METADATA_ITEM_KEY;
+       else
+               key.type = BTRFS_EXTENT_ITEM_KEY;
 
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
-       path->search_commit_root = !!search_commit_root;
+       if (!trans)
+               path->search_commit_root = 1;
 
        /*
         * grab both a lock on the path and a lock on the delayed ref head.
@@ -825,7 +833,7 @@ again:
                goto out;
        BUG_ON(ret == 0);
 
-       if (trans != BTRFS_BACKREF_SEARCH_COMMIT_ROOT) {
+       if (trans) {
                /*
                 * look if there are updates for this ref queued and lock the
                 * head
@@ -869,7 +877,8 @@ again:
                slot = path->slots[0];
                btrfs_item_key_to_cpu(leaf, &key, slot);
                if (key.objectid == bytenr &&
-                   key.type == BTRFS_EXTENT_ITEM_KEY) {
+                   (key.type == BTRFS_EXTENT_ITEM_KEY ||
+                    key.type == BTRFS_METADATA_ITEM_KEY)) {
                        ret = __add_inline_refs(fs_info, path, bytenr,
                                                &info_level, &prefs);
                        if (ret)
@@ -890,8 +899,8 @@ again:
 
        __merge_refs(&prefs, 1);
 
-       ret = __resolve_indirect_refs(fs_info, search_commit_root, time_seq,
-                                     &prefs, extent_item_pos);
+       ret = __resolve_indirect_refs(fs_info, path, time_seq, &prefs,
+                                     extent_item_pos);
        if (ret)
                goto out;
 
@@ -1283,12 +1292,16 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
 {
        int ret;
        u64 flags;
+       u64 size = 0;
        u32 item_size;
        struct extent_buffer *eb;
        struct btrfs_extent_item *ei;
        struct btrfs_key key;
 
-       key.type = BTRFS_EXTENT_ITEM_KEY;
+       if (btrfs_fs_incompat(fs_info, SKINNY_METADATA))
+               key.type = BTRFS_METADATA_ITEM_KEY;
+       else
+               key.type = BTRFS_EXTENT_ITEM_KEY;
        key.objectid = logical;
        key.offset = (u64)-1;
 
@@ -1301,9 +1314,15 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
                return ret;
 
        btrfs_item_key_to_cpu(path->nodes[0], found_key, path->slots[0]);
-       if (found_key->type != BTRFS_EXTENT_ITEM_KEY ||
+       if (found_key->type == BTRFS_METADATA_ITEM_KEY)
+               size = fs_info->extent_root->leafsize;
+       else if (found_key->type == BTRFS_EXTENT_ITEM_KEY)
+               size = found_key->offset;
+
+       if ((found_key->type != BTRFS_EXTENT_ITEM_KEY &&
+            found_key->type != BTRFS_METADATA_ITEM_KEY) ||
            found_key->objectid > logical ||
-           found_key->objectid + found_key->offset <= logical) {
+           found_key->objectid + size <= logical) {
                pr_debug("logical %llu is not within any extent\n",
                         (unsigned long long)logical);
                return -ENOENT;
@@ -1459,7 +1478,7 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
                                iterate_extent_inodes_t *iterate, void *ctx)
 {
        int ret;
-       struct btrfs_trans_handle *trans;
+       struct btrfs_trans_handle *trans = NULL;
        struct ulist *refs = NULL;
        struct ulist *roots = NULL;
        struct ulist_node *ref_node = NULL;
@@ -1471,9 +1490,7 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
        pr_debug("resolving all inodes for extent %llu\n",
                        extent_item_objectid);
 
-       if (search_commit_root) {
-               trans = BTRFS_BACKREF_SEARCH_COMMIT_ROOT;
-       } else {
+       if (!search_commit_root) {
                trans = btrfs_join_transaction(fs_info->extent_root);
                if (IS_ERR(trans))
                        return PTR_ERR(trans);