Coverage Report

Created: 2026-08-31 07:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libagdb/libagdb/libagdb_io_handle.c
Line
Count
Source
1
/*
2
 * Input/Output (IO) handle functions
3
 *
4
 * Copyright (C) 2014-2026, 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_debug.h"
28
#include "libagdb_definitions.h"
29
#include "libagdb_io_handle.h"
30
#include "libagdb_libbfio.h"
31
#include "libagdb_libcerror.h"
32
#include "libagdb_libcnotify.h"
33
#include "libagdb_libfcache.h"
34
#include "libagdb_libfdata.h"
35
#include "libagdb_unused.h"
36
37
const char *agdb_mem_file_signature_vista  = "MEMO";
38
const char *agdb_mem_file_signature_win7   = "MEM0";
39
const char *agdb_mem_file_signature_win8_0 = "MEM\xb0";
40
const char *agdb_mam_file_signature_win8_1 = "MAM\x84";
41
42
/* Creates an IO handle
43
 * Make sure the value io_handle is referencing, is set to NULL
44
 * Returns 1 if successful or -1 on error
45
 */
46
int libagdb_io_handle_initialize(
47
     libagdb_io_handle_t **io_handle,
48
     libcerror_error_t **error )
49
2.37k
{
50
2.37k
  static char *function = "libagdb_io_handle_initialize";
51
52
2.37k
  if( io_handle == NULL )
53
0
  {
54
0
    libcerror_error_set(
55
0
     error,
56
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
57
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
58
0
     "%s: invalid IO handle.",
59
0
     function );
60
61
0
    return( -1 );
62
0
  }
63
2.37k
  if( *io_handle != NULL )
64
0
  {
65
0
    libcerror_error_set(
66
0
     error,
67
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
68
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
69
0
     "%s: invalid IO handle value already set.",
70
0
     function );
71
72
0
    return( -1 );
73
0
  }
74
2.37k
  *io_handle = memory_allocate_structure(
75
2.37k
                libagdb_io_handle_t );
76
77
2.37k
  if( *io_handle == NULL )
78
0
  {
79
0
    libcerror_error_set(
80
0
     error,
81
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
82
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
83
0
     "%s: unable to create IO handle.",
84
0
     function );
85
86
0
    goto on_error;
87
0
  }
88
2.37k
  if( memory_set(
89
2.37k
       *io_handle,
90
2.37k
       0,
91
2.37k
       sizeof( libagdb_io_handle_t ) ) == NULL )
92
0
  {
93
0
    libcerror_error_set(
94
0
     error,
95
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
96
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
97
0
     "%s: unable to clear file.",
98
0
     function );
99
100
0
    goto on_error;
101
0
  }
102
2.37k
  return( 1 );
103
104
0
on_error:
105
0
  if( *io_handle != NULL )
106
0
  {
107
0
    memory_free(
108
0
     *io_handle );
109
110
0
    *io_handle = NULL;
111
0
  }
112
0
  return( -1 );
113
2.37k
}
114
115
/* Frees a IO handle
116
 * Returns 1 if successful or -1 on error
117
 */
118
int libagdb_io_handle_free(
119
     libagdb_io_handle_t **io_handle,
120
     libcerror_error_t **error )
121
2.37k
{
122
2.37k
  static char *function = "libagdb_io_handle_free";
123
124
2.37k
  if( io_handle == NULL )
125
0
  {
126
0
    libcerror_error_set(
127
0
     error,
128
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
129
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
130
0
     "%s: invalid IO handle.",
131
0
     function );
132
133
0
    return( -1 );
134
0
  }
135
2.37k
  if( *io_handle != NULL )
136
2.37k
  {
137
2.37k
    memory_free(
138
2.37k
     *io_handle );
139
140
2.37k
    *io_handle = NULL;
141
2.37k
  }
142
2.37k
  return( 1 );
143
2.37k
}
144
145
/* Clears the IO handle
146
 * Returns 1 if successful or -1 on error
147
 */
148
int libagdb_io_handle_clear(
149
     libagdb_io_handle_t *io_handle,
150
     libcerror_error_t **error )
