Coverage Report

Created: 2026-01-13 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmdmp/libmdmp/libmdmp_io_handle.c
Line
Count
Source
1
/*
2
 * Input/Output (IO) handle functions
3
 *
4
 * Copyright (C) 2014-2025, 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 "libmdmp_debug.h"
28
#include "libmdmp_definitions.h"
29
#include "libmdmp_io_handle.h"
30
#include "libmdmp_libbfio.h"
31
#include "libmdmp_libcerror.h"
32
#include "libmdmp_libcnotify.h"
33
#include "libmdmp_stream_descriptor.h"
34
#include "libmdmp_unused.h"
35
36
#include "mdmp_file_header.h"
37
38
const char *mdmp_file_signature = "MDMP";
39
40
const uint8_t empty_streams_directory_entry[ 12 ] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
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 libmdmp_io_handle_initialize(
47
     libmdmp_io_handle_t **io_handle,
48
     libcerror_error_t **error )
49
361
{
50
361
  static char *function = "libmdmp_io_handle_initialize";
51
52
361
  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
361
  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
361
  *io_handle = memory_allocate_structure(
75
361
                libmdmp_io_handle_t );
76
77
361
  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
361
  if( memory_set(
89
361
       *io_handle,
90
361
       0,
91
361
       sizeof( libmdmp_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
361
  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
361
}
114
115
/* Frees a IO handle
116
 * Returns 1 if successful or -1 on error
117
 */
118
int libmdmp_io_handle_free(
119
     libmdmp_io_handle_t **io_handle,
120
     libcerror_error_t **error )
121
361
{
122
361
  static char *function = "libmdmp_io_handle_free";
123
124
361
  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
361
  if( *io_handle != NULL )
136
361
  {
137
361
    memory_free(
138
361
     *io_handle );
139
140
361
    *io_handle = NULL;
141
361
  }
142
361
  return( 1 );
143
361
}
144
145
/* Clears the IO handle
146
 * Returns 1 if successful or -1 on error
147
 */
148
int libmdmp_io_handle_clear(
149
     libmdmp_io_handle_t *io_handle,
150
     libcerror_error_t **error )
