Coverage Report

Created: 2026-03-05 07:45

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
12.2k
{
52
12.2k
  static char *function = "libfshfs_btree_file_initialize";
53
54
12.2k
  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
12.2k
  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
12.2k
  *btree_file = memory_allocate_structure(
77
12.2k
                 libfshfs_btree_file_t );
78
79
12.2k
  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
12.2k
  if( memory_set(
91
12.2k
       *btree_file,
92
12.2k
       0,
93
12.2k
       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
12.2k
  if( libcdata_array_initialize(
110
12.2k
       &( ( *btree_file )->extents ),
111
12.2k
       0,
112
12.2k
       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
12.2k
  if( libfshfs_btree_header_initialize(
124
12.2k
       &( ( *btree_file )->header ),
125
12.2k
       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
12.2k
  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
12.2k
}
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
12.2k
{
163
12.2k
  static char *function = "libfshfs_btree_file_free";
164
12.2k
  int result            = 1;
165
166
12.2k
  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
12.2k
  if( *btree_file != NULL )
178
12.2k
  {
179
12.2k
    if( libcdata_array_free(
180
12.2k
         &( ( *btree_file )->extents ),
181
12.2k
         (int (*)(intptr_t **, libcerror_error_t **)) &libfshfs_extent_free,
182
12.2k
         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
12.2k
    if( libfshfs_btree_header_free(
194
12.2k
         &( ( *btree_file )->header ),
195
12.2k
         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
12.2k
    if( ( *btree_file )->node_vector != NULL )
207
10.7k
    {
208
10.7k
      if( libfshfs_btree_node_vector_free(
209
10.7k
           &( ( *btree_file )->node_vector ),
210
10.7k
           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
10.7k
    }
222
12.2k
    memory_free(
223
12.2k
     *btree_file );
224
225
12.2k
    *btree_file = NULL;
226
12.2k
  }
227
12.2k
  return( result );
228
12.2k
}
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
11.5k
{
239
11.5k
  uint8_t header_node_data[ 512 ];
240
241
11.5k
  libfshfs_btree_node_descriptor_t *header_node_descriptor = NULL;
242
11.5k
  libfshfs_extent_t *extent                                = NULL;
243
11.5k
  static char *function                                    = "libfshfs_btree_file_read_file_io_handle";
244
11.5k
  ssize_t read_count                                       = 0;
245
11.5k
  off64_t file_offset                                      = 0;
246
247
11.5k
  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
11.5k
  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
11.5k
  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
11.5k
  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
11.5k
  if( btree_file->size > 0 )
292
11.2k
  {
293
    /* Read the header record first to determine the B-tree node size.
294
     */
295
11.2k
    if( libcdata_array_get_entry_by_index(
296
11.2k
         btree_file->extents,
297
11.2k
         0,
298
11.2k
         (intptr_t **) &extent,
299
11.2k
         error ) != 1 )
300
36
    {
301
36
      libcerror_error_set(
302
36
       error,
303
36
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
304
36
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
305
36
       "%s: unable to retrieve extent: 0.",
306
36
       function );
307
308
36
      goto on_error;
309
36
    }
310
11.1k
    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
11.1k
    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
11.1k
    read_count = libbfio_handle_read_buffer_at_offset(
334
11.1k
                  file_io_handle,
335
11.1k
                  header_node_data,
336
11.1k
                  512,
337
11.1k
                  file_offset,
338
11.1k
                  error );
339
340
11.1k
    if( read_count != (ssize_t) 512 )
341
191
    {
342
191
      libcerror_error_set(
343
191
       error,
344
191
       LIBCERROR_ERROR_DOMAIN_IO,
345
191
       LIBCERROR_IO_ERROR_READ_FAILED,
346
191
       "%s: unable to read B-tree header node data at offset: %" PRIi64 " (0x%08" PRIx64 ").",
347
191
       function,
348
191
       file_offset,
349
191
       file_offset );
350
351
191
      goto on_error;
352
191
    }
353
11.0k
    if( libfshfs_btree_node_descriptor_initialize(
354
11.0k
         &header_node_descriptor,
355
11.0k
         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
11.0k
    if( libfshfs_btree_node_descriptor_read_data(
367
11.0k
         header_node_descriptor,
368
11.0k
         header_node_data,
369
11.0k
         512,
370
11.0k
         error ) != 1 )
371
8
    {
372
8
      libcerror_error_set(
373
8
       error,
374
8
       LIBCERROR_ERROR_DOMAIN_IO,
375
8
       LIBCERROR_IO_ERROR_READ_FAILED,
376
8
       "%s: unable to read B-tree header node descriptor.",
377
8
       function );
378
379
8
      goto on_error;
380
8
    }
381
11.0k
    if( header_node_descriptor->type != LIBFSHFS_BTREE_NODE_TYPE_HEADER_NODE )
382
33
    {
383
33
      libcerror_error_set(
384
33
       error,
385
33
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
386
33
       LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
387
33
       "%s: unuspported B-tree header node type.",
388
33
       function );
389
390
33
      goto on_error;
391
33
    }
392
10.9k
    if( libfshfs_btree_node_descriptor_free(
393
10.9k
         &header_node_descriptor,
394
10.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
10.9k
    if( libfshfs_btree_header_read_data(
406
10.9k
         btree_file->header,
407
10.9k
         &( header_node_data[ sizeof( fshfs_btree_node_descriptor_t ) ] ),
408
10.9k
         512 - sizeof( fshfs_btree_node_descriptor_t ),
409
10.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
10.9k
    if( libfshfs_btree_node_vector_initialize(
423
10.9k
         &( btree_file->node_vector ),
424
10.9k
         io_handle,
425
10.9k
         btree_file->size,
426
10.9k
         btree_file->header->node_size,
427
10.9k
         btree_file->extents,
428
10.9k
         error ) != 1 )
429
241
    {
430
241
      libcerror_error_set(
431
241
       error,
432
241
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
433
241
       LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
434
241
       "%s: unable to create B-tree node vector.",
435
241
       function );
436
437
241
      goto on_error;
438
241
    }
439
10.9k
  }
440
10.9k
  return( 1 );
441
442
509
on_error:
443
509
  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
509
  if( header_node_descriptor != NULL )
450
41
  {
451
41
    libfshfs_btree_node_descriptor_free(
452
41
     &header_node_descriptor,
453
41
     NULL );
454
41
  }
455
509
  return( -1 );
456
11.5k
}
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
62.2k
{
470
62.2k
  static char *function = "libfshfs_btree_file_get_node_by_number";
471
472
62.2k
  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
62.2k
  if( ( depth <= 0 )
484
62.2k
   || ( depth >= 9 ) )
485
994
  {
486
994
    libcerror_error_set(
487
994
     error,
488
994
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
489
994
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
490
994
     "%s: invalid depth value out of bounds.",
491
994
     function );
492
493
994
    return( -1 );
494
994
  }
495
61.2k
  if( libfshfs_btree_node_vector_get_node_by_number(
496
61.2k
       btree_file->node_vector,
497
61.2k
       file_io_handle,
498
61.2k
       node_cache,
499
61.2k
       depth,
500
61.2k
       node_number,
501
61.2k
       node,
502
61.2k
       error ) != 1 )
503
298
  {
504
298
    libcerror_error_set(
505
298
     error,
506
298
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
507
298
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
508
298
     "%s: unable to retrieve B-tree node: %" PRIu32 " at depth: %d.",
509
298
     function,
510
298
     node_number,
511
298
     depth );
512
513
298
    return( -1 );
514
298
  }
515
60.9k
  return( 1 );
516
61.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
13.5k
{
528
13.5k
  static char *function = "libfshfs_btree_file_get_root_node";
529
530
13.5k
  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
13.5k
  if( libfshfs_btree_node_vector_get_node_by_number(
542
13.5k
       btree_file->node_vector,
543
13.5k
       file_io_handle,
544
13.5k
       node_cache,
545
13.5k
       0,
546
13.5k
       btree_file->header->root_node_number,
547
13.5k
       root_node,
548
13.5k
       error ) != 1 )
549
940
  {
550
940
    libcerror_error_set(
551
940
     error,
552
940
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
553
940
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
554
940
     "%s: unable to retrieve B-tree root node: %" PRIu32 " at depth: 0.",
555
940
     function,
556
940
     btree_file->header->root_node_number );
557
558
940
    return( -1 );
559
940
  }
560
12.6k
  return( 1 );
561
13.5k
}
562