HEX
Server: nginx/1.18.0
System: Linux mail.dakarash.co.id 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User: www-data (33)
PHP: 8.1.2-1ubuntu2.23
Disabled: NONE
Upload Files
File: /home/django/libpff/libpff/libpff_recover.c
/*
 * Recover functions
 *
 * Copyright (C) 2008-2024, Joachim Metz <joachim.metz@gmail.com>
 *
 * Refer to AUTHORS for acknowledgements.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <common.h>
#include <byte_stream.h>
#include <memory.h>
#include <types.h>

#include "libpff_checksum.h"
#include "libpff_data_block.h"
#include "libpff_definitions.h"
#include "libpff_descriptors_index.h"
#include "libpff_index.h"
#include "libpff_index_node.h"
#include "libpff_index_value.h"
#include "libpff_index_values_list.h"
#include "libpff_item_descriptor.h"
#include "libpff_item_tree.h"
#include "libpff_io_handle.h"
#include "libpff_libbfio.h"
#include "libpff_libcdata.h"
#include "libpff_libcerror.h"
#include "libpff_libcnotify.h"
#include "libpff_local_descriptors_node.h"
#include "libpff_offsets_index.h"
#include "libpff_recover.h"

#include "pff_block.h"
#include "pff_index_node.h"

/* Scans for recoverable items
 * By default only the unallocated space is checked for recoverable items
 * Returns 1 if successful or -1 on error
 */
int libpff_recover_items(
     libpff_io_handle_t *io_handle,
     libbfio_handle_t *file_io_handle,
     libpff_descriptors_index_t *descriptors_index,
     libpff_offsets_index_t *offsets_index,
     libcdata_range_list_t *unallocated_data_block_list,
     libcdata_range_list_t *unallocated_page_block_list,
     libcdata_list_t *recovered_item_list,
     uint8_t recovery_flags,
     libcerror_error_t **error )
{
	libcdata_tree_node_t *item_tree_node          = NULL;
	libpff_index_value_t *descriptors_index_value = NULL;
	libpff_index_values_list_t *index_values_list = NULL;
	libpff_item_descriptor_t *item_descriptor     = NULL;
	static char *function                         = "libpff_recover_items";
	int data_identifier_value_index               = 0;
	int index_value_iterator                      = 0;
	int index_values_list_iterator                = 0;
	int local_descriptors_identifier_value_index  = 0;
	int number_of_index_values                    = 0;
	int number_of_index_values_lists              = 0;
	int result                                    = 0;

#ifdef TODO
        uint32_t maximum_data_block_data_size         = 0;
#endif

	if( io_handle == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid IO handle.",
		 function );

		return( -1 );
	}
	if( ( io_handle->file_type != LIBPFF_FILE_TYPE_32BIT )
	 && ( io_handle->file_type != LIBPFF_FILE_TYPE_64BIT )
	 && ( io_handle->file_type != LIBPFF_FILE_TYPE_64BIT_4K_PAGE ) )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
		 "%s: unsupported file type.",
		 function );

		return( -1 );
	}
	if( descriptors_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid descriptors index.",
		 function );

		return( -1 );
	}
	if( descriptors_index->index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
		 "%s: invalid descriptors index - missing index.",
		 function );

		return( -1 );
	}
	if( offsets_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid offsets index.",
		 function );

		return( -1 );
	}
	if( offsets_index->index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
		 "%s: invalid offsets index - missing index.",
		 function );

		return( -1 );
	}
	if( recovered_item_list == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid recovered item list.",
		 function );

		return( -1 );
	}
#ifdef TODO
	if( io_handle->file_type == LIBPFF_FILE_TYPE_32BIT )
	{
		maximum_data_block_data_size = 8192 - 12;
	}
	else if( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT )
	{
		maximum_data_block_data_size = 8192 - 16;
	}
	else
	{
/* TODO: this value is currently assumed based on the 512 x 8 = 4k page */
		maximum_data_block_data_size = 65536 - 24;
	}
#endif /* TODO */
#if defined( HAVE_DEBUG_OUTPUT )
	if( libbfio_handle_set_track_offsets_read(
	     file_io_handle,
	     0,
	     error ) != 1 )
	{
                libcerror_error_set(
                 error,
                 LIBCERROR_ERROR_DOMAIN_RUNTIME,
                 LIBCERROR_RUNTIME_ERROR_SET_FAILED,
                 "%s: unable to set track offsets read in file IO handle.",
                 function );

		goto on_error;
	}
#endif
#ifdef TODO
	/* Scan the offsets index nodes for deleted values
	 */
	if( libpff_recover_analyze_offsets_index_node(
	     offsets_index,
	     io_handle,
	     file_io_handle,
	     offsets_index->index->root_node_offset,
	     offsets_index->index->root_node_back_pointer,
	     maximum_data_block_data_size,
	     0,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_IO,
		 LIBCERROR_IO_ERROR_READ_FAILED,
		 "%s: unable to recover offsets index nodes.",
		 function );

		return( -1 );
	}
#endif /* TODO */

	if( libpff_recover_data_blocks(
	     io_handle,
	     file_io_handle,
	     descriptors_index,
	     offsets_index,
	     unallocated_data_block_list,
	     unallocated_page_block_list,
	     recovery_flags,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_IO,
		 LIBCERROR_IO_ERROR_READ_FAILED,
		 "%s: unable to recover data blocks.",
		 function );

		goto on_error;
	}
	/* Scan the descriptors index nodes for deleted values
	 */
	if( libpff_recover_descriptors_index_values(
	     descriptors_index,
	     io_handle,
	     file_io_handle,
	     descriptors_index->index->root_node_offset,
	     descriptors_index->index->root_node_back_pointer,
	     0,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_IO,
		 LIBCERROR_IO_ERROR_READ_FAILED,
		 "%s: unable to recover descriptors index values.",
		 function );

		return( -1 );
	}
/* TODO combine the offsets index value check part with libpff_recover_descriptors_index_values so that the size of descriptors_index->recovered_index_values_tree remains small */

	/* For each recovered descriptors index value check if the corresonding local descriptor and data offsets index values exist
	 */
	if( libcdata_btree_get_number_of_values(
	     descriptors_index->recovered_index_values_tree,
	     &number_of_index_values_lists,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve number of recovered descriptors index values.",
		 function );

		goto on_error;
	}
	for( index_values_list_iterator = 0;
	     index_values_list_iterator < number_of_index_values_lists;
	     index_values_list_iterator++ )
	{
		if( io_handle->abort != 0 )
		{
/* TODO break instead of error ? */
			goto on_error;
		}
		if( libcdata_btree_get_value_by_index(
		     descriptors_index->recovered_index_values_tree,
		     index_values_list_iterator,
		     (intptr_t **) &index_values_list,
		     error ) != 1 )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
			 "%s: unable to retrieve recovered descriptors index values list: %d.",
			 function,
			 index_values_list_iterator );

			goto on_error;
		}
		if( libpff_index_values_list_number_of_values(
		     index_values_list,
		     &number_of_index_values,
		     error ) != 1 )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
			 "%s: unable to retrieve number of recovered descriptors index values list: %d elements.",
			 function,
			 index_values_list_iterator );

			goto on_error;
		}
		for( index_value_iterator = 0;
		     index_value_iterator < number_of_index_values;
		     index_value_iterator++ )
		{
			if( io_handle->abort != 0 )
			{
/* TODO break instead of error ? */
				goto on_error;
			}
			if( libpff_index_values_list_get_value_by_index(
			     index_values_list,
			     index_value_iterator,
			     &descriptors_index_value,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to retrieve recovered descriptors index value: %" PRIu64 " list element: %d.",
				 function,
				 index_values_list->identifier,
				 index_value_iterator );

				goto on_error;
			}
			if( descriptors_index_value == NULL )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
				 "%s: missing recovered descriptors index value: %" PRIu64 " list element: %d.",
				 function,
				 index_values_list->identifier,
				 index_value_iterator );

				goto on_error;
			}
#if defined( HAVE_DEBUG_OUTPUT )
			if( libcnotify_verbose != 0 )
			{
				libcnotify_printf(
				 "%s: analyzing identifier: %" PRIu64 ", data: %" PRIu64 ", local descriptors: %" PRIu64 ", parent: %" PRIu32 "\n",
				 function,
				 descriptors_index_value->identifier,
				 descriptors_index_value->data_identifier,
				 descriptors_index_value->local_descriptors_identifier,
				 descriptors_index_value->parent_identifier );
			}
#endif
			result = libpff_recover_analyze_descriptor_data_identifier(
				  io_handle,
				  file_io_handle,
				  offsets_index,
				  descriptors_index_value,
				  &data_identifier_value_index,
				  error );

			if( result == -1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to recover descriptors index value: %" PRIu64 " data identifier: %" PRIu64 ".",
				 function,
				 descriptors_index_value->identifier,
				 descriptors_index_value->data_identifier & (uint64_t) LIBPFF_OFFSET_INDEX_IDENTIFIER_MASK );

				goto on_error;
			}
			else if( result == 0 )
			{
				continue;
			}
			result = libpff_recover_analyze_descriptor_local_descriptors_identifier(
				  io_handle,
				  file_io_handle,
				  offsets_index,
				  descriptors_index_value,
				  &local_descriptors_identifier_value_index,
				  error );

			if( result == -1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to recover descriptors index value: %" PRIu64 " local descriptors identifier: %" PRIu64 ".",
				 function,
				 descriptors_index_value->identifier,
				 descriptors_index_value->local_descriptors_identifier & (uint64_t) LIBPFF_OFFSET_INDEX_IDENTIFIER_MASK );

				goto on_error;
			}
			else if( result == 0 )
			{
				continue;
			}
#if defined( HAVE_DEBUG_OUTPUT )
			if( libcnotify_verbose != 0 )
			{
				libcnotify_printf(
				 "%s: item descriptor: %" PRIu64 " is recoverable.\n",
				 function,
				 descriptors_index_value->identifier );
			}
#endif
			/* Create a new item descriptor
			 */
			if( libpff_item_descriptor_initialize(
			     &item_descriptor,
			     (uint32_t) descriptors_index_value->identifier,
			     descriptors_index_value->data_identifier,
			     descriptors_index_value->local_descriptors_identifier,
			     1,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
				 "%s: unable to create item descriptor: %" PRIu64 ".",
				 function,
				 descriptors_index_value->identifier );

				goto on_error;
			}
			if( item_descriptor == NULL )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
				 "%s: missing item descriptor: %" PRIu64 ".",
				 function,
				 descriptors_index_value->identifier );

				goto on_error;
			}
/* TODO add to initialize function or create a separate function for setting these value in the item descriptor */
			item_descriptor->recovered_data_identifier_value_index              = data_identifier_value_index;
			item_descriptor->recovered_local_descriptors_identifier_value_index = local_descriptors_identifier_value_index;

			/* Create a new item tree node
			 */
			if( libcdata_tree_node_initialize(
			     &item_tree_node,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
				 "%s: unable to create item tree node.",
				 function );

				goto on_error;
			}
			if( libcdata_tree_node_set_value(
			     item_tree_node,
			     (intptr_t *) item_descriptor,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_SET_FAILED,
				 "%s: unable to set item descriptor in item tree node.",
				 function );

				goto on_error;
			}
			item_descriptor = NULL;

			if( libcdata_list_append_value(
			     recovered_item_list,
			     (intptr_t *) item_tree_node,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
				 "%s: unable to append tree node to recovered item list.",
				 function );

				goto on_error;
			}
			item_tree_node = NULL;
		}
	}
#if defined( HAVE_DEBUG_OUTPUT )
	if( libbfio_handle_set_track_offsets_read(
	     file_io_handle,
	     0,
	     error ) != 1 )
	{
                libcerror_error_set(
                 error,
                 LIBCERROR_ERROR_DOMAIN_RUNTIME,
                 LIBCERROR_RUNTIME_ERROR_SET_FAILED,
                 "%s: unable to set track offsets read in file IO handle.",
                 function );

                goto on_error;
	}
