Coverage Report

Created: 2025-06-22 07:35

/src/libfshfs/libfshfs/libfshfs_btree_node_vector.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * The B-tree file node vector functions
3
 *
4
 * Copyright (C) 2009-2024, 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 <memory.h>
24
#include <types.h>
25
26
#include "libfshfs_btree_node.h"
27
#include "libfshfs_btree_node_cache.h"
28
#include "libfshfs_btree_node_vector.h"
29
#include "libfshfs_definitions.h"
30
#include "libfshfs_extent.h"
31
#include "libfshfs_io_handle.h"
32
#include "libfshfs_libcdata.h"
33
#include "libfshfs_libcerror.h"
34
#include "libfshfs_libcnotify.h"
35
#include "libfshfs_libfcache.h"
36
#include "libfshfs_profiler.h"
37
#include "libfshfs_unused.h"
38
39
/* Creates a B-tree node vector
40
 * Make sure the value node_vector is referencing, is set to NULL
41
 * Returns 1 if successful or -1 on error
42
 */
43
int libfshfs_btree_node_vector_initialize(
44
     libfshfs_btree_node_vector_t **node_vector,
45
     libfshfs_io_handle_t *io_handle,
46
     uint64_t size,
47
     uint16_t node_size,
48
     libcdata_array_t *extents,
49
     libcerror_error_t **error )
50
11.3k
{
51
11.3k
  static char *function          = "libfshfs_btree_node_vector_initialize";
52
11.3k
  uint64_t total_number_of_nodes = 0;
53
54
11.3k
  if( node_vector == 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 node vector.",
61
0
     function );
62
63
0
    return( -1 );
64
0
  }
65
11.3k
  if( *node_vector != 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 node vector value already set.",
72
0
     function );
73
74
0
    return( -1 );
75
0
  }
76
11.3k
  if( io_handle == NULL )
77
0
  {
78
0
    libcerror_error_set(
79
0
     error,
80
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
81
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
82
0
     "%s: invalid IO handle.",
83
0
     function );
84
85
0
    return( -1 );
86
0
  }
87
11.3k
  if( node_size == 0 )
88
3
  {
89
3
    libcerror_error_set(
90
3
     error,
91
3
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
92
3
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
93
3
     "%s: invalid node size value out of bounds.",
94
3
     function );
95
96
3
    return( -1 );
97
3
  }
98
11.3k
  *node_vector = memory_allocate_structure(
99
11.3k
                  libfshfs_btree_node_vector_t );
100
101
11.3k
  if( *node_vector == NULL )
102
0
  {
103
0
    libcerror_error_set(
104
0
     error,
105
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
106
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
107
0
     "%s: unable to create B-tree node vector.",
108
0
     function );
109
110
0
    goto on_error;
111
0
  }
112
11.3k
  if( memory_set(
113
11.3k
       *node_vector,
114
11.3k
       0,
115
11.3k
       sizeof( libfshfs_btree_node_vector_t ) ) == NULL )
116
0
  {
117
0
    libcerror_error_set(
118
0
     error,
119
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
120
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
121
0
     "%s: unable to clear B-tree node vector.",
122
0
     function );
123
124
0
    memory_free(
125
0
     *node_vector );
126
127
0
    *node_vector = NULL;
128
129
0
    return( -1 );
130
0
  }
131
11.3k
  if( size > ( ( (uint64_t) UINT64_MAX / node_size ) - 1 ) )
132
8
  {
133
8
    libcerror_error_set(
134
8
     error,
135
8
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
136
8
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
137
8
     "%s: invalid total number of blocks value out of bounds.",
138
8
     function );
139
140
8
    goto on_error;
141
8
  }
142
11.2k
  total_number_of_nodes = size / node_size;
143
144
11.2k
  if( ( size % node_size ) != 0 )
145
7.33k
  {
146
7.33k
    total_number_of_nodes += 1;
147
7.33k
  }
148
11.2k
  if( total_number_of_nodes > (uint64_t) UINT32_MAX )
149
101
  {
150
101
    libcerror_error_set(
151
101
     error,
152
101
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
153
101
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
154
101
     "%s: invalid number of nodes value out of bounds.",
155
101
     function );
156
157
101
    goto on_error;
158
101
  }
159
11.1k
  if( libfcache_date_time_get_timestamp(
160
11.1k
       &( ( *node_vector )->cache_timestamp ),
161
11.1k
       error ) != 1 )
162
0
  {
163
0
    libcerror_error_set(
164
0
     error,
165
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
166
0
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
167
0
     "%s: unable to retrieve cache timestamp.",
168
0
     function );
169
170
0
    goto on_error;
171
0
  }
