Coverage Report

Created: 2025-12-05 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmsiecf/libmsiecf/libmsiecf_io_handle.c
Line
Count
Source
1
/*
2
 * Input/Output (IO) handle
3
 *
4
 * Copyright (C) 2009-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 "libmsiecf_codepage.h"
28
#include "libmsiecf_debug.h"
29
#include "libmsiecf_definitions.h"
30
#include "libmsiecf_hash_table.h"
31
#include "libmsiecf_io_handle.h"
32
#include "libmsiecf_item_descriptor.h"
33
#include "libmsiecf_libbfio.h"
34
#include "libmsiecf_libcdata.h"
35
#include "libmsiecf_libcerror.h"
36
#include "libmsiecf_libcnotify.h"
37
#include "libmsiecf_redirected_values.h"
38
#include "libmsiecf_url_values.h"
39
40
#include "msiecf_record.h"
41
42
char *msiecf_file_signature = "Client UrlCache MMF Ver ";
43
44
/* Creates an IO handle
45
 * Make sure the value io_handle is referencing, is set to NULL
46
 * Returns 1 if successful or -1 on error
47
 */
48
int libmsiecf_io_handle_initialize(
49
     libmsiecf_io_handle_t **io_handle,
50
     libcerror_error_t **error )
51
3.22k
{
52
3.22k
  static char *function = "libmsiecf_io_handle_initialize";
53
54
3.22k
  if( io_handle == NULL )
55
0
  {
56
0
    libcerror_error_set(
57
0
     error,
58
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
59
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
60
0
     "%s: invalid IO handle.",
61
0
     function );
62
63
0
    return( -1 );
64
0
  }
65
3.22k
  if( *io_handle != NULL )
66
0
  {
67
0
    libcerror_error_set(
68
0
     error,
69
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
70
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
71
0
     "%s: invalid IO handle value already set.",
72
0
     function );
73
74
0
    return( -1 );
75
0
  }
76
3.22k
  *io_handle = memory_allocate_structure(
77
3.22k
                libmsiecf_io_handle_t );
78
79
3.22k
  if( *io_handle == NULL )
80
0
  {
81
0
    libcerror_error_set(
82
0
     error,
83
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
84
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
85
0
     "%s: unable to create IO handle.",
86
0
     function );
87
88
0
    goto on_error;
89
0
  }
90
3.22k
  if( memory_set(
91
3.22k
       *io_handle,
92
3.22k
       0,
93
3.22k
       sizeof( libmsiecf_io_handle_t ) ) == NULL )
94
0
  {
95
0
    libcerror_error_set(
96
0
     error,
97
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
98
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
99
0
     "%s: unable to clear file.",
100
0
     function );
101
102
0
    goto on_error;
103
0
  }
104
3.22k
  ( *io_handle )->block_size     = LIBMSIECF_DEFAULT_BLOCK_SIZE;
105
3.22k
  ( *io_handle )->ascii_codepage = LIBMSIECF_CODEPAGE_WINDOWS_1252;
106
107
3.22k
  return( 1 );
108
109
0
on_error:
110
0
  if( *io_handle != NULL )
111
0
  {
112
0
    memory_free(
113
0
     *io_handle );
114
115
0
    *io_handle = NULL;
116
0
  }
117
0
  return( -1 );
118
3.22k
}
119
120
/* Frees an IO handle
121
 * Returns 1 if successful or -1 on error
122
 */
123
int libmsiecf_io_handle_free(
124
     libmsiecf_io_handle_t **io_handle,
125
     libcerror_error_t **error )
126
3.22k
{
127
3.22k
  static char *function = "libmsiecf_io_handle_free";
128
3.22k
  int result            = 1;
129
130
3.22k
  if( io_handle == NULL )
131
0
  {
132
0
    libcerror_error_set(
133
0
     error,
134
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
135
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
136
0
     "%s: invalid IO handle.",
137
0
     function );
138
139
0
    return( -1 );
140
0
  }
141
3.22k
  if( *io_handle != NULL )
142
3.22k
  {
143
3.22k
    memory_free(
144
3.22k
     *io_handle );
145
146
3.22k
    *io_handle = NULL;
147
3.22k
  }
148
3.22k
  return( result );
149
3.22k
}
150
151
/* Clears an IO handle
152
 * Returns 1 if successful or -1 on error
153
 */
