/* Retrieves the SCSI identifier
 * Returns 1 if successful or -1 on error
 */
int libsmdev_scsi_get_identier(
     int file_descriptor,
     liberror_error_t **error )
{
#if defined( SG_GET_SCSI_ID )
	struct
	{
		int four_in_one;
		int host_unique_id;
	} identifier;
#endif

	static char *function = "libsmdev_scsi_get_identifier";

	if( file_descriptor == -1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid file descriptor.",
		 function );

		return( -1 );
	}
#if defined( SG_GET_SCSI_ID )
	if( ioctl(
	     file_descriptor,
	     SCSI_IOCTL_GET_IDLUN,
	     &identifier ) == -1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_IO,
		 LIBERROR_IO_ERROR_IOCTL_FAILED,
		 "%s: unable to query device for: SCSI_IOCTL_GET_IDLUN.",
		 function );

		return( -1 );
	}
#if defined( HAVE_DEBUG_OUTPUT )
	if( libnotify_verbose != 0 )
	{
		libnotify_printf(
		 "%s: identifier:\n",
		 function );
		libnotify_print_data(
		 (uint8_t *) &identifier,
		 sizeof( identifier ),
		 0 );
	}
#endif
#endif
	return( 1 );
}
/* Prints the data on the notify stream
 * Returns the number of printed characters if successful or -1 on error
 */
int libnotify_print_data(
     const uint8_t *data,
     size_t data_size )
{
	size_t byte_iterator  = 0;
	size_t data_iterator  = 0;
	int print_count       = 0;
	int total_print_count = 0;

	if( libnotify_stream == NULL )
	{
		return( -1 );
	}
	if( data == NULL )
	{
		return( -1 );
	}
	while( data_iterator < data_size )
	{
		while( byte_iterator < data_size )
		{
			if( byte_iterator % 16 == 0 )
			{
				print_count = libnotify_printf(
					       "%.8" PRIzx ": ",
					       byte_iterator );

				if( print_count <= -1 )
				{
					return( -1 );
				}
				total_print_count += print_count;
			}
			print_count = libnotify_printf(
				       "%.2" PRIx8 " ",
				       data[ byte_iterator++ ] );

			if( print_count <= -1 )
			{
				return( -1 );
			}
			total_print_count += print_count;

			if( byte_iterator % 16 == 0 )
			{
				break;
			}
			else if( byte_iterator % 8 == 0 )
			{
				print_count = libnotify_printf(
					       " " );

				if( print_count <= -1 )
				{
					return( -1 );
				}
				total_print_count += print_count;
			}
		}
		while( byte_iterator % 16 != 0 )
		{
			byte_iterator++;

			print_count = libnotify_printf(
				       "   " );

			if( print_count <= -1 )
			{
				return( -1 );
			}
			total_print_count += print_count;

			if( ( byte_iterator % 8 == 0 )
			 && ( byte_iterator % 16 != 0 ) )
			{
				print_count = libnotify_printf(
					       " " );

				if( print_count <= -1 )
				{
					return( -1 );
				}
				total_print_count += print_count;
			}
		}
		print_count = libnotify_printf(
			       "  " );

		if( print_count <= -1 )
		{
			return( -1 );
		}
		total_print_count += print_count;

		byte_iterator = data_iterator;

		while( byte_iterator < data_size )
		{
			if( ( data[ byte_iterator ] >= 0x20 )
			 && ( data[ byte_iterator ] <= 0x7e ) )
			{
				print_count = libnotify_printf(
					       "%c",
					       (char) data[ byte_iterator ] );
			}
			else
			{
				print_count = libnotify_printf(
					       "." );
			}
			if( print_count <= -1 )
			{
				return( -1 );
			}
			total_print_count += print_count;

			byte_iterator++;

			if( byte_iterator % 16 == 0 )
			{
				break;
			}
			else if( byte_iterator % 8 == 0 )
			{
				print_count = libnotify_printf(
					       " " );

				if( print_count <= -1 )
				{
					return( -1 );
				}
				total_print_count += print_count;
			}
		}
		print_count = libnotify_printf(
			       "\n" );

		if( print_count <= -1 )
		{
			return( -1 );
		}
		total_print_count += print_count;

		data_iterator = byte_iterator;
	}
	print_count = libnotify_printf(
		       "\n" );

	if( print_count <= -1 )
	{
		return( -1 );
	}
	total_print_count += print_count;

	return( total_print_count );
}
/* Copies the sector data to the buffer
 * Returns the number of bytes copied if successful or -1 on error
 */
