Coverage Report

Created: 2026-08-31 07:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libpff/libpff/libpff_allocation_table.c
Line
Count
Source
1
/*
2
 * Allocation table functions
3
 *
4
 * Copyright (C) 2008-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 "libpff_allocation_table.h"
28
#include "libpff_checksum.h"
29
#include "libpff_definitions.h"
30
#include "libpff_libbfio.h"
31
#include "libpff_libcdata.h"
32
#include "libpff_libcerror.h"
33
#include "libpff_libcnotify.h"
34
35
#include "pff_allocation_table.h"
36
37
/* Reads allocation table data
38
 * Returns 1 if successful or -1 on error
39
 */
40
int libpff_allocation_table_read_data(
41
     libcdata_range_list_t *unallocated_block_list,
42
     const uint8_t *data,
43
     size_t data_size,
44
     uint8_t file_type,
45
     libcerror_error_t **error )
46
0
{
47
0
  uint8_t *table_data                = NULL;
48
0
  static char *function              = "libpff_allocation_table_read_data";
49
0
  size_t allocation_block_size       = 0;
50
0
  size_t allocation_table_data_size  = 0;
51
0
  size_t unallocated_size            = 0;
52
0
  off64_t back_pointer_offset        = 0;
53
0
  off64_t unallocated_offset         = 0;
54
0
  uint32_t calculated_checksum       = 0;
55
0
  uint32_t stored_checksum           = 0;
56
0
  uint16_t table_data_index          = 0;
57
0
  uint16_t table_data_size           = 0;
58
0
  uint8_t allocation_table_entry     = 0;
59
0
  uint8_t allocation_table_type      = 0;
60
0
  uint8_t allocation_table_type_copy = 0;
61
0
  uint8_t bit_index                  = 0;
62
0
  int result                         = 0;
63
64
#if defined( HAVE_DEBUG_OUTPUT )
65
  uint64_t value_64bit               = 0;
66
  uint16_t value_16bit               = 0;
67
#endif
68
69
0
  if( unallocated_block_list == NULL )
70
0
  {
71
0
    libcerror_error_set(
72
0
     error,
73
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
74
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
75
0
     "%s: invalid unallocated block list.",
76
0
     function );
77
78
0
    return( -1 );
79
0
  }
80
0
  if( data == NULL )
81
0
  {
82
0
    libcerror_error_set(
83
0
     error,
84
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
85
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
86
0
     "%s: invalid data.",
87
0
     function );
88
89
0
    return( -1 );
90
0
  }
91
0
  if( data_size > (size_t) SSIZE_MAX )
92
0
  {
93
0
    libcerror_error_set(
94
0
     error,
95
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
96
0
     LIBCERROR_RUNTIME_ERROR_VALUE_EXCEEDS_MAXIMUM,
97
0
     "%s: invalid data size value exceeds maximum.",
98
0
     function );
99
100
0
    return( -1 );
101
0
  }
102
0
  if( ( file_type != LIBPFF_FILE_TYPE_32BIT )
103
0
   && ( file_type != LIBPFF_FILE_TYPE_64BIT )
104
0
   && ( file_type != LIBPFF_FILE_TYPE_64BIT_4K_PAGE ) )
105
0
  {
106
0
    libcerror_error_set(
107
0
     error,
108
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
109
0
     LIBCERROR_ARGUMENT_ERROR_UNSUPPORTED_VALUE,
110
0
     "%s: unsupported file type.",
111
0
     function );
112
113
0
    return( -1 );
114
0
  }
115
0
  if( file_type == LIBPFF_FILE_TYPE_32BIT )
116
0
  {
117
0
    allocation_table_data_size = sizeof( pff_allocation_table_32bit_t );
118
0
    table_data_size            = 496;
119
0
  }
120
0
  else if( file_type == LIBPFF_FILE_TYPE_64BIT )
121
0
  {
122
0
    allocation_table_data_size = sizeof( pff_allocation_table_64bit_t );
123
0
    table_data_size            = 496;
124
0
  }
125
0
  else if( file_type == LIBPFF_FILE_TYPE_64BIT_4K_PAGE )
126
0
  {
127
0
    allocation_table_data_size = sizeof( pff_allocation_table_64bit_4k_page_t );
128
0
    table_data_size            = 4072;
129
0
  }
130
0
  if( data_size < allocation_table_data_size )
131
0
  {
132
0
    libcerror_error_set(
133
0
     error,
134
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
135
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
136
0
     "%s: invalid data size value too small.",
137
0
     function );
138
139
0
    return( -1 );
140
0
  }
141
#if defined( HAVE_DEBUG_OUTPUT )
142
  if( libcnotify_verbose != 0 )
143
  {
144
    libcnotify_printf(
145
     "%s: allocation table data:\n",
146
     function );
147
    libcnotify_print_data(
148
     data,
149
     allocation_table_data_size,
150
     LIBCNOTIFY_PRINT_DATA_FLAG_GROUP_DATA );
151
  }
152
#endif
153
0
  if( file_type == LIBPFF_FILE_TYPE_32BIT )
154
0
  {
155
0
    table_data                 = ( (pff_allocation_table_32bit_t *) data )->data;
156
0
    allocation_table_type      = ( (pff_allocation_table_32bit_t *) data )->type;
157
0
    allocation_table_type_copy = ( (pff_allocation_table_32bit_t *) data )->type_copy;
158
159
0
    byte_stream_copy_to_uint32_little_endian(
160
0
     ( (pff_allocation_table_32bit_t *) data )->back_pointer,
161
0
     back_pointer_offset );
162
0
    byte_stream_copy_to_uint32_little_endian(
163
0
     ( (pff_allocation_table_32bit_t *) data )->checksum,
164
0
     stored_checksum );
165
0
  }
166
0
  else if( file_type == LIBPFF_FILE_TYPE_64BIT )
167
0
  {
168
0
    table_data                 = ( (pff_allocation_table_64bit_t *) data )->data;
169
0
    allocation_table_type      = ( (pff_allocation_table_64bit_t *) data )->type;
170
0
    allocation_table_type_copy = ( (pff_allocation_table_64bit_t *) data )->type_copy;
171
172
0
    byte_stream_copy_to_uint32_little_endian(
173
0
     ( (pff_allocation_table_64bit_t *) data )->checksum,
174
0
     stored_checksum );
175
0
    byte_stream_copy_to_uint64_little_endian(
176
0
     ( (pff_allocation_table_64bit_t *) data )->back_pointer,
177
0
     back_pointer_offset );
178
0
  }
179
0
  else if( file_type == LIBPFF_FILE_TYPE_64BIT_4K_PAGE )
180
0
  {
181
0
    table_data                 = ( (pff_allocation_table_64bit_4k_page_t *) data )->data;
182
0
    allocation_table_type      = ( (pff_allocation_table_64bit_4k_page_t *) data )->type;
183
0
    allocation_table_type_copy = ( (pff_allocation_table_64bit_4k_page_t *) data )->type_copy;
184
185
0
    byte_stream_copy_to_uint32_little_endian(
186
0
     ( (pff_allocation_table_64bit_4k_page_t *) data )->checksum,
187
0
     stored_checksum );
188
0
    byte_stream_copy_to_uint64_little_endian(
189
0
     ( (pff_allocation_table_64bit_4k_page_t *) data )->back_pointer,
190
0
     back_pointer_offset );
191
0
  }
192
#if defined( HAVE_DEBUG_OUTPUT )
193
  if( libcnotify_verbose != 0 )
194
  {
195
    libcnotify_printf(
196
     "%s: type\t\t\t\t\t: 0x%02" PRIx8 "\n",
197
     function,
198
     allocation_table_type );
199
    libcnotify_printf(
200
     "%s: type copy\t\t\t\t: 0x%02" PRIx8 "\n",
201
     function,
202
     allocation_table_type_copy );
203
204
    if( file_type == LIBPFF_FILE_TYPE_32BIT )
205
    {
206
      byte_stream_copy_to_uint16_little_endian(
207
       ( (pff_allocation_table_32bit_t *) data )->signature,
208
       value_16bit );
209
      libcnotify_printf(
210
       "%s: signature\t\t\t\t: 0x%04" PRIx16 "\n",
211
       function,
212
       value_16bit );
213
214
      libcnotify_printf(
215
       "%s: back pointer\t\t\t\t: %" PRIu64 "\n",
216
       function,
217
       back_pointer_offset );
218
219
      libcnotify_printf(
220
       "%s: checksum\t\t\t\t: 0x%" PRIx32 "\n",
221
       function,
222
       stored_checksum );
223
    }
224
    else if( ( file_type == LIBPFF_FILE_TYPE_64BIT )
225
          || ( file_type == LIBPFF_FILE_TYPE_64BIT_4K_PAGE ) )
226
    {
227
      if( file_type == LIBPFF_FILE_TYPE_64BIT )
228
      {
229
        byte_stream_copy_to_uint16_little_endian(
230
         ( (pff_allocation_table_64bit_t *) data )->signature,
231
         value_16bit );
232
      }
233
      else
234
      {
235
        byte_stream_copy_to_uint16_little_endian(
236
         ( (pff_allocation_table_64bit_4k_page_t *) data )->signature,
237
         value_16bit );
238
      }
239
      libcnotify_printf(
240
       "%s: signature\t\t\t\t: 0x%04" PRIx16 "\n",
241
       function,
242
       value_16bit );
243
244
      libcnotify_printf(
245
       "%s: checksum\t\t\t\t: 0x%" PRIx32 "\n",
246
       function,
247
       stored_checksum );
248
249
      libcnotify_printf(
250
       "%s: back pointer\t\t\t\t: %" PRIu64 "\n",
251
       function,
252
       back_pointer_offset );
253
254
      if( file_type == LIBPFF_FILE_TYPE_64BIT_4K_PAGE )
255
      {
256
        byte_stream_copy_to_uint64_little_endian(
257
         ( (pff_allocation_table_64bit_4k_page_t *) data )->unknown1,
258
         value_64bit );
259
        libcnotify_printf(
260
         "%s: unknown1\t\t\t\t: 0x%08" PRIx64 "\n",
261
         function,
262
         value_64bit );
263
      }
264
    }
265
    libcnotify_printf(
266
     "\n" );
267
  }
268
#endif /* defined( HAVE_DEBUG_OUTPUT ) */
269
270
0
  if( libpff_checksum_calculate_weak_crc32(
271
0
       &calculated_checksum,
272
0
       table_data,
273
0
       (size_t) table_data_size,
274
0
       0,
275
0
       error ) != 1 )
276
0
  {
277
0
    libcerror_error_set(
278
0
     error,
279
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
280
0
     LIBCERROR_RUNTIME_ERROR_SET_FAILED,
281
0
     "%s: unable to calculate weak CRC-32.",
282
0
     function );
283
284
0
    goto on_error;
285
0
  }
286
0
  if( stored_checksum != calculated_checksum )
287
0
  {
288
0
    libcerror_error_set(
289
0
     error,
290
0
     LIBCERROR_ERROR_DOMAIN_INPUT,
291
0
     LIBCERROR_INPUT_ERROR_CHECKSUM_MISMATCH,
292
0
     "%s: mismatch in checksum ( 0x%08" PRIx32 " != 0x%08" PRIx32 " ).",
293
0
     function,
294
0
     stored_checksum,
295
0
     calculated_checksum );
296
297
/* TODO implement error tollerance */
298
0
    goto on_error;
299
0
  }
300
0
  if( allocation_table_type != allocation_table_type_copy )
301
0
  {
302
0
    libcerror_error_set(
303
0
     error,
304
0
     LIBCERROR_ERROR_DOMAIN_INPUT,
305
0
     LIBCERROR_INPUT_ERROR_CHECKSUM_MISMATCH,
306
0
     "%s: mismatch in allocation table type ( 0x%02" PRIx8 " != 0x%02" PRIx8 " ).",
307
0
     function,
308
0
     allocation_table_type,
309
0
     allocation_table_type_copy );
310
311
/* TODO implement error tollerance */
312
0
    goto on_error;
313
0
  }
314
0
  if( ( allocation_table_type != LIBPFF_ALLOCATION_TABLE_TYPE_DATA )
315
0
   && ( allocation_table_type != LIBPFF_ALLOCATION_TABLE_TYPE_PAGE ) )
316
0
  {
317
0
    libcerror_error_set(
318
0
     error,
319
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
320
0
     LIBCERROR_ARGUMENT_ERROR_UNSUPPORTED_VALUE,
321
0
     "%s: unsupported allocation table type: 0x%08" PRIx32 "",
322
0
     function,
323
0
     allocation_table_type );
324
325
/* TODO implement error tollerance */
326
0
    goto on_error;
327
0
  }
328
0
  if( allocation_table_type == LIBPFF_ALLOCATION_TABLE_TYPE_PAGE )
329
0
  {
330
    /* The page type allocation has not yet been seen i.c.w. 4k pages */
331
0
    if( file_type == LIBPFF_FILE_TYPE_64BIT_4K_PAGE )
332
0
    {
333
0
      libcerror_error_set(
334
0
       error,
335
0
       LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
336
0
       LIBCERROR_ARGUMENT_ERROR_UNSUPPORTED_VALUE,
337
0
       "%s: unsupported file type.",
338
0
       function );
339
340
0
      goto on_error;
341
0
    }
342
0
    allocation_block_size = 512;
343
0
    back_pointer_offset  -= 0x200;
344
0
  }
345
0
  else if( allocation_table_type == LIBPFF_ALLOCATION_TABLE_TYPE_DATA )
346
0
  {
347
0
    allocation_block_size = 64;
348
0
  }
349
0
  for( table_data_index = 0;
350
0
       table_data_index < table_data_size;
351
0
       table_data_index++ )
352
0
  {
353
0
    allocation_table_entry = table_data[ table_data_index ];
354
355
0
    for( bit_index = 0;
356
0
         bit_index < 8;
357
0
         bit_index++ )
358
0
    {
359
0
      if( ( allocation_table_entry & 0x80 ) == 0 )
360
0
      {
361
0
        if( unallocated_size == 0 )
362
0
        {
363
0
          unallocated_offset = back_pointer_offset;
364
0
        }
365
0
        unallocated_size += allocation_block_size;
366
0
      }
367
0
      else if( unallocated_size > 0 )
368
0
      {
369
#if defined( HAVE_DEBUG_OUTPUT )
370
        if( libcnotify_verbose != 0 )
371
        {
372
          libcnotify_printf(
373
           "%s: unallocated block: 0x%08" PRIx64 " - 0x%08" PRIx64 " (%" PRIu64 ")\n",
374
           function,
375
           unallocated_offset,
376
           unallocated_offset + unallocated_size,
377
           unallocated_size );
378
        }
379
#endif
380
0
        result = libcdata_range_list_insert_range(
381
0
                  unallocated_block_list,
382
0
                  unallocated_offset,
383
0
                  unallocated_size,
384
0
                  NULL,
385
0
                  NULL,
386
0
                  NULL,
387
0
                  error );
388
389
0
        if( result == -1 )
390
0
        {
391
0
          libcerror_error_set(
392
0
           error,
393
0
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
394
0
           LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
395
0
           "%s: unable to append unallocated block to list.",
396
0
           function );
397
398
0
          goto on_error;
399
0
        }
400
0
        unallocated_size = 0;
401
0
      }
402
0
      allocation_table_entry <<= 1;
403
404
0
      back_pointer_offset += allocation_block_size;
405
0
    }
406
0
  }
407
0
  if( unallocated_size > 0 )
408
0
  {
409
#if defined( HAVE_DEBUG_OUTPUT )
410
    if( libcnotify_verbose != 0 )
411
    {
412
      libcnotify_printf(
413
       "%s: unallocated block: 0x%08" PRIx64 " - 0x%08" PRIx64 " (%" PRIu64 ")\n",
414
       function,
415
       unallocated_offset,
416
       unallocated_offset + unallocated_size,
417
       unallocated_size );
418
    }
419
#endif
420
0
    result = libcdata_range_list_insert_range(
421
0
              unallocated_block_list,
422
0
              unallocated_offset,
423
0
              unallocated_size,
424
0
              NULL,
425
0
              NULL,
426
0
              NULL,
427
0
              error );
428
429
0
    if( result == -1 )
430
0
    {
431
0
      libcerror_error_set(
432
0
       error,
433
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
434
0
       LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
435
0
       "%s: unable to append unallocated block to list.",
436
0
       function );
437
438
0
      goto on_error;
439
0
    }
440
0
  }
441
0
  return( 1 );
442
443
0
on_error:
444
/* TODO clear allocation table on error ? */
445
0
  return( -1 );
446
0
}
447
448
/* Reads an allocation table
449
 * Returns 1 if successful or -1 on error
450
 */
