btrfs: make check_compressed_csum() to be subpage compatible
authorQu Wenruo <wqu@suse.com>
Thu, 4 Feb 2021 07:03:24 +0000 (15:03 +0800)
committerDavid Sterba <dsterba@suse.com>
Mon, 22 Feb 2021 16:15:27 +0000 (17:15 +0100)
Currently check_compressed_csum() completely relies on sectorsize ==
PAGE_SIZE to do checksum verification for compressed extents.

To make it subpage compatible, this patch will:
- Do extra calculation for the csum range
  Since we have multiple sectors inside a page, we need to only hash
  the range we want, not the full page anymore.

- Do sector-by-sector hash inside the page

With this patch and previous conversion on
btrfs_submit_compressed_read(), now we can read subpage compressed
extents properly, and do proper csum verification.

Reviewed-by: Anand Jain <anand.jain@oracle.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/compression.c

index 5bad3a0..375dee6 100644 (file)
@@ -141,6 +141,7 @@ static int check_compressed_csum(struct btrfs_inode *inode, struct bio *bio,
        struct btrfs_fs_info *fs_info = inode->root->fs_info;
        SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);
        const u32 csum_size = fs_info->csum_size;
+       const u32 sectorsize = fs_info->sectorsize;
        struct page *page;
        unsigned long i;
        char *kaddr;
@@ -154,22 +155,34 @@ static int check_compressed_csum(struct btrfs_inode *inode, struct bio *bio,
        shash->tfm = fs_info->csum_shash;
 
        for (i = 0; i < cb->nr_pages; i++) {
+               u32 pg_offset;
+               u32 bytes_left = PAGE_SIZE;
                page = cb->compressed_pages[i];
 
-               kaddr = kmap_atomic(page);
-               crypto_shash_digest(shash, kaddr, PAGE_SIZE, csum);
-               kunmap_atomic(kaddr);
-
-               if (memcmp(&csum, cb_sum, csum_size)) {
-                       btrfs_print_data_csum_error(inode, disk_start,
-                                       csum, cb_sum, cb->mirror_num);
-                       if (btrfs_io_bio(bio)->device)
-                               btrfs_dev_stat_inc_and_print(
-                                       btrfs_io_bio(bio)->device,
-                                       BTRFS_DEV_STAT_CORRUPTION_ERRS);
-                       return -EIO;
+               /* Determine the remaining bytes inside the page first */
+               if (i == cb->nr_pages - 1)
+                       bytes_left = cb->compressed_len - i * PAGE_SIZE;
+
+               /* Hash through the page sector by sector */
+               for (pg_offset = 0; pg_offset < bytes_left;
+                    pg_offset += sectorsize) {
+                       kaddr = kmap_atomic(page);
+                       crypto_shash_digest(shash, kaddr + pg_offset,
+                                           sectorsize, csum);
+                       kunmap_atomic(kaddr);
+
+                       if (memcmp(&csum, cb_sum, csum_size) != 0) {
+                               btrfs_print_data_csum_error(inode, disk_start,
+                                               csum, cb_sum, cb->mirror_num);
+                               if (btrfs_io_bio(bio)->device)
+                                       btrfs_dev_stat_inc_and_print(
+                                               btrfs_io_bio(bio)->device,
+                                               BTRFS_DEV_STAT_CORRUPTION_ERRS);
+                               return -EIO;
+                       }
+                       cb_sum += csum_size;
+                       disk_start += sectorsize;
                }
-               cb_sum += csum_size;
        }
        return 0;
 }