Coverage Report

Created: 2026-08-31 07:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libfsxfs/libfsxfs/libfsxfs_inode_btree.c
Line
Count
Source
1
/*
2
 * Inode B+ tree functions
3
 *
4
 * Copyright (C) 2020-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 "libfsxfs_btree_block.h"
28
#include "libfsxfs_btree_header.h"
29
#include "libfsxfs_definitions.h"
30
#include "libfsxfs_inode_btree.h"
31
#include "libfsxfs_inode_btree_record.h"
32
#include "libfsxfs_inode_information.h"
33
#include "libfsxfs_libbfio.h"
34
#include "libfsxfs_libcdata.h"
35
#include "libfsxfs_libcerror.h"
36
#include "libfsxfs_libcnotify.h"
37
38
/* Creates an inode B+ tree
39
 * Make sure the value inode_btree is referencing, is set to NULL
40
 * Returns 1 if successful or -1 on error
41
 */
42
int libfsxfs_inode_btree_initialize(
43
     libfsxfs_inode_btree_t **inode_btree,
44
     libcerror_error_t **error )
45
5.96k
{
46
5.96k
  static char *function = "libfsxfs_inode_btree_initialize";
47
48
5.96k
  if( inode_btree == NULL )
49
0
  {
50
0
    libcerror_error_set(
51
0
     error,
52
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
53
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
54
0
     "%s: invalid inode B+ tree.",
55
0
     function );
56
57
0
    return( -1 );
58
0
  }
59
5.96k
  if( *inode_btree != NULL )
60
0
  {
61
0
    libcerror_error_set(
62
0
     error,
63
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
64
0
     LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
65
0
     "%s: invalid inode B+ tree value already set.",
66
0
     function );
67
68
0
    return( -1 );
69
0
  }
70
5.96k
  *inode_btree = memory_allocate_structure(
71
5.96k
                  libfsxfs_inode_btree_t );
72
73
5.96k
  if( *inode_btree == NULL )
74
0
  {
75
0
    libcerror_error_set(
76
0
     error,
77
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
78
0
     LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
79
0
     "%s: unable to create inode B+ tree.",
80
0
     function );
81
82
0
    goto on_error;
83
0
  }
84
5.96k
  if( memory_set(
85
5.96k
       *inode_btree,
86
5.96k
       0,
87
5.96k
       sizeof( libfsxfs_inode_btree_t ) ) == NULL )
88
0
  {
89
0
    libcerror_error_set(
90
0
     error,
91
0
     LIBCERROR_ERROR_DOMAIN_MEMORY,
92
0
     LIBCERROR_MEMORY_ERROR_SET_FAILED,
93
0
     "%s: unable to clear inode B+ tree.",
94
0
     function );
95
96
0
    memory_free(
97
0
     *inode_btree );
98
99
0
    *inode_btree = NULL;
100
101
0
    return( -1 );
102
0
  }
103
5.96k
  if( libcdata_array_initialize(
104
5.96k
       &( ( *inode_btree )->inode_information_array ),
105
5.96k
       0,
106
5.96k
       error ) != 1 )
107
0
  {
108
0
    libcerror_error_set(
109
0
     error,
110
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
111
0
     LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
112
0
     "%s: unable to create inode information array.",
113
0
     function );
114
115
0
    goto on_error;
116
0
  }
117
5.96k
  return( 1 );
118
119
0
on_error:
120
0
  if( *inode_btree != NULL )
121
0
  {
122
0
    memory_free(
123
0
     *inode_btree );
124
125
0
    *inode_btree = NULL;
126
0
  }
127
0
  return( -1 );
128
5.96k
}
129
130
/* Frees an inode B+ tree
131
 * Returns 1 if successful or -1 on error
132
 */
133
int libfsxfs_inode_btree_free(
134
     libfsxfs_inode_btree_t **inode_btree,
135
     libcerror_error_t **error )