ssize_t libodraw_io_handle_copy_sector_data_to_buffer(
         libodraw_io_handle_t *io_handle,
         const uint8_t *sector_data,
         size_t sector_data_size,
         uint32_t bytes_per_sector,
         uint8_t track_type,
         uint8_t *buffer,
         size_t buffer_size,
         uint32_t sector_index,
         uint32_t sector_offset,
         liberror_error_t **error )
{
	static char *function     = "libodraw_io_handle_copy_sector_data_to_buffer";
	size_t buffer_offset      = 0;
	size_t read_size          = 0;
	size_t sector_data_offset = 0;
	uint32_t sector_lba       = 0;

#if defined( HAVE_DEBUG_OUTPUT )
	uint8_t sector_mode       = 0;
#endif

	if( io_handle == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid IO handle.",
		 function );

		return( -1 );
	}
	if( sector_data == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid sector data.",
		 function );

		return( -1 );
	}
	if( sector_data_size > (size_t) SSIZE_MAX )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_VALUE_EXCEEDS_MAXIMUM,
		 "%s: invalid sector data size value exceeds maximum.",
		 function );

		return( -1 );
	}
	if( buffer == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid buffer.",
		 function );

		return( -1 );
	}
	if( buffer_size > (size_t) SSIZE_MAX )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_VALUE_EXCEEDS_MAXIMUM,
		 "%s: invalid buffer size value exceeds maximum.",
		 function );

		return( -1 );
	}
	if( ( (size_t) sector_offset >= sector_data_size )
	 || ( sector_offset >= io_handle->bytes_per_sector ) )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
		 "%s: invalid sector offset value out of bounds.",
		 function );

		return( -1 );
	}
	while( sector_data_offset < sector_data_size )
	{
		if( io_handle->bytes_per_sector == 2048 )
		{
			if( ( track_type == LIBODRAW_TRACK_TYPE_MODE1_2352 )
			 || ( track_type == LIBODRAW_TRACK_TYPE_MODE2_2352 ) )
			{
				if( ( sector_data_offset + 16 ) >= sector_data_size )
				{
					liberror_error_set(
					 error,
					 LIBERROR_ERROR_DOMAIN_RUNTIME,
					 LIBERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
					 "%s: sector data too small.",
					 function );

					return( -1 );
				}
#if defined( HAVE_DEBUG_OUTPUT )
				if( libnotify_verbose != 0 )
				{
					libnotify_printf(
				 	 "%s: sector: %" PRIu32 " header:\n",
					 function,
					 sector_index );
					libnotify_print_data(
					 &( sector_data[ sector_data_offset ] ),
					 16 );
				}
#endif
				if( memory_compare(
				     &( sector_data[ sector_data_offset ] ),
				     libodraw_sector_synchronisation_data,
				     12 ) != 0 )
				{
					liberror_error_set(
					 error,
					 LIBERROR_ERROR_DOMAIN_INPUT,
					 LIBERROR_INPUT_ERROR_VALUE_MISMATCH,
					 "%s: unsupported sector synchronisation data.",
					 function );

					return( -1 );
				}
#if defined( HAVE_DEBUG_OUTPUT )
				if( libnotify_verbose != 0 )
				{
					libnotify_printf(
				 	 "%s: sector: %" PRIu32 " synchronisation data:\n",
					 function,
					 sector_index );
					libnotify_print_data(
					 &( sector_data[ sector_data_offset ] ),
					 12 );
				}
#endif
				sector_data_offset += 12;

				libodraw_optical_disk_copy_msf_to_lba(
				 sector_data[ sector_data_offset ],
				 sector_data[ sector_data_offset + 1 ],
				 sector_data[ sector_data_offset + 2 ],
				 sector_lba );

#if defined( HAVE_DEBUG_OUTPUT )
				sector_mode = sector_data[ sector_data_offset + 3 ] & 0x03;

				if( libnotify_verbose != 0 )
				{
					libnotify_printf(
				 	 "%s: sector: %" PRIu32 " MSF\t\t: %02" PRIu8 ":%02" PRIu8 ":%02" PRIu8 " (%" PRIu32 ")\n",
					 function,
					 sector_index,
					 sector_data[ sector_data_offset ],
					 sector_data[ sector_data_offset + 1 ],
					 sector_data[ sector_data_offset + 2 ],
					 sector_lba );

					libnotify_printf(
				 	 "%s: sector: %" PRIu32 " mode bits\t: 0x%02" PRIx8 "\n",
					 function,
					 sector_index,
					 sector_data[ sector_data_offset + 3 ] );
				}
#endif
#if defined( HAVE_VERBOSE_OUTPUT )
				if( sector_lba != sector_index )
				{
					libnotify_printf(
					 "%s: sector MSF (LBA) does not match current.\n",
					 function );
				}
				if( ( ( track_type == LIBODRAW_TRACK_TYPE_MODE1_2352 )
				  &&  ( sector_mode != 1 ) )
				 || ( ( track_type == LIBODRAW_TRACK_TYPE_MODE2_2352 )
				  &&  ( sector_mode != 2 ) ) )
				{
					libnotify_printf(
					 "%s: sector mode does not match table of contents.\n",
					 function );
				}
#endif
				sector_data_offset += 4;
			}
			else if( bytes_per_sector == 2352 )
			{
				sector_data_offset += 16;
			}
			if( ( track_type == LIBODRAW_TRACK_TYPE_MODE2_2336 )
			 || ( track_type == LIBODRAW_TRACK_TYPE_MODE2_2352 ) )
			{
				if( ( sector_data[ sector_data_offset     ] != sector_data[ sector_data_offset + 4 ] )
				 || ( sector_data[ sector_data_offset + 1 ] != sector_data[ sector_data_offset + 5 ] )
				 || ( sector_data[ sector_data_offset + 2 ] != sector_data[ sector_data_offset + 6 ] )
				 || ( sector_data[ sector_data_offset + 3 ] != sector_data[ sector_data_offset + 7 ] ) )
				{
					liberror_error_set(
					 error,
					 LIBERROR_ERROR_DOMAIN_INPUT,
					 LIBERROR_INPUT_ERROR_VALUE_MISMATCH,
					 "%s: unsupported or corrupt XA sub-header.",
					 function );

					return( -1 );
				}
				if( sector_data[ sector_data_offset + 1 ] >= 32 )
				{
					liberror_error_set(
					 error,
					 LIBERROR_ERROR_DOMAIN_INPUT,
					 LIBERROR_INPUT_ERROR_VALUE_MISMATCH,
					 "%s: unsupported XA sub-header channel number.",
					 function );

					return( -1 );
				}
#if defined( HAVE_VERBOSE_OUTPUT )
				if( libnotify_verbose != 0 )
				{
					if( ( sector_data[ sector_data_offset + 2 ] & 0x08 ) != 0 )
					{
						libnotify_printf(
						 "%s: data flag not set in XA sub-header sub-mode flags.\n",
						 function );
					}
				}
#endif

/* TODO some writers seem to ignore these flags
				if( ( sector_data[ sector_data_offset + 2 ] & 0x08 ) != 0 )
				{
					liberror_error_set(
					 error,
					 LIBERROR_ERROR_DOMAIN_INPUT,
					 LIBERROR_INPUT_ERROR_VALUE_MISMATCH,
					 "%s: unsupported XA sub-header sub-mode flags - data flag not set.",
					 function );

					return( -1 );
				}
*/
				sector_data_offset += 8;
			}
		}
		else if( io_handle->bytes_per_sector == 2352 )
		{
			if( sector_offset == 0 )
			{
/* TODO what about run-out and lead-out data (have argument indicate non-track data) */
				if( ( io_handle->mode == 1 )
				 || ( io_handle->mode == 2 ) )
				{
					read_size = 12;

					if( ( read_size + buffer_offset ) > buffer_size )
					{
						read_size = buffer_size - buffer_offset;
					}
					if( memory_copy(
					     &( buffer[ buffer_offset ] ),
					     libodraw_sector_synchronisation_data,
					     read_size ) != 0 )
					{
						liberror_error_set(
						 error,
						 LIBERROR_ERROR_DOMAIN_RUNTIME,
						 LIBERROR_RUNTIME_ERROR_COPY_FAILED,
						 "%s: unsupported copy sector synchronisation data to buffer.",
						 function );

						return( -1 );
					}
					buffer_offset += read_size;

					if( buffer_offset >= buffer_size )
					{
						break;
					}
/* TODO set MSF requires current sector */
					buffer[ buffer_offset++ ] = 0;

					if( buffer_offset >= buffer_size )
					{
						break;
					}
					buffer[ buffer_offset++ ] = 0;

					if( buffer_offset >= buffer_size )
					{
						break;
					}
					buffer[ buffer_offset++ ] = 0;

					if( buffer_offset >= buffer_size )
					{
						break;
					}
					if( io_handle->mode == 1 )
					{
						buffer[ buffer_offset++ ] = 1;
					}
					else if ( io_handle->mode == 2 )
					{
						buffer[ buffer_offset++ ] = 2;
					}
					if( buffer_offset >= buffer_size )
					{
						break;
					}
				}
				else
				{
					read_size = 16;

					if( ( read_size + buffer_offset ) > buffer_size )
					{
						read_size = buffer_size - buffer_offset;
					}
					if( memory_set(
					     &( buffer[ buffer_offset ] ),
					     0,
					     read_size ) == NULL )
					{
						liberror_error_set(
						 error,
						 LIBERROR_ERROR_DOMAIN_MEMORY,
						 LIBERROR_MEMORY_ERROR_SET_FAILED,
						 "%s: unable to set sector data in buffer.",
						 function );

						return( -1 );
					}
					buffer_offset += read_size;

					if( buffer_offset >= buffer_size )
					{
						break;
					}
				}
				if( io_handle->mode == 2 )
				{
/* TODO what about XA sub-header */
				}
			}
		}
		read_size = io_handle->bytes_per_sector;

		if( sector_offset != 0 )
		{
			sector_data_offset += sector_offset;
			read_size          -= (size_t) sector_offset;
			sector_offset       = 0;
		}
		if( ( read_size + buffer_offset ) > buffer_size )
		{
			read_size = buffer_size - buffer_offset;
		}
		if( ( track_type == LIBODRAW_TRACK_TYPE_AUDIO )
		 && ( io_handle->bytes_per_sector != 2352 ) )
		{
			/* If the sector size is not 2352 just return 0 bytes
			 * for audio data
			 */
			if( memory_set(
			     &( buffer[ buffer_offset ] ),
			     0,
			     read_size ) == NULL )
			{
				liberror_error_set(
				 error,
				 LIBERROR_ERROR_DOMAIN_MEMORY,
				 LIBERROR_MEMORY_ERROR_COPY_FAILED,
				 "%s: unable to set sector data to buffer.",
				 function );

				return( -1 );
			}
		}
		else
		{
			if( memory_copy(
			     &( buffer[ buffer_offset ] ),
			     &( sector_data[ sector_data_offset ] ),
			     read_size ) == NULL )
			{
				liberror_error_set(
				 error,
				 LIBERROR_ERROR_DOMAIN_MEMORY,
				 LIBERROR_MEMORY_ERROR_COPY_FAILED,
				 "%s: unable to copy sector data to buffer.",
				 function );

				return( -1 );
			}
		}
		buffer_offset      += read_size;
		sector_data_offset += read_size;

		if( buffer_offset >= buffer_size )
		{
			break;
		}
		if( io_handle->bytes_per_sector == 2048 )
		{
			if( track_type == LIBODRAW_TRACK_TYPE_MODE1_2352 )
			{
/* TODO calculate checksum, what about read errors ?*/
				sector_data_offset += 4;

/* TODO check padding */
				sector_data_offset += 8;

/* TODO check ECC data if necessary, what about read errors ? */
				sector_data_offset += 276;
			}
			else if( ( track_type == LIBODRAW_TRACK_TYPE_MODE2_2336 )
			      || ( track_type == LIBODRAW_TRACK_TYPE_MODE2_2352 ) )
			{
/* TODO calculate checksum, what about read errors ?*/
				sector_data_offset += 4;

/* TODO check ECC data if necessary, what about read errors ? */
				sector_data_offset += 276;
			}
			else if( bytes_per_sector == 2352 )
			{
				sector_data_offset += 288;
			}
		}
		else if( io_handle->bytes_per_sector == 2352 )
		{
/* TODO what about run-out and lead-out data (have argument indicate non-track data) */
			if( ( io_handle->mode == 1 )
			 || ( io_handle->mode == 2 ) )
			{
				read_size = 4;

				if( ( read_size + buffer_offset ) > buffer_size )
				{
					read_size = buffer_size - buffer_offset;
				}
/* TODO determine missing data instead of 0 fill */
				if( memory_set(
				     &( buffer[ buffer_offset ] ),
				     0,
				     read_size ) == NULL )
				{
					liberror_error_set(
					 error,
					 LIBERROR_ERROR_DOMAIN_MEMORY,
					 LIBERROR_MEMORY_ERROR_SET_FAILED,
					 "%s: unable to set sector checksum in buffer.",
					 function );

					return( -1 );
				}
				buffer_offset += read_size;

				if( buffer_offset >= buffer_size )
				{
					break;
				}
			}
			if( io_handle->mode == 1 )
			{
				read_size = 8;

				if( ( read_size + buffer_offset ) > buffer_size )
				{
					read_size = buffer_size - buffer_offset;
				}
				if( memory_set(
				     &( buffer[ buffer_offset ] ),
				     0,
				     read_size ) == NULL )
				{
					liberror_error_set(
					 error,
					 LIBERROR_ERROR_DOMAIN_MEMORY,
					 LIBERROR_MEMORY_ERROR_SET_FAILED,
					 "%s: unable to set sector reserved in buffer.",
					 function );

					return( -1 );
				}
				buffer_offset += read_size;

				if( buffer_offset >= buffer_size )
				{
					break;
				}
			}
			if( ( io_handle->mode == 1 )
			 || ( io_handle->mode == 2 ) )
			{
				read_size = 276;

				if( ( read_size + buffer_offset ) > buffer_size )
				{
					read_size = buffer_size - buffer_offset;
				}
/* TODO determine missing data instead of 0 fill */
				if( memory_set(
				     &( buffer[ buffer_offset ] ),
				     0,
				     read_size ) == NULL )
				{
					liberror_error_set(
					 error,
					 LIBERROR_ERROR_DOMAIN_MEMORY,
					 LIBERROR_MEMORY_ERROR_SET_FAILED,
					 "%s: unable to set sector error correction data in buffer.",
					 function );

					return( -1 );
				}
				buffer_offset += read_size;

				if( buffer_offset >= buffer_size )
				{
					break;
				}
			}
			else
			{
				read_size = 288;

				if( ( read_size + buffer_offset ) > buffer_size )
				{
					read_size = buffer_size - buffer_offset;
				}
				if( memory_set(
				     &( buffer[ buffer_offset ] ),
				     0,
				     read_size ) == NULL )
				{
					liberror_error_set(
					 error,
					 LIBERROR_ERROR_DOMAIN_MEMORY,
					 LIBERROR_MEMORY_ERROR_SET_FAILED,
					 "%s: unable to set sector data in buffer.",
					 function );

					return( -1 );
				}
				buffer_offset += read_size;

				if( buffer_offset >= buffer_size )
				{
					break;
				}
			}
		}
		sector_index++;
	}
	return( (ssize_t) buffer_offset );
}
/* Determines and retrieves the PCI bus address
 * Returns 1 if successful or -1 on error
 */
