Coverage Report

Created: 2026-10-22 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libfsfat/libfsfat/libfsfat_allocation_table.c
Line
Count
Source
1
/*
2
 * Allocation table functions
3
 *
4
 * Copyright (C) 2021-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 "libfsfat_allocation_table.h"
28
#include "libfsfat_debug.h"
29
#include "libfsfat_definitions.h"
30
#include "libfsfat_io_handle.h"
31
#include "libfsfat_libbfio.h"
32
#include "libfsfat_libcerror.h"
33
#include "libfsfat_libcnotify.h"
34
35
/* Creates an allocation table
36
 * Make sure the value allocation_table is referencing, is set to NULL
37
 * Returns 1 if successful or -1 on error
38
 */
39
int libfsfat_allocation_table_initialize(
40
     libfsfat_allocation_table_t **allocation_table,
41
     uint32_t total_number_of_clusters,
42
     libcerror_error_t **error )
43
3.46k
{
44
3.46k
  static char *function            = "libfsfat_allocation_table_initialize";
45
3.46k
  size_t cluster_numbers_data_size = 0;
46
47
3.46k
  if( allocation_table == NULL )
48
0
  {
49
0
    libcerror_error_set(
50
0
     error,
51
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
52
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
53
0
     "%s: invalid allocation table.",
54
0
     function );
55
56
0
    return( -1 );
57
0
  }
58
3.46k
  if( *allocation_table != NULL )
59
0
  {
60
0
    libcerror_error_set(
61
0
     error,
62
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
63
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
64
0
     "%s: invalid allocation table value already set.",
65
0
     function );
66
67
0
    return( -1 );
68
0
  }
69
3.46k
  if( ( total_number_of_clusters == 0 )
70
3.46k
   || ( total_number_of_clusters > (uint32_t) LIBFSFAT_MAXIMUM_NUMBER_OF_CLUSTERS_IN_FAT ) )
71
138
  {
72
138
    libcerror_error_set(
73
138
     error,
74
138
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
75
138
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
76
138
     "%s: invalid total number of clusters value out of bounds.",
77
138
     function );
78
79
138
    return( -1 );
80
138
  }
81
3.32k
  *allocation_table = memory_allocate_structure(
82
3.32k
                       libfsfat_allocation_table_t );
83
84
3.32k
  if( *allocation_table == NULL )
85
0
  {
86
0
    libcerror_error_set(
87
0
     error,
88
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
89
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
90
0
     "%s: unable to create allocation table.",
91
0
     function );
92
93
0
    goto on_error;
94
0
  }
95
3.32k
  if( memory_set(
96
3.32k
       *allocation_table,
97
3.32k
       0,
98
3.32k
       sizeof( libfsfat_allocation_table_t ) ) == NULL )
99
0
  {
100
0
    libcerror_error_set(
101
0
     error,
102
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
103
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
104
0
     "%s: unable to clear allocation table.",
105
0
     function );
106
107
0
    memory_free(
108
0
     *allocation_table );
109
110
0
    *allocation_table = NULL;
111
112
0
    return( -1 );
113
0
  }
114
3.32k
  cluster_numbers_data_size = sizeof( uint32_t ) * total_number_of_clusters;
115
116
3.32k
  ( *allocation_table )->cluster_numbers = (uint32_t *) memory_allocate(
117
3.32k
                                                         cluster_numbers_data_size );
118
119
3.32k
  if( ( *allocation_table )->cluster_numbers == NULL )
120
0
  {
121
0
    libcerror_error_set(
122
0
     error,
123
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
124
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
125
0
     "%s: unable to create cluster numbers.",
126
0
     function );
127
128
0
    goto on_error;
129
0
  }
130
3.32k
  ( *allocation_table )->number_of_cluster_numbers = total_number_of_clusters;
131
132
3.32k
  if( memory_set(
133
3.32k
       ( *allocation_table )->cluster_numbers,
134
3.32k
       0,
135
3.32k
       cluster_numbers_data_size ) == NULL )
136
0
  {
137
0
    libcerror_error_set(
138
0
     error,
139
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
140
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
141
0
     "%s: unable to clear cluster numbers.",
142
0
     function );
143
144
0
    goto on_error;
145
0
  }
146
3.32k
  return( 1 );
147
148
0
on_error:
149
0
  if( *allocation_table != NULL )
150
0
  {
151
0
    if( ( *allocation_table )->cluster_numbers != NULL )
152
0
    {
153
0
      memory_free(
154
0
       ( *allocation_table )->cluster_numbers );
155
0
    }
156
0
    memory_free(
157
0
     *allocation_table );
158
159
0
    *allocation_table = NULL;
160
0
  }
161
0
  return( -1 );
162
3.32k
}
163
164
/* Frees an allocation table
165
 * Returns 1 if successful or -1 on error
166
 */
