Coverage Report

Created: 2026-07-10 07:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libfshfs/libfshfs/libfshfs_btree_file.c
Line
Count
Source
1
/*
2
 * B-tree file functions
3
 *
4
 * Copyright (C) 2009-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 "libfshfs_btree_file.h"
28
#include "libfshfs_btree_header.h"
29
#include "libfshfs_btree_node.h"
30
#include "libfshfs_btree_node_cache.h"
31
#include "libfshfs_btree_node_descriptor.h"
32
#include "libfshfs_btree_node_vector.h"
33
#include "libfshfs_debug.h"
34
#include "libfshfs_definitions.h"
35
#include "libfshfs_extent.h"
36
#include "libfshfs_libcdata.h"
37
#include "libfshfs_libcerror.h"
38
#include "libfshfs_libcnotify.h"
39
#include "libfshfs_libfcache.h"
40
#include "libfshfs_libfdata.h"
41
42
#include "fshfs_btree.h"
43
44
/* Creates a B-tree file
45
 * Make sure the value btree_file is referencing, is set to NULL
46
 * Returns 1 if successful or -1 on error
47
 */
48
int libfshfs_btree_file_initialize(
49
     libfshfs_btree_file_t **btree_file,
50
     libcerror_error_t **error )
51
17.7k
{
52
17.7k
  static char *function = "libfshfs_btree_file_initialize";
53
54
17.7k
  if( btree_file == 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 B-tree file.",
61
0
     function );
62
63
0
    return( -1 );
64
0
  }
65
17.7k
  if( *btree_file != 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 B-tree file value already set.",
72
0
     function );
73
74
0
    return( -1 );
75
0
  }
76
17.7k
  *btree_file = memory_allocate_structure(
77
17.7k
                 libfshfs_btree_file_t );
78
79
17.7k
  if( *btree_file == 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 B-tree file.",
86
0
     function );
87
88
0
    goto on_error;
89
0
  }
90
17.7k
  if( memory_set(
91
17.7k
       *btree_file,
92
17.7k
       0,
93
17.7k
       sizeof( libfshfs_btree_file_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 B-tree file.",
100
0
     function );
101
102
0
    memory_free(
103
0
     *btree_file );
104
105
0
    *btree_file = NULL;
106
107
0
    return( -1 );
108
0
  }
109
17.7k
  if( libcdata_array_initialize(
110
17.7k
       &( ( *btree_file )->extents ),
111
17.7k
       0,
112
17.7k
       error ) != 1 )
113
0
  {
114
0
    libcerror_error_set(
115
0
     error,
116
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
117
0
     LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
118
0
     "%s: unable to create extents array.",
119
0
     function );
120
121
0
    goto on_error;
122
0
  }
123
17.7k
  if( libfshfs_btree_header_initialize(
124
17.7k
       &( ( *btree_file )->header ),
125
17.7k
       error ) != 1 )
126
0
  {
127
0
    libcerror_error_set(
128
0
     error,
129
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
130
0
     LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
131
0
     "%s: unable to create header.",
132
0
     function );
133
134
0
    goto on_error;
135
0
  }
136
17.7k
  return( 1 );
137
138
0
on_error:
139
0
  if( *btree_file != NULL )
140
0
  {
141
0
    if( ( *btree_file )->extents != NULL )
142
0
    {
143
0
      libcdata_array_free(
144
0
       &( ( *btree_file )->extents ),
145
0
       NULL,
146
0
       NULL );
147
0
    }
148
0
    memory_free(
149
0
     *btree_file );
150
151
0
    *btree_file = NULL;
152
0
  }
153
0
  return( -1 );
154
17.7k
}
155
156
/* Frees B-tree file
157
 * Returns 1 if successful or -1 on error
158
 */
159
int libfshfs_btree_file_free(
160
     libfshfs_btree_file_t **btree_file,
161
     libcerror_error_t **error )