int libsmdev_scsi_get_pci_bus_address(
     int file_descriptor,
     uint8_t *pci_bus_address,
     size_t pci_bus_address_size,
     liberror_error_t **error )
{
	static char *function = "libsmdev_scsi_get_bus_type";

	if( file_descriptor == -1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid file descriptor.",
		 function );

		return( -1 );
	}
	if( pci_bus_address == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid PCI bus address.",
		 function );

		return( -1 );
	}
	if( pci_bus_address_size > (size_t) SSIZE_MAX )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
		 "%s: invalid PCI bus address size value exceeds maximum.",
		 function );

		return( -1 );
	}
	if( pci_bus_address_size <= 8 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
		 "%s: invalid PCI bus address size value too small.",
		 function );

		return( -1 );
	}
	if( memory_set(
	     pci_bus_address,
	     0,
	     pci_bus_address_size ) == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_MEMORY,
		 LIBERROR_MEMORY_ERROR_SET_FAILED,
		 "%s: unable to clear PCI bus address.",
		 function );

		return( -1 );
	}
#if defined( SCSI_IOCTL_GET_PCI )
	if( ioctl(
	     file_descriptor,
	     SCSI_IOCTL_GET_PCI,
	     pci_bus_address ) == -1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_IO,
		 LIBERROR_IO_ERROR_IOCTL_FAILED,
		 "%s: unable to query device for: SCSI_IOCTL_GET_PCI.",
		 function );

		return( -1 );
	}
	pci_bus_address[ pci_bus_address_size - 1 ] = 0;

