2018-05-06 21:58:06 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2017-09-03 15:00:28 +00:00
|
|
|
/*
|
|
|
|
* BTRFS filesystem implementation for U-Boot
|
|
|
|
*
|
|
|
|
* 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "btrfs.h"
|
2021-09-25 13:03:10 +00:00
|
|
|
#include <abuf.h>
|
2020-05-10 17:40:05 +00:00
|
|
|
#include <log.h>
|
2019-04-29 20:40:45 +00:00
|
|
|
#include <malloc.h>
|
2017-09-03 15:00:28 +00:00
|
|
|
#include <linux/lzo.h>
|
2019-04-29 20:40:45 +00:00
|
|
|
#include <linux/zstd.h>
|
fs: btrfs: Fix LZO false decompression error caused by pending zero
For certain btrfs files with compressed file extent, uboot will fail to
load it:
btrfs_read_extent_reg: disk_bytenr=14229504 disk_len=73728 offset=0 nr_bytes=131
072
decompress_lzo: tot_len=70770
decompress_lzo: in_len=1389
decompress_lzo: in_len=2400
decompress_lzo: in_len=3002
decompress_lzo: in_len=1379
decompress_lzo: in_len=88539136
decompress_lzo: header error, in_len=88539136 clen=65534 tot_len=62580
NOTE: except the last line, all other lines are debug output.
Btrfs lzo compression uses its own format to record compressed size
(segment header, LE32).
However to make decompression easier, we never put such segment header
across page boundary.
In above case, the xxd dump of the lzo compressed data looks like this:
00001fe0: 4cdc 02fc 0bfd 02c0 dc02 0d13 0100 0001 L...............
00001ff0: 0000 0008 0300 0000 0000 0011 0000|0000 ................
00002000: 4705 0000 0001 cc02 0000 0000 0000 1e01 G...............
'|' is the "expected" segment header start position.
But in that page, there are only 2 bytes left, can't contain the 4 bytes
segment header.
So btrfs compression will skip that 2 bytes, put the segment header in
next page directly.
Uboot doesn't have such check, and read the header with 2 bytes offset,
result 0x05470000 (88539136), other than the expected result
0x00000547 (1351), resulting above error.
Follow the btrfs-progs restore implementation, by introducing tot_in to
record total processed bytes (including headers), and do proper page
boundary skip to fix it.
Please note that, current code base doesn't parse fs_info thus we can't
grab sector size easily, so it uses PAGE_SIZE, and relying on fs open
time check to exclude unsupported sector size.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Cc: Marek Behun <marek.behun@nic.cz>
Reviewed-by: Marek Behún <marek.behun@nic.cz>
2020-03-26 05:35:56 +00:00
|
|
|
#include <linux/compat.h>
|
2017-09-03 15:00:28 +00:00
|
|
|
#include <u-boot/zlib.h>
|
2018-01-20 07:17:57 +00:00
|
|
|
#include <asm/unaligned.h>
|
2017-09-03 15:00:28 +00:00
|
|
|
|
2020-03-26 05:35:54 +00:00
|
|
|
/* Header for each segment, LE32, recording the compressed size */
|
|
|
|
#define LZO_LEN 4
|
2017-09-03 15:00:28 +00:00
|
|
|
static u32 decompress_lzo(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen)
|
|
|
|
{
|
fs: btrfs: Fix LZO false decompression error caused by pending zero
For certain btrfs files with compressed file extent, uboot will fail to
load it:
btrfs_read_extent_reg: disk_bytenr=14229504 disk_len=73728 offset=0 nr_bytes=131
072
decompress_lzo: tot_len=70770
decompress_lzo: in_len=1389
decompress_lzo: in_len=2400
decompress_lzo: in_len=3002
decompress_lzo: in_len=1379
decompress_lzo: in_len=88539136
decompress_lzo: header error, in_len=88539136 clen=65534 tot_len=62580
NOTE: except the last line, all other lines are debug output.
Btrfs lzo compression uses its own format to record compressed size
(segment header, LE32).
However to make decompression easier, we never put such segment header
across page boundary.
In above case, the xxd dump of the lzo compressed data looks like this:
00001fe0: 4cdc 02fc 0bfd 02c0 dc02 0d13 0100 0001 L...............
00001ff0: 0000 0008 0300 0000 0000 0011 0000|0000 ................
00002000: 4705 0000 0001 cc02 0000 0000 0000 1e01 G...............
'|' is the "expected" segment header start position.
But in that page, there are only 2 bytes left, can't contain the 4 bytes
segment header.
So btrfs compression will skip that 2 bytes, put the segment header in
next page directly.
Uboot doesn't have such check, and read the header with 2 bytes offset,
result 0x05470000 (88539136), other than the expected result
0x00000547 (1351), resulting above error.
Follow the btrfs-progs restore implementation, by introducing tot_in to
record total processed bytes (including headers), and do proper page
boundary skip to fix it.
Please note that, current code base doesn't parse fs_info thus we can't
grab sector size easily, so it uses PAGE_SIZE, and relying on fs open
time check to exclude unsupported sector size.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Cc: Marek Behun <marek.behun@nic.cz>
Reviewed-by: Marek Behún <marek.behun@nic.cz>
2020-03-26 05:35:56 +00:00
|
|
|
u32 tot_len, tot_in, in_len, res;
|
2017-09-03 15:00:28 +00:00
|
|
|
size_t out_len;
|
|
|
|
int ret;
|
|
|
|
|
2020-03-26 05:35:54 +00:00
|
|
|
if (clen < LZO_LEN)
|
2017-09-03 15:00:28 +00:00
|
|
|
return -1;
|
|
|
|
|
2018-01-20 07:17:57 +00:00
|
|
|
tot_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
|
fs: btrfs: Fix LZO false decompression error caused by pending zero
For certain btrfs files with compressed file extent, uboot will fail to
load it:
btrfs_read_extent_reg: disk_bytenr=14229504 disk_len=73728 offset=0 nr_bytes=131
072
decompress_lzo: tot_len=70770
decompress_lzo: in_len=1389
decompress_lzo: in_len=2400
decompress_lzo: in_len=3002
decompress_lzo: in_len=1379
decompress_lzo: in_len=88539136
decompress_lzo: header error, in_len=88539136 clen=65534 tot_len=62580
NOTE: except the last line, all other lines are debug output.
Btrfs lzo compression uses its own format to record compressed size
(segment header, LE32).
However to make decompression easier, we never put such segment header
across page boundary.
In above case, the xxd dump of the lzo compressed data looks like this:
00001fe0: 4cdc 02fc 0bfd 02c0 dc02 0d13 0100 0001 L...............
00001ff0: 0000 0008 0300 0000 0000 0011 0000|0000 ................
00002000: 4705 0000 0001 cc02 0000 0000 0000 1e01 G...............
'|' is the "expected" segment header start position.
But in that page, there are only 2 bytes left, can't contain the 4 bytes
segment header.
So btrfs compression will skip that 2 bytes, put the segment header in
next page directly.
Uboot doesn't have such check, and read the header with 2 bytes offset,
result 0x05470000 (88539136), other than the expected result
0x00000547 (1351), resulting above error.
Follow the btrfs-progs restore implementation, by introducing tot_in to
record total processed bytes (including headers), and do proper page
boundary skip to fix it.
Please note that, current code base doesn't parse fs_info thus we can't
grab sector size easily, so it uses PAGE_SIZE, and relying on fs open
time check to exclude unsupported sector size.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Cc: Marek Behun <marek.behun@nic.cz>
Reviewed-by: Marek Behún <marek.behun@nic.cz>
2020-03-26 05:35:56 +00:00
|
|
|
tot_in = 0;
|
2020-03-26 05:35:54 +00:00
|
|
|
cbuf += LZO_LEN;
|
|
|
|
clen -= LZO_LEN;
|
|
|
|
tot_len -= LZO_LEN;
|
fs: btrfs: Fix LZO false decompression error caused by pending zero
For certain btrfs files with compressed file extent, uboot will fail to
load it:
btrfs_read_extent_reg: disk_bytenr=14229504 disk_len=73728 offset=0 nr_bytes=131
072
decompress_lzo: tot_len=70770
decompress_lzo: in_len=1389
decompress_lzo: in_len=2400
decompress_lzo: in_len=3002
decompress_lzo: in_len=1379
decompress_lzo: in_len=88539136
decompress_lzo: header error, in_len=88539136 clen=65534 tot_len=62580
NOTE: except the last line, all other lines are debug output.
Btrfs lzo compression uses its own format to record compressed size
(segment header, LE32).
However to make decompression easier, we never put such segment header
across page boundary.
In above case, the xxd dump of the lzo compressed data looks like this:
00001fe0: 4cdc 02fc 0bfd 02c0 dc02 0d13 0100 0001 L...............
00001ff0: 0000 0008 0300 0000 0000 0011 0000|0000 ................
00002000: 4705 0000 0001 cc02 0000 0000 0000 1e01 G...............
'|' is the "expected" segment header start position.
But in that page, there are only 2 bytes left, can't contain the 4 bytes
segment header.
So btrfs compression will skip that 2 bytes, put the segment header in
next page directly.
Uboot doesn't have such check, and read the header with 2 bytes offset,
result 0x05470000 (88539136), other than the expected result
0x00000547 (1351), resulting above error.
Follow the btrfs-progs restore implementation, by introducing tot_in to
record total processed bytes (including headers), and do proper page
boundary skip to fix it.
Please note that, current code base doesn't parse fs_info thus we can't
grab sector size easily, so it uses PAGE_SIZE, and relying on fs open
time check to exclude unsupported sector size.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Cc: Marek Behun <marek.behun@nic.cz>
Reviewed-by: Marek Behún <marek.behun@nic.cz>
2020-03-26 05:35:56 +00:00
|
|
|
tot_in += LZO_LEN;
|
2017-09-03 15:00:28 +00:00
|
|
|
|
|
|
|
if (tot_len == 0 && dlen)
|
|
|
|
return -1;
|
2020-03-26 05:35:54 +00:00
|
|
|
if (tot_len < LZO_LEN)
|
2017-09-03 15:00:28 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
res = 0;
|
|
|
|
|
2020-03-26 05:35:54 +00:00
|
|
|
while (tot_len > LZO_LEN) {
|
fs: btrfs: Fix LZO false decompression error caused by pending zero
For certain btrfs files with compressed file extent, uboot will fail to
load it:
btrfs_read_extent_reg: disk_bytenr=14229504 disk_len=73728 offset=0 nr_bytes=131
072
decompress_lzo: tot_len=70770
decompress_lzo: in_len=1389
decompress_lzo: in_len=2400
decompress_lzo: in_len=3002
decompress_lzo: in_len=1379
decompress_lzo: in_len=88539136
decompress_lzo: header error, in_len=88539136 clen=65534 tot_len=62580
NOTE: except the last line, all other lines are debug output.
Btrfs lzo compression uses its own format to record compressed size
(segment header, LE32).
However to make decompression easier, we never put such segment header
across page boundary.
In above case, the xxd dump of the lzo compressed data looks like this:
00001fe0: 4cdc 02fc 0bfd 02c0 dc02 0d13 0100 0001 L...............
00001ff0: 0000 0008 0300 0000 0000 0011 0000|0000 ................
00002000: 4705 0000 0001 cc02 0000 0000 0000 1e01 G...............
'|' is the "expected" segment header start position.
But in that page, there are only 2 bytes left, can't contain the 4 bytes
segment header.
So btrfs compression will skip that 2 bytes, put the segment header in
next page directly.
Uboot doesn't have such check, and read the header with 2 bytes offset,
result 0x05470000 (88539136), other than the expected result
0x00000547 (1351), resulting above error.
Follow the btrfs-progs restore implementation, by introducing tot_in to
record total processed bytes (including headers), and do proper page
boundary skip to fix it.
Please note that, current code base doesn't parse fs_info thus we can't
grab sector size easily, so it uses PAGE_SIZE, and relying on fs open
time check to exclude unsupported sector size.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Cc: Marek Behun <marek.behun@nic.cz>
Reviewed-by: Marek Behún <marek.behun@nic.cz>
2020-03-26 05:35:56 +00:00
|
|
|
u32 rem_page;
|
|
|
|
|
2018-01-20 07:17:57 +00:00
|
|
|
in_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
|
2020-03-26 05:35:54 +00:00
|
|
|
cbuf += LZO_LEN;
|
|
|
|
clen -= LZO_LEN;
|
2017-09-03 15:00:28 +00:00
|
|
|
|
2020-03-26 05:35:54 +00:00
|
|
|
if (in_len > clen || tot_len < LZO_LEN + in_len)
|
2017-09-03 15:00:28 +00:00
|
|
|
return -1;
|
|
|
|
|
2020-03-26 05:35:54 +00:00
|
|
|
tot_len -= (LZO_LEN + in_len);
|
fs: btrfs: Fix LZO false decompression error caused by pending zero
For certain btrfs files with compressed file extent, uboot will fail to
load it:
btrfs_read_extent_reg: disk_bytenr=14229504 disk_len=73728 offset=0 nr_bytes=131
072
decompress_lzo: tot_len=70770
decompress_lzo: in_len=1389
decompress_lzo: in_len=2400
decompress_lzo: in_len=3002
decompress_lzo: in_len=1379
decompress_lzo: in_len=88539136
decompress_lzo: header error, in_len=88539136 clen=65534 tot_len=62580
NOTE: except the last line, all other lines are debug output.
Btrfs lzo compression uses its own format to record compressed size
(segment header, LE32).
However to make decompression easier, we never put such segment header
across page boundary.
In above case, the xxd dump of the lzo compressed data looks like this:
00001fe0: 4cdc 02fc 0bfd 02c0 dc02 0d13 0100 0001 L...............
00001ff0: 0000 0008 0300 0000 0000 0011 0000|0000 ................
00002000: 4705 0000 0001 cc02 0000 0000 0000 1e01 G...............
'|' is the "expected" segment header start position.
But in that page, there are only 2 bytes left, can't contain the 4 bytes
segment header.
So btrfs compression will skip that 2 bytes, put the segment header in
next page directly.
Uboot doesn't have such check, and read the header with 2 bytes offset,
result 0x05470000 (88539136), other than the expected result
0x00000547 (1351), resulting above error.
Follow the btrfs-progs restore implementation, by introducing tot_in to
record total processed bytes (including headers), and do proper page
boundary skip to fix it.
Please note that, current code base doesn't parse fs_info thus we can't
grab sector size easily, so it uses PAGE_SIZE, and relying on fs open
time check to exclude unsupported sector size.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Cc: Marek Behun <marek.behun@nic.cz>
Reviewed-by: Marek Behún <marek.behun@nic.cz>
2020-03-26 05:35:56 +00:00
|
|
|
tot_in += (LZO_LEN + in_len);
|
2017-09-03 15:00:28 +00:00
|
|
|
|
|
|
|
out_len = dlen;
|
|
|
|
ret = lzo1x_decompress_safe(cbuf, in_len, dbuf, &out_len);
|
|
|
|
if (ret != LZO_E_OK)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
cbuf += in_len;
|
|
|
|
clen -= in_len;
|
|
|
|
dbuf += out_len;
|
|
|
|
dlen -= out_len;
|
|
|
|
|
|
|
|
res += out_len;
|
fs: btrfs: Fix LZO false decompression error caused by pending zero
For certain btrfs files with compressed file extent, uboot will fail to
load it:
btrfs_read_extent_reg: disk_bytenr=14229504 disk_len=73728 offset=0 nr_bytes=131
072
decompress_lzo: tot_len=70770
decompress_lzo: in_len=1389
decompress_lzo: in_len=2400
decompress_lzo: in_len=3002
decompress_lzo: in_len=1379
decompress_lzo: in_len=88539136
decompress_lzo: header error, in_len=88539136 clen=65534 tot_len=62580
NOTE: except the last line, all other lines are debug output.
Btrfs lzo compression uses its own format to record compressed size
(segment header, LE32).
However to make decompression easier, we never put such segment header
across page boundary.
In above case, the xxd dump of the lzo compressed data looks like this:
00001fe0: 4cdc 02fc 0bfd 02c0 dc02 0d13 0100 0001 L...............
00001ff0: 0000 0008 0300 0000 0000 0011 0000|0000 ................
00002000: 4705 0000 0001 cc02 0000 0000 0000 1e01 G...............
'|' is the "expected" segment header start position.
But in that page, there are only 2 bytes left, can't contain the 4 bytes
segment header.
So btrfs compression will skip that 2 bytes, put the segment header in
next page directly.
Uboot doesn't have such check, and read the header with 2 bytes offset,
result 0x05470000 (88539136), other than the expected result
0x00000547 (1351), resulting above error.
Follow the btrfs-progs restore implementation, by introducing tot_in to
record total processed bytes (including headers), and do proper page
boundary skip to fix it.
Please note that, current code base doesn't parse fs_info thus we can't
grab sector size easily, so it uses PAGE_SIZE, and relying on fs open
time check to exclude unsupported sector size.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Cc: Marek Behun <marek.behun@nic.cz>
Reviewed-by: Marek Behún <marek.behun@nic.cz>
2020-03-26 05:35:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the 4 bytes header does not fit to the rest of the page we
|
|
|
|
* have to move to next one, or we read some garbage.
|
|
|
|
*/
|
|
|
|
rem_page = PAGE_SIZE - (tot_in % PAGE_SIZE);
|
|
|
|
if (rem_page < LZO_LEN) {
|
|
|
|
cbuf += rem_page;
|
|
|
|
tot_in += rem_page;
|
|
|
|
clen -= rem_page;
|
|
|
|
tot_len -= rem_page;
|
|
|
|
}
|
2017-09-03 15:00:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* from zutil.h */
|
|
|
|
#define PRESET_DICT 0x20
|
|
|
|
|
|
|
|
static u32 decompress_zlib(const u8 *_cbuf, u32 clen, u8 *dbuf, u32 dlen)
|
|
|
|
{
|
|
|
|
int wbits = MAX_WBITS, ret = -1;
|
|
|
|
z_stream stream;
|
|
|
|
u8 *cbuf;
|
|
|
|
u32 res;
|
|
|
|
|
|
|
|
memset(&stream, 0, sizeof(stream));
|
|
|
|
|
|
|
|
cbuf = (u8 *) _cbuf;
|
|
|
|
|
|
|
|
stream.total_in = 0;
|
|
|
|
|
|
|
|
stream.next_out = dbuf;
|
|
|
|
stream.avail_out = dlen;
|
|
|
|
stream.total_out = 0;
|
|
|
|
|
|
|
|
/* skip adler32 check if deflate and no dictionary */
|
|
|
|
if (clen > 2 && !(cbuf[1] & PRESET_DICT) &&
|
|
|
|
((cbuf[0] & 0x0f) == Z_DEFLATED) &&
|
|
|
|
!(((cbuf[0] << 8) + cbuf[1]) % 31)) {
|
|
|
|
wbits = -((cbuf[0] >> 4) + 8);
|
|
|
|
cbuf += 2;
|
|
|
|
clen -= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Z_OK != inflateInit2(&stream, wbits))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while (stream.total_in < clen) {
|
|
|
|
stream.next_in = cbuf + stream.total_in;
|
|
|
|
stream.avail_in = min((u32) (clen - stream.total_in),
|
2020-06-24 16:03:15 +00:00
|
|
|
current_fs_info->sectorsize);
|
2017-09-03 15:00:28 +00:00
|
|
|
|
|
|
|
ret = inflate(&stream, Z_NO_FLUSH);
|
|
|
|
if (ret != Z_OK)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = stream.total_out;
|
|
|
|
inflateEnd(&stream);
|
|
|
|
|
|
|
|
if (ret != Z_STREAM_END)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2019-04-29 20:40:45 +00:00
|
|
|
#define ZSTD_BTRFS_MAX_WINDOWLOG 17
|
|
|
|
#define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG)
|
|
|
|
|
|
|
|
static u32 decompress_zstd(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen)
|
|
|
|
{
|
2021-09-25 13:03:10 +00:00
|
|
|
struct abuf in, out;
|
2019-04-29 20:40:45 +00:00
|
|
|
|
2021-09-25 13:03:10 +00:00
|
|
|
abuf_init_set(&in, (u8 *)cbuf, clen);
|
|
|
|
abuf_init_set(&out, dbuf, dlen);
|
2019-04-29 20:40:45 +00:00
|
|
|
|
2021-09-25 13:03:10 +00:00
|
|
|
return zstd_decompress(&in, &out);
|
2019-04-29 20:40:45 +00:00
|
|
|
}
|
|
|
|
|
2017-09-03 15:00:28 +00:00
|
|
|
u32 btrfs_decompress(u8 type, const char *c, u32 clen, char *d, u32 dlen)
|
|
|
|
{
|
|
|
|
u32 res;
|
|
|
|
const u8 *cbuf;
|
|
|
|
u8 *dbuf;
|
|
|
|
|
|
|
|
cbuf = (const u8 *) c;
|
|
|
|
dbuf = (u8 *) d;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case BTRFS_COMPRESS_NONE:
|
|
|
|
res = dlen < clen ? dlen : clen;
|
|
|
|
memcpy(dbuf, cbuf, res);
|
|
|
|
return res;
|
|
|
|
case BTRFS_COMPRESS_ZLIB:
|
|
|
|
return decompress_zlib(cbuf, clen, dbuf, dlen);
|
|
|
|
case BTRFS_COMPRESS_LZO:
|
|
|
|
return decompress_lzo(cbuf, clen, dbuf, dlen);
|
2019-04-29 20:40:45 +00:00
|
|
|
case BTRFS_COMPRESS_ZSTD:
|
|
|
|
return decompress_zstd(cbuf, clen, dbuf, dlen);
|
2017-09-03 15:00:28 +00:00
|
|
|
default:
|
|
|
|
printf("%s: Unsupported compression in extent: %i\n", __func__,
|
|
|
|
type);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|