162
17.7k
{
163
17.7k
  static char *function = "libfshfs_btree_file_free";
164
17.7k
  int result            = 1;
165
166
17.7k
  if( btree_file == NULL )
167
0
  {
168
0
    libcerror_error_set(
169
0
     error,
170
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
171
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
172
0
     "%s: invalid B-tree file.",
173
0
     function );
174
175
0
    return( -1 );
176
0
  }
177
17.7k
  if( *btree_file != NULL )
178
17.7k
  {
179
17.7k
    if( libcdata_array_free(
180
17.7k
         &( ( *btree_file )->extents ),
181
17.7k
         (int (*)(intptr_t **, libcerror_error_t **)) &libfshfs_extent_free,
182
17.7k
         error ) != 1 )
183
0
    {
184
0
      libcerror_error_set(
185
0
       error,
186
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
187
0
       LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
188
0
       "%s: unable to free extents array.",
189
0
       function );
190
191
0
      result = -1;
192
0
    }
193
17.7k
    if( libfshfs_btree_header_free(
194
17.7k
         &( ( *btree_file )->header ),
195
17.7k
         error ) != 1 )
196
0
    {
197
0
      libcerror_error_set(
198
0
       error,
199
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
200
0
       LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
201
0
       "%s: unable to free header.",
202
0
       function );
203
204
0
      result = -1;
205
0
    }
206
17.7k
    if( ( *btree_file )->node_vector != NULL )
207
15.6k
    {
208
15.6k
      if( libfshfs_btree_node_vector_free(
209
15.6k
           &( ( *btree_file )->node_vector ),
210
15.6k
           error ) != 1 )
211
0
      {
212
0
        libcerror_error_set(
213
0
         error,
214
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
215
0
         LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
216
0
         "%s: unable to free B-tree node vector.",
217
0
         function );
218
219
0
        result = -1;
220
0
      }
221
15.6k
    }
222
17.7k
    memory_free(
223
17.7k
     *btree_file );
224
225
17.7k
    *btree_file = NULL;
226
17.7k
  }
227
17.7k
  return( result );
228
17.7k
}
229
230
/* Reads the B-tree file
231
 * Returns 1 if successful or -1 on error
232
 */
233
int libfshfs_btree_file_read_file_io_handle(
234
     libfshfs_btree_file_t *btree_file,
235
     libfshfs_io_handle_t *io_handle,
236
     libbfio_handle_t *file_io_handle,
237
     libcerror_error_t **error )