151
26
{
152
26
  static char *function = "libagdb_io_handle_clear";
153
154
26
  if( io_handle == NULL )
155
0
  {
156
0
    libcerror_error_set(
157
0
     error,
158
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
159
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
160
0
     "%s: invalid IO handle.",
161
0
     function );
162
163
0
    return( -1 );
164
0
  }
165
26
  if( memory_set(
166
26
       io_handle,
167
26
       0,
168
26
       sizeof( libagdb_io_handle_t ) ) == NULL )
169
0
  {
170
0
    libcerror_error_set(
171
0
     error,
172
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
173
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
174
0
     "%s: unable to clear IO handle.",
175
0
     function );
176
177
0
    return( -1 );
178
0
  }
179
26
  return( 1 );
180
26
}
181
182
/* Reads the compressed blocks
183
 * Returns 1 if successful or -1 on error
184
 */
185
int libagdb_io_handle_read_compressed_blocks(
186
     libagdb_io_handle_t *io_handle,
187
     libbfio_handle_t *file_io_handle,
188
     libfdata_list_t *compressed_blocks_list,
189
     libcerror_error_t **error )
190
1.16k
{
191
1.16k
  uint8_t compressed_block_data[ 4 ];
192
193
1.16k
  static char *function            = "libagdb_io_handle_read_compressed_blocks";
194
1.16k
  off64_t file_offset              = 0;
195
1.16k
  size_t read_size                 = 0;
196
1.16k
  ssize_t read_count               = 0;
197
1.16k
  uint32_t compressed_block_size   = 0;
198
1.16k
  uint32_t uncompressed_data_size  = 0;
199
1.16k
  uint32_t uncompressed_block_size = 0;
200
1.16k
  int compressed_block_index       = 0;
201
1.16k
  int element_index                = 0;
202
203
1.16k
  if( io_handle == NULL )
204
0
  {
205
0
    libcerror_error_set(
206
0
     error,
207
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
208
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
209
0
     "%s: invalid IO handle.",
210
0
     function );
211
212
0
    return( -1 );
213
0
  }
214
1.16k
  if( ( io_handle->file_type != LIBAGDB_FILE_TYPE_COMPRESSED_VISTA )
215
624
   && ( io_handle->file_type != LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS7 )
216
237
   && ( io_handle->file_type != LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS8_0 )
217
222
   && ( io_handle->file_type != LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS8_1 ) )
218
0
  {
219
0
    libcerror_error_set(
220
0
     error,
221
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
222
0
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
223
0
     "%s: invalid IO handle - unsupported file type.",
224
0
     function );
225
226
0
    return( -1 );
227
0
  }
228
1.16k
  if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_VISTA )
229
545
  {
230
545
    file_offset = 8;
231
545
    read_size   = 2;
232
545
  }
233
624
  else if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS7 )
234
387
  {
235
387
    file_offset = 8;
236
387
    read_size   = 4;
237
387
  }
238
237
  else if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS8_0 )
239
15
  {
240
15
    file_offset = 12;
241
15
    read_size   = 4;
242
15
  }
243
222
  else if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS8_1 )
244
222
  {
245
222
    file_offset = 12;
246
222
    read_size   = 4;
247
222
  }
248
1.16k
  uncompressed_data_size = io_handle->uncompressed_data_size;
249
250
1.58M
  while( (size64_t) file_offset < io_handle->file_size )
251
1.58M
  {
252
#if defined( HAVE_DEBUG_OUTPUT )
253
    if( libcnotify_verbose != 0 )
254
    {
255
      libcnotify_printf(
256
       "%s: reading compressed data block at offset: %" PRIi64 " (0x%08" PRIx64 ")\n",
257
       function,
258
       file_offset,
259
       file_offset );
260
    }
261
#endif
262
1.58M
    read_count = libbfio_handle_read_buffer_at_offset(
263
1.58M
                  file_io_handle,
264
1.58M
                  compressed_block_data,
265
1.58M
                  read_size,
266
1.58M
                  file_offset,
267
1.58M
                  error );
268
269
1.58M
    if( read_count != (ssize_t) read_size )
270
30
    {
271
30
      libcerror_error_set(
272
30
       error,
273
30
       LIBCERROR_ERROR_DOMAIN_IO,
274
30
       LIBCERROR_IO_ERROR_READ_FAILED,
275
30
       "%s: unable to read compressed block data at offset: %" PRIi64 " (0x%08" PRIx64 ").",
276
30
       function,
277
30
       file_offset,
278
30
       file_offset );
279
280
30
      return( -1 );
281
30
    }
282
1.58M
    if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_VISTA )
