1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * BTRFS filesystem implementation for U-Boot
4  *
5  * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
6  */
7 
8 #include "btrfs.h"
9 #include <linux/lzo.h>
10 #include <u-boot/zlib.h>
11 #include <asm/unaligned.h>
12 
decompress_lzo(const u8 * cbuf,u32 clen,u8 * dbuf,u32 dlen)13 static u32 decompress_lzo(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen)
14 {
15 	u32 tot_len, in_len, res;
16 	size_t out_len;
17 	int ret;
18 
19 	if (clen < 4)
20 		return -1;
21 
22 	tot_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
23 	cbuf += 4;
24 	clen -= 4;
25 	tot_len -= 4;
26 
27 	if (tot_len == 0 && dlen)
28 		return -1;
29 	if (tot_len < 4)
30 		return -1;
31 
32 	res = 0;
33 
34 	while (tot_len > 4) {
35 		in_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
36 		cbuf += 4;
37 		clen -= 4;
38 
39 		if (in_len > clen || tot_len < 4 + in_len)
40 			return -1;
41 
42 		tot_len -= 4 + in_len;
43 
44 		out_len = dlen;
45 		ret = lzo1x_decompress_safe(cbuf, in_len, dbuf, &out_len);
46 		if (ret != LZO_E_OK)
47 			return -1;
48 
49 		cbuf += in_len;
50 		clen -= in_len;
51 		dbuf += out_len;
52 		dlen -= out_len;
53 
54 		res += out_len;
55 	}
56 
57 	return res;
58 }
59 
60 /* from zutil.h */
61 #define PRESET_DICT 0x20
62 
decompress_zlib(const u8 * _cbuf,u32 clen,u8 * dbuf,u32 dlen)63 static u32 decompress_zlib(const u8 *_cbuf, u32 clen, u8 *dbuf, u32 dlen)
64 {
65 	int wbits = MAX_WBITS, ret = -1;
66 	z_stream stream;
67 	u8 *cbuf;
68 	u32 res;
69 
70 	memset(&stream, 0, sizeof(stream));
71 
72 	cbuf = (u8 *) _cbuf;
73 
74 	stream.total_in = 0;
75 
76 	stream.next_out = dbuf;
77 	stream.avail_out = dlen;
78 	stream.total_out = 0;
79 
80 	/* skip adler32 check if deflate and no dictionary */
81 	if (clen > 2 && !(cbuf[1] & PRESET_DICT) &&
82 	    ((cbuf[0] & 0x0f) == Z_DEFLATED) &&
83 	    !(((cbuf[0] << 8) + cbuf[1]) % 31)) {
84 		wbits = -((cbuf[0] >> 4) + 8);
85 		cbuf += 2;
86 		clen -= 2;
87 	}
88 
89 	if (Z_OK != inflateInit2(&stream, wbits))
90 		return -1;
91 
92 	while (stream.total_in < clen) {
93 		stream.next_in = cbuf + stream.total_in;
94 		stream.avail_in = min((u32) (clen - stream.total_in),
95 				      (u32) btrfs_info.sb.sectorsize);
96 
97 		ret = inflate(&stream, Z_NO_FLUSH);
98 		if (ret != Z_OK)
99 			break;
100 	}
101 
102 	res = stream.total_out;
103 	inflateEnd(&stream);
104 
105 	if (ret != Z_STREAM_END)
106 		return -1;
107 
108 	return res;
109 }
110 
btrfs_decompress(u8 type,const char * c,u32 clen,char * d,u32 dlen)111 u32 btrfs_decompress(u8 type, const char *c, u32 clen, char *d, u32 dlen)
112 {
113 	u32 res;
114 	const u8 *cbuf;
115 	u8 *dbuf;
116 
117 	cbuf = (const u8 *) c;
118 	dbuf = (u8 *) d;
119 
120 	switch (type) {
121 	case BTRFS_COMPRESS_NONE:
122 		res = dlen < clen ? dlen : clen;
123 		memcpy(dbuf, cbuf, res);
124 		return res;
125 	case BTRFS_COMPRESS_ZLIB:
126 		return decompress_zlib(cbuf, clen, dbuf, dlen);
127 	case BTRFS_COMPRESS_LZO:
128 		return decompress_lzo(cbuf, clen, dbuf, dlen);
129 	default:
130 		printf("%s: Unsupported compression in extent: %i\n", __func__,
131 		       type);
132 		return -1;
133 	}
134 }
135