#endif
	/* TODO
	 * link recovered descriptors to parent? and add to item hierarchy?
	 * handle scanning without index data
	 * what about 'encryption' ?
	 */
	return( 1 );

on_error:
	if( item_tree_node != NULL )
	{
		libcdata_tree_node_free(
		 &item_tree_node,
		 (int (*)(intptr_t **, libcerror_error_t **)) &libpff_item_descriptor_free,
		 NULL );
	}
	if( item_descriptor != NULL )
	{
		libpff_item_descriptor_free(
		 &item_descriptor,
		 NULL );
	}
	libcdata_list_empty(
	 recovered_item_list,
	 (int (*)(intptr_t **, libcerror_error_t **)) &libpff_item_tree_node_free_recovered,
	 NULL );

	return( -1 );
}

/* Analyze if a specific descriptor data identifier is recoverable
 * Returns 1 if recoverable, 0 if not or -1 on error
 */
int libpff_recover_analyze_descriptor_data_identifier(
     libpff_io_handle_t *io_handle,
     libbfio_handle_t *file_io_handle,
     libpff_offsets_index_t *offsets_index,
     libpff_index_value_t *descriptors_index_value,
     int *data_identifier_value_index,
     libcerror_error_t **error )
{
	libcdata_tree_node_t *upper_node              = NULL;
	libpff_index_value_t *index_value             = NULL;
	libpff_index_value_t *lookup_index_value      = NULL;
	libpff_index_values_list_t *index_values_list = NULL;
	static char *function                         = "libpff_recover_analyze_descriptor_data_identifier";
	uint64_t lookup_identifier                    = 0;
	int index_value_iterator                      = 0;
	int number_of_index_values                    = 0;
	int result                                    = 0;

	if( offsets_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid offsets index.",
		 function );

		return( -1 );
	}
	if( descriptors_index_value == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid descriptors index value.",
		 function );

		return( -1 );
	}
	if( data_identifier_value_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid data identifier value index.",
		 function );

		return( -1 );
	}
	lookup_identifier = descriptors_index_value->data_identifier & (uint64_t) LIBPFF_OFFSET_INDEX_IDENTIFIER_MASK;

	if( libpff_index_value_initialize(
	     &lookup_index_value,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
		 "%s: unable to create lookup index value.",
		 function );

		goto on_error;
	}
	lookup_index_value->identifier = lookup_identifier;

/* TODO have a compare function that directly uses the lookup_identifier not the lookup_index_value */
	result = libcdata_btree_get_value_by_value(
	          offsets_index->recovered_index_values_tree,
	          (intptr_t *) lookup_index_value,
	          (int (*)(intptr_t *, intptr_t *, libcerror_error_t **)) &libpff_index_values_list_compare,
	          &upper_node, 
	          (intptr_t **) &index_values_list,
	          error );

	if( result == -1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve recovered offsets index value: %" PRIu64 " list.",
		 function,
		 lookup_identifier );

		goto on_error;
	}
	if( libpff_index_value_free(
	     &lookup_index_value,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
		 "%s: unable to free lookup index value.",
		 function,
		 lookup_identifier );

		goto on_error;
	}
	if( result == 0 )
	{
		return( 0 );
	}
	if( libpff_index_values_list_number_of_values(
	     index_values_list,
	     &number_of_index_values,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve number of recovered offsets index value: %" PRIu64 " list.",
		 function,
		 lookup_identifier );

		goto on_error;
	}
/* TODO handle if more than 1 offsets index value is recoverable */

	result = 0;

	for( index_value_iterator = 0;
	     index_value_iterator < number_of_index_values;
	     index_value_iterator++ )
	{
		if( libpff_index_values_list_get_value_by_index(
		     index_values_list,
		     index_value_iterator,
		     &index_value,
		     error ) != 1 )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
			 "%s: unable to retrieve recovered offsets index value: %d for data identifier: %" PRIu64 ".",
			 function,
			 index_value_iterator,
			 lookup_identifier );

			goto on_error;
		}
		/* Check if a data block is recoverable
		 */
		result = libpff_recover_analyze_data_block(
			  io_handle,
			  file_io_handle,
			  (uint32_t) descriptors_index_value->identifier,
			  index_value,
			  error );

		if( result == -1 )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_IO,
			 LIBCERROR_IO_ERROR_READ_FAILED,
			 "%s: unable to recover data block: %" PRIu64 ".",
			 function,
			 descriptors_index_value->data_identifier );

#if defined( HAVE_DEBUG_OUTPUT )
			if( ( libcnotify_verbose != 0 )
			 && ( error != NULL )
			 && ( *error != NULL ) )
			{
				libcnotify_print_error_backtrace(
				 *error );
			}
#endif
			libcerror_error_free(
			 error );

/* TODO remove unreadable offset identifier from offsets_index->recovered_index_values_tree ? */
		}
		else if( result != 0 )
		{
			break;
		}
	}
	if( result != 0 )
	{
		*data_identifier_value_index = index_value_iterator;
	}
#if defined( HAVE_DEBUG_OUTPUT )
	else if( libcnotify_verbose != 0 )
	{
		libcnotify_printf(
		 "%s: recovered offsets index value for data identifier: %" PRIu64 " not available.\n",
		 function,
		 lookup_identifier );
	}
#endif
	return( result );

on_error:
	if( lookup_index_value != NULL )
	{
		libpff_index_value_free(
		 &lookup_index_value,
		 NULL );
	}
	return( -1 );
}

/* Analyze if a specific descriptor local descriptors identifier is recoverable
 * Returns 1 if recoverable, 0 if not or -1 on error
 */
int libpff_recover_analyze_descriptor_local_descriptors_identifier(
     libpff_io_handle_t *io_handle,
     libbfio_handle_t *file_io_handle,
     libpff_offsets_index_t *offsets_index,
     libpff_index_value_t *descriptors_index_value,
     int *local_descriptors_identifier_value_index,
     libcerror_error_t **error )
{
	libcdata_tree_node_t *upper_node              = NULL;
	libpff_index_value_t *index_value             = NULL;
	libpff_index_value_t *lookup_index_value      = NULL;
	libpff_index_values_list_t *index_values_list = NULL;
	static char *function                         = "libpff_recover_analyze_descriptor_local_descriptors_identifier";
	uint64_t lookup_identifier                    = 0;
	int index_value_iterator                      = 0;
	int number_of_index_values                    = 0;
	int result                                    = 0;

	if( offsets_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid offsets index.",
		 function );

		return( -1 );
	}
	if( descriptors_index_value == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid descriptors index value.",
		 function );

		return( -1 );
	}
	if( local_descriptors_identifier_value_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid local descriptors identifier value index.",
		 function );

		return( -1 );
	}
	/* The local descriptors identifier is 0 if not set
	 */
	if( descriptors_index_value->local_descriptors_identifier == 0 )
	{
		return( 1 );
	}
	lookup_identifier = descriptors_index_value->local_descriptors_identifier & (uint64_t) LIBPFF_OFFSET_INDEX_IDENTIFIER_MASK;

	if( libpff_index_value_initialize(
	     &lookup_index_value,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
		 "%s: unable to create lookup index value.",
		 function );

		goto on_error;
	}
	lookup_index_value->identifier = lookup_identifier;

/* TODO have a compare function that directly uses the lookup_identifier not the lookup_index_value */
	result = libcdata_btree_get_value_by_value(
	          offsets_index->recovered_index_values_tree,
	          (intptr_t *) lookup_index_value,
	          (int (*)(intptr_t *, intptr_t *, libcerror_error_t **)) &libpff_index_values_list_compare,
	          &upper_node, 
	          (intptr_t **) &index_values_list,
	          error );

	if( result == -1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve recovered offsets index value: %" PRIu64 " list.",
		 function,
		 lookup_identifier );

		goto on_error;
	}
	if( libpff_index_value_free(
	     &lookup_index_value,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
		 "%s: unable to free lookup index value.",
		 function,
		 lookup_identifier );

		goto on_error;
	}
	if( result == 0 )
	{
		return( 0 );
	}
	if( libpff_index_values_list_number_of_values(
	     index_values_list,
	     &number_of_index_values,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve number of recovered offsets index value: %" PRIu64 " list.",
		 function,
		 lookup_identifier );

		goto on_error;
	}
/* TODO handle if more than 1 offsets index value is recoverable */

	result = 0;

	for( index_value_iterator = 0;
	     index_value_iterator < number_of_index_values;
	     index_value_iterator++ )
	{
		if( libpff_index_values_list_get_value_by_index(
		     index_values_list,
		     index_value_iterator,
		     &index_value,
		     error ) != 1 )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
			 "%s: unable to retrieve recovered offsets index value: %d for data identifier: %" PRIu64 ".",
			 function,
			 index_value_iterator,
			 lookup_identifier );

			goto on_error;
		}
		/* Check if local descriptors are recoverable
		 */
		result = libpff_recover_analyze_local_descriptors(
		          io_handle,
		          file_io_handle,
		          offsets_index,
		          descriptors_index_value->local_descriptors_identifier,
		          error );

		if( result == -1 )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_IO,
			 LIBCERROR_IO_ERROR_READ_FAILED,
			 "%s: unable to recover local descriptors: %" PRIu64 ".",
			 function,
			 descriptors_index_value->local_descriptors_identifier );

			goto on_error;
		}
		else if( result != 0 )
		{
			break;
		}
	}
	if( result != 0 )
	{
		*local_descriptors_identifier_value_index = index_value_iterator;
	}
#if defined( HAVE_DEBUG_OUTPUT )
	else if( libcnotify_verbose != 0 )
	{
		libcnotify_printf(
		 "%s: recovered offsets index value for local descriptors identifier: %" PRIu64 " not available.\n",
		 function,
		 lookup_identifier );
	}
#endif
	return( result );

on_error:
	if( lookup_index_value != NULL )
	{
		libpff_index_value_free(
		 &lookup_index_value,
		 NULL );
	}
	return( -1 );
}

/* Analyze if a specific data block back pointer is recoverable
 * Returns 1 if recoverable, 0 if not or -1 on error
 */
int libpff_recover_analyze_data_block_back_pointer(
     libpff_offsets_index_t *offsets_index,
     uint64_t data_block_back_pointer,
     off64_t data_block_data_offset,
     size32_t data_block_data_size,
     libcerror_error_t **error )
{
	libcdata_tree_node_t *upper_node              = NULL;
	libpff_index_value_t *index_value             = NULL;
	libpff_index_value_t *lookup_index_value      = NULL;
	libpff_index_values_list_t *index_values_list = NULL;
	static char *function                         = "libpff_recover_analyze_data_block_back_pointer";
	uint64_t lookup_identifier                    = 0;
	int index_value_iterator                      = 0;
	int number_of_index_values                    = 0;
	int result                                    = 0;

	if( offsets_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid offsets index.",
		 function );

		return( -1 );
	}
	lookup_identifier = data_block_back_pointer;

	if( libpff_index_value_initialize(
	     &lookup_index_value,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
		 "%s: unable to create lookup index value.",
		 function );

		goto on_error;
	}
	lookup_index_value->identifier = lookup_identifier;

/* TODO have a compare function that directly uses the lookup_identifier not the lookup_index_value */
	result = libcdata_btree_get_value_by_value(
	          offsets_index->recovered_index_values_tree,
	          (intptr_t *) lookup_index_value,
	          (int (*)(intptr_t *, intptr_t *, libcerror_error_t **)) &libpff_index_values_list_compare,
	          &upper_node, 
	          (intptr_t **) &index_values_list,
	          error );

	if( result == -1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve recovered offsets index value: %" PRIu64 " list.",
		 function,
		 lookup_identifier );

		goto on_error;
	}
	if( libpff_index_value_free(
	     &lookup_index_value,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
		 "%s: unable to free lookup index value.",
		 function,
		 lookup_identifier );

		goto on_error;
	}
	if( result == 0 )
	{
		return( 0 );
	}
	if( libpff_index_values_list_number_of_values(
	     index_values_list,
	     &number_of_index_values,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve number of recovered offsets index value: %" PRIu64 " list.",
		 function,
		 lookup_identifier );

		goto on_error;
	}
