Coverage Report

Created: 2025-08-28 07:10

/src/libagdb/libagdb/libagdb_compressed_block.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Compressed block functions
3
 *
4
 * Copyright (C) 2014-2024, 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 <byte_stream.h>
24
#include <memory.h>
25
#include <types.h>
26
27
#include "libagdb_compressed_block.h"
28
#include "libagdb_definitions.h"
29
#include "libagdb_file.h"
30
#include "libagdb_io_handle.h"
31
#include "libagdb_libcerror.h"
32
#include "libagdb_libcnotify.h"
33
#include "libagdb_libfcache.h"
34
#include "libagdb_libfdata.h"
35
#include "libagdb_libfwnt.h"
36
#include "libagdb_unused.h"
37
38
/* Creates compressed block
39
 * Make sure the value compressed_block is referencing, is set to NULL
40
 * Returns 1 if successful or -1 on error
41
 */
42
int libagdb_compressed_block_initialize(
43
     libagdb_compressed_block_t **compressed_block,
44
     size_t data_size,
45
     libcerror_error_t **error )
46
2.78k
{
47
2.78k
  static char *function = "libagdb_compressed_block_initialize";
48
49
2.78k
  if( compressed_block == NULL )
50
0
  {
51
0
    libcerror_error_set(
52
0
     error,
53
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
54
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
55
0
     "%s: invalid compressed block.",
56
0
     function );
57
58
0
    return( -1 );
59
0
  }
60
2.78k
  if( *compressed_block != NULL )
61
0
  {
62
0
    libcerror_error_set(
63
0
     error,
64
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
65
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
66
0
     "%s: invalid compressed block value already set.",
67
0
     function );
68
69
0
    return( -1 );
70
0
  }
71
2.78k
  if( ( data_size == 0 )
72
2.78k
   || ( data_size > (size_t) MEMORY_MAXIMUM_ALLOCATION_SIZE ) )
73
0
  {
74
0
    libcerror_error_set(
75
0
     error,
76
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
77
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
78
0
     "%s: invalid data size value out of bounds.",
79
0
     function );
80
81
0
    return( -1 );
82
0
  }
83
2.78k
  *compressed_block = memory_allocate_structure(
84
2.78k
                       libagdb_compressed_block_t );
85
86
2.78k
  if( *compressed_block == NULL )
87
0
  {
88
0
    libcerror_error_set(
89
0
     error,
90
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
91
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
92
0
     "%s: unable to create compressed block.",
93
0
     function );
94
95
0
    goto on_error;
96
0
  }
97
2.78k
  if( memory_set(
98
2.78k
       *compressed_block,
99
2.78k
       0,
100
2.78k
       sizeof( libagdb_compressed_block_t ) ) == NULL )
101
0
  {
102
0
    libcerror_error_set(
103
0
     error,
104
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
105
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
106
0
     "%s: unable to clear compressed block.",
107
0
     function );
108
109
0
    goto on_error;
110
0
  }
111
2.78k
  ( *compressed_block )->data = (uint8_t *) memory_allocate(
112
2.78k
                                             sizeof( uint8_t ) * data_size );
113
114
2.78k
  if( ( *compressed_block )->data == NULL )
115
0
  {
116
0
    libcerror_error_set(
117
0
     error,
118
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
119
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
120
0
     "%s: unable to create data.",
121
0
     function );
122
123
0
    goto on_error;
124
0
  }
125
2.78k
  ( *compressed_block )->data_size = data_size;
126
127
2.78k
  return( 1 );
128
129
0
on_error:
130
0
  if( *compressed_block != NULL )
131
0
  {
132
0
    memory_free(
133
0
     *compressed_block );
134
135
0
    *compressed_block = NULL;
136
0
  }
137
0
  return( -1 );
138
2.78k
}
139
140
/* Frees compressed block
141
 * Returns 1 if successful or -1 on error
142
 */