167
int libfsfat_allocation_table_free(
168
     libfsfat_allocation_table_t **allocation_table,
169
     libcerror_error_t **error )
170
3.32k
{
171
3.32k
  static char *function = "libfsfat_allocation_table_free";
172
173
3.32k
  if( allocation_table == NULL )
174
0
  {
175
0
    libcerror_error_set(
176
0
     error,
177
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
178
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
179
0
     "%s: invalid allocation table.",
180
0
     function );
181
182
0
    return( -1 );
183
0
  }
184
3.32k
  if( *allocation_table != NULL )
185
3.32k
  {
186
3.32k
    if( ( *allocation_table )->cluster_numbers != NULL )
187
3.32k
    {
188
3.32k
      memory_free(
189
3.32k
       ( *allocation_table )->cluster_numbers );
190
3.32k
    }
191
3.32k
    memory_free(
192
3.32k
     *allocation_table );
193
194
3.32k
    *allocation_table = NULL;
195
3.32k
  }
196
3.32k
  return( 1 );
197
3.32k
}
198
199
/* Reads an allocation table
200
 * Returns 1 if successful or -1 on error
201
 */
202
int libfsfat_allocation_table_read_file_io_handle(
203
     libfsfat_allocation_table_t *allocation_table,
204
     libfsfat_allocation_table_t *reversed_allocation_table,
205
     libfsfat_io_handle_t *io_handle,
206
     libbfio_handle_t *file_io_handle,
207
     off64_t file_offset,
208
     size64_t size,
209
     libcerror_error_t **error )