151
158
{
152
158
  static char *function = "libmdmp_io_handle_clear";
153
154
158
  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
158
  if( memory_set(
166
158
       io_handle,
167
158
       0,
168
158
       sizeof( libmdmp_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
158
  return( 1 );
180
158
}
181
182
/* Reads the streams directory
183
 * Returns 1 if successful or -1 on error
184
 */
185
int libmdmp_io_handle_read_streams_directory(
186
     libmdmp_io_handle_t *io_handle,
187
     libbfio_handle_t *file_io_handle,
188
     uint32_t streams_directory_offset,
189
     uint32_t number_of_streams,
190
     libcdata_array_t *streams_array,
191
     libcerror_error_t **error )
192
320
{
193
320
  libmdmp_stream_descriptor_t *stream_descriptor = NULL;
194
320
  uint8_t *streams_directory_data                = NULL;
195
320
  uint8_t *streams_directory_entry_data          = NULL;
196
320
  static char *function                          = "libmdmp_io_handle_read_streams_directory";
197
320
  size_t streams_directory_data_size             = 0;
198
320
  ssize_t read_count                             = 0;
199
320
  uint32_t stream_data_offset                    = 0;
200
320
  uint32_t stream_data_size                      = 0;
201
320
  uint32_t stream_index                          = 0;
202
320
  int entry_index                                = 0;
203
320
  int result                                     = 0;
204
205
320
  if( io_handle == NULL )
206
0
  {
207
0
    libcerror_error_set(
208
0
     error,
209
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
210
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
211
0
     "%s: invalid IO handle.",
212
0
     function );
213
214
0
    return( -1 );
215
0
  }
216
320
  if( ( number_of_streams == 0 )
217
319
   || ( number_of_streams > (size_t) ( MEMORY_MAXIMUM_ALLOCATION_SIZE / sizeof( mdmp_streams_directory_entry_t ) ) ) )
218
41
  {
219
41
    libcerror_error_set(
220
41
     error,
221
41
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
222
41
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
223
41
     "%s: invalid number of streams value out of bounds.",
224
41
     function );
225
226
41
    goto on_error;
227
41
  }
228
279
  streams_directory_data_size = sizeof( mdmp_streams_directory_entry_t ) * number_of_streams;
229
230
279
  streams_directory_data = (uint8_t *) memory_allocate(
231
279
                                        sizeof( uint8_t ) * streams_directory_data_size );
232
233
279
  if( streams_directory_data == NULL )
234
0
  {
235
0
    libcerror_error_set(
236
0
     error,
237
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
238
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
239
0
     "%s: unable to create streams directory data.",
240
0
     function );
241
242
0
    goto on_error;
243
0
  }
244
#if defined( HAVE_DEBUG_OUTPUT )
245
  if( libcnotify_verbose != 0 )
246
  {
247
    libcnotify_printf(
248
     "%s: reading streams directory at offset: %" PRIu32 " (0x%08" PRIx32 ")\n",
249
     function,
250
     streams_directory_offset,
251
     streams_directory_offset );
252
  }
253
#endif
254
279
  read_count = libbfio_handle_read_buffer_at_offset(
255
279
                file_io_handle,
256
279
                streams_directory_data,
257
279
                streams_directory_data_size,
258
279
                streams_directory_offset,
259
279
                error );
260
261
279
  if( read_count != (ssize_t) streams_directory_data_size )
262
121
  {
263
121
    libcerror_error_set(
264
121
     error,
265
121
     LIBCERROR_ERROR_DOMAIN_IO,
266
121
     LIBCERROR_IO_ERROR_READ_FAILED,
267
121
     "%s: unable to read streams directory data at offset: %" PRIu32 " (0x%08" PRIx32 ").",
268
121
     function,
269
121
     streams_directory_offset,
270
121
     streams_directory_offset );
271
272
121
    goto on_error;
273
121
  }
274
#if defined( HAVE_DEBUG_OUTPUT )
275
  if( libcnotify_verbose != 0 )
276
  {
277
    libcnotify_printf(
278
     "%s: streams directory data:\n",
279
     function );
280
    libcnotify_print_data(
281
     streams_directory_data,
282
     streams_directory_data_size,
283
     0 );
284
  }
285
#endif
286
158
  streams_directory_entry_data = streams_directory_data;
287
288
158
  for( stream_index = 0;
289
417k
       stream_index < number_of_streams;
290
417k
       stream_index++ )
291
417k
  {
292
417k
    if( stream_descriptor == NULL )
293
417k
    {
294
417k
      if( libmdmp_stream_descriptor_initialize(
295
417k
           &stream_descriptor,
296
417k
           error ) != 1 )
297
0
      {
298
0
        libcerror_error_set(
299
0
         error,
300
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
301
0
         LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
302
0
         "%s: unable to create stream descriptor.",
303
0
         function );
304
305
0
        goto on_error;
306
0
      }
307
417k
    }
308
417k
    byte_stream_copy_to_uint32_little_endian(
309
417k
     ( (mdmp_streams_directory_entry_t *) streams_directory_entry_data )->stream_type,
310
417k
     stream_descriptor->type );
311
312
417k
    byte_stream_copy_to_uint32_little_endian(
313
417k
     ( (mdmp_streams_directory_entry_t *) streams_directory_entry_data )->stream_data_size,
314
417k
     stream_data_size );
315
316
417k
    byte_stream_copy_to_uint32_little_endian(
317
417k
     ( (mdmp_streams_directory_entry_t *) streams_directory_entry_data )->stream_data_rva,
318
417k
     stream_data_offset );
319
320
#if defined( HAVE_DEBUG_OUTPUT )
321
    if( libcnotify_verbose != 0 )
322
    {
323
      libcnotify_printf(
324
       "%s: stream: %02" PRIu32 " type\t\t: %" PRIu32 " (%s)\n",
325
       function,
326
       stream_index,
327
       stream_descriptor->type,
328
       libmdmp_debug_get_stream_type(
329
        stream_descriptor->type ) );
330
331
      libcnotify_printf(
332
       "%s: stream: %02" PRIu32 " data size\t\t: %" PRIu32 "\n",
333
       function,
334
       stream_index,
335
       stream_data_size );
336
337
      libcnotify_printf(
338
       "%s: stream: %02" PRIu32 " RVA\t\t: 0x%08" PRIx32 "\n",
339
       function,
340
       stream_index,
341
       stream_data_offset );
342
    }
343
#endif
344
417k
    result = memory_compare(
345
417k
              streams_directory_entry_data,
346
417k
              empty_streams_directory_entry,
347
417k
              sizeof( mdmp_streams_directory_entry_t ) );
348
349
417k
    streams_directory_entry_data += sizeof( mdmp_streams_directory_entry_t );
350
351
417k
    if( result != 0 )
352
417k
    {
353
417k
      if( libmdmp_stream_descriptor_set_data_range(
354
417k
           stream_descriptor,
355
417k
           (off64_t) stream_data_offset,
356
417k
           (size64_t) stream_data_size,
357
417k
           error ) != 1 )
358
0
      {
359
0
        libcerror_error_set(
360
0
         error,
361
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
362
0
         LIBCERROR_RUNTIME_ERROR_SET_FAILED,
363
0
         "%s: unable to set data range in stream descriptor.",
364
0
         function );
365
366
0
        goto on_error;
367
0
      }
368
417k
      if( libcdata_array_append_entry(
369
417k
           streams_array,
370
417k
           &entry_index,
371
417k
           (intptr_t *) stream_descriptor,
372
417k
           error ) != 1 )
373
0
      {
374
0
        libcerror_error_set(
375
0
         error,
376
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
377
0
         LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
378
0
         "%s: unable to append stream descriptor to sections array.",
379
0
         function );
380
381
0
        goto on_error;
382
0
      }
383
417k
      stream_descriptor = NULL;
384
417k
    }
385
417k
  }
386
158
  if( stream_descriptor != NULL )
387
13
  {
388
13
    if( libmdmp_stream_descriptor_free(
389
13
         &stream_descriptor,
390
13
         error ) != 1 )
391
0
    {
392
0
      libcerror_error_set(
393
0
       error,
394
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
395
0
       LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
396
0
       "%s: unable to free stream descriptor.",
397
0
       function );
398
399
0
      goto on_error;
400
0
    }
401
13
  }
402
158
  memory_free(
403
158
   streams_directory_data );
404
405
158
  return( 1 );
406
407
162
on_error:
408
162
  if( stream_descriptor != NULL )
409
0
  {
410
0
    libmdmp_stream_descriptor_free(
411
0
     &stream_descriptor,
412
0
     NULL );
413
0
  }
414
162
  if( streams_directory_data != NULL )
415
121
  {
416
121
    memory_free(
417
121
     streams_directory_data );
418
121
  }
419
162
  return( -1 );
420
158
}
421
422
/* Reads the segment data into the buffer
423
 * Callback function for the section stream
424
 * Returns the number of bytes read or -1 on error
425
 */
426
ssize_t libmdmp_io_handle_read_segment_data(
427
         intptr_t *data_handle LIBMDMP_ATTRIBUTE_UNUSED,
428
         libbfio_handle_t *file_io_handle,
429
         int segment_index LIBMDMP_ATTRIBUTE_UNUSED,
430
         int segment_file_index LIBMDMP_ATTRIBUTE_UNUSED,
431
         uint8_t *segment_data,
432
         size_t segment_data_size,
433
         uint32_t segment_flags LIBMDMP_ATTRIBUTE_UNUSED,
434
         uint8_t read_flags LIBMDMP_ATTRIBUTE_UNUSED,
435
         libcerror_error_t **error )
436
0
{
437
0
  static char *function = "libmdmp_io_handle_read_segment_data";
438
0
  ssize_t read_count    = 0;
439
440
0
  LIBMDMP_UNREFERENCED_PARAMETER( data_handle )
441
0
  LIBMDMP_UNREFERENCED_PARAMETER( segment_index )
442
0
  LIBMDMP_UNREFERENCED_PARAMETER( segment_file_index )
443
0
  LIBMDMP_UNREFERENCED_PARAMETER( segment_flags )
444
0
  LIBMDMP_UNREFERENCED_PARAMETER( read_flags )
445
446
0
  read_count = libbfio_handle_read_buffer(
447
0
          file_io_handle,
448
0
          segment_data,
449
0
          segment_data_size,
450
0
          error );
451
452
0
  if( read_count != (ssize_t) segment_data_size )
453
0
  {
454
0
    libcerror_error_set(
455
0
     error,
456
0
     LIBCERROR_ERROR_DOMAIN_IO,
457
0
     LIBCERROR_IO_ERROR_READ_FAILED,
458
0
     "%s: unable to read segment data.",
459
0
     function );
460
461
0
    return( -1 );
462
0
  }
463
0
  return( read_count );
464
0
}
465
466
/* Seeks a certain segment offset
467
 * Callback function for the section stream
468
 * Returns the offset or -1 on error
469
 */
470
off64_t libmdmp_io_handle_seek_segment_offset(
471
         intptr_t *data_handle LIBMDMP_ATTRIBUTE_UNUSED,
472
         libbfio_handle_t *file_io_handle,
473
         int segment_index LIBMDMP_ATTRIBUTE_UNUSED,
474
         int segment_file_index LIBMDMP_ATTRIBUTE_UNUSED,
475
         off64_t segment_offset,
476
         libcerror_error_t **error )
477
0
{
478
0
  static char *function = "libmdmp_io_handle_seek_segment_offset";
479
480
0
  LIBMDMP_UNREFERENCED_PARAMETER( data_handle )
481
0
  LIBMDMP_UNREFERENCED_PARAMETER( segment_index )
482
0
  LIBMDMP_UNREFERENCED_PARAMETER( segment_file_index )
483
484
0
  segment_offset = libbfio_handle_seek_offset(
485
0
                    file_io_handle,
486
0
                    segment_offset,
487
0
                    SEEK_SET,
488
0
                    error );
489
490
0
  if( segment_offset == -1 )
491
0
  {
492
0
    libcerror_error_set(
493
0
     error,
494
0
     LIBCERROR_ERROR_DOMAIN_IO,
495
0
     LIBCERROR_IO_ERROR_READ_FAILED,
496
0
     "%s: unable to seek segment offset.",
497
0
     function );
498
499
0
    return( -1 );
500
0
  }
501
0
  return( segment_offset );
502
0
}
503