diff options
author | Gui Hecheng <guihc.fnst@cn.fujitsu.com> | 2014-09-22 16:29:28 +0800 |
---|---|---|
committer | David Sterba <dsterba@suse.cz> | 2015-03-23 23:43:49 +0100 |
commit | f18f8b7afc6f4c714ece79bbd56064d012cbbea4 (patch) | |
tree | 6c5ac9826bc544dd5e15990b2d1d450ce0e6068f /cmds-restore.c | |
parent | 895b7729965d50e42d4ae71f45a5002627eaa8a3 (diff) | |
download | btrfs-progs-f18f8b7afc6f4c714ece79bbd56064d012cbbea4.tar.gz |
btrfs-progs: restore, fix page alignment issue for lzo compression
When runing restore under lzo compression, "bad compress length"
problems are encountered.
It is because there is a page alignment problem with the @decompress_lzo,
as follows:
|------| |----|-| |------|...|------|
page ^ page page
|
3 bytes left
When lzo pages are compressed in memory, we will ensure that the 4 bytes
length header will not cross a page boundary. There is a situation that
3 (or less) bytes are left at the end of a page, and then the 4 bytes
len is stored at the start of the next page. But the @decompress_lzo
doesn't go to the start of the next page and continue to read the next 4
bytes which crosses two pages, so a random value is fetched as a "bad
compress length".
So we check page alignment every time before we are going to fetch the
next @len and after the former piece of data is decompressed. If the
current page that we reach has less than 4 bytes left, then we should
fetch the next @len at the start of next page.
Signed-off-by: Gui Hecheng <guihc.fnst@cn.fujitsu.com>
[simplifed and moved into decompress_lzo]
Signed-off-by: David Sterba <dsterba@suse.cz>
Diffstat (limited to 'cmds-restore.c')
-rw-r--r-- | cmds-restore.c | 13 |
1 files changed, 13 insertions, 0 deletions
diff --git a/cmds-restore.c b/cmds-restore.c index 4a3f795..d2caa6a 100644 --- a/cmds-restore.c +++ b/cmds-restore.c @@ -111,6 +111,8 @@ static int decompress_lzo(unsigned char *inbuf, char *outbuf, u64 compress_len, tot_in = LZO_LEN; while (tot_in < tot_len) { + size_t mod_page; + size_t rem_page; in_len = read_compress_length(inbuf); if ((tot_in + LZO_LEN + in_len) > tot_len) { @@ -134,6 +136,17 @@ static int decompress_lzo(unsigned char *inbuf, char *outbuf, u64 compress_len, outbuf += new_len; inbuf += in_len; tot_in += in_len; + + /* + * If the 4 byte header does not fit to the rest of the page we + * have to move to the next one, unless we read some garbage + */ + mod_page = tot_in % PAGE_CACHE_SIZE; + rem_page = PAGE_CACHE_SIZE - mod_page; + if (rem_page < LZO_LEN) { + inbuf += rem_page; + tot_in += rem_page; + } } *decompress_len = out_len; |