154
int libmsiecf_io_handle_clear(
155
     libmsiecf_io_handle_t *io_handle,
156
     libcerror_error_t **error )
157
749
{
158
749
  static char *function = "libmsiecf_io_handle_clear";
159
160
749
  if( io_handle == NULL )
161
0
  {
162
0
    libcerror_error_set(
163
0
     error,
164
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
165
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
166
0
     "%s: invalid IO handle.",
167
0
     function );
168
169
0
    return( -1 );
170
0
  }
171
749
  if( memory_set(
172
749
       io_handle,
173
749
       0,
174
749
       sizeof( libmsiecf_io_handle_t ) ) == NULL )
175
0
  {
176
0
    libcerror_error_set(
177
0
     error,
178
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
179
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
180
0
     "%s: unable to clear IO handle.",
181
0
     function );
182
183
0
    return( -1 );
184
0
  }
185
749
  io_handle->block_size     = LIBMSIECF_DEFAULT_BLOCK_SIZE;
186
749
  io_handle->ascii_codepage = LIBMSIECF_CODEPAGE_WINDOWS_1252;
187
188
749
  return( 1 );
189
749
}
190
191
/* Reads the hash table from the HASH record blocks
192
 * Returns 1 if successful or -1 on error
193
 */
194
int libmsiecf_io_handle_read_hash_table(
195
     libcdata_array_t *hash_table,
196
     libmsiecf_io_handle_t *io_handle,
197
     libbfio_handle_t *file_io_handle,
198
     off64_t hash_table_offset,
199
     libcerror_error_t **error )
200
2.46k
{
201
2.46k
  static char *function = "libmsiecf_io_handle_read_hash_table";
202
2.46k
  int recursion_depth   = 0;
203
204
2.46k
  if( io_handle == NULL )
205
0
  {
206
0
    libcerror_error_set(
207
0
     error,
208
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
209
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
210
0
     "%s: invalid IO handle.",
211
0
     function );
212
213
0
    return( -1 );
214
0
  }
215
5.48k
  while( hash_table_offset != 0 )
216
3.45k
  {
217
3.45k
    if( recursion_depth >= LIBMSIECF_MAXIMUM_BTREE_NODE_RECURSION_DEPTH )
218
10
    {
219
10
      libcerror_error_set(
220
10
       error,
221
10
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
222
10
       LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
223
10
       "%s: invalid recursion depth value out of bounds.",
224
10
       function );
225
226
10
      return( -1 );
227
10
    }
228
3.44k
    if( libmsiecf_hash_table_read_file_io_handle(
229
3.44k
         hash_table,
230
3.44k
         &hash_table_offset,
231
3.44k
         file_io_handle,
232
3.44k
         hash_table_offset,
233
3.44k
         io_handle->block_size,
234
3.44k
         error ) != 1 )
235
424
    {
236
424
      libcerror_error_set(
237
424
       error,
238
424
       LIBCERROR_ERROR_DOMAIN_IO,
239
424
       LIBCERROR_IO_ERROR_READ_FAILED,
240
424
       "%s: unable to read hash table at offset: %" PRIi64 " (0x%08" PRIx64 ").",
241
424
       function,
242
424
       hash_table_offset,
243
424
       hash_table_offset );
244
245
424
      return( -1 );
246
424
    }
247
3.02k
    recursion_depth++;
248
3.02k
  }
249
2.03k
  return( 1 );
250
2.46k
}
251
252
/* Scans the allocated part of the file for records
253
 * Returns 1 if successful or -1 on error
254
 */
