Coverage Report

Created: 2026-04-10 07:49

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-2025, 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
4.24k
{
46
4.24k
  static char *function = "libfsxfs_inode_btree_initialize";
47
48
4.24k
  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
4.24k
  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
4.24k
  *inode_btree = memory_allocate_structure(
71
4.24k
                  libfsxfs_inode_btree_t );
72
73
4.24k
  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
4.24k
  if( memory_set(
85
4.24k
       *inode_btree,
86
4.24k
       0,
87
4.24k
       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
4.24k
  if( libcdata_array_initialize(
104
4.24k
       &( ( *inode_btree )->inode_information_array ),
105
4.24k
       0,
106
4.24k
       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
4.24k
  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
4.24k
}
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
4.24k
{
137
4.24k
  static char *function = "libfsxfs_inode_btree_free";
138
4.24k
  int result            = 1;
139
140
4.24k
  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
4.24k
  if( *inode_btree != NULL )
152
4.24k
  {
153
4.24k
    if( libcdata_array_free(
154
4.24k
         &( ( *inode_btree )->inode_information_array ),
155
4.24k
         (int (*)(intptr_t **, libcerror_error_t **)) &libfsxfs_inode_information_free,
156
4.24k
         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
4.24k
    memory_free(
168
4.24k
     *inode_btree );
169
170
4.24k
    *inode_btree = NULL;
171
4.24k
  }
172
4.24k
  return( result );
173
4.24k
}
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
15.6k
{
185
15.6k
  libfsxfs_inode_information_t *inode_information = NULL;
186
15.6k
  static char *function                           = "libfsxfs_file_system_read_inode_information";
187
15.6k
  int entry_index                                 = 0;
188
189
15.6k
  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
15.6k
  if( libfsxfs_inode_information_initialize(
201
15.6k
       &inode_information,
202
15.6k
       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
15.6k
  if( libfsxfs_inode_information_read_file_io_handle(
214
15.6k
       inode_information,
215
15.6k
       io_handle,
216
15.6k
       file_io_handle,
217
15.6k
       file_offset,
218
15.6k
       error ) != 1 )
219
9.03k
  {
220
9.03k
    libcerror_error_set(
221
9.03k
     error,
222
9.03k
     LIBCERROR_ERROR_DOMAIN_IO,
223
9.03k
     LIBCERROR_IO_ERROR_READ_FAILED,
224
9.03k
     "%s: unable to read inode information at offset: %" PRIi64 " (0x%08" PRIx64 ").",
225
9.03k
     function,
226
9.03k
     file_offset,
227
9.03k
     file_offset );
228
229
9.03k
    goto on_error;
230
9.03k
  }
231
6.61k
  if( libcdata_array_append_entry(
232
6.61k
       inode_btree->inode_information_array,
233
6.61k
       &entry_index,
234
6.61k
       (intptr_t *) inode_information,
235
6.61k
       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
6.61k
  return( 1 );
247
248
9.03k
on_error:
249
9.03k
  if( inode_information != NULL )
250
9.03k
  {
251
9.03k
    libfsxfs_inode_information_free(
252
9.03k
     &inode_information,
253
9.03k
     NULL );
254
9.03k
  }
255
9.03k
  return( -1 );
256
6.61k
}
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
5.73k
{
273
5.73k
  static char *function              = "libfsxfs_inode_btree_get_inode_from_branch_node";
274
5.73k
  size_t number_of_key_value_pairs   = 0;
275
5.73k
  size_t records_data_offset         = 0;
276
5.73k
  uint32_t relative_key_inode_number = 0;
277
5.73k
  uint32_t relative_sub_block_number = 0;
278
5.73k
  uint16_t record_index              = 0;
279
5.73k
  int result                         = 0;
280
281
5.73k
  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
5.73k
  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
5.73k
  if( ( records_data_size == 0 )
304
5.73k
   || ( 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
5.73k
  if( ( recursion_depth < 0 )
316
5.73k
   || ( recursion_depth > LIBFSXFS_MAXIMUM_RECURSION_DEPTH ) )
317
21
  {
318
21
    libcerror_error_set(
319
21
     error,
320
21
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
321
21
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
322
21
     "%s: invalid recursion depth value out of bounds.",
323
21
     function );
324
325
21
    return( -1 );
326
21
  }
327
5.71k
  number_of_key_value_pairs = records_data_size / 8;
328
329
5.71k
  if( (size_t) number_of_records > number_of_key_value_pairs )
330
28
  {
331
28
    libcerror_error_set(
332
28
     error,
333
28
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
334
28
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
335
28
     "%s: invalid number of records value out of bounds.",
336
28
     function );
337
338
28
    return( -1 );
339
28
  }
340
5.68k
  for( record_index = 0;
341
36.8k
       record_index < number_of_records;
342
31.1k
       record_index++ )
343
35.6k
  {
344
35.6k
    byte_stream_copy_to_uint32_big_endian(
345
35.6k
     &( records_data[ records_data_offset ] ),
346
35.6k
     relative_key_inode_number );
347
348
35.6k
    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
35.6k
    if( relative_inode_number < relative_key_inode_number )
360
4.49k
    {
361
4.49k
      break;
362
4.49k
    }
363
35.6k
  }
364
5.68k
  if( ( record_index > 0 )
365
5.64k
   && ( record_index <= number_of_records ) )
366
5.64k
  {
367
5.64k
    records_data_offset = ( number_of_key_value_pairs + record_index - 1 ) * 4;
368
369
5.64k
    byte_stream_copy_to_uint32_big_endian(
370
5.64k
     &( records_data[ records_data_offset ] ),
371
5.64k
     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
5.64k
    result = libfsxfs_inode_btree_get_inode_from_node(
386
5.64k
              inode_btree,
387
5.64k
              io_handle,
388
5.64k
              file_io_handle,
389
5.64k
              allocation_group_block_number,
390
5.64k
              relative_sub_block_number,
391
5.64k
              relative_inode_number,
392
5.64k
              recursion_depth + 1,
393
5.64k
              error );
394
395
5.64k
    if( result == -1 )
396
5.57k
    {
397
5.57k
      libcerror_error_set(
398
5.57k
       error,
399
5.57k
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
400
5.57k
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
401
5.57k
       "%s: unable to retrieve inode from node.",
402
5.57k
       function );
403
404
5.57k
      return( -1 );
405
5.57k
    }
406
5.64k
  }
407
#if defined( HAVE_DEBUG_OUTPUT )
408
  else if( libcnotify_verbose != 0 )
409
  {
410
    libcnotify_printf(
411
     "\n" );
412
  }
413
#endif
414
108
  return( result );
415
5.68k
}
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
3.36k
{
428
3.36k
  libfsxfs_inode_btree_record_t *inode_btree_record = NULL;
429
3.36k
  static char *function                             = "libfsxfs_inode_btree_get_inode_from_leaf_node";
430
3.36k
  size_t records_data_offset                        = 0;
431
3.36k
  uint16_t record_index                             = 0;
432
3.36k
  int result                                        = 0;
433
434
3.36k
  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
3.36k
  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
3.36k
  if( ( records_data_size == 0 )
457
3.36k
   || ( 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
3.36k
  if( (size_t) number_of_records > ( records_data_size / 16 ) )
469
22
  {
470
22
    libcerror_error_set(
471
22
     error,
472
22
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
473
22
     LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
474
22
     "%s: invalid number of records value out of bounds.",
475
22
     function );
476
477
22
    return( -1 );
478
22
  }
479
3.34k
  for( record_index = 0;
480
26.5k
       record_index < number_of_records;
481
23.2k
       record_index++ )
482
26.5k
  {
483
26.5k
    if( libfsxfs_inode_btree_record_initialize(
484
26.5k
         &inode_btree_record,
485
26.5k
         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
26.5k
    if( libfsxfs_inode_btree_record_read_data(
498
26.5k
         inode_btree_record,
499
26.5k
         &( records_data[ records_data_offset ] ),
500
26.5k
         16,
501
26.5k
         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
26.5k
    records_data_offset += 16;
514
515
26.5k
    if( ( inode_number >= inode_btree_record->inode_number )
516
4.51k
     && ( inode_number < ( inode_btree_record->inode_number + 64 ) ) )
517
3.27k
    {
518
/* TODO check bitmap */
519
3.27k
      result = 1;
520
3.27k
    }
521
/* TODO cache records in block */
522
26.5k
    if( libfsxfs_inode_btree_record_free(
523
26.5k
         &inode_btree_record,
524
26.5k
         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
26.5k
    if( result != 0 )
537
3.27k
    {
538
3.27k
      break;
539
3.27k
    }
540
26.5k
  }
541
3.34k
  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
3.34k
}
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.34k
{
566
9.34k
  libfsxfs_btree_block_t *btree_block = NULL;
567
9.34k
  static char *function               = "libfsxfs_inode_btree_get_inode_from_node";
568
9.34k
  off64_t btree_block_offset          = 0;
569
9.34k
  int compare_result                  = 0;
570
9.34k
  int result                          = 0;
571
572
9.34k
  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.34k
  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.34k
  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.34k
  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.34k
  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.34k
  btree_block_offset = ( allocation_group_block_number + relative_block_number ) * io_handle->block_size;
628
629
9.34k
  if( libfsxfs_btree_block_initialize(
630
9.34k
       &btree_block,
631
9.34k
       io_handle->block_size,
632
9.34k
       4,
633
9.34k
       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.34k
  if( libfsxfs_btree_block_read_file_io_handle(
645
9.34k
       btree_block,
646
9.34k
       io_handle,
647
9.34k
       file_io_handle,
648
9.34k
       btree_block_offset,
649
9.34k
       error ) != 1 )
650
171
  {
651
171
    libcerror_error_set(
652
171
     error,
653
171
     LIBCERROR_ERROR_DOMAIN_IO,
654
171
     LIBCERROR_IO_ERROR_READ_FAILED,
655
171
     "%s: unable to read inode B+ tree block: %" PRIu32 " at offset: %" PRIi64 " (0x%08" PRIx64 ").",
656
171
     function,
657
171
     relative_block_number,
658
171
     btree_block_offset,
659
171
     btree_block_offset );
660
661
171
    goto on_error;
662
171
  }
663
9.17k
  if( io_handle->format_version == 5 )
664
4.69k
  {
665
4.69k
    compare_result = memory_compare(
666
4.69k
                      btree_block->header->signature,
667
4.69k
                      "IAB3",
668
4.69k
                      4 );
669
4.69k
  }
670
4.48k
  else
671
4.48k
  {
672
4.48k
    compare_result = memory_compare(
673
4.48k
                      btree_block->header->signature,
674
4.48k
                      "IABT",
675
4.48k
                      4 );
676
4.48k
  }
677
9.17k
  if( compare_result != 0 )
678
74
  {
679
74
    libcerror_error_set(
680
74
     error,
681
74
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
682
74
     LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
683
74
     "%s: unsupported block signature.",
684
74
     function );
685
686
74
    goto on_error;
687
74
  }
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.10k
  if( btree_block->header->level == 0 )
702
3.36k
  {
703
3.36k
    result = libfsxfs_inode_btree_get_inode_from_leaf_node(
704
3.36k
              inode_btree,
705
3.36k
              btree_block->header->number_of_records,
706
3.36k
              btree_block->records_data,
707
3.36k
              btree_block->records_data_size,
708
3.36k
              relative_inode_number,
709
3.36k
              error );
710
711
3.36k
    if( result == -1 )
712
22
    {
713
22
      libcerror_error_set(
714
22
       error,
715
22
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
716
22
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
717
22
       "%s: unable to retrieve inode from leaf node.",
718
22
       function );
719
720
22
      goto on_error;
721
22
    }
722
3.36k
  }
723
5.73k
  else
724
5.73k
  {
725
5.73k
    result = libfsxfs_inode_btree_get_inode_from_branch_node(
726
5.73k
              inode_btree,
727
5.73k
              io_handle,
728
5.73k
              file_io_handle,
729
5.73k
              allocation_group_block_number,
730
5.73k
              btree_block->header->number_of_records,
731
5.73k
              btree_block->records_data,
732
5.73k
              btree_block->records_data_size,
733
5.73k
              relative_inode_number,
734
5.73k
              recursion_depth,
735
5.73k
              error );
736
737
5.73k
    if( result == -1 )
738
5.62k
    {
739
5.62k
      libcerror_error_set(
740
5.62k
       error,
741
5.62k
       LIBCERROR_ERROR_DOMAIN_RUNTIME,
742
5.62k
       LIBCERROR_RUNTIME_ERROR_GET_FAILED,
743
5.62k
       "%s: unable to retrieve inode from branch node.",
744
5.62k
       function );
745
746
5.62k
      goto on_error;
747
5.62k
    }
748
5.73k
  }
749
3.45k
  if( libfsxfs_btree_block_free(
750
3.45k
       &btree_block,
751
3.45k
       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
3.45k
  return( result );
763
764
5.89k
on_error:
765
5.89k
  if( btree_block != NULL )
766
5.89k
  {
767
5.89k
    libfsxfs_btree_block_free(
768
5.89k
     &btree_block,
769
5.89k
     NULL );
770
5.89k
  }
771
5.89k
  return( -1 );
772
3.45k
}
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
3.97k
{
785
3.97k
  libfsxfs_inode_information_t *inode_information = NULL;
786
3.97k
  static char *function                           = "libfsxfs_inode_btree_get_inode_by_number";
787
3.97k
  uint64_t allocation_group_block_number          = 0;
788
3.97k
  uint64_t relative_inode_number                  = 0;
789
3.97k
  int allocation_group_index                      = 0;
790
3.97k
  int result                                      = 0;
791
792
3.97k
  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
3.97k
  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
3.97k
  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
3.97k
  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
3.97k
  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
3.97k
  allocation_group_index = (int) ( absolute_inode_number >> io_handle->number_of_relative_inode_number_bits );
848
3.97k
  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
3.97k
  if( libcdata_array_get_entry_by_index(
869
3.97k
       inode_btree->inode_information_array,
870
3.97k
       allocation_group_index,
871
3.97k
       (intptr_t **) &inode_information,
872
3.97k
       error ) != 1 )
873
268
  {
874
268
    libcerror_error_set(
875
268
     error,
876
268
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
877
268
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
878
268
     "%s: unable to retrieve inode information: %d.",
879
268
     function,
880
268
     allocation_group_index );
881
882
268
    return( -1 );
883
268
  }
884
3.70k
  allocation_group_block_number = (uint64_t) allocation_group_index * io_handle->allocation_group_size;
885
886
3.70k
  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
3.70k
  result = libfsxfs_inode_btree_get_inode_from_node(
898
3.70k
            inode_btree,
899
3.70k
            io_handle,
900
3.70k
            file_io_handle,
901
3.70k
            allocation_group_block_number,
902
3.70k
            inode_information->inode_btree_root_block_number,
903
3.70k
            relative_inode_number,
904
3.70k
            0,
905
3.70k
            error );
906
907
3.70k
  if( result == -1 )
908
316
  {
909
316
    libcerror_error_set(
910
316
     error,
911
316
     LIBCERROR_ERROR_DOMAIN_RUNTIME,
912
316
     LIBCERROR_RUNTIME_ERROR_GET_FAILED,
913
316
     "%s: unable to retrieve inode: %" PRIu64 " from root node: %" PRIu32 ".",
914
316
     function,
915
316
     relative_inode_number,
916
316
     inode_information->inode_btree_root_block_number );
917
918
316
    return( -1 );
919
316
  }
920
3.38k
  else if( result != 0 )
921
3.27k
  {
922
3.27k
    *file_offset = ( (off64_t) allocation_group_block_number * io_handle->block_size ) + ( (off64_t) relative_inode_number * io_handle->inode_size );
923
3.27k
  }
924
3.38k
  return( result );
925
3.70k
}
926