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_ZSTD)
22 #include <linux/zstd.h>
23 #endif
24 
25 #include "sqfs_decompressor.h"
26 #include "sqfs_utils.h"
27 
sqfs_decompressor_init(struct squashfs_ctxt * ctxt)28 int sqfs_decompressor_init(struct squashfs_ctxt *ctxt)
29 {
30 	u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression);
31 
32 	switch (comp_type) {
33 #if IS_ENABLED(CONFIG_LZO)
34 	case SQFS_COMP_LZO:
35 		break;
36 #endif
37 #if IS_ENABLED(CONFIG_ZLIB)
38 	case SQFS_COMP_ZLIB:
39 		break;
40 #endif
41 #if IS_ENABLED(CONFIG_ZSTD)
42 	case SQFS_COMP_ZSTD:
43 		ctxt->zstd_workspace = malloc(ZSTD_DCtxWorkspaceBound());
44 		if (!ctxt->zstd_workspace)
45 			return -ENOMEM;
46 		break;
47 #endif
48 	default:
49 		printf("Error: unknown compression type.\n");
50 		return -EINVAL;
51 	}
52 
53 	return 0;
54 }
55 
sqfs_decompressor_cleanup(struct squashfs_ctxt * ctxt)56 void sqfs_decompressor_cleanup(struct squashfs_ctxt *ctxt)
57 {
58 	u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression);
59 
60 	switch (comp_type) {
61 #if IS_ENABLED(CONFIG_LZO)
62 	case SQFS_COMP_LZO:
63 		break;
64 #endif
65 #if IS_ENABLED(CONFIG_ZLIB)
66 	case SQFS_COMP_ZLIB:
67 		break;
68 #endif
69 #if IS_ENABLED(CONFIG_ZSTD)
70 	case SQFS_COMP_ZSTD:
71 		free(ctxt->zstd_workspace);
72 		break;
73 #endif
74 	}
75 }
76 
77 #if IS_ENABLED(CONFIG_ZLIB)
zlib_decompression_status(int ret)78 static void zlib_decompression_status(int ret)
79 {
80 	switch (ret) {
81 	case Z_BUF_ERROR:
82 		printf("Error: 'dest' buffer is not large enough.\n");
83 		break;
84 	case Z_DATA_ERROR:
85 		printf("Error: corrupted compressed data.\n");
86 		break;
87 	case Z_MEM_ERROR:
88 		printf("Error: insufficient memory.\n");
89 		break;
90 	}
91 }
92 #endif
93 
94 #if IS_ENABLED(CONFIG_ZSTD)
sqfs_zstd_decompress(struct squashfs_ctxt * ctxt,void * dest,unsigned long dest_len,void * source,u32 src_len)95 static int sqfs_zstd_decompress(struct squashfs_ctxt *ctxt, void *dest,
96 				unsigned long dest_len, void *source, u32 src_len)
97 {
98 	ZSTD_DCtx *ctx;
99 	size_t wsize;
100 	int ret;
101 
102 	wsize = ZSTD_DCtxWorkspaceBound();
103 	ctx = ZSTD_initDCtx(ctxt->zstd_workspace, wsize);
104 	ret = ZSTD_decompressDCtx(ctx, dest, dest_len, source, src_len);
105 
106 	return ZSTD_isError(ret);
107 }
108 #endif /* CONFIG_ZSTD */
109 
sqfs_decompress(struct squashfs_ctxt * ctxt,void * dest,unsigned long * dest_len,void * source,u32 src_len)110 int sqfs_decompress(struct squashfs_ctxt *ctxt, void *dest,
111 		    unsigned long *dest_len, void *source, u32 src_len)
112 {
113 	u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression);
114 	int ret = 0;
115 
116 	switch (comp_type) {
117 #if IS_ENABLED(CONFIG_LZO)
118 	case SQFS_COMP_LZO: {
119 		size_t lzo_dest_len = *dest_len;
120 		ret = lzo1x_decompress_safe(source, src_len, dest, &lzo_dest_len);
121 		if (ret) {
122 			printf("LZO decompression failed. Error code: %d\n", ret);
123 			return -EINVAL;
124 		}
125 
126 		break;
127 	}
128 #endif
129 #if IS_ENABLED(CONFIG_ZLIB)
130 	case SQFS_COMP_ZLIB:
131 		ret = uncompress(dest, dest_len, source, src_len);
132 		if (ret) {
133 			zlib_decompression_status(ret);
134 			return -EINVAL;
135 		}
136 
137 		break;
138 #endif
139 #if IS_ENABLED(CONFIG_ZSTD)
140 	case SQFS_COMP_ZSTD:
141 		ret = sqfs_zstd_decompress(ctxt, dest, *dest_len, source, src_len);
142 		if (ret) {
143 			printf("ZSTD Error code: %d\n", ZSTD_getErrorCode(ret));
144 			return -EINVAL;
145 		}
146 
147 		break;
148 #endif
149 	default:
150 		printf("Error: unknown compression type.\n");
151 		return -EINVAL;
152 	}
153 
154 	return ret;
155 }
156