Skip to content

Commit

Permalink
Revert "btrfs: zstd: fix and simplify the inline extent decompression"
Browse files Browse the repository at this point in the history
This reverts commit 1e7f6de.

It causes my machine to not even boot, and Klara Modin reports that the
cause is that small zstd-compressed files return garbage when read.

Reported-by: Klara Modin <klarasmodin@gmail.com>
Link: https://lore.kernel.org/linux-btrfs/CABq1_vj4GpUeZpVG49OHCo-3sdbe2-2ROcu_xDvUG-6-5zPRXg@mail.gmail.com/
Reported-and-bisected-by: Linus Torvalds <torvalds@linux-foundation.org>
Acked-by: David Sterba <dsterba@suse.com>
Cc: Qu Wenruo <wqu@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
torvalds committed Jan 22, 2024
1 parent 5d9248e commit e01a83e
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 23 deletions.
2 changes: 1 addition & 1 deletion fs/btrfs/compression.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ int zstd_compress_pages(struct list_head *ws, struct address_space *mapping,
unsigned long *total_in, unsigned long *total_out);
int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb);
int zstd_decompress(struct list_head *ws, const u8 *data_in,
struct page *dest_page, unsigned long dest_pgoff, size_t srclen,
struct page *dest_page, unsigned long start_byte, size_t srclen,
size_t destlen);
void zstd_init_workspace_manager(void);
void zstd_cleanup_workspace_manager(void);
Expand Down
75 changes: 53 additions & 22 deletions fs/btrfs/zstd.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include "misc.h"
#include "compression.h"
#include "ctree.h"
#include "super.h"

#define ZSTD_BTRFS_MAX_WINDOWLOG 17
#define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG)
Expand Down Expand Up @@ -619,48 +618,80 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
}

int zstd_decompress(struct list_head *ws, const u8 *data_in,
struct page *dest_page, unsigned long dest_pgoff, size_t srclen,
struct page *dest_page, unsigned long start_byte, size_t srclen,
size_t destlen)
{
struct workspace *workspace = list_entry(ws, struct workspace, list);
struct btrfs_fs_info *fs_info = btrfs_sb(dest_page->mapping->host->i_sb);
const u32 sectorsize = fs_info->sectorsize;
zstd_dstream *stream;
int ret = 0;
unsigned long to_copy = 0;
size_t ret2;
unsigned long total_out = 0;
unsigned long pg_offset = 0;

stream = zstd_init_dstream(
ZSTD_BTRFS_MAX_INPUT, workspace->mem, workspace->size);
if (!stream) {
pr_warn("BTRFS: zstd_init_dstream failed\n");
ret = -EIO;
goto finish;
}

destlen = min_t(size_t, destlen, PAGE_SIZE);

workspace->in_buf.src = data_in;
workspace->in_buf.pos = 0;
workspace->in_buf.size = srclen;

workspace->out_buf.dst = workspace->buf;
workspace->out_buf.pos = 0;
workspace->out_buf.size = sectorsize;

/*
* Since both input and output buffers should not exceed one sector,
* one call should end the decompression.
*/
ret = zstd_decompress_stream(stream, &workspace->out_buf, &workspace->in_buf);
if (zstd_is_error(ret)) {
pr_warn_ratelimited("BTRFS: zstd_decompress_stream return %d\n",
zstd_get_error_code(ret));
goto finish;
workspace->out_buf.size = PAGE_SIZE;

ret2 = 1;
while (pg_offset < destlen
&& workspace->in_buf.pos < workspace->in_buf.size) {
unsigned long buf_start;
unsigned long buf_offset;
unsigned long bytes;

/* Check if the frame is over and we still need more input */
if (ret2 == 0) {
pr_debug("BTRFS: zstd_decompress_stream ended early\n");
ret = -EIO;
goto finish;
}
ret2 = zstd_decompress_stream(stream, &workspace->out_buf,
&workspace->in_buf);
if (zstd_is_error(ret2)) {
pr_debug("BTRFS: zstd_decompress_stream returned %d\n",
zstd_get_error_code(ret2));
ret = -EIO;
goto finish;
}

buf_start = total_out;
total_out += workspace->out_buf.pos;
workspace->out_buf.pos = 0;

if (total_out <= start_byte)
continue;

if (total_out > start_byte && buf_start < start_byte)
buf_offset = start_byte - buf_start;
else
buf_offset = 0;

bytes = min_t(unsigned long, destlen - pg_offset,
workspace->out_buf.size - buf_offset);

memcpy_to_page(dest_page, pg_offset,
workspace->out_buf.dst + buf_offset, bytes);

pg_offset += bytes;
}
to_copy = workspace->out_buf.pos;
memcpy_to_page(dest_page, dest_pgoff + to_copy, workspace->out_buf.dst, to_copy);
ret = 0;
finish:
/* Error or early end. */
if (unlikely(to_copy < destlen)) {
ret = -EIO;
memzero_page(dest_page, dest_pgoff + to_copy, destlen - to_copy);
if (pg_offset < destlen) {
memzero_page(dest_page, pg_offset, destlen - pg_offset);
}
return ret;
}
Expand Down

0 comments on commit e01a83e

Please sign in to comment.