Skip to content

Commit

Permalink
Btrfs: fix a crash of clone with inline extents's split
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Liu Bo authored and masoncl committed Mar 22, 2014
1 parent 73b802f commit 00fdf13
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 7 deletions.
15 changes: 12 additions & 3 deletions fs/btrfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,10 @@ int __btrfs_drop_extents(struct btrfs_trans_handle *trans,
*/
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;
Expand Down Expand Up @@ -847,7 +850,10 @@ int __btrfs_drop_extents(struct btrfs_trans_handle *trans,
* | -------- 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;
Expand All @@ -870,7 +876,10 @@ int __btrfs_drop_extents(struct btrfs_trans_handle *trans,
*/
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);
Expand Down
10 changes: 6 additions & 4 deletions fs/btrfs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -3087,8 +3087,9 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
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;
}
Expand Down Expand Up @@ -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 */
Expand Down

0 comments on commit 00fdf13

Please sign in to comment.