diff options
| author | Chris Mason <clm@fb.com> | 2014-11-30 08:56:33 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-11-30 12:33:51 -0500 |
| commit | 2f19cad94cee3c9bd52d0c9ca584ef506302fb7c (patch) | |
| tree | ca1a69874842c86904ff6b66cb7430c818f2baa4 | |
| parent | 7a5a4f978750756755dc839014e13d1b088ccc8e (diff) | |
btrfs: zero out left over bytes after processing compression streams
Don Bailey noticed that our page zeroing for compression at end-io time
isn't complete. This reworks a patch from Linus to push the zeroing
into the zlib and lzo specific functions instead of trying to handle the
corners inside btrfs_decompress_buf2page
Signed-off-by: Chris Mason <clm@fb.com>
Reviewed-by: Josef Bacik <jbacik@fb.com>
Reported-by: Don A. Bailey <donb@securitymouse.com>
cc: stable@vger.kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | fs/btrfs/compression.c | 33 | ||||
| -rw-r--r-- | fs/btrfs/compression.h | 4 | ||||
| -rw-r--r-- | fs/btrfs/lzo.c | 15 | ||||
| -rw-r--r-- | fs/btrfs/zlib.c | 20 |
4 files changed, 67 insertions, 5 deletions
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index d3220d31d3cb..dcd9be32ac57 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c | |||
| @@ -1011,8 +1011,6 @@ int btrfs_decompress_buf2page(char *buf, unsigned long buf_start, | |||
| 1011 | bytes = min(bytes, working_bytes); | 1011 | bytes = min(bytes, working_bytes); |
| 1012 | kaddr = kmap_atomic(page_out); | 1012 | kaddr = kmap_atomic(page_out); |
| 1013 | memcpy(kaddr + *pg_offset, buf + buf_offset, bytes); | 1013 | memcpy(kaddr + *pg_offset, buf + buf_offset, bytes); |
| 1014 | if (*pg_index == (vcnt - 1) && *pg_offset == 0) | ||
| 1015 | memset(kaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); | ||
| 1016 | kunmap_atomic(kaddr); | 1014 | kunmap_atomic(kaddr); |
| 1017 | flush_dcache_page(page_out); | 1015 | flush_dcache_page(page_out); |
| 1018 | 1016 | ||
| @@ -1054,3 +1052,34 @@ int btrfs_decompress_buf2page(char *buf, unsigned long buf_start, | |||
| 1054 | 1052 | ||
| 1055 | return 1; | 1053 | return 1; |
| 1056 | } | 1054 | } |
| 1055 | |||
| 1056 | /* | ||
| 1057 | * When uncompressing data, we need to make sure and zero any parts of | ||
| 1058 | * the biovec that were not filled in by the decompression code. pg_index | ||
| 1059 | * and pg_offset indicate the last page and the last offset of that page | ||
| 1060 | * that have been filled in. This will zero everything remaining in the | ||
| 1061 | * biovec. | ||
| 1062 | */ | ||
| 1063 | void btrfs_clear_biovec_end(struct bio_vec *bvec, int vcnt, | ||
| 1064 | unsigned long pg_index, | ||
| 1065 | unsigned long pg_offset) | ||
| 1066 | { | ||
| 1067 | while (pg_index < vcnt) { | ||
| 1068 | struct page *page = bvec[pg_index].bv_page; | ||
| 1069 | unsigned long off = bvec[pg_index].bv_offset; | ||
| 1070 | unsigned long len = bvec[pg_index].bv_len; | ||
| 1071 | |||
| 1072 | if (pg_offset < off) | ||
| 1073 | pg_offset = off; | ||
| 1074 | if (pg_offset < off + len) { | ||
| 1075 | unsigned long bytes = off + len - pg_offset; | ||
| 1076 | char *kaddr; | ||
| 1077 | |||
| 1078 | kaddr = kmap_atomic(page); | ||
| 1079 | memset(kaddr + pg_offset, 0, bytes); | ||
| 1080 | kunmap_atomic(kaddr); | ||
| 1081 | } | ||
| 1082 | pg_index++; | ||
| 1083 | pg_offset = 0; | ||
| 1084 | } | ||
| 1085 | } | ||
diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h index 0c803b4fbf93..d181f70caae0 100644 --- a/fs/btrfs/compression.h +++ b/fs/btrfs/compression.h | |||
| @@ -45,7 +45,9 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start, | |||
| 45 | unsigned long nr_pages); | 45 | unsigned long nr_pages); |
| 46 | int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, | 46 | int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, |
| 47 | int mirror_num, unsigned long bio_flags); | 47 | int mirror_num, unsigned long bio_flags); |
| 48 | 48 | void btrfs_clear_biovec_end(struct bio_vec *bvec, int vcnt, | |
| 49 | unsigned long pg_index, | ||
| 50 | unsigned long pg_offset); | ||
| 49 | struct btrfs_compress_op { | 51 | struct btrfs_compress_op { |
| 50 | struct list_head *(*alloc_workspace)(void); | 52 | struct list_head *(*alloc_workspace)(void); |
| 51 | 53 | ||
diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 78285f30909e..617553cdb7d3 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c | |||
| @@ -373,6 +373,8 @@ cont: | |||
| 373 | } | 373 | } |
| 374 | done: | 374 | done: |
| 375 | kunmap(pages_in[page_in_index]); | 375 | kunmap(pages_in[page_in_index]); |
| 376 | if (!ret) | ||
| 377 | btrfs_clear_biovec_end(bvec, vcnt, page_out_index, pg_offset); | ||
| 376 | return ret; | 378 | return ret; |
| 377 | } | 379 | } |
| 378 | 380 | ||
| @@ -410,10 +412,23 @@ static int lzo_decompress(struct list_head *ws, unsigned char *data_in, | |||
| 410 | goto out; | 412 | goto out; |
| 411 | } | 413 | } |
| 412 | 414 | ||
| 415 | /* | ||
| 416 | * the caller is already checking against PAGE_SIZE, but lets | ||
| 417 | * move this check closer to the memcpy/memset | ||
| 418 | */ | ||
| 419 | destlen = min_t(unsigned long, destlen, PAGE_SIZE); | ||
| 413 | bytes = min_t(unsigned long, destlen, out_len - start_byte); | 420 | bytes = min_t(unsigned long, destlen, out_len - start_byte); |
| 414 | 421 | ||
| 415 | kaddr = kmap_atomic(dest_page); | 422 | kaddr = kmap_atomic(dest_page); |
| 416 | memcpy(kaddr, workspace->buf + start_byte, bytes); | 423 | memcpy(kaddr, workspace->buf + start_byte, bytes); |
| 424 | |||
| 425 | /* | ||
| 426 | * btrfs_getblock is doing a zero on the tail of the page too, | ||
| 427 | * but this will cover anything missing from the decompressed | ||
| 428 | * data. | ||
| 429 | */ | ||
| 430 | if (bytes < destlen) | ||
| 431 | memset(kaddr+bytes, 0, destlen-bytes); | ||
| 417 | kunmap_atomic(kaddr); | 432 | kunmap_atomic(kaddr); |
| 418 | out: | 433 | out: |
| 419 | return ret; | 434 | return ret; |
diff --git a/fs/btrfs/zlib.c b/fs/btrfs/zlib.c index 759fa4e2de8f..fb22fd8d8fb8 100644 --- a/fs/btrfs/zlib.c +++ b/fs/btrfs/zlib.c | |||
| @@ -299,6 +299,8 @@ done: | |||
| 299 | zlib_inflateEnd(&workspace->strm); | 299 | zlib_inflateEnd(&workspace->strm); |
| 300 | if (data_in) | 300 | if (data_in) |
| 301 | kunmap(pages_in[page_in_index]); | 301 | kunmap(pages_in[page_in_index]); |
| 302 | if (!ret) | ||
| 303 | btrfs_clear_biovec_end(bvec, vcnt, page_out_index, pg_offset); | ||
| 302 | return ret; | 304 | return ret; |
| 303 | } | 305 | } |
| 304 | 306 | ||
| @@ -310,10 +312,14 @@ static int zlib_decompress(struct list_head *ws, unsigned char *data_in, | |||
| 310 | struct workspace *workspace = list_entry(ws, struct workspace, list); | 312 | struct workspace *workspace = list_entry(ws, struct workspace, list); |
| 311 | int ret = 0; | 313 | int ret = 0; |
| 312 | int wbits = MAX_WBITS; | 314 | int wbits = MAX_WBITS; |
| 313 | unsigned long bytes_left = destlen; | 315 | unsigned long bytes_left; |
| 314 | unsigned long total_out = 0; | 316 | unsigned long total_out = 0; |
| 317 | unsigned long pg_offset = 0; | ||
| 315 | char *kaddr; | 318 | char *kaddr; |
| 316 | 319 | ||
| 320 | destlen = min_t(unsigned long, destlen, PAGE_SIZE); | ||
| 321 | bytes_left = destlen; | ||
| 322 | |||
| 317 | workspace->strm.next_in = data_in; | 323 | workspace->strm.next_in = data_in; |
| 318 | workspace->strm.avail_in = srclen; | 324 | workspace->strm.avail_in = srclen; |
| 319 | workspace->strm.total_in = 0; | 325 | workspace->strm.total_in = 0; |
| @@ -341,7 +347,6 @@ static int zlib_decompress(struct list_head *ws, unsigned char *data_in, | |||
| 341 | unsigned long buf_start; | 347 | unsigned long buf_start; |
| 342 | unsigned long buf_offset; | 348 | unsigned long buf_offset; |
| 343 | unsigned long bytes; | 349 | unsigned long bytes; |
| 344 | unsigned long pg_offset = 0; | ||
| 345 | 350 | ||
| 346 | ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH); | 351 | ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH); |
| 347 | if (ret != Z_OK && ret != Z_STREAM_END) | 352 | if (ret != Z_OK && ret != Z_STREAM_END) |
| @@ -384,6 +389,17 @@ next: | |||
| 384 | ret = 0; | 389 | ret = 0; |
| 385 | 390 | ||
| 386 | zlib_inflateEnd(&workspace->strm); | 391 | zlib_inflateEnd(&workspace->strm); |
| 392 | |||
| 393 | /* | ||
| 394 | * this should only happen if zlib returned fewer bytes than we | ||
| 395 | * expected. btrfs_get_block is responsible for zeroing from the | ||
| 396 | * end of the inline extent (destlen) to the end of the page | ||
| 397 | */ | ||
| 398 | if (pg_offset < destlen) { | ||
| 399 | kaddr = kmap_atomic(dest_page); | ||
| 400 | memset(kaddr + pg_offset, 0, destlen - pg_offset); | ||
| 401 | kunmap_atomic(kaddr); | ||
| 402 | } | ||
| 387 | return ret; | 403 | return ret; |
| 388 | } | 404 | } |
| 389 | 405 | ||