283
1.57M
    {
284
1.57M
      byte_stream_copy_to_uint16_little_endian(
285
1.57M
       compressed_block_data,
286
1.57M
       compressed_block_size );
287
288
1.57M
      compressed_block_size &= 0x0fff;
289
290
1.57M
      if( compressed_block_size == 0 )
291
4
      {
292
4
        libcerror_error_set(
293
4
         error,
294
4
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
295
4
         LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
296
4
         "%s: invalid compressed block size value out of bounds.",
297
4
         function );
298
299
4
        return( -1 );
300
4
      }
301
1.57M
      compressed_block_size += 3;
302
303
1.57M
      uncompressed_block_size = io_handle->uncompressed_block_size;
304
1.57M
    }
305
3.57k
    else if( ( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS7 )
306
442
          || ( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS8_0 ) )
307
3.35k
    {
308
3.35k
      byte_stream_copy_to_uint32_little_endian(
309
3.35k
       compressed_block_data,
310
3.35k
       compressed_block_size );
311
312
3.35k
      file_offset += 4;
313
314
3.35k
      uncompressed_block_size = io_handle->uncompressed_block_size;
315
3.35k
    }
316
220
    else if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS8_1 )
317
220
    {
318
220
      if( io_handle->file_size > (size64_t) UINT32_MAX )
319
0
      {
320
0
        libcerror_error_set(
321
0
         error,
322
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
323
0
         LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
324
0
         "%s: invalid compressed block size value out of bounds.",
325
0
         function );
326
327
0
        return( -1 );
328
0
      }
329
220
      compressed_block_size = (uint32_t) io_handle->file_size - 12;
330
331
220
      if( ( uncompressed_data_size % io_handle->uncompressed_block_size ) != 0 )
332
219
      {
333
219
        uncompressed_data_size /= io_handle->uncompressed_block_size;
334
219
        uncompressed_data_size += 1;
335
219
        uncompressed_data_size *= io_handle->uncompressed_block_size;
336
219
      }
337
220
      uncompressed_block_size = uncompressed_data_size;
338
220
    }
339
1.58M
    if( uncompressed_data_size < uncompressed_block_size )
340
239
    {
341
239
      uncompressed_block_size = uncompressed_data_size;
342
239
    }
343
1.58M
    else
344
1.58M
    {
345
1.58M
      uncompressed_block_size = uncompressed_block_size;
346
1.58M
    }
347
#if defined( HAVE_DEBUG_OUTPUT )
348
    if( libcnotify_verbose != 0 )
349
    {
350
      libcnotify_printf(
351
       "%s: compressed block: %d size\t: %" PRIu32 "\n",
352
       function,
353
       compressed_block_index,
354
       compressed_block_size );
355
    }
356
#endif
357
1.58M
    if( compressed_block_size == 0 )
358
3
    {
359
3
      libcerror_error_set(
360
3
       error,
361
3
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
362
3
       LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
363
3
       "%s: invalid compressed block size value out of bounds.",
364
3
       function );
365
366
3
      return( -1 );
367
3
    }
368
1.58M
    if( libfdata_list_append_element_with_mapped_size(
369
1.58M
         compressed_blocks_list,
370
1.58M
         &element_index,
371
1.58M
         0,
372
1.58M
         file_offset,
373
1.58M
         (size64_t) compressed_block_size,
374
1.58M
         LIBFDATA_RANGE_FLAG_IS_COMPRESSED,
375
1.58M
         uncompressed_block_size,
376
1.58M
         error ) != 1 )
377
0
    {
378
0
      libcerror_error_set(
379
0
       error,
380
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
381
0
       LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
382
0
       "%s: unable to append compressed block: %d to list.",
383
0
       function,
384
0
       compressed_block_index );
385
386
0
      return( -1 );
387
0
    }
388
1.58M
    file_offset            += compressed_block_size;
389
1.58M
    uncompressed_data_size -= uncompressed_block_size;
390
391
1.58M
    if( uncompressed_data_size == 0 )
392
459
    {
393
459
      break;
394
459
    }
395
1.58M
    compressed_block_index++;
396
1.58M
  }
397
#if defined( HAVE_DEBUG_OUTPUT )
398
  if( libcnotify_verbose != 0 )