238
16.7k
{
239
16.7k
  uint8_t header_node_data[ 512 ];
240
241
16.7k
  libfshfs_btree_node_descriptor_t *header_node_descriptor = NULL;
242
16.7k
  libfshfs_extent_t *extent                                = NULL;
243
16.7k
  static char *function                                    = "libfshfs_btree_file_read_file_io_handle";
244
16.7k
  ssize_t read_count                                       = 0;
245
16.7k
  off64_t file_offset                                      = 0;
246
247
16.7k
  if( btree_file == NULL )
248
0
  {
249
0
    libcerror_error_set(
250
0
     error,
251
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
252
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
253
0
     "%s: invalid B-tree file.",
254
0
     function );
255
256
0
    return( -1 );
257
0
  }
258
16.7k
  if( btree_file->node_vector != NULL )
259
0
  {
260
0
    libcerror_error_set(
261
0
     error,
262
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
263
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
264
0
     "%s: invalid B-tree file - node vector already set.",
265
0
     function );
266
267
0
    return( -1 );
268
0
  }
269
16.7k
  if( io_handle == NULL )
270
0
  {
271
0
    libcerror_error_set(
272
0
     error,
273
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
274
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
275
0
     "%s: invalid IO handle.",
276
0
     function );
277
278
0
    return( -1 );
279
0
  }
280
16.7k
  if( io_handle->block_size == 0 )
281
3
  {
282
3
    libcerror_error_set(
283
3
     error,
284
3
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
285
3
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
286
3
     "%s: invalid IO handle - block size value out of bounds.",
287
3
     function );
288
289
3
    return( -1 );
290
3
  }
291
16.7k
  if( btree_file->size > 0 )
292
16.3k
  {
293
    /* Read the header record first to determine the B-tree node size.
294
     */
295
16.3k
    if( libcdata_array_get_entry_by_index(
296
16.3k
         btree_file->extents,
297
16.3k
         0,
298
16.3k
         (intptr_t **) &extent,
299
16.3k
         error ) != 1 )
300
49
    {
301
49
      libcerror_error_set(
302
49
       error,
303
49
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
304
49
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
305
49
       "%s: unable to retrieve extent: 0.",
306
49
       function );
307
308
49
      goto on_error;
309
49
    }
310
16.3k
    if( extent == NULL )
311
0
    {
312
0
      libcerror_error_set(
313
0
       error,
314
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
315
0
       LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
316
0
       "%s: missing extent: 0.",
317
0
       function );
318
319
0
      goto on_error;
320
0
    }
321
16.3k
    file_offset = extent->block_number * io_handle->block_size;
322
323
#if defined( HAVE_DEBUG_OUTPUT )
324
    if( libcnotify_verbose != 0 )
325
    {
326
      libcnotify_printf(
327
       "%s: reading B-tree header node at offset: %" PRIi64 " (0x%08" PRIx64 ")\n",
328
       function,
329
       file_offset,
330
       file_offset );
331
    }
332
#endif
333
16.3k
    read_count = libbfio_handle_read_buffer_at_offset(
334
16.3k
                  file_io_handle,
335
16.3k
                  header_node_data,
336
16.3k
                  512,
337
16.3k
                  file_offset,
338
16.3k
                  error );
339
340
16.3k
    if( read_count != (ssize_t) 512 )
341
334
    {
342
334
      libcerror_error_set(
343
334
       error,
344
334
       LIBCERROR_ERROR_DOMAIN_IO,
345
334
       LIBCERROR_IO_ERROR_READ_FAILED,
346
334
       "%s: unable to read B-tree header node data at offset: %" PRIi64 " (0x%08" PRIx64 ").",
347
334
       function,
348
334
       file_offset,
349
334
       file_offset );
350
351
334
      goto on_error;
352
334
    }
353
15.9k
    if( libfshfs_btree_node_descriptor_initialize(
354
15.9k
         &header_node_descriptor,
355
15.9k
         error ) != 1 )
356
0
    {
357
0
      libcerror_error_set(
358
0
       error,
359
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
360
0
       LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
361
0
       "%s: unable to create B-tree header node descriptor.",
362
0
       function );
363
364
0
      goto on_error;
365
0
    }
366
15.9k
    if( libfshfs_btree_node_descriptor_read_data(
367
15.9k
         header_node_descriptor,
368
15.9k
         header_node_data,
369
15.9k
         512,
370
15.9k
         error ) != 1 )
371
18
    {
372
18
      libcerror_error_set(
373
18
       error,
374
18
       LIBCERROR_ERROR_DOMAIN_IO,
375
18
       LIBCERROR_IO_ERROR_READ_FAILED,
376
18
       "%s: unable to read B-tree header node descriptor.",
377
18
       function );
378
379
18
      goto on_error;
380
18
    }
381
15.9k
    if( header_node_descriptor->type != LIBFSHFS_BTREE_NODE_TYPE_HEADER_NODE )
382
30
    {
383
30
      libcerror_error_set(
384
30
       error,
385
30
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
386
30
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
387
30
       "%s: unuspported B-tree header node type.",
388
30
       function );
389
390
30
      goto on_error;
391
30
    }
392
15.9k
    if( libfshfs_btree_node_descriptor_free(
393
15.9k
         &header_node_descriptor,
394
15.9k
         error ) != 1 )
395
0
    {
396
0
      libcerror_error_set(
397
0
       error,
398
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
399
0
       LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
400
0
       "%s: unable to free B-tree header node descriptor.",
401
0
       function );
402
403
0
      goto on_error;
404
0
    }
405
15.9k
    if( libfshfs_btree_header_read_data(
406
15.9k
         btree_file->header,
407
15.9k
         &( header_node_data[ sizeof( fshfs_btree_node_descriptor_t ) ] ),
408
15.9k
         512 - sizeof( fshfs_btree_node_descriptor_t ),
409
15.9k
         error ) != 1 )
410
0
    {
411
0
      libcerror_error_set(
412
0
       error,
413
0
       LIBCERROR_ERROR_DOMAIN_IO,
414
0
       LIBCERROR_IO_ERROR_READ_FAILED,
415
0
       "%s: unable to read B-tree header.",
416
0
       function );
417
418
0
      goto on_error;
419
0
    }
420
    /* Read the root node using the node vector
421
     */
422
15.9k
    if( libfshfs_btree_node_vector_initialize(
423
15.9k
         &( btree_file->node_vector ),
424
15.9k
         io_handle,
425
15.9k
         btree_file->size,
426
15.9k
         btree_file->header->node_size,
427
15.9k
         btree_file->extents,
428
15.9k
         error ) != 1 )
429
324
    {
430
324
      libcerror_error_set(
431
324
       error,
432
324
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
433
324
       LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
434
324
       "%s: unable to create B-tree node vector.",
435
324
       function );
436
437
324
      goto on_error;
438
324
    }
439
15.9k
  }
440
15.9k
  return( 1 );
441
442
755
on_error:
443
755
  if( btree_file->node_vector != NULL )
444
0
  {
445
0
    libfshfs_btree_node_vector_free(
446
0
     &( btree_file->node_vector ),
447
0
     NULL );
448
0
  }
449
755
  if( header_node_descriptor != NULL )
450
48
  {
451
48
    libfshfs_btree_node_descriptor_free(
452
48
     &header_node_descriptor,
453
48
     NULL );
454
48
  }
455
755
  return( -1 );
456
16.7k
}
457
458
/* Retrieves a specific B-tree node
459
 * Returns 1 if successful or -1 on error
460
 */
