1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2020 Bootlin
4 *
5 * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
6 */
7
8#include <errno.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <stdlib.h>
12
13#if IS_ENABLED(CONFIG_LZO)
14#include <linux/lzo.h>
15#endif
16
17#if IS_ENABLED(CONFIG_ZLIB)
18#include <u-boot/zlib.h>
19#endif
20
21#if IS_ENABLED(CONFIG_LZ4)
22#include <u-boot/lz4.h>
23#endif
24
25#if IS_ENABLED(CONFIG_ZSTD)
26#include <linux/zstd.h>
27#endif
28
29#include "sqfs_decompressor.h"
30#include "sqfs_utils.h"
31
32int sqfs_decompressor_init(struct squashfs_ctxt *ctxt)
33{
34	u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression);
35
36	switch (comp_type) {
37#if IS_ENABLED(CONFIG_LZO)
38	case SQFS_COMP_LZO:
39		break;
40#endif
41#if IS_ENABLED(CONFIG_ZLIB)
42	case SQFS_COMP_ZLIB:
43		break;
44#endif
45#if IS_ENABLED(CONFIG_LZ4)
46	case SQFS_COMP_LZ4:
47		break;
48#endif
49#if IS_ENABLED(CONFIG_ZSTD)
50	case SQFS_COMP_ZSTD:
51		ctxt->zstd_workspace = malloc(zstd_dctx_workspace_bound());
52		if (!ctxt->zstd_workspace)
53			return -ENOMEM;
54		break;
55#endif
56	default:
57		printf("Error: unknown compression type.\n");
58		return -EINVAL;
59	}
60
61	return 0;
62}
63
64void sqfs_decompressor_cleanup(struct squashfs_ctxt *ctxt)
65{
66	u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression);
67
68	switch (comp_type) {
69#if IS_ENABLED(CONFIG_LZO)
70	case SQFS_COMP_LZO:
71		break;
72#endif
73#if IS_ENABLED(CONFIG_ZLIB)
74	case SQFS_COMP_ZLIB:
75		break;
76#endif
77#if IS_ENABLED(CONFIG_LZ4)
78	case SQFS_COMP_LZ4:
79		break;
80#endif
81#if IS_ENABLED(CONFIG_ZSTD)
82	case SQFS_COMP_ZSTD:
83		free(ctxt->zstd_workspace);
84		break;
85#endif
86	}
87}
88
89#if IS_ENABLED(CONFIG_ZLIB)
90static void zlib_decompression_status(int ret)
91{
92	switch (ret) {
93	case Z_BUF_ERROR:
94		printf("Error: 'dest' buffer is not large enough.\n");
95		break;
96	case Z_DATA_ERROR:
97		printf("Error: corrupted compressed data.\n");
98		break;
99	case Z_MEM_ERROR:
100		printf("Error: insufficient memory.\n");
101		break;
102	}
103}
104#endif
105
106#if IS_ENABLED(CONFIG_ZSTD)
107static int sqfs_zstd_decompress(struct squashfs_ctxt *ctxt, void *dest,
108				unsigned long dest_len, void *source, u32 src_len)
109{
110	ZSTD_DCtx *ctx;
111	size_t wsize;
112	int ret;
113
114	wsize = zstd_dctx_workspace_bound();
115
116	ctx = zstd_init_dctx(ctxt->zstd_workspace, wsize);
117	if (!ctx)
118		return -EINVAL;
119	ret = zstd_decompress_dctx(ctx, dest, dest_len, source, src_len);
120
121	return zstd_is_error(ret);
122}
123#endif /* CONFIG_ZSTD */
124
125int sqfs_decompress(struct squashfs_ctxt *ctxt, void *dest,
126		    unsigned long *dest_len, void *source, u32 src_len)
127{
128	u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression);
129	int ret = 0;
130
131	switch (comp_type) {
132#if IS_ENABLED(CONFIG_LZO)
133	case SQFS_COMP_LZO: {
134		size_t lzo_dest_len = *dest_len;
135		ret = lzo1x_decompress_safe(source, src_len, dest, &lzo_dest_len);
136		if (ret) {
137			printf("LZO decompression failed. Error code: %d\n", ret);
138			return -EINVAL;
139		}
140
141		break;
142	}
143#endif
144#if IS_ENABLED(CONFIG_ZLIB)
145	case SQFS_COMP_ZLIB:
146		ret = uncompress(dest, dest_len, source, src_len);
147		if (ret) {
148			zlib_decompression_status(ret);
149			return -EINVAL;
150		}
151
152		break;
153#endif
154#if IS_ENABLED(CONFIG_LZ4)
155	case SQFS_COMP_LZ4:
156		ret = LZ4_decompress_safe(source, dest, src_len, *dest_len);
157		if (ret < 0) {
158			printf("LZ4 decompression failed.\n");
159			return -EINVAL;
160		}
161
162		ret = 0;
163		break;
164#endif
165#if IS_ENABLED(CONFIG_ZSTD)
166	case SQFS_COMP_ZSTD:
167		ret = sqfs_zstd_decompress(ctxt, dest, *dest_len, source, src_len);
168		if (ret) {
169			printf("ZSTD Error code: %d\n", zstd_get_error_code(ret));
170			return -EINVAL;
171		}
172
173		break;
174#endif
175	default:
176		printf("Error: unknown compression type.\n");
177		return -EINVAL;
178	}
179
180	return ret;
181}
182