399
  {
400
    if( (size64_t) file_offset < io_handle->file_size )
401
    {
402
      libcnotify_printf(
403
       "%s: trailing data size\t: %" PRIu64 "\n",
404
       function,
405
       io_handle->file_size - (size64_t) file_offset );
406
    }
407
  }
408
#endif /* defined( HAVE_DEBUG_OUTPUT ) */
409
410
1.13k
  return( 1 );
411
1.16k
}
412
413
/* Reads data from the current offset into a buffer
414
 * Callback for the uncompressed block stream
415
 * Returns the number of bytes read or -1 on error
416
 */
417
ssize_t libagdb_io_handle_read_segment_data(
418
         intptr_t *data_handle LIBAGDB_ATTRIBUTE_UNUSED,
419
         intptr_t *file_io_handle,
420
         int segment_index,
421
         int segment_file_index LIBAGDB_ATTRIBUTE_UNUSED,
422
         uint8_t *segment_data,
423
         size_t segment_data_size,
424
         uint32_t segment_flags LIBAGDB_ATTRIBUTE_UNUSED,
425
         uint8_t read_flags LIBAGDB_ATTRIBUTE_UNUSED,
426
         libcerror_error_t **error )
427
211k
{
428
211k
  static char *function = "libagdb_io_handle_read_segment_data";
429
211k
  ssize_t read_count    = 0;
430
431
211k
  LIBAGDB_UNREFERENCED_PARAMETER( data_handle )
432
211k
  LIBAGDB_UNREFERENCED_PARAMETER( segment_file_index )
433
211k
  LIBAGDB_UNREFERENCED_PARAMETER( segment_flags )
434
211k
  LIBAGDB_UNREFERENCED_PARAMETER( read_flags )
435
436
211k
  if( segment_data_size > (size64_t) SSIZE_MAX )
437
0
  {
438
0
    libcerror_error_set(
439
0
     error,
440
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
441
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
442
0
     "%s: invalid segment data size value out of bounds.",
443
0
     function );
444
445
0
    return( -1 );
446
0
  }
447
211k
  read_count = libbfio_handle_read_buffer(
448
211k
                (libbfio_handle_t *) file_io_handle,
449
211k
                segment_data,
450
211k
                segment_data_size,
451
211k
                error );
452
453
211k
  if( read_count != (ssize_t) segment_data_size )
454
443
  {
455
443
    libcerror_error_set(
456
443
     error,
457
443
     LIBCERROR_ERROR_DOMAIN_IO,
458
443
     LIBCERROR_IO_ERROR_READ_FAILED,
459
443
     "%s: unable to read segment: %d data.",
460
443
     function,
461
443
     segment_index );
462
463
443
    return( -1 );
464
443
  }
465
211k
  return( read_count );
466
211k
}
467
468
/* Seeks a certain offset of the data
469
 * Callback for the uncompressed block stream
470
 * Returns the offset if seek is successful or -1 on error
471
 */
472
off64_t libagdb_io_handle_seek_segment_offset(
473
         intptr_t *data_handle LIBAGDB_ATTRIBUTE_UNUSED,
474
         intptr_t *file_io_handle,
475
         int segment_index,
476
         int segment_file_index LIBAGDB_ATTRIBUTE_UNUSED,
477
         off64_t segment_offset,
478
         libcerror_error_t **error )
479
211k
{
480
211k
  static char *function = "libagdb_io_handle_seek_segment_offset";
481
482
211k
  LIBAGDB_UNREFERENCED_PARAMETER( data_handle )
483
211k
  LIBAGDB_UNREFERENCED_PARAMETER( segment_file_index )
484
485
211k
  if( libbfio_handle_seek_offset(
486
211k
       (libbfio_handle_t *) file_io_handle,
487
211k
       segment_offset,
488
211k
       SEEK_SET,
489
211k
       error ) == -1 )
490
0
  {
491
0
    libcerror_error_set(
492
0
     error,
493
0
     LIBCERROR_ERROR_DOMAIN_IO,
494
0
     LIBCERROR_IO_ERROR_SEEK_FAILED,
495
0
     "%s: unable to seek segment: %d offset: %" PRIi64 ".",
496
0
     function,
497
0
     segment_index,
498
0
     segment_offset );
499
500
0
    return( -1 );
501
0
  }
502
211k
  return( segment_offset );
503
211k
}
504