compression.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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. #include "btrfs.h"
  8. #include <malloc.h>
  9. #include <linux/lzo.h>
  10. #include <linux/zstd.h>
  11. #include <linux/compat.h>
  12. #include <u-boot/zlib.h>
  13. #include <asm/unaligned.h>
  14. /* Header for each segment, LE32, recording the compressed size */
  15. #define LZO_LEN 4
  16. static u32 decompress_lzo(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen)
  17. {
  18. u32 tot_len, tot_in, in_len, res;
  19. size_t out_len;
  20. int ret;
  21. if (clen < LZO_LEN)
  22. return -1;
  23. tot_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
  24. tot_in = 0;
  25. cbuf += LZO_LEN;
  26. clen -= LZO_LEN;
  27. tot_len -= LZO_LEN;
  28. tot_in += LZO_LEN;
  29. if (tot_len == 0 && dlen)
  30. return -1;
  31. if (tot_len < LZO_LEN)
  32. return -1;
  33. res = 0;
  34. while (tot_len > LZO_LEN) {
  35. u32 rem_page;
  36. in_len = le32_to_cpu(get_unaligned((u32 *)cbuf));
  37. cbuf += LZO_LEN;
  38. clen -= LZO_LEN;
  39. if (in_len > clen || tot_len < LZO_LEN + in_len)
  40. return -1;
  41. tot_len -= (LZO_LEN + in_len);
  42. tot_in += (LZO_LEN + in_len);
  43. out_len = dlen;
  44. ret = lzo1x_decompress_safe(cbuf, in_len, dbuf, &out_len);
  45. if (ret != LZO_E_OK)
  46. return -1;
  47. cbuf += in_len;
  48. clen -= in_len;
  49. dbuf += out_len;
  50. dlen -= out_len;
  51. res += out_len;
  52. /*
  53. * If the 4 bytes header does not fit to the rest of the page we
  54. * have to move to next one, or we read some garbage.
  55. */
  56. rem_page = PAGE_SIZE - (tot_in % PAGE_SIZE);
  57. if (rem_page < LZO_LEN) {
  58. cbuf += rem_page;
  59. tot_in += rem_page;
  60. clen -= rem_page;
  61. tot_len -= rem_page;
  62. }
  63. }
  64. return res;
  65. }
  66. /* from zutil.h */
  67. #define PRESET_DICT 0x20
  68. static u32 decompress_zlib(const u8 *_cbuf, u32 clen, u8 *dbuf, u32 dlen)
  69. {
  70. int wbits = MAX_WBITS, ret = -1;
  71. z_stream stream;
  72. u8 *cbuf;
  73. u32 res;
  74. memset(&stream, 0, sizeof(stream));
  75. cbuf = (u8 *) _cbuf;
  76. stream.total_in = 0;
  77. stream.next_out = dbuf;
  78. stream.avail_out = dlen;
  79. stream.total_out = 0;
  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. if (Z_OK != inflateInit2(&stream, wbits))
  89. return -1;
  90. while (stream.total_in < clen) {
  91. stream.next_in = cbuf + stream.total_in;
  92. stream.avail_in = min((u32) (clen - stream.total_in),
  93. (u32) btrfs_info.sb.sectorsize);
  94. ret = inflate(&stream, Z_NO_FLUSH);
  95. if (ret != Z_OK)
  96. break;
  97. }
  98. res = stream.total_out;
  99. inflateEnd(&stream);
  100. if (ret != Z_STREAM_END)
  101. return -1;
  102. return res;
  103. }
  104. #define ZSTD_BTRFS_MAX_WINDOWLOG 17
  105. #define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG)
  106. static u32 decompress_zstd(const u8 *cbuf, u32 clen, u8 *dbuf, u32 dlen)
  107. {
  108. ZSTD_DStream *dstream;
  109. ZSTD_inBuffer in_buf;
  110. ZSTD_outBuffer out_buf;
  111. void *workspace;
  112. size_t wsize;
  113. u32 res = -1;
  114. wsize = ZSTD_DStreamWorkspaceBound(ZSTD_BTRFS_MAX_INPUT);
  115. workspace = malloc(wsize);
  116. if (!workspace) {
  117. debug("%s: cannot allocate workspace of size %zu\n", __func__,
  118. wsize);
  119. return -1;
  120. }
  121. dstream = ZSTD_initDStream(ZSTD_BTRFS_MAX_INPUT, workspace, wsize);
  122. if (!dstream) {
  123. printf("%s: ZSTD_initDStream failed\n", __func__);
  124. goto err_free;
  125. }
  126. in_buf.src = cbuf;
  127. in_buf.pos = 0;
  128. in_buf.size = clen;
  129. out_buf.dst = dbuf;
  130. out_buf.pos = 0;
  131. out_buf.size = dlen;
  132. while (1) {
  133. size_t ret;
  134. ret = ZSTD_decompressStream(dstream, &out_buf, &in_buf);
  135. if (ZSTD_isError(ret)) {
  136. printf("%s: ZSTD_decompressStream error %d\n", __func__,
  137. ZSTD_getErrorCode(ret));
  138. goto err_free;
  139. }
  140. if (in_buf.pos >= clen || !ret)
  141. break;
  142. }
  143. res = out_buf.pos;
  144. err_free:
  145. free(workspace);
  146. return res;
  147. }
  148. u32 btrfs_decompress(u8 type, const char *c, u32 clen, char *d, u32 dlen)
  149. {
  150. u32 res;
  151. const u8 *cbuf;
  152. u8 *dbuf;
  153. cbuf = (const u8 *) c;
  154. dbuf = (u8 *) d;
  155. switch (type) {
  156. case BTRFS_COMPRESS_NONE:
  157. res = dlen < clen ? dlen : clen;
  158. memcpy(dbuf, cbuf, res);
  159. return res;
  160. case BTRFS_COMPRESS_ZLIB:
  161. return decompress_zlib(cbuf, clen, dbuf, dlen);
  162. case BTRFS_COMPRESS_LZO:
  163. return decompress_lzo(cbuf, clen, dbuf, dlen);
  164. case BTRFS_COMPRESS_ZSTD:
  165. return decompress_zstd(cbuf, clen, dbuf, dlen);
  166. default:
  167. printf("%s: Unsupported compression in extent: %i\n", __func__,
  168. type);
  169. return -1;
  170. }
  171. }