172
11.1k
  ( *node_vector )->number_of_nodes = (uint32_t) total_number_of_nodes;
173
11.1k
  ( *node_vector )->io_handle       = io_handle;
174
11.1k
  ( *node_vector )->node_size       = node_size;
175
11.1k
  ( *node_vector )->extents         = extents;
176
177
11.1k
  return( 1 );
178
179
109
on_error:
180
109
  if( *node_vector != NULL )
181
109
  {
182
109
    memory_free(
183
109
     *node_vector );
184
185
109
    *node_vector = NULL;
186
109
  }
187
109
  return( -1 );
188
11.1k
}
189
190
/* Frees a B-tree node vector
191
 * Returns 1 if successful or -1 on error
192
 */
193
int libfshfs_btree_node_vector_free(
194
     libfshfs_btree_node_vector_t **node_vector,
195
     libcerror_error_t **error )
196
11.1k
{
197
11.1k
  static char *function = "libfshfs_btree_node_vector_free";
198
11.1k
  int result            = 1;
199
200
11.1k
  if( node_vector == NULL )
201
0
  {
202
0
    libcerror_error_set(
203
0
     error,
204
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
205
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
206
0
     "%s: invalid B-tree node vector.",
207
0
     function );
208
209
0
    return( -1 );
210
0
  }
211
11.1k
  if( *node_vector != NULL )
212
11.1k
  {
213
    /* The extents reference is freed elsewhere
214
     */
215
11.1k
    memory_free(
216
11.1k
     *node_vector );
217
218
11.1k
    *node_vector = NULL;
219
11.1k
  }
220
11.1k
  return( result );
221
11.1k
}
222
223
/* Retrieves a specific B-tree node
224
 * Returns 1 if successful or -1 on error
225
 */
226
int libfshfs_btree_node_vector_get_node_by_number(
227
     libfshfs_btree_node_vector_t *node_vector,
228
     libbfio_handle_t *file_io_handle,
229
     libfshfs_btree_node_cache_t *node_cache,
230
     int depth,
231
     uint32_t node_number,
232
     libfshfs_btree_node_t **node,
233
     libcerror_error_t **error )