136
5.96k
{
137
5.96k
  static char *function = "libfsxfs_inode_btree_free";
138
5.96k
  int result            = 1;
139
140
5.96k
  if( inode_btree == NULL )
141
0
  {
142
0
    libcerror_error_set(
143
0
     error,
144
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
145
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
146
0
     "%s: invalid inode B+ tree.",
147
0
     function );
148
149
0
    return( -1 );
150
0
  }
151
5.96k
  if( *inode_btree != NULL )
152
5.96k
  {
153
5.96k
    if( libcdata_array_free(
154
5.96k
         &( ( *inode_btree )->inode_information_array ),
155
5.96k
         (int (*)(intptr_t **, libcerror_error_t **)) &libfsxfs_inode_information_free,
156
5.96k
         error ) != 1 )
157
0
    {
158
0
      libcerror_error_set(
159
0
       error,
160
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
161
0
       LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
162
0
       "%s: unable to free inode information array.",
163
0
       function );
164
165
0
      result = -1;
166
0
    }
167
5.96k
    memory_free(
168
5.96k
     *inode_btree );
169
170
5.96k
    *inode_btree = NULL;
171
5.96k
  }
172
5.96k
  return( result );
173
5.96k
}
174
175
/* Reads the inode information
176
 * Returns 1 if successful or -1 on error
177
 */
178
int libfsxfs_inode_btree_read_inode_information(
179
     libfsxfs_inode_btree_t *inode_btree,
180
     libfsxfs_io_handle_t *io_handle,
181
     libbfio_handle_t *file_io_handle,
182
     off64_t file_offset,
183
     libcerror_error_t **error )
184
13.5k
{
185
13.5k
  libfsxfs_inode_information_t *inode_information = NULL;
186
13.5k
  static char *function                           = "libfsxfs_inode_btree_read_inode_information";
187
13.5k
  int entry_index                                 = 0;
188
189
13.5k
  if( inode_btree == NULL )
190
0
  {
191
0
    libcerror_error_set(
192
0
     error,
193
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
194
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
195
0
     "%s: invalid inode B+ tree.",
196
0
     function );
197
198
0
    return( -1 );
199
0
  }
200
13.5k
  if( libfsxfs_inode_information_initialize(
201
13.5k
       &inode_information,
202
13.5k
       error ) != 1 )
203
0
  {
204
0
    libcerror_error_set(
205
0
     error,
206
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
207
0
     LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
208
0
     "%s: unable to create inode information.",
209
0
     function );
210
211
0
    goto on_error;
212
0
  }
213
13.5k
  if( libfsxfs_inode_information_read_file_io_handle(
214
13.5k
       inode_information,
215
13.5k
       io_handle,
216
13.5k
       file_io_handle,
217
13.5k
       file_offset,
218
13.5k
       error ) != 1 )
219
5.67k
  {
220
5.67k
    libcerror_error_set(
221
5.67k
     error,
222
5.67k
     LIBCERROR_ERROR_DOMAIN_IO,
223
5.67k
     LIBCERROR_IO_ERROR_READ_FAILED,
224
5.67k
     "%s: unable to read inode information at offset: %" PRIi64 " (0x%08" PRIx64 ").",
225
5.67k
     function,
226
5.67k
     file_offset,
227
5.67k
     file_offset );
228
229
5.67k
    goto on_error;
230
5.67k
  }
231
7.83k
  if( libcdata_array_append_entry(
232
7.83k
       inode_btree->inode_information_array,
233
7.83k
       &entry_index,
234
7.83k
       (intptr_t *) inode_information,
235
7.83k
       error ) != 1 )
236
0
  {
237
0
    libcerror_error_set(
238
0
     error,
239
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
240
0
     LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
241
0
     "%s: unable to append inode information to array.",
242
0
     function );
243
244
0
    goto on_error;
245
0
  }
246
7.83k
  return( 1 );
247
248
5.67k
on_error:
249
5.67k
  if( inode_information != NULL )
250
5.67k
  {
251
5.67k
    libfsxfs_inode_information_free(
252
5.67k
     &inode_information,
253
5.67k
     NULL );
254
5.67k
  }
255
5.67k
  return( -1 );
256
7.83k
}
257
258
/* Retrieves the inode from the inode B+ tree branch node
259
 * Returns 1 if successful or -1 on error
260
 */