143
int libagdb_compressed_block_free(
144
     libagdb_compressed_block_t **compressed_block,
145
     libcerror_error_t **error )
146
2.78k
{
147
2.78k
  static char *function = "libagdb_compressed_block_free";
148
149
2.78k
  if( compressed_block == NULL )
150
0
  {
151
0
    libcerror_error_set(
152
0
     error,
153
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
154
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
155
0
     "%s: invalid compressed block.",
156
0
     function );
157
158
0
    return( -1 );
159
0
  }
160
2.78k
  if( *compressed_block != NULL )
161
2.78k
  {
162
2.78k
    if( ( *compressed_block )->data != NULL )
163
2.78k
    {
164
2.78k
      memory_free(
165
2.78k
       ( *compressed_block )->data );
166
2.78k
    }
167
2.78k
    memory_free(
168
2.78k
     *compressed_block );
169
170
2.78k
    *compressed_block = NULL;
171
2.78k
  }
172
2.78k
  return( 1 );
173
2.78k
}
174
175
/* Reads a compressed block
176
 * Returns 1 if successful or -1 on error
177
 */
178
int libagdb_compressed_block_read(
179
     libagdb_compressed_block_t *compressed_block,
180
     libagdb_io_handle_t *io_handle,
181
     libbfio_handle_t *file_io_handle,
182
     off64_t compressed_block_offset,
183
     size_t compressed_block_size,
184
     libcerror_error_t **error )
185
2.78k
{
186
2.78k
  uint8_t *compressed_data = NULL;
187
2.78k
        static char *function    = "libagdb_compressed_block_read";
188
2.78k
  ssize_t read_count       = 0;
189
2.78k
  int result               = 0;
190
191
2.78k
  if( compressed_block == NULL )
192
0
  {
193
0
    libcerror_error_set(
194
0
     error,
195
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
196
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
197
0
     "%s: invalid compressed block.",
198
0
     function );
199
200
0
    return( -1 );
201
0
  }
202
2.78k
  if( ( compressed_block_size == 0 )
203
2.78k
   || ( compressed_block_size > (size_t) MEMORY_MAXIMUM_ALLOCATION_SIZE ) )
204
34
  {
205
34
    libcerror_error_set(
206
34
     error,
207
34
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
208
34
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
209
34
     "%s: invalid compressed block size value out of bounds.",
210
34
     function );
211
212
34
    return( -1 );
213
34
  }
214
2.74k
  compressed_data = (uint8_t *) memory_allocate(
215
2.74k
                                 sizeof( uint8_t ) * compressed_block_size );
216
217
2.74k
  if( compressed_data == NULL )
218
0
  {
219
0
    libcerror_error_set(
220
0
     error,
221
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
222
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
223
0
     "%s: unable to create compressed data.",
224
0
     function );
225
226
0
    goto on_error;
227
0
  }
228
2.74k
  read_count = libbfio_handle_read_buffer_at_offset(
229
2.74k
          file_io_handle,
230
2.74k
          compressed_data,
231
2.74k
          compressed_block_size,
232
2.74k
          compressed_block_offset,
233
2.74k
          error );
234
235
2.74k
  if( read_count != (ssize_t) compressed_block_size )
236
138
  {
237
138
    libcerror_error_set(
238
138
     error,
239
138
     LIBCERROR_ERROR_DOMAIN_IO,
240
138
     LIBCERROR_IO_ERROR_READ_FAILED,
241
138
     "%s: unable to read compressed block at offset: %" PRIi64 " (0x%08" PRIx64 ").",
242
138
     function,
243
138
     compressed_block_size,
244
138
     compressed_block_size );
245
246
138
    goto on_error;
247
138
  }
248
2.60k
  if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_VISTA )
