Coverage Report

Created: 2026-10-22 07:28

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.45k
{
50
2.45k
  static char *function = "libagdb_io_handle_initialize";
51
52
2.45k
  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.45k
  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.45k
  *io_handle = memory_allocate_structure(
75
2.45k
                libagdb_io_handle_t );
76
77
2.45k
  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.45k
  if( memory_set(
89
2.45k
       *io_handle,
90
2.45k
       0,
91
2.45k
       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.45k
  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.45k
}
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.45k
{
122
2.45k
  static char *function = "libagdb_io_handle_free";
123
124
2.45k
  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.45k
  if( *io_handle != NULL )
136
2.45k
  {
137
2.45k
    memory_free(
138
2.45k
     *io_handle );
139
140
2.45k
    *io_handle = NULL;
141
2.45k
  }
142
2.45k
  return( 1 );
143
2.45k
}
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
28
{
152
28
  static char *function = "libagdb_io_handle_clear";
153
154
28
  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
28
  if( memory_set(
166
28
       io_handle,
167
28
       0,
168
28
       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
28
  return( 1 );
180
28
}
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.24k
{
191
1.24k
  uint8_t compressed_block_data[ 4 ];
192
193
1.24k
  static char *function            = "libagdb_io_handle_read_compressed_blocks";
194
1.24k
  off64_t file_offset              = 0;
195
1.24k
  size_t read_size                 = 0;
196
1.24k
  ssize_t read_count               = 0;
197
1.24k
  uint32_t compressed_block_size   = 0;
198
1.24k
  uint32_t uncompressed_data_size  = 0;
199
1.24k
  uint32_t uncompressed_block_size = 0;
200
1.24k
  int compressed_block_index       = 0;
201
1.24k
  int element_index                = 0;
202
203
1.24k
  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.24k
  if( ( io_handle->file_type != LIBAGDB_FILE_TYPE_COMPRESSED_VISTA )
215
658
   && ( io_handle->file_type != LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS7 )
216
270
   && ( io_handle->file_type != LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS8_0 )
217
257
   && ( 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.24k
  if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_VISTA )
229
586
  {
230
586
    file_offset = 8;
231
586
    read_size   = 2;
232
586
  }
233
658
  else if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS7 )
234
388
  {
235
388
    file_offset = 8;
236
388
    read_size   = 4;
237
388
  }
238
270
  else if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS8_0 )
239
13
  {
240
13
    file_offset = 12;
241
13
    read_size   = 4;
242
13
  }
243
257
  else if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS8_1 )
244
257
  {
245
257
    file_offset = 12;
246
257
    read_size   = 4;
247
257
  }
248
1.24k
  uncompressed_data_size = io_handle->uncompressed_data_size;
249
250
1.57M
  while( (size64_t) file_offset < io_handle->file_size )
251
1.57M
  {
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.57M
    read_count = libbfio_handle_read_buffer_at_offset(
263
1.57M
                  file_io_handle,
264
1.57M
                  compressed_block_data,
265
1.57M
                  read_size,
266
1.57M
                  file_offset,
267
1.57M
                  error );
268
269
1.57M
    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.57M
    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
6
      {
292
6
        libcerror_error_set(
293
6
         error,
294
6
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
295
6
         LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
296
6
         "%s: invalid compressed block size value out of bounds.",
297
6
         function );
298
299
6
        return( -1 );
300
6
      }
301
1.57M
      compressed_block_size += 3;
302
303
1.57M
      uncompressed_block_size = io_handle->uncompressed_block_size;
304
1.57M
    }
305
3.59k
    else if( ( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS7 )
306
475
          || ( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS8_0 ) )
307
3.33k
    {
308
3.33k
      byte_stream_copy_to_uint32_little_endian(
309
3.33k
       compressed_block_data,
310
3.33k
       compressed_block_size );
311
312
3.33k
      file_offset += 4;
313
314
3.33k
      uncompressed_block_size = io_handle->uncompressed_block_size;
315
3.33k
    }
316
255
    else if( io_handle->file_type == LIBAGDB_FILE_TYPE_COMPRESSED_WINDOWS8_1 )
317
255
    {
318
255
      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
255
      compressed_block_size = (uint32_t) io_handle->file_size - 12;
330
331
255
      if( ( uncompressed_data_size % io_handle->uncompressed_block_size ) != 0 )
332
254
      {
333
254
        uncompressed_data_size /= io_handle->uncompressed_block_size;
334
254
        uncompressed_data_size += 1;
335
254
        uncompressed_data_size *= io_handle->uncompressed_block_size;
336
254
      }
337
255
      uncompressed_block_size = uncompressed_data_size;
338
255
    }
339
1.57M
    if( uncompressed_data_size < uncompressed_block_size )
340
233
    {
341
233
      uncompressed_block_size = uncompressed_data_size;
342
233
    }
343
1.57M
    else
344
1.57M
    {
345
1.57M
      uncompressed_block_size = uncompressed_block_size;
346
1.57M
    }
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.57M
    if( compressed_block_size == 0 )
358
2
    {
359
2
      libcerror_error_set(
360
2
       error,
361
2
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
362
2
       LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
363
2
       "%s: invalid compressed block size value out of bounds.",
364
2
       function );
365
366
2
      return( -1 );
367
2
    }
368
1.57M
    if( libfdata_list_append_element_with_mapped_size(
369
1.57M
         compressed_blocks_list,
370
1.57M
         &element_index,
371
1.57M
         0,
372
1.57M
         file_offset,
373
1.57M
         (size64_t) compressed_block_size,
374
1.57M
         LIBFDATA_RANGE_FLAG_IS_COMPRESSED,
375
1.57M
         uncompressed_block_size,
376
1.57M
         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.57M
    file_offset            += compressed_block_size;
389
1.57M
    uncompressed_data_size -= uncompressed_block_size;
390
391
1.57M
    if( uncompressed_data_size == 0 )
392
488
    {
393
488
      break;
394
488
    }
395
1.57M
    compressed_block_index++;
396
1.57M
  }
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.20k
  return( 1 );
411
1.24k
}
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
169k
{
428
169k
  static char *function = "libagdb_io_handle_read_segment_data";
429
169k
  ssize_t read_count    = 0;
430
431
169k
  LIBAGDB_UNREFERENCED_PARAMETER( data_handle )
432
169k
  LIBAGDB_UNREFERENCED_PARAMETER( segment_file_index )
433
169k
  LIBAGDB_UNREFERENCED_PARAMETER( segment_flags )
434
169k
  LIBAGDB_UNREFERENCED_PARAMETER( read_flags )
435
436
169k
  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
169k
  read_count = libbfio_handle_read_buffer(
448
169k
                (libbfio_handle_t *) file_io_handle,
449
169k
                segment_data,
450
169k
                segment_data_size,
451
169k
                error );
452
453
169k
  if( read_count != (ssize_t) segment_data_size )
454
430
  {
455
430
    libcerror_error_set(
456
430
     error,
457
430
     LIBCERROR_ERROR_DOMAIN_IO,
458
430
     LIBCERROR_IO_ERROR_READ_FAILED,
459
430
     "%s: unable to read segment: %d data.",
460
430
     function,
461
430
     segment_index );
462
463
430
    return( -1 );
464
430
  }
465
169k
  return( read_count );
466
169k
}
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
169k
{
480
169k
  static char *function = "libagdb_io_handle_seek_segment_offset";
481
482
169k
  LIBAGDB_UNREFERENCED_PARAMETER( data_handle )
483
169k
  LIBAGDB_UNREFERENCED_PARAMETER( segment_file_index )
484
485
169k
  if( libbfio_handle_seek_offset(
486
169k
       (libbfio_handle_t *) file_io_handle,
487
169k
       segment_offset,
488
169k
       SEEK_SET,
489
169k
       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
169k
  return( segment_offset );
503
169k
}
504