255
int libmsiecf_io_handle_read_record_scan(
256
     libcdata_array_t *item_table,
257
     libcdata_array_t *recovered_item_table,
258
     libmsiecf_io_handle_t *io_handle,
259
     libbfio_handle_t *file_io_handle,
260
     off64_t file_offset,
261
     libcdata_range_list_t *unallocated_block_list,
262
     libcerror_error_t **error )
263
2.03k
{
264
2.03k
  msiecf_record_header_t record_header;
265
266
2.03k
  libmsiecf_item_descriptor_t *item_descriptor      = NULL;
267
2.03k
  libmsiecf_item_descriptor_t *last_item_descriptor = NULL;
268
2.03k
  static char *function                             = "libmsiecf_io_handle_read_record_scan";
269
2.03k
  intptr_t *value                                   = NULL;
270
2.03k
  uint64_t range_offset                             = 0;
271
2.03k
  uint64_t range_size                               = 0;
272
2.03k
  size32_t remaining_record_size                    = 0;
273
2.03k
  size32_t record_size                              = 0;
274
2.03k
  ssize_t read_count                                = 0;
275
2.03k
  uint32_t number_of_blocks                         = 0;
276
2.03k
  int item_table_entry_index                        = 0;
277
2.03k
  int detected_new_record                           = 0;
278
2.03k
  int result                                        = 0;
279
2.03k
  uint8_t item_type                                 = 0;
280
2.03k
  uint8_t new_item_type                             = 0;
281
282
#if defined( HAVE_DEBUG_OUTPUT )
283
  const char *record_type                           = NULL;
284
  int number_of_items                               = 0;
285
  int number_of_recovered_items                     = 0;
286
  int number_of_partial_recovered_items             = 0;
287
#endif
288
289
2.03k
  if( item_table == NULL )
290
0
  {
291
0
    libcerror_error_set(
292
0
     error,
293
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
294
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
295
0
     "%s: invalid item table.",
296
0
     function );
297
298
0
    return( -1 );
299
0
  }
300
2.03k
  if( recovered_item_table == NULL )
301
0
  {
302
0
    libcerror_error_set(
303
0
     error,
304
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
305
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
306
0
     "%s: invalid recovered item table.",
307
0
     function );
308
309
0
    return( -1 );
310
0
  }
311
2.03k
  if( io_handle == NULL )
312
0
  {
313
0
    libcerror_error_set(
314
0
     error,
315
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
316
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
317
0
     "%s: invalid IO handle.",
318
0
     function );
319
320
0
    return( -1 );
321
0
  }
322
202k
  while( file_offset < (off64_t) io_handle->file_size )
323
201k
  {
324
#if defined( HAVE_DEBUG_OUTPUT )
325
    if( libcnotify_verbose != 0 )
326
    {
327
      libcnotify_printf(
328
       "%s: scanning for record at offset: %" PRIi64 " (0x%08" PRIx64 ")\n",
329
       function,
330
       file_offset,
331
       file_offset );
332
    }
333
#endif
334
201k
    read_count = libbfio_handle_read_buffer_at_offset(
335
201k
            file_io_handle,
336
201k
            (uint8_t *) &record_header,
337
201k
            sizeof( msiecf_record_header_t ),
338
201k
            file_offset,
339
201k
            error );
340
341
201k
    if( read_count != (ssize_t) sizeof( msiecf_record_header_t ) )
342
1.28k
    {
343
1.28k
      libcerror_error_set(
344
1.28k
       error,
345
1.28k
       LIBCERROR_ERROR_DOMAIN_IO,
346
1.28k
       LIBCERROR_IO_ERROR_READ_FAILED,
347
1.28k
       "%s: unable to read record header at offset: %" PRIi64 " (0x%08" PRIx64 ").",
348
1.28k
       function,
349
1.28k
       file_offset,
350
1.28k
       file_offset );
351
352
1.28k
      goto on_error;
353
1.28k
    }
354
#if defined( HAVE_DEBUG_OUTPUT )
355
    if( libcnotify_verbose != 0 )
356
    {
357
      libcnotify_printf(
358
       "%s: record header:\n",
359
       function );
360
      libcnotify_print_data(
361
       (uint8_t *) &record_header,
362
       sizeof( msiecf_record_header_t ),
363
       0 );
364
    }
365
#endif
366
200k
    detected_new_record = 0;
367
368
    /* The most common record type is checked first
369
     */
370
200k
    if( ( record_header.signature[ 0 ] == (uint8_t) 'U' )
371
10.2k
     && ( record_header.signature[ 1 ] == (uint8_t) 'R' )
372
5.75k
     && ( record_header.signature[ 2 ] == (uint8_t) 'L' )
373
3.82k
     && ( record_header.signature[ 3 ] == (uint8_t) ' ' ) )
374
3.27k
    {
375
3.27k
      byte_stream_copy_to_uint32_little_endian(
376
3.27k
       record_header.number_of_blocks,
377
3.27k
       number_of_blocks );
378
379
3.27k
      new_item_type       = LIBMSIECF_ITEM_TYPE_URL;
380
3.27k
      detected_new_record = 1;
381
382
#if defined( HAVE_DEBUG_OUTPUT )
383
      record_type = "URL";
384
#endif
385
3.27k
    }
386
197k
    else if( ( record_header.signature[ 0 ] == (uint8_t) 'H' )
387
3.41k
          && ( record_header.signature[ 1 ] == (uint8_t) 'A' )
388
1.75k
          && ( record_header.signature[ 2 ] == (uint8_t) 'S' )
389
1.32k
          && ( record_header.signature[ 3 ] == (uint8_t) 'H' ) )
390
765
    {
391
765
      byte_stream_copy_to_uint32_little_endian(
392
765
       record_header.number_of_blocks,
393
765
       number_of_blocks );
394
395
765
      new_item_type       = LIBMSIECF_ITEM_TYPE_UNDEFINED;
396
765
      detected_new_record = 1;
397
398
#if defined( HAVE_DEBUG_OUTPUT )
399
      record_type = "HASH";
400
#endif
401
765
    }
402
196k
    else if( ( record_header.signature[ 0 ] == (uint8_t) 'R' )
403
8.63k
          && ( record_header.signature[ 1 ] == (uint8_t) 'E' )
404
4.30k
          && ( record_header.signature[ 2 ] == (uint8_t) 'D' )
405
3.71k
          && ( record_header.signature[ 3 ] == (uint8_t) 'R' ) )
406
3.10k
    {
407
3.10k
      byte_stream_copy_to_uint32_little_endian(
408
3.10k
       record_header.number_of_blocks,
409
3.10k
       number_of_blocks );
410
411
3.10k
      new_item_type       = LIBMSIECF_ITEM_TYPE_REDIRECTED;
412
3.10k
      detected_new_record = 1;
413
414
#if defined( HAVE_DEBUG_OUTPUT )
415
      record_type = "REDR";
416
#endif
417
3.10k
    }
418
193k
    else if( ( record_header.signature[ 0 ] == (uint8_t) 'L' )
419
7.63k
          && ( record_header.signature[ 1 ] == (uint8_t) 'E' )
420
5.42k
          && ( record_header.signature[ 2 ] == (uint8_t) 'A' )
421
4.59k
          && ( record_header.signature[ 3 ] == (uint8_t) 'K' ) )
422
4.17k
    {
423
4.17k
      byte_stream_copy_to_uint32_little_endian(
424
4.17k
       record_header.number_of_blocks,
425
4.17k
       number_of_blocks );
426
427
4.17k
      new_item_type       = LIBMSIECF_ITEM_TYPE_LEAK;
428
4.17k
      detected_new_record = 1;
429
430
#if defined( HAVE_DEBUG_OUTPUT )
431
      record_type = "LEAK";
432
#endif
433
4.17k
    }
434
#ifdef INVALIDDATARESET
435
    else if( ( record_header.signature[ 0 ] == 0x0d )
436
          && ( record_header.signature[ 1 ] == 0xf0 )
437
          && ( record_header.signature[ 2 ] == 0xad )
438
          && ( record_header.signature[ 3 ] == 0x0b ) )
439
    {
440
      number_of_blocks    = 1;
441
      new_item_type       = LIBMSIECF_ITEM_TYPE_UNDEFINED;
442
      detected_new_record = 1;
443
    }
444
    else if( ( record_header.signature[ 0 ] == 0xef )
445
          && ( record_header.signature[ 1 ] == 0xbe )
446
          && ( record_header.signature[ 2 ] == 0xad )
447
          && ( record_header.signature[ 3 ] == 0xde ) )
448
    {
449
      number_of_blocks    = 1;
450
      new_item_type       = LIBMSIECF_ITEM_TYPE_UNDEFINED;
451
      detected_new_record = 1;
452
    }
453
#endif
454
189k
    else if( remaining_record_size == 0 )
455
184k
    {
456
184k
      number_of_blocks = 1;
457
184k
      item_type        = LIBMSIECF_ITEM_TYPE_UNDEFINED;
458
184k
    }
459
200k
    if( ( number_of_blocks == 0 )
460
198k
     || ( number_of_blocks > ( ( io_handle->file_size - file_offset ) / io_handle->block_size ) ) )
461
9.30k
    {
462
9.30k
      number_of_blocks = 1;
463
9.30k
    }
464
200k
    if( last_item_descriptor != NULL )
465
9.45k
    {
466
      /* Flag the previous item descriptor it can contain an invalid
467
       * number of blocks value
468
       */
469
9.45k
      if( ( remaining_record_size == 0 )
470
8.63k
       && ( detected_new_record == 0 ) )
471
3.23k
      {
472
#if defined( HAVE_DEBUG_OUTPUT )
473
        if( libcnotify_verbose != 0 )
474
        {
475
          libcnotify_printf(
476
           "%s: marking record as tainted.\n",
477
           function );
478
        }
479
#endif
480
3.23k
        last_item_descriptor->flags |= LIBMSIECF_ITEM_FLAG_TAINTED;
481
3.23k
      }
482
9.45k
      last_item_descriptor = NULL;
483
9.45k
    }
484
200k
    if( detected_new_record != 0 )
485
11.3k
    {
486
      /* Add an unallocated partial item
487
       */
488
11.3k
      if( remaining_record_size != 0 )
489
890
      {
490
890
        if( libmsiecf_item_descriptor_initialize(
491
890
             &item_descriptor,
492
890
             error ) != 1 )
493
0
        {
494
0
          libcerror_error_set(
495
0
           error,
496
0
           LIBCERROR_ERROR_DOMAIN_MEMORY,
497
0
           LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
498
0
           "%s: unable to create item descriptor.",
499
0
           function );
500
501
0
          goto on_error;
502
0
        }
503
890
        if( item_descriptor == NULL )
504
0
        {
505
0
          libcerror_error_set(
506
0
           error,
507
0
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
508
0
           LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
509
0
           "%s: missing item descriptor.",
510
0
           function );
511
512
0
          goto on_error;
513
0
        }
514
890
        item_descriptor->type        = item_type;
515
890
        item_descriptor->record_size = record_size - remaining_record_size;
516
890
        item_descriptor->file_offset = file_offset - item_descriptor->record_size;
517
890
        item_descriptor->flags       = LIBMSIECF_ITEM_FLAG_RECOVERED
518
890
                                     | LIBMSIECF_ITEM_FLAG_PARTIAL;
519
520
#if defined( HAVE_DEBUG_OUTPUT )
521
        if( libcnotify_verbose != 0 )
522
        {
523
          libcnotify_printf(
524
           "%s: partial item at offset: %" PRIi64 " (0x%08" PRIx64 ")\n",
525
           function,
526
           file_offset - item_descriptor->record_size,
527
           file_offset - item_descriptor->record_size );
528
        }
529
#endif
530
890
        if( libcdata_array_append_entry(
531
890
             recovered_item_table,
532
890
             &item_table_entry_index,
533
890
             (intptr_t *) item_descriptor,
534
890
             error ) != 1 )
535
0
        {
536
0
          libcerror_error_set(
537
0
           error,
538
0
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
539
0
           LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
540
0
           "%s: unable to append item descriptor to recovered item table.",
541
0
           function );
542
543
0
          goto on_error;
544
0
        }
545
890
        last_item_descriptor = item_descriptor;
546
890
        item_descriptor      = NULL;
547
548
#if defined( HAVE_DEBUG_OUTPUT )
549
        number_of_partial_recovered_items++;
550
#endif
551
890
      }
552
11.3k
      item_type             = new_item_type;
553
11.3k
      remaining_record_size = 0;
554
11.3k
    }
555
200k
    if( remaining_record_size == 0 )
556
195k
    {
557
195k
      record_size = number_of_blocks * io_handle->block_size;
558
559
195k
      result = libcdata_range_list_get_range_at_offset(
560
195k
          unallocated_block_list,
561
195k
          (uint64_t) file_offset,
562
195k
          &range_offset,
563
195k
          &range_size,
564
195k
          &value,
565
195k
          error );
566
567
195k
      if( result == -1 )
568
0
      {
569
0
        libcerror_error_set(
570
0
         error,
571
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
572
0
         LIBCERROR_RUNTIME_ERROR_GET_FAILED,
573
0
         "%s: unable to determine if record offset range is unallocated.",
574
0
         function );
575
576
0
        goto on_error;
577
0
      }
578
#if defined( HAVE_DEBUG_OUTPUT )
579
      if( libcnotify_verbose != 0 )
580
      {
581
        if( record_type != NULL )
582
        {
583
          libcnotify_printf(
584
           "%s: found ",
585
           function );
586
587
          if( result != 0 )
588
          {
589
            libcnotify_printf(
590
             "unallocated " );
591
          }
592
          libcnotify_printf(
593
           "%s record of size: %" PRIu32 " at offset: %" PRIi64 " (0x%08" PRIx64 ")\n",
594
           function,
595
           record_size,
596
           file_offset,
597
           file_offset );
598
599
          libcnotify_printf(
600
           "\n" );
601
        }
602
      }
603
#endif /* defined( HAVE_DEBUG_OUTPUT ) */
604
605
195k
      if( item_type != LIBMSIECF_ITEM_TYPE_UNDEFINED )
606
10.5k
      {
607
        /* Add an allocated item
608
         */
609
10.5k
        if( result == 0 )
610
7.66k
        {
611
7.66k
          if( libmsiecf_item_descriptor_initialize(
612
7.66k
               &item_descriptor,
613
7.66k
               error ) != 1 )
614
0
          {
615
0
            libcerror_error_set(
616
0
             error,
617
0
             LIBCERROR_ERROR_DOMAIN_MEMORY,
618
0
             LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
619
0
             "%s: unable to create item descriptor.",
620
0
             function );
621
622
0
            goto on_error;
623
0
          }
624
7.66k
          if( item_descriptor == NULL )
625
0
          {
626
0
            libcerror_error_set(
627
0
             error,
628
0
             LIBCERROR_ERROR_DOMAIN_RUNTIME,
629
0
             LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
630
0
             "%s: missing item descriptor.",
631
0
             function );
632
633
0
            goto on_error;
634
0
          }
635
7.66k
          item_descriptor->type        = item_type;
636
7.66k
          item_descriptor->file_offset = file_offset;
637
7.66k
          item_descriptor->record_size = record_size;
638
7.66k
          item_descriptor->flags       = 0;
639
640
7.66k
          if( libcdata_array_append_entry(
641
7.66k
               item_table,
642
7.66k
               &item_table_entry_index,
643
7.66k
               (intptr_t *) item_descriptor,
644
7.66k
               error ) != 1 )
645
0
          {
646
0
            libcerror_error_set(
647
0
             error,
648
0
             LIBCERROR_ERROR_DOMAIN_RUNTIME,
649
0
             LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
650
0
             "%s: unable to append item descriptor to item table.",
651
0
             function );
652
653
0
            goto on_error;
654
0
          }
655
7.66k
          last_item_descriptor = item_descriptor;
656
7.66k
          item_descriptor      = NULL;
657
658
7.66k
          item_type = LIBMSIECF_ITEM_TYPE_UNDEFINED;
659
#if defined( HAVE_DEBUG_OUTPUT )
660
          record_type = NULL;
661
662
          number_of_items++;
663
#endif
664
7.66k
        }
665
        /* Make sure the unallocated record is not partially overwritten
666
         * by another record
667
         */
668
2.89k
        else
669
2.89k
        {
670
2.89k
          remaining_record_size = record_size;
671
2.89k
        }
672
10.5k
      }
673
195k
    }
674
200k
    if( remaining_record_size == 0 )
675
193k
    {
676
193k
      file_offset += (off64_t) record_size;
677
193k
    }
678
7.41k
    else
679
7.41k
    {
680
7.41k
      file_offset           += (size32_t) io_handle->block_size;
681
7.41k
      remaining_record_size -= (size32_t) io_handle->block_size;
682
683
      /* Add an unallocated item
684
       */
685
7.41k
      if( remaining_record_size == 0 )
686
1.81k
      {
687
1.81k
        if( libmsiecf_item_descriptor_initialize(
688
1.81k
             &item_descriptor,
689
1.81k
             error ) != 1 )
690
0
        {
691
0
          libcerror_error_set(
692
0
           error,
693
0
           LIBCERROR_ERROR_DOMAIN_MEMORY,
694
0
           LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
695
0
           "%s: unable to create item descriptor.",
696
0
           function );
697
698
0
          goto on_error;
699
0
        }
700
1.81k
        if( item_descriptor == NULL )
701
0
        {
702
0
          libcerror_error_set(
703
0
           error,
704
0
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
705
0
           LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
706
0
           "%s: missing item descriptor.",
707
0
           function );
708
709
0
          goto on_error;
710
0
        }
711
1.81k
        item_descriptor->type        = item_type;
712
1.81k
        item_descriptor->file_offset = file_offset - record_size;
713
1.81k
        item_descriptor->record_size = record_size;
714
1.81k
        item_descriptor->flags       = LIBMSIECF_ITEM_FLAG_RECOVERED;
715
716
1.81k
        if( libcdata_array_append_entry(
717
1.81k
             recovered_item_table,
718
1.81k
             &item_table_entry_index,
719
1.81k
             (intptr_t *) item_descriptor,
720
1.81k
             error ) != 1 )
721
0
        {
722
0
          libcerror_error_set(
723
0
           error,
724
0
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
725
0
           LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
726
0
           "%s: unable to append item descriptor to recovered item table.",
727
0
           function );
728
729
0
          goto on_error;
730
0
        }
731
1.81k
        last_item_descriptor = item_descriptor;
732
1.81k
        item_descriptor      = NULL;
733
734
1.81k
        item_type = LIBMSIECF_ITEM_TYPE_UNDEFINED;
735
736
#if defined( HAVE_DEBUG_OUTPUT )
737
        number_of_recovered_items++;
738
739
        record_type = NULL;
740
#endif
741
1.81k
      }
742
7.41k
    }
743
200k
  }
744
#if defined( HAVE_DEBUG_OUTPUT )
745
  if( libcnotify_verbose != 0 )
746
  {
747
    libcnotify_printf(
748
     "%s: found %d items.\n",
749
     function,
750
     number_of_items );
751
752
    libcnotify_printf(
753
     "%s: recovered %d items (partial recovered items %d).\n",
754
     function,
755
     number_of_recovered_items + number_of_partial_recovered_items,
756
     number_of_partial_recovered_items );
757
758
    libcnotify_printf(
759
     "\n" );
760
  }
761
#endif /* defined( HAVE_DEBUG_OUTPUT ) */
762
763
749
  return( 1 );
764
765
1.28k
on_error:
766
1.28k
  if( item_descriptor != NULL )
767
0
  {
768
0
    libmsiecf_item_descriptor_free(
769
0
     &item_descriptor,
770
     NULL );
771
0
  }
772
1.28k
  return( -1 );
773
2.03k
}
774