461
int libfshfs_btree_file_get_node_by_number(
462
     libfshfs_btree_file_t *btree_file,
463
     libbfio_handle_t *file_io_handle,
464
     libfshfs_btree_node_cache_t *node_cache,
465
     int depth,
466
     uint32_t node_number,
467
     libfshfs_btree_node_t **node,
468
     libcerror_error_t **error )
469
85.4k
{
470
85.4k
  static char *function = "libfshfs_btree_file_get_node_by_number";
471
472
85.4k
  if( btree_file == NULL )
473
0
  {
474
0
    libcerror_error_set(
475
0
     error,
476
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
477
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
478
0
     "%s: invalid B-tree file.",
479
0
     function );
480
481
0
    return( -1 );
482
0
  }
483
85.4k
  if( ( depth <= 0 )
484
85.4k
   || ( depth >= 9 ) )
485
1.24k
  {
486
1.24k
    libcerror_error_set(
487
1.24k
     error,
488
1.24k
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
489
1.24k
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
490
1.24k
     "%s: invalid depth value out of bounds.",
491
1.24k
     function );
492
493
1.24k
    return( -1 );
494
1.24k
  }
495
84.2k
  if( libfshfs_btree_node_vector_get_node_by_number(
496
84.2k
       btree_file->node_vector,
497
84.2k
       file_io_handle,
498
84.2k
       node_cache,
499
84.2k
       depth,
500
84.2k
       node_number,
501
84.2k
       node,
502
84.2k
       error ) != 1 )
503
390
  {
504
390
    libcerror_error_set(
505
390
     error,
506
390
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
507
390
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
508
390
     "%s: unable to retrieve B-tree node: %" PRIu32 " at depth: %d.",
509
390
     function,
510
390
     node_number,
511
390
     depth );
512
513
390
    return( -1 );
514
390
  }
515
83.8k
  return( 1 );
516
84.2k
}
517
518
/* Retrieves the B-tree root node
519
 * Returns 1 if successful or -1 on error
520
 */
521
int libfshfs_btree_file_get_root_node(
522
     libfshfs_btree_file_t *btree_file,
523
     libbfio_handle_t *file_io_handle,
524
     libfshfs_btree_node_cache_t *node_cache,
525
     libfshfs_btree_node_t **root_node,
526
     libcerror_error_t **error )
527
20.1k
{
528
20.1k
  static char *function = "libfshfs_btree_file_get_root_node";
529
530
20.1k
  if( btree_file == NULL )
531
0
  {
532
0
    libcerror_error_set(
533
0
     error,
534
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
535
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
536
0
     "%s: invalid B-tree file.",
537
0
     function );
538
539
0
    return( -1 );
540
0
  }
541
20.1k
  if( libfshfs_btree_node_vector_get_node_by_number(
542
20.1k
       btree_file->node_vector,
543
20.1k
       file_io_handle,
544
20.1k
       node_cache,
545
20.1k
       0,
546
20.1k
       btree_file->header->root_node_number,
547
20.1k
       root_node,
548
20.1k
       error ) != 1 )
549
1.31k
  {
550
1.31k
    libcerror_error_set(
551
1.31k
     error,
552
1.31k
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
553
1.31k
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
554
1.31k
     "%s: unable to retrieve B-tree root node: %" PRIu32 " at depth: 0.",
555
1.31k
     function,
556
1.31k
     btree_file->header->root_node_number );
557
558
1.31k
    return( -1 );
559
1.31k
  }
560
18.8k
  return( 1 );
561
20.1k
}
562