diff --git a/trunk/libxmount/libxmount.c b/trunk/libxmount/libxmount.c index 8d79d34..855b971 100644 --- a/trunk/libxmount/libxmount.c +++ b/trunk/libxmount/libxmount.c @@ -1,95 +1,97 @@ /******************************************************************************* * xmount Copyright (c) 2008-2014 by Gillen Daniel * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU 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 General Public License along with * * this program. If not, see . * *******************************************************************************/ +#include + #include #include #include #include "libxmount.h" /* * StrToInt32 */ int32_t StrToInt32(const char *p_value, int *p_ok) { long int num; char *p_tail; errno=0; num=strtol(p_value,&p_tail,0); if(errno==ERANGE || *p_tail!='\0' || numINT32_MAX) { *p_ok=0; return 0; } *p_ok=1; return (int32_t)num; } /* * StrToUint32 */ uint32_t StrToUint32(const char *p_value, int *p_ok) { unsigned long int num; char *p_tail; errno=0; num=strtoul(p_value,&p_tail,0); if(errno==ERANGE || *p_tail!='\0' || num>UINT32_MAX) { *p_ok=0; return 0; } *p_ok=1; return (uint32_t)num; } /* * StrToInt64 */ int64_t StrToInt64(const char *p_value, int *p_ok) { long long int num; char *p_tail; errno=0; num=strtoll(p_value,&p_tail,0); if(errno==ERANGE || *p_tail!='\0' || numINT64_MAX) { *p_ok=0; return 0; } *p_ok=1; return (int64_t)num; } /* * StrToUint64 */ uint64_t StrToUint64(const char *p_value, int *p_ok) { unsigned long long int num; char *p_tail; errno=0; num=strtoull(p_value,&p_tail,0); if(errno==ERANGE || *p_tail!='\0' || num>UINT64_MAX) { *p_ok=0; return 0; } *p_ok=1; return (uint64_t)num; } diff --git a/trunk/libxmount/libxmount.h b/trunk/libxmount/libxmount.h index 40db04f..e7d0adb 100644 --- a/trunk/libxmount/libxmount.h +++ b/trunk/libxmount/libxmount.h @@ -1,37 +1,39 @@ /******************************************************************************* * xmount Copyright (c) 2008-2014 by Gillen Daniel * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU 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 General Public License along with * * this program. If not, see . * *******************************************************************************/ #ifndef LIBXMOUNT_H #define LIBXMOUNT_H +#include "../src/endianness.h" + //! Struct containing lib options typedef struct s_LibXmountOptions { //! Option name char *p_key; //! Option value char *p_value; //! Set to 1 if key/value has been parsed and is valid uint8_t valid; } ts_LibXmountOptions, *pts_LibXmountOptions; int32_t StrToInt32(const char *p_value, int *p_ok); uint32_t StrToUint32(const char *p_value, int *p_ok); int64_t StrToInt64(const char *p_value, int *p_ok); uint64_t StrToUint64(const char *p_value, int *p_ok); #endif // LIBXMOUNT_H diff --git a/trunk/libxmount_morphing/CMakeLists.txt b/trunk/libxmount_morphing/CMakeLists.txt index a7c74cb..5279a00 100644 --- a/trunk/libxmount_morphing/CMakeLists.txt +++ b/trunk/libxmount_morphing/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(libxmount_morphing_combine) add_subdirectory(libxmount_morphing_raid) +add_subdirectory(libxmount_morphing_freespace) diff --git a/trunk/libxmount_morphing/libxmount_morphing_freespace/CMakeLists.txt b/trunk/libxmount_morphing/libxmount_morphing_freespace/CMakeLists.txt new file mode 100644 index 0000000..3cb055b --- /dev/null +++ b/trunk/libxmount_morphing/libxmount_morphing_freespace/CMakeLists.txt @@ -0,0 +1,10 @@ +if(POLICY CMP0042) + cmake_policy(SET CMP0042 NEW) # CMake 3.0 +endif(POLICY CMP0042) + +project(libxmount_morphing_freespace C) + +add_library(xmount_morphing_freespace SHARED libxmount_morphing_freespace.c ../../libxmount/libxmount.c) + +install(TARGETS xmount_morphing_freespace DESTINATION lib/xmount) + diff --git a/trunk/libxmount_morphing/libxmount_morphing_freespace/libxmount_morphing_freespace.c b/trunk/libxmount_morphing/libxmount_morphing_freespace/libxmount_morphing_freespace.c new file mode 100644 index 0000000..9ee8fbd --- /dev/null +++ b/trunk/libxmount_morphing/libxmount_morphing_freespace/libxmount_morphing_freespace.c @@ -0,0 +1,591 @@ +/******************************************************************************* +* xmount Copyright (c) 2008-2014 by Gillen Daniel * +* * +* This program is free software: you can redistribute it and/or modify it * +* under the terms of the GNU 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 General Public License along with * +* this program. If not, see . * +*******************************************************************************/ + +#include +#include +#include + +#include "../libxmount_morphing.h" +#include "libxmount_morphing_freespace.h" + +/******************************************************************************* + * LibXmount_Morphing API implementation + ******************************************************************************/ +/* + * LibXmount_Morphing_GetApiVersion + */ +uint8_t LibXmount_Morphing_GetApiVersion() { + return LIBXMOUNT_MORPHING_API_VERSION; +} + +/* + * LibXmount_Morphing_GetSupportedFormats + */ +const char* LibXmount_Morphing_GetSupportedTypes() { + return "freespace\0\0"; +} + +/* + * LibXmount_Morphing_GetFunctions + */ +void LibXmount_Morphing_GetFunctions(ts_LibXmountMorphingFunctions *p_functions) +{ + p_functions->CreateHandle=&FreespaceCreateHandle; + p_functions->DestroyHandle=&FreespaceDestroyHandle; + p_functions->Morph=&FreespaceMorph; + p_functions->Size=&FreespaceSize; + p_functions->Read=&FreespaceRead; + p_functions->OptionsHelp=&FreespaceOptionsHelp; + p_functions->OptionsParse=&FreespaceOptionsParse; + p_functions->GetInfofileContent=&FreespaceGetInfofileContent; + p_functions->GetErrorMessage=&FreespaceGetErrorMessage; + p_functions->FreeBuffer=&FreespaceFreeBuffer; +} + +/******************************************************************************* + * Private + ******************************************************************************/ +/* + * FreespaceCreateHandle + */ +static int FreespaceCreateHandle(void **pp_handle, + char *p_format, + uint8_t debug) +{ + pts_FreespaceHandle p_freespace_handle; + + // Alloc new handle + p_freespace_handle=malloc(sizeof(ts_FreespaceHandle)); + if(p_freespace_handle==NULL) return FREESPACE_MEMALLOC_FAILED; + + // Init handle values + p_freespace_handle->debug=debug; + //p_freespace_handle->fs_type=FreespaceFsType_Unknown; + // DEBUG + p_freespace_handle->fs_type=FreespaceFsType_HfsPlus; + p_freespace_handle->p_input_functions=NULL; + p_freespace_handle->morphed_image_size=0; + p_freespace_handle->hfsplus.p_vh=NULL; + p_freespace_handle->hfsplus.p_alloc_file=NULL; + p_freespace_handle->hfsplus.free_block_map_size=0; + p_freespace_handle->hfsplus.p_free_block_map=NULL; + + LOG_DEBUG("Created new LibXmount_Morphing_Freespace handle\n"); + + // Return new handle + *pp_handle=p_freespace_handle; + return FREESPACE_OK; +} + +/* + * FreespaceDestroyHandle + */ +static int FreespaceDestroyHandle(void **pp_handle) { + pts_FreespaceHandle p_freespace_handle=(pts_FreespaceHandle)*pp_handle; + + LOG_DEBUG("Destroying LibXmount_Morphing_Freespace handle\n"); + + // Free fs data + switch(p_freespace_handle->fs_type) { + case FreespaceFsType_HfsPlus: { + if(p_freespace_handle->hfsplus.p_vh!=NULL) + free(p_freespace_handle->hfsplus.p_vh); + if(p_freespace_handle->hfsplus.p_alloc_file!=NULL) + free(p_freespace_handle->hfsplus.p_alloc_file); + if(p_freespace_handle->hfsplus.p_free_block_map!=NULL) + free(p_freespace_handle->hfsplus.p_free_block_map); + break; + } + case FreespaceFsType_Unknown: + default: + break; + } + + // Free handle + free(p_freespace_handle); + + *pp_handle=NULL; + return FREESPACE_OK; +} + +/* + * FreespaceMorph + */ +static int FreespaceMorph(void *p_handle, + pts_LibXmountMorphingInputFunctions p_input_functions) +{ + pts_FreespaceHandle p_freespace_handle=(pts_FreespaceHandle)p_handle; + uint64_t input_images_count; + int ret; + + LOG_DEBUG("Initializing LibXmount_Morphing_Raid\n"); + + // Make sure freespace_fs was given + if(p_freespace_handle->fs_type==FreespaceFsType_Unknown) { + return FREESPACE_NO_FS_SPECIFIED; + } + + // Set input functions and get image count + p_freespace_handle->p_input_functions=p_input_functions; + if(p_freespace_handle-> + p_input_functions-> + ImageCount(&input_images_count)!=0) + { + return FREESPACE_CANNOT_GET_IMAGECOUNT; + } + + // Make sure there is exactly one input image + if(input_images_count==0 || input_images_count>1) { + return FREESPACE_WRONG_INPUT_IMAGE_COUNT; + } + + // Extract unallocated blocks from input image + switch(p_freespace_handle->fs_type) { + case FreespaceFsType_HfsPlus: { + // Read HFS+ VH + ret=FreespaceReadHfsPlusHeader(p_freespace_handle); + if(ret!=FREESPACE_OK) return ret; + // Read HFS+ alloc file + ret=FreespaceReadHfsPlusAllocFile(p_freespace_handle); + if(ret!=FREESPACE_OK) return ret; + // Build free block map + ret=FreespaceBuildHfsPlusBlockMap(p_freespace_handle); + if(ret!=FREESPACE_OK) return ret; + // Calculate morphed image size + p_freespace_handle->morphed_image_size= + p_freespace_handle->hfsplus.p_vh->block_size* + p_freespace_handle->hfsplus.free_block_map_size; + break; + } + case FreespaceFsType_Unknown: + default: + return FREESPACE_UNSUPPORTED_FS_SPECIFIED; + } + + LOG_DEBUG("Total size of unallocated blocks is %" PRIu64 " bytes\n", + p_freespace_handle->morphed_image_size); + + return FREESPACE_OK; +} + +/* + * FreespaceSize + */ +static int FreespaceSize(void *p_handle, uint64_t *p_size) { + *p_size=((pts_FreespaceHandle)(p_handle))->morphed_image_size; + return FREESPACE_OK; +} + +/* + * FreespaceRead + */ +static int FreespaceRead(void *p_handle, + char *p_buf, + off_t offset, + size_t count, + size_t *p_read) +{ + pts_FreespaceHandle p_freespace_handle=(pts_FreespaceHandle)p_handle; + int ret; + + LOG_DEBUG("Reading %zu bytes at offset %zu from morphed image\n", + count, + offset); + + // Make sure read parameters are within morphed image bounds + if(offset>=p_freespace_handle->morphed_image_size || + offset+count>p_freespace_handle->morphed_image_size) + { + return FREESPACE_READ_BEYOND_END_OF_IMAGE; + } + + // Read data + switch(p_freespace_handle->fs_type) { + case FreespaceFsType_HfsPlus: { + ret=FreespaceReadHfsPlusBlock(p_freespace_handle, + p_buf, + offset, + count, + p_read); + if(ret!=FREESPACE_OK) return ret; + break; + } + case FreespaceFsType_Unknown: + default: + return FREESPACE_UNSUPPORTED_FS_SPECIFIED; + } + + return FREESPACE_OK; +} + +/* + * FreespaceOptionsHelp + */ +static const char* FreespaceOptionsHelp() { + return " freespace_fs : Specify the filesystem to extract unallocated " + "blocks from. Supported filesystems are: 'hfs+'"; +} + +/* + * FreespaceOptionsParse + */ +static int FreespaceOptionsParse(void *p_handle, + uint32_t options_count, + pts_LibXmountOptions *pp_options, + char **pp_error) +{ + pts_FreespaceHandle p_freespace_handle=(pts_FreespaceHandle)p_handle; + int ok; + + for(uint32_t i=0;ip_key,"freespace_fs")==0) { + if(strcmp(pp_options[i]->p_value,"hfs+")==0) { + p_freespace_handle->fs_type=FreespaceFsType_HfsPlus; + } else { + ok=asprintf(pp_error, + "Unsupported filesystem '%s' specified", + pp_options[i]->p_value); + if(ok<0 || *pp_error==NULL) { + *pp_error=NULL; + return FREESPACE_MEMALLOC_FAILED; + } + return FREESPACE_UNSUPPORTED_FS_SPECIFIED; + } + + LOG_DEBUG("Setting fs to %s\n",pp_options[i]->p_value); + + pp_options[i]->valid=1; + } + } + + return FREESPACE_OK; +} + +/* + * FreespaceGetInfofileContent + */ +static int FreespaceGetInfofileContent(void *p_handle, char **pp_info_buf) { + //pts_FreespaceHandle p_freespace_handle=(pts_FreespaceHandle)p_handle; + //int ret; + + *pp_info_buf=NULL; + // TODO +/* + ret=asprintf(pp_info_buf, + "Simulating RAID level 0 over %" PRIu64 " disks.\n" + "Chunk size: %" PRIu32 " bytes\n" + "Chunks per disk: %" PRIu64 "\n" + "Total capacity: %" PRIu64 " bytes (%0.3f GiB)\n", + p_raid_handle->input_images_count, + p_raid_handle->chunk_size, + p_raid_handle->chunks_per_image, + p_raid_handle->morphed_image_size, + p_raid_handle->morphed_image_size/(1024.0*1024.0*1024.0)); + if(ret<0 || *pp_info_buf==NULL) return RAID_MEMALLOC_FAILED; +*/ + return FREESPACE_OK; +} + +/* + * FreespaceGetErrorMessage + */ +static const char* FreespaceGetErrorMessage(int err_num) { + switch(err_num) { + case FREESPACE_MEMALLOC_FAILED: + return "Unable to allocate memory"; + break; + case FREESPACE_NO_FS_SPECIFIED: + return "No filesystem specified using option freespace_fs"; + break; + case FREESPACE_UNSUPPORTED_FS_SPECIFIED: + return "Unsupported fs specified"; + case FREESPACE_CANNOT_GET_IMAGECOUNT: + return "Unable to get input image count"; + break; + case FREESPACE_WRONG_INPUT_IMAGE_COUNT: + return "Only 1 input image is supported"; + break; + case FREESPACE_CANNOT_GET_IMAGESIZE: + return "Unable to get input image size"; + break; + case FREESPACE_READ_BEYOND_END_OF_IMAGE: + return "Unable to read data: Attempt to read past EOF"; + break; + case FREESPACE_CANNOT_READ_DATA: + return "Unable to read data"; + break; + case FREESPACE_CANNOT_PARSE_OPTION: + return "Unable to parse library option"; + break; + case FREESPACE_CANNOT_READ_HFSPLUS_HEADER: + return "Unable to read HFS+ volume header"; + break; + case FREESPACE_INVALID_HFSPLUS_HEADER: + return "Found invalid HFS+ volume header"; + break; + case FREESPACE_CANNOT_READ_HFSPLUS_ALLOC_FILE: + return "Unable to read HFS+ allocation file"; + break; + case FREESPACE_ALLOC_FILE_HAS_TOO_MUCH_EXTENDS: + return "HFS+ allocation file has more then 8 extends. This is unsupported"; + break; + default: + return "Unknown error"; + } +} + +/* + * FreespaceFreeBuffer + */ +static void FreespaceFreeBuffer(void *p_buf) { + free(p_buf); +} + +/******************************************************************************* + * Private helper functions + ******************************************************************************/ +/* + * FreespaceReadHfsPlusHeader + */ +static int FreespaceReadHfsPlusHeader(pts_FreespaceHandle p_freespace_handle) { + pts_FreespaceHfsPlusData p_hfs_data=&(p_freespace_handle->hfsplus); + int ret; + size_t bytes_read; + pts_FreespaceHfsPlusExtend p_extend; + + LOG_DEBUG("Reading HFS+ volume header\n"); + + // Alloc buffer for header + p_hfs_data->p_vh=calloc(1,sizeof(ts_FreespaceHfsPlusVH)); + if(p_hfs_data->p_vh==NULL) return FREESPACE_MEMALLOC_FAILED; + + // Read VH from input image + ret=p_freespace_handle-> + p_input_functions-> + Read(0, + (char*)(p_hfs_data->p_vh), + FREESPACE_HFSPLUS_VH_OFFSET, + sizeof(ts_FreespaceHfsPlusVH), + &bytes_read); + if(ret!=0 || bytes_read!=sizeof(ts_FreespaceHfsPlusVH)) { + free(p_hfs_data->p_vh); + p_hfs_data->p_vh=NULL; + return FREESPACE_CANNOT_READ_HFSPLUS_HEADER; + } + + // Convert VH to host endianness + p_hfs_data->p_vh->signature=be16toh(p_hfs_data->p_vh->signature); + p_hfs_data->p_vh->version=be16toh(p_hfs_data->p_vh->version); + p_hfs_data->p_vh->block_size=be32toh(p_hfs_data->p_vh->block_size); + p_hfs_data->p_vh->total_blocks=be32toh(p_hfs_data->p_vh->total_blocks); + p_hfs_data->p_vh->free_blocks=be32toh(p_hfs_data->p_vh->free_blocks); + p_hfs_data->p_vh->alloc_file_size=be64toh(p_hfs_data->p_vh->alloc_file_size); + p_hfs_data->p_vh->alloc_file_clump_size= + be32toh(p_hfs_data->p_vh->alloc_file_clump_size); + p_hfs_data->p_vh->alloc_file_total_blocks= + be32toh(p_hfs_data->p_vh->alloc_file_total_blocks); + for(int i=0;i<8;i++) { + p_extend=&(p_hfs_data->p_vh->alloc_file_extends[i]); + p_extend->start_block=be32toh(p_extend->start_block); + p_extend->block_count=be32toh(p_extend->block_count); + } + + LOG_DEBUG("HFS+ VH signature: 0x%04X\n",p_hfs_data->p_vh->signature); + LOG_DEBUG("HFS+ VH version: %" PRIu16 "\n",p_hfs_data->p_vh->version); + LOG_DEBUG("HFS+ block size: %" PRIu32 " bytes\n",p_hfs_data->p_vh->block_size); + LOG_DEBUG("HFS+ total blocks: %" PRIu32 "\n",p_hfs_data->p_vh->total_blocks); + LOG_DEBUG("HFS+ free blocks: %" PRIu32 "\n",p_hfs_data->p_vh->free_blocks); + LOG_DEBUG("HFS+ allocation file size: %" PRIu64 " bytes\n", + p_hfs_data->p_vh->alloc_file_size); + LOG_DEBUG("HFS+ allocation file blocks: %" PRIu32 "\n", + p_hfs_data->p_vh->alloc_file_total_blocks); + + // Check header signature and version + if(p_hfs_data->p_vh->signature!=FREESPACE_HFSPLUS_VH_SIGNATURE || + p_hfs_data->p_vh->version!=FREESPACE_HFSPLUS_VH_VERSION) + { + free(p_hfs_data->p_vh); + p_hfs_data->p_vh=NULL; + return FREESPACE_INVALID_HFSPLUS_HEADER; + } + + return FREESPACE_OK; +} + +/* + * FreespaceReadHfsPlusAllocFile + */ +static int FreespaceReadHfsPlusAllocFile(pts_FreespaceHandle p_freespace_handle) +{ + pts_FreespaceHfsPlusData p_hfs_data=&(p_freespace_handle->hfsplus); + pts_FreespaceHfsPlusExtend p_extend; + int ret; + char *p_buf; + size_t bytes_read; + uint64_t total_bytes_read=0; + + LOG_DEBUG("Reading HFS+ allocation file\n"); + + // Alloc buffer for file + p_hfs_data->p_alloc_file=calloc(1,p_hfs_data->p_vh->alloc_file_size); + if(p_hfs_data->p_alloc_file==NULL) return FREESPACE_MEMALLOC_FAILED; + + // Loop over extends and read data + p_buf=(char*)(p_hfs_data->p_alloc_file); + for(int i=0;i<8;i++) { + p_extend=&(p_hfs_data->p_vh->alloc_file_extends[i]); + + // If start_block and block_count are zero, we parsed last extend + if(p_extend->start_block==0 && p_extend->block_count==0) break; + + LOG_DEBUG("Extend %d contains %" PRIu32 + " block(s) starting with block %" PRIu32 "\n", + i, + p_extend->block_count, + p_extend->start_block); + + // Read data + for(uint32_t ii=0;iiblock_count;ii++) { + LOG_DEBUG("Reading %" PRIu32 " bytes from block %" PRIu32 + " at offset %" PRIu64 "\n", + p_hfs_data->p_vh->block_size, + p_extend->start_block+ii, + (uint64_t)((p_extend->start_block+ii)* + p_hfs_data->p_vh->block_size)); + + ret=p_freespace_handle-> + p_input_functions-> + Read(0, + p_buf, + (p_extend->start_block+ii)*p_hfs_data->p_vh->block_size, + p_hfs_data->p_vh->block_size, + &bytes_read); + if(ret!=0 || bytes_read!=p_hfs_data->p_vh->block_size) { + free(p_hfs_data->p_alloc_file); + p_hfs_data->p_alloc_file=NULL; + return FREESPACE_CANNOT_READ_HFSPLUS_ALLOC_FILE; + } + p_buf+=p_hfs_data->p_vh->block_size; + total_bytes_read+=p_hfs_data->p_vh->block_size; + } + } + + // Alloc files with more then 8 extends aren't supported yet + if(total_bytes_read!=p_hfs_data->p_vh->alloc_file_size) { + free(p_hfs_data->p_alloc_file); + p_hfs_data->p_alloc_file=NULL; + return FREESPACE_ALLOC_FILE_HAS_TOO_MUCH_EXTENDS; + } + + return FREESPACE_OK; +} + +/* + * FreespaceBuildHfsPlusBlockMap + */ +static int FreespaceBuildHfsPlusBlockMap(pts_FreespaceHandle p_freespace_handle) +{ + pts_FreespaceHfsPlusData p_hfs_data=&(p_freespace_handle->hfsplus); + + LOG_DEBUG("Searching unallocated HFS+ blocks\n"); + + // Save offset of every unallocated block in block map + for(uint32_t cur_block=0; + cur_blockp_vh->total_blocks; + cur_block++) + { + if((p_hfs_data->p_alloc_file[cur_block/8] & (1<<(7-(cur_block%8))))==0) { + p_hfs_data->p_free_block_map=realloc(p_hfs_data->p_free_block_map, + (p_hfs_data->free_block_map_size+1)* + sizeof(uint64_t)); + if(p_hfs_data->p_free_block_map==NULL) { + p_hfs_data->free_block_map_size=0; + return FREESPACE_MEMALLOC_FAILED; + } + p_hfs_data->p_free_block_map[p_hfs_data->free_block_map_size]= + cur_block*p_hfs_data->p_vh->block_size; + p_hfs_data->free_block_map_size++; + } + } + + LOG_DEBUG("According to VH, there should be %" PRIu64 " unallocated blocks\n", + p_hfs_data->p_vh->free_blocks); + LOG_DEBUG("Found %" PRIu64 " unallocated HFS+ blocks\n", + p_hfs_data->free_block_map_size); + + return FREESPACE_OK; +} + +/* + * FreespaceReadHfsPlusBlock + */ +static int FreespaceReadHfsPlusBlock(pts_FreespaceHandle p_freespace_handle, + char *p_buf, + off_t offset, + size_t count, + size_t *p_read) +{ + pts_FreespaceHfsPlusData p_hfs_data=&(p_freespace_handle->hfsplus); + uint64_t cur_block; + off_t cur_block_offset; + off_t cur_image_offset; + size_t cur_count; + int ret; + size_t bytes_read; + + // Calculate starting block and block offset + cur_block=offset/p_hfs_data->p_vh->block_size; + cur_block_offset=offset-(cur_block*p_hfs_data->p_vh->block_size); + + // Init p_read + *p_read=0; + + while(count!=0) { + // Calculate input image offset to read from + cur_image_offset=p_hfs_data->p_free_block_map[cur_block]+cur_block_offset; + + // Calculate how many bytes to read from current block + if(cur_block_offset+count>p_hfs_data->p_vh->block_size) { + cur_count=p_hfs_data->p_vh->block_size-cur_block_offset; + } else { + cur_count=count; + } + + LOG_DEBUG("Reading %zu bytes at offset %zu (block %" PRIu64 ")\n", + cur_count, + cur_image_offset+cur_block_offset, + cur_block); + + // Read bytes + ret=p_freespace_handle->p_input_functions-> + Read(0, + p_buf, + cur_image_offset+cur_block_offset, + cur_count, + &bytes_read); + if(ret!=0 || bytes_read!=cur_count) return FREESPACE_CANNOT_READ_DATA; + + p_buf+=cur_count; + cur_block_offset=0; + count-=cur_count; + cur_block++; + (*p_read)+=cur_count; + } + + return FREESPACE_OK; +} + diff --git a/trunk/libxmount_morphing/libxmount_morphing_freespace/libxmount_morphing_freespace.h b/trunk/libxmount_morphing/libxmount_morphing_freespace/libxmount_morphing_freespace.h new file mode 100644 index 0000000..afe8cf0 --- /dev/null +++ b/trunk/libxmount_morphing/libxmount_morphing_freespace/libxmount_morphing_freespace.h @@ -0,0 +1,146 @@ +/******************************************************************************* +* xmount Copyright (c) 2008-2014 by Gillen Daniel * +* * +* This program is free software: you can redistribute it and/or modify it * +* under the terms of the GNU 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 General Public License along with * +* this program. If not, see . * +*******************************************************************************/ + +#ifndef LIBXMOUNT_MORPHING_FREESPACE_H +#define LIBXMOUNT_MORPHING_FREESPACE_H + +#define LOG_ERROR(...) { \ + LibXmount_Morphing_LogMessage("ERROR", \ + (char*)__FUNCTION__, \ + __LINE__, \ + __VA_ARGS__); \ +} +#define LOG_WARNING(...) { \ + LibXmount_Morphing_LogMessage("WARNING", \ + (char*)__FUNCTION__, \ + __LINE__, \ + __VA_ARGS__); \ +} +#define LOG_DEBUG(...) { \ + if(p_freespace_handle->debug==1) \ + LibXmount_Morphing_LogMessage("DEBUG", \ + (char*)__FUNCTION__, \ + __LINE__, \ + __VA_ARGS__); \ +} + +/******************************************************************************* + * Enums, type defs, etc... + ******************************************************************************/ +// Error codes +enum { + FREESPACE_OK=0, + FREESPACE_MEMALLOC_FAILED, + FREESPACE_NO_FS_SPECIFIED, + FREESPACE_UNSUPPORTED_FS_SPECIFIED, + FREESPACE_CANNOT_GET_IMAGECOUNT, + FREESPACE_WRONG_INPUT_IMAGE_COUNT, + FREESPACE_CANNOT_GET_IMAGESIZE, + FREESPACE_READ_BEYOND_END_OF_IMAGE, + FREESPACE_CANNOT_READ_DATA, + FREESPACE_CANNOT_PARSE_OPTION, + FREESPACE_CANNOT_READ_HFSPLUS_HEADER, + FREESPACE_INVALID_HFSPLUS_HEADER, + FREESPACE_CANNOT_READ_HFSPLUS_ALLOC_FILE, + FREESPACE_ALLOC_FILE_HAS_TOO_MUCH_EXTENDS +}; + +// Supported fs types +typedef enum e_FreespaceFsType { + FreespaceFsType_Unknown=0, + FreespaceFsType_HfsPlus +} te_FreespaceFsType; + +// HFS+ extend +typedef struct s_FreespaceHfsPlusExtend { + uint32_t start_block; + uint32_t block_count; +} __attribute__ ((packed)) ts_FreespaceHfsPlusExtend, *pts_FreespaceHfsPlusExtend; + +// Needed parts of the HFS+ volume header +#define FREESPACE_HFSPLUS_VH_OFFSET 1024 +#define FREESPACE_HFSPLUS_VH_SIGNATURE 0x482b //"H+" +#define FREESPACE_HFSPLUS_VH_VERSION 4 +typedef struct s_FreespaceHfsPlusVH { + uint16_t signature; // "H+" + uint16_t version; // Currently 4 for HFS+ + uint32_t unused01[9]; + uint32_t block_size; + uint32_t total_blocks; + uint32_t free_blocks; + uint32_t unused02[15]; + uint64_t alloc_file_size; + uint32_t alloc_file_clump_size; + uint32_t alloc_file_total_blocks; + ts_FreespaceHfsPlusExtend alloc_file_extends[8]; +} __attribute__ ((packed)) ts_FreespaceHfsPlusVH, *pts_FreespaceHfsPlusVH; + +// Needed data for HFS+ +typedef struct s_FreespaceHfsPlusData { + pts_FreespaceHfsPlusVH p_vh; + uint8_t *p_alloc_file; + uint64_t free_block_map_size; + uint64_t *p_free_block_map; +} ts_FreespaceHfsPlusData, *pts_FreespaceHfsPlusData; + +// Handle +typedef struct s_FreespaceHandle { + uint8_t debug; + te_FreespaceFsType fs_type; + pts_LibXmountMorphingInputFunctions p_input_functions; + uint64_t morphed_image_size; + ts_FreespaceHfsPlusData hfsplus; +} ts_FreespaceHandle, *pts_FreespaceHandle; + +/******************************************************************************* + * Forward declarations + ******************************************************************************/ +static int FreespaceCreateHandle(void **pp_handle, + char *p_format, + uint8_t debug); +static int FreespaceDestroyHandle(void **pp_handle); +static int FreespaceMorph(void *p_handle, + pts_LibXmountMorphingInputFunctions p_input_functions); +static int FreespaceSize(void *p_handle, + uint64_t *p_size); +static int FreespaceRead(void *p_handle, + char *p_buf, + off_t offset, + size_t count, + size_t *p_read); +static const char* FreespaceOptionsHelp(); +static int FreespaceOptionsParse(void *p_handle, + uint32_t options_count, + pts_LibXmountOptions *pp_options, + char **pp_error); +static int FreespaceGetInfofileContent(void *p_handle, + char **pp_info_buf); +static const char* FreespaceGetErrorMessage(int err_num); +static void FreespaceFreeBuffer(void *p_buf); + +// Helper functions +static int FreespaceReadHfsPlusHeader(pts_FreespaceHandle p_freespace_handle); +static int FreespaceReadHfsPlusAllocFile(pts_FreespaceHandle p_freespace_handle); +static int FreespaceBuildHfsPlusBlockMap(pts_FreespaceHandle p_freespace_handle); +static int FreespaceReadHfsPlusBlock(pts_FreespaceHandle p_freespace_handle, + char *p_buf, + off_t offset, + size_t count, + size_t *p_read); + +#endif // LIBXMOUNT_MORPHING_FREESPACE_H +