| From: Liu Bo <bo.li.liu@oracle.com> |
| Date: Wed, 31 Jan 2018 17:09:13 -0700 |
| Subject: Btrfs: fix unexpected cow in run_delalloc_nocow |
| |
| commit 5811375325420052fcadd944792a416a43072b7f upstream. |
| |
| Fstests generic/475 provides a way to fail metadata reads while |
| checking if checksum exists for the inode inside run_delalloc_nocow(), |
| and csum_exist_in_range() interprets error (-EIO) as inode having |
| checksum and makes its caller enter the cow path. |
| |
| In case of free space inode, this ends up with a warning in |
| cow_file_range(). |
| |
| The same problem applies to btrfs_cross_ref_exist() since it may also |
| read metadata in between. |
| |
| With this, run_delalloc_nocow() bails out when errors occur at the two |
| places. |
| |
| Fixes: 17d217fe970d ("Btrfs: fix nodatasum handling in balancing code") |
| Signed-off-by: Liu Bo <bo.li.liu@oracle.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| [bwh: Backported to 3.16: adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| --- a/fs/btrfs/inode.c |
| +++ b/fs/btrfs/inode.c |
| @@ -1145,6 +1145,8 @@ static noinline int csum_exist_in_range( |
| list_del(&sums->list); |
| kfree(sums); |
| } |
| + if (ret < 0) |
| + return ret; |
| return 1; |
| } |
| |
| @@ -1294,10 +1296,23 @@ next_slot: |
| goto out_check; |
| if (btrfs_extent_readonly(root, disk_bytenr)) |
| goto out_check; |
| - if (btrfs_cross_ref_exist(trans, root, ino, |
| - found_key.offset - |
| - extent_offset, disk_bytenr)) |
| + ret = btrfs_cross_ref_exist(trans, root, ino, |
| + found_key.offset - |
| + extent_offset, disk_bytenr); |
| + if (ret) { |
| + /* |
| + * ret could be -EIO if the above fails to read |
| + * metadata. |
| + */ |
| + if (ret < 0) { |
| + if (cow_start != (u64)-1) |
| + cur_offset = cow_start; |
| + goto error; |
| + } |
| + |
| + WARN_ON_ONCE(nolock); |
| goto out_check; |
| + } |
| disk_bytenr += extent_offset; |
| disk_bytenr += cur_offset - found_key.offset; |
| num_bytes = min(end + 1, extent_end) - cur_offset; |
| @@ -1315,8 +1330,22 @@ next_slot: |
| * this ensure that csum for a given extent are |
| * either valid or do not exist. |
| */ |
| - if (csum_exist_in_range(root, disk_bytenr, num_bytes)) |
| + ret = csum_exist_in_range(root, disk_bytenr, |
| + num_bytes); |
| + if (ret) { |
| + |
| + /* |
| + * ret could be -EIO if the above fails to read |
| + * metadata. |
| + */ |
| + if (ret < 0) { |
| + if (cow_start != (u64)-1) |
| + cur_offset = cow_start; |
| + goto error; |
| + } |
| + WARN_ON_ONCE(nolock); |
| goto out_check; |
| + } |
| nocow = 1; |
| } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) { |
| extent_end = found_key.offset + |