#if defined( HAVE_DEBUG_OUTPUT )
	if( libnotify_verbose != 0 )
	{
		libnotify_printf(
		 "%s: SCSI_IOCTL_GET_PCI: %s\n",
		 function,
		 pci_bus_address );

		libnotify_printf(
		 "\n" );
	}
#endif
#endif
	return( 1 );
}
/* Determines and retrieves the bus type
 * Returns 1 if successful or -1 on error
 */
int libsmdev_scsi_get_bus_type(
     int file_descriptor,
     uint8_t *bus_type,
     liberror_error_t **error )
{
#if defined( SCSI_IOCTL_PROBE_HOST )
	union 
	{
		int length;
		char buffer[ 128 ];
	} sg_probe_host;

	size_t sg_probe_host_length = 0;
#endif

	static char *function       = "libsmdev_scsi_get_bus_type";

	if( file_descriptor == -1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid file descriptor.",
		 function );

		return( -1 );
	}
	if( bus_type == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid bus type.",
		 function );

		return( -1 );
	}
	*bus_type = LIBSMDEV_BUS_TYPE_UNKNOWN;

#if defined( SCSI_IOCTL_PROBE_HOST )
	sg_probe_host.length = 127;

	if( ioctl(
	     file_descriptor,
	     SCSI_IOCTL_PROBE_HOST,
	     &sg_probe_host ) == -1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_IO,
		 LIBERROR_IO_ERROR_IOCTL_FAILED,
		 "%s: unable to query device for: SCSI_IOCTL_PROBE_HOST.",
		 function );

		return( -1 );
	}
	sg_probe_host.buffer[ 127 ] = 0;

	sg_probe_host_length = libcstring_narrow_string_length(
	                        sg_probe_host.buffer );

#if defined( HAVE_DEBUG_OUTPUT )
	if( libnotify_verbose != 0 )
	{
		libnotify_printf(
		 "%s: SCSI_IOCTL_PROBE_HOST (length: %d): %s\n",
		 function,
		 sg_probe_host_length,
		 sg_probe_host.buffer );

		libnotify_printf(
		 "\n" );
	}
#endif
	if( sg_probe_host_length >= 4 )
	{
		if( libcstring_narrow_string_compare(
		     sg_probe_host.buffer,
		     "ahci",
		     4 ) == 0 )
		{
			*bus_type = LIBSMDEV_BUS_TYPE_ATA;
		}
		else if( libcstring_narrow_string_compare(
		          sg_probe_host.buffer,
		          "pata",
		          4 ) == 0 )
		{
			*bus_type = LIBSMDEV_BUS_TYPE_ATA;
		}
		else if( libcstring_narrow_string_compare(
		          sg_probe_host.buffer,
		          "sata",
		          4 ) == 0 )
		{
			*bus_type = LIBSMDEV_BUS_TYPE_ATA;
		}
	}
	/* Serial Bus Protocol (SBP-2)
	 */
	else if( ( sg_probe_host_length == 15 )
	      && ( libcstring_narrow_string_compare(
	            sg_probe_host.buffer,
	            "SBP-2 IEEE-1394",
	            15 ) == 0 ) )
	{
		*bus_type = LIBSMDEV_BUS_TYPE_FIREWIRE;
	}
	else if( ( sg_probe_host_length == 43 )
	      && ( libcstring_narrow_string_compare(
	            sg_probe_host.buffer,
	            "SCSI emulation for USB Mass Storage devices",
	            43 ) == 0 ) )
	{
		*bus_type = LIBSMDEV_BUS_TYPE_USB;
	}
#endif
	return( 1 );
}
/* Sends a SCSI read track information to the file descriptor
 * Returns the number of bytes read if successful or -1 on error
 */
ssize_t libsmdev_scsi_read_track_information(
         int file_descriptor,
         uint32_t offset,
         uint8_t *response,
         size_t response_size,
         liberror_error_t **error )
{
	libsmdev_scsi_read_track_information_cdb_t command;

	uint8_t sense[ LIBSMDEV_SCSI_SENSE_SIZE ];

	static char *function  = "libsmdev_scsi_read_track_information";
	ssize_t response_count = 0;

	if( file_descriptor == -1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid file descriptor.",
		 function );

		return( -1 );
	}
	if( response == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid response.",
		 function );

		return( -1 );
	}
	if( response_size > (size_t) SSIZE_MAX )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
		 "%s: invalid response size value exceeds maximum.",
		 function );

		return( -1 );
	}
	if( memory_set(
	     &command,
	     0,
	     sizeof( libsmdev_scsi_read_track_information_cdb_t ) ) == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_MEMORY,
		 LIBERROR_MEMORY_ERROR_SET_FAILED,
		 "%s: unable to clear command.",
		 function );

		return( -1 );
	}
	command.operation_code = LIBSMDEV_SCSI_OPERATION_CODE_READ_TRACK_INFORMATION;
	command.address_type   = LIBSMDEV_SCSI_TRACK_INFORMATION_ADDRESS_TYPE_LBA;

	byte_stream_copy_from_uint32_big_endian(
	 command.offset,
	 offset );

	byte_stream_copy_from_uint16_big_endian(
	 command.receive_size,
	 response_size );

	if( libsmdev_scsi_command(
	     file_descriptor,
	     (uint8_t *) &command,
	     sizeof( libsmdev_scsi_read_track_information_cdb_t ),
	     response,
	     response_size,
	     sense,
	     LIBSMDEV_SCSI_SENSE_SIZE,
	     error ) != 1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_IO,
		 LIBERROR_IO_ERROR_GENERIC,
		 "%s: SCSI READ TRACK INFORMATION command failed.",
		 function );

		return( -1 );
	}
	byte_stream_copy_to_uint16_big_endian(
	 response,
	 response_count );

	if( response_count > (ssize_t) response_size )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
		 "%s: response too small.",
		 function );

		return( -1 );
	}