/* TODO handle if more than 1 offsets index value is recoverable */

	result = 0;

	for( index_value_iterator = 0;
	     index_value_iterator < number_of_index_values;
	     index_value_iterator++ )
	{
		if( libpff_index_values_list_get_value_by_index(
		     index_values_list,
		     index_value_iterator,
		     &index_value,
		     error ) != 1 )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
			 "%s: unable to retrieve recovered offsets index value: %d for data identifier: %" PRIu64 ".",
			 function,
			 index_value_iterator,
			 lookup_identifier );

			goto on_error;
		}
		if( ( data_block_data_offset == index_value->file_offset )
		 && ( data_block_data_size == index_value->data_size ) )
		{
			result = 1;
		}
		if( result != 0 )
		{
			break;
		}
	}
	if( result != 0 )
	{
#if defined( HAVE_DEBUG_OUTPUT )
		if( libcnotify_verbose != 0 )
		{
			libcnotify_printf(
			 "%s: recovered data block with identifier: %" PRIu64 " matches existing recovered item value.\n",
			 function,
			 lookup_identifier );
		}
#endif
	}
	return( result );

on_error:
	if( lookup_index_value != NULL )
	{
		libpff_index_value_free(
		 &lookup_index_value,
		 NULL );
	}
	return( -1 );
}

/* Analyze if a specific descriptors index value is recoverable
 * Returns 1 if recoverable, 0 if not or -1 on error
 */
int libpff_recover_analyze_descriptors_index_value(
     libpff_descriptors_index_t *descriptors_index,
     libpff_io_handle_t *io_handle,
     libbfio_handle_t *file_io_handle,
     libpff_index_value_t *descriptors_index_value,
     libcerror_error_t **error )
{
	libpff_index_value_t *existing_index_value = NULL;
	static char *function                      = "libpff_recover_analyze_descriptors_index_value";
	int result                                 = 0;

	if( descriptors_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid descriptors index.",
		 function );

		return( -1 );
	}
	if( descriptors_index_value == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid descriptors index value.",
		 function );

		return( -1 );
	}
#if defined( HAVE_DEBUG_OUTPUT )
	if( libcnotify_verbose != 0 )
	{
		libcnotify_printf(
		 "%s: analyzing identifier: %" PRIu64 ", data: %" PRIu64 ", local descriptors: %" PRIu64 ", parent: %" PRIu32 "\n",
		 function,
		 descriptors_index_value->identifier,
		 descriptors_index_value->data_identifier,
		 descriptors_index_value->local_descriptors_identifier,
		 descriptors_index_value->parent_identifier );
	}
#endif
	/* Check if the descriptors index value matches an existing index value
	 */
	result = libpff_index_get_value_by_identifier(
		  descriptors_index->index,
		  io_handle,
		  file_io_handle,
		  descriptors_index_value->identifier,
		  &existing_index_value,
		  error );

	if( result == -1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve descriptors index value: %" PRIu64 " from index.",
		 function,
		 descriptors_index_value->identifier );

		goto on_error;
	}
	else if( result != 0 )
	{
		if( existing_index_value == NULL )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
			 "%s: missing descriptors index value for identifier: %" PRIu64 ".",
			 function,
			 descriptors_index_value->identifier );

			goto on_error;
		}
		result = 0;

/* TODO what about parent changes ? */
		if( ( descriptors_index_value->data_identifier == existing_index_value->data_identifier )
		 && ( descriptors_index_value->local_descriptors_identifier == existing_index_value->local_descriptors_identifier ) )
		{
#if defined( HAVE_DEBUG_OUTPUT )
			if( libcnotify_verbose != 0 )
			{
				libcnotify_printf(
				 "%s: deleted descriptors index value: %" PRIu64 " matches existing item value.\n",
				 function,
				 descriptors_index_value->identifier );
			}
#endif
			result = 1;
		}
		if( libpff_index_value_free(
		     &existing_index_value,
		     error ) != 1 )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
			 "%s: unable to free index value: %d.",
			 function,
			 descriptors_index_value->identifier );

			goto on_error;
		}
		if( result != 0 )
		{
			return( 0 );
		}
	}
	result = libpff_recover_check_descriptors_index_for_recovered_value(
		  descriptors_index,
		  descriptors_index_value,
		  error );

	if( result == -1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to determine if deleted descriptors index value: %" PRIu64 " was previously recovered.",
		 function,
		 descriptors_index_value->identifier );

		goto on_error;
	}
	else if( result != 0 )
	{
#if defined( HAVE_DEBUG_OUTPUT )
		if( libcnotify_verbose != 0 )
		{
			libcnotify_printf(
			 "%s: deleted descriptors index value: %" PRIu64 " matches previous recovered index value.\n",
			 function,
			 descriptors_index_value->identifier );
		}
#endif
		return( 0 );
	}
	return( 1 );

on_error:
	if( existing_index_value != NULL )
	{
		libpff_index_value_free(
		 &existing_index_value,
		 NULL );
	}
	return( -1 );
}

/* Checks if the specific descriptors index contains a specific recovered index value
 * Returns 1 if exists, 0 if not or -1 on error
 */
int libpff_recover_check_descriptors_index_for_recovered_value(
     libpff_descriptors_index_t *descriptors_index,
     libpff_index_value_t *descriptors_index_value,
     libcerror_error_t **error )
{
	libcdata_tree_node_t *upper_node              = NULL;
	libpff_index_value_t *index_value             = NULL;
	libpff_index_values_list_t *index_values_list = NULL;
	static char *function                         = "libpff_recover_check_descriptors_index_for_recovered_value";
	int index_value_iterator                      = 0;
	int number_of_index_values                    = 0;
	int result                                    = 0;

	if( descriptors_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid descriptors index.",
		 function );

		return( -1 );
	}
	if( descriptors_index_value == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid descriptors index value.",
		 function );

		return( -1 );
	}
	result = libcdata_btree_get_value_by_value(
		  descriptors_index->recovered_index_values_tree,
		  (intptr_t *) descriptors_index_value,
		  (int (*)(intptr_t *, intptr_t *, libcerror_error_t **)) &libpff_index_values_list_compare,
		  &upper_node,
		  (intptr_t **) &index_values_list,
		  error );

	if( result == -1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve recovered descriptors index value: %" PRIu64 " list.",
		 function,
		 descriptors_index_value->identifier );

		return( -1 );
	}
	else if( result != 0 )
	{
		if( libpff_index_values_list_number_of_values(
		     index_values_list,
		     &number_of_index_values,
		     error ) != 1 )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
			 "%s: unable to retrieve number of recovered descriptors index values for identifier: %" PRIu64 ".",
			 function,
			 descriptors_index_value->identifier );

			return( -1 );
		}
		result = 0;

		for( index_value_iterator = 0;
		     index_value_iterator < number_of_index_values;
		     index_value_iterator++ )
		{
			if( libpff_index_values_list_get_value_by_index(
			     index_values_list,
			     index_value_iterator,
			     &index_value,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to retrieve recovered descriptors index value: %" PRIu64 " list element: %d.",
				 function,
				 descriptors_index_value->identifier,
				 index_value_iterator );

				return( -1 );
			}
			if( index_value == NULL )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
				 "%s: missing recovered descriptors index value: %" PRIu64 " list element: %d.",
				 function,
				 descriptors_index_value->identifier,
				 index_value_iterator );

				return( -1 );
			}
/* TODO what about parent changes ? */
			if( ( descriptors_index_value->data_identifier == index_value->data_identifier )
			 && ( descriptors_index_value->local_descriptors_identifier == index_value->local_descriptors_identifier ) )
			{
				result = 1;

				break;
			}
		}
	}
	return( result );
}

/* Recovers descriptors index values
 * Returns 1 if successful or -1 on error
 */
int libpff_recover_descriptors_index_values(
     libpff_descriptors_index_t *descriptors_index,
     libpff_io_handle_t *io_handle,
     libbfio_handle_t *file_io_handle,
     off64_t node_offset,
     uint64_t node_back_pointer,
     int recursion_depth,
     libcerror_error_t **error )
{
	libpff_index_node_t *index_node   = NULL;
	libpff_index_value_t *index_value = NULL;
	uint8_t *node_entry_data          = NULL;
	static char *function             = "libpff_recover_descriptors_index_values";
	size64_t node_data_size           = 0;
	off64_t node_data_offset          = 0;
	uint64_t sub_node_back_pointer    = 0;
	uint64_t sub_node_offset          = 0;
	uint16_t entry_index              = 0;
	int result                        = 0;

	if( descriptors_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid descriptors index.",
		 function );

		return( -1 );
	}
	if( descriptors_index->index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
		 "%s: invalid descriptors index - missing index.",
		 function );

		return( -1 );
	}
	if( io_handle == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid IO handle.",
		 function );

		return( -1 );
	}
	if( ( recursion_depth < 0 )
	 || ( recursion_depth > LIBPFF_MAXIMUM_ITEM_TREE_RECURSION_DEPTH ) )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
		 "%s: invalid recursion depth value out of bounds.",
		 function );

		return( -1 );
	}
	if( libpff_index_node_initialize(
	     &index_node,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
		 "%s: unable to create descriptors index node.",
		 function );

		goto on_error;
	}
	if( libpff_index_node_read_file_io_handle(
	     index_node,
	     file_io_handle,
	     node_offset,
	     io_handle->file_type,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_IO,
		 LIBCERROR_IO_ERROR_READ_FAILED,
		 "%s: unable to read descriptors index node at offset: %" PRIi64 " (0x%08" PRIx64 ").",
		 function,
		 node_offset,
		 node_offset );

		goto on_error;
	}
	if( index_node->level == LIBPFF_INDEX_NODE_LEVEL_LEAF )
	{
		for( entry_index = index_node->number_of_entries;
		     entry_index < index_node->maximum_number_of_entries;
		     entry_index++ )
		{
/* TODO add if( io_handle->abort != 0 ) */
			if( libpff_index_node_get_entry_data(
			     index_node,
			     entry_index,
			     &node_entry_data,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to retrieve deleted node entry: %" PRIu16 " data.",
				 function,
				 entry_index );

				goto on_error;
			}
			if( node_entry_data == NULL )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
				 "%s: missing deleted node entry: %" PRIu16 " data.",
				 function,
				 entry_index );

				goto on_error;
			}
			if( libpff_index_value_initialize(
			     &index_value,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
				 "%s: unable to create index value.",
				 function );

				goto on_error;
			}
			if( libpff_index_value_read_data(
			     index_value,
			     io_handle,
			     LIBPFF_INDEX_TYPE_DESCRIPTOR,
			     node_entry_data,
			     (size_t) index_node->entry_size,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_IO,
				 LIBCERROR_IO_ERROR_READ_FAILED,
				 "%s: unable to read index value.",
				 function );

				goto on_error;
			}
			result = libpff_recover_analyze_descriptors_index_value(
			          descriptors_index,
			          io_handle,
			          file_io_handle,
			          index_value,
			          error );

			if( result == -1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_GENERIC,
				 "%s: unable to analyze deleted descriptors index value: %" PRIu16 ".",
				 function,
				 entry_index );

				goto on_error;
			}
			else if( result != 0 )
			{
				/* Add the recovered descriptors index values to the index tree
				 */
#if defined( HAVE_DEBUG_OUTPUT )
				if( libcnotify_verbose != 0 )
				{
					libcnotify_printf(
					 "%s: decriptor index value: %" PRIu16 " identifier: %" PRIu64 " is recoverable.\n",
					 function,
					 entry_index,
					 index_value->identifier );
				}
#endif
				node_data_offset = node_offset + ( entry_index * index_node->entry_size );
				node_data_size   = index_node->entry_size;

				if( libpff_descriptors_index_insert_recovered_index_value(
				     descriptors_index,
				     index_value,
				     error ) != 1 )
				{
					libcerror_error_set(
					 error,
					 LIBCERROR_ERROR_DOMAIN_RUNTIME,
					 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
					 "%s: unable to insert recovered descriptors index value: %" PRIu64 " list.",
					 function,
					 index_value->identifier );

					goto on_error;
				}
				index_value = NULL;
			}
			else
			{
				if( libpff_index_value_free(
				     &index_value,
				     error ) != 1 )
				{
					libcerror_error_set(
					 error,
					 LIBCERROR_ERROR_DOMAIN_RUNTIME,
					 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
					 "%s: unable to free index value.",
					 function );

					goto on_error;
				}
			}
		}
	}
	else
	{
		if( node_back_pointer != index_node->back_pointer )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
			 "%s: back pointer mismatch (entry: %" PRIu64 ", node: %" PRIu64 ").",
			 function,
			 node_back_pointer,
			 index_node->back_pointer );

			goto on_error;
		}
		for( entry_index = 0;
		     entry_index < index_node->number_of_entries;
		     entry_index++ )
		{
/* TODO add if( io_handle->abort != 0 ) */
			if( libpff_index_node_get_entry_data(
			     index_node,
			     entry_index,
			     &node_entry_data,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to retrieve node entry: %" PRIu16 " data.",
				 function,
				 entry_index );

				goto on_error;
			}
			if( node_entry_data == NULL )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
				 "%s: missing node entry: %" PRIu16 " data.",
				 function,
				 entry_index );

				goto on_error;
			}
			if( io_handle->file_type == LIBPFF_FILE_TYPE_32BIT )
			{
				byte_stream_copy_to_uint32_little_endian(
				 ( (pff_index_node_branch_entry_32bit_t *) node_entry_data )->file_offset,
				 sub_node_offset );

				byte_stream_copy_to_uint32_little_endian(
				 ( (pff_index_node_branch_entry_32bit_t *) node_entry_data )->back_pointer,
				 sub_node_back_pointer );
			}
			else if( ( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT )
			      || ( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT_4K_PAGE ) )
			{
				byte_stream_copy_to_uint64_little_endian(
				 ( (pff_index_node_branch_entry_64bit_t *) node_entry_data )->file_offset,
				 sub_node_offset );

				byte_stream_copy_to_uint64_little_endian(
				 ( (pff_index_node_branch_entry_64bit_t *) node_entry_data )->back_pointer,
				 sub_node_back_pointer );
			}
