diff --git a/trunk/CMakeLists.txt b/trunk/CMakeLists.txt index 3f1333a..3f417cc 100644 --- a/trunk/CMakeLists.txt +++ b/trunk/CMakeLists.txt @@ -1,94 +1,96 @@ cmake_minimum_required(VERSION 2.8) project(xmount C) include(CheckIncludeFiles) #include(CheckCSourceCompiles) # Only for internal packaging #set(STATIC_EWF 1) #set(STATIC_AFF 1) +add_definitions(-DXMOUNT_VERSION="0.7.0") + if(APPLE) # On OSx, do not try to find frameworks but only std headers / libraries set(CMAKE_FIND_FRAMEWORK "NEVER") endif(APPLE) # Make sure CMAKE_BUILD_TYPE is set if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") else(NOT CMAKE_BUILD_TYPE) if(NOT (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "Debug")) message(FATAL_ERROR "Only build types 'Release' and 'Debug' are supported!") endif(NOT (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "Debug")) endif(NOT CMAKE_BUILD_TYPE) # Add cmake_modules dir to CMAKE_MODULE_PATH set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake_modules/") # Check required headers check_include_files(stdlib.h HAVE_STDLIB_H) check_include_files(stdio.h HAVE_STDIO_H) check_include_files(stdint.h HAVE_STDINT_H) check_include_files(stdarg.h HAVE_STDARG_H) check_include_files(string.h HAVE_STRING_H) check_include_files(errno.h HAVE_ERRNO_H) check_include_files(fcntl.h HAVE_FCNTL_H) check_include_files(dlfcn.h HAVE_DLFCN_H) check_include_files(dirent.h HAVE_DIRENT_H) check_include_files(unistd.h HAVE_UNISTD_H) check_include_files(sys/ioctl.h HAVE_SYS_IOCTL_H) check_include_files(sys/stat.h HAVE_SYS_STAT_H) check_include_files(sys/types.h HAVE_SYS_TYPES_H) check_include_files(linux/fs.h HAVE_LINUX_FS_H) check_include_files(grp.h HAVE_GRP_H) check_include_files(pwd.h HAVE_PWD_H) check_include_files(pthread.h HAVE_PTHREAD_H) check_include_files(time.h HAVE_TIME_H) check_include_files(inttypes.h HAVE_INTTYPES_H) check_include_files(byteswap.h HAVE_BYTESWAP_H) check_include_files(endian.h HAVE_ENDIAN_H) check_include_files(libkern/OSByteOrder.h HAVE_LIBKERN_OSBYTEORDER_H) # Check for required libs if(NOT APPLE) find_package(LibFUSE REQUIRED) else(NOT APPLE) # On OSx, search for osxfuse find_package(LibOSXFUSE REQUIRED) endif(NOT APPLE) # Generate config.h and add it's path to the include dirs configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) include_directories(${CMAKE_CURRENT_BINARY_DIR}) # Add preprocessor definitions add_definitions(-D_LARGEFILE64_SOURCE) add_definitions(-D_FILE_OFFSET_BITS=64) add_definitions(-D_GNU_SOURCE) set(CMAKE_C_FLAGS "-fno-strict-aliasing -std=c99 -Wall") set(CMAKE_C_FLAGS_RELEASE "-O2 ${CMAKE_C_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "-ggdb -O0 ${CMAKE_C_FLAGS}") # Check that off_t can represent 2**63 - 1 correctly. # If it can't, we need to set _FILE_OFFSET_BITS=64 #check_c_source_compiles(" # #include # #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) # int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; # int main() { return 0; } #" _OFFT_IS_64BIT) #if(NOT ${_OFFT_IS_64BIT}) # set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} "-D_FILE_OFFSET_BITS=64") #endif(NOT ${_OFFT_IS_64BIT}) # Compile stuff in sub dirs add_subdirectory(libxmount_input) add_subdirectory(libxmount_morphing) add_subdirectory(src) # Install man page INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/xmount.1 DESTINATION share/man/man1) diff --git a/trunk/libxmount_morphing/libxmount_morphing.h b/trunk/libxmount_morphing/libxmount_morphing.h index 9e7832c..3af65e6 100644 --- a/trunk/libxmount_morphing/libxmount_morphing.h +++ b/trunk/libxmount_morphing/libxmount_morphing.h @@ -1,205 +1,269 @@ /******************************************************************************* * 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_H #define LIBXMOUNT_MORPHING_H #define LIBXMOUNT_MORPHING_API_VERSION 1 #include +#include // For printf #include // For int*_t and uint*_t +#include // For va_*, vprintf #include // For PRI* -typedef struct s_LibXmountMorphingInputImage { - void *p_image_handle; - uint64_t size; - int (*Read)(void *p_handle, char *p_buf, off_t offset, size_t count, size_t *p_read); -} ts_LibXmountMorphingInputImage, *pts_LibXmountMorphingInputImage; +/******************************************************************************* + * Type defs + ******************************************************************************/ +//! Struct containing pointers to input image functions +typedef struct s_LibXmountMorphingInputFunctions { + //! Function to get the amount of input images + /*! + * \param p_count Count of input images + * \return 0 on success + */ + int (*ImageCount)(uint64_t *p_count); + + //! Function to get the size of the morphed data + /*! + * \param image Image number + * \param p_size Pointer to store input image's size to + * \return 0 on success + */ + int (*Size)(uint64_t image, + uint64_t *p_size); + + //! Function to read data from input image + /*! + * \param image Image number + * \param p_buf Buffer to store read data to + * \param offset Position at which to start reading + * \param count Amount of bytes to read + * \param p_read Number of read bytes on success + * \return 0 on success or negated error code on error + */ + int (*Read)(uint64_t image, + char *p_buf, + off_t offset, + size_t count, + size_t *p_read); +} ts_LibXmountMorphingInputFunctions, *pts_LibXmountMorphingInputFunctions; //! Structure containing pointers to the lib's functions typedef struct s_LibXmountMorphingFunctions { //! Function to initialize handle /*! * This function is called once to allow the lib to alloc any needed * structures before other functions that rely upon a valid handle are called * (for ex. OptionsParse or Morph). * * The p_format parameter specifies one of the morphing functions returned by * LibXmount_Morphing_GetSupportedMorphFunctions() which should be used for * this handle. * * \param pp_handle Pointer to store handle to * \param p_type Morph type to use + * \param debug If set to 1, print debugging infos to stdout * \return 0 on success or error code */ - int (*CreateHandle)(void **pp_handle, char *p_type); + int (*CreateHandle)(void **pp_handle, + char *p_type, + uint8_t debug); //! Function to destroy handle /*! * In this function, any structures allocated with CreateHandle should be * freed. It is generally the last function called before unloading of lib * happens. * * By convention, after this function has been called, *pp_handle must be * NULL. * * \param pp_handle Pointer to store handle to * \return 0 on success or error code */ int (*DestroyHandle)(void **pp_handle); //! Function to start morphing /*! * Begins to morph input image * * \param p_handle Handle * \param p_input_functions ts_LibXmountInputFunctions structure * \return 0 on success or error code */ int (*Morph)(void *p_handle, - uint64_t input_images, - const pts_LibXmountMorphingInputImage *pp_input_images); + pts_LibXmountMorphingInputFunctions p_input_functions); //! Function to get the size of the morphed data /*! * \param p_handle Handle to the opened image * \param p_size Pointer to store input image's size to * \return 0 on success or error code */ int (*Size)(void *p_handle, uint64_t *p_size); //! Function to read data from an input image /*! * Reads count bytes at offset from input image and copies them into memory * starting at the address of p_buf. Memory is pre-allocated to as much bytes * as should be read. * * \param p_handle Handle to the opened image * \param p_buf Buffer to store read data to * \param offset Position at which to start reading * \param count Amount of bytes to read * \param p_read Number of read bytes on success * \return 0 on success or negated error code on error */ int (*Read)(void *p_handle, char *p_buf, off_t offset, size_t count, size_t *p_read); //! Function to get a help message for any supported lib-specific options /*! * Calling this function should return a string containing help messages for * any supported lib-specific options. Every line of this text must be * prepended with 6 spaces. * * Returned string must be constant. It won't be freed! * * If there is no help text, this function must return NULL. * * \return Pointer to a null-terminated string containing the help text */ const char* (*OptionsHelp)(); //! Function to parse any lib-specific options /*! * This function is called with the options given with the --inopts parameter. * All contained options are for the lib. If errors or unknown options are * found, this function should fail and return an error message in pp_error. * pp_error will be freed by the caller by using FreeBuffer. * * \param p_handle Handle to the opened image * \param p_options String with specified options * \param pp_error Pointer to a string with error message * \return 0 on success or error code and error message */ int (*OptionsParse)(void *p_handle, char *p_options, char **pp_error); //! Function to get content to add to the info file /*! * The returned string is added to xmount's info file. This function is only * called once when the info file is generated. The returned string is then * freed with a call to FreeBuffer. * * \param p_handle Handle to the opened image * \param pp_info_buf Pointer to store the null-terminated content * \return 0 on success or error code */ int (*GetInfofileContent)(void *p_handle, char **pp_info_buf); //! Function to get an error message /*! * This function should translate an error code that was previously returned * by one of the library functions into a human readable error message. * * By convention, this function must always return a valid pointer to a * NULL-terminated string! * * \param err_num Error code as returned by lib */ const char* (*GetErrorMessage)(int err_num); //! Function to free buffers that were allocated by lib /*! * \param p_buf Buffer to free */ void (*FreeBuffer)(void *p_buf); } ts_LibXmountMorphingFunctions, *pts_LibXmountMorphingFunctions; +/******************************************************************************* + * API functions + ******************************************************************************/ //! Get library API version /*! * This function should return the value of LIBXMOUNT_MORPHING_API_VERSION * * \return Supported version */ uint8_t LibXmount_Morphing_GetApiVersion(); typedef uint8_t (*t_LibXmount_Morphing_GetApiVersion)(); //! Get a list of supported morphing functions /*! * Gets a list of supported morphing functions. These is the string * specified with xmount's --morph command line option. The returned * string must be a constant vector of morphing functions split by \0 chars. To * mark the end of the vector, a single \0 must be used. * * As an example, "first\0second\0\0" would be a correct string to return for * a lib supporting two morphing functions. * * \return Vector containing supported morphing functions */ const char* LibXmount_Morphing_GetSupportedTypes(); typedef const char* (*t_LibXmount_Morphing_GetSupportedTypes)(); //! Get the lib's s_LibXmountMorphingFunctions structure /*! * This function should set the members of the given * s_LibXmountMorphingFunctions structure to the internal lib functions. All * members have to be set. * * \param p_functions s_LibXmountMorphingFunctions structure to fill */ void LibXmount_Morphing_GetFunctions(pts_LibXmountMorphingFunctions p_functions); typedef void (*t_LibXmount_Morphing_GetFunctions)(pts_LibXmountMorphingFunctions); +/******************************************************************************* + * Helper functions + ******************************************************************************/ +//! Print error and debug messages to stdout +/*! + * \param p_msg_type "ERROR" or "DEBUG" + * \param p_calling_fun Name of calling function + * \param line Line number of call + * \param p_msg Message string + * \param ... Variable params with values to include in message string + */ +static inline void LibXmount_Morphing_LogMessage(char *p_msg_type, + char *p_calling_fun, + int line, + char *p_msg, + ...) +{ + va_list var_list; + + // Print message "header" + printf("%s: %s.%s@%u : ",p_msg_type,p_calling_fun,XMOUNT_VERSION,line); + // Print message with variable parameters + va_start(var_list,p_msg); + vprintf(p_msg,var_list); + va_end(var_list); +} #endif // LIBXMOUNT_MORPHING_H diff --git a/trunk/libxmount_morphing/libxmount_morphing_combine/libxmount_morphing_combine.c b/trunk/libxmount_morphing/libxmount_morphing_combine/libxmount_morphing_combine.c index 37300d1..b677a39 100644 --- a/trunk/libxmount_morphing/libxmount_morphing_combine/libxmount_morphing_combine.c +++ b/trunk/libxmount_morphing/libxmount_morphing_combine/libxmount_morphing_combine.c @@ -1,248 +1,289 @@ /******************************************************************************* * 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_combine.h" #define DEBUG /******************************************************************************* * 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 "combine\0\0"; } /* * LibXmount_Morphing_GetFunctions */ void LibXmount_Morphing_GetFunctions(ts_LibXmountMorphingFunctions *p_functions) { p_functions->CreateHandle=&CombineCreateHandle; p_functions->DestroyHandle=&CombineDestroyHandle; p_functions->Morph=&CombineMorph; p_functions->Size=&CombineSize; p_functions->Read=&CombineRead; p_functions->OptionsHelp=&CombineOptionsHelp; p_functions->OptionsParse=&CombineOptionsParse; p_functions->GetInfofileContent=&CombineGetInfofileContent; p_functions->GetErrorMessage=&CombineGetErrorMessage; p_functions->FreeBuffer=&CombineFreeBuffer; } /******************************************************************************* * Private ******************************************************************************/ /* * CombineCreateHandle */ -static int CombineCreateHandle(void **pp_handle, char *p_format) { - pts_CombineHandle p_handle; +static int CombineCreateHandle(void **pp_handle, + char *p_format, + uint8_t debug) +{ + pts_CombineHandle p_combine_handle; + + LOG_DEBUG("Creating new LibXmount_Morphing_Combine handle\n"); // Alloc new handle - p_handle=malloc(sizeof(ts_CombineHandle)); - if(p_handle==NULL) return COMBINE_MEMALLOC_FAILED; + p_combine_handle=malloc(sizeof(ts_CombineHandle)); + if(p_combine_handle==NULL) return COMBINE_MEMALLOC_FAILED; // Init handle values - p_handle->input_images_count=0; - p_handle->pp_input_images=NULL; - p_handle->morphed_image_size=0; + p_combine_handle->debug=debug; + p_combine_handle->input_images_count=0; + p_combine_handle->p_input_functions=NULL; + p_combine_handle->morphed_image_size=0; // Return new handle - *pp_handle=p_handle; + *pp_handle=p_combine_handle; return COMBINE_OK; } /* * CombineDestroyHandle */ static int CombineDestroyHandle(void **pp_handle) { - pts_CombineHandle p_handle=(pts_CombineHandle)*pp_handle; + pts_CombineHandle p_combine_handle=(pts_CombineHandle)*pp_handle; + + LOG_DEBUG("Freeing LibXmount_Morphing_Combine handle\n"); // Free handle - free(p_handle); + free(p_combine_handle); *pp_handle=NULL; return COMBINE_OK; } /* * CombineMorph */ static int CombineMorph(void *p_handle, - uint64_t input_images, - const pts_LibXmountMorphingInputImage *pp_input_images) + pts_LibXmountMorphingInputFunctions p_input_functions) { pts_CombineHandle p_combine_handle=(pts_CombineHandle)p_handle; + int ret; + uint64_t input_image_size; - // Add given values to our handle - p_combine_handle->input_images_count=input_images; - p_combine_handle->pp_input_images=pp_input_images; + LOG_DEBUG("Initializing LibXmount_Morphing_Combine\n"); + + // Set input functions and get image count + p_combine_handle->p_input_functions=p_input_functions; + if(p_combine_handle-> + p_input_functions-> + ImageCount(&p_combine_handle->input_images_count)!=0) + { + return COMBINE_CANNOT_GET_IMAGECOUNT; + } // Calculate morphed image size - for(uint64_t i=0;isize, - i); -#endif - p_combine_handle->morphed_image_size+=pp_input_images[i]->size; + for(uint64_t i=0;iinput_images_count;i++) { + ret=p_combine_handle-> + p_input_functions-> + Size(i,&input_image_size); + if(ret!=0) return COMBINE_CANNOT_GET_IMAGESIZE; + + LOG_DEBUG("Adding %" PRIu64 " bytes from image %" PRIu64 "\n", + input_image_size, + i); + + p_combine_handle->morphed_image_size+=input_image_size; } -#ifdef DEBUG - printf("Total morphed image size is %" PRIu64 " bytes.\n", - p_combine_handle->morphed_image_size); -#endif + LOG_DEBUG("Total morphed image size is %" PRIu64 " bytes\n", + p_combine_handle->morphed_image_size); return COMBINE_OK; } /* * CombineSize */ static int CombineSize(void *p_handle, uint64_t *p_size) { *p_size=((pts_CombineHandle)(p_handle))->morphed_image_size; return COMBINE_OK; } /* * CombineRead */ static int CombineRead(void *p_handle, char *p_buf, off_t offset, size_t count, size_t *p_read) { pts_CombineHandle p_combine_handle=(pts_CombineHandle)p_handle; uint64_t cur_input_image=0; + uint64_t cur_input_image_size=0; off_t cur_offset=offset; int ret; size_t cur_count; size_t read; + 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_combine_handle->morphed_image_size || offset+count>p_combine_handle->morphed_image_size) { return COMBINE_READ_BEYOND_END_OF_IMAGE; } // Search starting image to read from - while(cur_offset>=p_combine_handle->pp_input_images[cur_input_image]->size) { - cur_offset-=p_combine_handle->pp_input_images[cur_input_image]->size; + ret=p_combine_handle->p_input_functions->Size(cur_input_image, + &cur_input_image_size); + while(ret==0 && cur_offset>=cur_input_image_size) { + cur_offset-=cur_input_image_size; cur_input_image++; + ret=p_combine_handle->p_input_functions->Size(cur_input_image, + &cur_input_image_size); } + if(ret!=0) return COMBINE_CANNOT_GET_IMAGESIZE; // Read data - while(count!=0) { + while(cur_input_imageinput_images_count && count!=0) { + // Get current input image size + ret=p_combine_handle->p_input_functions->Size(cur_input_image, + &cur_input_image_size); + if(ret!=0) return COMBINE_CANNOT_GET_IMAGESIZE; + // Calculate how many bytes to read from current input image - if(cur_offset+count> - p_combine_handle->pp_input_images[cur_input_image]->size) - { - cur_count= - p_combine_handle->pp_input_images[cur_input_image]->size-cur_offset; + if(cur_offset+count>cur_input_image_size) { + cur_count=cur_input_image_size-cur_offset; } else { cur_count=count; } + LOG_DEBUG("Reading %zu bytes at offset %zu from input image %" PRIu64 "\n", + cur_count, + cur_offset, + cur_input_image); + // Read bytes - ret=p_combine_handle->pp_input_images[cur_input_image]-> - Read(p_combine_handle->pp_input_images[cur_input_image]-> - p_image_handle, + ret=p_combine_handle->p_input_functions-> + Read(cur_input_image, p_buf, cur_offset, cur_count, &read); if(ret!=0 || read!=cur_count) return COMBINE_CANNOT_READ_DATA; p_buf+=cur_count; cur_offset=0; count-=cur_count; cur_input_image++; } + if(count!=0) return COMBINE_CANNOT_READ_DATA; *p_read=count; return COMBINE_OK; } /* * CombineOptionsHelp */ static const char* CombineOptionsHelp() { return COMBINE_OK; } /* * CombineOptionsParse */ static int CombineOptionsParse(void *p_handle, char *p_options, char **pp_error) { *pp_error=NULL; return COMBINE_OK; } /* * CombineGetInfofileContent */ static int CombineGetInfofileContent(void *p_handle, char **pp_info_buf) { *pp_info_buf=NULL; return COMBINE_OK; } /* * CombineGetErrorMessage */ static const char* CombineGetErrorMessage(int err_num) { switch(err_num) { case COMBINE_MEMALLOC_FAILED: return "Unable to allocate memory"; break; + case COMBINE_CANNOT_GET_IMAGECOUNT: + return "Unable to get input image count"; + break; + case COMBINE_CANNOT_GET_IMAGESIZE: + return "Unable to get input image size"; + break; case COMBINE_READ_BEYOND_END_OF_IMAGE: return "Unable to read data: Attempt to read past EOF"; break; case COMBINE_CANNOT_READ_DATA: return "Unable to read data"; break; default: return "Unknown error"; } } /* * CombineFreeBuffer */ static void CombineFreeBuffer(void *p_buf) { free(p_buf); } diff --git a/trunk/libxmount_morphing/libxmount_morphing_combine/libxmount_morphing_combine.h b/trunk/libxmount_morphing/libxmount_morphing_combine/libxmount_morphing_combine.h index 3c93476..61a8f79 100644 --- a/trunk/libxmount_morphing/libxmount_morphing_combine/libxmount_morphing_combine.h +++ b/trunk/libxmount_morphing/libxmount_morphing_combine/libxmount_morphing_combine.h @@ -1,63 +1,86 @@ /******************************************************************************* * 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_COMBINE_H #define LIBXMOUNT_MORPHING_COMBINE_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_combine_handle->debug==1) \ + LibXmount_Morphing_LogMessage("DEBUG", \ + (char*)__FUNCTION__, \ + __LINE__, \ + __VA_ARGS__); \ +} + /******************************************************************************* - * Error codes etc... + * Enums, type defs, etc... ******************************************************************************/ enum { COMBINE_OK=0, COMBINE_MEMALLOC_FAILED, + COMBINE_CANNOT_GET_IMAGECOUNT, + COMBINE_CANNOT_GET_IMAGESIZE, COMBINE_READ_BEYOND_END_OF_IMAGE, COMBINE_CANNOT_READ_DATA }; typedef struct s_CombineHandle { + uint8_t debug; uint64_t input_images_count; - const pts_LibXmountMorphingInputImage *pp_input_images; + pts_LibXmountMorphingInputFunctions p_input_functions; uint64_t morphed_image_size; } ts_CombineHandle, *pts_CombineHandle; /******************************************************************************* * Forward declarations ******************************************************************************/ static int CombineCreateHandle(void **pp_handle, - char *p_format); + char *p_format, + uint8_t debug); static int CombineDestroyHandle(void **pp_handle); static int CombineMorph(void *p_handle, - uint64_t input_images, - const pts_LibXmountMorphingInputImage *pp_input_images); + pts_LibXmountMorphingInputFunctions p_input_functions); static int CombineSize(void *p_handle, uint64_t *p_size); static int CombineRead(void *p_handle, char *p_buf, off_t offset, size_t count, size_t *p_read); static const char* CombineOptionsHelp(); static int CombineOptionsParse(void *p_handle, char *p_options, char **pp_error); static int CombineGetInfofileContent(void *p_handle, char **pp_info_buf); static const char* CombineGetErrorMessage(int err_num); static void CombineFreeBuffer(void *p_buf); #endif // LIBXMOUNT_MORPHING_COMBINE_H diff --git a/trunk/src/CMakeLists.txt b/trunk/src/CMakeLists.txt index 7cffe2c..26f37c9 100644 --- a/trunk/src/CMakeLists.txt +++ b/trunk/src/CMakeLists.txt @@ -1,20 +1,18 @@ -add_definitions(-DXMOUNT_VERSION="0.7.0") - if(NOT APPLE) include_directories(${LIBFUSE_INCLUDE_DIRS}) set(LIBS ${LIBS} ${LIBFUSE_LIBRARIES}) else(NOT APPLE) include_directories(${LIBOSXFUSE_INCLUDE_DIRS}) set(LIBS ${LIBS} ${LIBOSXFUSE_LIBRARIES}) endif(NOT APPLE) set(LIBS ${LIBS} "dl") add_definitions(-DXMOUNT_LIBRARY_PATH="${CMAKE_INSTALL_PREFIX}/lib/xmount") add_executable(xmount xmount.c md5.c) target_link_libraries(xmount ${LIBS}) install(TARGETS xmount DESTINATION bin) diff --git a/trunk/src/xmount.c b/trunk/src/xmount.c index 99aedc6..b02de39 100755 --- a/trunk/src/xmount.c +++ b/trunk/src/xmount.c @@ -1,3570 +1,3740 @@ /******************************************************************************* * xmount Copyright (c) 2008-2014 by Gillen Daniel * * * * xmount is a small tool to "fuse mount" various harddisk image formats as dd, * * vdi, vhd or vmdk files and enable virtual write access to them. * * * * 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 #include #include // For PRI* #include #include // For dlopen, dlclose, dlsym #include // For opendir, readdir, closedir #include #include #include // For fstat #include #ifdef HAVE_LINUX_FS_H #include // For SEEK_* ?? #endif #if !defined(__APPLE__) && defined(HAVE_GRP_H) && defined(HAVE_PWD_H) #include // For getgrnam, struct group #include // For getpwuid, struct passwd #endif #include #include // For time #define FUSE_USE_VERSION 26 #include #include "xmount.h" #include "md5.h" #include "endianness.h" #include "macros.h" #define XMOUNT_COPYRIGHT_NOTICE \ "xmount v%s Copyright (c) 2008-2014 by Gillen Daniel " /******************************************************************************* * Global vars ******************************************************************************/ //! Struct that contains various runtime configuration options static ts_XmountData glob_xmount; -//! Structs containing pointers to the libxmount_input functions -static uint32_t glob_input_libs_count=0; -static pts_InputLib *glob_pp_input_libs=NULL; - -//! Structs containing pointers to the libxmount_morphing functions -static uint32_t glob_morphing_libs_count=0; -static pts_MorphingLib *glob_pp_morphing_libs=NULL; -pts_LibXmountMorphingFunctions glob_p_morphing_functions=NULL; -void *glob_p_morphing_handle=NULL; -pts_LibXmountMorphingInputImage *glob_pp_morphing_input_images=NULL; - //! Pointer to virtual info file static char *glob_p_info_file=NULL; -//! Vars needed for VDI emulation -static pts_VdiFileHeader glob_p_vdi_header=NULL; -static uint32_t glob_vdi_header_size=0; -static char *glob_p_vdi_block_map=NULL; -static uint32_t glob_p_vdi_block_map_size=0; - -//! Vars needed for VHD emulation -static ts_VhdFileHeader *glob_p_vhd_header=NULL; - -//! Vars needed for VMDK emulation -static char *glob_p_vmdk_file=NULL; -static int glob_vmdk_file_size=0; -static char *glob_p_vmdk_lockdir1=NULL; -static char *glob_p_vmdk_lockdir2=NULL; -static char *glob_p_vmdk_lockfile_data=NULL; -static int glob_vmdk_lockfile_size=0; -static char *glob_p_vmdk_lockfile_name=NULL; - //! Vars needed for virtual write access static FILE *glob_p_cache_file=NULL; static pts_CacheFileHeader glob_p_cache_header=NULL; static pts_CacheFileBlockIndex glob_p_cache_blkidx=NULL; //! Mutexes to control concurrent read & write access static pthread_mutex_t glob_mutex_image_rw; static pthread_mutex_t glob_mutex_info_read; +/******************************************************************************* + * Forward declarations + ******************************************************************************/ +// Helper functions +static void LogMessage(char*, char*, int, char*, ...); +static void PrintUsage(char*); +static void CheckFuseSettings(); +static int ParseCmdLine(const int, char**, int*, char***, char**); +static int ExtractVirtFileNames(char*); +static int GetMorphedImageSize(uint64_t*); +static int GetVirtImageSize(uint64_t*); +static int GetInputImageData(pts_InputImage, char*, off_t, size_t, size_t*); +static int GetMorphedImageData(char*, off_t, size_t, size_t*); +static int GetVirtImageData(char*, off_t, size_t); +static int SetVdiFileHeaderData(char*, off_t, size_t); +static int SetVhdFileHeaderData(char*, off_t, size_t); +static int SetVirtImageData(const char*, off_t, size_t); +static int CalculateInputImageHash(uint64_t*, uint64_t*); +static int InitVirtVdiHeader(); +static int InitVirtVhdHeader(); +static int InitVirtualVmdkFile(); +static int InitVirtImageInfoFile(); +static int InitCacheFile(); +static int LoadLibs(); +static int FindInputLib(pts_InputImage); +static int FindMorphingLib(); +static void InitResources(); +static void FreeResources(); +// Functions exported to LibXmount_Morphing +static int LibXmount_Morphing_ImageCount(uint64_t*); +static int LibXmount_Morphing_Size(uint64_t, uint64_t*); +static int LibXmount_Morphing_Read(uint64_t, char*, off_t, size_t, size_t*); +// Functions implementing FUSE functions +static int FuseGetAttr(const char*, struct stat*); +static int FuseMkDir(const char*, mode_t); +static int FuseMkNod(const char*, mode_t, dev_t); +static int FuseReadDir(const char*, + void*, + fuse_fill_dir_t, + off_t, + struct fuse_file_info*); +static int FuseOpen(const char*, struct fuse_file_info*); +static int FuseRead(const char*, char*, size_t, off_t, struct fuse_file_info*); +static int FuseRename(const char*, const char*); +static int FuseRmDir(const char*); +static int FuseUnlink(const char*); +//static int FuseStatFs(const char*, struct statvfs*); +static int FuseWrite(const char *p_path, + const char*, + size_t, + off_t, + struct fuse_file_info*); + /******************************************************************************* * Helper functions ******************************************************************************/ //! Print error and debug messages to stdout /*! * \param p_msg_type "ERROR" or "DEBUG" * \param p_calling_fun Name of calling function * \param line Line number of call * \param p_msg Message string * \param ... Variable params with values to include in message string */ static void LogMessage(char *p_msg_type, char *p_calling_fun, int line, char *p_msg, ...) { va_list var_list; // Print message "header" printf("%s: %s.%s@%u : ",p_msg_type,p_calling_fun,XMOUNT_VERSION,line); // Print message with variable parameters va_start(var_list,p_msg); vprintf(p_msg,var_list); va_end(var_list); } //! Print usage instructions (cmdline options etc..) /*! * \param p_prog_name Program name (argv[0]) */ static void PrintUsage(char *p_prog_name) { char *p_buf; int first=1; printf("\n" XMOUNT_COPYRIGHT_NOTICE "\n",XMOUNT_VERSION); printf("\nUsage:\n"); printf(" %s [fopts] --in [] \n\n",p_prog_name); printf("Options:\n"); printf(" fopts:\n"); printf(" -d : Enable FUSE's and xmount's debug mode.\n"); printf(" -h : Display this help message.\n"); printf(" -s : Run single threaded.\n"); printf(" -o no_allow_other : Disable automatic addition of FUSE's " "allow_other option.\n"); printf(" -o : Specify fuse mount options. Will also disable " "automatic addition of FUSE's allow_other option!\n"); printf("\n"); printf(" mopts:\n"); printf(" --cache : Enable virtual write support.\n"); printf(" specifies the cache file to use.\n"); printf(" --in : Input image format and source file(s). " "May be specified multiple times.\n"); printf(" can be "); // List supported input formats - for(uint32_t i=0;ip_supported_input_types; + for(uint32_t i=0;ip_supported_input_types; while(*p_buf!='\0') { if(first==1) { printf("\"%s\"",p_buf); first=0; } else printf(", \"%s\"",p_buf); p_buf+=(strlen(p_buf)+1); } } printf(".\n"); printf(" specifies the source file. If your image is split into " "multiple files, you have to specify them all!\n"); printf(" --inopts : Specify input library specific options.\n"); printf(" specifies a comma separated list of key=value options. " "See below for possible values.\n"); printf(" --info : Print out infos about used compiler and libraries.\n"); printf(" --morph : Morphing function to apply to input image(s). " "If not specified, defaults to \"combine\".\n"); printf(" can be "); // TODO: List supported morphing functions printf(".\n"); printf(" --offset : Move the output image data start bytes " "into the input image(s).\n"); printf(" --out : Output image format. If not specified, " "defaults to "); #ifdef __APPLE__ printf("\"dmg\".\n"); #else printf("\"dd\".\n"); #endif printf(" can be "); // List supported output formats printf("\"dd\", \"dmg\", \"vdi\", \"vhd\", \"vmdk\", \"vmdks\".\n"); printf(" --owcache : Same as --cache but overwrites " "existing cache file.\n"); printf(" --version : Same as --info.\n"); printf("\n"); printf(" mntp:\n"); printf(" Mount point where virtual files should be located.\n"); printf("\n"); printf("Infos:\n"); printf(" * The --in option is mandatory!\n"); printf(" * If you specify --in multiple times, all images are morphed " "using the specified morphing function before they are converted to " "the specified output format.\n"); printf(" * For VMDK emulation, you have to uncomment \"user_allow_other\" " "in /etc/fuse.conf or run xmount as root.\n"); printf("\n"); printf("Input library specific options:\n"); printf(" Some input libraries might support an own set of options to " "configure / tune their behaviour.\n"); printf(" Input libraries supporting this feature (if any) and and their " "options are listed below.\n"); printf("\n"); // List input lib options - for(uint32_t i=0;ilib_functions.OptionsHelp(); + for(uint32_t i=0;ilib_functions.OptionsHelp()); if(p_buf==NULL) continue; - printf(" - %s\n",glob_pp_input_libs[i]->p_name); + printf(" - %s\n",glob_xmount.input.pp_libs[i]->p_name); printf("%s\n",p_buf); printf("\n"); } } //! Check fuse settings /*! * Check if FUSE allows us to pass the -o allow_other parameter. This only works * if we are root or user_allow_other is set in /etc/fuse.conf. * * In addition, this function also checks if the user is member of the fuse * group which is generally needed to use fuse at all. */ static void CheckFuseSettings() { #if !defined(__APPLE__) && defined(HAVE_GRP_H) && defined(HAVE_PWD_H) struct group *p_group; struct passwd *p_passwd; #endif int found; FILE *h_fuse_conf; char line[256]; glob_xmount.may_set_fuse_allow_other=FALSE; if(geteuid()==0) { // Running as root, there should be no problems glob_xmount.may_set_fuse_allow_other=TRUE; return; } #if !defined(__APPLE__) && defined(HAVE_GRP_H) && defined(HAVE_PWD_H) // Check if a fuse group exists and if so, make sure user is a member of it. // Makes only sense on Linux because as far as I know osxfuse has no own group p_group=getgrnam("fuse"); if(p_group!=NULL) { // Get effective user name p_passwd=getpwuid(geteuid()); if(p_passwd==NULL) { printf("\nWARNING: Unable to determine your effective user name. If " "mounting works, you can ignore this message.\n\n"); return; } // Check if user is member of fuse group found=FALSE; while(*(p_group->gr_mem)!=NULL) { if(strcmp(*(p_group->gr_mem),p_passwd->pw_name)==0) { found=TRUE; break; } p_group->gr_mem++; } if(found==FALSE) { printf("\nWARNING: You are not a member of the \"fuse\" group. This will " "prevent you from mounting images using xmount. Please add " "yourself to the \"fuse\" group using the command " "\"sudo usermod -a -G fuse %s\" and reboot your system or " "execute xmount as root.\n\n", p_passwd->pw_name); return; } } else { printf("\nWARNING: Your system does not seem to have a \"fuse\" group. If " "mounting works, you can ignore this message.\n\n"); } #endif // Read FUSE's config file /etc/fuse.conf and check for set user_allow_other h_fuse_conf=(FILE*)FOPEN("/etc/fuse.conf","r"); if(h_fuse_conf!=NULL) { // Search conf file for set user_allow_others found=FALSE; while(fgets(line,sizeof(line),h_fuse_conf)!=NULL) { // TODO: This works as long as there is no other parameter beginning with // "user_allow_other" :) if(strncmp(line,"user_allow_other",16)==0) { found=TRUE; break; } } fclose(h_fuse_conf); if(found==TRUE) { glob_xmount.may_set_fuse_allow_other=TRUE; } else { printf("\nWARNING: FUSE will not allow other users nor root to access " "your virtual harddisk image. To change this behavior, please " "add \"user_allow_other\" to /etc/fuse.conf or execute xmount " "as root.\n\n"); } } else { printf("\nWARNING: Unable to open /etc/fuse.conf. If mounting works, you " "can ignore this message. If you encounter issues, please create " "the file and add a single line containing the string " "\"user_allow_other\" or execute xmount as root.\n\n"); return; } } //! Parse command line options /*! * \param argc Number of cmdline params * \param pp_argv Array containing cmdline params * \param p_nargv Number of FUSE options is written to this var * \param ppp_nargv FUSE options are written to this array * \param pp_mountpoint Mountpoint is written to this var * \return TRUE on success, FALSE on error */ static int ParseCmdLine(const int argc, char **pp_argv, int *p_nargv, char ***ppp_nargv, char **pp_mountpoint) { int i=1,opts=0,FuseMinusOControl=TRUE,FuseAllowOther=TRUE,first; char *p_buf; pts_InputImage p_input_image; // add pp_argv[0] to ppp_nargv opts++; XMOUNT_MALLOC(*ppp_nargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*ppp_nargv)[opts-1],pp_argv[0]) // Parse options while(i1 && *(pp_argv[i]+1)!='-') { // Options beginning with one - are mostly FUSE specific if(strcmp(pp_argv[i],"-d")==0) { // Enable FUSE's and xmount's debug mode opts++; XMOUNT_REALLOC(*ppp_nargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*ppp_nargv)[opts-1],pp_argv[i]) glob_xmount.debug=TRUE; } else if(strcmp(pp_argv[i],"-h")==0) { // Print help message PrintUsage(pp_argv[0]); exit(0); } else if(strcmp(pp_argv[i],"-o")==0) { // Next parameter specifies fuse / lib mount options if((argc+1)>i) { i++; // As the user specified the -o option, we assume he knows what he is // doing. We won't append allow_other automatically. And we allow him // to disable allow_other by passing a single "-o no_allow_other" // which won't be passed to FUSE as it is xmount specific. if(strcmp(pp_argv[i],"no_allow_other")!=0) { opts+=2; XMOUNT_REALLOC(*ppp_nargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*ppp_nargv)[opts-2],pp_argv[i-1]) XMOUNT_STRSET((*ppp_nargv)[opts-1],pp_argv[i]) FuseMinusOControl=FALSE; } else FuseAllowOther=FALSE; } else { LOG_ERROR("Couldn't parse mount options!\n") return FALSE; } } else if(strcmp(pp_argv[i],"-s")==0) { // Enable FUSE's single threaded mode opts++; XMOUNT_REALLOC(*ppp_nargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*ppp_nargv)[opts-1],pp_argv[i]) } else if(strcmp(pp_argv[i],"-V")==0) { // Display FUSE version info opts++; XMOUNT_REALLOC(*ppp_nargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*ppp_nargv)[opts-1],pp_argv[i]) } else { LOG_ERROR("Unknown command line option \"%s\"\n",pp_argv[i]); return FALSE; } } else { // Options beginning with -- are xmount specific if(strcmp(pp_argv[i],"--cache")==0 || strcmp(pp_argv[i],"--rw")==0) { // Emulate writable access to mounted image // Next parameter must be cache file to read/write changes from/to if((argc+1)>i) { i++; XMOUNT_STRSET(glob_xmount.p_cache_file,pp_argv[i]) - glob_xmount.writable=TRUE; + glob_xmount.output.writable=TRUE; } else { LOG_ERROR("You must specify a cache file!\n") return FALSE; } LOG_DEBUG("Enabling virtual write support using cache file \"%s\"\n", glob_xmount.p_cache_file) } else if(strcmp(pp_argv[i],"--in")==0) { // Specify input image type and source files if((argc+2)>i) { i++; // Alloc and init new ts_InputImage struct XMOUNT_MALLOC(p_input_image,pts_InputImage,sizeof(ts_InputImage)); XMOUNT_STRSET(p_input_image->p_type,pp_argv[i]); p_input_image->pp_files=NULL; p_input_image->p_functions=NULL; p_input_image->p_handle=NULL; // Parse input image filename(s) and add to p_input_image->pp_files i++; p_input_image->files_count=0; while(i<(argc-1) && strncmp(pp_argv[i],"--",2)!=0) { p_input_image->files_count++; XMOUNT_REALLOC(p_input_image->pp_files, char**, p_input_image->files_count*sizeof(char*)); XMOUNT_STRSET(p_input_image->pp_files[p_input_image->files_count-1], pp_argv[i]); i++; } i--; if(p_input_image->files_count==0) { LOG_ERROR("No input files specified for \"--in %s\"!\n", p_input_image->p_type) free(p_input_image->p_type); free(p_input_image); return FALSE; } // Add input image struct to input image array - glob_xmount.input_images_count++; - XMOUNT_REALLOC(glob_xmount.pp_input_images, + glob_xmount.input.images_count++; + XMOUNT_REALLOC(glob_xmount.input.pp_images, pts_InputImage*, - glob_xmount.input_images_count* + glob_xmount.input.images_count* sizeof(pts_InputImage)); - glob_xmount.pp_input_images[glob_xmount.input_images_count-1]= + glob_xmount.input.pp_images[glob_xmount.input.images_count-1]= p_input_image; } else { LOG_ERROR("You must specify an input image type and source file!\n"); return FALSE; } } else if(strcmp(pp_argv[i],"--inopts")==0) { if((argc+1)>i) { i++; - if(glob_xmount.p_lib_params==NULL) { - XMOUNT_STRSET(glob_xmount.p_lib_params,pp_argv[i]); + if(glob_xmount.input.p_lib_params==NULL) { + XMOUNT_STRSET(glob_xmount.input.p_lib_params,pp_argv[i]); } else { LOG_ERROR("You can only specify --inopts once!") return FALSE; } } else { LOG_ERROR("You must specify special options!\n"); return FALSE; } } else if(strcmp(pp_argv[i],"--out")==0) { // Specify output image type // Next parameter must be image type if((argc+1)>i) { i++; if(strcmp(pp_argv[i],"dd")==0) { - glob_xmount.VirtImageType=VirtImageType_DD; + glob_xmount.output.VirtImageType=VirtImageType_DD; LOG_DEBUG("Setting virtual image type to DD\n") } else if(strcmp(pp_argv[i],"dmg")==0) { - glob_xmount.VirtImageType=VirtImageType_DMG; + glob_xmount.output.VirtImageType=VirtImageType_DMG; LOG_DEBUG("Setting virtual image type to DMG\n") } else if(strcmp(pp_argv[i],"vdi")==0) { - glob_xmount.VirtImageType=VirtImageType_VDI; + glob_xmount.output.VirtImageType=VirtImageType_VDI; LOG_DEBUG("Setting virtual image type to VDI\n") } else if(strcmp(pp_argv[i],"vhd")==0) { - glob_xmount.VirtImageType=VirtImageType_VHD; + glob_xmount.output.VirtImageType=VirtImageType_VHD; LOG_DEBUG("Setting virtual image type to VHD\n") } else if(strcmp(pp_argv[i],"vmdk")==0) { - glob_xmount.VirtImageType=VirtImageType_VMDK; + glob_xmount.output.VirtImageType=VirtImageType_VMDK; LOG_DEBUG("Setting virtual image type to VMDK\n") } else if(strcmp(pp_argv[i],"vmdks")==0) { - glob_xmount.VirtImageType=VirtImageType_VMDKS; + glob_xmount.output.VirtImageType=VirtImageType_VMDKS; LOG_DEBUG("Setting virtual image type to VMDKS\n") } else { LOG_ERROR("Unknown output image type \"%s\"!\n",pp_argv[i]) return FALSE; } } else { LOG_ERROR("You must specify an output image type!\n"); return FALSE; } } else if(strcmp(pp_argv[i],"--owcache")==0) { // Enable writable access to mounted image and overwrite existing cache // Next parameter must be cache file to read/write changes from/to if((argc+1)>i) { i++; XMOUNT_STRSET(glob_xmount.p_cache_file,pp_argv[i]) - glob_xmount.writable=TRUE; + glob_xmount.output.writable=TRUE; glob_xmount.overwrite_cache=TRUE; } else { LOG_ERROR("You must specify a cache file!\n") return FALSE; } LOG_DEBUG("Enabling virtual write support overwriting cache file %s\n", glob_xmount.p_cache_file) } else if(strcmp(pp_argv[i],"--version")==0 || strcmp(pp_argv[i],"--info")==0) { printf(XMOUNT_COPYRIGHT_NOTICE "\n\n",XMOUNT_VERSION); #ifdef __GNUC__ printf(" compile timestamp: %s %s\n",__DATE__,__TIME__); printf(" gcc version: %s\n",__VERSION__); #endif printf(" loaded input libraries:\n"); - for(uint32_t ii=0;iip_name); - p_buf=glob_pp_input_libs[ii]->p_supported_input_types; + for(uint32_t ii=0;iip_name); + p_buf=glob_xmount.input.pp_libs[ii]->p_supported_input_types; first=TRUE; while(*p_buf!='\0') { if(first) { printf("\"%s\"",p_buf); first=FALSE; } else printf(", \"%s\"",p_buf); p_buf+=(strlen(p_buf)+1); } printf("\n"); } printf(" loaded morphing libraries:\n"); - for(uint32_t ii=0;iip_name); - p_buf=glob_pp_morphing_libs[ii]->p_supported_morphing_types; + for(uint32_t ii=0;iip_name); + p_buf=glob_xmount.morphing.pp_libs[ii]->p_supported_morphing_types; first=TRUE; while(*p_buf!='\0') { if(first) { printf("\"%s\"",p_buf); first=FALSE; } else printf(", \"%s\"",p_buf); p_buf+=(strlen(p_buf)+1); } printf("\n"); } printf("\n"); exit(0); } else if(strcmp(pp_argv[i],"--offset")==0) { if((argc+1)>i) { i++; - glob_xmount.orig_img_offset=strtoull(pp_argv[i],NULL,10); + glob_xmount.input.image_offset=strtoull(pp_argv[i],NULL,10); } else { LOG_ERROR("You must specify an offset!\n") return FALSE; } LOG_DEBUG("Setting input image offset to \"%" PRIu64 "\"\n", - glob_xmount.orig_img_offset) + glob_xmount.input.image_offset) } else { LOG_ERROR("Unknown command line option \"%s\"\n",pp_argv[i]); return FALSE; } } i++; } // Extract mountpoint if(i==(argc-1)) { XMOUNT_STRSET(*pp_mountpoint,pp_argv[argc-1]) opts++; XMOUNT_REALLOC(*ppp_nargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*ppp_nargv)[opts-1],*pp_mountpoint) } else { LOG_ERROR("No mountpoint specified!\n") return FALSE; } if(FuseMinusOControl==TRUE) { // We control the -o flag, set subtype, fsname and allow_other options opts+=2; XMOUNT_REALLOC(*ppp_nargv,char**,opts*sizeof(char*)); XMOUNT_STRSET((*ppp_nargv)[opts-2],"-o"); XMOUNT_STRSET((*ppp_nargv)[opts-1],"subtype=xmount"); - if(glob_xmount.input_images_count!=0) { + if(glob_xmount.input.images_count!=0) { // Set name of first source file as fsname XMOUNT_STRAPP((*ppp_nargv)[opts-1],",fsname="); XMOUNT_STRAPP((*ppp_nargv)[opts-1], - glob_xmount.pp_input_images[0]->pp_files[0]); + glob_xmount.input.pp_images[0]->pp_files[0]); } if(FuseAllowOther==TRUE) { // Add "allow_other" option if allowed if(glob_xmount.may_set_fuse_allow_other) { XMOUNT_STRAPP((*ppp_nargv)[opts-1],",allow_other") } } } *p_nargv=opts; return TRUE; } //! Extract virtual file name from input image name /*! * \param p_orig_name Name of input image (may include a path) * \return TRUE on success, FALSE on error */ static int ExtractVirtFileNames(char *p_orig_name) { char *tmp; // Truncate any leading path tmp=strrchr(p_orig_name,'/'); if(tmp!=NULL) p_orig_name=tmp+1; // Extract file extension tmp=strrchr(p_orig_name,'.'); // Set leading '/' - XMOUNT_STRSET(glob_xmount.p_virtual_image_path,"/") + XMOUNT_STRSET(glob_xmount.output.p_virtual_image_path,"/") XMOUNT_STRSET(glob_xmount.p_virtual_info_path,"/") - if(glob_xmount.VirtImageType==VirtImageType_VMDK || - glob_xmount.VirtImageType==VirtImageType_VMDKS) + if(glob_xmount.output.VirtImageType==VirtImageType_VMDK || + glob_xmount.output.VirtImageType==VirtImageType_VMDKS) { - XMOUNT_STRSET(glob_xmount.p_virtual_vmdk_path,"/") + XMOUNT_STRSET(glob_xmount.output.vmdk.p_virtual_vmdk_path,"/") } // Copy filename if(tmp==NULL) { // Input image filename has no extension - XMOUNT_STRAPP(glob_xmount.p_virtual_image_path,p_orig_name) + XMOUNT_STRAPP(glob_xmount.output.p_virtual_image_path,p_orig_name) XMOUNT_STRAPP(glob_xmount.p_virtual_info_path,p_orig_name) - if(glob_xmount.VirtImageType==VirtImageType_VMDK || - glob_xmount.VirtImageType==VirtImageType_VMDKS) + if(glob_xmount.output.VirtImageType==VirtImageType_VMDK || + glob_xmount.output.VirtImageType==VirtImageType_VMDKS) { - XMOUNT_STRAPP(glob_xmount.p_virtual_vmdk_path,p_orig_name) + XMOUNT_STRAPP(glob_xmount.output.vmdk.p_virtual_vmdk_path,p_orig_name) } XMOUNT_STRAPP(glob_xmount.p_virtual_info_path,".info") } else { - XMOUNT_STRNAPP(glob_xmount.p_virtual_image_path,p_orig_name, + XMOUNT_STRNAPP(glob_xmount.output.p_virtual_image_path,p_orig_name, strlen(p_orig_name)-strlen(tmp)) XMOUNT_STRNAPP(glob_xmount.p_virtual_info_path,p_orig_name, strlen(p_orig_name)-strlen(tmp)) - if(glob_xmount.VirtImageType==VirtImageType_VMDK || - glob_xmount.VirtImageType==VirtImageType_VMDKS) + if(glob_xmount.output.VirtImageType==VirtImageType_VMDK || + glob_xmount.output.VirtImageType==VirtImageType_VMDKS) { - XMOUNT_STRNAPP(glob_xmount.p_virtual_vmdk_path,p_orig_name, + XMOUNT_STRNAPP(glob_xmount.output.vmdk.p_virtual_vmdk_path,p_orig_name, strlen(p_orig_name)-strlen(tmp)) } XMOUNT_STRAPP(glob_xmount.p_virtual_info_path,".info") } // Add virtual file extensions - switch(glob_xmount.VirtImageType) { + switch(glob_xmount.output.VirtImageType) { case VirtImageType_DD: - XMOUNT_STRAPP(glob_xmount.p_virtual_image_path,".dd") + XMOUNT_STRAPP(glob_xmount.output.p_virtual_image_path,".dd") break; case VirtImageType_DMG: - XMOUNT_STRAPP(glob_xmount.p_virtual_image_path,".dmg") + XMOUNT_STRAPP(glob_xmount.output.p_virtual_image_path,".dmg") break; case VirtImageType_VDI: - XMOUNT_STRAPP(glob_xmount.p_virtual_image_path,".vdi") + XMOUNT_STRAPP(glob_xmount.output.p_virtual_image_path,".vdi") break; case VirtImageType_VHD: - XMOUNT_STRAPP(glob_xmount.p_virtual_image_path,".vhd") + XMOUNT_STRAPP(glob_xmount.output.p_virtual_image_path,".vhd") break; case VirtImageType_VMDK: case VirtImageType_VMDKS: - XMOUNT_STRAPP(glob_xmount.p_virtual_image_path,".dd") - XMOUNT_STRAPP(glob_xmount.p_virtual_vmdk_path,".vmdk") + XMOUNT_STRAPP(glob_xmount.output.p_virtual_image_path,".dd") + XMOUNT_STRAPP(glob_xmount.output.vmdk.p_virtual_vmdk_path,".vmdk") break; default: LOG_ERROR("Unknown virtual image type!\n") return FALSE; } LOG_DEBUG("Set virtual image name to \"%s\"\n", - glob_xmount.p_virtual_image_path) + glob_xmount.output.p_virtual_image_path) LOG_DEBUG("Set virtual image info name to \"%s\"\n", glob_xmount.p_virtual_info_path) - if(glob_xmount.VirtImageType==VirtImageType_VMDK || - glob_xmount.VirtImageType==VirtImageType_VMDKS) + if(glob_xmount.output.VirtImageType==VirtImageType_VMDK || + glob_xmount.output.VirtImageType==VirtImageType_VMDKS) { LOG_DEBUG("Set virtual vmdk name to \"%s\"\n", - glob_xmount.p_virtual_vmdk_path) + glob_xmount.output.vmdk.p_virtual_vmdk_path) } return TRUE; } //! Get size of morphed image /*! * \param p_size Buf to save size to * \return TRUE on success, FALSE on error */ static int GetMorphedImageSize(uint64_t *p_size) { int ret; - ret=glob_p_morphing_functions->Size(glob_p_morphing_handle,p_size); + ret=glob_xmount.morphing.p_functions->Size(glob_xmount.morphing.p_handle, + p_size); if(ret!=0) { LOG_ERROR("Unable to get morphed image size: %s!\n", - glob_p_morphing_functions->GetErrorMessage(ret)); + glob_xmount.morphing.p_functions->GetErrorMessage(ret)); return FALSE; } return TRUE; } //! Get size of virtual image /*! * \param p_size Pointer to an uint64_t to which the size will be written to * \return TRUE on success, FALSE on error */ static int GetVirtImageSize(uint64_t *p_size) { - if(glob_xmount.virt_image_size!=0) { - *p_size=glob_xmount.virt_image_size; + if(glob_xmount.output.image_size!=0) { + *p_size=glob_xmount.output.image_size; return TRUE; } - switch(glob_xmount.VirtImageType) { + switch(glob_xmount.output.VirtImageType) { case VirtImageType_DD: case VirtImageType_DMG: case VirtImageType_VMDK: case VirtImageType_VMDKS: // Virtual image is a DD, DMG or VMDK file. Just return the size of the // original image if(!GetMorphedImageSize(p_size)) { LOG_ERROR("Couldn't get size of input image!\n") return FALSE; } break; case VirtImageType_VDI: // Virtual image is a VDI file. Get size of original image and add size // of VDI header etc. if(!GetMorphedImageSize(p_size)) { LOG_ERROR("Couldn't get size of input image!\n") return FALSE; } - (*p_size)+=(sizeof(ts_VdiFileHeader)+glob_p_vdi_block_map_size); + (*p_size)+=(sizeof(ts_VdiFileHeader)+ + glob_xmount.output.vdi.vdi_block_map_size); break; case VirtImageType_VHD: // Virtual image is a VHD file. Get size of original image and add size // of VHD footer. if(!GetMorphedImageSize(p_size)) { LOG_ERROR("Couldn't get size of input image!\n") return FALSE; } (*p_size)+=sizeof(ts_VhdFileHeader); break; default: LOG_ERROR("Unsupported image type!\n") return FALSE; } - glob_xmount.virt_image_size=*p_size; + glob_xmount.output.image_size=*p_size; return TRUE; } //! Read data from input image /*! * \param p_image Image from which to read data * \param p_buf Pointer to buffer to write read data to (must be preallocated!) * \param offset Offset at which data should be read * \param size Size of data which should be read (size of buffer) * \param p_read Number of read bytes on success * \return 0 on success, negated error code on error */ static int GetInputImageData(pts_InputImage p_image, char *p_buf, off_t offset, size_t size, size_t *p_read) { int ret; size_t to_read=0; LOG_DEBUG("Trying to read %zu bytes at offset %zu from input image '%s'\n", size, offset, p_image->pp_files[0]); // Make sure we aren't reading past EOF of image file if(offset>=p_image->size) { // Offset is beyond image size LOG_DEBUG("Offset %zu is at / beyond size of input image '%s'.\n", offset, p_image->pp_files[0]); *p_read=0; return 0; } if(offset+size>p_image->size) { // Attempt to read data past EOF of image file to_read=p_image->size-offset; LOG_DEBUG("Attempt to read data past EOF of input image '%s'. " "Corrected size from %zu to %zu.\n", p_image->pp_files[0], size, to_read); } else to_read=size; // Read data from image file (adding input image offset if one was specified) ret=p_image->p_functions->Read(p_image->p_handle, - offset+glob_xmount.orig_img_offset, + offset+glob_xmount.input.image_offset, p_buf, to_read); if(ret!=0) { LOG_ERROR("Couldn't read %zu bytes at offset %zu from input image " "'%s': %s!\n", to_read, offset, p_image->pp_files[0], p_image->p_functions->GetErrorMessage(ret)); return -EIO; } *p_read=to_read; return 0; } -//! Wrapper for GetInputImageData -static int GetInputImageData_MorphWrapper(void *p_image, - char *p_buf, - off_t offset, - size_t size, - size_t *p_read) -{ - return GetInputImageData((pts_InputImage)p_image,p_buf,offset,size,p_read); -} - //! Read data from morphed image /*! * \param p_buf Pointer to buffer to write read data to (must be preallocated!) * \param offset Offset at which data should be read * \param size Size of data which should be read (size of buffer) * \param p_read Number of read bytes on success * \return TRUE on success, negated error code on error */ static int GetMorphedImageData(char *p_buf, off_t offset, size_t size, size_t *p_read) { int ret; size_t to_read=0; size_t read; uint64_t image_size=0; // Make sure we aren't reading past EOF of image file if(GetMorphedImageSize(&image_size)!=TRUE) { LOG_ERROR("Couldn't get size of morphed image!\n"); return -EIO; } if(offset>=image_size) { // Offset is beyond image size LOG_DEBUG("Offset %zu is at / beyond size of morphed image.\n",offset); *p_read=0; return 0; } if(offset+size>image_size) { // Attempt to read data past EOF of morphed image file to_read=image_size-offset; LOG_DEBUG("Attempt to read data past EOF of morphed image. Corrected size " "from %zu to %zu.\n", size, to_read); } else to_read=size; // Read data from morphed image - ret=glob_p_morphing_functions->Read(glob_p_morphing_handle, - p_buf, - offset, - to_read, - &read); + ret=glob_xmount.morphing.p_functions->Read(glob_xmount.morphing.p_handle, + p_buf, + offset, + to_read, + &read); if(ret!=0) { LOG_ERROR("Couldn't read %zu bytes at offset %zu from morphed image: %s!\n", to_read, offset, - glob_p_morphing_functions->GetErrorMessage(ret)); + glob_xmount.morphing.p_functions->GetErrorMessage(ret)); return -EIO; } *p_read=to_read; return TRUE; } //! Read data from virtual image /*! * \param p_buf Pointer to buffer to write read data to * \param offset Offset at which data should be read * \param size Size of data which should be read * \return Number of read bytes on success or negated error code on error */ static int GetVirtImageData(char *p_buf, off_t offset, size_t size) { uint32_t cur_block=0; uint64_t morphed_image_size, virt_image_size; size_t read, to_read=0, cur_to_read=0; off_t file_off=offset, block_off=0; size_t to_read_later=0; int ret; // Get virtual image size if(GetVirtImageSize(&virt_image_size)!=TRUE) { LOG_ERROR("Couldn't get size of virtual image!\n") return -EIO; } if(offset>=virt_image_size) { LOG_DEBUG("Offset %zu is at / beyond size of virtual image.\n",offset); return 0; } if(offset+size>virt_image_size) { LOG_DEBUG("Attempt to read data past EOF of virtual image. Corrected size " "from %zu to %zu.\n", size, virt_image_size-offset); size=virt_image_size-offset; } to_read=size; // Get morphed image size if(GetMorphedImageSize(&morphed_image_size)!=TRUE) { LOG_ERROR("Couldn't get morphed image size!") return -EIO; } // Read virtual image type specific data preceeding morphed image data - switch(glob_xmount.VirtImageType) { + switch(glob_xmount.output.VirtImageType) { case VirtImageType_DD: case VirtImageType_DMG: case VirtImageType_VMDK: case VirtImageType_VMDKS: break; case VirtImageType_VDI: - if(file_offglob_vdi_header_size) { - cur_to_read=glob_vdi_header_size-file_off; + if(file_offglob_xmount.output.vdi.vdi_header_size) { + cur_to_read=glob_xmount.output.vdi.vdi_header_size-file_off; } else { cur_to_read=to_read; } - if(glob_xmount.writable==TRUE && + if(glob_xmount.output.writable==TRUE && glob_p_cache_header->VdiFileHeaderCached==TRUE) { // VDI header was already cached if(fseeko(glob_p_cache_file, glob_p_cache_header->pVdiFileHeader+file_off, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to cached VDI header at offset %" PRIu64 "\n",glob_p_cache_header->pVdiFileHeader+file_off) return -EIO; } if(fread(p_buf,cur_to_read,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't read %zu bytes from cache file at offset %" PRIu64 "\n",cur_to_read, glob_p_cache_header->pVdiFileHeader+file_off) return -EIO; } LOG_DEBUG("Read %zd bytes from cached VDI header at offset %" PRIu64 " at cache file offset %" PRIu64 "\n", cur_to_read,file_off, glob_p_cache_header->pVdiFileHeader+file_off) } else { // VDI header isn't cached - memcpy(p_buf,((char*)glob_p_vdi_header)+file_off,cur_to_read); + memcpy(p_buf, + ((char*)glob_xmount.output.vdi.p_vdi_header)+file_off, + cur_to_read); LOG_DEBUG("Read %zd bytes at offset %" PRIu64 " from virtual VDI header\n",cur_to_read, file_off) } if(to_read==cur_to_read) return to_read; else { // Adjust values to read from morphed image to_read-=cur_to_read; p_buf+=cur_to_read; file_off=0; } - } else file_off-=glob_vdi_header_size; + } else file_off-=glob_xmount.output.vdi.vdi_header_size; break; case VirtImageType_VHD: // When emulating VHD, make sure the while loop below only reads data // available in the morphed image. Any VHD footer data must be read // afterwards. if(file_off>=morphed_image_size) { to_read_later=to_read; to_read=0; } else if((file_off+to_read)>morphed_image_size) { to_read_later=(file_off+to_read)-morphed_image_size; to_read-=to_read_later; } break; } // Calculate block to read data from cur_block=file_off/CACHE_BLOCK_SIZE; block_off=file_off%CACHE_BLOCK_SIZE; // Read image data while(to_read!=0) { // Calculate how many bytes we have to read from this block if(block_off+to_read>CACHE_BLOCK_SIZE) { cur_to_read=CACHE_BLOCK_SIZE-block_off; } else cur_to_read=to_read; - if(glob_xmount.writable==TRUE && + if(glob_xmount.output.writable==TRUE && glob_p_cache_blkidx[cur_block].Assigned==TRUE) { // Write support enabled and need to read altered data from cachefile if(fseeko(glob_p_cache_file, glob_p_cache_blkidx[cur_block].off_data+block_off, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to offset %" PRIu64 " in cache file\n") return -EIO; } if(fread(p_buf,cur_to_read,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't read data from cache file!\n") return -EIO; } LOG_DEBUG("Read %zd bytes at offset %" PRIu64 " from cache file\n",cur_to_read,file_off) } else { // No write support or data not cached ret=GetMorphedImageData(p_buf,file_off,cur_to_read,&read); if(ret!=TRUE || read!=cur_to_read) { LOG_ERROR("Couldn't read data from virtual image!\n") return -EIO; } LOG_DEBUG("Read %zu bytes at offset %zu from virtual image file\n", cur_to_read, file_off); } cur_block++; block_off=0; p_buf+=cur_to_read; to_read-=cur_to_read; file_off+=cur_to_read; } if(to_read_later!=0) { // Read virtual image type specific data following morphed image data - switch(glob_xmount.VirtImageType) { + switch(glob_xmount.output.VirtImageType) { case VirtImageType_DD: case VirtImageType_DMG: case VirtImageType_VMDK: case VirtImageType_VMDKS: case VirtImageType_VDI: break; case VirtImageType_VHD: // Micro$oft has choosen to use a footer rather then a header. - if(glob_xmount.writable==TRUE && + if(glob_xmount.output.writable==TRUE && glob_p_cache_header->VhdFileHeaderCached==TRUE) { // VHD footer was already cached if(fseeko(glob_p_cache_file, glob_p_cache_header->pVhdFileHeader+ (file_off-morphed_image_size), SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to cached VHD footer at offset %" PRIu64 "\n", glob_p_cache_header->pVhdFileHeader+ (file_off-morphed_image_size)) return -EIO; } if(fread(p_buf,to_read_later,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't read %zu bytes from cache file at offset %" PRIu64 "\n",to_read_later, glob_p_cache_header->pVhdFileHeader+ (file_off-morphed_image_size)) return -EIO; } LOG_DEBUG("Read %zd bytes from cached VHD footer at offset %" PRIu64 " at cache file offset %" PRIu64 "\n", to_read_later,(file_off-morphed_image_size), glob_p_cache_header->pVhdFileHeader+ (file_off-morphed_image_size)) } else { // VHD header isn't cached memcpy(p_buf, - ((char*)glob_p_vhd_header)+(file_off-morphed_image_size), + ((char*)glob_xmount.output.vhd.p_vhd_header)+ + (file_off-morphed_image_size), to_read_later); LOG_DEBUG("Read %zd bytes at offset %" PRIu64 " from virtual VHD header\n", to_read_later, (file_off-morphed_image_size)) } break; } } return size; } //! Write data to virtual VDI file header /*! * \param p_buf Buffer containing data to write * \param offset Offset of changes * \param size Amount of bytes to write * \return Number of written bytes on success or "-1" on error */ static int SetVdiFileHeaderData(char *p_buf,off_t offset,size_t size) { - if(offset+size>glob_vdi_header_size) size=glob_vdi_header_size-offset; + if(offset+size>glob_xmount.output.vdi.vdi_header_size) { + size=glob_xmount.output.vdi.vdi_header_size-offset; + } + LOG_DEBUG("Need to cache %zu bytes at offset %" PRIu64 - " from VDI header\n",size,offset) + " from VDI header\n", + size, + offset); + if(glob_p_cache_header->VdiFileHeaderCached==1) { // Header was already cached if(fseeko(glob_p_cache_file, glob_p_cache_header->pVdiFileHeader+offset, SEEK_SET)!=0) { - LOG_ERROR("Couldn't seek to cached VDI header at address %" - PRIu64 "\n",glob_p_cache_header->pVdiFileHeader+offset) + LOG_ERROR("Couldn't seek to cached VDI header at address %" PRIu64 "\n", + glob_p_cache_header->pVdiFileHeader+offset) return -1; } if(fwrite(p_buf,size,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" - PRIu64 "\n",size, + PRIu64 "\n", + size, glob_p_cache_header->pVdiFileHeader+offset) return -1; } + LOG_DEBUG("Wrote %zd bytes at offset %" PRIu64 " to cache file\n", - size,glob_p_cache_header->pVdiFileHeader+offset) + size, + glob_p_cache_header->pVdiFileHeader+offset) } else { // Header wasn't already cached. if(fseeko(glob_p_cache_file, 0, SEEK_END)!=0) { LOG_ERROR("Couldn't seek to end of cache file!") return -1; } glob_p_cache_header->pVdiFileHeader=ftello(glob_p_cache_file); LOG_DEBUG("Caching whole VDI header\n") if(offset>0) { // Changes do not begin at offset 0, need to prepend with data from // VDI header - if(fwrite((char*)glob_p_vdi_header,offset,1,glob_p_cache_file)!=1) { + if(fwrite((char*)glob_xmount.output.vdi.p_vdi_header, + offset, + 1, + glob_p_cache_file)!=1) + { LOG_ERROR("Error while writing %" PRIu64 " bytes " - "to cache file at offset %" PRIu64 "!\n", + "to cache file at offset %" PRIu64 "!\n", offset, glob_p_cache_header->pVdiFileHeader); return -1; } LOG_DEBUG("Prepended changed data with %" PRIu64 - " bytes at cache file offset %" PRIu64 "\n", - offset,glob_p_cache_header->pVdiFileHeader) + " bytes at cache file offset %" PRIu64 "\n", + offset, + glob_p_cache_header->pVdiFileHeader) } // Cache changed data if(fwrite(p_buf,size,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" - PRIu64 "\n",size, + PRIu64 "\n",size, glob_p_cache_header->pVdiFileHeader+offset) return -1; } LOG_DEBUG("Wrote %zu bytes of changed data to cache file offset %" - PRIu64 "\n",size, + PRIu64 "\n", + size, glob_p_cache_header->pVdiFileHeader+offset) - if(offset+size!=glob_vdi_header_size) { + if(offset+size!=glob_xmount.output.vdi.vdi_header_size) { // Need to append data from VDI header to cache whole data struct - if(fwrite(((char*)glob_p_vdi_header)+offset+size, - glob_vdi_header_size-(offset+size), + if(fwrite(((char*)glob_xmount.output.vdi.p_vdi_header)+offset+size, + glob_xmount.output.vdi.vdi_header_size-(offset+size), 1, glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" - PRIu64 "\n",glob_vdi_header_size-(offset+size), + PRIu64 "\n",glob_xmount.output.vdi.vdi_header_size-(offset+size), (uint64_t)(glob_p_cache_header->pVdiFileHeader+offset+size)) return -1; } LOG_DEBUG("Appended %" PRIu32 " bytes to changed data at cache file offset %" - PRIu64 "\n",glob_vdi_header_size-(offset+size), + PRIu64 "\n",glob_xmount.output.vdi.vdi_header_size-(offset+size), glob_p_cache_header->pVdiFileHeader+offset+size) } // Mark header as cached and update header in cache file glob_p_cache_header->VdiFileHeaderCached=1; if(fseeko(glob_p_cache_file,0,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to offset 0 of cache file!\n") return -1; } if(fwrite((char*)glob_p_cache_header, sizeof(ts_CacheFileHeader), 1, glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write changed cache file header!\n") return -1; } } // All important data has been written, now flush all buffers to make // sure data is written to cache file fflush(glob_p_cache_file); #ifndef __APPLE__ ioctl(fileno(glob_p_cache_file),BLKFLSBUF,0); #endif return size; } //! Write data to virtual VHD file footer /*! * \param p_buf Buffer containing data to write * \param offset Offset of changes * \param size Amount of bytes to write * \return Number of written bytes on success or "-1" on error */ static int SetVhdFileHeaderData(char *p_buf,off_t offset,size_t size) { LOG_DEBUG("Need to cache %zu bytes at offset %" PRIu64 " from VHD footer\n",size,offset) if(glob_p_cache_header->VhdFileHeaderCached==1) { // Header has already been cached if(fseeko(glob_p_cache_file, glob_p_cache_header->pVhdFileHeader+offset, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to cached VHD header at address %" PRIu64 "\n",glob_p_cache_header->pVhdFileHeader+offset) return -1; } if(fwrite(p_buf,size,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" PRIu64 "\n",size, glob_p_cache_header->pVhdFileHeader+offset) return -1; } LOG_DEBUG("Wrote %zd bytes at offset %" PRIu64 " to cache file\n", size,glob_p_cache_header->pVhdFileHeader+offset) } else { // Header hasn't been cached yet. if(fseeko(glob_p_cache_file, 0, SEEK_END)!=0) { LOG_ERROR("Couldn't seek to end of cache file!") return -1; } glob_p_cache_header->pVhdFileHeader=ftello(glob_p_cache_file); LOG_DEBUG("Caching whole VHD header\n") if(offset>0) { // Changes do not begin at offset 0, need to prepend with data from // VHD header - if(fwrite((char*)glob_p_vhd_header,offset,1,glob_p_cache_file)!=1) { + if(fwrite((char*)glob_xmount.output.vhd.p_vhd_header, + offset, + 1, + glob_p_cache_file)!=1) + { LOG_ERROR("Error while writing %" PRIu64 " bytes " "to cache file at offset %" PRIu64 "!\n", offset, glob_p_cache_header->pVhdFileHeader); return -1; } LOG_DEBUG("Prepended changed data with %" PRIu64 " bytes at cache file offset %" PRIu64 "\n", offset,glob_p_cache_header->pVhdFileHeader) } // Cache changed data if(fwrite(p_buf,size,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" PRIu64 "\n",size, glob_p_cache_header->pVhdFileHeader+offset) return -1; } LOG_DEBUG("Wrote %zu bytes of changed data to cache file offset %" PRIu64 "\n",size, glob_p_cache_header->pVhdFileHeader+offset) if(offset+size!=sizeof(ts_VhdFileHeader)) { // Need to append data from VHD header to cache whole data struct - if(fwrite(((char*)glob_p_vhd_header)+offset+size, + if(fwrite(((char*)glob_xmount.output.vhd.p_vhd_header)+offset+size, sizeof(ts_VhdFileHeader)-(offset+size), 1, glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" PRIu64 "\n",sizeof(ts_VhdFileHeader)-(offset+size), (uint64_t)(glob_p_cache_header->pVhdFileHeader+offset+size)) return -1; } LOG_DEBUG("Appended %" PRIu32 " bytes to changed data at cache file offset %" PRIu64 "\n",sizeof(ts_VhdFileHeader)-(offset+size), glob_p_cache_header->pVhdFileHeader+offset+size) } // Mark header as cached and update header in cache file glob_p_cache_header->VhdFileHeaderCached=1; if(fseeko(glob_p_cache_file,0,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to offset 0 of cache file!\n") return -1; } if(fwrite((char*)glob_p_cache_header, sizeof(ts_CacheFileHeader), 1, glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write changed cache file header!\n") return -1; } } // All important data has been written, now flush all buffers to make // sure data is written to cache file fflush(glob_p_cache_file); #ifndef __APPLE__ ioctl(fileno(glob_p_cache_file),BLKFLSBUF,0); #endif return size; } //! Write data to virtual image /*! * \param p_buf Buffer containing data to write * \param offset Offset to start writing at * \param size Size of data to be written * \return Number of written bytes on success or "-1" on error */ static int SetVirtImageData(const char *p_buf, off_t offset, size_t size) { uint64_t cur_block=0; uint64_t virt_image_size; uint64_t orig_image_size; size_t to_write=0; size_t to_write_later=0; size_t to_write_now=0; off_t file_offset=offset; off_t block_offset=0; char *p_write_buf=(char*)p_buf; char *p_buf2; int ret; size_t read; // Get virtual image size if(!GetVirtImageSize(&virt_image_size)) { LOG_ERROR("Couldn't get virtual image size!\n") return -1; } if(offset>=virt_image_size) { LOG_ERROR("Attempt to write beyond EOF of virtual image file!\n") return -1; } if(offset+size>virt_image_size) { LOG_DEBUG("Attempt to write past EOF of virtual image file\n") size=virt_image_size-offset; } to_write=size; // Get original image size if(!GetMorphedImageSize(&orig_image_size)) { LOG_ERROR("Couldn't get morphed image size!\n") return -1; } // Cache virtual image type specific data preceeding original image data - switch(glob_xmount.VirtImageType) { + switch(glob_xmount.output.VirtImageType) { case VirtImageType_DD: case VirtImageType_DMG: case VirtImageType_VMDK: case VirtImageType_VMDKS: break; case VirtImageType_VDI: - if(file_offset=orig_image_size) { to_write_later=to_write; to_write=0; } else if((file_offset+to_write)>orig_image_size) { to_write_later=(file_offset+to_write)-orig_image_size; to_write-=to_write_later; } break; } // Calculate block to write data to cur_block=file_offset/CACHE_BLOCK_SIZE; block_offset=file_offset%CACHE_BLOCK_SIZE; while(to_write!=0) { // Calculate how many bytes we have to write to this block if(block_offset+to_write>CACHE_BLOCK_SIZE) { to_write_now=CACHE_BLOCK_SIZE-block_offset; } else to_write_now=to_write; if(glob_p_cache_blkidx[cur_block].Assigned==1) { // Block was already cached // Seek to data offset in cache file if(fseeko(glob_p_cache_file, glob_p_cache_blkidx[cur_block].off_data+block_offset, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to cached block at address %" PRIu64 "\n", glob_p_cache_blkidx[cur_block].off_data+block_offset) return -1; } if(fwrite(p_write_buf,to_write_now,1,glob_p_cache_file)!=1) { LOG_ERROR("Error while writing %zu bytes " "to cache file at offset %" PRIu64 "!\n", to_write_now, glob_p_cache_blkidx[cur_block].off_data+block_offset); return -1; } LOG_DEBUG("Wrote %zd bytes at offset %" PRIu64 " to cache file\n",to_write_now, glob_p_cache_blkidx[cur_block].off_data+block_offset) } else { // Uncached block. Need to cache entire new block // Seek to end of cache file to append new cache block fseeko(glob_p_cache_file,0,SEEK_END); glob_p_cache_blkidx[cur_block].off_data=ftello(glob_p_cache_file); if(block_offset!=0) { // Changed data does not begin at block boundry. Need to prepend // with data from virtual image file XMOUNT_MALLOC(p_buf2,char*,block_offset*sizeof(char)); ret=GetMorphedImageData(p_buf2, file_offset-block_offset, block_offset, &read); if(ret!=TRUE || read!=block_offset) { LOG_ERROR("Couldn't read data from morphed image!\n") return -1; } if(fwrite(p_buf2,block_offset,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't writing %" PRIu64 " bytes " "to cache file at offset %" PRIu64 "!\n", block_offset, glob_p_cache_blkidx[cur_block].off_data); return -1; } LOG_DEBUG("Prepended changed data with %" PRIu64 " bytes from virtual image file at offset %" PRIu64 "\n",block_offset,file_offset-block_offset) free(p_buf2); } if(fwrite(p_write_buf,to_write_now,1,glob_p_cache_file)!=1) { LOG_ERROR("Error while writing %zd bytes " "to cache file at offset %" PRIu64 "!\n", to_write_now, glob_p_cache_blkidx[cur_block].off_data+block_offset); return -1; } if(block_offset+to_write_now!=CACHE_BLOCK_SIZE) { // Changed data does not end at block boundry. Need to append // with data from virtual image file XMOUNT_MALLOC(p_buf2,char*,(CACHE_BLOCK_SIZE- (block_offset+to_write_now))*sizeof(char)) memset(p_buf2,0,CACHE_BLOCK_SIZE-(block_offset+to_write_now)); if((file_offset-block_offset)+CACHE_BLOCK_SIZE>orig_image_size) { // Original image is smaller than full cache block ret=GetMorphedImageData(p_buf2, file_offset+to_write_now, orig_image_size-(file_offset+to_write_now), &read); if(ret!=TRUE || read!=orig_image_size-(file_offset+to_write_now)) { LOG_ERROR("Couldn't read data from virtual image file!\n") return -1; } } else { ret=GetMorphedImageData(p_buf2, file_offset+to_write_now, CACHE_BLOCK_SIZE-(block_offset+to_write_now), &read); if(ret!=TRUE || read!=CACHE_BLOCK_SIZE-(block_offset+to_write_now)) { LOG_ERROR("Couldn't read data from virtual image file!\n") return -1; } } if(fwrite(p_buf2, CACHE_BLOCK_SIZE-(block_offset+to_write_now), 1, glob_p_cache_file)!=1) { LOG_ERROR("Error while writing %zd bytes " "to cache file at offset %" PRIu64 "!\n", CACHE_BLOCK_SIZE-(block_offset+to_write_now), glob_p_cache_blkidx[cur_block].off_data+ block_offset+to_write_now); return -1; } free(p_buf2); } // All important data for this cache block has been written, // flush all buffers and mark cache block as assigned fflush(glob_p_cache_file); #ifndef __APPLE__ ioctl(fileno(glob_p_cache_file),BLKFLSBUF,0); #endif glob_p_cache_blkidx[cur_block].Assigned=1; // Update cache block index entry in cache file fseeko(glob_p_cache_file, sizeof(ts_CacheFileHeader)+ (cur_block*sizeof(ts_CacheFileBlockIndex)), SEEK_SET); if(fwrite(&(glob_p_cache_blkidx[cur_block]), sizeof(ts_CacheFileBlockIndex), 1, glob_p_cache_file)!=1) { LOG_ERROR("Couldn't update cache file block index!\n"); return -1; } LOG_DEBUG("Updated cache file block index: Number=%" PRIu64 ", Data offset=%" PRIu64 "\n",cur_block, glob_p_cache_blkidx[cur_block].off_data); } // Flush buffers fflush(glob_p_cache_file); #ifndef __APPLE__ ioctl(fileno(glob_p_cache_file),BLKFLSBUF,0); #endif block_offset=0; cur_block++; p_write_buf+=to_write_now; to_write-=to_write_now; file_offset+=to_write_now; } if(to_write_later!=0) { // Cache virtual image type specific data preceeding original image data - switch(glob_xmount.VirtImageType) { + switch(glob_xmount.output.VirtImageType) { case VirtImageType_DD: case VirtImageType_DMG: case VirtImageType_VMDK: case VirtImageType_VMDKS: case VirtImageType_VDI: break; case VirtImageType_VHD: // Micro$oft has choosen to use a footer rather then a header. ret=SetVhdFileHeaderData(p_write_buf, file_offset-orig_image_size, to_write_later); if(ret==-1) { LOG_ERROR("Couldn't write data to virtual VHD file footer!\n") return -1; } break; } } return size; } //! Calculates an MD5 hash of the first HASH_AMOUNT bytes of the input image /*! * \param p_hash_low Pointer to the lower 64 bit of the hash * \param p_hash_high Pointer to the higher 64 bit of the hash * \return TRUE on success, FALSE on error */ static int CalculateInputImageHash(uint64_t *p_hash_low, uint64_t *p_hash_high) { char hash[16]; md5_state_t md5_state; char *p_buf; int ret; size_t read_data; XMOUNT_MALLOC(p_buf,char*,HASH_AMOUNT*sizeof(char)); ret=GetMorphedImageData(p_buf,0,HASH_AMOUNT,&read_data); if(ret!=TRUE || read_data==0) { LOG_ERROR("Couldn't read data from morphed image file!\n") free(p_buf); return FALSE; } // Calculate MD5 hash md5_init(&md5_state); md5_append(&md5_state,(const md5_byte_t*)p_buf,read_data); md5_finish(&md5_state,(md5_byte_t*)hash); // Convert MD5 hash into two 64bit integers *p_hash_low=*((uint64_t*)hash); *p_hash_high=*((uint64_t*)(hash+8)); free(p_buf); return TRUE; } //! Build and init virtual VDI file header /*! * \return TRUE on success, FALSE on error */ static int InitVirtVdiHeader() { // See http://forums.virtualbox.org/viewtopic.php?t=8046 for a // "description" of the various header fields uint64_t image_size; off_t offset; uint32_t i,block_entries; // Get input image size if(!GetMorphedImageSize(&image_size)) { LOG_ERROR("Couldn't get morphed image size!\n") return FALSE; } // Calculate how many VDI blocks we need block_entries=image_size/VDI_IMAGE_BLOCK_SIZE; if((image_size%VDI_IMAGE_BLOCK_SIZE)!=0) block_entries++; - glob_p_vdi_block_map_size=block_entries*sizeof(uint32_t); + glob_xmount.output.vdi.vdi_block_map_size=block_entries*sizeof(uint32_t); LOG_DEBUG("BlockMap: %d (%08X) entries, %d (%08X) bytes!\n", block_entries, block_entries, - glob_p_vdi_block_map_size, - glob_p_vdi_block_map_size) + glob_xmount.output.vdi.vdi_block_map_size, + glob_xmount.output.vdi.vdi_block_map_size) // Allocate memory for vdi header and block map - glob_vdi_header_size=sizeof(ts_VdiFileHeader)+glob_p_vdi_block_map_size; - XMOUNT_MALLOC(glob_p_vdi_header,pts_VdiFileHeader,glob_vdi_header_size) - memset(glob_p_vdi_header,0,glob_vdi_header_size); - glob_p_vdi_block_map=((void*)glob_p_vdi_header)+sizeof(ts_VdiFileHeader); + glob_xmount.output.vdi.vdi_header_size= + sizeof(ts_VdiFileHeader)+glob_xmount.output.vdi.vdi_block_map_size; + XMOUNT_MALLOC(glob_xmount.output.vdi.p_vdi_header, + pts_VdiFileHeader, + glob_xmount.output.vdi.vdi_header_size); + memset(glob_xmount.output.vdi.p_vdi_header, + 0, + glob_xmount.output.vdi.vdi_header_size); + glob_xmount.output.vdi.p_vdi_block_map= + ((void*)glob_xmount.output.vdi.p_vdi_header)+sizeof(ts_VdiFileHeader); // Init header values - strncpy(glob_p_vdi_header->szFileInfo,VDI_FILE_COMMENT, + strncpy(glob_xmount.output.vdi.p_vdi_header->szFileInfo, + VDI_FILE_COMMENT, strlen(VDI_FILE_COMMENT)+1); - glob_p_vdi_header->u32Signature=VDI_IMAGE_SIGNATURE; - glob_p_vdi_header->u32Version=VDI_IMAGE_VERSION; + glob_xmount.output.vdi.p_vdi_header->u32Signature=VDI_IMAGE_SIGNATURE; + glob_xmount.output.vdi.p_vdi_header->u32Version=VDI_IMAGE_VERSION; // No idea what the following value is for! Testimage had same value - glob_p_vdi_header->cbHeader=0x00000180; - glob_p_vdi_header->u32Type=VDI_IMAGE_TYPE_FIXED; - glob_p_vdi_header->fFlags=VDI_IMAGE_FLAGS; - strncpy(glob_p_vdi_header->szComment,VDI_HEADER_COMMENT, + glob_xmount.output.vdi.p_vdi_header->cbHeader=0x00000180; + glob_xmount.output.vdi.p_vdi_header->u32Type=VDI_IMAGE_TYPE_FIXED; + glob_xmount.output.vdi.p_vdi_header->fFlags=VDI_IMAGE_FLAGS; + strncpy(glob_xmount.output.vdi.p_vdi_header->szComment, + VDI_HEADER_COMMENT, strlen(VDI_HEADER_COMMENT)+1); - glob_p_vdi_header->offData=glob_vdi_header_size; - glob_p_vdi_header->offBlocks=sizeof(ts_VdiFileHeader); - glob_p_vdi_header->cCylinders=0; // Legacy info - glob_p_vdi_header->cHeads=0; // Legacy info - glob_p_vdi_header->cSectors=0; // Legacy info - glob_p_vdi_header->cbSector=512; // Legacy info - glob_p_vdi_header->u32Dummy=0; - glob_p_vdi_header->cbDisk=image_size; + glob_xmount.output.vdi.p_vdi_header->offData= + glob_xmount.output.vdi.vdi_header_size; + glob_xmount.output.vdi.p_vdi_header->offBlocks=sizeof(ts_VdiFileHeader); + glob_xmount.output.vdi.p_vdi_header->cCylinders=0; // Legacy info + glob_xmount.output.vdi.p_vdi_header->cHeads=0; // Legacy info + glob_xmount.output.vdi.p_vdi_header->cSectors=0; // Legacy info + glob_xmount.output.vdi.p_vdi_header->cbSector=512; // Legacy info + glob_xmount.output.vdi.p_vdi_header->u32Dummy=0; + glob_xmount.output.vdi.p_vdi_header->cbDisk=image_size; // Seems as VBox is always using a 1MB blocksize - glob_p_vdi_header->cbBlock=VDI_IMAGE_BLOCK_SIZE; - glob_p_vdi_header->cbBlockExtra=0; - glob_p_vdi_header->cBlocks=block_entries; - glob_p_vdi_header->cBlocksAllocated=block_entries; + glob_xmount.output.vdi.p_vdi_header->cbBlock=VDI_IMAGE_BLOCK_SIZE; + glob_xmount.output.vdi.p_vdi_header->cbBlockExtra=0; + glob_xmount.output.vdi.p_vdi_header->cBlocks=block_entries; + glob_xmount.output.vdi.p_vdi_header->cBlocksAllocated=block_entries; // Use partial MD5 input file hash as creation UUID and generate a random // modification UUID. VBox won't accept immages where create and modify UUIDS // aren't set. - glob_p_vdi_header->uuidCreate_l=glob_xmount.input_hash_lo; - glob_p_vdi_header->uuidCreate_h=glob_xmount.input_hash_hi; + glob_xmount.output.vdi.p_vdi_header->uuidCreate_l= + glob_xmount.input.image_hash_lo; + glob_xmount.output.vdi.p_vdi_header->uuidCreate_h= + glob_xmount.input.image_hash_hi; #define rand64(var) { \ *((uint32_t*)&(var))=rand(); \ *(((uint32_t*)&(var))+1)=rand(); \ } - rand64(glob_p_vdi_header->uuidModify_l); - rand64(glob_p_vdi_header->uuidModify_h); + rand64(glob_xmount.output.vdi.p_vdi_header->uuidModify_l); + rand64(glob_xmount.output.vdi.p_vdi_header->uuidModify_h); #undef rand64 // Generate block map i=0; - for(offset=0;offsetcookie=VHD_IMAGE_HVAL_COOKIE; - glob_p_vhd_header->features=VHD_IMAGE_HVAL_FEATURES; - glob_p_vhd_header->file_format_version=VHD_IMAGE_HVAL_FILE_FORMAT_VERSION; - glob_p_vhd_header->data_offset=VHD_IMAGE_HVAL_DATA_OFFSET; - glob_p_vhd_header->creation_time=htobe32(time(NULL)- - VHD_IMAGE_TIME_CONVERSION_OFFSET); - glob_p_vhd_header->creator_app=VHD_IMAGE_HVAL_CREATOR_APPLICATION; - glob_p_vhd_header->creator_ver=VHD_IMAGE_HVAL_CREATOR_VERSION; - glob_p_vhd_header->creator_os=VHD_IMAGE_HVAL_CREATOR_HOST_OS; - glob_p_vhd_header->size_original=htobe64(orig_image_size); - glob_p_vhd_header->size_current=glob_p_vhd_header->size_original; + glob_xmount.output.vhd.p_vhd_header->cookie=VHD_IMAGE_HVAL_COOKIE; + glob_xmount.output.vhd.p_vhd_header->features=VHD_IMAGE_HVAL_FEATURES; + glob_xmount.output.vhd.p_vhd_header->file_format_version= + VHD_IMAGE_HVAL_FILE_FORMAT_VERSION; + glob_xmount.output.vhd.p_vhd_header->data_offset=VHD_IMAGE_HVAL_DATA_OFFSET; + glob_xmount.output.vhd.p_vhd_header->creation_time= + htobe32(time(NULL)-VHD_IMAGE_TIME_CONVERSION_OFFSET); + glob_xmount.output.vhd.p_vhd_header->creator_app= + VHD_IMAGE_HVAL_CREATOR_APPLICATION; + glob_xmount.output.vhd.p_vhd_header->creator_ver= + VHD_IMAGE_HVAL_CREATOR_VERSION; + glob_xmount.output.vhd.p_vhd_header->creator_os= + VHD_IMAGE_HVAL_CREATOR_HOST_OS; + glob_xmount.output.vhd.p_vhd_header->size_original=htobe64(orig_image_size); + glob_xmount.output.vhd.p_vhd_header->size_current= + glob_xmount.output.vhd.p_vhd_header->size_original; // Convert size to sectors if(orig_image_size>136899993600) { // image is larger then CHS values can address. // Set sectors to max (C65535*H16*S255). geom_tot_s=267382800; } else { // Calculate actual sectors geom_tot_s=orig_image_size/512; if((orig_image_size%512)!=0) geom_tot_s++; } // Calculate CHS values. This is done according to the VHD specs if(geom_tot_s>=66059280) { // C65535 * H16 * S63 geom_s=255; geom_h=16; geom_c_x_h=geom_tot_s/geom_s; } else { geom_s=17; geom_c_x_h=geom_tot_s/geom_s; geom_h=(geom_c_x_h+1023)/1024; if(geom_h<4) geom_h=4; if(geom_c_x_h>=(geom_h*1024) || geom_h>16) { geom_s=31; geom_h=16; geom_c_x_h=geom_tot_s/geom_s; } if(geom_c_x_h>=(geom_h*1024)) { geom_s=63; geom_h=16; geom_c_x_h=geom_tot_s/geom_s; } } geom_c=geom_c_x_h/geom_h; - glob_p_vhd_header->disk_geometry_c=htobe16(geom_c); - glob_p_vhd_header->disk_geometry_h=geom_h; - glob_p_vhd_header->disk_geometry_s=geom_s; + glob_xmount.output.vhd.p_vhd_header->disk_geometry_c=htobe16(geom_c); + glob_xmount.output.vhd.p_vhd_header->disk_geometry_h=geom_h; + glob_xmount.output.vhd.p_vhd_header->disk_geometry_s=geom_s; - glob_p_vhd_header->disk_type=VHD_IMAGE_HVAL_DISK_TYPE; + glob_xmount.output.vhd.p_vhd_header->disk_type=VHD_IMAGE_HVAL_DISK_TYPE; - glob_p_vhd_header->uuid_l=glob_xmount.input_hash_lo; - glob_p_vhd_header->uuid_h=glob_xmount.input_hash_hi; - glob_p_vhd_header->saved_state=0x00; + glob_xmount.output.vhd.p_vhd_header->uuid_l=glob_xmount.input.image_hash_lo; + glob_xmount.output.vhd.p_vhd_header->uuid_h=glob_xmount.input.image_hash_hi; + glob_xmount.output.vhd.p_vhd_header->saved_state=0x00; // Calculate footer checksum for(i=0;ichecksum=htobe32(~checksum); + glob_xmount.output.vhd.p_vhd_header->checksum=htobe32(~checksum); LOG_DEBUG("VHD header size = %u\n",sizeof(ts_VhdFileHeader)); return TRUE; } //! Init the virtual VMDK file /*! * \return TRUE on success, FALSE on error */ static int InitVirtualVmdkFile() { uint64_t image_size=0; uint64_t image_blocks=0; char buf[500]; // Get original image size if(!GetMorphedImageSize(&image_size)) { LOG_ERROR("Couldn't get morphed image size!\n") return FALSE; } image_blocks=image_size/512; if(image_size%512!=0) image_blocks++; #define VMDK_DESC_FILE "# Disk DescriptorFile\n" \ "version=1\n" \ "CID=fffffffe\n" \ "parentCID=ffffffff\n" \ "createType=\"monolithicFlat\"\n\n" \ "# Extent description\n" \ "RW %" PRIu64 " FLAT \"%s\" 0\n\n" \ "# The Disk Data Base\n" \ "#DDB\n" \ "ddb.virtualHWVersion = \"3\"\n" \ "ddb.adapterType = \"%s\"\n" \ "ddb.geometry.cylinders = \"0\"\n" \ "ddb.geometry.heads = \"0\"\n" \ "ddb.geometry.sectors = \"0\"\n" - if(glob_xmount.VirtImageType==VirtImageType_VMDK) { + if(glob_xmount.output.VirtImageType==VirtImageType_VMDK) { // VMDK with IDE bus sprintf(buf, VMDK_DESC_FILE, image_blocks, - (glob_xmount.p_virtual_image_path)+1, + (glob_xmount.output.p_virtual_image_path)+1, "ide"); - } else if(glob_xmount.VirtImageType==VirtImageType_VMDKS){ + } else if(glob_xmount.output.VirtImageType==VirtImageType_VMDKS){ // VMDK with SCSI bus sprintf(buf, VMDK_DESC_FILE, image_blocks, - (glob_xmount.p_virtual_image_path)+1, + (glob_xmount.output.p_virtual_image_path)+1, "scsi"); } else { LOG_ERROR("Unknown virtual VMDK file format!\n") return FALSE; } #undef VMDK_DESC_FILE // Do not use XMOUNT_STRSET here to avoid adding '\0' to the buffer! - XMOUNT_MALLOC(glob_p_vmdk_file,char*,strlen(buf)) - strncpy(glob_p_vmdk_file,buf,strlen(buf)); - glob_vmdk_file_size=strlen(buf); + XMOUNT_MALLOC(glob_xmount.output.vmdk.p_vmdk_file,char*,strlen(buf)) + strncpy(glob_xmount.output.vmdk.p_vmdk_file,buf,strlen(buf)); + glob_xmount.output.vmdk.vmdk_file_size=strlen(buf); return TRUE; } //! Create virtual image info file /*! * \return TRUE on success, FALSE on error */ static int InitVirtImageInfoFile() { //int ret; //char *p_buf; // Add static header XMOUNT_MALLOC(glob_p_info_file,char*,strlen(IMAGE_INFO_HEADER)+1) strncpy(glob_p_info_file,IMAGE_INFO_HEADER,strlen(IMAGE_INFO_HEADER)+1); // Get infos from input lib // TODO /* ret=glob_p_input_functions->GetInfofileContent(glob_p_input_image,&p_buf); if(ret!=0) { LOG_ERROR("Unable to get info file content: %s!\n", glob_p_input_functions->GetErrorMessage(ret)); return FALSE; } // Add infos to main buffer and free p_buf XMOUNT_STRAPP(glob_p_info_file,p_buf); glob_p_input_functions->FreeBuffer(p_buf); */ return TRUE; } //! Create / load cache file to enable virtual write support /*! * \return TRUE on success, FALSE on error */ static int InitCacheFile() { uint64_t image_size=0; uint64_t blockindex_size=0; uint64_t cachefile_header_size=0; uint64_t cachefile_size=0; uint32_t needed_blocks=0; uint64_t buf; if(!glob_xmount.overwrite_cache) { // Try to open an existing cache file or create a new one glob_p_cache_file=(FILE*)FOPEN(glob_xmount.p_cache_file,"rb+"); if(glob_p_cache_file==NULL) { // As the c lib seems to have no possibility to open a file rw wether it // exists or not (w+ does not work because it truncates an existing file), // when r+ returns NULL the file could simply not exist LOG_DEBUG("Cache file does not exist. Creating new one\n") glob_p_cache_file=(FILE*)FOPEN(glob_xmount.p_cache_file,"wb+"); if(glob_p_cache_file==NULL) { // There is really a problem opening the file LOG_ERROR("Couldn't open cache file \"%s\"!\n", glob_xmount.p_cache_file) return FALSE; } } } else { // Overwrite existing cache file or create a new one glob_p_cache_file=(FILE*)FOPEN(glob_xmount.p_cache_file,"wb+"); if(glob_p_cache_file==NULL) { LOG_ERROR("Couldn't open cache file \"%s\"!\n", glob_xmount.p_cache_file) return FALSE; } } // Get input image size if(!GetMorphedImageSize(&image_size)) { LOG_ERROR("Couldn't get morphed image size!\n") return FALSE; } // Calculate how many blocks are needed and how big the buffers must be // for the actual cache file version needed_blocks=image_size/CACHE_BLOCK_SIZE; if((image_size%CACHE_BLOCK_SIZE)!=0) needed_blocks++; blockindex_size=needed_blocks*sizeof(ts_CacheFileBlockIndex); cachefile_header_size=sizeof(ts_CacheFileHeader)+blockindex_size; LOG_DEBUG("Cache blocks: %u (%04X) entries, %zd (%08zX) bytes\n", needed_blocks, needed_blocks, blockindex_size, blockindex_size) // Get cache file size // fseeko64 had massive problems! if(fseeko(glob_p_cache_file,0,SEEK_END)!=0) { LOG_ERROR("Couldn't seek to end of cache file!\n") return FALSE; } // Same here, ftello64 didn't work at all and returned 0 all the times cachefile_size=ftello(glob_p_cache_file); LOG_DEBUG("Cache file has %zd bytes\n",cachefile_size) if(cachefile_size>0) { // Cache file isn't empty, parse block header LOG_DEBUG("Cache file not empty. Parsing block header\n") if(fseeko(glob_p_cache_file,0,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to beginning of cache file!\n") return FALSE; } // Read and check file signature if(fread(&buf,8,1,glob_p_cache_file)!=1 || buf!=CACHE_FILE_SIGNATURE) { free(glob_p_cache_header); LOG_ERROR("Not an xmount cache file or cache file corrupt!\n") return FALSE; } // Now get cache file version (Has only 32bit!) if(fread(&buf,4,1,glob_p_cache_file)!=1) { free(glob_p_cache_header); LOG_ERROR("Not an xmount cache file or cache file corrupt!\n") return FALSE; } switch((uint32_t)buf) { case 0x00000001: // Old v1 cache file. LOG_ERROR("Unsupported cache file version!\n") LOG_ERROR("Please use xmount-tool to upgrade your cache file.\n") return FALSE; case CUR_CACHE_FILE_VERSION: // Current version if(fseeko(glob_p_cache_file,0,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to beginning of cache file!\n") return FALSE; } // Alloc memory for header and block index XMOUNT_MALLOC(glob_p_cache_header, pts_CacheFileHeader, cachefile_header_size); memset(glob_p_cache_header,0,cachefile_header_size); // Read header and block index from file if(fread(glob_p_cache_header, cachefile_header_size, 1, glob_p_cache_file)!=1) { // Cache file isn't big enough free(glob_p_cache_header); LOG_ERROR("Cache file corrupt!\n") return FALSE; } break; default: LOG_ERROR("Unknown cache file version!\n") return FALSE; } // Check if cache file has same block size as we do if(glob_p_cache_header->BlockSize!=CACHE_BLOCK_SIZE) { LOG_ERROR("Cache file does not use default cache block size!\n") return FALSE; } // Set pointer to block index glob_p_cache_blkidx=(pts_CacheFileBlockIndex)((void*)glob_p_cache_header+ glob_p_cache_header->pBlockIndex); } else { // New cache file, generate a new block header LOG_DEBUG("Cache file is empty. Generating new block header\n"); // Alloc memory for header and block index XMOUNT_MALLOC(glob_p_cache_header,pts_CacheFileHeader,cachefile_header_size) memset(glob_p_cache_header,0,cachefile_header_size); glob_p_cache_header->FileSignature=CACHE_FILE_SIGNATURE; glob_p_cache_header->CacheFileVersion=CUR_CACHE_FILE_VERSION; glob_p_cache_header->BlockSize=CACHE_BLOCK_SIZE; glob_p_cache_header->BlockCount=needed_blocks; //glob_p_cache_header->UsedBlocks=0; // The following pointer is only usuable when reading data from cache file glob_p_cache_header->pBlockIndex=sizeof(ts_CacheFileHeader); glob_p_cache_blkidx=(pts_CacheFileBlockIndex)((void*)glob_p_cache_header+ sizeof(ts_CacheFileHeader)); glob_p_cache_header->VdiFileHeaderCached=FALSE; glob_p_cache_header->pVdiFileHeader=0; glob_p_cache_header->VmdkFileCached=FALSE; glob_p_cache_header->VmdkFileSize=0; glob_p_cache_header->pVmdkFile=0; glob_p_cache_header->VhdFileHeaderCached=FALSE; glob_p_cache_header->pVhdFileHeader=0; // Write header to file if(fwrite(glob_p_cache_header, cachefile_header_size, 1, glob_p_cache_file)!=1) { free(glob_p_cache_header); LOG_ERROR("Couldn't write cache file header to file!\n"); return FALSE; } } return TRUE; } //! Load input / morphing libs /*! * \return TRUE on success, FALSE on error */ static int LoadLibs() { DIR *p_dir=NULL; struct dirent *p_dirent=NULL; int base_library_path_len=0; char *p_library_path=NULL; void *p_libxmount=NULL; t_LibXmount_Input_GetApiVersion pfun_input_GetApiVersion; t_LibXmount_Input_GetSupportedFormats pfun_input_GetSupportedFormats; t_LibXmount_Input_GetFunctions pfun_input_GetFunctions; t_LibXmount_Morphing_GetApiVersion pfun_morphing_GetApiVersion; t_LibXmount_Morphing_GetSupportedTypes pfun_morphing_GetSupportedTypes; t_LibXmount_Morphing_GetFunctions pfun_morphing_GetFunctions; const char *p_supported_formats=NULL; const char *p_buf; uint32_t supported_formats_len=0; pts_InputLib p_input_lib=NULL; pts_MorphingLib p_morphing_lib=NULL; LOG_DEBUG("Searching for xmount libraries in '%s'.\n", XMOUNT_LIBRARY_PATH); // Open lib dir p_dir=opendir(XMOUNT_LIBRARY_PATH); if(p_dir==NULL) { LOG_ERROR("Unable to access xmount library directory '%s'!\n", XMOUNT_LIBRARY_PATH); return FALSE; } // Construct base library path base_library_path_len=strlen(XMOUNT_LIBRARY_PATH); XMOUNT_STRSET(p_library_path,XMOUNT_LIBRARY_PATH); if(XMOUNT_LIBRARY_PATH[base_library_path_len]!='/') { base_library_path_len++; XMOUNT_STRAPP(p_library_path,"/"); } #define LIBXMOUNT_LOAD(path) { \ p_libxmount=dlopen(path,RTLD_NOW); \ if(p_libxmount==NULL) { \ LOG_ERROR("Unable to load input library '%s': %s!\n", \ path, \ dlerror()); \ continue; \ } \ } #define LIBXMOUNT_LOAD_SYMBOL(name,pfun) { \ if((pfun=dlsym(p_libxmount,name))==NULL) { \ LOG_ERROR("Unable to load symbol '%s' from library '%s'!\n", \ name, \ p_library_path); \ dlclose(p_libxmount); \ p_libxmount=NULL; \ continue; \ } \ } // Loop over lib dir while((p_dirent=readdir(p_dir))!=NULL) { LOG_DEBUG("Trying to load '%s'\n",p_dirent->d_name); // Construct full path to found object p_library_path=realloc(p_library_path, base_library_path_len+strlen(p_dirent->d_name)+1); if(p_library_path==NULL) { LOG_ERROR("Couldn't allocate memmory!\n"); exit(1); } strcpy(p_library_path+base_library_path_len,p_dirent->d_name); if(strncmp(p_dirent->d_name,"libxmount_input_",16)==0) { // Found possible input lib. Try to load it LIBXMOUNT_LOAD(p_library_path); // Load library symbols LIBXMOUNT_LOAD_SYMBOL("LibXmount_Input_GetApiVersion", pfun_input_GetApiVersion); // Check library's API version if(pfun_input_GetApiVersion()!=LIBXMOUNT_INPUT_API_VERSION) { LOG_DEBUG("Failed! Wrong API version.\n"); LOG_ERROR("Unable to load input library '%s'. Wrong API version\n", p_library_path); dlclose(p_libxmount); continue; } LIBXMOUNT_LOAD_SYMBOL("LibXmount_Input_GetSupportedFormats", pfun_input_GetSupportedFormats); LIBXMOUNT_LOAD_SYMBOL("LibXmount_Input_GetFunctions", pfun_input_GetFunctions); // Construct new entry for our library list XMOUNT_MALLOC(p_input_lib,pts_InputLib,sizeof(ts_InputLib)); // Initialize lib_functions structure to NULL memset(&(p_input_lib->lib_functions), 0, sizeof(ts_LibXmountInputFunctions)); // Set name and handle XMOUNT_STRSET(p_input_lib->p_name,p_dirent->d_name); p_input_lib->p_lib=p_libxmount; // Get and set supported formats p_supported_formats=pfun_input_GetSupportedFormats(); supported_formats_len=0; p_buf=p_supported_formats; while(*p_buf!='\0') { supported_formats_len+=(strlen(p_buf)+1); p_buf+=(strlen(p_buf)+1); } supported_formats_len++; XMOUNT_MALLOC(p_input_lib->p_supported_input_types, char*, supported_formats_len); memcpy(p_input_lib->p_supported_input_types, p_supported_formats, supported_formats_len); // Get, set and check lib_functions pfun_input_GetFunctions(&(p_input_lib->lib_functions)); if(p_input_lib->lib_functions.CreateHandle==NULL || p_input_lib->lib_functions.DestroyHandle==NULL || p_input_lib->lib_functions.Open==NULL || p_input_lib->lib_functions.Close==NULL || p_input_lib->lib_functions.Size==NULL || p_input_lib->lib_functions.Read==NULL || p_input_lib->lib_functions.OptionsHelp==NULL || p_input_lib->lib_functions.OptionsParse==NULL || p_input_lib->lib_functions.GetInfofileContent==NULL || p_input_lib->lib_functions.GetErrorMessage==NULL || p_input_lib->lib_functions.FreeBuffer==NULL) { LOG_DEBUG("Missing implemention of one or more functions in lib %s!\n", p_dirent->d_name); free(p_input_lib->p_supported_input_types); free(p_input_lib->p_name); free(p_input_lib); dlclose(p_libxmount); continue; } // Add entry to the input library list - XMOUNT_REALLOC(glob_pp_input_libs, + XMOUNT_REALLOC(glob_xmount.input.pp_libs, pts_InputLib*, - sizeof(pts_InputLib)*(glob_input_libs_count+1)); - glob_pp_input_libs[glob_input_libs_count++]=p_input_lib; + sizeof(pts_InputLib)*(glob_xmount.input.libs_count+1)); + glob_xmount.input.pp_libs[glob_xmount.input.libs_count++]=p_input_lib; LOG_DEBUG("Input library '%s' loaded successfully\n",p_dirent->d_name); } if(strncmp(p_dirent->d_name,"libxmount_morphing_",19)==0) { // Found possible morphing lib. Try to load it LIBXMOUNT_LOAD(p_library_path); // Load library symbols LIBXMOUNT_LOAD_SYMBOL("LibXmount_Morphing_GetApiVersion", pfun_morphing_GetApiVersion); // Check library's API version if(pfun_morphing_GetApiVersion()!=LIBXMOUNT_MORPHING_API_VERSION) { LOG_DEBUG("Failed! Wrong API version.\n"); LOG_ERROR("Unable to load morphing library '%s'. Wrong API version\n", p_library_path); dlclose(p_libxmount); continue; } LIBXMOUNT_LOAD_SYMBOL("LibXmount_Morphing_GetSupportedTypes", pfun_morphing_GetSupportedTypes); LIBXMOUNT_LOAD_SYMBOL("LibXmount_Morphing_GetFunctions", pfun_morphing_GetFunctions); // Construct new entry for our library list XMOUNT_MALLOC(p_morphing_lib,pts_MorphingLib,sizeof(ts_MorphingLib)); // Initialize lib_functions structure to NULL memset(&(p_morphing_lib->lib_functions), 0, sizeof(ts_LibXmountMorphingFunctions)); // Set name and handle XMOUNT_STRSET(p_morphing_lib->p_name,p_dirent->d_name); p_morphing_lib->p_lib=p_libxmount; // Get and set supported types p_supported_formats=pfun_morphing_GetSupportedTypes(); supported_formats_len=0; p_buf=p_supported_formats; while(*p_buf!='\0') { supported_formats_len+=(strlen(p_buf)+1); p_buf+=(strlen(p_buf)+1); } supported_formats_len++; XMOUNT_MALLOC(p_morphing_lib->p_supported_morphing_types, char*, supported_formats_len); memcpy(p_morphing_lib->p_supported_morphing_types, p_supported_formats, supported_formats_len); // Get, set and check lib_functions pfun_morphing_GetFunctions(&(p_morphing_lib->lib_functions)); /* // TODO if(p_input_lib->lib_functions.CreateHandle==NULL || p_input_lib->lib_functions.DestroyHandle==NULL || p_input_lib->lib_functions.Open==NULL || p_input_lib->lib_functions.Close==NULL || p_input_lib->lib_functions.Size==NULL || p_input_lib->lib_functions.Read==NULL || p_input_lib->lib_functions.OptionsHelp==NULL || p_input_lib->lib_functions.OptionsParse==NULL || p_input_lib->lib_functions.GetInfofileContent==NULL || p_input_lib->lib_functions.GetErrorMessage==NULL || p_input_lib->lib_functions.FreeBuffer==NULL) { LOG_DEBUG("Missing implemention of one or more functions in lib %s!\n", p_dirent->d_name); free(p_input_lib->p_supported_input_types); free(p_input_lib->p_name); free(p_input_lib); dlclose(p_libxmount); continue; } */ // Add entry to the input library list - XMOUNT_REALLOC(glob_pp_morphing_libs, + XMOUNT_REALLOC(glob_xmount.morphing.pp_libs, pts_MorphingLib*, - sizeof(pts_MorphingLib)*(glob_input_libs_count+1)); - glob_pp_morphing_libs[glob_morphing_libs_count++]=p_morphing_lib; + sizeof(pts_MorphingLib)* + (glob_xmount.morphing.libs_count+1)); + glob_xmount.morphing.pp_libs[glob_xmount.morphing.libs_count++]= + p_morphing_lib; LOG_DEBUG("Morphing library '%s' loaded successfully\n",p_dirent->d_name); } else { LOG_DEBUG("Ignoring '%s'.\n",p_dirent->d_name); continue; } } #undef LIBXMOUNT_LOAD_SYMBOL #undef LIBXMOUNT_LOAD LOG_DEBUG("A total of %u input libs and %u morphing libs were loaded.\n", - glob_morphing_libs_count, - glob_input_libs_count); + glob_xmount.input.libs_count, + glob_xmount.morphing.libs_count); free(p_library_path); closedir(p_dir); - return ((glob_input_libs_count>0 && - glob_morphing_libs_count>0) ? TRUE : FALSE); -} - -//! Unload input libs -/*! - * \return TRUE on success, FALSE on error - */ -static void UnloadInputLibs() { - LOG_DEBUG("Unloading all input libs.\n"); - for(uint32_t i=0;ip_name); - dlclose(glob_pp_input_libs[i]->p_lib); - free(glob_pp_input_libs[i]->p_supported_input_types); - free(glob_pp_input_libs[i]); - } - free(glob_pp_input_libs); - glob_pp_input_libs=NULL; - glob_input_libs_count=0; + return ((glob_xmount.input.libs_count>0 && + glob_xmount.morphing.libs_count>0) ? TRUE : FALSE); } //! Search an appropriate input lib for specified input type /*! * \param p_input_image Input image to search input lib for * \return TRUE on success, FALSE on error */ static int FindInputLib(pts_InputImage p_input_image) { char *p_buf; LOG_DEBUG("Trying to find suitable library for input type '%s'.\n", p_input_image->p_type); // Loop over all loaded libs - for(uint32_t i=0;ip_name); - p_buf=glob_pp_input_libs[i]->p_supported_input_types; + for(uint32_t i=0;ip_name); + p_buf=glob_xmount.input.pp_libs[i]->p_supported_input_types; while(*p_buf!='\0') { if(strcmp(p_buf,p_input_image->p_type)==0) { // Library supports input type, set lib functions LOG_DEBUG("Input library '%s' pretends to handle that input type.\n", - glob_pp_input_libs[i]->p_name); - p_input_image->p_functions=&(glob_pp_input_libs[i]->lib_functions); + glob_xmount.input.pp_libs[i]->p_name); + p_input_image->p_functions= + &(glob_xmount.input.pp_libs[i]->lib_functions); return TRUE; } p_buf+=(strlen(p_buf)+1); } } LOG_DEBUG("Couldn't find any suitable library.\n"); // No library supporting input type found return FALSE; } //! Search an appropriate morphing lib for the specified morph type /*! * \return TRUE on success, FALSE on error */ static int FindMorphingLib() { char *p_buf; LOG_DEBUG("Trying to find suitable library for morph type '%s'.\n", - glob_xmount.p_morph_type); + glob_xmount.morphing.p_morph_type); // Loop over all loaded libs - for(uint32_t i=0;ip_name); - p_buf=glob_pp_morphing_libs[i]->p_supported_morphing_types; + glob_xmount.morphing.pp_libs[i]->p_name); + p_buf=glob_xmount.morphing.pp_libs[i]->p_supported_morphing_types; while(*p_buf!='\0') { - if(strcmp(p_buf,glob_xmount.p_morph_type)==0) { + if(strcmp(p_buf,glob_xmount.morphing.p_morph_type)==0) { // Library supports morph type, set lib functions LOG_DEBUG("Morphing library '%s' pretends to handle that morph type.\n", - glob_pp_morphing_libs[i]->p_name); - glob_p_morphing_functions=&(glob_pp_morphing_libs[i]->lib_functions); + glob_xmount.morphing.pp_libs[i]->p_name); + glob_xmount.morphing.p_functions= + &(glob_xmount.morphing.pp_libs[i]->lib_functions); return TRUE; } p_buf+=(strlen(p_buf)+1); } } LOG_DEBUG("Couldn't find any suitable library.\n"); // No library supporting input type found return FALSE; } +static void InitResources() { + // Input + glob_xmount.input.libs_count=0; + glob_xmount.input.pp_libs=NULL; + glob_xmount.input.p_lib_params=NULL; + glob_xmount.input.images_count=0; + glob_xmount.input.pp_images=NULL; + glob_xmount.input.image_offset=0; + glob_xmount.input.image_hash_lo=0; + glob_xmount.input.image_hash_hi=0; + // Morphing + glob_xmount.morphing.libs_count=0; + glob_xmount.morphing.pp_libs=NULL; + glob_xmount.morphing.p_morph_type=NULL; + glob_xmount.morphing.pp_lib_params=NULL; + glob_xmount.morphing.p_handle=NULL; + glob_xmount.morphing.p_functions=NULL; + glob_xmount.morphing.input_image_functions.ImageCount= + &LibXmount_Morphing_ImageCount; + glob_xmount.morphing.input_image_functions.Size=&LibXmount_Morphing_Size; + glob_xmount.morphing.input_image_functions.Read=&LibXmount_Morphing_Read; + // Output +#ifndef __APPLE__ + glob_xmount.output.VirtImageType=VirtImageType_DD; +#else + glob_xmount.output.VirtImageType=VirtImageType_DMG; +#endif + glob_xmount.output.image_size=0; + glob_xmount.output.writable=FALSE; + glob_xmount.output.p_virtual_image_path=NULL; + glob_xmount.output.vdi.vdi_header_size=0; + glob_xmount.output.vdi.p_vdi_header=NULL; + glob_xmount.output.vdi.vdi_block_map_size=0; + glob_xmount.output.vdi.p_vdi_block_map=NULL; + glob_xmount.output.vhd.p_vhd_header=NULL; + glob_xmount.output.vmdk.p_virtual_vmdk_path=NULL; + glob_xmount.output.vmdk.p_vmdk_file=NULL; + glob_xmount.output.vmdk.vmdk_file_size=0; + glob_xmount.output.vmdk.p_vmdk_lockdir1=NULL; + glob_xmount.output.vmdk.p_vmdk_lockdir2=NULL; + glob_xmount.output.vmdk.p_vmdk_lockfile_data=NULL; + glob_xmount.output.vmdk.vmdk_lockfile_size=0; + glob_xmount.output.vmdk.p_vmdk_lockfile_name=NULL; + // Misc data + glob_xmount.debug=FALSE; + glob_xmount.p_virtual_info_path=NULL; + glob_xmount.overwrite_cache=FALSE; + glob_xmount.p_cache_file=NULL; + glob_xmount.may_set_fuse_allow_other=FALSE; +} + +/* + * FreeResources + */ +static void FreeResources() { + int ret; + + LOG_DEBUG("Freeing all resources\n"); + + // Misc data + if(glob_xmount.p_cache_file!=NULL) + free(glob_xmount.p_cache_file); + if(glob_xmount.p_virtual_info_path!=NULL) + free(glob_xmount.p_virtual_info_path); + + // Output + if(glob_xmount.output.vmdk.p_vmdk_lockfile_name!=NULL) + free(glob_xmount.output.vmdk.p_vmdk_lockfile_name); + if(glob_xmount.output.vmdk.p_vmdk_lockfile_data!=NULL) + free(glob_xmount.output.vmdk.p_vmdk_lockfile_data); + if(glob_xmount.output.vmdk.p_vmdk_lockdir2!=NULL) + free(glob_xmount.output.vmdk.p_vmdk_lockdir2); + if(glob_xmount.output.vmdk.p_vmdk_lockdir1!=NULL) + free(glob_xmount.output.vmdk.p_vmdk_lockdir1); + if(glob_xmount.output.vmdk.p_vmdk_file!=NULL) + free(glob_xmount.output.vmdk.p_vmdk_file); + if(glob_xmount.output.vmdk.p_virtual_vmdk_path!=NULL) + free(glob_xmount.output.vmdk.p_virtual_vmdk_path); + if(glob_xmount.output.vhd.p_vhd_header!=NULL) + free(glob_xmount.output.vhd.p_vhd_header); + if(glob_xmount.output.vdi.p_vdi_block_map!=NULL) + free(glob_xmount.output.vdi.p_vdi_block_map); + if(glob_xmount.output.vdi.p_vdi_header!=NULL) + free(glob_xmount.output.vdi.p_vdi_header); + if(glob_xmount.output.p_virtual_image_path!=NULL) + free(glob_xmount.output.p_virtual_image_path); + + // Morphing + if(glob_xmount.morphing.p_functions!=NULL) { + if(glob_xmount.morphing.p_handle!=NULL) { + // Destroy morphing handle + ret=glob_xmount.morphing.p_functions-> + DestroyHandle(&(glob_xmount.morphing.p_handle)); + if(ret!=0) { + LOG_ERROR("Unable to destroy morphing handle: %s!\n", + glob_xmount.morphing.p_functions->GetErrorMessage(ret)); + } + } + } + if(glob_xmount.morphing.pp_lib_params!=NULL) + // TODO: Free if array + free(glob_xmount.morphing.pp_lib_params); + if(glob_xmount.morphing.p_morph_type!=NULL) + free(glob_xmount.morphing.p_morph_type); + if(glob_xmount.morphing.pp_libs!=NULL) { + // Unload morphing libs + for(uint32_t i=0;ip_supported_morphing_types!=NULL) + free(glob_xmount.morphing.pp_libs[i]->p_supported_morphing_types); + if(glob_xmount.morphing.pp_libs[i]->p_lib!=NULL) + dlclose(glob_xmount.morphing.pp_libs[i]->p_lib); + if(glob_xmount.morphing.pp_libs[i]->p_name!=NULL) + free(glob_xmount.morphing.pp_libs[i]->p_name); + free(glob_xmount.morphing.pp_libs[i]); + } + free(glob_xmount.morphing.pp_libs); + } + + // Input + if(glob_xmount.input.pp_images!=NULL) { + // Close all input images + for(uint64_t i=0;ip_functions!=NULL) { + if(glob_xmount.input.pp_images[i]->p_handle!=NULL) { + ret=glob_xmount.input.pp_images[i]->p_functions-> + Close(&(glob_xmount.input.pp_images[i]->p_handle)); + if(ret!=0) { + LOG_ERROR("Unable to close input image: %s\n", + glob_xmount.input.pp_images[i]->p_functions-> + GetErrorMessage(ret)); + } + ret=glob_xmount.input.pp_images[i]->p_functions-> + DestroyHandle(&(glob_xmount.input.pp_images[i]->p_handle)); + if(ret!=0) { + LOG_ERROR("Unable to destroy input image handle: %s\n", + glob_xmount.input.pp_images[i]->p_functions-> + GetErrorMessage(ret)); + } + } + } + if(glob_xmount.input.pp_images[i]->pp_files!=NULL) { + for(uint64_t ii=0;iifiles_count;ii++) { + if(glob_xmount.input.pp_images[i]->pp_files[ii]!=NULL) + free(glob_xmount.input.pp_images[i]->pp_files[ii]); + } + free(glob_xmount.input.pp_images[i]->pp_files); + } + if(glob_xmount.input.pp_images[i]->p_type!=NULL) + free(glob_xmount.input.pp_images[i]->p_type); + free(glob_xmount.input.pp_images[i]); + } + free(glob_xmount.input.pp_images); + } + if(glob_xmount.input.p_lib_params!=NULL) free(glob_xmount.input.p_lib_params); + if(glob_xmount.input.pp_libs!=NULL) { + // Unload all input libs + for(uint32_t i=0;ip_supported_input_types!=NULL) + free(glob_xmount.input.pp_libs[i]->p_supported_input_types); + if(glob_xmount.input.pp_libs[i]->p_lib!=NULL) + dlclose(glob_xmount.input.pp_libs[i]->p_lib); + if(glob_xmount.input.pp_libs[i]->p_name!=NULL) + free(glob_xmount.input.pp_libs[i]->p_name); + free(glob_xmount.input.pp_libs[i]); + } + free(glob_xmount.input.pp_libs); + } + + // Before we return, initialize everything in case ReleaseResources would be + // called again. + InitResources(); +} + +/******************************************************************************* + * LibXmount_Morphing function implementation + ******************************************************************************/ +//! Function to get the amount of input images +/*! + * \param p_count Count of input images + * \return 0 on success + */ +static int LibXmount_Morphing_ImageCount(uint64_t *p_count) { + *p_count=glob_xmount.input.images_count; + return 0; +} + +//! Function to get the size of the morphed data +/*! + * \param image Image number + * \param p_size Pointer to store input image's size to + * \return 0 on success + */ +static int LibXmount_Morphing_Size(uint64_t image, uint64_t *p_size) { + if(image>=glob_xmount.input.images_count) return -1; + *p_size=glob_xmount.input.pp_images[image]->size; + return 0; +} + +//! Function to read data from input image +/*! + * \param image Image number + * \param p_buf Buffer to store read data to + * \param offset Position at which to start reading + * \param count Amount of bytes to read + * \param p_read Number of read bytes on success + * \return 0 on success or negated error code on error + */ +static int LibXmount_Morphing_Read(uint64_t image, + char *p_buf, + off_t offset, + size_t count, + size_t *p_read) +{ + if(image>=glob_xmount.input.images_count) return -EIO; + return GetInputImageData(glob_xmount.input.pp_images[image], + p_buf, + offset, + count, + p_read); +} + /******************************************************************************* * FUSE function implementation ******************************************************************************/ //! FUSE access implementation /*! * \param p_path Path of file to get attributes from * \param perm Requested permissisons * \return 0 on success, negated error code on error */ /* static int FuseAccess(const char *path, int perm) { // TODO: Implement propper file permission handling // http://www.cs.cf.ac.uk/Dave/C/node20.html // Values for the second argument to access. // These may be OR'd together. //#define R_OK 4 // Test for read permission. //#define W_OK 2 // Test for write permission. //#define X_OK 1 // Test for execute permission. //#define F_OK 0 // Test for existence. return 0; } */ //! FUSE getattr implementation /*! * \param p_path Path of file to get attributes from * \param p_stat Pointer to stat structure to save attributes to * \return 0 on success, negated error code on error */ static int FuseGetAttr(const char *p_path, struct stat *p_stat) { memset(p_stat,0,sizeof(struct stat)); if(strcmp(p_path,"/")==0) { // Attributes of mountpoint p_stat->st_mode=S_IFDIR | 0777; p_stat->st_nlink=2; - } else if(strcmp(p_path,glob_xmount.p_virtual_image_path)==0) { + } else if(strcmp(p_path,glob_xmount.output.p_virtual_image_path)==0) { // Attributes of virtual image - if(!glob_xmount.writable) p_stat->st_mode=S_IFREG | 0444; + if(!glob_xmount.output.writable) p_stat->st_mode=S_IFREG | 0444; else p_stat->st_mode=S_IFREG | 0666; p_stat->st_nlink=1; // Get virtual image file size if(!GetVirtImageSize((uint64_t*)&(p_stat->st_size))) { LOG_ERROR("Couldn't get image size!\n"); return -ENOENT; } - if(glob_xmount.VirtImageType==VirtImageType_VHD) { + if(glob_xmount.output.VirtImageType==VirtImageType_VHD) { // Make sure virtual image seems to be fully allocated (not sparse file). // Without this, Windows won't attach the vhd file! p_stat->st_blocks=p_stat->st_size/512; if(p_stat->st_size%512!=0) p_stat->st_blocks++; } } else if(strcmp(p_path,glob_xmount.p_virtual_info_path)==0) { // Attributes of virtual image info file p_stat->st_mode=S_IFREG | 0444; p_stat->st_nlink=1; // Get virtual image info file size if(glob_p_info_file!=NULL) { p_stat->st_size=strlen(glob_p_info_file); } else p_stat->st_size=0; - } else if(glob_xmount.VirtImageType==VirtImageType_VMDK || - glob_xmount.VirtImageType==VirtImageType_VMDKS) + } else if(glob_xmount.output.VirtImageType==VirtImageType_VMDK || + glob_xmount.output.VirtImageType==VirtImageType_VMDKS) { // Some special files only present when emulating VMDK files - if(strcmp(p_path,glob_xmount.p_virtual_vmdk_path)==0) { + if(strcmp(p_path,glob_xmount.output.vmdk.p_virtual_vmdk_path)==0) { // Attributes of virtual vmdk file - if(!glob_xmount.writable) p_stat->st_mode=S_IFREG | 0444; + if(!glob_xmount.output.writable) p_stat->st_mode=S_IFREG | 0444; else p_stat->st_mode=S_IFREG | 0666; p_stat->st_nlink=1; // Get virtual image info file size - if(glob_p_vmdk_file!=NULL) { - p_stat->st_size=glob_vmdk_file_size; + if(glob_xmount.output.vmdk.p_vmdk_file!=NULL) { + p_stat->st_size=glob_xmount.output.vmdk.vmdk_file_size; } else p_stat->st_size=0; - } else if(glob_p_vmdk_lockdir1!=NULL && - strcmp(p_path,glob_p_vmdk_lockdir1)==0) + } else if(glob_xmount.output.vmdk.p_vmdk_lockdir1!=NULL && + strcmp(p_path,glob_xmount.output.vmdk.p_vmdk_lockdir1)==0) { p_stat->st_mode=S_IFDIR | 0777; p_stat->st_nlink=2; - } else if(glob_p_vmdk_lockdir2!=NULL && - strcmp(p_path,glob_p_vmdk_lockdir2)==0) + } else if(glob_xmount.output.vmdk.p_vmdk_lockdir2!=NULL && + strcmp(p_path,glob_xmount.output.vmdk.p_vmdk_lockdir2)==0) { p_stat->st_mode=S_IFDIR | 0777; p_stat->st_nlink=2; - } else if(glob_p_vmdk_lockfile_name!=NULL && - strcmp(p_path,glob_p_vmdk_lockfile_name)==0) + } else if(glob_xmount.output.vmdk.p_vmdk_lockfile_name!=NULL && + strcmp(p_path,glob_xmount.output.vmdk.p_vmdk_lockfile_name)==0) { p_stat->st_mode=S_IFREG | 0666; - if(glob_p_vmdk_lockfile_name!=NULL) { - p_stat->st_size=strlen(glob_p_vmdk_lockfile_name); + if(glob_xmount.output.vmdk.p_vmdk_lockfile_name!=NULL) { + p_stat->st_size=strlen(glob_xmount.output.vmdk.p_vmdk_lockfile_name); } else p_stat->st_size=0; } else return -ENOENT; } else return -ENOENT; // Set uid and gid of all files to uid and gid of current process p_stat->st_uid=getuid(); p_stat->st_gid=getgid(); return 0; } //! FUSE mkdir implementation /*! * \param p_path Directory path * \param mode Directory permissions * \return 0 on success, negated error code on error */ static int FuseMkDir(const char *p_path, mode_t mode) { // Only allow creation of VMWare's lock directories - if(glob_xmount.VirtImageType==VirtImageType_VMDK || - glob_xmount.VirtImageType==VirtImageType_VMDKS) + if(glob_xmount.output.VirtImageType==VirtImageType_VMDK || + glob_xmount.output.VirtImageType==VirtImageType_VMDKS) { - if(glob_p_vmdk_lockdir1==NULL) { - char aVmdkLockDir[strlen(glob_xmount.p_virtual_vmdk_path)+5]; - sprintf(aVmdkLockDir,"%s.lck",glob_xmount.p_virtual_vmdk_path); + if(glob_xmount.output.vmdk.p_vmdk_lockdir1==NULL) { + char aVmdkLockDir[strlen(glob_xmount.output.vmdk.p_virtual_vmdk_path)+5]; + sprintf(aVmdkLockDir, + "%s.lck", + glob_xmount.output.vmdk.p_virtual_vmdk_path); if(strcmp(p_path,aVmdkLockDir)==0) { LOG_DEBUG("Creating virtual directory \"%s\"\n",aVmdkLockDir) - XMOUNT_STRSET(glob_p_vmdk_lockdir1,aVmdkLockDir) + XMOUNT_STRSET(glob_xmount.output.vmdk.p_vmdk_lockdir1,aVmdkLockDir) return 0; } else { LOG_ERROR("Attempt to create illegal directory \"%s\"!\n",p_path) LOG_DEBUG("Supposed: %s\n",aVmdkLockDir) return -1; } - } else if(glob_p_vmdk_lockdir2==NULL && + } else if(glob_xmount.output.vmdk.p_vmdk_lockdir2==NULL && strncmp(p_path, - glob_p_vmdk_lockdir1, - strlen(glob_p_vmdk_lockdir1))==0) + glob_xmount.output.vmdk.p_vmdk_lockdir1, + strlen(glob_xmount.output.vmdk.p_vmdk_lockdir1))==0) { LOG_DEBUG("Creating virtual directory \"%s\"\n",p_path) - XMOUNT_STRSET(glob_p_vmdk_lockdir2,p_path) + XMOUNT_STRSET(glob_xmount.output.vmdk.p_vmdk_lockdir2,p_path) return 0; } else { LOG_ERROR("Attempt to create illegal directory \"%s\"!\n",p_path) LOG_DEBUG("Compared to first %u chars of \"%s\"\n", - strlen(glob_p_vmdk_lockdir1), - glob_p_vmdk_lockdir1) + strlen(glob_xmount.output.vmdk.p_vmdk_lockdir1), + glob_xmount.output.vmdk.p_vmdk_lockdir1) return -1; } } LOG_ERROR("Attempt to create directory \"%s\" " "on read-only filesystem!\n",p_path) return -1; } //! FUSE create implementation. /*! * Currently only allows to create VMWare's lock file * * \param p_path File to create * \param mode File mode * \param dev ??? but not used * \return 0 on success, negated error code on error */ static int FuseMkNod(const char *p_path, mode_t mode, dev_t dev) { - if((glob_xmount.VirtImageType==VirtImageType_VMDK || - glob_xmount.VirtImageType==VirtImageType_VMDKS) && - glob_p_vmdk_lockdir1!=NULL && glob_p_vmdk_lockfile_name==NULL) + if((glob_xmount.output.VirtImageType==VirtImageType_VMDK || + glob_xmount.output.VirtImageType==VirtImageType_VMDKS) && + glob_xmount.output.vmdk.p_vmdk_lockdir1!=NULL && + glob_xmount.output.vmdk.p_vmdk_lockfile_name==NULL) { LOG_DEBUG("Creating virtual file \"%s\"\n",p_path) - XMOUNT_STRSET(glob_p_vmdk_lockfile_name,p_path); + XMOUNT_STRSET(glob_xmount.output.vmdk.p_vmdk_lockfile_name,p_path); return 0; } else { LOG_ERROR("Attempt to create illegal file \"%s\"\n",p_path) return -1; } } //! FUSE readdir implementation /*! * \param p_path Path from where files should be listed * \param p_buf Buffer to write file entrys to * \param filler Function to write dir entrys to buffer * \param offset ??? but not used * \param p_fi File info struct * \return 0 on success, negated error code on error */ static int FuseReadDir(const char *p_path, void *p_buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *p_fi) { // Ignore some params (void)offset; (void)p_fi; if(strcmp(p_path,"/")==0) { // Add std . and .. entrys filler(p_buf,".",NULL,0); filler(p_buf,"..",NULL,0); // Add our virtual files (p+1 to ignore starting "/") - filler(p_buf,glob_xmount.p_virtual_image_path+1,NULL,0); + filler(p_buf,glob_xmount.output.p_virtual_image_path+1,NULL,0); filler(p_buf,glob_xmount.p_virtual_info_path+1,NULL,0); - if(glob_xmount.VirtImageType==VirtImageType_VMDK || - glob_xmount.VirtImageType==VirtImageType_VMDKS) + if(glob_xmount.output.VirtImageType==VirtImageType_VMDK || + glob_xmount.output.VirtImageType==VirtImageType_VMDKS) { // For VMDK's, we use an additional descriptor file - filler(p_buf,glob_xmount.p_virtual_vmdk_path+1,NULL,0); + filler(p_buf,glob_xmount.output.vmdk.p_virtual_vmdk_path+1,NULL,0); // And there could also be a lock directory - if(glob_p_vmdk_lockdir1!=NULL) { - filler(p_buf,glob_p_vmdk_lockdir1+1,NULL,0); + if(glob_xmount.output.vmdk.p_vmdk_lockdir1!=NULL) { + filler(p_buf,glob_xmount.output.vmdk.p_vmdk_lockdir1+1,NULL,0); } } - } else if(glob_xmount.VirtImageType==VirtImageType_VMDK || - glob_xmount.VirtImageType==VirtImageType_VMDKS) + } else if(glob_xmount.output.VirtImageType==VirtImageType_VMDK || + glob_xmount.output.VirtImageType==VirtImageType_VMDKS) { // For VMDK emulation, there could be a lock directory - if(glob_p_vmdk_lockdir1!=NULL && strcmp(p_path,glob_p_vmdk_lockdir1)==0) { + if(glob_xmount.output.vmdk.p_vmdk_lockdir1!=NULL && + strcmp(p_path,glob_xmount.output.vmdk.p_vmdk_lockdir1)==0) + { filler(p_buf,".",NULL,0); filler(p_buf,"..",NULL,0); - if(glob_p_vmdk_lockfile_name!=NULL) { + if(glob_xmount.output.vmdk.p_vmdk_lockfile_name!=NULL) { filler(p_buf, - glob_p_vmdk_lockfile_name+strlen(glob_p_vmdk_lockdir1)+1, + glob_xmount.output.vmdk.p_vmdk_lockfile_name+ + strlen(glob_xmount.output.vmdk.p_vmdk_lockdir1)+1, NULL, 0); } - } else if(glob_p_vmdk_lockdir2!=NULL && - strcmp(p_path,glob_p_vmdk_lockdir2)==0) + } else if(glob_xmount.output.vmdk.p_vmdk_lockdir2!=NULL && + strcmp(p_path,glob_xmount.output.vmdk.p_vmdk_lockdir2)==0) { filler(p_buf,".",NULL,0); filler(p_buf,"..",NULL,0); } else return -ENOENT; } else return -ENOENT; return 0; } //! FUSE open implementation /*! * \param p_path Path to file to open * \param p_fi File info struct * \return 0 on success, negated error code on error */ static int FuseOpen(const char *p_path, struct fuse_file_info *p_fi) { #define CHECK_OPEN_PERMS() { \ - if(!glob_xmount.writable && (p_fi->flags & 3)!=O_RDONLY) { \ + if(!glob_xmount.output.writable && (p_fi->flags & 3)!=O_RDONLY) { \ LOG_DEBUG("Attempt to open the read-only file \"%s\" for writing.\n", \ p_path) \ return -EACCES; \ } \ return 0; \ } - if(strcmp(p_path,glob_xmount.p_virtual_image_path)==0 || + if(strcmp(p_path,glob_xmount.output.p_virtual_image_path)==0 || strcmp(p_path,glob_xmount.p_virtual_info_path)==0) { CHECK_OPEN_PERMS(); - } else if(glob_xmount.VirtImageType==VirtImageType_VMDK || - glob_xmount.VirtImageType==VirtImageType_VMDKS) + } else if(glob_xmount.output.VirtImageType==VirtImageType_VMDK || + glob_xmount.output.VirtImageType==VirtImageType_VMDKS) { - if(strcmp(p_path,glob_xmount.p_virtual_vmdk_path)==0 || - (glob_p_vmdk_lockfile_name!=NULL && - strcmp(p_path,glob_p_vmdk_lockfile_name)==0)) + if(strcmp(p_path,glob_xmount.output.vmdk.p_virtual_vmdk_path)==0 || + (glob_xmount.output.vmdk.p_vmdk_lockfile_name!=NULL && + strcmp(p_path,glob_xmount.output.vmdk.p_vmdk_lockfile_name)==0)) { CHECK_OPEN_PERMS(); } } #undef CHECK_OPEN_PERMS LOG_DEBUG("Attempt to open inexistant file \"%s\".\n",p_path); return -ENOENT; } //! FUSE read implementation /*! * \param p_path Path (relative to mount folder) of file to read data from * \param p_buf Pre-allocated buffer where read data should be written to * \param size Number of bytes to read * \param offset Offset to start reading at * \param p_fi: File info struct * \return Read bytes on success, negated error code on error */ static int FuseRead(const char *p_path, char *p_buf, size_t size, off_t offset, struct fuse_file_info *p_fi) { (void)p_fi; int ret; uint64_t len; #define READ_MEM_FILE(filebuf,filesize,filetypestr,mutex) { \ len=filesize; \ if(offsetlen) { \ LOG_DEBUG("Attempt to read past EOF of virtual " filetypestr " file\n"); \ LOG_DEBUG("Adjusting read size from %u to %u\n",size,len-offset); \ size=len-offset; \ } \ pthread_mutex_lock(&mutex); \ memcpy(p_buf,filebuf+offset,size); \ pthread_mutex_unlock(&mutex); \ LOG_DEBUG("Read %" PRIu64 " bytes at offset %" PRIu64 \ " from virtual " filetypestr " file\n",size,offset); \ ret=size; \ } else { \ LOG_DEBUG("Attempt to read behind EOF of virtual " filetypestr " file\n"); \ ret=0; \ } \ } - if(strcmp(p_path,glob_xmount.p_virtual_image_path)==0) { + if(strcmp(p_path,glob_xmount.output.p_virtual_image_path)==0) { // Read data from virtual output file // Wait for other threads to end reading/writing data pthread_mutex_lock(&glob_mutex_image_rw); // Get requested data if((ret=GetVirtImageData(p_buf,offset,size))<0) { LOG_ERROR("Couldn't read data from virtual image file!\n") } // Allow other threads to read/write data again pthread_mutex_unlock(&glob_mutex_image_rw); } else if(strcmp(p_path,glob_xmount.p_virtual_info_path)==0) { // Read data from virtual info file READ_MEM_FILE(glob_p_info_file, strlen(glob_p_info_file), "info", glob_mutex_info_read); - } else if(strcmp(p_path,glob_xmount.p_virtual_vmdk_path)==0) { + } else if(strcmp(p_path,glob_xmount.output.vmdk.p_virtual_vmdk_path)==0) { // Read data from virtual vmdk file - READ_MEM_FILE(glob_p_vmdk_file, - glob_vmdk_file_size, + READ_MEM_FILE(glob_xmount.output.vmdk.p_vmdk_file, + glob_xmount.output.vmdk.vmdk_file_size, "vmdk", glob_mutex_image_rw); - } else if(glob_p_vmdk_lockfile_name!=NULL && - strcmp(p_path,glob_p_vmdk_lockfile_name)==0) + } else if(glob_xmount.output.vmdk.p_vmdk_lockfile_name!=NULL && + strcmp(p_path,glob_xmount.output.vmdk.p_vmdk_lockfile_name)==0) { // Read data from virtual lock file - READ_MEM_FILE(glob_p_vmdk_lockfile_data, - glob_vmdk_lockfile_size, + READ_MEM_FILE(glob_xmount.output.vmdk.p_vmdk_lockfile_data, + glob_xmount.output.vmdk.vmdk_lockfile_size, "vmdk lock", glob_mutex_image_rw); } else { // Attempt to read non existant file LOG_DEBUG("Attempt to read from non existant file \"%s\"\n",p_path) ret=-ENOENT; } #undef READ_MEM_FILE return ret; } //! FUSE rename implementation /*! * \param p_path File to rename * \param p_npath New filename * \return 0 on error, negated error code on error */ static int FuseRename(const char *p_path, const char *p_npath) { - if(glob_xmount.VirtImageType==VirtImageType_VMDK || - glob_xmount.VirtImageType==VirtImageType_VMDKS) + if(glob_xmount.output.VirtImageType==VirtImageType_VMDK || + glob_xmount.output.VirtImageType==VirtImageType_VMDKS) { - if(glob_p_vmdk_lockfile_name!=NULL && - strcmp(p_path,glob_p_vmdk_lockfile_name)==0) + if(glob_xmount.output.vmdk.p_vmdk_lockfile_name!=NULL && + strcmp(p_path,glob_xmount.output.vmdk.p_vmdk_lockfile_name)==0) { LOG_DEBUG("Renaming virtual lock file from \"%s\" to \"%s\"\n", - glob_p_vmdk_lockfile_name, + glob_xmount.output.vmdk.p_vmdk_lockfile_name, p_npath) - XMOUNT_REALLOC(glob_p_vmdk_lockfile_name,char*, + XMOUNT_REALLOC(glob_xmount.output.vmdk.p_vmdk_lockfile_name,char*, (strlen(p_npath)+1)*sizeof(char)); - strcpy(glob_p_vmdk_lockfile_name,p_npath); + strcpy(glob_xmount.output.vmdk.p_vmdk_lockfile_name,p_npath); return 0; } } return -ENOENT; } //! FUSE rmdir implementation /*! * \param p_path Directory to delete * \return 0 on success, negated error code on error */ static int FuseRmDir(const char *p_path) { // Only VMWare's lock directories can be deleted - if(glob_xmount.VirtImageType==VirtImageType_VMDK || - glob_xmount.VirtImageType==VirtImageType_VMDKS) + if(glob_xmount.output.VirtImageType==VirtImageType_VMDK || + glob_xmount.output.VirtImageType==VirtImageType_VMDKS) { - if(glob_p_vmdk_lockdir1!=NULL && strcmp(p_path,glob_p_vmdk_lockdir1)==0) { - LOG_DEBUG("Deleting virtual lock dir \"%s\"\n",glob_p_vmdk_lockdir1) - free(glob_p_vmdk_lockdir1); - glob_p_vmdk_lockdir1=NULL; + if(glob_xmount.output.vmdk.p_vmdk_lockdir1!=NULL && + strcmp(p_path,glob_xmount.output.vmdk.p_vmdk_lockdir1)==0) + { + LOG_DEBUG("Deleting virtual lock dir \"%s\"\n", + glob_xmount.output.vmdk.p_vmdk_lockdir1) + free(glob_xmount.output.vmdk.p_vmdk_lockdir1); + glob_xmount.output.vmdk.p_vmdk_lockdir1=NULL; return 0; - } else if(glob_p_vmdk_lockdir2!=NULL && - strcmp(p_path,glob_p_vmdk_lockdir2)==0) + } else if(glob_xmount.output.vmdk.p_vmdk_lockdir2!=NULL && + strcmp(p_path,glob_xmount.output.vmdk.p_vmdk_lockdir2)==0) { - LOG_DEBUG("Deleting virtual lock dir \"%s\"\n",glob_p_vmdk_lockdir1) - free(glob_p_vmdk_lockdir2); - glob_p_vmdk_lockdir2=NULL; + LOG_DEBUG("Deleting virtual lock dir \"%s\"\n", + glob_xmount.output.vmdk.p_vmdk_lockdir1) + free(glob_xmount.output.vmdk.p_vmdk_lockdir2); + glob_xmount.output.vmdk.p_vmdk_lockdir2=NULL; return 0; } } return -1; } //! FUSE unlink implementation /*! * \param p_path File to delete * \return 0 on success, negated error code on error */ static int FuseUnlink(const char *p_path) { // Only VMWare's lock file can be deleted - if(glob_xmount.VirtImageType==VirtImageType_VMDK || - glob_xmount.VirtImageType==VirtImageType_VMDKS) + if(glob_xmount.output.VirtImageType==VirtImageType_VMDK || + glob_xmount.output.VirtImageType==VirtImageType_VMDKS) { - if(glob_p_vmdk_lockfile_name!=NULL && - strcmp(p_path,glob_p_vmdk_lockfile_name)==0) + if(glob_xmount.output.vmdk.p_vmdk_lockfile_name!=NULL && + strcmp(p_path,glob_xmount.output.vmdk.p_vmdk_lockfile_name)==0) { - LOG_DEBUG("Deleting virtual file \"%s\"\n",glob_p_vmdk_lockfile_name) - free(glob_p_vmdk_lockfile_name); - free(glob_p_vmdk_lockfile_data); - glob_p_vmdk_lockfile_name=NULL; - glob_p_vmdk_lockfile_data=NULL; - glob_vmdk_lockfile_size=0; + LOG_DEBUG("Deleting virtual file \"%s\"\n", + glob_xmount.output.vmdk.p_vmdk_lockfile_name) + free(glob_xmount.output.vmdk.p_vmdk_lockfile_name); + free(glob_xmount.output.vmdk.p_vmdk_lockfile_data); + glob_xmount.output.vmdk.p_vmdk_lockfile_name=NULL; + glob_xmount.output.vmdk.p_vmdk_lockfile_data=NULL; + glob_xmount.output.vmdk.vmdk_lockfile_size=0; return 0; } } return -1; } //! FUSE statfs implementation /*! * \param p_path Get stats for fs that the specified file resides in * \param stats Stats * \return 0 on success, negated error code on error */ /* static int FuseStatFs(const char *p_path, struct statvfs *stats) { struct statvfs CacheFileFsStats; int ret; if(glob_xmount.writable==TRUE) { // If write support is enabled, return stats of fs upon which cache file // resides in if((ret=statvfs(glob_xmount.p_cache_file,&CacheFileFsStats))==0) { memcpy(stats,&CacheFileFsStats,sizeof(struct statvfs)); return 0; } else { LOG_ERROR("Couldn't get stats for fs upon which resides \"%s\"\n", glob_xmount.p_cache_file) return ret; } } else { // TODO: Return read only return 0; } } */ // FUSE write implementation /*! * \param p_buf Buffer containing data to write * \param size Number of bytes to write * \param offset Offset to start writing at * \param p_fi: File info struct * * Returns: * Written bytes on success, negated error code on error */ static int FuseWrite(const char *p_path, const char *p_buf, size_t size, off_t offset, struct fuse_file_info *p_fi) { (void)p_fi; uint64_t len; - if(strcmp(p_path,glob_xmount.p_virtual_image_path)==0) { + if(strcmp(p_path,glob_xmount.output.p_virtual_image_path)==0) { // Wait for other threads to end reading/writing data pthread_mutex_lock(&glob_mutex_image_rw); // Get virtual image file size if(!GetVirtImageSize(&len)) { LOG_ERROR("Couldn't get virtual image size!\n") pthread_mutex_unlock(&glob_mutex_image_rw); return 0; } if(offsetlen) size=len-offset; if(SetVirtImageData(p_buf,offset,size)!=size) { LOG_ERROR("Couldn't write data to virtual image file!\n") pthread_mutex_unlock(&glob_mutex_image_rw); return 0; } } else { LOG_DEBUG("Attempt to write past EOF of virtual image file\n") pthread_mutex_unlock(&glob_mutex_image_rw); return 0; } // Allow other threads to read/write data again pthread_mutex_unlock(&glob_mutex_image_rw); - } else if(strcmp(p_path,glob_xmount.p_virtual_vmdk_path)==0) { + } else if(strcmp(p_path,glob_xmount.output.vmdk.p_virtual_vmdk_path)==0) { pthread_mutex_lock(&glob_mutex_image_rw); - len=glob_vmdk_file_size; + len=glob_xmount.output.vmdk.vmdk_file_size; if((offset+size)>len) { // Enlarge or create buffer if needed if(len==0) { len=offset+size; - XMOUNT_MALLOC(glob_p_vmdk_file,char*,len*sizeof(char)) + XMOUNT_MALLOC(glob_xmount.output.vmdk.p_vmdk_file, + char*, + len*sizeof(char)); } else { len=offset+size; - XMOUNT_REALLOC(glob_p_vmdk_file,char*,len*sizeof(char)) + XMOUNT_REALLOC(glob_xmount.output.vmdk.p_vmdk_file, + char*, + len*sizeof(char)); } - glob_vmdk_file_size=offset+size; + glob_xmount.output.vmdk.vmdk_file_size=offset+size; } // Copy data to buffer - memcpy(glob_p_vmdk_file+offset,p_buf,size); + memcpy(glob_xmount.output.vmdk.p_vmdk_file+offset,p_buf,size); pthread_mutex_unlock(&glob_mutex_image_rw); - } else if(glob_p_vmdk_lockfile_name!=NULL && - strcmp(p_path,glob_p_vmdk_lockfile_name)==0) + } else if(glob_xmount.output.vmdk.p_vmdk_lockfile_name!=NULL && + strcmp(p_path,glob_xmount.output.vmdk.p_vmdk_lockfile_name)==0) { pthread_mutex_lock(&glob_mutex_image_rw); - if((offset+size)>glob_vmdk_lockfile_size) { + if((offset+size)>glob_xmount.output.vmdk.vmdk_lockfile_size) { // Enlarge or create buffer if needed - if(glob_vmdk_lockfile_size==0) { - glob_vmdk_lockfile_size=offset+size; - XMOUNT_MALLOC(glob_p_vmdk_lockfile_data,char*, - glob_vmdk_lockfile_size*sizeof(char)) + if(glob_xmount.output.vmdk.vmdk_lockfile_size==0) { + glob_xmount.output.vmdk.vmdk_lockfile_size=offset+size; + XMOUNT_MALLOC(glob_xmount.output.vmdk.p_vmdk_lockfile_data,char*, + glob_xmount.output.vmdk.vmdk_lockfile_size*sizeof(char)) } else { - glob_vmdk_lockfile_size=offset+size; - XMOUNT_REALLOC(glob_p_vmdk_lockfile_data,char*, - glob_vmdk_lockfile_size*sizeof(char)) + glob_xmount.output.vmdk.vmdk_lockfile_size=offset+size; + XMOUNT_REALLOC(glob_xmount.output.vmdk.p_vmdk_lockfile_data,char*, + glob_xmount.output.vmdk.vmdk_lockfile_size*sizeof(char)) } } // Copy data to buffer - memcpy(glob_p_vmdk_lockfile_data+offset,p_buf,size); + memcpy(glob_xmount.output.vmdk.p_vmdk_lockfile_data+offset,p_buf,size); pthread_mutex_unlock(&glob_mutex_image_rw); } else if(strcmp(p_path,glob_xmount.p_virtual_info_path)==0) { // Attempt to write data to read only image info file LOG_DEBUG("Attempt to write data to virtual info file\n"); return -ENOENT; } else { // Attempt to write to non existant file LOG_DEBUG("Attempt to write to the non existant file \"%s\"\n",p_path) return -ENOENT; } return size; } /******************************************************************************* * Main ******************************************************************************/ int main(int argc, char *argv[]) { int nargc=0; char **pp_nargv=NULL; char *p_mountpoint=NULL; struct stat file_stat; int ret; int fuse_ret; char *p_err_msg; - pts_LibXmountMorphingInputImage p_morphing_input_image; // Set implemented FUSE functions struct fuse_operations xmount_operations = { //.access=FuseAccess, .getattr=FuseGetAttr, .mkdir=FuseMkDir, .mknod=FuseMkNod, .open=FuseOpen, .readdir=FuseReadDir, .read=FuseRead, .rename=FuseRename, .rmdir=FuseRmDir, //.statfs=FuseStatFs, .unlink=FuseUnlink, .write=FuseWrite }; // Disable std output / input buffering setbuf(stdout,NULL); setbuf(stderr,NULL); // Init glob_xmount - glob_xmount.input_images_count=0; - glob_xmount.pp_input_images=NULL; -#ifndef __APPLE__ - glob_xmount.VirtImageType=VirtImageType_DD; -#else - glob_xmount.VirtImageType=VirtImageType_DMG; -#endif - glob_xmount.debug=FALSE; - glob_xmount.p_virtual_image_path=NULL; - glob_xmount.p_virtual_vmdk_path=NULL; - glob_xmount.p_virtual_info_path=NULL; - glob_xmount.writable=FALSE; - glob_xmount.overwrite_cache=FALSE; - glob_xmount.p_cache_file=NULL; - glob_xmount.orig_image_size=0; - glob_xmount.virt_image_size=0; - glob_xmount.input_hash_lo=0; - glob_xmount.input_hash_hi=0; - glob_xmount.orig_img_offset=0; - glob_xmount.p_lib_params=NULL; - glob_xmount.may_set_fuse_allow_other=FALSE; - glob_xmount.p_morph_type=NULL; + InitResources(); // Load input and morphing libs if(!LoadLibs()) { LOG_ERROR("Unable to load any libraries!\n") return 1; } // Check FUSE settings CheckFuseSettings(); // Parse command line options if(!ParseCmdLine(argc, argv, &nargc, &pp_nargv, &p_mountpoint)) { PrintUsage(argv[0]); - UnloadInputLibs(); - if(glob_xmount.pp_input_images!=NULL) { - for(uint64_t i=0;ip_type); - for(uint64_t ii=0; - iifiles_count; - ii++) - { - free(glob_xmount.pp_input_images[i]->pp_files[ii]); - } - // There will always be files if struct was added to pp_input_images, - // so no need to check for NULL here - free(glob_xmount.pp_input_images[i]->pp_files); - free(glob_xmount.pp_input_images[i]); - } - free(glob_xmount.pp_input_images); - } + FreeResources(); return 1; } // Check command line options - if(glob_xmount.input_images_count==0) { + if(glob_xmount.input.images_count==0) { LOG_ERROR("No --in command line option specified!\n") PrintUsage(argv[0]); - UnloadInputLibs(); + FreeResources(); return 1; } if(nargc<2) { LOG_ERROR("Couldn't parse command line options!\n") PrintUsage(argv[0]); - UnloadInputLibs(); + FreeResources(); return 1; } - if(glob_xmount.p_morph_type==NULL) { - XMOUNT_STRSET(glob_xmount.p_morph_type,"combine"); + if(glob_xmount.morphing.p_morph_type==NULL) { + XMOUNT_STRSET(glob_xmount.morphing.p_morph_type,"combine"); } // Check if mountpoint is a valid dir if(stat(p_mountpoint,&file_stat)!=0) { LOG_ERROR("Unable to stat mount point '%s'!\n",p_mountpoint); PrintUsage(argv[0]); - UnloadInputLibs(); + FreeResources(); return 1; } if(!S_ISDIR(file_stat.st_mode)) { LOG_ERROR("Mount point '%s' is not a directory!\n",p_mountpoint); PrintUsage(argv[0]); - UnloadInputLibs(); + FreeResources(); return 1; } if(glob_xmount.debug==TRUE) { LOG_DEBUG("Options passed to FUSE: ") for(int i=0;ifiles_count==1) { + if(glob_xmount.input.pp_images[i]->files_count==1) { LOG_DEBUG("Loading image file \"%s\"...\n", - glob_xmount.pp_input_images[i]->pp_files[0]) + glob_xmount.input.pp_images[i]->pp_files[0]) } else { LOG_DEBUG("Loading image files \"%s .. %s\"...\n", - glob_xmount.pp_input_images[i]->pp_files[0], - glob_xmount.pp_input_images[i]-> - pp_files[glob_xmount.pp_input_images[i]->files_count-1]) + glob_xmount.input.pp_images[i]->pp_files[0], + glob_xmount.input.pp_images[i]-> + pp_files[glob_xmount.input.pp_images[i]->files_count-1]) } } // Find input lib - if(!FindInputLib(glob_xmount.pp_input_images[i])) { + if(!FindInputLib(glob_xmount.input.pp_images[i])) { LOG_ERROR("Unknown input image type '%s' for input image '%s'!\n", - glob_xmount.pp_input_images[i]->p_type, - glob_xmount.pp_input_images[i]->pp_files[0]) + glob_xmount.input.pp_images[i]->p_type, + glob_xmount.input.pp_images[i]->pp_files[0]) PrintUsage(argv[0]); - // TODO: Free already created handles - UnloadInputLibs(); - // TODO: Free glob_xmount members + FreeResources(); return 1; } // Init input image handle - ret=glob_xmount.pp_input_images[i]->p_functions-> - CreateHandle(&(glob_xmount.pp_input_images[i]->p_handle), - glob_xmount.pp_input_images[i]->p_type); + ret=glob_xmount.input.pp_images[i]->p_functions-> + CreateHandle(&(glob_xmount.input.pp_images[i]->p_handle), + glob_xmount.input.pp_images[i]->p_type); if(ret!=0) { LOG_ERROR("Unable to init input handle for input image '%s': %s!\n", - glob_xmount.pp_input_images[i]->pp_files[0], - glob_xmount.pp_input_images[i]->p_functions-> + glob_xmount.input.pp_images[i]->pp_files[0], + glob_xmount.input.pp_images[i]->p_functions-> GetErrorMessage(ret)); - // TODO: Free already created handles - UnloadInputLibs(); - // TODO: Free glob_xmount members + FreeResources(); return 1; } // Parse input lib specific options - if(glob_xmount.p_lib_params!=NULL) { - ret=glob_xmount.pp_input_images[i]->p_functions-> - OptionsParse(glob_xmount.pp_input_images[i]->p_handle, - glob_xmount.p_lib_params, + if(glob_xmount.input.p_lib_params!=NULL) { + ret=glob_xmount.input.pp_images[i]->p_functions-> + OptionsParse(glob_xmount.input.pp_images[i]->p_handle, + glob_xmount.input.p_lib_params, &p_err_msg); if(ret!=0) { if(p_err_msg!=NULL) { LOG_ERROR("Unable to parse input library specific options for image " "'%s': %s: %s!\n", - glob_xmount.pp_input_images[i]->pp_files[0], - glob_xmount.pp_input_images[i]->p_functions-> + glob_xmount.input.pp_images[i]->pp_files[0], + glob_xmount.input.pp_images[i]->p_functions-> GetErrorMessage(ret), p_err_msg); - glob_xmount.pp_input_images[i]->p_functions->FreeBuffer(p_err_msg); - // TODO: Free already created handles - UnloadInputLibs(); - // TODO: Free glob_xmount members + glob_xmount.input.pp_images[i]->p_functions->FreeBuffer(p_err_msg); + FreeResources(); return 1; } else { LOG_ERROR("Unable to parse input library specific options for image " "'%s': %s!\n", - glob_xmount.pp_input_images[i]->pp_files[0], - glob_xmount.pp_input_images[i]->p_functions-> + glob_xmount.input.pp_images[i]->pp_files[0], + glob_xmount.input.pp_images[i]->p_functions-> GetErrorMessage(ret)); - // TODO: Free already created handles - UnloadInputLibs(); - // TODO: Free glob_xmount members + FreeResources(); return 1; } } } // Open input image ret= - glob_xmount.pp_input_images[i]-> + glob_xmount.input.pp_images[i]-> p_functions-> - Open(&glob_xmount.pp_input_images[i]->p_handle, - (const char**)(glob_xmount.pp_input_images[i]->pp_files), - glob_xmount.pp_input_images[i]->files_count); + Open(&glob_xmount.input.pp_images[i]->p_handle, + (const char**)(glob_xmount.input.pp_images[i]->pp_files), + glob_xmount.input.pp_images[i]->files_count); if(ret!=0) { LOG_ERROR("Unable to open input image file '%s': %s!\n", - glob_xmount.pp_input_images[i]->pp_files[0], - glob_xmount.pp_input_images[i]->p_functions-> + glob_xmount.input.pp_images[i]->pp_files[0], + glob_xmount.input.pp_images[i]->p_functions-> GetErrorMessage(ret)); - // TODO: Free already created handles - UnloadInputLibs(); - // TODO: Free glob_xmount members + FreeResources(); return 1; } // Determine input image size - ret=glob_xmount.pp_input_images[i]-> + ret=glob_xmount.input.pp_images[i]-> p_functions-> - Size(glob_xmount.pp_input_images[i]->p_handle, - &(glob_xmount.pp_input_images[i]->size)); + Size(glob_xmount.input.pp_images[i]->p_handle, + &(glob_xmount.input.pp_images[i]->size)); if(ret!=0) { LOG_ERROR("Unable to determine size of input image '%s': %s!\n", - glob_xmount.pp_input_images[i]->pp_files[0], - glob_xmount.pp_input_images[i]-> + glob_xmount.input.pp_images[i]->pp_files[0], + glob_xmount.input.pp_images[i]-> p_functions->GetErrorMessage(ret)); - // TODO: Free + FreeResources(); return 1; } // If an offset was specified, check it against offset and change size - if(glob_xmount.orig_img_offset!=0) { - if(glob_xmount.orig_img_offset>glob_xmount.pp_input_images[i]->size) { + if(glob_xmount.input.image_offset!=0) { + if(glob_xmount.input.image_offset>glob_xmount.input.pp_images[i]->size) { LOG_ERROR("The specified offset is larger then the size of the input " "image '%s'! (%" PRIu64 " > %" PRIu64 ")\n", - glob_xmount.pp_input_images[i]->pp_files[0], - glob_xmount.orig_img_offset, - glob_xmount.pp_input_images[i]->size); - // TODO: Free already created handles - UnloadInputLibs(); - // TODO: Free glob_xmount members + glob_xmount.input.pp_images[i]->pp_files[0], + glob_xmount.input.image_offset, + glob_xmount.input.pp_images[i]->size); + FreeResources(); return 1; } - glob_xmount.pp_input_images[i]->size-=glob_xmount.orig_img_offset; + glob_xmount.input.pp_images[i]->size-=glob_xmount.input.image_offset; } LOG_DEBUG("Input image loaded successfully\n") } // Find morphing lib if(FindMorphingLib()!=TRUE) { LOG_ERROR("Unable to find a library supporting the morphing type '%s'!\n", - glob_xmount.p_morph_type); - // TODO: Free + glob_xmount.morphing.p_morph_type); + FreeResources(); return 1; } // Init morphing - ret=glob_p_morphing_functions->CreateHandle(&glob_p_morphing_handle, - glob_xmount.p_morph_type); + ret=glob_xmount.morphing.p_functions-> + CreateHandle(&glob_xmount.morphing.p_handle, + glob_xmount.morphing.p_morph_type, + glob_xmount.debug); if(ret!=0) { LOG_ERROR("Unable to create morphing handle: %s!\n", - glob_p_morphing_functions->GetErrorMessage(ret)); - // TODO: Free - return 1; - } - glob_pp_morphing_input_images= - malloc(glob_xmount.input_images_count* - sizeof(pts_LibXmountMorphingInputImage)); - if(glob_pp_morphing_input_images==NULL) { - // TODO: Free + glob_xmount.morphing.p_functions->GetErrorMessage(ret)); + FreeResources(); return 1; } - for(uint64_t i=0;ip_image_handle=glob_xmount.pp_input_images[i]; - p_morphing_input_image->size=glob_xmount.pp_input_images[i]->size; - p_morphing_input_image->Read=&GetInputImageData_MorphWrapper; - glob_pp_morphing_input_images[i]=p_morphing_input_image; - } - ret=glob_p_morphing_functions->Morph(glob_p_morphing_handle, - glob_xmount.input_images_count, - glob_pp_morphing_input_images); + ret=glob_xmount.morphing.p_functions-> + Morph(glob_xmount.morphing.p_handle, + &(glob_xmount.morphing.input_image_functions)); if(ret!=0) { LOG_ERROR("Unable to start morphing: %s!\n", - glob_p_morphing_functions->GetErrorMessage(ret)); - // TODO: Free + glob_xmount.morphing.p_functions->GetErrorMessage(ret)); + FreeResources(); return 1; } // Init mutexes pthread_mutex_init(&glob_mutex_image_rw,NULL); pthread_mutex_init(&glob_mutex_info_read,NULL); // Init random generator srand(time(NULL)); // Calculate partial MD5 hash of input image file - if(CalculateInputImageHash(&(glob_xmount.input_hash_lo), - &(glob_xmount.input_hash_hi))==FALSE) + if(CalculateInputImageHash(&(glob_xmount.input.image_hash_lo), + &(glob_xmount.input.image_hash_hi))==FALSE) { LOG_ERROR("Couldn't calculate partial hash of morphed image!\n") return 1; } if(glob_xmount.debug==TRUE) { LOG_DEBUG("Partial MD5 hash of morphed image: ") for(int i=0;i<8;i++) - printf("%02hhx",*(((char*)(&(glob_xmount.input_hash_lo)))+i)); + printf("%02hhx",*(((char*)(&(glob_xmount.input.image_hash_lo)))+i)); for(int i=0;i<8;i++) - printf("%02hhx",*(((char*)(&(glob_xmount.input_hash_hi)))+i)); + printf("%02hhx",*(((char*)(&(glob_xmount.input.image_hash_hi)))+i)); printf("\n"); } - if(!ExtractVirtFileNames(glob_xmount.pp_input_images[0]->pp_files[0])) { + if(!ExtractVirtFileNames(glob_xmount.input.pp_images[0]->pp_files[0])) { LOG_ERROR("Couldn't extract virtual file names!\n"); - UnloadInputLibs(); + FreeResources(); return 1; } LOG_DEBUG("Virtual file names extracted successfully\n") // Gather infos for info file if(!InitVirtImageInfoFile()) { LOG_ERROR("Couldn't gather infos for virtual image info file!\n") - UnloadInputLibs(); + FreeResources(); return 1; } LOG_DEBUG("Virtual image info file build successfully\n") // Do some virtual image type specific initialisations - switch(glob_xmount.VirtImageType) { + switch(glob_xmount.output.VirtImageType) { case VirtImageType_DD: case VirtImageType_DMG: break; case VirtImageType_VDI: // When mounting as VDI, we need to construct a vdi header if(!InitVirtVdiHeader()) { LOG_ERROR("Couldn't initialize virtual VDI file header!\n") - UnloadInputLibs(); + FreeResources(); return 1; } LOG_DEBUG("Virtual VDI file header build successfully\n") break; case VirtImageType_VHD: // When mounting as VHD, we need to construct a vhd footer if(!InitVirtVhdHeader()) { LOG_ERROR("Couldn't initialize virtual VHD file footer!\n") - UnloadInputLibs(); + FreeResources(); return 1; } LOG_DEBUG("Virtual VHD file footer build successfully\n") break; case VirtImageType_VMDK: case VirtImageType_VMDKS: // When mounting as VMDK, we need to construct the VMDK descriptor file if(!InitVirtualVmdkFile()) { LOG_ERROR("Couldn't initialize virtual VMDK file!\n") - UnloadInputLibs(); + FreeResources(); return 1; } break; } - if(glob_xmount.writable) { + if(glob_xmount.output.writable) { // Init cache file and cache file block index if(!InitCacheFile()) { LOG_ERROR("Couldn't initialize cache file!\n") - UnloadInputLibs(); + FreeResources(); return 1; } LOG_DEBUG("Cache file initialized successfully\n") } // Call fuse_main to do the fuse magic fuse_ret=fuse_main(nargc,pp_nargv,&xmount_operations,NULL); // Destroy mutexes pthread_mutex_destroy(&glob_mutex_image_rw); pthread_mutex_destroy(&glob_mutex_info_read); - // TODO: Close input images and destroy handle -/* - for(uint64_t i=0;ipp_files[0]); - - - } - - ret=glob_p_input_functions->Close(&glob_p_input_image); - if(ret!=0) { - LOG_ERROR("Unable to close input image file: %s!", - glob_p_input_functions->GetErrorMessage(ret)); - } - ret=glob_p_input_functions->DestroyHandle(&glob_p_input_image); - if(ret!=0) { - LOG_ERROR("Unable to destroy input image handle: %s!", - glob_p_input_functions->GetErrorMessage(ret)); - } -*/ - // Close cache file if write support was enabled - if(glob_xmount.writable) { + if(glob_xmount.output.writable) { fclose(glob_p_cache_file); free(glob_p_cache_header); } // Free allocated memory // Free info file content if(glob_p_info_file!=NULL) free(glob_p_info_file); - // Free output image specific data - switch(glob_xmount.VirtImageType) { - case VirtImageType_DD: - case VirtImageType_DMG: - break; - case VirtImageType_VDI: - free(glob_p_vdi_header); - break; - case VirtImageType_VHD: - free(glob_p_vhd_header); - break; - case VirtImageType_VMDK: - case VirtImageType_VMDKS: { - free(glob_p_vmdk_file); - free(glob_xmount.p_virtual_vmdk_path); - if(glob_p_vmdk_lockfile_name!=NULL) free(glob_p_vmdk_lockfile_name); - if(glob_p_vmdk_lockfile_data!=NULL) free(glob_p_vmdk_lockfile_data); - if(glob_p_vmdk_lockdir1!=NULL) free(glob_p_vmdk_lockdir1); - if(glob_p_vmdk_lockdir2!=NULL) free(glob_p_vmdk_lockdir2); - break; - } - } + // Free constructed argv if(pp_nargv!=NULL) { for(int i=0;i32bit 20090308: * Added SetVdiFileHeaderData function to handle virtual image type specific data to be cached. This makes cache files independent from virtual image type 20090316: v0.2.0 released 20090327: v0.2.1 released * Fixed a bug in virtual write support. Checking whether data is cached didn't use semaphores. This could corrupt cache files when running multi-threaded. * Added IsVdiFileHeaderCached function to check whether VDI file header was already cached * Added IsBlockCached function to check whether a block was already cached 20090331: v0.2.2 released (Internal release) * Further changes to semaphores to fix write support bug. 20090410: v0.2.3 released * Reverted most of the fixes from v0.2.1 and v0.2.2 as those did not solve the write support bug. * Removed all semaphores * Added two pthread mutexes to protect virtual image and virtual info file. 20090508: * Configure script will now exit when needed libraries aren't found * Added support for newest libewf beta version 20090506 as it seems to reduce memory usage when working with EWF files by about 1/2. * Added LIBEWF_BETA define to adept source to new libewf API. * Added function InitVirtualVmdkFile to build a VmWare virtual disk descriptor file. 20090519: * Added function CreateVirtDir implementing FUSE's mkdir to allow VMWare to create his .vmdk.lck lock folder. Function does not allow to create other folders! * Changed cache file handling as VMDK caching will need new cache file structure incompatible to the old one. 20090522: v0.3.0 released * Added function DeleteVirtFile and DeleteVirtDir so VMWare can remove his lock directories and files. * Added function RenameVirtFile because VMWare needs to rename his lock files. * VMDK support should work now but descriptor file won't get cached as I didn't implement it yet. 20090604: * Added --cache commandline parameter doing the same as --rw. * Added --owcache commandline parameter doing the same as --rw but overwrites any existing cache data. This can be handy for debugging and testing purposes. * Added "vmdks" output type. Same as "vmdk" but generates a disk connected to the SCSI bus rather than the IDE bus. 20090710: v0.3.1 released 20090721: * Added function CheckFuseAllowOther to check wether FUSE supports the "-o allow_other" option. It is supported when "user_allow_other" is set in /etc/fuse.conf or when running xmount as root. * Automatic addition of FUSE's "-o allow_other" option if it is supported. * Added special "-o no_allow_other" command line parameter to disable automatic addition of the above option. * Reorganisation of FUSE's and xmount's command line options processing. * Added LogWarnMessage function to output a warning message. 20090722: * Added function CalculateInputImageHash to calculate an MD5 hash of the first input image's HASH_AMOUNT bytes of data. This hash is used as VDI creation UUID and will later be used to match cache files to input images. 20090724: v0.3.2 released 20090725: v0.4.0 released * Added AFF input image support. * Due to various problems with libewf and libaff packages (Mainly in Debian and Ubuntu), I decided to include them into xmount's source tree and link them in statically. This has the advantage that I can use whatever version I want. 20090727: v0.4.1 released * Added again the ability to compile xmount with shared libs as the Debian folks don't like the static ones :) 20090812: * Added TXMountConfData.OrigImageSize and TXMountConfData.VirtImageSize to save the size of the input and output image in order to avoid regetting it always from disk. 20090814: * Replaced all malloc and realloc occurences with the two macros XMOUNT_MALLOC and XMOUNT_REALLOC. 20090816: * Replaced where applicable all occurences of str(n)cpy or alike with their corresponding macros XMOUNT_STRSET, XMOUNT_STRCPY and XMOUNT_STRNCPY pendants. 20090907: v0.4.2 released * Fixed a bug in VMDK lock file access. glob_vmdk_lockfile_size wasn't reset to 0 when the file was deleted. * Fixed a bug in VMDK descriptor file access. Had to add glob_vmdk_file_size to track the size of this file as strlen was a bad idea :). 20100324: v0.4.3 released * Changed all header structs to prevent different sizes on i386 and amd64. See xmount.h for more details. 20100810: v0.4.4 released * Found a bug in InitVirtVdiHeader(). The 64bit values were addressed incorrectly while filled with rand(). This leads to an error message when trying to add a VDI file to VirtualBox 3.2.8. 20110210: * Adding subtype and fsname FUSE options in order to display mounted source in mount command output. 20110211: v0.4.5 released 20111011: * Changes to deal with libewf v2 API (thx to Joachim Metz) 20111109: v0.4.6 released * Added support for DMG output type (actually a DD with .dmg file extension). This type is used as default output type when using xmount under Mac OS X. 20120130: v0.4.7 released * Made InitVirtImageInfoFile less picky about missing EWF infos. 20120507: * Added support for VHD output image as requested by various people. * Statically linked libs updated to 20120504 (libewf) and 3.7.0 (afflib). 20120510: v0.5.0 released * Added stbuf->st_blocks calculation for VHD images in function GetVirtFileAttr. This makes Windows not think the emulated file would be a sparse file. Sparse vhd files are not attachable in Windows. 20130726: v0.6.0 released * Added libaaff to replace libaff (thx to Guy Voncken). * Added libdd to replace raw dd input file handling and finally support split dd files (thx to Guy Voncken). 20140311: * Added libaewf (thx to Guy Voncken). 20140726: * Added support for dynamically loading of input libs. This should ease adding support for new input image formats in the future. * Moved input image functions to their corresponding dynamically loadable libs. * Prepended "glob_" to all global vars for better identification. 20140731: * Added --offset option as requested by HPM. * Began massive code cleanup. 20140803: * Added correct return code handling when calling input lib functions including getting error messages using GetErrorMessage. * Added input lib specific option parsing. * Re-implemented InitVirtImageInfoFile using input lib's GetInfofileContent function. * Further code cleanups. 20140807: * Further code cleanups. * Renamed GetVirtFileAttr to FuseGetAttr * Renamed CreateVirtDir to FuseMkDir * Renamed CreateVirtDir to FuseMkNod * Renamed OpenVirtFile to FuseOpen * Renamed GetVirtFiles to FuseReadDir * Renamed ReadVirtFile to FuseRead * Renamed RenameVirtFile to FuseRename * Renamed DeleteVirtDir to FuseRmDir * Renamed DeleteVirtFile to FuseUnlink * Renamed WriteVirtFile to FuseWrite * Fixed bug in CalculateInputImageHash where always HASH_AMOUNT bytes were hased even if input image is smaller. * Fixed a newly introduced bug in FuseRead and GetVirtImageData returning -EIO when trying to read behind EOF. The correct return value is 0. 20140811: * Renamed CheckFuseAllowOther to CheckFuseSettings and added a check to see if user is part of the fuse group. 20140814: * Replaced cuserid() with getpwuid(geteuid()) in CheckFuseSettings as it is deprecated on Linux and not available on OSx. * Only build fuse group checks from CheckFuseSettings on Linux. + 20140825: * Added InitResources() and FreeResources() functions. */ diff --git a/trunk/src/xmount.h b/trunk/src/xmount.h index 52a3ed3..0926d19 100755 --- a/trunk/src/xmount.h +++ b/trunk/src/xmount.h @@ -1,371 +1,438 @@ /******************************************************************************* * xmount Copyright (c) 2008-2014 by Gillen Daniel * * * * xmount is a small tool to "fuse mount" various image formats and enable * * virtual write access. * * * * 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 "../libxmount_input/libxmount_input.h" #include "../libxmount_morphing/libxmount_morphing.h" #undef FALSE #undef TRUE #define FALSE 0 #define TRUE 1 /* * Constants */ #define IMAGE_INFO_HEADER "The following values have been extracted from " \ "the mounted image file:\n\n" -//! Virtual image types -typedef enum e_VirtImageType { - //! Virtual image is a DD file - VirtImageType_DD, - //! Virtual image is a DMG file - VirtImageType_DMG, - //! Virtual image is a VDI file - VirtImageType_VDI, - //! Virtual image is a VMDK file (IDE bus) - VirtImageType_VMDK, - //! Virtual image is a VMDK file (SCSI bus) - VirtImageType_VMDKS, - //! Virtual image is a VHD file - VirtImageType_VHD -} te_VirtImageType; - -//! Infos about input libs -typedef struct s_InputLib { - //! Filename of lib (without path) - char *p_name; - //! Handle to the loaded lib - void *p_lib; - //! Array of supported input types - char *p_supported_input_types; - //! Struct containing lib functions - ts_LibXmountInputFunctions lib_functions; -} ts_InputLib, *pts_InputLib; - -//! Input image array element -typedef struct s_InputImage { - //! Image type - char *p_type; - //! Image source file count - uint64_t files_count; - //! Image source files - char **pp_files; - //! Input lib functions for this image - pts_LibXmountInputFunctions p_functions; - //! Image handle - void *p_handle; - //! Image size - uint64_t size; -} ts_InputImage, *pts_InputImage; - -//! Infos about morphing libs -typedef struct s_MorphingLib { - //! Filename of lib (without path) - char *p_name; - //! Handle to the loaded lib - void *p_lib; - //! Array of supported morphing types - char *p_supported_morphing_types; - //! Struct containing lib functions - ts_LibXmountMorphingFunctions lib_functions; -} ts_MorphingLib, *pts_MorphingLib; - -//! Xmount global runtime data -typedef struct s_XmountData { - //! Input image count - uint64_t input_images_count; - //! Input images - pts_InputImage *pp_input_images; - //! Virtual image type - te_VirtImageType VirtImageType; - //! Enable debug output - uint8_t debug; - //! Path of virtual image file - char *p_virtual_image_path; - //! Path of virtual VMDK file - char *p_virtual_vmdk_path; - //! Path of virtual image info file - char *p_virtual_info_path; - //! Enable virtual write support - uint8_t writable; - //! Overwrite existing cache - uint8_t overwrite_cache; - //! Cache file to save changes to - char *p_cache_file; - //! Size of input image (after morph) - uint64_t orig_image_size; - //! Size of virtual image - uint64_t virt_image_size; - //! MD5 hash of partial input image (lower 64 bit) (after morph) - uint64_t input_hash_lo; - //! MD5 hash of partial input image (higher 64 bit) (after morph) - uint64_t input_hash_hi; - //! Input image offset - uint64_t orig_img_offset; - //! Input lib params - char *p_lib_params; - //! Set if we are allowed to set fuse's allow_other option - uint8_t may_set_fuse_allow_other; - //! Specified morph type - char *p_morph_type; - //! Morph lib params - char **pp_morph_params; -} ts_XmountData; +/******************************************************************************* + * Structures of output images + ******************************************************************************/ #define VDI_FILE_COMMENT "<<< This is a virtual VDI image >>>" #define VDI_HEADER_COMMENT "This VDI was emulated using xmount v" XMOUNT_VERSION #define VDI_IMAGE_SIGNATURE 0xBEDA107F // 1:1 copy from hp #define VDI_IMAGE_VERSION 0x00010001 // Vers 1.1 #define VDI_IMAGE_TYPE_FIXED 0x00000002 // Type 2 (fixed size) #define VDI_IMAGE_FLAGS 0 #define VDI_IMAGE_BLOCK_SIZE (1024*1024) // 1 Megabyte //! VDI Binary File Header structure typedef struct s_VdiFileHeader { // ----- VDIPREHEADER ------ //! Just text info about image type, for eyes only char szFileInfo[64]; //! The image signature (VDI_IMAGE_SIGNATURE) uint32_t u32Signature; //! The image version (VDI_IMAGE_VERSION) uint32_t u32Version; // ----- VDIHEADER1PLUS ----- //! Size of header structure in bytes. uint32_t cbHeader; //! The image type (VDI_IMAGE_TYPE_*) uint32_t u32Type; //! Image flags (VDI_IMAGE_FLAGS_*) uint32_t fFlags; //! Image comment (UTF-8) char szComment[256]; //! Offset of Blocks array from the begining of image file. // Should be sector-aligned for HDD access optimization. uint32_t offBlocks; //! Offset of image data from the begining of image file. // Should be sector-aligned for HDD access optimization. uint32_t offData; //! Legacy image geometry - Cylinders uint32_t cCylinders; //! Legacy image geometry - Heads uint32_t cHeads; //! Legacy image geometry - Sectors per track uint32_t cSectors; //! Legacy image geometry - Sector size (bytes per sector) uint32_t cbSector; //! Was BIOS HDD translation mode, now unused uint32_t u32Dummy; //! Size of disk (in bytes) uint64_t cbDisk; //! Block size (for instance VDI_IMAGE_BLOCK_SIZE). Must be a power of 2! uint32_t cbBlock; //! Size of additional service information of every data block. // Prepended before block data. May be 0. // Should be a power of 2 and sector-aligned for optimization reasons. uint32_t cbBlockExtra; //! Number of blocks uint32_t cBlocks; //! Number of allocated blocks uint32_t cBlocksAllocated; //! UUID of image (lower 64 bit) uint64_t uuidCreate_l; //! UUID of image (higher 64 bit) uint64_t uuidCreate_h; //! UUID of image's last modification (lower 64 bit) uint64_t uuidModify_l; //! UUID of image's last modification (higher 64 bit) uint64_t uuidModify_h; //! Only for secondary images - UUID of previous image (lower 64 bit) uint64_t uuidLinkage_l; //! Only for secondary images - UUID of previous image (higher 64 bit) uint64_t uuidLinkage_h; //! Only for secondary images - UUID of prev image's last mod (lower 64 bit) uint64_t uuidParentModify_l; //! Only for secondary images - UUID of prev image's last mod (higher 64 bit) uint64_t uuidParentModify_h; //! Padding to get 512 byte alignment char padding[56]; } __attribute__ ((packed)) ts_VdiFileHeader, *pts_VdiFileHeader; // /** The way the UUID is declared by the DCE specification. */ // struct // { // uint32_t u32TimeLow; // uint16_t u16TimeMid; // uint16_t u16TimeHiAndVersion; // uint8_t u8ClockSeqHiAndReserved; // uint8_t u8ClockSeqLow; // uint8_t au8Node[6]; // } Gen; /* * VHD Binary File footer structure * * At the time of writing, the specs could be found here: * http://www.microsoft.com/downloads/details.aspx? * FamilyID=C2D03242-2FFB-48EF-A211-F0C44741109E * * Warning: All values are big-endian! */ // #ifdef __LP64__ #define VHD_IMAGE_HVAL_COOKIE 0x78697463656E6F63 // "conectix" #else #define VHD_IMAGE_HVAL_COOKIE 0x78697463656E6F63LL #endif #define VHD_IMAGE_HVAL_FEATURES 0x02000000 #define VHD_IMAGE_HVAL_FILE_FORMAT_VERSION 0x00000100 #ifdef __LP64__ #define VHD_IMAGE_HVAL_DATA_OFFSET 0xFFFFFFFFFFFFFFFF #else #define VHD_IMAGE_HVAL_DATA_OFFSET 0xFFFFFFFFFFFFFFFFLL #endif #define VHD_IMAGE_HVAL_CREATOR_APPLICATION 0x746E6D78 // "xmnt" #define VHD_IMAGE_HVAL_CREATOR_VERSION 0x00000500 // This one is funny! According to VHD specs, I can only choose between Windows // and Macintosh. I'm going to choose the most common one. #define VHD_IMAGE_HVAL_CREATOR_HOST_OS 0x6B326957 // "Win2k" #define VHD_IMAGE_HVAL_DISK_TYPE 0x02000000 // Seconds from January 1st, 1970 to January 1st, 2000 #define VHD_IMAGE_TIME_CONVERSION_OFFSET 0x386D97E0 typedef struct s_VhdFileHeader { uint64_t cookie; uint32_t features; uint32_t file_format_version; uint64_t data_offset; uint32_t creation_time; uint32_t creator_app; uint32_t creator_ver; uint32_t creator_os; uint64_t size_original; uint64_t size_current; uint16_t disk_geometry_c; uint8_t disk_geometry_h; uint8_t disk_geometry_s; uint32_t disk_type; uint32_t checksum; uint64_t uuid_l; uint64_t uuid_h; uint8_t saved_state; char reserved[427]; } __attribute__ ((packed)) ts_VhdFileHeader, *pts_VhdFileHeader; +/******************************************************************************* + * Xmount specific structures + ******************************************************************************/ + #ifdef __LP64__ #define CACHE_BLOCK_FREE 0xFFFFFFFFFFFFFFFF #else #define CACHE_BLOCK_FREE 0xFFFFFFFFFFFFFFFFLL #endif //! Cache file block index array element typedef struct s_CacheFileBlockIndex { //! Set to 1 if block is assigned (this block has data in cache file) uint32_t Assigned; //! Offset to data in cache file uint64_t off_data; } __attribute__ ((packed)) ts_CacheFileBlockIndex, *pts_CacheFileBlockIndex; #define CACHE_BLOCK_SIZE (1024*1024) // 1 megabyte #ifdef __LP64__ #define CACHE_FILE_SIGNATURE 0xFFFF746E756F6D78 // "xmount\xFF\xFF" #else #define CACHE_FILE_SIGNATURE 0xFFFF746E756F6D78LL #endif #define CUR_CACHE_FILE_VERSION 0x00000002 // Current cache file version #define HASH_AMOUNT (1024*1024)*10 // Amount of data used to construct a // "unique" hash for every input image // (10MByte) //! Cache file header structure typedef struct s_CacheFileHeader { //! Simple signature to identify cache files uint64_t FileSignature; //! Cache file version uint32_t CacheFileVersion; //! Cache block size uint64_t BlockSize; //! Total amount of cache blocks uint64_t BlockCount; //! Offset to the first block index array element uint64_t pBlockIndex; //! Set to 1 if VDI file header is cached uint32_t VdiFileHeaderCached; //! Offset to cached VDI file header uint64_t pVdiFileHeader; //! Set to 1 if VMDK file is cached uint32_t VmdkFileCached; //! Size of VMDK file uint64_t VmdkFileSize; //! Offset to cached VMDK file uint64_t pVmdkFile; //! Set to 1 if VHD header is cached uint32_t VhdFileHeaderCached; //! Offset to cached VHD header uint64_t pVhdFileHeader; //! Padding to get 512 byte alignment and ease further additions char HeaderPadding[432]; } __attribute__ ((packed)) ts_CacheFileHeader, *pts_CacheFileHeader; //! Cache file header structure - Old v1 header typedef struct s_CacheFileHeader_v1 { //! Simple signature to identify cache files uint64_t FileSignature; //! Cache file version uint32_t CacheFileVersion; //! Total amount of cache blocks uint64_t BlockCount; //! Offset to the first block index array element uint64_t pBlockIndex; //! Set to 1 if VDI file header is cached uint32_t VdiFileHeaderCached; //! Offset to cached VDI file header uint64_t pVdiFileHeader; //! Set to 1 if VMDK file is cached } ts_CacheFileHeader_v1, *pts_CacheFileHeader_v1; +//! Virtual image types +typedef enum e_VirtImageType { + //! Virtual image is a DD file + VirtImageType_DD, + //! Virtual image is a DMG file + VirtImageType_DMG, + //! Virtual image is a VDI file + VirtImageType_VDI, + //! Virtual image is a VMDK file (IDE bus) + VirtImageType_VMDK, + //! Virtual image is a VMDK file (SCSI bus) + VirtImageType_VMDKS, + //! Virtual image is a VHD file + VirtImageType_VHD +} te_VirtImageType; + +//! Structure containing infos about input libs +typedef struct s_InputLib { + //! Filename of lib (without path) + char *p_name; + //! Handle to the loaded lib + void *p_lib; + //! Array of supported input types + char *p_supported_input_types; + //! Struct containing lib functions + ts_LibXmountInputFunctions lib_functions; +} ts_InputLib, *pts_InputLib; + +//! Structure containing infos about input images +typedef struct s_InputImage { + //! Image type + char *p_type; + //! Image source file count + uint64_t files_count; + //! Image source files + char **pp_files; + //! Input lib functions for this image + pts_LibXmountInputFunctions p_functions; + //! Image handle + void *p_handle; + //! Image size + uint64_t size; +} ts_InputImage, *pts_InputImage; + +typedef struct s_InputData { + //! Loaded input lib count + uint32_t libs_count; + //! Array containing infos about loaded input libs + pts_InputLib *pp_libs; + //! Input lib params (--inopts) + char *p_lib_params; + //! Input image count + uint64_t images_count; + //! Input images + pts_InputImage *pp_images; + //! Input image offset (--offset) + uint64_t image_offset; + //! MD5 hash of partial input image (lower 64 bit) (after morph) + uint64_t image_hash_lo; + //! MD5 hash of partial input image (higher 64 bit) (after morph) + uint64_t image_hash_hi; +} ts_InputData; + +//! Structure containing infos about morphing libs +typedef struct s_MorphingLib { + //! Filename of lib (without path) + char *p_name; + //! Handle to the loaded lib + void *p_lib; + //! Array of supported morphing types + char *p_supported_morphing_types; + //! Struct containing lib functions + ts_LibXmountMorphingFunctions lib_functions; +} ts_MorphingLib, *pts_MorphingLib; + +//! Structures and vars needed for morph support +typedef struct s_MorphingData { + //! Loaded morphing lib count + uint32_t libs_count; + //! Array containing infos about loaded morphing libs + pts_MorphingLib *pp_libs; + //! Specified morphing type (--morph) + char *p_morph_type; + //! Specified morphing lib params (--morphopts) + char **pp_lib_params; + //! Handle to initialized morphing lib + void *p_handle; + //! Morphing functions of initialized lib + pts_LibXmountMorphingFunctions p_functions; + //! Input image functions passed to morphing lib + ts_LibXmountMorphingInputFunctions input_image_functions; +} ts_MorphingData; + +//! Structures and vars needed for VDI support +typedef struct s_OutputImageVdiData { + uint32_t vdi_header_size; + pts_VdiFileHeader p_vdi_header; + uint32_t vdi_block_map_size; + char *p_vdi_block_map; +} ts_OutputImageVdiData; + +//! Structures and vars needed for VHD support +typedef struct s_OutputImageVhdData { + ts_VhdFileHeader *p_vhd_header; +} ts_OutputImageVhdData; + +//! Structures and vars needed for VMDK support +typedef struct s_OutputImageVmdkData { + //! Path of virtual VMDK file + char *p_virtual_vmdk_path; + char *p_vmdk_file; + int vmdk_file_size; + char *p_vmdk_lockdir1; + char *p_vmdk_lockdir2; + char *p_vmdk_lockfile_data; + int vmdk_lockfile_size; + char *p_vmdk_lockfile_name; +} ts_OutputImageVmdkData; + +//! Structure containing infos about output image +typedef struct s_OutputData { + //! Virtual image type + te_VirtImageType VirtImageType; + //! Size + uint64_t image_size; + //! Writable? (Set to 1 if --cache was specified) + uint8_t writable; + //! Path of virtual image file + char *p_virtual_image_path; + //! VDI related data + ts_OutputImageVdiData vdi; + //! VHD related data + ts_OutputImageVhdData vhd; + //! VMDK related data + ts_OutputImageVmdkData vmdk; +} ts_OutputData; + +//! Structure containing global xmount runtime infos +typedef struct s_XmountData { + //! Input image related data + ts_InputData input; + //! Morphing related data + ts_MorphingData morphing; + //! Output image related data + ts_OutputData output; + //! Path of virtual image info file + char *p_virtual_info_path; + //! Enable debug output + uint8_t debug; + //! Overwrite existing cache + uint8_t overwrite_cache; + //! Cache file to save changes to + char *p_cache_file; + //! Set if we are allowed to set fuse's allow_other option + uint8_t may_set_fuse_allow_other; +} ts_XmountData; + /* ----- Change log ----- 20090226: * Added change history information to this file. * Added TVirtImageType enum to identify virtual image type. * Added TOrigImageType enum to identify input image type. * Added TMountimgConfData struct to hold various mountimg runtime options. * Renamed VDIFILEHEADER to ts_VdiFileHeader. 20090228: * Added LOG_ERROR and LOG_DEBUG macros * Added defines for various static VDI header values * Added defines for TRUE and FALSE 20090307: * Added defines for various static cache file header values * Added VdiFileHeaderCached and pVdiFileHeader values to be able to cache the VDI file header separatly. 20090519: * Added new cache file header structure and moved old one to ts_CacheFileHeader_v1. * New cache file structure includes VmdkFileCached and pVmdkFile to cache virtual VMDK file and makes room for further additions so current cache file version 2 cache files can be easily converted to newer ones. 20090814: * Added XMOUNT_MALLOC and XMOUNT_REALLOC macros. 20090816: * Added XMOUNT_STRSET, XMOUNT_STRNSET, XMOUNT_STRAPP and XMOUNT_STRNAPP macros. 20100324: * Added "__attribute__ ((packed))" to all header structs to prevent different sizes on i386 and amd64. 20111109: * Added TVirtImageType_DMG type. 20120130: * Added LOG_WARNING macro. 20120507: * Added ts_VhdFileHeader structure. 20120511: * Added endianness conversation macros 20140809: * Moved endianness macros to separate file 20140810: * Moved convenience macros to separate file */