210
1.92k
{
211
1.92k
  uint8_t *table_data      = NULL;
212
1.92k
  static char *function    = "libfsfat_allocation_table_read_file_io_handle";
213
1.92k
  size_t read_offset       = 0;
214
1.92k
  size_t read_size         = 0;
215
1.92k
  size_t table_data_offset = 0;
216
1.92k
  size_t table_offset      = 0;
217
1.92k
  ssize_t read_count       = 0;
218
1.92k
  uint32_t cluster_number  = 0;
219
1.92k
  int table_index          = 0;
220
221
1.92k
  if( allocation_table == NULL )
222
0
  {
223
0
    libcerror_error_set(
224
0
     error,
225
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
226
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
227
0
     "%s: invalid allocation table.",
228
0
     function );
229
230
0
    return( -1 );
231
0
  }
232
1.92k
  if( io_handle == NULL )
233
0
  {
234
0
    libcerror_error_set(
235
0
     error,
236
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
237
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
238
0
     "%s: invalid IO handle.",
239
0
     function );
240
241
0
    return( -1 );
242
0
  }
243
1.92k
  if( io_handle->bytes_per_sector == 0 )
244
0
  {
245
0
    libcerror_error_set(
246
0
     error,
247
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
248
0
     LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
249
0
     "%s: invalid IO handle - missing bytes per sector.",
250
0
     function );
251
252
0
    return( -1 );
253
0
  }
254
  /* Ensure we can read 3 bytes from the table data buffer
255
   */
256
1.92k
  table_data = (uint8_t *) memory_allocate(
257
1.92k
                            (size_t) io_handle->bytes_per_sector + 3 );
258
259
1.92k
  if( table_data == NULL )
260
0
  {
261
0
    libcerror_error_set(
262
0
     error,
263
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
264
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
265
0
     "%s: unable to create table data.",
266
0
     function );
267
268
0
    goto on_error;
269
0
  }
270
1.92k
  table_data[ io_handle->bytes_per_sector     ] = 0;
271
1.92k
  table_data[ io_handle->bytes_per_sector + 1 ] = 0;
272
273
6.01k
  while( table_offset < size )
274
4.58k
  {
275
4.58k
    read_size = io_handle->bytes_per_sector;
276
277
4.58k
    if( read_size > ( size - table_offset ) )
278
0
    {
279
0
      read_size = (size_t) size - table_offset;
280
0
    }
281
4.58k
    read_count = libbfio_handle_read_buffer_at_offset(
282
4.58k
                  file_io_handle,
283
4.58k
                  &( table_data[ read_offset ] ),
284
4.58k
                  read_size,
285
4.58k
                  file_offset,
286
4.58k
                  error );
287
288
4.58k
    if( read_count != (ssize_t) read_size )
289
276
    {
290
276
      libcerror_error_set(
291
276
       error,
292
276
       LIBCERROR_ERROR_DOMAIN_IO,
293
276
       LIBCERROR_IO_ERROR_READ_FAILED,
294
276
       "%s: unable to read allocation table data at offset: %" PRIi64 " (0x%08" PRIx64 ").",
295
276
       function,
296
276
       file_offset,
297
276
       file_offset );
298
299
276
      goto on_error;
300
276
    }
301
#if defined( HAVE_DEBUG_OUTPUT )
302
    if( libcnotify_verbose != 0 )
303
    {
304
      libcnotify_printf(
305
       "%s: allocation table data:\n",
306
       function );
307
      libcnotify_print_data(
308
       table_data,
309
       read_size,
310
       LIBCNOTIFY_PRINT_DATA_FLAG_GROUP_DATA );
311
    }
312
#endif
313
4.31k
    file_offset  += read_size;
314
4.31k
    table_offset += read_size;
315
316
4.31k
    table_data_offset = 0;
317
4.31k
    read_size        += read_offset;
318
319
1.14M
    while( table_data_offset < read_size )
320
1.14M
    {
321
1.14M
      if( table_index >= allocation_table->number_of_cluster_numbers )
322
160
      {
323
160
        break;
324
160
      }
325
1.14M
      if( io_handle->file_system_format == LIBFSFAT_FILE_SYSTEM_FORMAT_FAT12 )
326
59.1k
      {
327
59.1k
        byte_stream_copy_to_uint24_little_endian(
328
59.1k
         &( table_data[ table_data_offset ] ),
329
59.1k
         cluster_number );
330
331
59.1k
        table_data_offset += 3;
332
333
#if defined( HAVE_DEBUG_OUTPUT )
334
        if( libcnotify_verbose != 0 )
335
        {
336
          libcnotify_printf(
337
           "%s: table data: % 4d number\t: 0x%08" PRIx32 "\n",
338
           function,
339
           table_index,
340
           cluster_number );
341
342
          if( ( cluster_number & 0x00000fffUL ) >= 0x00000ff0UL )
343
          {
344
            libcnotify_printf(
345
             "%s: cluster: % 4d number\t: 0x%03" PRIx32 " (%s)\n",
346
             function,
347
             table_index,
348
             cluster_number & 0x00000fffUL,
349
             libfsfat_debug_print_fat12_cluster_type(
350
              cluster_number & 0x00000fffUL ) );
351
          }
352
          else if( ( cluster_number & 0x00000fffUL ) != 0 )
353
          {
354
            libcnotify_printf(
355
             "%s: cluster: % 4d number\t: %" PRIu32 "\n",
356
             function,
357
             table_index,
358
             cluster_number & 0x00000fffUL );
359
          }
360
        }
361
#endif
362
59.1k
        allocation_table->cluster_numbers[ table_index ] = cluster_number & 0x00000fffUL;
363
364
59.1k
        if( ( reversed_allocation_table != NULL )
365
59.1k
         && ( cluster_number < (uint32_t) allocation_table->number_of_cluster_numbers ) )
366
14.2k
        {
367
14.2k
          reversed_allocation_table->cluster_numbers[ cluster_number & 0x00000fffUL ] = table_index;
368
14.2k
        }
369
59.1k
        table_index++;
370
371
59.1k
        if( table_index >= allocation_table->number_of_cluster_numbers )
372
62
        {
373
62
          break;
374
62
        }
375
59.1k
        cluster_number >>= 12;
376
377
#if defined( HAVE_DEBUG_OUTPUT )
378
        if( libcnotify_verbose != 0 )
379
        {
380
          if( cluster_number >= 0x00000ff0UL )
381
          {
382
            libcnotify_printf(
383
             "%s: cluster: % 4d number\t: 0x%03" PRIx32 " (%s)\n",
384
             function,
385
             table_index,
386
             cluster_number,
387
             libfsfat_debug_print_fat12_cluster_type(
388
              cluster_number ) );
389
          }
390
          else if( cluster_number != 0 )
391
          {
392
            libcnotify_printf(
393
             "%s: cluster: % 4d number\t: %" PRIu32 "\n",
394
             function,
395
             table_index,
396
             cluster_number );
397
          }
398
        }
399
#endif
400
59.1k
      }
401
1.08M
      else if( io_handle->file_system_format == LIBFSFAT_FILE_SYSTEM_FORMAT_FAT16 )
402
278k
      {
403
278k
        byte_stream_copy_to_uint16_little_endian(
404
278k
         &( table_data[ table_data_offset ] ),
405
278k
         cluster_number );
406
407
278k
        table_data_offset += 2;
408
409
#if defined( HAVE_DEBUG_OUTPUT )
410
        if( libcnotify_verbose != 0 )
411
        {
412
          if( cluster_number >= 0x0000fff0UL )
413
          {
414
            libcnotify_printf(
415
             "%s: cluster: %04d number\t: 0x%04" PRIx32 " (%s)\n",
416
             function,
417
             table_index,
418
             cluster_number,
419
             libfsfat_debug_print_fat16_cluster_type(
420
              cluster_number ) );
421
          }
422
          else if( cluster_number != 0 )
423
          {
424
            libcnotify_printf(
425
             "%s: cluster: %04d number\t: %" PRIu32 "\n",
426
             function,
427
             table_index,
428
             cluster_number );
429
          }
430
        }
431
#endif
432
278k
      }
433
803k
      else
434
803k
      {
435
803k
        byte_stream_copy_to_uint32_little_endian(
436
803k
         &( table_data[ table_data_offset ] ),
437
803k
         cluster_number );
438
439
803k
        table_data_offset += 4;
440
441
#if defined( HAVE_DEBUG_OUTPUT )
442
        if( libcnotify_verbose != 0 )
443
        {
444
          if( cluster_number >= 0x0ffffff0UL )
445
          {
446
            if( io_handle->file_system_format == LIBFSFAT_FILE_SYSTEM_FORMAT_FAT32 )
447
            {
448
              libcnotify_printf(
449
               "%s: cluster: %04d number\t: 0x%08" PRIx32 " (%s)\n",
450
               function,
451
               table_index,
452
               cluster_number,
453
               libfsfat_debug_print_fat32_cluster_type(
454
                cluster_number ) );
455
            }
456
            else
457
            {
458
              libcnotify_printf(
459
               "%s: cluster: %04d number\t: 0x%08" PRIx32 " (%s)\n",
460
               function,
461
               table_index,
462
               cluster_number,
463
               libfsfat_debug_print_exfat_cluster_type(
464
                cluster_number ) );
465
            }
466
          }
467
          else if( cluster_number != 0 )
468
          {
469
            libcnotify_printf(
470
             "%s: cluster: %04d number\t: %" PRIu32 "\n",
471
             function,
472
             table_index,
473
             cluster_number );
474
          }
475
        }
476
#endif /* defined( HAVE_DEBUG_OUTPUT ) */
477
803k
      }
478
1.14M
      allocation_table->cluster_numbers[ table_index ] = cluster_number;
479
480
1.14M
      if( ( reversed_allocation_table != NULL )
481
1.13M
       && ( cluster_number < (uint32_t) allocation_table->number_of_cluster_numbers ) )
482
245k
      {
483
245k
        reversed_allocation_table->cluster_numbers[ cluster_number ] = table_index;
484
245k
      }
485
1.14M
      table_index++;
486
487
1.14M
      if( io_handle->file_system_format == LIBFSFAT_FILE_SYSTEM_FORMAT_FAT12 )
488
59.1k
      {
489
59.1k
        if( table_data_offset > ( read_size - 3 ) )
490
237
        {
491
237
          break;
492
237
        }
493
59.1k
      }
494
1.14M
    }
495
4.31k
    if( table_index >= allocation_table->number_of_cluster_numbers )
496
222
    {
497
222
      break;
498
222
    }
499
4.09k
    if( io_handle->file_system_format == LIBFSFAT_FILE_SYSTEM_FORMAT_FAT12 )
500
237
    {
501
237
      read_offset = 0;
502
503
529
      while( table_data_offset < read_size )
504
292
      {
505
292
        table_data[ read_offset++ ] = table_data[ table_data_offset++ ];
506
292
      }
507
237
    }
508
4.09k
  }
509
1.64k
  memory_free(
510
1.64k
   table_data );
511
512
#if defined( HAVE_DEBUG_OUTPUT )
513
  if( libcnotify_verbose != 0 )
514
  {
515
    libcnotify_printf(
516
     "\n" );
517
  }
518
#endif
519
1.64k
  return( 1 );
520
521
276
on_error:
522
276
  if( table_data != NULL )
523
276
  {
524
276
    memory_free(
525
276
     table_data );
526
276
  }
527
276
  return( -1 );
528
1.92k
}
529
530
/* Retrieves a specific cluster number from the allocation table
531
 * Returns 1 if successful or -1 on error
532
 */