#if defined( HAVE_DEBUG_OUTPUT )
	if( libnotify_verbose != 0 )
	{
		libnotify_printf(
		 "%s: response:\n",
		 function );
		libnotify_print_data(
		 response,
		 response_count,
		 0 );
	}
#endif
	return( response_count );
}
/* Sends a SCSI inquiry to the file descriptor
 * Returns the number of bytes read if successful or -1 on error
 */
ssize_t libsmdev_scsi_inquiry(
         int file_descriptor,
         uint8_t inquiry_vital_product_data,
         uint8_t code_page,
         uint8_t *response,
         size_t response_size,
         liberror_error_t **error )
{
	libsmdev_scsi_inquiry_cdb_t command;

	uint8_t sense[ LIBSMDEV_SCSI_SENSE_SIZE ];

	static char *function  = "libsmdev_scsi_inquiry";
	ssize_t response_count = 0;

	if( file_descriptor == -1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid file descriptor.",
		 function );

		return( -1 );
	}
	if( response == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid response.",
		 function );

		return( -1 );
	}
	if( response_size > (size_t) SSIZE_MAX )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
		 "%s: invalid response size value exceeds maximum.",
		 function );

		return( -1 );
	}
	if( memory_set(
	     &command,
	     0,
	     sizeof( libsmdev_scsi_inquiry_cdb_t ) ) == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_MEMORY,
		 LIBERROR_MEMORY_ERROR_SET_FAILED,
		 "%s: unable to clear command.",
		 function );

		return( -1 );
	}
	command.operation_code = LIBSMDEV_SCSI_OPERATION_CODE_INQUIRY;

	if( inquiry_vital_product_data != 0 )
	{
		command.lun      |= 0x01;
		command.reserved1 = code_page;
	}
	if( libsmdev_scsi_command(
	     file_descriptor,
	     (uint8_t *) &command,
	     sizeof( libsmdev_scsi_inquiry_cdb_t ),
	     response,
	     response_size,
	     sense,
	     LIBSMDEV_SCSI_SENSE_SIZE,
	     error ) != 1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_IO,
		 LIBERROR_IO_ERROR_GENERIC,
		 "%s: SCSI INQUIRY command failed.",
		 function );

		return( -1 );
	}
	/* In standard inquiry mode the additional size is in the 5th byte
	 * in vital produce data inquiry mode it is in the 4th byte
	 */
	if( inquiry_vital_product_data == 0 )
	{
		response_count = (ssize_t) ( response[ 4 ] + 5 );
	}
	else
	{
		response_count = (ssize_t) ( response[ 3 ] + 4 );
	}
	if( response_count > (ssize_t) response_size )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
		 "%s: response too small.",
		 function );

		return( -1 );
	}
#if defined( HAVE_DEBUG_OUTPUT )
	if( libnotify_verbose != 0 )
	{
		libnotify_printf(
		 "%s: response:\n",
		 function );
		libnotify_print_data(
		 response,
		 response_count,
		 0 );
	}
#endif
	return( response_count );
}
/* Unpacks the chunk data
 * This function either validates the checksum or decompresses the chunk data
 * Returns 1 if successful or -1 on error
 */
int libewf_chunk_data_unpack(
     libewf_chunk_data_t *chunk_data,
     size_t chunk_size,
     liberror_error_t **error )
{
	static char *function        = "libewf_chunk_data_unpack";
	uint32_t calculated_checksum = 0;
	uint32_t stored_checksum     = 0;
	int result                   = 0;

	if( chunk_data == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid chunk data.",
		 function );

		return( -1 );
	}
	if( chunk_data->data == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_VALUE_MISSING,
		 "%s: invalid chunk data - missing data.",
		 function );

		return( -1 );
	}
	if( ( chunk_size == 0 )
	 || ( chunk_size > (size_t) SSIZE_MAX ) )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
		 "%s: chunk size value out of bounds.",
		 function );

		return( -1 );
	}
	if( chunk_data->is_packed == 0 )
	{
		return( 1 );
	}
	if( chunk_data->is_compressed == 0 )
	{
		if( ( chunk_data->data_size < sizeof( uint32_t ) )
		 || ( chunk_data->data_size > (size_t) SSIZE_MAX ) )
		{
			liberror_error_set(
			 error,
			 LIBERROR_ERROR_DOMAIN_RUNTIME,
			 LIBERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
			 "%s: chunk data size value out of bounds.",
			 function );

			return( -1 );
		}
		chunk_data->data_size -= sizeof( uint32_t );

		byte_stream_copy_to_uint32_little_endian(
		 &( ( chunk_data->data )[ chunk_data->data_size ] ),
		 stored_checksum );

		calculated_checksum = ewf_checksum_calculate(
				       chunk_data->data,
				       chunk_data->data_size,
				       1 );

		if( stored_checksum != calculated_checksum )
		{
#if defined( HAVE_VERBOSE_OUTPUT )
			if( libnotify_verbose != 0 )
			{
				libnotify_printf(
				 "%s: chunk data checksum does not match (stored: 0x%08" PRIx32 " calculated: 0x%08" PRIx32 ").\n",
				 function,
				 stored_checksum,
				 calculated_checksum );
			}
#endif
			chunk_data->is_corrupt = 1;
		}
	}
	else
	{
		if( chunk_data->compressed_data != NULL )
		{
			liberror_error_set(
			 error,
			 LIBERROR_ERROR_DOMAIN_RUNTIME,
			 LIBERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
			 "%s: invalid chunk data - compressed data value already set.",
			 function );

			return( -1 );
		}
		chunk_data->compressed_data      = chunk_data->data;
		chunk_data->compressed_data_size = chunk_data->data_size;

		/* Reserve 4 bytes for the checksum
		 */
		chunk_data->allocated_data_size = chunk_size + sizeof( uint32_t );

		chunk_data->data = (uint8_t *) memory_allocate(
		                                sizeof( uint8_t ) * chunk_data->allocated_data_size );

		if( chunk_data->data == NULL )
		{
			liberror_error_set(
			 error,
			 LIBERROR_ERROR_DOMAIN_MEMORY,
			 LIBERROR_MEMORY_ERROR_INSUFFICIENT,
			 "%s: unable to create data.",
			 function );

			return( -1 );
		}
		chunk_data->data_size = chunk_size;

		result = libewf_decompress(
			  chunk_data->data,
			  &( chunk_data->data_size ),
			  chunk_data->compressed_data,
			  chunk_data->compressed_data_size,
			  error );

		if( result == -1 )
		{
			liberror_error_set(
			 error,
			 LIBERROR_ERROR_DOMAIN_COMPRESSION,
			 LIBERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED,
			 "%s: unable to decompress chunk data.",
			 function );

			return( -1 );
		}
		else if( result == 0 )
		{
#if defined( HAVE_VERBOSE_OUTPUT )
			if( libnotify_verbose != 0 )
			{
				libnotify_printf(
				 "%s: unable to decompress chunk data.\n",
				 function );
			}
#endif
			chunk_data->is_corrupt = 1;
		}
	}
	chunk_data->is_packed = 0;

	return( 1 );
}
/* Determines the EWF file format based on known characteristics
 * Returns 1 if the format was determined, 0 if not or -1 on error
 */