#if defined( HAVE_DEBUG_OUTPUT )
			if( libcnotify_verbose != 0 )
			{
				libcnotify_printf(
				 "%s: node entry: %" PRIu16 " sub node offset\t: %" PRIi64 " (0x%08" PRIx64 ")\n",
				 function,
				 entry_index,
				 sub_node_offset,
				 sub_node_offset );
			}
#endif /* defined( HAVE_DEBUG_OUTPUT ) */

			if( libpff_recover_descriptors_index_values(
			     descriptors_index,
			     io_handle,
			     file_io_handle,
			     sub_node_offset,
			     sub_node_back_pointer,
			     recursion_depth + 1,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
				 "%s: unable to analyze descriptors index node at offset: %" PRIi64 " (0x%08" PRIx64 ").",
				 function,
				 sub_node_offset,
				 sub_node_offset );

				goto on_error;
			}
		}
	}
	if( libpff_index_node_free(
	     &index_node,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
		 "%s: unable to free index node.",
		 function );

		goto on_error;
	}
	return( 1 );

on_error:
	if( index_value != NULL )
	{
		libpff_index_value_free(
		 &index_value,
		 NULL );
	}
	if( index_node != NULL )
	{
		libpff_index_node_free(
		 &index_node,
		 NULL );
	}
	return( -1 );
}

/* Analyze if a specific offsets index value is recoverable
 * Returns 1 if recoverable, 0 if not or -1 on error
 */
int libpff_recover_analyze_offsets_index_value(
     libpff_offsets_index_t *offsets_index,
     libpff_io_handle_t *io_handle,
     libbfio_handle_t *file_io_handle,
     libpff_index_value_t *offsets_index_value,
     uint32_t maximum_data_block_data_size,
     libcerror_error_t **error )
{
	libpff_index_value_t *existing_index_value = NULL;
	static char *function                      = "libpff_recover_analyze_offsets_index_value";
	int result                                 = 0;

	if( offsets_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid offsets index.",
		 function );

		return( -1 );
	}
	if( offsets_index_value == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid offsets index value.",
		 function );

		return( -1 );
	}
#if defined( HAVE_DEBUG_OUTPUT )
	if( libcnotify_verbose != 0 )
	{
		libcnotify_printf(
		 "%s: analyzing identifier: %" PRIu64 " (%s) at offset: %" PRIi64 " of size: %" PRIu32 "\n",
		 function,
		 offsets_index_value->identifier,
		 ( ( offsets_index_value->identifier & LIBPFF_OFFSET_INDEX_IDENTIFIER_FLAG_INTERNAL ) ? "internal" : "external" ),
		 offsets_index_value->file_offset,
		 offsets_index_value->data_size );
	}
#endif
	/* Ignore index values without a valid file offset
	 */
	if( offsets_index_value->file_offset <= 0 )
	{
#if defined( HAVE_DEBUG_OUTPUT )
		if( libcnotify_verbose != 0 )
		{
			libcnotify_printf(
			 "%s: deleted offsets index value: %" PRIu64 " has an invalid file offset: %" PRIi64 ".\n",
			 function,
			 offsets_index_value->identifier,
			 offsets_index_value->file_offset );
		}
#endif
		return( 0 );
	}
	/* Ignore index values without a valid data size
	 */
	if( ( offsets_index_value->data_size == 0 )
	 || ( (uint32_t) offsets_index_value->data_size > maximum_data_block_data_size ) )
	{
#if defined( HAVE_DEBUG_OUTPUT )
		if( libcnotify_verbose != 0 )
		{
			libcnotify_printf(
			 "%s: deleted offsets index value: %" PRIu64 " has an invalid data size: %" PRIu32 ".\n",
			 function,
			 offsets_index_value->identifier,
			 offsets_index_value->data_size );
		}
#endif
		return( 0 );
	}
	/* Check if the offsets index value matches an existing index value
	 */
	result = libpff_index_get_value_by_identifier(
		  offsets_index->index,
		  io_handle,
		  file_io_handle,
		  offsets_index_value->identifier,
		  &existing_index_value,
		  error );

	if( result == -1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve offsets index value for identifier: %" PRIu64 ".",
		 function,
		 offsets_index_value->identifier );

		goto on_error;
	}
	else if( result != 0 )
	{
		if( existing_index_value == NULL )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
			 "%s: missing offsets index value for identifier: %" PRIu64 ".",
			 function,
			 offsets_index_value->identifier );

			goto on_error;
		}
		result = 0;

		if( ( offsets_index_value->file_offset == existing_index_value->file_offset )
		 && ( offsets_index_value->data_size == existing_index_value->data_size ) )
		{
#if defined( HAVE_DEBUG_OUTPUT )
			if( libcnotify_verbose != 0 )
			{
				libcnotify_printf(
				 "%s: deleted offsets index value: %" PRIu64 " matches existing item value.\n",
				 function,
				 offsets_index_value->identifier );
			}
#endif
			result = 1;
		}
		if( libpff_index_value_free(
		     &existing_index_value,
		     error ) != 1 )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
			 "%s: unable to free index value: %d.",
			 function,
			 offsets_index_value->identifier );

			goto on_error;
		}
		if( result != 0 )
		{
			return( 0 );
		}
	}
	/* Check if the offsets index value matches a previously recovered index value
	 */
	result = libpff_recover_check_offsets_index_for_recovered_value(
		  offsets_index,
		  offsets_index_value,
		  error );

	if( result == -1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to determine if deleted offsets index value: %" PRIu64 " was previously recovered.",
		 function,
		 offsets_index_value->identifier );

		goto on_error;
	}
	else if( result != 0 )
	{
#if defined( HAVE_DEBUG_OUTPUT )
		if( libcnotify_verbose != 0 )
		{
			libcnotify_printf(
			 "%s: deleted offsets index value: %" PRIu64 " matches previous recovered index value.\n",
			 function,
			 offsets_index_value->identifier );
		}
#endif
		return( 0 );
	}
	return( 1 );

on_error:
	if( existing_index_value != NULL )
	{
		libpff_index_value_free(
		 &existing_index_value,
		 NULL );
	}
	return( -1 );
}

/* Checks if the specific offsets index contains a specific recovered index value
 * Returns 1 if exists, 0 if not or -1 on error
 */
int libpff_recover_check_offsets_index_for_recovered_value(
     libpff_offsets_index_t *offsets_index,
     libpff_index_value_t *offsets_index_value,
     libcerror_error_t **error )
{
	libcdata_tree_node_t *upper_node              = NULL;
	libpff_index_value_t *index_value             = NULL;
	libpff_index_values_list_t *index_values_list = NULL;
	static char *function                         = "libpff_recover_check_offsets_index_for_recovered_value";
	int index_value_iterator                      = 0;
	int number_of_index_values                    = 0;
	int result                                    = 0;

	if( offsets_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid offsets index.",
		 function );

		return( -1 );
	}
	if( offsets_index_value == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid offsets index value.",
		 function );

		return( -1 );
	}
	result = libcdata_btree_get_value_by_value(
		  offsets_index->recovered_index_values_tree,
		  (intptr_t *) offsets_index_value,
		  (int (*)(intptr_t *, intptr_t *, libcerror_error_t **)) &libpff_index_values_list_compare,
		  &upper_node,
		  (intptr_t **) &index_values_list,
		  error );

	if( result == -1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve recovered offsets index value: %" PRIu64 " list.",
		 function,
		 offsets_index_value->identifier );

		return( -1 );
	}
	else if( result != 0 )
	{
		if( libpff_index_values_list_number_of_values(
		     index_values_list,
		     &number_of_index_values,
		     error ) != 1 )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
			 "%s: unable to retrieve number of recovered offsets index values for identifier: %" PRIu64 ".",
			 function,
			 offsets_index_value->identifier );

			return( -1 );
		}
		result = 0;

		for( index_value_iterator = 0;
		     index_value_iterator < number_of_index_values;
		     index_value_iterator++ )
		{
			if( libpff_index_values_list_get_value_by_index(
			     index_values_list,
			     index_value_iterator,
			     &index_value,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to retrieve recovered offsets index value: %" PRIu64 " list element: %d.",
				 function,
				 offsets_index_value->identifier,
				 index_value_iterator );

				return( -1 );
			}
			if( index_value == NULL )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
				 "%s: missing recovered offsets index value: %" PRIu64 " list element: %d.",
				 function,
				 offsets_index_value->identifier,
				 index_value_iterator );

				return( -1 );
			}
			if( ( offsets_index_value->file_offset == index_value->file_offset )
			 && ( offsets_index_value->data_size == index_value->data_size ) )
			{
				result = 1;

				break;
			}
		}
	}
	return( result );
}

/* Scans an offsets index node for recoverable index nodes
 * Returns 1 if successful or -1 on error
 */
