1 /*
2  * Compression functions
3  *
4  * Copyright (C) 2012-2021, Joachim Metz <joachim.metz@gmail.com>
5  *
6  * Refer to AUTHORS for acknowledgements.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <common.h>
23 #include <memory.h>
24 #include <types.h>
25 
26 #if defined( HAVE_STDLIB_H ) || defined( WINAPI )
27 #include <stdlib.h>
28 #endif
29 
30 #if defined( HAVE_BZLIB ) || defined( BZ_DLL )
31 #include <bzlib.h>
32 #endif
33 
34 #if defined( HAVE_LIBLZMA ) || defined( LIBLZMA_DLL )
35 #include <lzma.h>
36 #endif
37 
38 #if defined( HAVE_ZLIB ) || defined( ZLIB_DLL )
39 #include <zlib.h>
40 #endif
41 
42 #include "libmodi_adc.h"
43 #include "libmodi_compression.h"
44 #include "libmodi_definitions.h"
45 #include "libmodi_deflate.h"
46 #include "libmodi_libcerror.h"
47 #include "libmodi_libcnotify.h"
48 
49 /* Decompresses data using the compression method
50  * Returns 1 on success or -1 on error
51  */
libmodi_decompress_data(const uint8_t * compressed_data,size_t compressed_data_size,int compression_method,uint8_t * uncompressed_data,size_t * uncompressed_data_size,libcerror_error_t ** error)52 int libmodi_decompress_data(
53      const uint8_t *compressed_data,
54      size_t compressed_data_size,
55      int compression_method,
56      uint8_t *uncompressed_data,
57      size_t *uncompressed_data_size,
58      libcerror_error_t **error )
59 {
60 	static char *function                     = "libmodi_decompress_data";
61 
62 #if defined( HAVE_BZLIB ) || defined( BZ_DLL )
63 	unsigned int bzip2_uncompressed_data_size = 0;
64 	int bzlib_result                          = 0;
65 #endif
66 #if defined( HAVE_LIBLZMA ) || defined( LIBLZMA_DLL )
67 	lzma_stream lzma_compressed_stream        = LZMA_STREAM_INIT;
68 	lzma_ret lzma_result                      = 0;
69 #endif
70 #if ( defined( HAVE_ZLIB ) && defined( HAVE_ZLIB_UNCOMPRESS ) ) || defined( ZLIB_DLL )
71 	uLongf zlib_uncompressed_data_size        = 0;
72 	int zlib_result                           = 0;
73 #endif
74 
75 	if( compressed_data == NULL )
76 	{
77 		libcerror_error_set(
78 		 error,
79 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
80 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
81 		 "%s: invalid compressed data buffer.",
82 		 function );
83 
84 		return( -1 );
85 	}
86 	if( uncompressed_data == NULL )
87 	{
88 		libcerror_error_set(
89 		 error,
90 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
91 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
92 		 "%s: invalid uncompressed data buffer.",
93 		 function );
94 
95 		return( -1 );
96 	}
97 	if( uncompressed_data == compressed_data )
98 	{
99 		libcerror_error_set(
100 		 error,
101 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
102 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
103 		 "%s: invalid compressed data buffer equals uncompressed data buffer.",
104 		 function );
105 
106 		return( -1 );
107 	}
108 	if( uncompressed_data_size == NULL )
109 	{
110 		libcerror_error_set(
111 		 error,
112 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
113 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
114 		 "%s: invalid uncompressed data size.",
115 		 function );
116 
117 		return( -1 );
118 	}
119 	if( compression_method == LIBMODI_COMPRESSION_METHOD_ADC )
120 	{
121 		if( libmodi_adc_decompress(
122 		     compressed_data,
123 		     compressed_data_size,
124 		     uncompressed_data,
125 		     uncompressed_data_size,
126 		     error ) != 1 )
127 		{
128 			libcerror_error_set(
129 			 error,
130 			 LIBCERROR_ERROR_DOMAIN_COMPRESSION,
131 			 LIBCERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED,
132 			 "%s: unable to decompress ADC compressed data.",
133 			 function );
134 
135 			goto on_error;
136 		}
137 	}
138 	else if( compression_method == LIBMODI_COMPRESSION_METHOD_BZIP2 )
139 	{
140 #if defined( HAVE_BZLIB ) || defined( BZ_DLL )
141 		if( compressed_data_size > (size_t) UINT_MAX )
142 		{
143 			libcerror_error_set(
144 			 error,
145 			 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
146 			 LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
147 			 "%s: invalid compressed data size value exceeds maximum.",
148 			 function );
149 
150 			goto on_error;
151 		}
152 		if( *uncompressed_data_size > (size_t) UINT_MAX )
153 		{
154 			libcerror_error_set(
155 			 error,
156 			 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
157 			 LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
158 			 "%s: invalid uncompressed data size value exceeds maximum.",
159 			 function );
160 
161 			goto on_error;
162 		}
163 		bzip2_uncompressed_data_size = (unsigned int) *uncompressed_data_size;
164 
165 		bzlib_result = BZ2_bzBuffToBuffDecompress(
166 		                (char *) uncompressed_data,
167 		                &bzip2_uncompressed_data_size,
168 		                (char *) compressed_data,
169 		                (unsigned int) compressed_data_size,
170 		                0,
171 		                0 );
172 
173 		if( ( bzlib_result == BZ_DATA_ERROR )
174 		 || ( bzlib_result == BZ_DATA_ERROR_MAGIC ) )
175 		{
176 			libcerror_error_set(
177 			 error,
178 			 LIBCERROR_ERROR_DOMAIN_MEMORY,
179 			 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
180 			 "%s: unable to decompress data: data error.",
181 			 function );
182 
183 			goto on_error;
184 		}
185 		else if( bzlib_result == BZ_OUTBUFF_FULL )
186 		{
187 			libcerror_error_set(
188 			 error,
189 			 LIBCERROR_ERROR_DOMAIN_COMPRESSION,
190 			 LIBCERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED,
191 			 "%s: unable to decompress data: target buffer too small.",
192 			 function );
193 
194 			goto on_error;
195 		}
196 		else if( bzlib_result == BZ_MEM_ERROR )
197 		{
198 			libcerror_error_set(
199 			 error,
200 			 LIBCERROR_ERROR_DOMAIN_MEMORY,
201 			 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
202 			 "%s: unable to decompress data: insufficient memory.",
203 			 function );
204 
205 			goto on_error;
206 		}
207 		else if( bzlib_result != BZ_OK )
208 		{
209 			libcerror_error_set(
210 			 error,
211 			 LIBCERROR_ERROR_DOMAIN_COMPRESSION,
212 			 LIBCERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED,
213 			 "%s: bzlib returned undefined error: %d.",
214 			 function,
215 			 bzlib_result );
216 
217 			goto on_error;
218 		}
219 		*uncompressed_data_size = (size_t) bzip2_uncompressed_data_size;
220 #else
221 		libcerror_error_set(
222 		 error,
223 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
224 		 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
225 		 "%s: missing support for bzip2 compression.",
226 		 function );
227 
228 		goto on_error;
229 #endif /* defined( HAVE_BZLIB ) || defined( BZ_DLL ) */
230 	}
231 	else if( compression_method == LIBMODI_COMPRESSION_METHOD_DEFLATE )
232 	{
233 #if ( defined( HAVE_ZLIB ) && defined( HAVE_ZLIB_UNCOMPRESS ) ) || defined( ZLIB_DLL )
234 		if( compressed_data_size > (size_t) ULONG_MAX )
235 		{
236 			libcerror_error_set(
237 			 error,
238 			 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
239 			 LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
240 			 "%s: invalid compressed data size value exceeds maximum.",
241 			 function );
242 
243 			goto on_error;
244 		}
245 		if( *uncompressed_data_size > (size_t) ULONG_MAX )
246 		{
247 			libcerror_error_set(
248 			 error,
249 			 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
250 			 LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
251 			 "%s: invalid uncompressed data size value exceeds maximum.",
252 			 function );
253 
254 			goto on_error;
255 		}
256 		zlib_uncompressed_data_size = (uLongf) *uncompressed_data_size;
257 
258 		zlib_result = uncompress(
259 		               (Bytef *) uncompressed_data,
260 		               &zlib_uncompressed_data_size,
261 		               (Bytef *) compressed_data,
262 		               (uLong) compressed_data_size );
263 
264 		if( zlib_result == Z_DATA_ERROR )
265 		{
266 			libcerror_error_set(
267 			 error,
268 			 LIBCERROR_ERROR_DOMAIN_MEMORY,
269 			 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
270 			 "%s: unable to decompress data: data error.",
271 			 function );
272 
273 			goto on_error;
274 		}
275 		else if( zlib_result == Z_BUF_ERROR )
276 		{
277 			libcerror_error_set(
278 			 error,
279 			 LIBCERROR_ERROR_DOMAIN_COMPRESSION,
280 			 LIBCERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED,
281 			 "%s: unable to decompress data: target buffer too small.",
282 			 function );
283 
284 			goto on_error;
285 		}
286 		else if( zlib_result == Z_MEM_ERROR )
287 		{
288 			libcerror_error_set(
289 			 error,
290 			 LIBCERROR_ERROR_DOMAIN_MEMORY,
291 			 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
292 			 "%s: unable to read compressed data: insufficient memory.",
293 			 function );
294 
295 			goto on_error;
296 		}
297 		else if( zlib_result != Z_OK )
298 		{
299 			libcerror_error_set(
300 			 error,
301 			 LIBCERROR_ERROR_DOMAIN_COMPRESSION,
302 			 LIBCERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED,
303 			 "%s: zlib returned undefined error: %d.",
304 			 function,
305 			 zlib_result );
306 
307 			goto on_error;
308 		}
309 		*uncompressed_data_size = (size_t) zlib_uncompressed_data_size;
310 #else
311 		if( libmodi_deflate_decompress_zlib(
312 		     compressed_data,
313 		     compressed_data_size,
314 		     uncompressed_data,
315 		     uncompressed_data_size,
316 		     error ) != 1 )
317 		{
318 			libcerror_error_set(
319 			 error,
320 			 LIBCERROR_ERROR_DOMAIN_COMPRESSION,
321 			 LIBCERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED,
322 			 "%s: unable to decompress zlib+DEFLATE compressed data.",
323 			 function );
324 
325 			goto on_error;
326 		}
327 #endif /* ( defined( HAVE_ZLIB ) && defined( HAVE_ZLIB_UNCOMPRESS ) ) || defined( ZLIB_DLL ) */
328 	}
329 	else if( compression_method == LIBMODI_COMPRESSION_METHOD_LZFSE )
330 	{
331 		libcerror_error_set(
332 		 error,
333 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
334 		 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
335 		 "%s: missing support for LZFSE compression.",
336 		 function );
337 
338 		goto on_error;
339 	}
340 	else if( compression_method == LIBMODI_COMPRESSION_METHOD_LZMA )
341 	{
342 #if defined( HAVE_LIBLZMA ) || defined( LIBLZMA_DLL )
343 		lzma_result = lzma_stream_decoder(
344 		               &lzma_compressed_stream,
345 		               UINT64_MAX,
346 		               0 );
347 
348 		if( lzma_result != LZMA_OK )
349 		{
350 			libcerror_error_set(
351 			 error,
352 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
353 			 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
354 			 "%s: unable to create LZMA stream.",
355 			 function );
356 
357 			goto on_error;
358 		}
359 		lzma_compressed_stream.next_in   = compressed_data;
360 		lzma_compressed_stream.avail_in  = compressed_data_size;
361 		lzma_compressed_stream.next_out  = uncompressed_data;
362 		lzma_compressed_stream.avail_out = *uncompressed_data_size;
363 
364 		lzma_result = lzma_code(
365 		               &lzma_compressed_stream,
366 		               LZMA_RUN );
367 
368 		if( ( lzma_result == LZMA_DATA_ERROR )
369 		 || ( lzma_result == LZMA_FORMAT_ERROR ) )
370 		{
371 			libcerror_error_set(
372 			 error,
373 			 LIBCERROR_ERROR_DOMAIN_MEMORY,
374 			 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
375 			 "%s: unable to decompress data: data error.",
376 			 function );
377 
378 			goto on_error;
379 		}
380 		else if( lzma_result == LZMA_BUF_ERROR )
381 		{
382 			libcerror_error_set(
383 			 error,
384 			 LIBCERROR_ERROR_DOMAIN_COMPRESSION,
385 			 LIBCERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED,
386 			 "%s: unable to decompress data: target buffer too small.",
387 			 function );
388 
389 			goto on_error;
390 		}
391 		else if( lzma_result == LZMA_MEM_ERROR )
392 		{
393 			libcerror_error_set(
394 			 error,
395 			 LIBCERROR_ERROR_DOMAIN_MEMORY,
396 			 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
397 			 "%s: unable to read compressed data: insufficient memory.",
398 			 function );
399 
400 			goto on_error;
401 		}
402 		else if( lzma_result != LZMA_STREAM_END )
403 		{
404 			libcerror_error_set(
405 			 error,
406 			 LIBCERROR_ERROR_DOMAIN_COMPRESSION,
407 			 LIBCERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED,
408 			 "%s: liblzma returned undefined error: %d.",
409 			 function,
410 			 lzma_result );
411 
412 			goto on_error;
413 		}
414 		*uncompressed_data_size = (size_t) lzma_compressed_stream.total_out;
415 
416 		lzma_end(
417 		 &lzma_compressed_stream );
418 #else
419 		libcerror_error_set(
420 		 error,
421 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
422 		 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
423 		 "%s: missing support for LZMA compression.",
424 		 function );
425 
426 		goto on_error;
427 #endif /* defined( HAVE_LIBLZMA ) || defined( LIBLZMA_DLL ) */
428 	}
429 	else
430 	{
431 		libcerror_error_set(
432 		 error,
433 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
434 		 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
435 		 "%s: unsupported compression method.",
436 		 function );
437 
438 		goto on_error;
439 	}
440 	return( 1 );
441 
442 on_error:
443 #if defined( HAVE_LIBLZMA ) || defined( LIBLZMA_DLL )
444 	lzma_end(
445 	 &lzma_compressed_stream );
446 #endif
447 	return( -1 );
448 }
449 
450