int libewf_header_sections_determine_format(
     libewf_header_sections_t *header_sections,
     uint8_t ewf_format,
     uint8_t *format,
     liberror_error_t **error )
{
	static char *function = "libewf_header_sections_determine_format";
	int result            = 0;

	if( header_sections == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid header sections.",
		 function );

		return( -1 );
	}
	if( format == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid format.",
		 function );

		return( -1 );
	}
	if( ewf_format == EWF_FORMAT_S01 )
	{
		/* The format identifier for the EWF-S01 format was already set
		 * while reading the volume section
		 */
	}
	else if( ewf_format == EWF_FORMAT_E01 )
	{
		if( header_sections->xheader != NULL )
		{
			*format = LIBEWF_FORMAT_EWFX;
			result  = 1;
		}
		/* The header2 in raw format starts with 0xff 0xfe <number>
		 */
		else if( header_sections->header2 != NULL )
		{
			if( header_sections->header2[ 2 ] == (uint8_t) '3' )
			{
				/* The EnCase5 header2 contains av on the 6th position (36 ... 38 ...)
				 * the header2 is an UTF16 string
				 */
				if( ( header_sections->header2[ 36 ] == (uint8_t) 'a' )
				 && ( header_sections->header2[ 38 ] == (uint8_t) 'v' ) )
				{
					*format = LIBEWF_FORMAT_ENCASE5;
					result  = 1;
				}
				else if( ( header_sections->header2[ 36 ] == (uint8_t) 'm' )
				 && ( header_sections->header2[ 38 ] == (uint8_t) 'd' ) )
				{
					*format = LIBEWF_FORMAT_ENCASE6;
					result  = 1;
				}
#if defined( HAVE_VERBOSE_OUTPUT )
				else if( libnotify_verbose != 0 )
				{
					libnotify_printf(
					 "%s: unsupported header2 format: %c%c.\n",
					 function,
					 (char) header_sections->header2[ 36 ],
					 (char) header_sections->header2[ 38 ] );
				}
#endif
			}
			else if( header_sections->header2[ 2 ] == (uint8_t) '1' )
			{
				*format = LIBEWF_FORMAT_ENCASE4;
				result  = 1;
			}
#if defined( HAVE_VERBOSE_OUTPUT )
			else if( libnotify_verbose != 0 )
			{
				libnotify_printf(
				 "%s: unsupported header2 version: %c.\n",
				 function,
				 (char) header_sections->header2[ 2 ] );
			}
#endif
		}
		else if( header_sections->header != NULL )
		{
			if( header_sections->header[ 0 ] == (uint8_t) '3' )
			{
				/* The linen5 header2 contains av on the 6th position (17 18)
				 * the header2 is an UTF16 string
				 */
				if( ( header_sections->header[ 17 ] == (uint8_t) 'a' )
				 && ( header_sections->header[ 18 ] == (uint8_t) 'v' ) )
				{
					*format = LIBEWF_FORMAT_LINEN5;
					result  = 1;
				}
				else if( ( header_sections->header[ 17 ] == (uint8_t) 'm' )
				 && ( header_sections->header[ 18 ] == (uint8_t) 'd' ) )
				{
					*format = LIBEWF_FORMAT_LINEN6;
					result  = 1;
				}
#if defined( HAVE_VERBOSE_OUTPUT )
				else if( libnotify_verbose != 0 )
				{
					libnotify_printf(
					 "%s: unsupported header format: %c%c.\n",
					 function,
					 (char) header_sections->header[ 17 ],
					 (char) header_sections->header[ 18 ] );
				}
#endif
			}
			else if( header_sections->header[ 0 ] == (uint8_t) '1' )
			{
				/* EnCase uses \r\n
				 */
				if( header_sections->header[ 1 ] == (uint8_t) '\r' )
				{
					if( header_sections->header[ 25 ] == (uint8_t) 'r' )
					{
						*format = LIBEWF_FORMAT_ENCASE1;
						result  = 1;

#if defined( HAVE_VERBOSE_OUTPUT )
						if( header_sections->number_of_header_sections != 1 )
						{
							if( libnotify_verbose != 0 )
							{
								libnotify_printf(
								 "%s: multiple header sections found.\n",
								 function );
							}
						}
#endif
					}
					else if( header_sections->header[ 31 ] == (uint8_t) 'r' )
					{
						*format = LIBEWF_FORMAT_ENCASE2;
						result  = 1;
					}
#if defined( HAVE_VERBOSE_OUTPUT )
					else if( libnotify_verbose != 0 )
					{
						libnotify_printf(
						 "%s: unsupported header version.\n",
						 function );
					}
#endif
				}
				/* FTK Imager uses \n
				 */
				else if( header_sections->header[ 1 ] == (uint8_t) '\n' )
				{
					if( header_sections->header[ 29 ] == (uint8_t) 'r' )
					{
						*format = LIBEWF_FORMAT_FTK;
						result  = 1;
					}
#if defined( HAVE_VERBOSE_OUTPUT )
					else if( libnotify_verbose != 0 )
					{
						libnotify_printf(
						 "%s: unsupported header version.\n",
						 function );
					}
#endif
				}
#if defined( HAVE_VERBOSE_OUTPUT )
				else if( libnotify_verbose != 0 )
				{
					libnotify_printf(
					 "%s: unsupported header version.\n",
					 function );
				}
#endif
			}
#if defined( HAVE_VERBOSE_OUTPUT )
			else if( libnotify_verbose != 0 )
			{
				libnotify_printf(
				 "%s: unsupported header version.\n",
				 function );
			}
#endif
		}
#if defined( HAVE_VERBOSE_OUTPUT )
		else if( libnotify_verbose != 0 )
		{
			libnotify_printf(
			 "%s: missing header information.\n",
			 function );
		}
#endif
	}
	else if( ewf_format == EWF_FORMAT_L01 )
	{
		*format = LIBEWF_FORMAT_LVF;
		result  = 1;
	}
#if defined( HAVE_VERBOSE_OUTPUT )
	else if( libnotify_verbose != 0 )
	{
		libnotify_printf(
		 "%s: unsupported EWF file format.\n",
		 function );
	}
#endif
	return( result );
}
/* Retrieves the node value of the node
 * Returns 1 if successful or -1 on error
 */