249
1.70k
  {
250
1.70k
    result = libfwnt_lznt1_decompress(
251
1.70k
              compressed_data,
252
1.70k
              (size_t) compressed_block_size,
253
1.70k
              compressed_block->data,
254
1.70k
              &( compressed_block->data_size ),
255
1.70k
              error );
256
1.70k
  }
257
900
  else if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS7 )
258
900
  {
259
900
    result = libfwnt_lzxpress_huffman_decompress(
260
900
              compressed_data,
261
900
              (size_t) compressed_block_size,
262
900
              compressed_block->data,
263
900
              &( compressed_block->data_size ),
264
900
              error );
265
900
  }
266
0
  else if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS8 )
267
0
  {
268
0
    result = libfwnt_lzxpress_huffman_decompress(
269
0
              compressed_data,
270
0
              (size_t) compressed_block_size,
271
0
              compressed_block->data,
272
0
              &( compressed_block->data_size ),
273
0
              error );
274
0
  }
275
2.60k
  if( result != 1 )
276
383
  {
277
383
    libcerror_error_set(
278
383
     error,
279
383
     LIBCERROR_ERROR_DOMAIN_COMPRESSION,
280
383
     LIBCERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED,
281
383
     "%s: unable to decompress block.",
282
383
     function );
283
284
383
    goto on_error;
285
383
  }
286
2.22k
  memory_free(
287
2.22k
   compressed_data );
288
289
2.22k
  compressed_data = NULL;
290
291
2.22k
  return( 1 );
292
293
521
on_error:
294
521
  if( compressed_data != NULL )
295
521
  {
296
521
    memory_free(
297
521
     compressed_data );
298
521
  }
299
521
  return( -1 );
300
2.60k
}
301
302
/* Reads a compressed block
303
 * Callback function for the compressed blocks list
304
 * Returns 1 if successful or -1 on error
305
 */
306
int libagdb_compressed_block_read_element_data(
307
     libagdb_io_handle_t *io_handle,
308
     libbfio_handle_t *file_io_handle,
309
     libfdata_list_element_t *element,
310
     libfcache_cache_t *cache,
311
     int element_file_index LIBAGDB_ATTRIBUTE_UNUSED,
312
     off64_t compressed_block_offset,
313
     size64_t compressed_block_size,
314
     uint32_t compressed_block_flags,
315
     uint8_t read_flags LIBAGDB_ATTRIBUTE_UNUSED,
316
     libcerror_error_t **error )
