/src/libfsxfs/libfsxfs/libfsxfs_extent_btree.c
Line | Count | Source |
1 | | /* |
2 | | * Extent 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_definitions.h" |
29 | | #include "libfsxfs_extent.h" |
30 | | #include "libfsxfs_extent_btree.h" |
31 | | #include "libfsxfs_extents.h" |
32 | | #include "libfsxfs_libbfio.h" |
33 | | #include "libfsxfs_libcerror.h" |
34 | | #include "libfsxfs_libcnotify.h" |
35 | | |
36 | | /* Creates an extent B+ tree |
37 | | * Make sure the value extent_btree is referencing, is set to NULL |
38 | | * Returns 1 if successful or -1 on error |
39 | | */ |
40 | | int libfsxfs_extent_btree_initialize( |
41 | | libfsxfs_extent_btree_t **extent_btree, |
42 | | libcerror_error_t **error ) |
43 | 972 | { |
44 | 972 | static char *function = "libfsxfs_extent_btree_initialize"; |
45 | | |
46 | 972 | if( extent_btree == NULL ) |
47 | 0 | { |
48 | 0 | libcerror_error_set( |
49 | 0 | error, |
50 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
51 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
52 | 0 | "%s: invalid extent B+ tree.", |
53 | 0 | function ); |
54 | |
|
55 | 0 | return( -1 ); |
56 | 0 | } |
57 | 972 | if( *extent_btree != NULL ) |
58 | 0 | { |
59 | 0 | libcerror_error_set( |
60 | 0 | error, |
61 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
62 | 0 | LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET, |
63 | 0 | "%s: invalid extent B+ tree value already set.", |
64 | 0 | function ); |
65 | |
|
66 | 0 | return( -1 ); |
67 | 0 | } |
68 | 972 | *extent_btree = memory_allocate_structure( |
69 | 972 | libfsxfs_extent_btree_t ); |
70 | | |
71 | 972 | if( *extent_btree == NULL ) |
72 | 0 | { |
73 | 0 | libcerror_error_set( |
74 | 0 | error, |
75 | 0 | LIBCERROR_ERROR_DOMAIN_MEMORY, |
76 | 0 | LIBCERROR_MEMORY_ERROR_INSUFFICIENT, |
77 | 0 | "%s: unable to create extent B+ tree.", |
78 | 0 | function ); |
79 | |
|
80 | 0 | goto on_error; |
81 | 0 | } |
82 | 972 | if( memory_set( |
83 | 972 | *extent_btree, |
84 | 972 | 0, |
85 | 972 | sizeof( libfsxfs_extent_btree_t ) ) == NULL ) |
86 | 0 | { |
87 | 0 | libcerror_error_set( |
88 | 0 | error, |
89 | 0 | LIBCERROR_ERROR_DOMAIN_MEMORY, |
90 | 0 | LIBCERROR_MEMORY_ERROR_SET_FAILED, |
91 | 0 | "%s: unable to clear extent B+ tree.", |
92 | 0 | function ); |
93 | |
|
94 | 0 | goto on_error; |
95 | 0 | } |
96 | 972 | return( 1 ); |
97 | | |
98 | 0 | on_error: |
99 | 0 | if( *extent_btree != NULL ) |
100 | 0 | { |
101 | 0 | memory_free( |
102 | 0 | *extent_btree ); |
103 | |
|
104 | 0 | *extent_btree = NULL; |
105 | 0 | } |
106 | 0 | return( -1 ); |
107 | 972 | } |
108 | | |
109 | | /* Frees an extent B+ tree |
110 | | * Returns 1 if successful or -1 on error |
111 | | */ |
112 | | int libfsxfs_extent_btree_free( |
113 | | libfsxfs_extent_btree_t **extent_btree, |
114 | | libcerror_error_t **error ) |
115 | 972 | { |
116 | 972 | static char *function = "libfsxfs_extent_btree_free"; |
117 | | |
118 | 972 | if( extent_btree == NULL ) |
119 | 0 | { |
120 | 0 | libcerror_error_set( |
121 | 0 | error, |
122 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
123 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
124 | 0 | "%s: invalid extent B+ tree.", |
125 | 0 | function ); |
126 | |
|
127 | 0 | return( -1 ); |
128 | 0 | } |
129 | 972 | if( *extent_btree != NULL ) |
130 | 972 | { |
131 | 972 | memory_free( |
132 | 972 | *extent_btree ); |
133 | | |
134 | 972 | *extent_btree = NULL; |
135 | 972 | } |
136 | 972 | return( 1 ); |
137 | 972 | } |
138 | | |
139 | | /* Retrieves the extents from the extent B+ tree root node |
140 | | * Returns 1 if successful or -1 on error |
141 | | */ |
142 | | int libfsxfs_extent_btree_get_extents_from_root_node( |
143 | | libfsxfs_extent_btree_t *extent_btree, |
144 | | libfsxfs_io_handle_t *io_handle, |
145 | | libbfio_handle_t *file_io_handle, |
146 | | uint64_t number_of_blocks, |
147 | | const uint8_t *data, |
148 | | size_t data_size, |
149 | | libcdata_array_t *extents_array, |
150 | | uint8_t add_sparse_extents, |
151 | | libcerror_error_t **error ) |
152 | 972 | { |
153 | 972 | libfsxfs_extent_t *last_extent = NULL; |
154 | 972 | libfsxfs_extent_t *sparse_extent = NULL; |
155 | 972 | static char *function = "libfsxfs_extent_btree_get_extents_from_root_node"; |
156 | 972 | uint64_t logical_block_number = 0; |
157 | 972 | uint16_t level = 0; |
158 | 972 | uint16_t number_of_records = 0; |
159 | 972 | int entry_index = 0; |
160 | | |
161 | 972 | if( extent_btree == NULL ) |
162 | 0 | { |
163 | 0 | libcerror_error_set( |
164 | 0 | error, |
165 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
166 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
167 | 0 | "%s: invalid extent B+ tree.", |
168 | 0 | function ); |
169 | |
|
170 | 0 | return( -1 ); |
171 | 0 | } |
172 | 972 | if( data == NULL ) |
173 | 0 | { |
174 | 0 | libcerror_error_set( |
175 | 0 | error, |
176 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
177 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
178 | 0 | "%s: invalid data.", |
179 | 0 | function ); |
180 | |
|
181 | 0 | return( -1 ); |
182 | 0 | } |
183 | 972 | if( ( data_size < 4 ) |
184 | 972 | || ( data_size > (size_t) SSIZE_MAX ) ) |
185 | 0 | { |
186 | 0 | libcerror_error_set( |
187 | 0 | error, |
188 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
189 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS, |
190 | 0 | "%s: invalid data size value out of bounds.", |
191 | 0 | function ); |
192 | |
|
193 | 0 | return( -1 ); |
194 | 0 | } |
195 | | #if defined( HAVE_DEBUG_OUTPUT ) |
196 | | if( libcnotify_verbose != 0 ) |
197 | | { |
198 | | libcnotify_printf( |
199 | | "%s: extents B+ tree root node data:\n", |
200 | | function ); |
201 | | libcnotify_print_data( |
202 | | data, |
203 | | data_size, |
204 | | LIBCNOTIFY_PRINT_DATA_FLAG_GROUP_DATA ); |
205 | | } |
206 | | #endif /* defined( HAVE_DEBUG_OUTPUT ) */ |
207 | | |
208 | 972 | byte_stream_copy_to_uint16_big_endian( |
209 | 972 | &( data[ 0 ] ), |
210 | 972 | level ); |
211 | | |
212 | 972 | byte_stream_copy_to_uint16_big_endian( |
213 | 972 | &( data[ 2 ] ), |
214 | 972 | number_of_records ); |
215 | | |
216 | | #if defined( HAVE_DEBUG_OUTPUT ) |
217 | | if( libcnotify_verbose != 0 ) |
218 | | { |
219 | | libcnotify_printf( |
220 | | "%s: node level\t\t: %" PRIu16 "\n", |
221 | | function, |
222 | | level ); |
223 | | |
224 | | libcnotify_printf( |
225 | | "%s: number of records\t: %" PRIu16 "\n", |
226 | | function, |
227 | | number_of_records ); |
228 | | |
229 | | libcnotify_printf( |
230 | | "\n" ); |
231 | | } |
232 | | #endif /* defined( HAVE_DEBUG_OUTPUT ) */ |
233 | | |
234 | 972 | if( level == 0 ) |
235 | 8 | { |
236 | 8 | libcerror_error_set( |
237 | 8 | error, |
238 | 8 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
239 | 8 | LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, |
240 | 8 | "%s: unsupported B+ tree root node level.", |
241 | 8 | function ); |
242 | | |
243 | 8 | goto on_error; |
244 | 8 | } |
245 | 964 | if( libfsxfs_extent_btree_get_extents_from_branch_node( |
246 | 964 | extent_btree, |
247 | 964 | io_handle, |
248 | 964 | file_io_handle, |
249 | 964 | number_of_records, |
250 | 964 | &( data[ 4 ] ), |
251 | 964 | data_size - 4, |
252 | 964 | extents_array, |
253 | 964 | add_sparse_extents, |
254 | 964 | 0, |
255 | 964 | error ) != 1 ) |
256 | 304 | { |
257 | 304 | libcerror_error_set( |
258 | 304 | error, |
259 | 304 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
260 | 304 | LIBCERROR_RUNTIME_ERROR_GET_FAILED, |
261 | 304 | "%s: unable to retrieve extents from root node.", |
262 | 304 | function ); |
263 | | |
264 | 304 | goto on_error; |
265 | 304 | } |
266 | 660 | if( libfsxfs_extents_get_last_extent( |
267 | 660 | extents_array, |
268 | 660 | &last_extent, |
269 | 660 | error ) != 1 ) |
270 | 0 | { |
271 | 0 | libcerror_error_set( |
272 | 0 | error, |
273 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
274 | 0 | LIBCERROR_RUNTIME_ERROR_GET_FAILED, |
275 | 0 | "%s: unable to retrieve last extent.", |
276 | 0 | function ); |
277 | |
|
278 | 0 | goto on_error; |
279 | 0 | } |
280 | 660 | if( last_extent != NULL ) |
281 | 354 | { |
282 | 354 | logical_block_number = last_extent->logical_block_number + last_extent->number_of_blocks; |
283 | 354 | } |
284 | 660 | if( ( add_sparse_extents != 0 ) |
285 | 259 | && ( logical_block_number < number_of_blocks ) ) |
286 | 103 | { |
287 | 103 | if( ( last_extent == NULL ) |
288 | 12 | || ( ( last_extent->range_flags & LIBFSXFS_EXTENT_FLAG_IS_SPARSE ) == 0 ) ) |
289 | 96 | { |
290 | 96 | if( libfsxfs_extent_initialize( |
291 | 96 | &sparse_extent, |
292 | 96 | error ) != 1 ) |
293 | 0 | { |
294 | 0 | libcerror_error_set( |
295 | 0 | error, |
296 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
297 | 0 | LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED, |
298 | 0 | "%s: unable to create sparse extent.", |
299 | 0 | function ); |
300 | |
|
301 | 0 | goto on_error; |
302 | 0 | } |
303 | 96 | sparse_extent->logical_block_number = logical_block_number; |
304 | 96 | sparse_extent->range_flags = LIBFSXFS_EXTENT_FLAG_IS_SPARSE; |
305 | | |
306 | 96 | if( libcdata_array_append_entry( |
307 | 96 | extents_array, |
308 | 96 | &entry_index, |
309 | 96 | (intptr_t *) sparse_extent, |
310 | 96 | error ) != 1 ) |
311 | 0 | { |
312 | 0 | libcerror_error_set( |
313 | 0 | error, |
314 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
315 | 0 | LIBCERROR_RUNTIME_ERROR_APPEND_FAILED, |
316 | 0 | "%s: unable to append sparse extent to array.", |
317 | 0 | function ); |
318 | |
|
319 | 0 | goto on_error; |
320 | 0 | } |
321 | 96 | last_extent = sparse_extent; |
322 | 96 | sparse_extent = NULL; |
323 | 96 | } |
324 | 103 | last_extent->number_of_blocks += number_of_blocks - logical_block_number; |
325 | | |
326 | | #if defined( HAVE_DEBUG_OUTPUT ) |
327 | | if( libcnotify_verbose != 0 ) |
328 | | { |
329 | | libcnotify_printf( |
330 | | "%s: logical block number\t\t\t: %" PRIu64 "\n", |
331 | | function, |
332 | | last_extent->logical_block_number ); |
333 | | |
334 | | libcnotify_printf( |
335 | | "%s: physical block number\t\t\t: %" PRIu64 "\n", |
336 | | function, |
337 | | last_extent->physical_block_number ); |
338 | | |
339 | | libcnotify_printf( |
340 | | "%s: number of blocks\t\t\t: %" PRIu64 "\n", |
341 | | function, |
342 | | last_extent->number_of_blocks ); |
343 | | |
344 | | libcnotify_printf( |
345 | | "\n" ); |
346 | | } |
347 | | #endif /* defined( HAVE_DEBUG_OUTPUT ) */ |
348 | 103 | } |
349 | 660 | return( 1 ); |
350 | | |
351 | 312 | on_error: |
352 | 312 | if( sparse_extent != NULL ) |
353 | 0 | { |
354 | 0 | libfsxfs_extent_free( |
355 | 0 | &sparse_extent, |
356 | 0 | NULL ); |
357 | 0 | } |
358 | 312 | libcdata_array_empty( |
359 | 312 | extents_array, |
360 | 312 | (int (*)(intptr_t **, libcerror_error_t **)) &libfsxfs_extent_free, |
361 | 312 | NULL ); |
362 | | |
363 | 312 | return( -1 ); |
364 | 660 | } |
365 | | |
366 | | /* Retrieves the extents from the extent B+ tree branch node |
367 | | * Returns 1 if successful or -1 on error |
368 | | */ |
369 | | int libfsxfs_extent_btree_get_extents_from_branch_node( |
370 | | libfsxfs_extent_btree_t *extent_btree, |
371 | | libfsxfs_io_handle_t *io_handle, |
372 | | libbfio_handle_t *file_io_handle, |
373 | | uint16_t number_of_records, |
374 | | const uint8_t *records_data, |
375 | | size_t records_data_size, |
376 | | libcdata_array_t *extents_array, |
377 | | uint8_t add_sparse_extents, |
378 | | int recursion_depth, |
379 | | libcerror_error_t **error ) |
380 | 964 | { |
381 | 964 | static char *function = "libfsxfs_extent_btree_get_extents_from_branch_node"; |
382 | 964 | size_t number_of_key_value_pairs = 0; |
383 | 964 | size_t records_data_offset = 0; |
384 | 964 | uint64_t sub_block_number = 0; |
385 | 964 | uint16_t record_index = 0; |
386 | | |
387 | | #if defined( HAVE_DEBUG_OUTPUT ) |
388 | | uint64_t value_64bit = 0; |
389 | | #endif |
390 | | |
391 | 964 | if( extent_btree == NULL ) |
392 | 0 | { |
393 | 0 | libcerror_error_set( |
394 | 0 | error, |
395 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
396 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
397 | 0 | "%s: invalid extent B+ tree.", |
398 | 0 | function ); |
399 | |
|
400 | 0 | return( -1 ); |
401 | 0 | } |
402 | 964 | if( records_data == NULL ) |
403 | 0 | { |
404 | 0 | libcerror_error_set( |
405 | 0 | error, |
406 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
407 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
408 | 0 | "%s: invalid records data.", |
409 | 0 | function ); |
410 | |
|
411 | 0 | return( -1 ); |
412 | 0 | } |
413 | 964 | if( ( records_data_size == 0 ) |
414 | 961 | || ( records_data_size > (size_t) SSIZE_MAX ) ) |
415 | 3 | { |
416 | 3 | libcerror_error_set( |
417 | 3 | error, |
418 | 3 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
419 | 3 | LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS, |
420 | 3 | "%s: invalid records data size value out of bounds.", |
421 | 3 | function ); |
422 | | |
423 | 3 | return( -1 ); |
424 | 3 | } |
425 | 961 | if( ( recursion_depth < 0 ) |
426 | 961 | || ( recursion_depth > LIBFSXFS_MAXIMUM_RECURSION_DEPTH ) ) |
427 | 0 | { |
428 | 0 | libcerror_error_set( |
429 | 0 | error, |
430 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
431 | 0 | LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, |
432 | 0 | "%s: invalid recursion depth value out of bounds.", |
433 | 0 | function ); |
434 | |
|
435 | 0 | return( -1 ); |
436 | 0 | } |
437 | 961 | number_of_key_value_pairs = records_data_size / 16; |
438 | | |
439 | 961 | if( (size_t) number_of_records > number_of_key_value_pairs ) |
440 | 31 | { |
441 | 31 | libcerror_error_set( |
442 | 31 | error, |
443 | 31 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
444 | 31 | LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, |
445 | 31 | "%s: invalid number of records value out of bounds.", |
446 | 31 | function ); |
447 | | |
448 | 31 | return( -1 ); |
449 | 31 | } |
450 | 930 | for( record_index = 0; |
451 | 9.57k | record_index < number_of_records; |
452 | 8.64k | record_index++ ) |
453 | 8.64k | { |
454 | | #if defined( HAVE_DEBUG_OUTPUT ) |
455 | | if( libcnotify_verbose != 0 ) |
456 | | { |
457 | | byte_stream_copy_to_uint64_big_endian( |
458 | | &( records_data[ records_data_offset ] ), |
459 | | value_64bit ); |
460 | | libcnotify_printf( |
461 | | "%s: key: %" PRIu16 "\t\t: %" PRIu64 "\n", |
462 | | function, |
463 | | record_index, |
464 | | value_64bit ); |
465 | | } |
466 | | #endif /* defined( HAVE_DEBUG_OUTPUT ) */ |
467 | | |
468 | 8.64k | records_data_offset += 8; |
469 | 8.64k | } |
470 | | #if defined( HAVE_DEBUG_OUTPUT ) |
471 | | if( libcnotify_verbose != 0 ) |
472 | | { |
473 | | libcnotify_printf( |
474 | | "\n" ); |
475 | | } |
476 | | #endif |
477 | 930 | records_data_offset = number_of_key_value_pairs * 8; |
478 | | |
479 | 930 | for( record_index = 0; |
480 | 6.89k | record_index < number_of_records; |
481 | 5.96k | record_index++ ) |
482 | 6.23k | { |
483 | 6.23k | byte_stream_copy_to_uint64_big_endian( |
484 | 6.23k | &( records_data[ records_data_offset ] ), |
485 | 6.23k | sub_block_number ); |
486 | | |
487 | | #if defined( HAVE_DEBUG_OUTPUT ) |
488 | | if( libcnotify_verbose != 0 ) |
489 | | { |
490 | | libcnotify_printf( |
491 | | "%s: value: %" PRIu16 "\t\t: %" PRIu64 "\n", |
492 | | function, |
493 | | record_index, |
494 | | sub_block_number ); |
495 | | |
496 | | libcnotify_printf( |
497 | | "\n" ); |
498 | | } |
499 | | #endif /* defined( HAVE_DEBUG_OUTPUT ) */ |
500 | | |
501 | 6.23k | records_data_offset += 8; |
502 | | |
503 | 6.23k | if( libfsxfs_extent_btree_get_extents_from_node( |
504 | 6.23k | extent_btree, |
505 | 6.23k | io_handle, |
506 | 6.23k | file_io_handle, |
507 | 6.23k | sub_block_number, |
508 | 6.23k | extents_array, |
509 | 6.23k | add_sparse_extents, |
510 | 6.23k | recursion_depth + 1, |
511 | 6.23k | error ) != 1 ) |
512 | 270 | { |
513 | 270 | libcerror_error_set( |
514 | 270 | error, |
515 | 270 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
516 | 270 | LIBCERROR_RUNTIME_ERROR_GET_FAILED, |
517 | 270 | "%s: unable to retrieve extents from branch node.", |
518 | 270 | function ); |
519 | | |
520 | 270 | goto on_error; |
521 | 270 | } |
522 | 6.23k | } |
523 | | #if defined( HAVE_DEBUG_OUTPUT ) |
524 | | if( libcnotify_verbose != 0 ) |
525 | | { |
526 | | libcnotify_printf( |
527 | | "\n" ); |
528 | | } |
529 | | #endif |
530 | 660 | return( 1 ); |
531 | | |
532 | 270 | on_error: |
533 | 270 | libcdata_array_empty( |
534 | 270 | extents_array, |
535 | 270 | (int (*)(intptr_t **, libcerror_error_t **)) &libfsxfs_extent_free, |
536 | 270 | NULL ); |
537 | | |
538 | 270 | return( -1 ); |
539 | 930 | } |
540 | | |
541 | | /* Retrieves the extents from the extent B+ tree node |
542 | | * Returns 1 if successful or -1 on error |
543 | | */ |
544 | | int libfsxfs_extent_btree_get_extents_from_node( |
545 | | libfsxfs_extent_btree_t *extent_btree, |
546 | | libfsxfs_io_handle_t *io_handle, |
547 | | libbfio_handle_t *file_io_handle, |
548 | | uint64_t block_number, |
549 | | libcdata_array_t *extents_array, |
550 | | uint8_t add_sparse_extents, |
551 | | int recursion_depth, |
552 | | libcerror_error_t **error ) |
553 | 6.23k | { |
554 | 6.23k | libfsxfs_btree_block_t *btree_block = NULL; |
555 | 6.23k | static char *function = "libfsxfs_extent_btree_get_extents_from_node"; |
556 | 6.23k | off64_t btree_block_offset = 0; |
557 | 6.23k | uint64_t relative_block_number = 0; |
558 | 6.23k | int allocation_group_index = 0; |
559 | 6.23k | int compare_result = 0; |
560 | | |
561 | 6.23k | if( extent_btree == NULL ) |
562 | 0 | { |
563 | 0 | libcerror_error_set( |
564 | 0 | error, |
565 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
566 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
567 | 0 | "%s: invalid extent B+ tree.", |
568 | 0 | function ); |
569 | |
|
570 | 0 | return( -1 ); |
571 | 0 | } |
572 | 6.23k | if( io_handle == 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 IO handle.", |
579 | 0 | function ); |
580 | |
|
581 | 0 | return( -1 ); |
582 | 0 | } |
583 | 6.23k | if( io_handle->allocation_group_size == 0 ) |
584 | 0 | { |
585 | 0 | libcerror_error_set( |
586 | 0 | error, |
587 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
588 | 0 | LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, |
589 | 0 | "%s: invalid IO handle - allocation group size value out of bounds.", |
590 | 0 | function ); |
591 | |
|
592 | 0 | return( -1 ); |
593 | 0 | } |
594 | 6.23k | 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 | 6.23k | allocation_group_index = (int) ( block_number >> io_handle->number_of_relative_block_number_bits ); |
606 | 6.23k | relative_block_number = block_number & ( ( 1 << io_handle->number_of_relative_block_number_bits ) - 1 ); |
607 | | |
608 | | #if defined( HAVE_DEBUG_OUTPUT ) |
609 | | if( libcnotify_verbose != 0 ) |
610 | | { |
611 | | libcnotify_printf( |
612 | | "%s: allocation group index\t: %d\n", |
613 | | function, |
614 | | allocation_group_index ); |
615 | | |
616 | | libcnotify_printf( |
617 | | "%s: relative block number\t: %" PRIu64 "\n", |
618 | | function, |
619 | | relative_block_number ); |
620 | | |
621 | | libcnotify_printf( |
622 | | "\n" ); |
623 | | } |
624 | | #endif /* defined( HAVE_DEBUG_OUTPUT ) */ |
625 | | |
626 | 6.23k | btree_block_offset = ( ( (off64_t) allocation_group_index * io_handle->allocation_group_size ) + relative_block_number ) * io_handle->block_size; |
627 | | |
628 | 6.23k | if( libfsxfs_btree_block_initialize( |
629 | 6.23k | &btree_block, |
630 | 6.23k | io_handle->block_size, |
631 | 6.23k | 8, |
632 | 6.23k | error ) != 1 ) |
633 | 0 | { |
634 | 0 | libcerror_error_set( |
635 | 0 | error, |
636 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
637 | 0 | LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED, |
638 | 0 | "%s: unable to create B+ tree block.", |
639 | 0 | function ); |
640 | |
|
641 | 0 | goto on_error; |
642 | 0 | } |
643 | 6.23k | if( libfsxfs_btree_block_read_file_io_handle( |
644 | 6.23k | btree_block, |
645 | 6.23k | io_handle, |
646 | 6.23k | file_io_handle, |
647 | 6.23k | btree_block_offset, |
648 | 6.23k | error ) != 1 ) |
649 | 223 | { |
650 | 223 | libcerror_error_set( |
651 | 223 | error, |
652 | 223 | LIBCERROR_ERROR_DOMAIN_IO, |
653 | 223 | LIBCERROR_IO_ERROR_READ_FAILED, |
654 | 223 | "%s: unable to read extent B+ tree block: %" PRIu32 " at offset: %" PRIi64 " (0x%08" PRIx64 ").", |
655 | 223 | function, |
656 | 223 | block_number, |
657 | 223 | btree_block_offset, |
658 | 223 | btree_block_offset ); |
659 | | |
660 | 223 | goto on_error; |
661 | 223 | } |
662 | 6.01k | if( io_handle->format_version == 5 ) |
663 | 44 | { |
664 | 44 | compare_result = memory_compare( |
665 | 44 | btree_block->header->signature, |
666 | 44 | "BMA3", |
667 | 44 | 4 ); |
668 | 44 | } |
669 | 5.96k | else |
670 | 5.96k | { |
671 | 5.96k | compare_result = memory_compare( |
672 | 5.96k | btree_block->header->signature, |
673 | 5.96k | "BMAP", |
674 | 5.96k | 4 ); |
675 | 5.96k | } |
676 | 6.01k | if( compare_result != 0 ) |
677 | 28 | { |
678 | 28 | libcerror_error_set( |
679 | 28 | error, |
680 | 28 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
681 | 28 | LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, |
682 | 28 | "%s: unsupported block signature.", |
683 | 28 | function ); |
684 | | |
685 | 28 | goto on_error; |
686 | 28 | } |
687 | 5.98k | if( btree_block->header->level > extent_btree->maximum_depth ) |
688 | 17 | { |
689 | 17 | libcerror_error_set( |
690 | 17 | error, |
691 | 17 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
692 | 17 | LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, |
693 | 17 | "%s: unsupported B+ tree node level.", |
694 | 17 | function ); |
695 | | |
696 | 17 | goto on_error; |
697 | 17 | } |
698 | 5.96k | if( btree_block->header->level == 0 ) |
699 | 5.96k | { |
700 | 5.96k | if( libfsxfs_extents_read_data( |
701 | 5.96k | extents_array, |
702 | 5.96k | (uint32_t) btree_block->header->number_of_records, |
703 | 5.96k | btree_block->records_data, |
704 | 5.96k | btree_block->records_data_size, |
705 | 5.96k | add_sparse_extents, |
706 | 5.96k | error ) != 1 ) |
707 | 2 | { |
708 | 2 | libcerror_error_set( |
709 | 2 | error, |
710 | 2 | LIBCERROR_ERROR_DOMAIN_IO, |
711 | 2 | LIBCERROR_IO_ERROR_READ_FAILED, |
712 | 2 | "%s: unable to read data extents.", |
713 | 2 | function ); |
714 | | |
715 | 2 | goto on_error; |
716 | 2 | } |
717 | 5.96k | } |
718 | 0 | else |
719 | 0 | { |
720 | 0 | if( libfsxfs_extent_btree_get_extents_from_branch_node( |
721 | 0 | extent_btree, |
722 | 0 | io_handle, |
723 | 0 | file_io_handle, |
724 | 0 | btree_block->header->number_of_records, |
725 | 0 | btree_block->records_data, |
726 | 0 | btree_block->records_data_size, |
727 | 0 | extents_array, |
728 | 0 | add_sparse_extents, |
729 | 0 | recursion_depth, |
730 | 0 | error ) != 1 ) |
731 | 0 | { |
732 | 0 | libcerror_error_set( |
733 | 0 | error, |
734 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
735 | 0 | LIBCERROR_RUNTIME_ERROR_GET_FAILED, |
736 | 0 | "%s: unable to retrieve extents from branch node.", |
737 | 0 | function ); |
738 | |
|
739 | 0 | goto on_error; |
740 | 0 | } |
741 | 0 | } |
742 | 5.96k | if( libfsxfs_btree_block_free( |
743 | 5.96k | &btree_block, |
744 | 5.96k | error ) != 1 ) |
745 | 0 | { |
746 | 0 | libcerror_error_set( |
747 | 0 | error, |
748 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
749 | 0 | LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED, |
750 | 0 | "%s: unable to free B+ tree block.", |
751 | 0 | function ); |
752 | |
|
753 | 0 | goto on_error; |
754 | 0 | } |
755 | 5.96k | return( 1 ); |
756 | | |
757 | 270 | on_error: |
758 | 270 | if( btree_block != NULL ) |
759 | 270 | { |
760 | 270 | libfsxfs_btree_block_free( |
761 | 270 | &btree_block, |
762 | 270 | NULL ); |
763 | 270 | } |
764 | 270 | libcdata_array_empty( |
765 | 270 | extents_array, |
766 | 270 | (int (*)(intptr_t **, libcerror_error_t **)) &libfsxfs_extent_free, |
767 | 270 | NULL ); |
768 | | |
769 | 270 | return( -1 ); |
770 | 5.96k | } |
771 | | |