261
int libfsxfs_inode_btree_get_inode_from_branch_node(
262
     libfsxfs_inode_btree_t *inode_btree,
263
     libfsxfs_io_handle_t *io_handle,
264
     libbfio_handle_t *file_io_handle,
265
     uint64_t allocation_group_block_number,
266
     uint16_t number_of_records,
267
     const uint8_t *records_data,
268
     size_t records_data_size,
269
     uint64_t relative_inode_number,
270
     int recursion_depth,
271
     libcerror_error_t **error )
272
3.91k
{
273
3.91k
  static char *function              = "libfsxfs_inode_btree_get_inode_from_branch_node";
274
3.91k
  size_t number_of_key_value_pairs   = 0;
275
3.91k
  size_t records_data_offset         = 0;
276
3.91k
  uint32_t relative_key_inode_number = 0;
277
3.91k
  uint32_t relative_sub_block_number = 0;
278
3.91k
  uint16_t record_index              = 0;
279
3.91k
  int result                         = 0;
280
281
3.91k
  if( inode_btree == NULL )
282
0
  {
283
0
    libcerror_error_set(
284
0
     error,
285
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
286
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
287
0
     "%s: invalid inode B+ tree.",
288
0
     function );
289
290
0
    return( -1 );
291
0
  }
292
3.91k
  if( records_data == NULL )
293
0
  {
294
0
    libcerror_error_set(
295
0
     error,
296
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
297
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
298
0
     "%s: invalid records data.",
299
0
     function );
300
301
0
    return( -1 );
302
0
  }
303
3.91k
  if( ( records_data_size == 0 )
304
3.91k
   || ( records_data_size > (size_t) SSIZE_MAX ) )
305
0
  {
306
0
    libcerror_error_set(
307
0
     error,
308
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
309
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
310
0
     "%s: invalid records data size value out of bounds.",
311
0
     function );
312
313
0
    return( -1 );
314
0
  }
315
3.91k
  if( ( recursion_depth < 0 )
316
3.91k
   || ( recursion_depth > LIBFSXFS_MAXIMUM_RECURSION_DEPTH ) )
317
14
  {
318
14
    libcerror_error_set(
319
14
     error,
320
14
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
321
14
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
322
14
     "%s: invalid recursion depth value out of bounds.",
323
14
     function );
324
325
14
    return( -1 );
326
14
  }
327
3.90k
  number_of_key_value_pairs = records_data_size / 8;
328
329
3.90k
  if( (size_t) number_of_records > number_of_key_value_pairs )
330
27
  {
331
27
    libcerror_error_set(
332
27
     error,
333
27
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
334
27
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
335
27
     "%s: invalid number of records value out of bounds.",
336
27
     function );
337
338
27
    return( -1 );
339
27
  }
340
3.87k
  for( record_index = 0;
341
38.2k
       record_index < number_of_records;
342
34.4k
       record_index++ )
343
36.8k
  {
344
36.8k
    byte_stream_copy_to_uint32_big_endian(
345
36.8k
     &( records_data[ records_data_offset ] ),
346
36.8k
     relative_key_inode_number );
347
348
36.8k
    records_data_offset += 4;
349
350
#if defined( HAVE_DEBUG_OUTPUT )
351
    if( libcnotify_verbose != 0 )
352
    {
353
      libcnotify_printf(
354
       "%s: inode number\t\t: %" PRIu32 "\n",
355
       function,
356
       relative_key_inode_number );
357
    }
358
#endif
359
36.8k
    if( relative_inode_number < relative_key_inode_number )
360
2.42k
    {
361
2.42k
      break;
362
2.42k
    }
363
36.8k
  }
364
3.87k
  if( ( record_index > 0 )
365
3.83k
   && ( record_index <= number_of_records ) )
366
3.83k
  {
367
3.83k
    records_data_offset = ( number_of_key_value_pairs + record_index - 1 ) * 4;
368
369
3.83k
    byte_stream_copy_to_uint32_big_endian(
370
3.83k
     &( records_data[ records_data_offset ] ),
371
3.83k
     relative_sub_block_number );
372
373
#if defined( HAVE_DEBUG_OUTPUT )
374
    if( libcnotify_verbose != 0 )
375
    {
376
      libcnotify_printf(
377
       "%s: sub block number\t: %" PRIu32 "\n",
378
       function,
379
       relative_sub_block_number );
380
381
      libcnotify_printf(
382
       "\n" );
383
    }
384
#endif
385
3.83k
    result = libfsxfs_inode_btree_get_inode_from_node(
386
3.83k
              inode_btree,
387
3.83k
              io_handle,
388
3.83k
              file_io_handle,
389
3.83k
              allocation_group_block_number,
390
3.83k
              relative_sub_block_number,
391
3.83k
              relative_inode_number,
392
3.83k
              recursion_depth + 1,
393
3.83k
              error );
394
395
3.83k
    if( result == -1 )
396
3.77k
    {
397
3.77k
      libcerror_error_set(
398
3.77k
       error,
399
3.77k
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
400
3.77k
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
401
3.77k
       "%s: unable to retrieve inode from node.",
402
3.77k
       function );
403
404
3.77k
      return( -1 );
405
3.77k
    }
406
3.83k
  }
407
#if defined( HAVE_DEBUG_OUTPUT )
408
  else if( libcnotify_verbose != 0 )
409
  {
410
    libcnotify_printf(
411
     "\n" );
412
  }
413
#endif
414
104
  return( result );
415
3.87k
}
416
417
/* Retrieves the inode from the inode B+ tree leaf node
418
 * Returns 1 if successful or -1 on error
419
 */
