Coverage Report

Created: 2025-09-05 06:58

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