int libpff_recover_analyze_offsets_index_node(
     libpff_offsets_index_t *offsets_index,
     libpff_io_handle_t *io_handle,
     libbfio_handle_t *file_io_handle,
     off64_t node_offset,
     uint64_t node_back_pointer,
     uint32_t maximum_data_block_data_size,
     int recursion_depth,
     libcerror_error_t **error )
{
	libpff_index_node_t *index_node   = NULL;
	libpff_index_value_t *index_value = NULL;
	uint8_t *node_entry_data          = NULL;
	static char *function             = "libpff_recover_analyze_offsets_index_node";
	size64_t node_data_size           = 0;
	off64_t node_data_offset          = 0;
	uint64_t sub_node_back_pointer    = 0;
	uint64_t sub_node_offset          = 0;
	uint16_t entry_index              = 0;
	int result                        = 0;

	if( offsets_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid offsets index.",
		 function );

		return( -1 );
	}
	if( offsets_index->index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
		 "%s: invalid offsets index - missing index.",
		 function );

		return( -1 );
	}
	if( io_handle == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid IO handle.",
		 function );

		return( -1 );
	}
	if( ( recursion_depth < 0 )
	 || ( recursion_depth > LIBPFF_MAXIMUM_ITEM_TREE_RECURSION_DEPTH ) )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
		 "%s: invalid recursion depth value out of bounds.",
		 function );

		return( -1 );
	}
	if( libpff_index_node_initialize(
	     &index_node,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
		 "%s: unable to create offsets index node.",
		 function );

		goto on_error;
	}
	if( libpff_index_node_read_file_io_handle(
	     index_node,
	     file_io_handle,
	     node_offset,
	     io_handle->file_type,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_IO,
		 LIBCERROR_IO_ERROR_READ_FAILED,
		 "%s: unable to read offsets index node at offset: %" PRIi64 " (0x%08" PRIx64 ").",
		 function,
		 node_offset,
		 node_offset );

		goto on_error;
	}
	if( index_node->level == LIBPFF_INDEX_NODE_LEVEL_LEAF )
	{
		for( entry_index = index_node->number_of_entries;
		     entry_index < index_node->maximum_number_of_entries;
		     entry_index++ )
		{
/* TODO add if( io_handle->abort != 0 ) */
			if( libpff_index_node_get_entry_data(
			     index_node,
			     entry_index,
			     &node_entry_data,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to retrieve deleted node entry: %" PRIu16 " data.",
				 function,
				 entry_index );

				goto on_error;
			}
			if( node_entry_data == NULL )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
				 "%s: missing deleted node entry: %" PRIu16 " data.",
				 function,
				 entry_index );

				goto on_error;
			}
			if( libpff_index_value_initialize(
			     &index_value,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
				 "%s: unable to create index value.",
				 function );

				goto on_error;
			}
			if( libpff_index_value_read_data(
			     index_value,
			     io_handle,
			     LIBPFF_INDEX_TYPE_OFFSET,
			     node_entry_data,
			     (size_t) index_node->entry_size,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_IO,
				 LIBCERROR_IO_ERROR_READ_FAILED,
				 "%s: unable to read index value.",
				 function );

				goto on_error;
			}
			result = libpff_recover_analyze_offsets_index_value(
			          offsets_index,
			          io_handle,
			          file_io_handle,
			          index_value,
			          maximum_data_block_data_size,
			          error );

			if( result == -1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_GENERIC,
				 "%s: unable to analyze deleted offsets index value: %" PRIu16 ".",
				 function,
				 entry_index );

				goto on_error;
			}
			else if( result != 0 )
			{
				/* Add the recovered offsets index values to the index tree
				 */
#if defined( HAVE_DEBUG_OUTPUT )
				if( libcnotify_verbose != 0 )
				{
					libcnotify_printf(
					 "%s: offsets index value: %" PRIu16 " identifier: %" PRIu64 " is recoverable.\n",
					 function,
					 entry_index,
					 index_value->identifier );
				}
#endif
				node_data_offset = node_offset + ( entry_index * index_node->entry_size );
				node_data_size   = index_node->entry_size;

				if( libpff_offsets_index_insert_recovered_index_value(
				     offsets_index,
				     index_value,
				     error ) != 1 )
				{
					libcerror_error_set(
					 error,
					 LIBCERROR_ERROR_DOMAIN_RUNTIME,
					 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
					 "%s: unable to insert recovered offsets index value: %" PRIu64 " list.",
					 function,
					 index_value->identifier );

					goto on_error;
				}
				index_value = NULL;
			}
			else
			{
				if( libpff_index_value_free(
				     &index_value,
				     error ) != 1 )
				{
					libcerror_error_set(
					 error,
					 LIBCERROR_ERROR_DOMAIN_RUNTIME,
					 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
					 "%s: unable to free index value.",
					 function );

					goto on_error;
				}
			}
		}
	}
	else
	{
		if( node_back_pointer != index_node->back_pointer )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
			 "%s: back pointer mismatch (entry: %" PRIu64 ", node: %" PRIu64 ").",
			 function,
			 node_back_pointer,
			 index_node->back_pointer );

			goto on_error;
		}
		for( entry_index = 0;
		     entry_index < index_node->number_of_entries;
		     entry_index++ )
		{
/* TODO add if( io_handle->abort != 0 ) */
			if( libpff_index_node_get_entry_data(
			     index_node,
			     entry_index,
			     &node_entry_data,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to retrieve node entry: %" PRIu16 " data.",
				 function,
				 entry_index );

				goto on_error;
			}
			if( node_entry_data == NULL )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
				 "%s: missing node entry: %" PRIu16 " data.",
				 function,
				 entry_index );

				goto on_error;
			}
			if( io_handle->file_type == LIBPFF_FILE_TYPE_32BIT )
			{
				byte_stream_copy_to_uint32_little_endian(
				 ( (pff_index_node_branch_entry_32bit_t *) node_entry_data )->file_offset,
				 sub_node_offset );

				byte_stream_copy_to_uint32_little_endian(
				 ( (pff_index_node_branch_entry_32bit_t *) node_entry_data )->back_pointer,
				 sub_node_back_pointer );
			}
			else if( ( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT )
			      || ( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT_4K_PAGE ) )
			{
				byte_stream_copy_to_uint64_little_endian(
				 ( (pff_index_node_branch_entry_64bit_t *) node_entry_data )->file_offset,
				 sub_node_offset );

				byte_stream_copy_to_uint64_little_endian(
				 ( (pff_index_node_branch_entry_64bit_t *) node_entry_data )->back_pointer,
				 sub_node_back_pointer );
			}
#if defined( HAVE_DEBUG_OUTPUT )
			if( libcnotify_verbose != 0 )
			{
				libcnotify_printf(
				 "%s: node entry: %" PRIu16 " sub node offset\t: %" PRIi64 " (0x%08" PRIx64 ")\n",
				 function,
				 entry_index,
				 sub_node_offset,
				 sub_node_offset );
			}
#endif /* defined( HAVE_DEBUG_OUTPUT ) */

			if( libpff_recover_analyze_offsets_index_node(
			     offsets_index,
			     io_handle,
			     file_io_handle,
			     sub_node_offset,
			     sub_node_back_pointer,
			     maximum_data_block_data_size,
			     recursion_depth + 1,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
				 "%s: unable to analyze offsets index node at offset: %" PRIi64 " (0x%08" PRIx64 ").",
				 function,
				 sub_node_offset,
				 sub_node_offset );

				goto on_error;
			}
		}
	}
	if( libpff_index_node_free(
	     &index_node,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
		 "%s: unable to free index node.",
		 function );

		goto on_error;
	}
	return( 1 );

on_error:
	if( index_value != NULL )
	{
		libpff_index_value_free(
		 &index_value,
		 NULL );
	}
	if( index_node != NULL )
	{
		libpff_index_node_free(
		 &index_node,
		 NULL );
	}
	return( -1 );
}

/* Scans for recoverable data blocks
 * Returns 1 if successful or -1 on error
 */