420
int libfsxfs_inode_btree_get_inode_from_leaf_node(
421
     libfsxfs_inode_btree_t *inode_btree,
422
     uint16_t number_of_records,
423
     const uint8_t *records_data,
424
     size_t records_data_size,
425
     uint64_t inode_number,
426
     libcerror_error_t **error )
427
5.19k
{
428
5.19k
  libfsxfs_inode_btree_record_t *inode_btree_record = NULL;
429
5.19k
  static char *function                             = "libfsxfs_inode_btree_get_inode_from_leaf_node";
430
5.19k
  size_t records_data_offset                        = 0;
431
5.19k
  uint16_t record_index                             = 0;
432
5.19k
  int result                                        = 0;
433
434
5.19k
  if( inode_btree == NULL )
435
0
  {
436
0
    libcerror_error_set(
437
0
     error,
438
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
439
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
440
0
     "%s: invalid inode B+ tree.",
441
0
     function );
442
443
0
    return( -1 );
444
0
  }
445
5.19k
  if( records_data == NULL )
446
0
  {
447
0
    libcerror_error_set(
448
0
     error,
449
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
450
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
451
0
     "%s: invalid records data.",
452
0
     function );
453
454
0
    return( -1 );
455
0
  }
456
5.19k
  if( ( records_data_size == 0 )
457
5.19k
   || ( records_data_size > (size_t) SSIZE_MAX ) )
458
0
  {
459
0
    libcerror_error_set(
460
0
     error,
461
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
462
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
463
0
     "%s: invalid records data size value out of bounds.",
464
0
     function );
465
466
0
    return( -1 );
467
0
  }
468
5.19k
  if( (size_t) number_of_records > ( records_data_size / 16 ) )
469
3
  {
470
3
    libcerror_error_set(
471
3
     error,
472
3
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
473
3
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
474
3
     "%s: invalid number of records value out of bounds.",
475
3
     function );
476
477
3
    return( -1 );
478
3
  }
479
5.19k
  for( record_index = 0;
480
33.7k
       record_index < number_of_records;
481
28.5k
       record_index++ )
482
33.6k
  {
483
33.6k
    if( libfsxfs_inode_btree_record_initialize(
484
33.6k
         &inode_btree_record,
485
33.6k
         error ) != 1 )
486
0
    {
487
0
      libcerror_error_set(
488
0
       error,
489
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
490
0
       LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
491
0
       "%s: unable to create inode B+ tree record: %" PRIu16 ".",
492
0
       function,
493
0
       record_index );
494
495
0
      goto on_error;
496
0
    }
497
33.6k
    if( libfsxfs_inode_btree_record_read_data(
498
33.6k
         inode_btree_record,
499
33.6k
         &( records_data[ records_data_offset ] ),
500
33.6k
         16,
501
33.6k
         error ) != 1 )
502
0
    {
503
0
      libcerror_error_set(
504
0
       error,
505
0
       LIBCERROR_ERROR_DOMAIN_IO,
506
0
       LIBCERROR_IO_ERROR_READ_FAILED,
507
0
       "%s: unable to read inode B+ tree record: %" PRIu16 ".",
508
0
       function,
509
0
       record_index );
510
511
0
      goto on_error;
512
0
    }
513
33.6k
    records_data_offset += 16;
514
515
33.6k
    if( ( inode_number >= inode_btree_record->inode_number )
516
6.01k
     && ( inode_number < ( inode_btree_record->inode_number + 64 ) ) )
517
5.13k
    {
518
/* TODO check bitmap */
519
5.13k
      result = 1;
520
5.13k
    }
521
/* TODO cache records in block */
522
33.6k
    if( libfsxfs_inode_btree_record_free(
523
33.6k
         &inode_btree_record,
524
33.6k
         error ) != 1 )
525
0
    {
526
0
      libcerror_error_set(
527
0
       error,
528
0
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
529
0
       LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
530
0
       "%s: unable to free inode B+ tree record: %" PRIu16 ".",
531
0
       function,
532
0
       record_index );
533
534
0
      goto on_error;
535
0
    }
536
33.6k
    if( result != 0 )
537
5.13k
    {
538
5.13k
      break;
539
5.13k
    }
540
33.6k
  }
541
5.19k
  return( result );
542
543
0
on_error:
544
0
  if( inode_btree_record != NULL )
545
0
  {
546
0
    libfsxfs_inode_btree_record_free(
547
0
     &inode_btree_record,
548
0
     NULL );
549
0
  }
550
0
  return( -1 );
551
5.19k
}
552
553
/* Retrieves the inode from the inode B+ tree node
554
 * Returns 1 if successful or -1 on error
555
 */