317
2.78k
{
318
2.78k
  libagdb_compressed_block_t *compressed_block = NULL;
319
2.78k
  static char *function                        = "libagdb_compressed_block_read_element_data";
320
2.78k
  size64_t uncompressed_size                   = 0;
321
2.78k
  int result                                   = 0;
322
323
2.78k
  LIBAGDB_UNREFERENCED_PARAMETER( element_file_index )
324
2.78k
  LIBAGDB_UNREFERENCED_PARAMETER( read_flags )
325
326
2.78k
  if( io_handle == NULL )
327
0
  {
328
0
    libcerror_error_set(
329
0
     error,
330
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
331
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
332
0
     "%s: invalid IO handle.",
333
0
     function );
334
335
0
    return( -1 );
336
0
  }
337
2.78k
  if( ( io_handle->file_type != LIBAGDB_FILE_TYPE_COMPRESSED_VISTA )
338
2.78k
   && ( io_handle->file_type != LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS7 )
339
2.78k
   && ( io_handle->file_type != LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS8 ) )
340
0
  {
341
0
    libcerror_error_set(
342
0
     error,
343
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
344
0
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
345
0
     "%s: invalid IO handle - unsupported file type.",
346
0
     function );
347
348
0
    return( -1 );
349
0
  }
350
2.78k
  if( compressed_block_size > (size64_t) SSIZE_MAX )
351
0
  {
352
0
    libcerror_error_set(
353
0
     error,
354
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
355
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
356
0
     "%s: invalid compressed block size value out of bounds.",
357
0
     function );
358
359
0
    return( -1 );
360
0
  }
361
2.78k
  if( ( compressed_block_flags & LIBFDATA_RANGE_FLAG_IS_COMPRESSED ) == 0 )
362
0
  {
363
0
    libcerror_error_set(
364
0
     error,
365
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
366
0
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
367
0
     "%s: compressed block flags not set.",
368
0
     function );
369
370
0
    return( -1 );
371
0
  }
372
2.78k
  if( libfdata_list_element_get_mapped_size(
373
2.78k
       element,
374
2.78k
       &uncompressed_size,
375
2.78k
       error ) != 1 )
376
0
  {
377
0
    libcerror_error_set(
378
0
     error,
379
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
380
0
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
381
0
     "%s: unable to retrieve list element mapped size.",
382
0
     function );
383
384
0
    goto on_error;
385
0
  }
386
2.78k
  if( uncompressed_size > (size64_t) io_handle->uncompressed_block_size )
387
0
  {
388
0
    libcerror_error_set(
389
0
     error,
390
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
391
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
392
0
     "%s: invalid uncompressed size value out of bounds.",
393
0
     function );
394
395
0
    return( -1 );
396
0
  }
397
2.78k
  if( libagdb_compressed_block_initialize(
398
2.78k
       &compressed_block,
399
2.78k
       (size_t) uncompressed_size,
400
2.78k
       error ) != 1 )
401
0
  {
402
0
    libcerror_error_set(
403
0
     error,
404
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
405
0
     LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
406
0
     "%s: unable to create compressed block.",
407
0
     function );
408
409
0
    goto on_error;
410
0
  }
411
2.78k
  result = libagdb_compressed_block_read(
412
2.78k
            compressed_block,
413
2.78k
            io_handle,
414
2.78k
            file_io_handle,
415
2.78k
            compressed_block_offset,
416
2.78k
            (size_t) compressed_block_size,
417
2.78k
            error );
418
419
2.78k
  if( result != 1 )
420
555
  {
421
555
    libcerror_error_set(
422
555
     error,
423
555
     LIBCERROR_ERROR_DOMAIN_IO,
424
555
     LIBCERROR_IO_ERROR_READ_FAILED,
425
555
     "%s: unable to read compressed block.",
426
555
     function );
427
428
555
    goto on_error;
429
555
  }
430
#if defined( HAVE_DEBUG_OUTPUT )
431
  if( libcnotify_verbose != 0 )
432
  {
433
    libcnotify_printf(
434
     "%s: uncompressed data:\n",
435
     function );
436
    libcnotify_print_data(
437
     compressed_block->data,
438
     compressed_block->data_size,
439
     LIBCNOTIFY_PRINT_DATA_FLAG_GROUP_DATA );
440
  }
441
#endif
442
2.22k
  if( libfdata_list_element_set_element_value(
443
2.22k
       element,
444
2.22k
       (intptr_t *) file_io_handle,
445
2.22k
       (libfdata_cache_t *) cache,
446
2.22k
       (intptr_t *) compressed_block,
447
2.22k
       (int (*)(intptr_t **, libcerror_error_t **)) &libagdb_compressed_block_free,
448
2.22k
       LIBFDATA_LIST_ELEMENT_VALUE_FLAG_MANAGED,
449
2.22k
       error ) != 1 )
450
0
  {
451
0
    libcerror_error_set(
452
0
     error,
453
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
454
0
     LIBCERROR_RUNTIME_ERROR_SET_FAILED,
455
0
     "%s: unable to set compressed block as element value.",
456
0
     function );
457
458
0
    goto on_error;
459
0
  }
460
2.22k
  return( 1 );
461
462
555
on_error:
463
555
  if( compressed_block != NULL )
464
555
  {
465
555
    libagdb_compressed_block_free(
466
555
     &compressed_block,
467
555
     NULL );
468
555
  }
469
555
  return( -1 );
470
2.22k
}
471