533
int libfsfat_allocation_table_get_cluster_number_by_index(
534
     libfsfat_allocation_table_t *allocation_table,
535
     int entry_index,
536
     uint32_t *cluster_number,
537
     libcerror_error_t **error )
538
531
{
539
531
  static char *function = "libfsfat_allocation_table_resize";
540
541
531
  if( allocation_table == NULL )
542
0
  {
543
0
    libcerror_error_set(
544
0
     error,
545
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
546
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
547
0
     "%s: invalid allocation table.",
548
0
     function );
549
550
0
    return( -1 );
551
0
  }
552
531
  if( ( entry_index < 0 )
553
531
   || ( entry_index >= allocation_table->number_of_cluster_numbers ) )
554
36
  {
555
36
    libcerror_error_set(
556
36
     error,
557
36
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
558
36
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
559
36
     "%s: invalid entry index value out of bounds.",
560
36
     function );
561
562
36
    return( -1 );
563
36
  }
564
495
  if( cluster_number == NULL )
565
0
  {
566
0
    libcerror_error_set(
567
0
     error,
568
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
569
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
570
0
     "%s: invalid cluster number.",
571
0
     function );
572
573
0
    return( -1 );
574
0
  }
575
495
  *cluster_number = allocation_table->cluster_numbers[ entry_index ];
576
577
495
  return( 1 );
578
495
}
579