556
int libfsxfs_inode_btree_get_inode_from_node(
557
     libfsxfs_inode_btree_t *inode_btree,
558
     libfsxfs_io_handle_t *io_handle,
559
     libbfio_handle_t *file_io_handle,
560
     uint64_t allocation_group_block_number,
561
     uint64_t relative_block_number,
562
     uint64_t relative_inode_number,
563
     int recursion_depth,
564
     libcerror_error_t **error )
565
9.31k
{
566
9.31k
  libfsxfs_btree_block_t *btree_block = NULL;
567
9.31k
  static char *function               = "libfsxfs_inode_btree_get_inode_from_node";
568
9.31k
  off64_t btree_block_offset          = 0;
569
9.31k
  int compare_result                  = 0;
570
9.31k
  int result                          = 0;
571
572
9.31k
  if( inode_btree == NULL )
573
0
  {
574
0
    libcerror_error_set(
575
0
     error,
576
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
577
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
578
0
     "%s: invalid inode B+ tree.",
579
0
     function );
580
581
0
    return( -1 );
582
0
  }
583
9.31k
  if( io_handle == NULL )
584
0
  {
585
0
    libcerror_error_set(
586
0
     error,
587
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
588
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
589
0
     "%s: invalid IO handle.",
590
0
     function );
591
592
0
    return( -1 );
593
0
  }
594
9.31k
  if( io_handle->block_size == 0 )
595
0
  {
596
0
    libcerror_error_set(
597
0
     error,
598
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
599
0
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
600
0
     "%s: invalid IO handle - block size value out of bounds.",
601
0
     function );
602
603
0
    return( -1 );
604
0
  }
605
9.31k
  if( allocation_group_block_number > (uint64_t) ( INT64_MAX / io_handle->block_size ) )
606
0
  {
607
0
    libcerror_error_set(
608
0
     error,
609
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
610
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
611
0
     "%s: invalid allocation group block number value out of bounds.",
612
0
     function );
613
614
0
    return( -1 );
615
0
  }
616
9.31k
  if( relative_block_number > ( (uint64_t) ( INT64_MAX / io_handle->block_size ) - allocation_group_block_number ) )
617
0
  {
618
0
    libcerror_error_set(
619
0
     error,
620
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
621
0
     LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
622
0
     "%s: invalid relative block number value out of bounds.",
623
0
     function );
624
625
0
    return( -1 );
626
0
  }
627
9.31k
  btree_block_offset = ( allocation_group_block_number + relative_block_number ) * io_handle->block_size;
628
629
9.31k
  if( libfsxfs_btree_block_initialize(
630
9.31k
       &btree_block,
631
9.31k
       io_handle->block_size,
632
9.31k
       4,
633
9.31k
       error ) != 1 )
634
0
  {
635
0
    libcerror_error_set(
636
0
     error,
637
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
638
0
     LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
639
0
     "%s: unable to create B+ tree block.",
640
0
     function );
641
642
0
    goto on_error;
643
0
  }
644
9.31k
  if( libfsxfs_btree_block_read_file_io_handle(
645
9.31k
       btree_block,
646
9.31k
       io_handle,
647
9.31k
       file_io_handle,
648
9.31k
       btree_block_offset,
649
9.31k
       error ) != 1 )
650
139
  {
651
139
    libcerror_error_set(
652
139
     error,
653
139
     LIBCERROR_ERROR_DOMAIN_IO,
654
139
     LIBCERROR_IO_ERROR_READ_FAILED,
655
139
     "%s: unable to read inode B+ tree block: %" PRIu32 " at offset: %" PRIi64 " (0x%08" PRIx64 ").",
656
139
     function,
657
139
     relative_block_number,
658
139
     btree_block_offset,
659
139
     btree_block_offset );
660
661
139
    goto on_error;
662
139
  }
663
9.17k
  if( io_handle->format_version == 5 )
664
3.74k
  {
665
3.74k
    compare_result = memory_compare(
666
3.74k
                      btree_block->header->signature,
667
3.74k
                      "IAB3",
668
3.74k
                      4 );
669
3.74k
  }
670
5.43k
  else
671
5.43k
  {
672
5.43k
    compare_result = memory_compare(
673
5.43k
                      btree_block->header->signature,
674
5.43k
                      "IABT",
675
5.43k
                      4 );
676
5.43k
  }
677
9.17k
  if( compare_result != 0 )
678
66
  {
679
66
    libcerror_error_set(
680
66
     error,
681
66
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
682
66
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
683
66
     "%s: unsupported block signature.",
684
66
     function );
685
686
66
    goto on_error;
687
66
  }
688
/* TODO
689
  if( btree_block->header->level > inode_btree->maximum_depth )
690
  {
691
    libcerror_error_set(
692
     error,
693
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
694
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
695
     "%s: unsupported B+ tree node level.",
696
     function );
697
698
    goto on_error;
699
  }
700
*/
701
9.11k
  if( btree_block->header->level == 0 )
702
5.19k
  {
703
5.19k
    result = libfsxfs_inode_btree_get_inode_from_leaf_node(
704
5.19k
              inode_btree,
705
5.19k
              btree_block->header->number_of_records,
706
5.19k
              btree_block->records_data,
707
5.19k
              btree_block->records_data_size,
708
5.19k
              relative_inode_number,
709
5.19k
              error );
710
711
5.19k
    if( result == -1 )
712
3
    {
713
3
      libcerror_error_set(
714
3
       error,
715
3
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
716
3
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
717
3
       "%s: unable to retrieve inode from leaf node.",
718
3
       function );
719
720
3
      goto on_error;
721
3
    }
722
5.19k
  }
723
3.91k
  else
724
3.91k
  {
725
3.91k
    result = libfsxfs_inode_btree_get_inode_from_branch_node(
726
3.91k
              inode_btree,
727
3.91k
              io_handle,
728
3.91k
              file_io_handle,
729
3.91k
              allocation_group_block_number,
730
3.91k
              btree_block->header->number_of_records,
731
3.91k
              btree_block->records_data,
732
3.91k
              btree_block->records_data_size,
733
3.91k
              relative_inode_number,
734
3.91k
              recursion_depth,
735
3.91k
              error );
736
737
3.91k
    if( result == -1 )
738
3.81k
    {
739
3.81k
      libcerror_error_set(
740
3.81k
       error,
741
3.81k
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
742
3.81k
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
743
3.81k
       "%s: unable to retrieve inode from branch node.",
744
3.81k
       function );
745
746
3.81k
      goto on_error;
747
3.81k
    }
748
3.91k
  }
749
5.29k
  if( libfsxfs_btree_block_free(
750
5.29k
       &btree_block,
751
5.29k
       error ) != 1 )
752
0
  {
753
0
    libcerror_error_set(
754
0
     error,
755
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
756
0
     LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
757
0
     "%s: unable to free B+ tree block.",
758
0
     function );
759
760
0
    goto on_error;
761
0
  }
762
5.29k
  return( result );
763
764
4.02k
on_error:
765
4.02k
  if( btree_block != NULL )
766
4.02k
  {
767
4.02k
    libfsxfs_btree_block_free(
768
4.02k
     &btree_block,
769
4.02k
     NULL );
770
4.02k
  }
771
4.02k
  return( -1 );
772
5.29k
}
773
774
/* Retrieves a specific inode from the inode B+ tree
775
 * Returns 1 if successful, 0 if no such value or -1 on error
776
 */
