Btrfs: fix a crash of clone with inline extents's split
authorLiu Bo <bo.li.liu@oracle.com>
Mon, 10 Mar 2014 10:56:07 +0000 (18:56 +0800)
committerChris Mason <clm@fb.com>
Sat, 22 Mar 2014 00:35:18 +0000 (17:35 -0700)
xfstests's btrfs/035 triggers a BUG_ON, which we use to detect the split
of inline extents in __btrfs_drop_extents().

For inline extents, we cannot duplicate another EXTENT_DATA item, because
it breaks the rule of inline extents, that is, 'start offset' needs to be 0.

We have set limitations for the source inode's compressed inline extents,
because it needs to decompress and recompress.  Now the destination inode's
inline extents also need similar limitations.

With this, xfstests btrfs/035 doesn't run into panic.

Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Chris Mason <clm@fb.com>
fs/btrfs/file.c
fs/btrfs/ioctl.c

index b2143b8c33c577528bb0133c923662818e74d382..036f506cabd8af2787734158940f14af8ef22b27 100644 (file)
@@ -804,7 +804,10 @@ next_slot:
                 */
                if (start > key.offset && end < extent_end) {
                        BUG_ON(del_nr > 0);
-                       BUG_ON(extent_type == BTRFS_FILE_EXTENT_INLINE);
+                       if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+                               ret = -EINVAL;
+                               break;
+                       }
 
                        memcpy(&new_key, &key, sizeof(new_key));
                        new_key.offset = start;
@@ -847,7 +850,10 @@ next_slot:
                 *      | -------- extent -------- |
                 */
                if (start <= key.offset && end < extent_end) {
-                       BUG_ON(extent_type == BTRFS_FILE_EXTENT_INLINE);
+                       if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+                               ret = -EINVAL;
+                               break;
+                       }
 
                        memcpy(&new_key, &key, sizeof(new_key));
                        new_key.offset = end;
@@ -870,7 +876,10 @@ next_slot:
                 */
                if (start > key.offset && end >= extent_end) {
                        BUG_ON(del_nr > 0);
-                       BUG_ON(extent_type == BTRFS_FILE_EXTENT_INLINE);
+                       if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+                               ret = -EINVAL;
+                               break;
+                       }
 
                        btrfs_set_file_extent_num_bytes(leaf, fi,
                                                        start - key.offset);
index 3ca313b138caf7d2b73c07fe9b3a4bd7ed95f4fd..6778fa3c6ed2f004094d24e00cc2cb725db72459 100644 (file)
@@ -3087,8 +3087,9 @@ process_slot:
                                                         new_key.offset + datal,
                                                         1);
                                if (ret) {
-                                       btrfs_abort_transaction(trans, root,
-                                                               ret);
+                                       if (ret != -EINVAL)
+                                               btrfs_abort_transaction(trans,
+                                                               root, ret);
                                        btrfs_end_transaction(trans, root);
                                        goto out;
                                }
@@ -3246,8 +3247,9 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
         *   decompress into destination's address_space (the file offset
         *   may change, so source mapping won't do), then recompress (or
         *   otherwise reinsert) a subrange.
-        * - allow ranges within the same file to be cloned (provided
-        *   they don't overlap)?
+        *
+        * - split destination inode's inline extents.  The inline extents can
+        *   be either compressed or non-compressed.
         */
 
        /* the destination must be opened for writing */