int libfdata_tree_get_node_value(
     libfdata_tree_t *tree,
     libbfio_handle_t *file_io_handle,
     libfcache_cache_t *cache,
     libfdata_tree_node_t *node,
     intptr_t **node_value,
     uint8_t read_flags,
     liberror_error_t **error )
{
	libfcache_cache_value_t *cache_value     = NULL;
	libfdata_internal_tree_t *internal_tree = NULL;
	static char *function                   = "libfdata_tree_get_node_value";
	off64_t cache_value_offset              = (off64_t) -1;
	off64_t node_data_offset                = 0;
	size64_t node_data_size                 = 0;
	time_t cache_value_timestamp            = 0;
	time_t node_timestamp                   = 0;
	uint32_t node_data_flags                = 0;
	int cache_entry_index                   = -1;
	int number_of_cache_entries             = 0;
	int result                              = 0;

	if( tree == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid tree.",
		 function );

		return( -1 );
	}
	internal_tree = (libfdata_internal_tree_t *) tree;

	if( internal_tree->read_node_data == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_VALUE_MISSING,
		 "%s: invalid tree - missing read node data function.",
		 function );

		return( -1 );
	}
	if( libfdata_tree_node_get_data_range(
	     node,
	     &node_data_offset,
	     &node_data_size,
	     &node_data_flags,
	     error ) != 1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve data range from tree node.",
		 function );

		return( -1 );
	}
	if( libfcache_cache_get_number_of_entries(
	     cache,
	     &number_of_cache_entries,
	     error ) != 1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve number of cache entries.",
		 function );

		return( -1 );
	}
	if( number_of_cache_entries <= 0 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
		 "%s: invalid number of cache entries value out of bounds.",
		 function );

		return( -1 );
	}
	if( ( read_flags & LIBFDATA_READ_FLAG_IGNORE_CACHE ) == 0 )
	{
		if( ( ( ( (libfdata_internal_tree_node_t *) node )->flags & LIBFDATA_TREE_NODE_FLAG_IS_VIRTUAL ) == 0 )
		 && ( ( ( (libfdata_internal_tree_node_t *) node )->flags & LIBFDATA_TREE_NODE_FLAG_IS_LEAF ) != 0 ) )
		{
			cache_entry_index = libfdata_tree_node_calculate_leaf_node_cache_entry_index(
			                     node_data_offset,
			                     number_of_cache_entries );
		}
		else
		{
			cache_entry_index = libfdata_tree_node_calculate_branch_node_cache_entry_index(
			                     node_data_offset,
			                     number_of_cache_entries );
		}
		if( libfcache_cache_get_value_by_index(
		     cache,
		     cache_entry_index,
		     &cache_value,
		     error ) != 1 )
		{
			liberror_error_set(
			 error,
			 LIBERROR_ERROR_DOMAIN_RUNTIME,
			 LIBERROR_RUNTIME_ERROR_GET_FAILED,
			 "%s: unable to retrieve cache entry: %d from cache.",
			 function,
			 cache_entry_index );

			return( -1 );
		}
		if( cache_value != NULL )
		{
			if( libfdata_tree_node_get_timestamp(
			     node,
			     &node_timestamp,
			     error ) != 1 )
			{
				liberror_error_set(
				 error,
				 LIBERROR_ERROR_DOMAIN_RUNTIME,
				 LIBERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to retrieve time stamp from tree node.",
				 function );

				return( -1 );
			}
			if( libfcache_cache_value_get_identifier(
			     cache_value,
			     &cache_value_offset,
			     &cache_value_timestamp,
			     error ) != 1 )
			{
				liberror_error_set(
				 error,
				 LIBERROR_ERROR_DOMAIN_RUNTIME,
				 LIBERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to retrieve cache value identifier.",
				 function );

				return( -1 );
			}
		}
		if( ( node_data_offset == cache_value_offset )
		 && ( node_timestamp == cache_value_timestamp ) )
		{
			result = 1;
		}
#if defined( HAVE_DEBUG_OUTPUT )
		if( libnotify_verbose != 0 )
		{
			if( result == 0 )
			{
				libnotify_printf(
				 "%s: cache: 0x%08" PRIjx " miss (entry: %d, want: %" PRIi64 ", got: %" PRIi64 ")\n",
				 function,
				 (intptr_t) cache,
				 cache_entry_index,
				 node_data_offset,
				 cache_value_offset );
			}
			else
			{
				libnotify_printf(
				 "%s: cache: 0x%08" PRIjx " hit (entry: %d)\n",
				 function,
				 (intptr_t) cache,
				 cache_entry_index );
			}
		}
#endif
	}
	if( result == 0 )
	{
#if defined( HAVE_DEBUG_OUTPUT )
		if( libnotify_verbose != 0 )
		{
			libnotify_printf(
			 "%s: reading node data at offset: %" PRIi64 " (0x%08" PRIx64 ") of size: %" PRIu64 "\n",
			 function,
			 node_data_offset,
			 node_data_offset,
			 node_data_size );
		}
#endif
		/* Read the node data from the file IO handle
		 */
		if( internal_tree->read_node_data(
		     internal_tree->io_handle,
		     file_io_handle,
		     node,
		     cache,
		     node_data_offset,
		     node_data_size,
		     read_flags,
		     error ) != 1 )
		{
			liberror_error_set(
			 error,
			 LIBERROR_ERROR_DOMAIN_IO,
			 LIBERROR_IO_ERROR_READ_FAILED,
			 "%s: unable to read node data at offset: %" PRIi64 ".",
			 function,
			 node_data_offset );

			return( -1 );
		}
		if( ( ( ( (libfdata_internal_tree_node_t *) node )->flags & LIBFDATA_TREE_NODE_FLAG_IS_VIRTUAL ) == 0 )
		 && ( ( ( (libfdata_internal_tree_node_t *) node )->flags & LIBFDATA_TREE_NODE_FLAG_IS_LEAF ) != 0 ) )
		{
			cache_entry_index = libfdata_tree_node_calculate_leaf_node_cache_entry_index(
			                     node_data_offset,
			                     number_of_cache_entries );
		}
		else
		{
			cache_entry_index = libfdata_tree_node_calculate_branch_node_cache_entry_index(
			                     node_data_offset,
			                     number_of_cache_entries );
		}
		if( libfcache_cache_get_value_by_index(
		     cache,
		     cache_entry_index,
		     &cache_value,
		     error ) != 1 )
		{
			liberror_error_set(
			 error,
			 LIBERROR_ERROR_DOMAIN_RUNTIME,
			 LIBERROR_RUNTIME_ERROR_GET_FAILED,
			 "%s: unable to retrieve cache entry: %d from cache.",
			 function,
			 cache_entry_index );

			return( -1 );
		}
		if( cache_value != NULL )
		{
			if( libfdata_tree_node_get_timestamp(
			     node,
			     &node_timestamp,
			     error ) != 1 )
			{
				liberror_error_set(
				 error,
				 LIBERROR_ERROR_DOMAIN_RUNTIME,
				 LIBERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to retrieve time stamp from tree node.",
				 function );

				return( -1 );
			}
			if( libfcache_cache_value_get_identifier(
			     cache_value,
			     &cache_value_offset,
			     &cache_value_timestamp,
			     error ) != 1 )
			{
				liberror_error_set(
				 error,
				 LIBERROR_ERROR_DOMAIN_RUNTIME,
				 LIBERROR_RUNTIME_ERROR_GET_FAILED,
				 "%s: unable to retrieve cache value identifier.",
				 function );

				return( -1 );
			}
		}
		if( ( node_data_offset != cache_value_offset )
		 || ( node_timestamp != cache_value_timestamp ) )
		{
			liberror_error_set(
			 error,
			 LIBERROR_ERROR_DOMAIN_RUNTIME,
			 LIBERROR_RUNTIME_ERROR_VALUE_MISSING,
			 "%s: missing cache value.",
			 function );

			return( -1 );
		}
	}
	if( libfcache_cache_value_get_value(
	     cache_value,
	     node_value,
	     error ) != 1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve node value.",
		 function );

		return( -1 );
	}
	return( 1 );
}
/* Reads the database
 * Returns 1 if successful or -1 on error
 */
