Merge branch 'for-linus' of git://git.kernel.dk/linux-block
[cascardo/linux.git] / fs / btrfs / backref.c
index 802fabb..ecbc63d 100644 (file)
@@ -206,10 +206,33 @@ static int __add_prelim_ref(struct list_head *head, u64 root_id,
                return -ENOMEM;
 
        ref->root_id = root_id;
-       if (key)
+       if (key) {
                ref->key_for_search = *key;
-       else
+               /*
+                * We can often find data backrefs with an offset that is too
+                * large (>= LLONG_MAX, maximum allowed file offset) due to
+                * underflows when subtracting a file's offset with the data
+                * offset of its corresponding extent data item. This can
+                * happen for example in the clone ioctl.
+                * So if we detect such case we set the search key's offset to
+                * zero to make sure we will find the matching file extent item
+                * at add_all_parents(), otherwise we will miss it because the
+                * offset taken form the backref is much larger then the offset
+                * of the file extent item. This can make us scan a very large
+                * number of file extent items, but at least it will not make
+                * us miss any.
+                * This is an ugly workaround for a behaviour that should have
+                * never existed, but it does and a fix for the clone ioctl
+                * would touch a lot of places, cause backwards incompatibility
+                * and would not fix the problem for extents cloned with older
+                * kernels.
+                */
+               if (ref->key_for_search.type == BTRFS_EXTENT_DATA_KEY &&
+                   ref->key_for_search.offset >= LLONG_MAX)
+                       ref->key_for_search.offset = 0;
+       } else {
                memset(&ref->key_for_search, 0, sizeof(ref->key_for_search));
+       }
 
        ref->inode_list = NULL;
        ref->level = level;
@@ -632,7 +655,7 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
                        struct btrfs_delayed_tree_ref *ref;
 
                        ref = btrfs_delayed_node_to_tree_ref(node);
-                       ret = __add_prelim_ref(prefs, ref->root, NULL,
+                       ret = __add_prelim_ref(prefs, 0, NULL,
                                               ref->level + 1, ref->parent,
                                               node->bytenr,
                                               node->ref_mod * sgn, GFP_ATOMIC);
@@ -664,11 +687,7 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq,
                        struct btrfs_delayed_data_ref *ref;
 
                        ref = btrfs_delayed_node_to_data_ref(node);
-
-                       key.objectid = ref->objectid;
-                       key.type = BTRFS_EXTENT_DATA_KEY;
-                       key.offset = ref->offset;
-                       ret = __add_prelim_ref(prefs, ref->root, &key, 0,
+                       ret = __add_prelim_ref(prefs, 0, NULL, 0,
                                               ref->parent, node->bytenr,
                                               node->ref_mod * sgn, GFP_ATOMIC);
                        break;