777
int libfsxfs_inode_btree_get_inode_by_number(
778
     libfsxfs_inode_btree_t *inode_btree,
779
     libfsxfs_io_handle_t *io_handle,
780
     libbfio_handle_t *file_io_handle,
781
     uint64_t absolute_inode_number,
782
     off64_t *file_offset,
783
     libcerror_error_t **error )
784
5.73k
{
785
5.73k
  libfsxfs_inode_information_t *inode_information = NULL;
786
5.73k
  static char *function                           = "libfsxfs_inode_btree_get_inode_by_number";
787
5.73k
  uint64_t allocation_group_block_number          = 0;
788
5.73k
  uint64_t relative_inode_number                  = 0;
789
5.73k
  int allocation_group_index                      = 0;
790
5.73k
  int result                                      = 0;
791
792
5.73k
  if( inode_btree == NULL )
793
0
  {
794
0
    libcerror_error_set(
795
0
     error,
796
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
797
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
798
0
     "%s: invalid inode B+ tree.",
799
0
     function );
800
801
0
    return( -1 );
802
0
  }
803
5.73k
  if( io_handle == NULL )
804
0
  {
805
0
    libcerror_error_set(
806
0
     error,
807
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
808
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
809
0
     "%s: invalid IO handle.",
810
0
     function );
811
812
0
    return( -1 );
813
0
  }
814
5.73k
  if( io_handle->allocation_group_size == 0 )
815
0
  {
816
0
    libcerror_error_set(
817
0
     error,
818
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
819
0
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
820
0
     "%s: invalid IO handle - allocation group size value out of bounds.",
821
0
     function );
822
823
0
    return( -1 );
824
0
  }
825
5.73k
  if( io_handle->block_size == 0 )
826
0
  {
827
0
    libcerror_error_set(
828
0
     error,
829
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
830
0
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
831
0
     "%s: invalid IO handle - block size value out of bounds.",
832
0
     function );
833
834
0
    return( -1 );
835
0
  }
836
5.73k
  if( file_offset == NULL )
837
0
  {
838
0
    libcerror_error_set(
839
0
     error,
840
0
     LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
841
0
     LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
842
0
     "%s: invalid file offset.",
843
0
     function );
844
845
0
    return( -1 );
846
0
  }
847
5.73k
  allocation_group_index = (int) ( absolute_inode_number >> io_handle->number_of_relative_inode_number_bits );
848
5.73k
  relative_inode_number  = absolute_inode_number & ( ( (uint64_t) 1 << io_handle->number_of_relative_inode_number_bits ) - 1 );
849
850
#if defined( HAVE_DEBUG_OUTPUT )
851
  if( libcnotify_verbose != 0 )
852
  {
853
    libcnotify_printf(
854
     "%s: allocation group index\t: %d\n",
855
     function,
856
     allocation_group_index );
857
858
    libcnotify_printf(
859
     "%s: relative inode number\t\t: %" PRIu64 "\n",
860
     function,
861
     relative_inode_number );
862
863
    libcnotify_printf(
864
     "\n" );
865
  }
866
#endif /* defined( HAVE_DEBUG_OUTPUT ) */
867
868
5.73k
  if( libcdata_array_get_entry_by_index(
869
5.73k
       inode_btree->inode_information_array,
870
5.73k
       allocation_group_index,
871
5.73k
       (intptr_t **) &inode_information,
872
5.73k
       error ) != 1 )
873
256
  {
874
256
    libcerror_error_set(
875
256
     error,
876
256
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
877
256
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
878
256
     "%s: unable to retrieve inode information: %d.",
879
256
     function,
880
256
     allocation_group_index );
881
882
256
    return( -1 );
883
256
  }
884
5.47k
  allocation_group_block_number = (uint64_t) allocation_group_index * io_handle->allocation_group_size;
885
886
5.47k
  if( inode_information == NULL )
887
0
  {
888
0
    libcerror_error_set(
889
0
     error,
890
0
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
891
0
     LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
892
0
     "%s: missing inode information.",
893
0
     function );
894
895
0
    return( -1 );
896
0
  }
897
5.47k
  result = libfsxfs_inode_btree_get_inode_from_node(
898
5.47k
            inode_btree,
899
5.47k
            io_handle,
900
5.47k
            file_io_handle,
901
5.47k
            allocation_group_block_number,
902
5.47k
            inode_information->inode_btree_root_block_number,
903
5.47k
            relative_inode_number,
904
5.47k
            0,
905
5.47k
            error );
906
907
5.47k
  if( result == -1 )
908
249
  {
909
249
    libcerror_error_set(
910
249
     error,
911
249
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
912
249
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
913
249
     "%s: unable to retrieve inode: %" PRIu64 " from root node: %" PRIu32 ".",
914
249
     function,
915
249
     relative_inode_number,
916
249
     inode_information->inode_btree_root_block_number );
917
918
249
    return( -1 );
919
249
  }
920
5.22k
  else if( result != 0 )
921
5.13k
  {
922
5.13k
    *file_offset = ( (off64_t) allocation_group_block_number * io_handle->block_size ) + ( (off64_t) relative_inode_number * io_handle->inode_size );
923
5.13k
  }
924
5.22k
  return( result );
925
5.47k
}
926