int libpff_recover_data_blocks(
     libpff_io_handle_t *io_handle,
     libbfio_handle_t *file_io_handle,
     libpff_descriptors_index_t *descriptors_index,
     libpff_offsets_index_t *offsets_index,
     libcdata_range_list_t *unallocated_data_block_list,
     libcdata_range_list_t *unallocated_page_block_list,
     uint8_t recovery_flags,
     libcerror_error_t **error )
{
	uint8_t *block_buffer                   = NULL;
	uint8_t *data_block_footer              = NULL;
	intptr_t *value                         = NULL;
	static char *function                   = "libpff_recover_data_blocks";
	off64_t block_buffer_data_offset        = 0;
	off64_t block_offset                    = 0;
	off64_t data_block_offset               = 0;
	off64_t page_block_offset               = 0;
	size64_t block_size                     = 0;
	size64_t data_block_size                = 0;
	size64_t page_block_size                = 0;
	size_t block_buffer_offset              = 0;
	size_t block_buffer_size_available      = 0;
	size_t data_block_data_offset           = 0;
	size_t read_size                        = 0;
	ssize_t read_count                      = 0;
	uint64_t data_block_back_pointer        = 0;
	uint32_t data_block_calculated_checksum = 0;
	uint32_t data_block_stored_checksum     = 0;
	uint32_t maximum_data_block_size        = 0;
	uint16_t data_block_data_size           = 0;
	uint16_t format_data_block_size         = 0;
	uint16_t format_page_block_size         = 0;
	uint16_t scan_block_size                = 0;
	uint8_t supported_recovery_flags        = 0;
	int number_of_unallocated_data_blocks   = 0;
	int number_of_unallocated_page_blocks   = 0;
	int result                              = 0;
	int unallocated_data_block_index        = 0;
	int unallocated_page_block_index        = 0;

	if( io_handle == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid IO handle.",
		 function );

		return( -1 );
	}
	if( ( io_handle->file_type != LIBPFF_FILE_TYPE_32BIT )
	 && ( io_handle->file_type != LIBPFF_FILE_TYPE_64BIT )
	 && ( io_handle->file_type != LIBPFF_FILE_TYPE_64BIT_4K_PAGE ) )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
		 "%s: unsupported file type.",
		 function );

		return( -1 );
	}
	if( ( io_handle->file_type == LIBPFF_FILE_TYPE_32BIT )
	 || ( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT ) )
	{
		format_data_block_size  = 64;
		format_page_block_size  = 512;
	}
	else
	{
		format_data_block_size  = 512;
		format_page_block_size  = 4096;
	}
	if( ( io_handle->file_type == LIBPFF_FILE_TYPE_32BIT )
	 || ( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT ) )
	{
		maximum_data_block_size = 8192;
	}
	else
	{
/* TODO: this value is currently assumed based on the 512 x 8 = 4k page */
		maximum_data_block_size = 65536;
	}
	supported_recovery_flags = LIBPFF_RECOVERY_FLAG_IGNORE_ALLOCATION_DATA
	                         | LIBPFF_RECOVERY_FLAG_SCAN_FOR_FRAGMENTS;

	if( ( recovery_flags & ~( supported_recovery_flags ) ) != 0 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_UNSUPPORTED_VALUE,
		 "%s: unsupported recovery flags.",
		 function );

		return( -1 );
	}
	/* Scan the unallocated page block list or all blocks for index nodes
	 */
	if( ( recovery_flags & LIBPFF_RECOVERY_FLAG_IGNORE_ALLOCATION_DATA ) == 0 )
	{
		if( libcdata_range_list_get_number_of_elements(
		     unallocated_data_block_list,
		     &number_of_unallocated_data_blocks,
		     error ) != 1 )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
			 "%s: unable to retrieve number of unallocated data blocks.",
			 function );

			goto on_error;
		}
		if( ( io_handle->file_type == LIBPFF_FILE_TYPE_32BIT )
		 || ( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT ) )
		{
			if( libcdata_range_list_get_number_of_elements(
			     unallocated_page_block_list,
			     &number_of_unallocated_page_blocks,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to retrieve number of unallocated page blocks.",
				 function );

				goto on_error;
			}
		}
		else
		{
			number_of_unallocated_page_blocks = 0;
		}
	}
	if( ( recovery_flags & LIBPFF_RECOVERY_FLAG_SCAN_FOR_FRAGMENTS ) == 0 )
	{
		scan_block_size = format_page_block_size;
	}
	else
	{
		scan_block_size = format_data_block_size;
	}
	block_buffer = (uint8_t *) memory_allocate(
	                            sizeof( uint8_t ) * ( maximum_data_block_size * 2 ) );

	if( block_buffer == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_MEMORY,
		 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
		 "%s: unable to create block buffer.",
		 function );

		goto on_error;
	}
	if( ( number_of_unallocated_data_blocks > 0 )
	 || ( number_of_unallocated_page_blocks > 0 )
	 || ( ( recovery_flags & LIBPFF_RECOVERY_FLAG_IGNORE_ALLOCATION_DATA ) != 0 ) )
	{
		block_offset      = 0;
		data_block_offset = -1;
		page_block_offset = -1;

		while( block_offset < (off64_t) io_handle->file_size )
		{
			if( io_handle->abort != 0 )
			{
				goto on_error;
			}
			if( ( recovery_flags & LIBPFF_RECOVERY_FLAG_IGNORE_ALLOCATION_DATA ) == 0 )
			{
				if( data_block_offset < block_offset )
				{
					if( unallocated_data_block_index < number_of_unallocated_data_blocks )
					{
						if( libcdata_range_list_get_range_by_index(
						     unallocated_data_block_list,
						     unallocated_data_block_index,
						     (uint64_t *) &data_block_offset,
						     (uint64_t *) &data_block_size,
						     &value,
						     error ) != 1 )
						{
							libcerror_error_set(
							 error,
							 LIBCERROR_ERROR_DOMAIN_RUNTIME,
							 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
							 "%s: unable to retrieve unallocated data block: %d.",
							 function,
							 unallocated_data_block_index );

							goto on_error;
						}
						unallocated_data_block_index++;
					}
					else
					{
						data_block_offset = (off64_t) io_handle->file_size;
						data_block_size   = 0;
					}
				}
				if( page_block_offset < block_offset )
				{
					if( unallocated_page_block_index < number_of_unallocated_page_blocks )
					{
						if( libcdata_range_list_get_range_by_index(
						     unallocated_page_block_list,
						     unallocated_page_block_index,
						     (uint64_t *) &page_block_offset,
						     (uint64_t *) &page_block_size,
						     &value,
						     error ) != 1 )
						{
							libcerror_error_set(
							 error,
							 LIBCERROR_ERROR_DOMAIN_RUNTIME,
							 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
							 "%s: unable to retrieve unallocated page block: %d.",
							 function,
							 unallocated_page_block_index );

							goto on_error;
						}
						unallocated_page_block_index++;
					}
					else
					{
						page_block_offset = (off64_t) io_handle->file_size;
						page_block_size   = 0;
					}
				}
			}
			if( ( recovery_flags & LIBPFF_RECOVERY_FLAG_IGNORE_ALLOCATION_DATA ) == 0 )
			{
				if( ( data_block_offset >= (off64_t) io_handle->file_size )
				 && ( page_block_offset >= (off64_t) io_handle->file_size ) )
				{
					break;
				}
				/* Process the smallest offset
				 */
				else if( ( data_block_offset < page_block_offset )
				      && ( data_block_size > scan_block_size ) )
				{
					block_offset = data_block_offset;
					block_size   = data_block_size;
				}
				else if( ( page_block_offset < data_block_offset )
				      && ( page_block_size > scan_block_size ) )
				{
					block_offset = page_block_offset;
					block_size   = page_block_size;
				}
				/* Process the largest range
				 */
				else if( data_block_size > page_block_size )
				{
					block_offset = data_block_offset;
					block_size   = data_block_size;
				}
				else
				{
					block_offset = page_block_offset;
					block_size   = page_block_size;
				}
			}
			else
			{
				block_size = scan_block_size;
			}
			if( ( block_offset % scan_block_size ) != 0 )
			{
				block_offset  = ( ( block_offset / scan_block_size ) + 1 ) * scan_block_size;
				block_size   -= block_size % scan_block_size;
			}
			if( block_size < scan_block_size )
			{
				block_offset += block_size;

				continue;
			}
			while( block_size >= scan_block_size )
			{
				/* The index nodes have a fixed block size and stored block size aligned
				 */
				if( ( block_size >= format_page_block_size )
				 && ( ( block_offset % format_page_block_size ) == 0 ) )
				{
					/* Scan for index values in the index node
					 */
					result = libpff_recover_index_values(
						  io_handle,
						  file_io_handle,
						  descriptors_index,
						  offsets_index,
						  unallocated_data_block_list,
						  block_offset,
						  recovery_flags,
						  error );

					if( result == -1 )
					{
						libcerror_error_set(
						 error,
						 LIBCERROR_ERROR_DOMAIN_RUNTIME,
						 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
						 "%s: unable to recover index node at offset: %" PRIi64 ".\n",
						 function,
						 block_offset );

						goto on_error;
					}
					else if( result == 1 )
					{
						block_offset += format_page_block_size;
						block_size   -= format_page_block_size;

						continue;
					}
				}
				if( ( recovery_flags & LIBPFF_RECOVERY_FLAG_SCAN_FOR_FRAGMENTS ) == 0 )
				{
					block_offset += scan_block_size;
					block_size   -= scan_block_size;

					continue;
				}
/* TODO optimize by minimizing amount of reads */
				if( block_buffer_size_available == 0 )
				{
					block_buffer_data_offset = block_offset;

					if( block_buffer_offset > 0 )
					{
/* TODO optimize by copying the needed data to the front of the buffer */
						block_buffer_data_offset -= format_data_block_size;
						block_buffer_offset       = maximum_data_block_size - format_data_block_size;
					}
					read_size = (size_t) block_size;

					if( read_size > (size_t) maximum_data_block_size )
					{
						read_size = (size_t) maximum_data_block_size;
					}
#if defined( HAVE_DEBUG_OUTPUT )
					if( libcnotify_verbose != 0 )
					{
						libcnotify_printf(
						 "%s: reading data block at offset: %" PRIi64 " (0x%08" PRIx64 ") of size: %" PRIzd "\n",
						 function,
						 block_buffer_data_offset,
						 block_buffer_data_offset,
						 read_size );
					}
#endif
					read_count = libbfio_handle_read_buffer_at_offset(
						      file_io_handle,
						      &( block_buffer[ block_buffer_offset ] ),
						      read_size,
						      block_buffer_data_offset,
						      error );

					if( read_count != (ssize_t) read_size )
					{
						libcerror_error_set(
						 error,
						 LIBCERROR_ERROR_DOMAIN_IO,
						 LIBCERROR_IO_ERROR_READ_FAILED,
						 "%s: unable to read data block at offset: %" PRIi64 " (0x%08" PRIx64 ").",
						 function,
						 block_buffer_data_offset,
						 block_buffer_data_offset );

						goto on_error;
					}
					block_buffer_size_available = read_size;
				}
				if( block_buffer_size_available >= format_data_block_size )
				{
					/* Scan the block for a data block footer
					 */
					data_block_footer = &( block_buffer[ block_buffer_offset ] );

					if( io_handle->file_type == LIBPFF_FILE_TYPE_32BIT )
					{
						data_block_footer += format_data_block_size - sizeof( pff_block_footer_32bit_t );

						byte_stream_copy_to_uint16_little_endian(
						 ( (pff_block_footer_32bit_t *) data_block_footer )->data_size,
						 data_block_data_size );
						byte_stream_copy_to_uint32_little_endian(
						 ( (pff_block_footer_32bit_t *) data_block_footer )->back_pointer,
						 data_block_back_pointer );
						byte_stream_copy_to_uint32_little_endian(
						 ( (pff_block_footer_32bit_t *) data_block_footer )->checksum,
						 data_block_stored_checksum );
					}
					else if( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT )
					{
						data_block_footer += format_data_block_size - sizeof( pff_block_footer_64bit_t );

						byte_stream_copy_to_uint16_little_endian(
						 ( (pff_block_footer_64bit_t *) data_block_footer )->data_size,
						 data_block_data_size );
						byte_stream_copy_to_uint32_little_endian(
						 ( (pff_block_footer_64bit_t *) data_block_footer )->checksum,
						 data_block_stored_checksum );
						byte_stream_copy_to_uint64_little_endian(
						 ( (pff_block_footer_64bit_t *) data_block_footer )->back_pointer,
						 data_block_back_pointer );
					}
					else if( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT_4K_PAGE )
					{
						data_block_footer += format_data_block_size - sizeof( pff_block_footer_64bit_4k_page_t );

						byte_stream_copy_to_uint16_little_endian(
						 ( (pff_block_footer_64bit_4k_page_t *) data_block_footer )->data_size,
						 data_block_data_size );
						byte_stream_copy_to_uint32_little_endian(
						 ( (pff_block_footer_64bit_4k_page_t *) data_block_footer )->checksum,
						 data_block_stored_checksum );
						byte_stream_copy_to_uint64_little_endian(
						 ( (pff_block_footer_64bit_4k_page_t *) data_block_footer )->back_pointer,
						 data_block_back_pointer );
					}
					/* Check if back pointer itself is not empty but the upper 32-bit are
					 */
					if( ( data_block_back_pointer != 0 )
					 && ( ( data_block_back_pointer >> 32 ) == 0 ) )
					{
						data_block_data_offset = block_buffer_offset - ( ( data_block_data_size / format_data_block_size ) * format_data_block_size );

						if( (size_t) data_block_data_size < read_size )
						{
							result = libpff_recover_analyze_data_block_back_pointer(
							          offsets_index,
							          data_block_back_pointer,
							          (off64_t) block_buffer_data_offset + data_block_data_offset,
							          (size32_t) data_block_data_size,
							          error );

							if( result == -1 )
							{
								libcerror_error_set(
								 error,
								 LIBCERROR_ERROR_DOMAIN_RUNTIME,
								 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
								 "%s: unable to recovere data block: %" PRIu64 ".",
								 function,
								 data_block_back_pointer );

								goto on_error;
							}
							else if( result != 0 )
							{
								block_offset += format_data_block_size;
								block_size   -= format_data_block_size;

/* TODO reset block_buffer_offset and block_buffer_size_available ? */

								continue;
							}
							if( data_block_stored_checksum != 0 )
							{
								if( libpff_checksum_calculate_weak_crc32(
								     &data_block_calculated_checksum,
								     &( block_buffer[ data_block_data_offset ] ),
								     data_block_data_size,
								     0,
								     error ) != 1 )
								{
									libcerror_error_set(
									 error,
									 LIBCERROR_ERROR_DOMAIN_RUNTIME,
									 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
									 "%s: unable to calculate weak CRC-32.",
									 function );

									goto on_error;
								}
								if( data_block_stored_checksum != data_block_calculated_checksum )
								{
#if defined( HAVE_DEBUG_OUTPUT )
									if( libcnotify_verbose != 0 )
									{
										libcnotify_printf(
										 "%s: mismatch in data block: %" PRIu64 " checksum ( %" PRIu32 " != %" PRIu32 " ).\n",
										 function,
										 data_block_back_pointer,
										 data_block_stored_checksum,
										 data_block_calculated_checksum );
									}
#endif
									block_offset += format_data_block_size;
									block_size   -= format_data_block_size;

									continue;
								}
							}
/* TODO consider data block as fragment */

							data_block_data_size = ( ( data_block_data_size / format_data_block_size ) + 1 ) * format_data_block_size;

#if defined( HAVE_DEBUG_OUTPUT )
							if( libcnotify_verbose != 0 )
							{
								libcnotify_printf(
								 "%s: data block back pointer: 0x%08" PRIx64 "\n",
								 function,
								 data_block_back_pointer );

								libcnotify_printf(
								 "%s: data block data at offset: %" PRIi64 " (0x%08" PRIx64 ") of size: %" PRIzd "\n",
								 function,
								 block_buffer_data_offset + data_block_data_offset,
								 block_buffer_data_offset + data_block_data_offset,
								 data_block_data_size );
								libcnotify_print_data(
								 &( block_buffer[ data_block_data_offset ] ),
								 data_block_data_size,
								 LIBCNOTIFY_PRINT_DATA_FLAG_GROUP_DATA );
							}
#endif
						}
					}
					block_buffer_offset         += format_data_block_size;
					block_buffer_size_available -= format_data_block_size;
				}
				else
				{
					block_buffer_offset         = 0;
					block_buffer_size_available = 0;
				}
				block_offset += scan_block_size;
				block_size   -= scan_block_size;
			}
		}
	}
	memory_free(
	 block_buffer );

	return( 1 );

on_error:
	if( block_buffer != NULL )
	{
		memory_free(
		 block_buffer );
	}
	return( -1 );
}

