aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorZach Brown <zab@redhat.com>2014-05-09 17:15:10 -0400
committerChris Mason <clm@fb.com>2014-06-09 20:20:23 -0400
commit166ae5a418756dacd2e8883c6f562c2d0ce2db2d (patch)
tree542143963601383f96aa0d9aa374f54d01205428 /fs
parent774bcb35f0d9ccb9adfd3a391328cc25523659fc (diff)
btrfs: fix inline compressed read err corruption
uncompress_inline() is dropping the error from btrfs_decompress() after testing it and zeroing the page that was supposed to hold decompressed data. This can silently turn compressed inline data in to zeros if decompression fails due to corrupt compressed data or memory allocation failure. I verified this by manually forcing the error from btrfs_decompress() for a silly named copy of od: if (!strcmp(current->comm, "failod")) ret = -ENOMEM; # od -x /mnt/btrfs/dir/80 | head -1 0000000 3031 3038 310a 2d30 6f70 6e69 0a74 3031 # echo 3 > /proc/sys/vm/drop_caches # cp $(which od) /tmp/failod # /tmp/failod -x /mnt/btrfs/dir/80 | head -1 0000000 0000 0000 0000 0000 0000 0000 0000 0000 The fix is to pass the error to its caller. Which still has a BUG_ON(). So we fix that too. There seems to be no reason for the zeroing of the page on the error from btrfs_decompress() but not from the allocation error a few lines above. So the page zeroing is removed. Signed-off-by: Zach Brown <zab@redhat.com> Reviewed-by: David Sterba <dsterba@suse.cz> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/inode.c15
1 files changed, 5 insertions, 10 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 5f805bc944fa..24dfa27a4f76 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -6086,16 +6086,8 @@ static noinline int uncompress_inline(struct btrfs_path *path,
6086 max_size = min_t(unsigned long, PAGE_CACHE_SIZE, max_size); 6086 max_size = min_t(unsigned long, PAGE_CACHE_SIZE, max_size);
6087 ret = btrfs_decompress(compress_type, tmp, page, 6087 ret = btrfs_decompress(compress_type, tmp, page,
6088 extent_offset, inline_size, max_size); 6088 extent_offset, inline_size, max_size);
6089 if (ret) {
6090 char *kaddr = kmap_atomic(page);
6091 unsigned long copy_size = min_t(u64,
6092 PAGE_CACHE_SIZE - pg_offset,
6093 max_size - extent_offset);
6094 memset(kaddr + pg_offset, 0, copy_size);
6095 kunmap_atomic(kaddr);
6096 }
6097 kfree(tmp); 6089 kfree(tmp);
6098 return 0; 6090 return ret;
6099} 6091}
6100 6092
6101/* 6093/*
@@ -6296,7 +6288,10 @@ next:
6296 ret = uncompress_inline(path, inode, page, 6288 ret = uncompress_inline(path, inode, page,
6297 pg_offset, 6289 pg_offset,
6298 extent_offset, item); 6290 extent_offset, item);
6299 BUG_ON(ret); /* -ENOMEM */ 6291 if (ret) {
6292 err = ret;
6293 goto out;
6294 }
6300 } else { 6295 } else {
6301 map = kmap(page); 6296 map = kmap(page);
6302 read_extent_buffer(leaf, map + pg_offset, ptr, 6297 read_extent_buffer(leaf, map + pg_offset, ptr,