1 /*
2  * Compressed block vector functions
3  *
4  * Copyright (C) 2010-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 #include "libfsntfs_compressed_block.h"
27 #include "libfsntfs_compressed_block_vector.h"
28 #include "libfsntfs_compression_unit_data_handle.h"
29 #include "libfsntfs_compression_unit_descriptor.h"
30 #include "libfsntfs_compression.h"
31 #include "libfsntfs_definitions.h"
32 #include "libfsntfs_io_handle.h"
33 #include "libfsntfs_libcerror.h"
34 #include "libfsntfs_libfdata.h"
35 #include "libfsntfs_mft_attribute.h"
36 #include "libfsntfs_unused.h"
37 
38 /* Creates a compressed block vector
39  * Make sure the value compressed_block_vector is referencing, is set to NULL
40  * Returns 1 if successful or -1 on error
41  */
libfsntfs_compressed_block_vector_initialize(libfdata_vector_t ** compressed_block_vector,libfsntfs_io_handle_t * io_handle,libfsntfs_mft_attribute_t * mft_attribute,libcerror_error_t ** error)42 int libfsntfs_compressed_block_vector_initialize(
43      libfdata_vector_t **compressed_block_vector,
44      libfsntfs_io_handle_t *io_handle,
45      libfsntfs_mft_attribute_t *mft_attribute,
46      libcerror_error_t **error )
47 {
48 	libfdata_vector_t *safe_compressed_block_vector       = NULL;
49 	libfsntfs_compression_unit_data_handle_t *data_handle = NULL;
50 	libfsntfs_compression_unit_descriptor_t *descriptor   = NULL;
51 	static char *function                                 = "libfsntfs_compressed_block_vector_initialize";
52 	int descriptor_index                                  = 0;
53 	int number_of_descriptors                             = 0;
54 	int segment_index                                     = 0;
55 
56 	if( compressed_block_vector == NULL )
57 	{
58 		libcerror_error_set(
59 		 error,
60 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
61 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
62 		 "%s: invalid compressed block vector.",
63 		 function );
64 
65 		return( -1 );
66 	}
67 	if( *compressed_block_vector != NULL )
68 	{
69 		libcerror_error_set(
70 		 error,
71 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
72 		 LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
73 		 "%s: invalid compressed block vector value already set.",
74 		 function );
75 
76 		return( -1 );
77 	}
78 	if( io_handle == NULL )
79 	{
80 		libcerror_error_set(
81 		 error,
82 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
83 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
84 		 "%s: invalid IO handle.",
85 		 function );
86 
87 		return( -1 );
88 	}
89 	if( libfsntfs_compression_unit_data_handle_initialize(
90 	     &data_handle,
91 	     io_handle,
92 	     mft_attribute,
93 	     error ) != 1 )
94 	{
95 		libcerror_error_set(
96 		 error,
97 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
98 		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
99 		 "%s: unable to create compression unit data handle.",
100 		 function );
101 
102 		goto on_error;
103 	}
104 	if( libfdata_vector_initialize(
105 	     &safe_compressed_block_vector,
106 	     (size64_t) data_handle->compression_unit_size,
107 	     (intptr_t *) data_handle,
108 	     (int (*)(intptr_t **, libcerror_error_t **)) &libfsntfs_compression_unit_data_handle_free,
109 	     NULL,
110 	     (int (*)(intptr_t *, intptr_t *, libfdata_vector_t *, libfdata_cache_t *, int, int, off64_t, size64_t, uint32_t, uint8_t, libcerror_error_t **)) &libfsntfs_compressed_block_vector_read_element_data,
111 	     NULL,
112 	     LIBFDATA_DATA_HANDLE_FLAG_MANAGED,
113 	     error ) != 1 )
114 	{
115 		libcerror_error_set(
116 		 error,
117 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
118 		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
119 		 "%s: unable to create compressed block vector.",
120 		 function );
121 
122 		goto on_error;
123 	}
124 	if( libfsntfs_compression_unit_data_handle_get_number_of_descriptors(
125 	     data_handle,
126 	     &number_of_descriptors,
127 	     error ) != 1 )
128 	{
129 		libcerror_error_set(
130 		 error,
131 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
132 		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
133 		 "%s: unable to retrieve number of descriptors.",
134 		 function );
135 
136 		data_handle = NULL;
137 
138 		goto on_error;
139 	}
140 	for( descriptor_index = 0;
141 	     descriptor_index < number_of_descriptors;
142 	     descriptor_index++ )
143 	{
144 		if( libfsntfs_compression_unit_data_handle_get_descriptor_by_index(
145 		     data_handle,
146 		     descriptor_index,
147 		     &descriptor,
148 		     error ) != 1 )
149 		{
150 			libcerror_error_set(
151 			 error,
152 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
153 			 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
154 			 "%s: unable to retrieve descriptor: %d.",
155 			 function,
156 			 descriptor_index );
157 
158 			data_handle = NULL;
159 
160 			goto on_error;
161 		}
162 		if( descriptor == NULL )
163 		{
164 			libcerror_error_set(
165 			 error,
166 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
167 			 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
168 			 "%s: missing descriptor: %d.",
169 			 function,
170 			 descriptor_index );
171 
172 			data_handle = NULL;
173 
174 			goto on_error;
175 		}
176 		if( libfdata_vector_append_segment(
177 		     safe_compressed_block_vector,
178 		     &segment_index,
179 		     descriptor_index,
180 		     descriptor->data_offset,
181 		     descriptor->compression_unit_size,
182 		     descriptor->data_range_flags,
183 		     error ) != 1 )
184 		{
185 			libcerror_error_set(
186 			 error,
187 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
188 			 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
189 			 "%s: unable to append compression unit: %d segment to compressed block vector.",
190 			 function,
191 			 descriptor_index );
192 
193 			data_handle = NULL;
194 
195 			goto on_error;
196 		}
197 	}
198 	*compressed_block_vector = safe_compressed_block_vector;
199 
200 	return( 1 );
201 
202 on_error:
203 	if( safe_compressed_block_vector != NULL )
204 	{
205 		libfdata_vector_free(
206 		 &safe_compressed_block_vector,
207 		 NULL );
208 	}
209 	if( data_handle != NULL )
210 	{
211 		libfsntfs_compression_unit_data_handle_free(
212 		 &data_handle,
213 		 NULL );
214 	}
215 	return( -1 );
216 }
217 
218 /* Reads a compressed block
219  * Callback function for the compressed block vector
220  * Returns 1 if successful or -1 on error
221  */
libfsntfs_compressed_block_vector_read_element_data(libfsntfs_compression_unit_data_handle_t * data_handle,libbfio_handle_t * file_io_handle,libfdata_vector_t * vector,libfdata_cache_t * cache,int element_index,int element_data_file_index,off64_t element_data_offset,size64_t compressed_block_size,uint32_t range_flags,uint8_t read_flags LIBFSNTFS_ATTRIBUTE_UNUSED,libcerror_error_t ** error)222 int libfsntfs_compressed_block_vector_read_element_data(
223      libfsntfs_compression_unit_data_handle_t *data_handle,
224      libbfio_handle_t *file_io_handle,
225      libfdata_vector_t *vector,
226      libfdata_cache_t *cache,
227      int element_index,
228      int element_data_file_index,
229      off64_t element_data_offset,
230      size64_t compressed_block_size,
231      uint32_t range_flags,
232      uint8_t read_flags LIBFSNTFS_ATTRIBUTE_UNUSED,
233      libcerror_error_t **error )
234 {
235 	libfsntfs_compressed_block_t *compressed_block                       = NULL;
236 	libfsntfs_compression_unit_descriptor_t *compression_unit_descriptor = NULL;
237 	uint8_t *compressed_block_data                                       = NULL;
238 	uint8_t *compressed_data                                             = NULL;
239 	const char *block_type                                               = NULL;
240 	static char *function                                                = "libfsntfs_compressed_block_vector_read_element_data";
241 	ssize_t read_count                                                   = 0;
242 	off64_t data_stream_offset                                           = 0;
243 	int result                                                           = 0;
244 
245 	LIBFSNTFS_UNREFERENCED_PARAMETER( read_flags )
246 
247 	if( ( compressed_block_size == 0 )
248 	 || ( compressed_block_size > (size64_t) SSIZE_MAX ) )
249 	{
250 		libcerror_error_set(
251 		 error,
252 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
253 		 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
254 		 "%s: invalid compressed block size value out of bounds.",
255 		 function );
256 
257 		return( -1 );
258 	}
259 	if( libfsntfs_compression_unit_data_handle_get_descriptor_by_index(
260 	     data_handle,
261 	     element_data_file_index,
262 	     &compression_unit_descriptor,
263 	     error ) != 1 )
264 	{
265 		libcerror_error_set(
266 		 error,
267 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
268 		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
269 		 "%s: unable to retrieve compression unit descriptor: %d.",
270 		 function,
271 		 element_data_file_index );
272 
273 		goto on_error;
274 	}
275 	if( compression_unit_descriptor == NULL )
276 	{
277 		libcerror_error_set(
278 		 error,
279 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
280 		 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
281 		 "%s: missing compression unit descriptor: %d.",
282 		 function,
283 		 element_data_file_index );
284 
285 		goto on_error;
286 	}
287 	if( libfsntfs_compressed_block_initialize(
288 	     &compressed_block,
289 	     compressed_block_size,
290 	     error ) != 1 )
291 	{
292 		libcerror_error_set(
293 		 error,
294 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
295 		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
296 		 "%s: unable to create compressed block.",
297 		 function );
298 
299 		goto on_error;
300 	}
301 	if( compressed_block == NULL )
302 	{
303 		libcerror_error_set(
304 		 error,
305 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
306 		 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
307 		 "%s: missing compressed block.",
308 		 function );
309 
310 		goto on_error;
311 	}
312 	if( ( range_flags & LIBFDATA_RANGE_FLAG_IS_COMPRESSED ) != 0 )
313 	{
314 		compressed_data = (uint8_t *) memory_allocate(
315 		                               sizeof( uint8_t ) * (size_t) compressed_block_size );
316 
317 		if( compressed_data == NULL )
318 		{
319 			libcerror_error_set(
320 			 error,
321 			 LIBCERROR_ERROR_DOMAIN_MEMORY,
322 			 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
323 			 "%s: unable to create compressed data.",
324 			 function );
325 
326 			goto on_error;
327 		}
328 		compressed_block_data = compressed_data;
329 
330 		/* Make sure to read from the start of the data stream
331 		 * otherwise successive reads will fail
332 		 */
333 		data_stream_offset = 0;
334 
335 		block_type = "compressed";
336 	}
337 	else
338 	{
339 		compressed_block_data = compressed_block->data;
340 		data_stream_offset    = element_data_offset - compression_unit_descriptor->data_offset;
341 
342 		block_type = "uncompressed";
343 	}
344 	read_count = libfdata_stream_read_buffer_at_offset(
345 	              compression_unit_descriptor->data_stream,
346 	              (intptr_t *) file_io_handle,
347 	              compressed_block_data,
348 	              compressed_block_size,
349 	              data_stream_offset,
350 	              0,
351 	              error );
352 
353 	if( read_count != (ssize_t) compressed_block_size )
354 	{
355 		libcerror_error_set(
356 		 error,
357 		 LIBCERROR_ERROR_DOMAIN_IO,
358 		 LIBCERROR_IO_ERROR_READ_FAILED,
359 		 "%s: unable to read %s block at offset: %" PRIi64 " (0x%08" PRIx64 ").",
360 		 function,
361 		 block_type,
362 		 data_stream_offset,
363 		 data_stream_offset );
364 
365 		goto on_error;
366 	}
367 	if( ( range_flags & LIBFDATA_RANGE_FLAG_IS_COMPRESSED ) != 0 )
368 	{
369 		result = libfsntfs_decompress_data(
370 		          compressed_data,
371 		          (size_t) compressed_block_size,
372 		          LIBFSNTFS_COMPRESSION_METHOD_LZNT1,
373 		          compressed_block->data,
374 		          &( compressed_block->data_size ),
375 		          error );
376 
377 		if( result != 1 )
378 		{
379 			libcerror_error_set(
380 			 error,
381 			 LIBCERROR_ERROR_DOMAIN_COMPRESSION,
382 			 LIBCERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED,
383 			 "%s: unable to decompress compressed data.",
384 			 function );
385 
386 			goto on_error;
387 		}
388 		memory_free(
389 		 compressed_data );
390 
391 		compressed_data = NULL;
392 
393 		/* If the compressed block data size is 0 or the compressed block was truncated
394 		 * fill the remainder of the compressed block with 0-byte values
395 		 */
396 		if( compressed_block->data_size < compressed_block_size )
397 		{
398 			if( memory_set(
399 			     &( compressed_block->data[ compressed_block->data_size ] ),
400 			     0,
401 			     compressed_block_size - compressed_block->data_size ) == NULL )
402 			{
403 				libcerror_error_set(
404 				 error,
405 				 LIBCERROR_ERROR_DOMAIN_MEMORY,
406 				 LIBCERROR_MEMORY_ERROR_SET_FAILED,
407 				 "%s: unable to clear remainder of compressed block.",
408 				 function );
409 
410 				goto on_error;
411 			}
412 			compressed_block->data_size = compressed_block_size;
413 		}
414 	}
415 	if( libfdata_vector_set_element_value_by_index(
416 	     vector,
417 	     (intptr_t *) file_io_handle,
418 	     cache,
419 	     element_index,
420 	     (intptr_t *) compressed_block,
421 	     (int (*)(intptr_t **, libcerror_error_t **)) &libfsntfs_compressed_block_free,
422 	     LIBFDATA_VECTOR_ELEMENT_VALUE_FLAG_MANAGED,
423 	     error ) != 1 )
424 	{
425 		libcerror_error_set(
426 		 error,
427 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
428 		 LIBCERROR_RUNTIME_ERROR_SET_FAILED,
429 		 "%s: unable to set cluster block as element value.",
430 		 function );
431 
432 		goto on_error;
433 	}
434 	return( 1 );
435 
436 on_error:
437 	if( compressed_data != NULL )
438 	{
439 		memory_free(
440 		 compressed_data );
441 	}
442 	if( compressed_block != NULL )
443 	{
444 		libfsntfs_compressed_block_free(
445 		 &compressed_block,
446 		 NULL );
447 	}
448 	return( -1 );
449 }
450 
451