/* Scans for recoverable index values in an index node
 * Returns 1 if successful, returns 0 if no valid index node could be found or -1 on error
 */
int libpff_recover_index_values(
     libpff_io_handle_t *io_handle,
     libbfio_handle_t *file_io_handle,
     libpff_descriptors_index_t *descriptors_index,
     libpff_offsets_index_t *offsets_index,
     libcdata_range_list_t *unallocated_data_block_list,
     size64_t node_offset,
     uint8_t recovery_flags,
     libcerror_error_t **error )
{
	libpff_index_node_t *index_node       = NULL;
	libpff_index_value_t *index_value     = NULL;
	uint8_t *node_entry_data              = NULL;
	static char *function                 = "libpff_recover_index_values";
	const char *index_string              = NULL;
	off64_t index_value_file_offset       = 0;
	uint64_t index_value_identifier       = 0;
        uint32_t maximum_data_block_data_size = 0;
	uint16_t index_value_data_size        = 0;
	uint8_t entry_index                   = 0;
	int recoverable                       = 0;
	int result                            = 0;

	if( io_handle == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid IO handle.",
		 function );

		return( -1 );
	}
	if( ( io_handle->file_type != LIBPFF_FILE_TYPE_32BIT )
	 && ( io_handle->file_type != LIBPFF_FILE_TYPE_64BIT )
	 && ( io_handle->file_type != LIBPFF_FILE_TYPE_64BIT_4K_PAGE ) )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
		 "%s: unsupported file type.",
		 function );

		return( -1 );
	}
	if( descriptors_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid descriptors index.",
		 function );

		return( -1 );
	}
	if( offsets_index == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid offsets index.",
		 function );

		return( -1 );
	}
	if( io_handle->file_type == LIBPFF_FILE_TYPE_32BIT )
	{
		maximum_data_block_data_size = 8192 - 12;
	}
	else if( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT )
	{
		maximum_data_block_data_size = 8192 - 16;
	}
	else
	{
/* TODO: this value is currently assumed based on the 512 x 8 = 4k page */
		maximum_data_block_data_size = 65536 - 24;
	}
	if( libpff_index_node_initialize(
	     &index_node,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
		 "%s: unable to create index node.",
		 function );

		goto on_error;
	}
	if( libpff_index_node_read_file_io_handle(
	     index_node,
	     file_io_handle,
	     node_offset,
	     io_handle->file_type,
	     error ) != 1 )
	{
#if defined( HAVE_DEBUG_OUTPUT )
		if( ( libcnotify_verbose != 0 )
		 && ( error != NULL )
		 && ( *error != NULL ) )
		{
			libcnotify_print_error_backtrace(
			 *error );
		}
#endif
		libcerror_error_free(
		 error );

		libpff_index_node_free(
		 &index_node,
		 NULL );

		return( 0 );
	}
	if( index_node->type == LIBPFF_INDEX_TYPE_DESCRIPTOR )
	{
		index_string = "descriptors";
	}
	else if( index_node->type == LIBPFF_INDEX_TYPE_OFFSET )
	{
		index_string = "offsets";
	}
	if( ( index_node->type != LIBPFF_INDEX_TYPE_DESCRIPTOR )
	 && ( index_node->type != LIBPFF_INDEX_TYPE_OFFSET ) )
	{
#if defined( HAVE_DEBUG_OUTPUT )
		if( libcnotify_verbose != 0 )
		{
			libcnotify_printf(
			 "%s: unsupported index type: 0x%02" PRIx8 ".\n",
			 function,
			 index_node->type );
		}
#endif
	}
	else if( index_node->level != LIBPFF_INDEX_NODE_LEVEL_LEAF )
	{
#if defined( HAVE_DEBUG_OUTPUT )
		if( libcnotify_verbose != 0 )
		{
			libcnotify_printf(
			 "%s: skipping %s index branch node at level: %" PRIu8 ".\n",
			 function,
			 index_string,
			 index_node->level );
		}
#endif
	}
	else
	{
		/* Check if the index leaf entries are recoverable
		 */
		for( entry_index = 0;
		     entry_index < index_node->maximum_number_of_entries;
		     entry_index++ )
		{
			if( libpff_index_node_get_entry_data(
			     index_node,
			     entry_index,
			     &node_entry_data,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to retrieve node entry: %" PRIu8 " data.",
				 function,
				 entry_index );

				goto on_error;
			}
			if( node_entry_data == NULL )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
				 "%s: missing node entry: %" PRIu8 " data.",
				 function,
				 entry_index );

				goto on_error;
			}
#if defined( HAVE_DEBUG_OUTPUT )
			if( libcnotify_verbose != 0 )
			{
				libcnotify_printf(
				 "%s: analyzing %s index entry: %" PRIu8 ".\n",
				 function,
				 index_string,
				 entry_index );
			}
#endif
			if( libpff_index_value_initialize(
			     &index_value,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
				 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
				 "%s: unable to create %s index value.",
				 function,
				 index_string );

				goto on_error;
			}
			if( libpff_index_value_read_data(
			     index_value,
			     io_handle,
			     index_node->type,
			     node_entry_data,
			     (size_t) index_node->entry_size,
			     error ) != 1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_IO,
				 LIBCERROR_IO_ERROR_READ_FAILED,
				 "%s: unable to read %s index value.",
				 function,
				 index_string );

				goto on_error;
			}
			recoverable = 1;

			/* Ignore index values without an identifier
			 */
			if( index_value->identifier == 0 )
			{
#if defined( HAVE_DEBUG_OUTPUT )
				if( libcnotify_verbose != 0 )
				{
					libcnotify_printf(
					 "%s: %s index entry: %" PRIu8 " has an empty identifier.\n",
					 function,
					 index_string,
					 entry_index );
				}
#endif
				recoverable = 0;
			}
			else if( index_node->type == LIBPFF_INDEX_TYPE_DESCRIPTOR )
			{
				/* Ignore descriptors index values without a data identifier
				 */
				if( index_value->data_identifier == 0 )
				{
#if defined( HAVE_DEBUG_OUTPUT )
					if( libcnotify_verbose != 0 )
					{
						libcnotify_printf(
						 "%s: %s index entry: %" PRIu8 " identifier: %" PRIu64 " has an empty data identifier.\n",
						 function,
						 index_string,
						 entry_index,
						 index_value->identifier );
					}
#endif
					recoverable = 0;
				}
			}
			else if( index_node->type == LIBPFF_INDEX_TYPE_OFFSET )
			{
				/* Ignore index values without a valid file offset
				 */
				if( index_value->file_offset <= 0 )
				{
#if defined( HAVE_DEBUG_OUTPUT )
					if( libcnotify_verbose != 0 )
					{
						libcnotify_printf(
						 "%s: %s index entry: %" PRIu8 " identifier: %" PRIu64 " has an invalid file offset: %" PRIi64 " (0x%" PRIx64 ").\n",
						 function,
						 index_string,
						 entry_index,
						 index_value->identifier,
						 index_value->file_offset,
						 index_value->file_offset );
					}
#endif
					recoverable = 0;
				}
				/* Ignore index values without a valid data size
				 */
				else if( ( index_value->data_size == 0 )
				      || ( (uint32_t) index_value->data_size > maximum_data_block_data_size ) )
				{
#if defined( HAVE_DEBUG_OUTPUT )
					if( libcnotify_verbose != 0 )
					{
						libcnotify_printf(
						 "%s: %s index entry: %" PRIu8 " identifier: %" PRIu64 " has an invalid data size: %" PRIu16 ".\n",
						 function,
						 index_string,
						 entry_index,
						 index_value->identifier,
						 index_value->data_size );
					}
#endif
					recoverable = 0;
				}
			}
			if( recoverable != 0 )
			{
				if( index_node->type == LIBPFF_INDEX_TYPE_DESCRIPTOR )
				{
					result = libpff_recover_analyze_descriptors_index_value(
						  descriptors_index,
						  io_handle,
						  file_io_handle,
						  index_value,
						  error );
				}
				else if( index_node->type == LIBPFF_INDEX_TYPE_OFFSET )
				{
					result = libpff_recover_analyze_offsets_index_value(
						  offsets_index,
						  io_handle,
						  file_io_handle,
						  index_value,
						  maximum_data_block_data_size,
						  error );
				}
				if( result == -1 )
				{
					libcerror_error_set(
					 error,
					 LIBCERROR_ERROR_DOMAIN_RUNTIME,
					 LIBCERROR_RUNTIME_ERROR_GENERIC,
					 "%s: unable to analyze deleted %d index value: %" PRIu64 ".",
					 function,
					 index_string,
					 index_value->identifier );

					goto on_error;
				}
				recoverable = result;
			}
			if( recoverable != 0 )
			{
				/* Check if the offsets index value is unallocated according to the
				 * unallocated data block list
				 */
				if( ( index_node->type == LIBPFF_INDEX_TYPE_OFFSET )
				 && ( ( recovery_flags & LIBPFF_RECOVERY_FLAG_IGNORE_ALLOCATION_DATA ) == 0 ) )
				{
					result = libcdata_range_list_range_is_present(
						  unallocated_data_block_list,
						  (uint64_t) index_value_file_offset,
						  (uint64_t) index_value_data_size,
						  error );

					if( result == -1 )
					{
						libcerror_error_set(
						 error,
						 LIBCERROR_ERROR_DOMAIN_RUNTIME,
						 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
						 "%s: error determining if offset range is unallocated.",
						 function );

						goto on_error;
					}
					else if( result == 0 )
					{
#if defined( HAVE_DEBUG_OUTPUT )
						if( libcnotify_verbose != 0 )
						{
							libcnotify_printf(
							 "%s: %s index entry: %" PRIu8 " identifier: %" PRIu64 " refers to allocated range: 0x%08" PRIx64 " - 0x%08" PRIx64 " (%" PRIu64 ").\n",
							 function,
							 index_string,
							 entry_index,
							 index_value_identifier,
							 index_value_file_offset,
							 index_value_file_offset + index_value_data_size,
							 index_value_data_size );
						}
#endif
						recoverable = 0;
					}
#if defined( HAVE_DEBUG_OUTPUT )
					else if( libcnotify_verbose != 0 )
					{
						libcnotify_printf(
						 "%s: %s index entry: %" PRIu8 " identifier: %" PRIu64 " refers to unallocated range: 0x%08" PRIx64 " - 0x%08" PRIx64 " (%" PRIu64 ").\n",
						 function,
						 index_string,
						 entry_index,
						 index_value_identifier,
						 index_value_file_offset,
						 index_value_file_offset + index_value_data_size,
						 index_value_data_size );
					}
#endif
				}
			}
			if( recoverable != 0 )
			{
				/* Move the recovered node to the index
				 */
#if defined( HAVE_DEBUG_OUTPUT )
				if( libcnotify_verbose != 0 )
				{
					libcnotify_printf(
					 "%s: %s index entry: %" PRIu8 " identifier: %" PRIu64 " is recoverable.\n",
					 function,
					 index_string,
					 entry_index,
					 index_value_identifier );
				}
#endif
				if( index_node->type == LIBPFF_INDEX_TYPE_DESCRIPTOR )
				{
					result = libpff_descriptors_index_insert_recovered_index_value(
					          descriptors_index,
					          index_value,
					          error );
				}
				else if( index_node->type == LIBPFF_INDEX_TYPE_OFFSET )
				{
					result = libpff_offsets_index_insert_recovered_index_value(
					          offsets_index,
					          index_value,
					          error );
				}
				if( result != 1 )
				{
					libcerror_error_set(
					 error,
					 LIBCERROR_ERROR_DOMAIN_RUNTIME,
					 LIBCERROR_RUNTIME_ERROR_APPEND_FAILED,
					 "%s: unable to insert recovered %s index value: %" PRIu64 " list.",
					 function,
					 index_string,
					 index_value_identifier );

					goto on_error;
				}
				index_value = NULL;
			}
			else
			{
				if( libpff_index_value_free(
				     &index_value,
				     error ) != 1 )
				{
					libcerror_error_set(
					 error,
					 LIBCERROR_ERROR_DOMAIN_RUNTIME,
					 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
					 "%s: unable to free index value.",
					 function );

					goto on_error;
				}
			}
			node_offset += index_node->entry_size;
		}
	}
	if( libpff_index_node_free(
	     &index_node,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
		 "%s: unable to free index node.",
		 function );

		goto on_error;
	}