234
71.8k
{
235
71.8k
  libfcache_cache_value_t *cache_value = NULL;
236
71.8k
  libfshfs_btree_node_t *safe_node     = NULL;
237
71.8k
  libfshfs_extent_t *extent            = NULL;
238
71.8k
  static char *function                = "libfshfs_btree_node_vector_get_node_by_number";
239
71.8k
  size64_t extent_size                 = 0;
240
71.8k
  off64_t file_offset                  = 0;
241
71.8k
  off64_t node_offset                  = 0;
242
71.8k
  int extent_index                     = 0;
243
71.8k
  int number_of_extents                = 0;
244
71.8k
  int result                           = 0;
245
246
#if defined( HAVE_PROFILER )
247
  int64_t profiler_start_timestamp     = 0;
248
  const char *cache_hit_or_miss        = NULL;
249
#endif
250
251
71.8k
  if( node_vector == NULL )
252
0
  {
253
0
    libcerror_error_set(
254
0
     error,
255
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
256
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
257
0
     "%s: invalid B-tree node vector.",
258
0
     function );
259
260
0
    return( -1 );
261
0
  }
262
71.8k
  if( node_vector->io_handle == NULL )
263
0
  {
264
0
    libcerror_error_set(
265
0
     error,
266
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
267
0
     LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
268
0
     "%s: invalid B-tree node vector - missing IO handle.",
269
0
     function );
270
271
0
    return( -1 );
272
0
  }
273
71.8k
  if( node_vector->io_handle->block_size == 0 )
274
0
  {
275
0
    libcerror_error_set(
276
0
     error,
277
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
278
0
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
279
0
     "%s: invalid B-tree node vector - invalid IO handle - block size value out of bounds.",
280
0
     function );
281
282
0
    return( -1 );
283
0
  }
284
71.8k
  if( node_vector->node_size == 0 )
285
0
  {
286
0
    libcerror_error_set(
287
0
     error,
288
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
289
0
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
290
0
     "%s: invalid B-tree node vector - node size value out of bounds.",
291
0
     function );
292
293
0
    return( -1 );
294
0
  }
295
71.8k
  if( node_number >= node_vector->number_of_nodes )
296
98
  {
297
98
    libcerror_error_set(
298
98
     error,
299
98
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
300
98
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
301
98
     "%s: invalid block number value out of bounds.",
302
98
     function );
303
304
98
    return( -1 );
305
98
  }
306
71.7k
  if( node_cache == NULL )
307
0
  {
308
0
    libcerror_error_set(
309
0
     error,
310
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
311
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
312
0
     "%s: invalid B-tree node cache.",
313
0
     function );
314
315
0
    return( -1 );
316
0
  }
317
#if defined( HAVE_PROFILER )
318
  if( node_vector->io_handle->profiler != NULL )
319
  {
320
    if( libfshfs_profiler_start_timing(
321
         node_vector->io_handle->profiler,
322
         &profiler_start_timestamp,
323
         error ) != 1 )
324
    {
325
      libcerror_error_set(
326
       error,
327
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
328
       LIBCERROR_RUNTIME_ERROR_SET_FAILED,
329
       "%s: unable to start timing.",
330
       function );
331
332
      goto on_error;
333
    }
334
  }
335
#endif /* defined( HAVE_PROFILER ) */
336
337
/* TODO move this into node_cache */
338
71.7k
  result = libfcache_cache_get_value_by_identifier(
339
71.7k
            node_cache->caches[ depth ],
340
71.7k
            0,
341
71.7k
            (off64_t) node_number,
342
71.7k
            node_vector->cache_timestamp,
343
71.7k
            &cache_value,
344
71.7k
            error );
345
346
71.7k
  if( result == -1 )
347
0
  {
348
0
    libcerror_error_set(
349
0
     error,
350
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
351
0
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
352
0
     "%s: unable to retrieve value from cache.",
353
0
     function );
354
355
0
    goto on_error;
356
0
  }
357
71.7k
  else if( result != 0 )
358
45.3k
  {
359
45.3k
    if( libfcache_cache_value_get_value(
360
45.3k
         cache_value,
361
45.3k
         (intptr_t **) node,
362
45.3k
         error ) != 1 )
363
0
    {
364
0
      libcerror_error_set(
365
0
       error,
366
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
367
0
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
368
0
       "%s: unable to retrieve cache value.",
369
0
       function );
370
371
0
      goto on_error;
372
0
    }
373
45.3k
  }
374
26.4k
  else
375
26.4k
  {
376
26.4k
    if( libfshfs_btree_node_initialize(
377
26.4k
         &safe_node,
378
26.4k
         (size_t) node_vector->node_size,
379
26.4k
         error ) != 1 )
380
0
    {
381
0
      libcerror_error_set(
382
0
       error,
383
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
384
0
       LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
385
0
       "%s: unable to create B-tree node.",
386
0
       function );
387
388
0
      goto on_error;
389
0
    }
390
26.4k
    if( libcdata_array_get_number_of_entries(
391
26.4k
         node_vector->extents,
392
26.4k
         &number_of_extents,
393
26.4k
         error ) != 1 )
394
0
    {
395
0
      libcerror_error_set(
396
0
       error,
397
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
398
0
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
399
0
       "%s: unable to retrieve number of extents.",
400
0
       function );
401
402
0
      goto on_error;
403
0
    }
404
26.4k
    if( (off64_t) node_number > ( (off64_t) INT64_MAX / node_vector->node_size ) )
405
0
    {
406
0
      libcerror_error_set(
407
0
       error,
408
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
409
0
       LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
410
0
       "%s: invalid node number value out of bounds.",
411
0
       function );
412
413
0
      goto on_error;
414
0
    }
415
26.4k
    node_offset = (off64_t) node_number * node_vector->node_size;
416
417
26.4k
    for( extent_index = 0;
418
60.5k
         extent_index < number_of_extents;
419
34.1k
         extent_index++ )
420
49.6k
    {
421
49.6k
      if( libcdata_array_get_entry_by_index(
422
49.6k
           node_vector->extents,
423
49.6k
           extent_index,
424
49.6k
           (intptr_t **) &extent,
425
49.6k
           error ) != 1 )
426
0
      {
427
0
        libcerror_error_set(
428
0
         error,
429
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
430
0
         LIBCERROR_RUNTIME_ERROR_GET_FAILED,
431
0
         "%s: unable to retrieve extent: %d.",
432
0
         function,
433
0
         extent_index );
434
435
0
        goto on_error;
436
0
      }
437
49.6k
      if( extent == NULL )
438
0
      {
439
0
        libcerror_error_set(
440
0
         error,
441
0
         LIBCERROR_ERROR_DOMAIN_RUNTIME,
442
0
         LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
443
0
         "%s: missing extent: %d.",
444
0
         function,
445
0
         extent_index );
446
447
0
        goto on_error;
448
0
      }
449
49.6k
      extent_size = (size64_t) extent->number_of_blocks * node_vector->io_handle->block_size;
450
451
49.6k
      if( (size64_t) node_offset < extent_size )
452
15.4k
      {
453
15.4k
        if( (off64_t) extent->block_number > ( ( (off64_t) INT64_MAX - node_offset ) / node_vector->io_handle->block_size ) )
454
12
        {
455
12
          libcerror_error_set(
456
12
           error,
457
12
           LIBCERROR_ERROR_DOMAIN_RUNTIME,
458
12
           LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
459
12
           "%s: invalid extent - block number value out of bounds.",
460
12
           function );
461
462
12
          goto on_error;
463
12
        }
464
15.4k
        file_offset  = ( (off64_t) extent->block_number * node_vector->io_handle->block_size ) + node_offset;
465
15.4k
        extent_size -= node_offset;
466
467
15.4k
        break;
468
15.4k
      }
469
34.1k
      node_offset -= extent_size;
470
34.1k
    }
471
26.4k
    if( extent_size < node_vector->node_size )
472
39
    {
473
39
      libcerror_error_set(
474
39
       error,
475
39
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
476
39
       LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
477
39
       "%s: invalid extent size value out of bounds.",
478
39
       function );
479
480
39
      goto on_error;
481
39
    }
482
26.3k
    if( libfshfs_btree_node_read_file_io_handle(
483
26.3k
         safe_node,
484
26.3k
         file_io_handle,
485
26.3k
         file_offset,
486
26.3k
         error ) != 1 )
487
1.05k
    {
488
1.05k
      libcerror_error_set(
489
1.05k
       error,
490
1.05k
       LIBCERROR_ERROR_DOMAIN_IO,
491
1.05k
       LIBCERROR_IO_ERROR_READ_FAILED,
492
1.05k
       "%s: unable to read element data at offset: %" PRIi64 " (0x%08" PRIx64 ").",
493
1.05k
       function,
494
1.05k
       file_offset,
495
1.05k
       file_offset );
496
497
1.05k
      goto on_error;
498
1.05k
    }
499
25.3k
    if( libfcache_cache_set_value_by_identifier(
500
25.3k
         node_cache->caches[ depth ],
501
25.3k
         0,
502
25.3k
         (off64_t) node_number,
503
25.3k
         node_vector->cache_timestamp,
504
25.3k
         (intptr_t *) safe_node,
505
25.3k
         (int (*)(intptr_t **, libcerror_error_t **)) &libfshfs_btree_node_free,
506
25.3k
         LIBFCACHE_CACHE_VALUE_FLAG_MANAGED,
507
25.3k
         error ) != 1 )
508
0
    {
509
0
      libcerror_error_set(
510
0
       error,
511
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
512
0
       LIBCERROR_RUNTIME_ERROR_SET_FAILED,
513
0
       "%s: unable to set value in cache.",
514
0
       function );
515
516
0
      goto on_error;
517
0
    }
518
25.3k
    *node     = safe_node;
519
25.3k
    safe_node = NULL;
520
25.3k
  }
521
#if defined( HAVE_PROFILER )
522
  if( node_vector->io_handle->profiler != NULL )
523
  {
524
    node_offset = (off64_t) node_number * node_vector->node_size;
525
526
    if( result == 0 )
527
    {
528
      cache_hit_or_miss = "miss";
529
    }
530
    else
531
    {
532
      cache_hit_or_miss = "hit";
533
    }
534
    if( libfshfs_profiler_stop_timing(
535
         node_vector->io_handle->profiler,
536
         profiler_start_timestamp,
537
         function,
538
         node_offset,
539
         node_vector->node_size,
540
         cache_hit_or_miss,
541
         error ) != 1 )
542
    {
543
      libcerror_error_set(
544
       error,
545
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
546
       LIBCERROR_RUNTIME_ERROR_SET_FAILED,
547
       "%s: unable to stop timing.",
548
       function );
549
550
      goto on_error;
551
    }
552
  }
553
#endif /* defined( HAVE_PROFILER ) */
554
555
70.6k
  return( 1 );
556
557
1.10k
on_error:
558
1.10k
  if( safe_node != NULL )
559
1.10k
  {
560
1.10k
    libfshfs_btree_node_free(
561
1.10k
     &safe_node,
562
1.10k
     NULL );
563
1.10k
  }
564
1.10k
  return( -1 );
565
71.7k
}
566