451
int libpff_allocation_table_read_file_io_handle(
452
     libcdata_range_list_t *unallocated_block_list,
453
     libbfio_handle_t *file_io_handle,
454
     off64_t allocation_table_offset,
455
     uint8_t file_type,
456
     libcerror_error_t **error )
457
0
{
458
0
  uint8_t *allocation_table_data    = NULL;
459
0
  static char *function             = "libpff_allocation_table_read_file_io_handle";
460
0
  size_t allocation_table_data_size = 0;
461
0
  ssize_t read_count                = 0;
462
463
0
  if( unallocated_block_list == NULL )
464
0
  {
465
0
    libcerror_error_set(
466
0
     error,
467
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
468
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
469
0
     "%s: invalid unallocated block list.",
470
0
     function );
471
472
0
    return( -1 );
473
0
  }
474
0
  if( file_io_handle == NULL )
475
0
  {
476
0
    libcerror_error_set(
477
0
     error,
478
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
479
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
480
0
     "%s: invalid file IO handle.",
481
0
     function );
482
483
0
    return( -1 );
484
0
  }
485
0
  if( ( file_type != LIBPFF_FILE_TYPE_32BIT )
486
0
   && ( file_type != LIBPFF_FILE_TYPE_64BIT )
487
0
   && ( file_type != LIBPFF_FILE_TYPE_64BIT_4K_PAGE ) )
488
0
  {
489
0
    libcerror_error_set(
490
0
     error,
491
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
492
0
     LIBCERROR_ARGUMENT_ERROR_UNSUPPORTED_VALUE,
493
0
     "%s: unsupported file type.",
494
0
     function );
495
496
0
    return( -1 );
497
0
  }
498
0
  if( file_type == LIBPFF_FILE_TYPE_32BIT )
499
0
  {
500
0
    allocation_table_data_size = sizeof( pff_allocation_table_32bit_t );
501
0
  }
502
0
  else if( file_type == LIBPFF_FILE_TYPE_64BIT )
503
0
  {
504
0
    allocation_table_data_size = sizeof( pff_allocation_table_64bit_t );
505
0
  }
506
0
  else if( file_type == LIBPFF_FILE_TYPE_64BIT_4K_PAGE )
507
0
  {
508
0
    allocation_table_data_size = sizeof( pff_allocation_table_64bit_4k_page_t );
509
0
  }
510
0
  allocation_table_data = (uint8_t *) memory_allocate(
511
0
                                       sizeof( uint8_t ) * allocation_table_data_size );
512
513
0
  if( allocation_table_data == NULL )
514
0
  {
515
0
    libcerror_error_set(
516
0
     error,
517
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
518
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
519
0
     "%s: unable to create alloction table data.",
520
0
     function );
521
522
0
    goto on_error;
523
0
  }
524
#if defined( HAVE_DEBUG_OUTPUT )
525
  if( libcnotify_verbose != 0 )
526
  {
527
    libcnotify_printf(
528
     "%s: reading allocation table at offset: %" PRIi64 " (0x%08" PRIx64 ")\n",
529
     function,
530
     allocation_table_offset,
531
     allocation_table_offset );
532
  }
533
#endif
534
0
  read_count = libbfio_handle_read_buffer_at_offset(
535
0
                file_io_handle,
536
0
                allocation_table_data,
537
0
                allocation_table_data_size,
538
0
                allocation_table_offset,
539
0
                error );
540
541
0
  if( read_count != (ssize_t) allocation_table_data_size )
542
0
  {
543
0
    libcerror_error_set(
544
0
     error,
545
0
     LIBCERROR_ERROR_DOMAIN_IO,
546
0
     LIBCERROR_IO_ERROR_READ_FAILED,
547
0
     "%s: unable to read allocation table data at offset: %" PRIi64 " (0x%08" PRIx64 ").",
548
0
     function,
549
0
     allocation_table_offset,
550
0
     allocation_table_offset );
551
552
0
    goto on_error;
553
0
  }
554
0
  if( libpff_allocation_table_read_data(
555
0
       unallocated_block_list,
556
0
       allocation_table_data,
557
0
       allocation_table_data_size,
558
0
       file_type,
559
0
       error ) != 1 )
560
0
  {
561
0
    libcerror_error_set(
562
0
     error,
563
0
     LIBCERROR_ERROR_DOMAIN_IO,
564
0
     LIBCERROR_IO_ERROR_READ_FAILED,
565
0
     "%s: unable to read allocation table.",
566
0
     function );
567
568
0
    goto on_error;
569
0
  }
570
0
  memory_free(
571
0
   allocation_table_data );
572
573
0
  return( 1 );
574
575
0
on_error:
576
/* TODO clear allocation table on error ? */
577
578
0
  if( allocation_table_data != NULL )
579
0
  {
580
0
    memory_free(
581
0
     allocation_table_data );
582
0
  }
583
0
  return( -1 );
584
0
}
585