Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | // SPDX-License-Identifier: GPL-2.0+ /* * BTRFS filesystem implementation for U-Boot * * 2017 Marek BehĂșn, CZ.NIC, kabel@kernel.org */ #include "btrfs.h" #include <abuf.h> #include <log.h> #include <malloc.h> #include <linux/lzo.h> #include <linux/zstd.h> #include <linux/compat.h> #include <u-boot/zlib.h> #include <asm/unaligned.h> /* Header for each segment, LE32, recording the compressed size */ #define LZO_LEN 4 static u32 decompress_lzo(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen) { u32 tot_len, tot_in, in_len, res; size_t out_len; int ret; if (clen < LZO_LEN) return -1; tot_len = le32_to_cpu(get_unaligned((u32 *)cbuf)); tot_in = 0; cbuf += LZO_LEN; clen -= LZO_LEN; tot_len -= LZO_LEN; tot_in += LZO_LEN; if (tot_len == 0 && dlen) return -1; if (tot_len < LZO_LEN) return -1; res = 0; while (tot_len > LZO_LEN) { u32 rem_page; in_len = le32_to_cpu(get_unaligned((u32 *)cbuf)); cbuf += LZO_LEN; clen -= LZO_LEN; if (in_len > clen || tot_len < LZO_LEN + in_len) return -1; tot_len -= (LZO_LEN + in_len); tot_in += (LZO_LEN + in_len); 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; /* * 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; } } 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), current_fs_info->sectorsize); 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; } #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) { struct abuf in, out; abuf_init_set(&in, (u8 *)cbuf, clen); abuf_init_set(&out, dbuf, dlen); return zstd_decompress(&in, &out); } 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); case BTRFS_COMPRESS_ZSTD: return decompress_zstd(cbuf, clen, dbuf, dlen); default: printf("%s: Unsupported compression in extent: %i\n", __func__, type); return -1; } } |