#if defined( HAVE_DEBUG_OUTPUT )
	if( libcnotify_verbose != 0 )
	{
		libcnotify_printf(
		 "\n" );
	}
#endif
	return( 1 );

on_error:
	if( index_value != NULL )
	{
		libpff_index_value_free(
		 &index_value,
		 NULL );
	}
	if( index_node != NULL )
	{
		libpff_index_node_free(
		 &index_node,
		 NULL );
	}
	return( -1 );
}

/* Analyze if specific data block is recoverable
 * Returns 1 if recoverable, 0 if not or -1 on error
 */
int libpff_recover_analyze_data_block(
     libpff_io_handle_t *io_handle,
     libbfio_handle_t *file_io_handle,
     uint32_t descriptor_identifier,
     libpff_index_value_t *offsets_index_value,
     libcerror_error_t **error )
{
	libpff_data_block_t *data_block = NULL;
	static char *function           = "libpff_recover_analyze_data_block";
	int result                      = 0;

	if( offsets_index_value == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid offsets index value.",
		 function );

		return( -1 );
	}
	/* Check if the data block is readable
	 */
	if( libpff_data_block_initialize(
	     &data_block,
	     io_handle,
	     descriptor_identifier,
	     offsets_index_value->identifier,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
		 "%s: unable to create data block.",
		 function );

		goto on_error;
	}
#if defined( HAVE_DEBUG_OUTPUT )
	if( libcnotify_verbose != 0 )
	{
		libcnotify_printf(
		 "%s: attempting to read data block at offset: %" PRIi64 " (0x%08" PRIx64 ")\n",
		 function,
		 offsets_index_value->file_offset,
		 offsets_index_value->file_offset );
	}
#endif
	result = libpff_data_block_read_file_io_handle(
		  data_block,
		  file_io_handle,
		  offsets_index_value->file_offset,
		  offsets_index_value->data_size,
		  io_handle->file_type,
		  error );

	if( result != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_IO,
		 LIBCERROR_IO_ERROR_READ_FAILED,
		 "%s: unable to read data block.",
		 function );
#if defined( HAVE_DEBUG_OUTPUT )
		if( ( libcnotify_verbose != 0 )
		 && ( error != NULL )
		 && ( *error != NULL ) )
		{
			libcnotify_print_error_backtrace(
			 *error );
		}
#endif
		libcerror_error_free(
		 error );

/* TODO delete unreadable offset identifier in offsets_index->recovered_index_tree */
	}
	if( libpff_data_block_free(
	     &data_block,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
		 "%s: unable to free recovered data block.",
		 function );

		goto on_error;
	}
/* TODO validate the block data ? */

	return( 1 );

on_error:
	if( data_block != NULL )
	{
		libpff_data_block_free(
		 &data_block,
		 NULL );
	}
	return( -1 );
}

/* Analyze if specific local descriptors are recoverable
 * Returns 1 if recoverable, 0 if not or -1 on error
 */
int libpff_recover_analyze_local_descriptors(
     libpff_io_handle_t *io_handle,
     libbfio_handle_t *file_io_handle,
     libpff_offsets_index_t *offsets_index,
     uint64_t local_descriptors_identifier,
     libcerror_error_t **error )
{
	libpff_index_value_t *offsets_index_value                    = NULL;
	libpff_local_descriptors_node_t *local_descriptors_node      = NULL;
	uint8_t *node_entry_data                                     = NULL;
	static char *function                                        = "libpff_recover_analyze_local_descriptors";
	uint64_t local_descriptor_value_data_identifier              = 0;
	uint64_t local_descriptor_value_identifier                   = 0;
	uint64_t local_descriptor_value_local_descriptors_identifier = 0;
	uint64_t local_descriptor_value_sub_node_identifier          = 0;
	uint16_t entry_index                                         = 0;
	int result                                                   = 1;

	if( io_handle == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid IO handle.",
		 function );

		return( -1 );
	}
	if( libpff_offsets_index_get_index_value_by_identifier(
	     offsets_index,
	     io_handle,
	     file_io_handle,
	     local_descriptors_identifier,
	     1,
	     0,
	     &offsets_index_value,
	     error ) != 1 )
	{
#if defined( HAVE_DEBUG_OUTPUT )
		if( ( libcnotify_verbose != 0 )
		 && ( error != NULL )
		 && ( *error != NULL ) )
		{
			libcnotify_print_error_backtrace(
			 *error );
		}
#endif
		libcerror_error_free(
		 error );

		return( 0 );
	}
	if( offsets_index_value == NULL )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
		 "%s: missing offsets index value.",
		 function );

		return( -1 );
	}
#if defined( HAVE_DEBUG_OUTPUT )
	if( libcnotify_verbose != 0 )
	{
		libcnotify_printf(
		 "%s: local descriptors node identifier: %" PRIu64 " (%s) at offset: %" PRIi64 " of size: %" PRIu32 "\n",
		 function,
		 offsets_index_value->identifier,
		 ( ( offsets_index_value->identifier & LIBPFF_OFFSET_INDEX_IDENTIFIER_FLAG_INTERNAL ) ? "internal" : "external" ),
		 offsets_index_value->file_offset,
		 offsets_index_value->data_size );
	}
#endif
	if( libpff_local_descriptors_node_initialize(
	     &local_descriptors_node,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
		 "%s: unable to create local descriptors node.",
		 function );

		return( -1 );
	}
	if( libpff_local_descriptors_node_read_file_io_handle(
	     local_descriptors_node,
	     io_handle,
	     file_io_handle,
	     0 /* TODO descriptor identifier */,
	     offsets_index_value->identifier,
	     offsets_index_value->file_offset,
	     offsets_index_value->data_size,
	     error ) != 1 )
	{
#if defined( HAVE_DEBUG_OUTPUT )
		if( ( libcnotify_verbose != 0 )
		 && ( error != NULL )
		 && ( *error != NULL ) )
		{
			libcnotify_print_error_backtrace(
			 *error );
		}
#endif
		libcerror_error_free(
		 error );

		libpff_local_descriptors_node_free(
		 &local_descriptors_node,
		 NULL );

		return( 0 );
	}
	for( entry_index = 0;
	     entry_index < local_descriptors_node->number_of_entries;
	     entry_index++ )
	{
		if( libpff_local_descriptors_node_get_entry_data(
		     local_descriptors_node,
		     entry_index,
		     &node_entry_data,
		     error ) != 1 )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
			 "%s: unable to retrieve node entry: %" PRIu16 " data.",
			 function,
			 entry_index );

			return( -1 );
		}
		if( node_entry_data == NULL )
		{
			libcerror_error_set(
			 error,
			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
			 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
			 "%s: missing node entry: %" PRIu16 " data.",
			 function,
			 entry_index );

			return( -1 );
		}
		if( io_handle->file_type == LIBPFF_FILE_TYPE_32BIT )
		{
			byte_stream_copy_to_uint32_little_endian(
			 node_entry_data,
			 local_descriptor_value_identifier );

			node_entry_data += 4;
		}
		else if( ( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT )
		      || ( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT_4K_PAGE ) )
		{
			byte_stream_copy_to_uint64_little_endian(
			 node_entry_data,
			 local_descriptor_value_identifier );

			node_entry_data += 8;
		}
		/* Ignore the upper 32-bit of local descriptor identifiers
		 */
		local_descriptor_value_identifier &= 0xffffffffUL;

		/* Ignore local descriptor values without a data identifier
		 */
		if( local_descriptor_value_identifier == 0 )
		{
#if defined( HAVE_DEBUG_OUTPUT )
			if( libcnotify_verbose != 0 )
			{
				libcnotify_printf(
				 "%s: local descriptor entry: %" PRIu8 " identifier: %" PRIu64 " has an empty identifier.\n",
				 function,
				 entry_index,
				 local_descriptor_value_identifier );
			}
#endif
			result = 0;

			break;
		}
		if( local_descriptors_node->level == LIBPFF_LOCAL_DESCRIPTOR_NODE_LEVEL_LEAF )
		{
			if( io_handle->file_type == LIBPFF_FILE_TYPE_32BIT )
			{
				byte_stream_copy_to_uint32_little_endian(
				 node_entry_data,
				 local_descriptor_value_data_identifier );

				node_entry_data += 4;

				byte_stream_copy_to_uint32_little_endian(
				 node_entry_data,
				 local_descriptor_value_local_descriptors_identifier );

				node_entry_data += 4;
			}
			else if( ( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT )
			      || ( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT_4K_PAGE ) )
			{
				byte_stream_copy_to_uint64_little_endian(
				 node_entry_data,
				 local_descriptor_value_data_identifier );

				node_entry_data += 8;

				byte_stream_copy_to_uint64_little_endian(
				 node_entry_data,
				 local_descriptor_value_local_descriptors_identifier );

				node_entry_data += 8;
			}
			/* Ignore local descriptor values without a data identifier
			 */
			if( local_descriptor_value_data_identifier == 0 )
			{
#if defined( HAVE_DEBUG_OUTPUT )
				if( libcnotify_verbose != 0 )
				{
					libcnotify_printf(
					 "%s: local descriptor entry: %" PRIu8 " identifier: %" PRIu64 " has an empty data identifier.\n",
					 function,
					 entry_index,
					 local_descriptor_value_identifier );
				}
#endif
				result = 0;

				break;
			}
		}
		else
		{
			if( io_handle->file_type == LIBPFF_FILE_TYPE_32BIT )
			{
				byte_stream_copy_to_uint32_little_endian(
				 node_entry_data,
				 local_descriptor_value_sub_node_identifier );

				node_entry_data += 4;
			}
			else if( ( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT )
			      || ( io_handle->file_type == LIBPFF_FILE_TYPE_64BIT_4K_PAGE ) )
			{
				byte_stream_copy_to_uint64_little_endian(
				 node_entry_data,
				 local_descriptor_value_sub_node_identifier );

				node_entry_data += 8;
			}
			/* Ignore local descriptor values without a sub node identifier
			 */
			if( local_descriptor_value_sub_node_identifier == 0 )
			{
#if defined( HAVE_DEBUG_OUTPUT )
				if( libcnotify_verbose != 0 )
				{
					libcnotify_printf(
					 "%s: local descriptor entry: %" PRIu8 " identifier: %" PRIu64 " has an empty sub node identifier.\n",
					 function,
					 entry_index,
					 local_descriptor_value_identifier );
				}
#endif
				result = 0;

				break;
			}
			result = libpff_recover_analyze_local_descriptors(
				  io_handle,
				  file_io_handle,
				  offsets_index,
				  local_descriptor_value_sub_node_identifier,
				  error );

			if( result == -1 )
			{
				libcerror_error_set(
				 error,
				 LIBCERROR_ERROR_DOMAIN_IO,
				 LIBCERROR_IO_ERROR_READ_FAILED,
				 "%s: unable to recover local descriptors: %" PRIu64 ".",
				 function,
				 local_descriptor_value_sub_node_identifier );

				libpff_local_descriptors_node_free(
				 &local_descriptors_node,
				 NULL );

				return( -1 );
			}
			else if( result == 0 )
			{
				break;
			}
		}
	}
	if( libpff_local_descriptors_node_free(
	     &local_descriptors_node,
	     error ) != 1 )
	{
		libcerror_error_set(
		 error,
		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
		 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
		 "%s: unable to free local descriptors node.",
		 function );

		return( -1 );
	}
#if defined( HAVE_DEBUG_OUTPUT )
	if( libcnotify_verbose != 0 )
	{
		libcnotify_printf(
		 "\n" );
	}
#endif
	return( result );
}