btrfs: scrub: Don't use inode pages for device replace
authorQu Wenruo <>
Tue, 5 Jun 2018 04:36:56 +0000 (12:36 +0800)
committerDavid Sterba <>
Mon, 11 Jun 2018 13:59:14 +0000 (15:59 +0200)
Btrfs can create compressed extent without checksum (even though it
shouldn't), and if we then try to replace device containing such extent,
the result device will contain all the uncompressed data instead of the
compressed one.

Test case already submitted to fstests:

When handling compressed extent without checksum, device replace will
goe into copy_nocow_pages() function.

In that function, btrfs will get all inodes referring to this data
extents and then use find_or_create_page() to get pages direct from that

The problem here is, pages directly from inode are always uncompressed.
And for compressed data extent, they mismatch with on-disk data.
Thus this leads to corrupted compressed data extent written to replace

In this attempt, we could just remove the "optimization" branch, and let
unified scrub_pages() to handle it.

Although scrub_pages() won't bother reusing page cache, it will be a
little slower, but it does the correct csum checking and won't cause
such data corruption caused by "optimization".

Note about the fix: this is the minimal fix that can be backported to
older stable trees without conflicts. The whole callchain from
copy_nocow_pages() can be deleted, and will be in followup patches.

Fixes: ff023aac3119 ("Btrfs: add code to scrub to copy read data to another disk")
CC: # 4.4+
Reported-by: James Harvey <>
Reviewed-by: James Harvey <>
Signed-off-by: Qu Wenruo <>
[ remove code removal, add note why ]
Signed-off-by: David Sterba <>

index a5900586201056f0c7429a35ad1b2255c917f40a..5723060364776d1fd3e3e1e09bdfc09d4bb7eadb 100644 (file)
@@ -2799,7 +2799,7 @@ static int scrub_extent(struct scrub_ctx *sctx, struct map_lookup *map,
                        have_csum = scrub_find_csum(sctx, logical, csum);
                        if (have_csum == 0)
-                       if (sctx->is_dev_replace && !have_csum) {
+                       if (0 && sctx->is_dev_replace && !have_csum) {
                                ret = copy_nocow_pages(sctx, logical, l,