int libesedb_database_read(
     libesedb_database_t *database,
     libbfio_handle_t *file_io_handle,
     libesedb_io_handle_t *io_handle,
     libfdata_vector_t *pages_vector,
     libfcache_cache_t *pages_cache,
     liberror_error_t **error )
{
	libesedb_page_tree_t *database_page_tree        = NULL;
	libesedb_values_tree_value_t *values_tree_value = NULL;
	libfcache_cache_t *database_values_cache        = NULL;
	libfdata_tree_t *database_values_tree           = NULL;
	libfdata_tree_node_t *database_values_tree_node = NULL;
	uint8_t *data                                   = NULL;
	static char *function                           = "libesedb_database_read";
	off64_t node_data_offset                        = 0;
	size_t data_size                                = 0;
	int number_of_leaf_nodes                        = 0;
	int leaf_node_index                             = 0;

	if( database == NULL )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_ARGUMENTS,
		 LIBERROR_ARGUMENT_ERROR_INVALID_VALUE,
		 "%s: invalid database.",
		 function );

		return( -1 );
	}
	if( libesedb_page_tree_initialize(
	     &database_page_tree,
	     io_handle,
	     pages_vector,
	     pages_cache,
	     LIBESEDB_FDP_OBJECT_IDENTIFIER_DATABASE,
	     NULL,
	     NULL,
	     error ) != 1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
		 "%s: unable to create database page tree.",
		 function );

		goto on_error;
	}
	/* TODO clone function
	 */
	if( libfdata_tree_initialize(
	     &database_values_tree,
	     (intptr_t *) database_page_tree,
	     (int (*)(intptr_t **, liberror_error_t **)) &libesedb_page_tree_free,
	     NULL,
	     &libesedb_page_tree_read_node_value,
	     &libesedb_page_tree_read_sub_nodes,
	     LIBFDATA_FLAG_IO_HANDLE_MANAGED,
	     error ) != 1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
		 "%s: unable to create database values tree.",
		 function );

		goto on_error;
	}
	database_page_tree = NULL;

	if( libfcache_cache_initialize(
	     &database_values_cache,
	     LIBESEDB_MAXIMUM_CACHE_ENTRIES_TREE_VALUES,
	     error ) != 1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
		 "%s: unable to create database values cache.",
		 function );

		goto on_error;
	}
	node_data_offset  = LIBESEDB_PAGE_NUMBER_DATABASE - 1;
	node_data_offset *= io_handle->page_size;

	if( libfdata_tree_set_root_node(
	     database_values_tree,
	     node_data_offset,
	     0,
	     error ) != 1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_SET_FAILED,
		 "%s: unable to set root node in database values tree.",
		 function );

		goto on_error;
	}
	if( libfdata_tree_get_number_of_leaf_nodes(
	     database_values_tree,
	     file_io_handle,
	     database_values_cache,
	     &number_of_leaf_nodes,
	     0,
	     error ) != 1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_GET_FAILED,
		 "%s: unable to retrieve number of leaf nodes from database values tree.",
		 function );

		goto on_error;
	}
	for( leaf_node_index = 0;
	     leaf_node_index < number_of_leaf_nodes;
	     leaf_node_index++ )
	{
		if( libfdata_tree_get_leaf_node_by_index(
		     database_values_tree,
		     file_io_handle,
		     database_values_cache,
		     leaf_node_index,
		     &database_values_tree_node,
		     0,
		     error ) != 1 )
		{
			liberror_error_set(
			 error,
			 LIBERROR_ERROR_DOMAIN_RUNTIME,
			 LIBERROR_RUNTIME_ERROR_GET_FAILED,
			 "%s: unable to retrieve leaf node: %d from database values tree.",
			 function,
			 leaf_node_index );

			goto on_error;
		}
		if( libfdata_tree_node_get_node_value(
		     database_values_tree_node,
		     file_io_handle,
		     database_values_cache,
		     (intptr_t **) &values_tree_value,
		     0,
		     error ) != 1 )
		{
			liberror_error_set(
			 error,
			 LIBERROR_ERROR_DOMAIN_RUNTIME,
			 LIBERROR_RUNTIME_ERROR_GET_FAILED,
			 "%s: unable to retrieve node value from values tree node.",
			 function );

			goto on_error;
		}
		if( libesedb_values_tree_value_read_data(
		     values_tree_value,
		     file_io_handle,
		     io_handle,
		     pages_vector,
		     pages_cache,
		     &data,
		     &data_size,
		     error ) != 1 )
		{
			liberror_error_set(
			 error,
			 LIBERROR_ERROR_DOMAIN_IO,
			 LIBERROR_IO_ERROR_READ_FAILED,
			 "%s: unable to read values tree value data.",
			 function );

			goto on_error;
		}
		/* TODO */
#if defined( HAVE_DEBUG_OUTPUT )
		if( libnotify_verbose != 0 )
		{
			if( data_size > 0 )
			{
				libnotify_printf(
				 "%s: database value: %d data:\n",
				 function,
				 leaf_node_index );
				libnotify_print_data(
				 data,
				 data_size );
			}
		}
#endif
	}
	if( libfcache_cache_free(
	     &database_values_cache,
	     error ) != 1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_FINALIZE_FAILED,
		 "%s: unable to free database values cache.",
		 function );

		goto on_error;
	}
	if( libfdata_tree_free(
	     &database_values_tree,
	     error ) != 1 )
	{
		liberror_error_set(
		 error,
		 LIBERROR_ERROR_DOMAIN_RUNTIME,
		 LIBERROR_RUNTIME_ERROR_FINALIZE_FAILED,
		 "%s: unable to free database values tree.",
		 function );

		goto on_error;
	}
	return( 1 );

on_error:
	if( database_values_cache != NULL )
	{
		libfcache_cache_free(
		 &database_values_cache,
		 NULL );
	}
	if( database_values_tree != NULL )
	{
		libfdata_tree_free(
		 &database_values_tree,
		 NULL );
	}
	if( database_page_tree != NULL )
	{
		libesedb_page_tree_free(
		 &database_page_tree,
		 NULL );
	}
	return( -1 );
}