diff --git a/src/xmount.c b/src/xmount.c index cae2851..bac6ddc 100755 --- a/src/xmount.c +++ b/src/xmount.c @@ -1,2170 +1,2168 @@ /******************************************************************************* * xmount Copyright (c) 2008-2016 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 // 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 #include "xmount.h" #include "xmount_fuse.h" #include "md5.h" #include "macros.h" #include "../libxmount/libxmount.h" #define XMOUNT_COPYRIGHT_NOTICE \ "xmount v%s Copyright (c) 2008-2016 by Gillen Daniel " #define LOG_WARNING(...) { \ LIBXMOUNT_LOG_WARNING(__VA_ARGS__); \ } #define LOG_ERROR(...) { \ LIBXMOUNT_LOG_ERROR(__VA_ARGS__); \ } #define LOG_DEBUG(...) { \ LIBXMOUNT_LOG_DEBUG(glob_xmount.debug,__VA_ARGS__); \ } #define HASH_AMOUNT (1024*1024)*10 // Amount of data used to construct a // "unique" hash for every input image // (10MByte) /******************************************************************************* * Forward declarations ******************************************************************************/ /* * Misc */ static void InitResources(); static void FreeResources(); static void PrintUsage(char*); static void CheckFuseSettings(); static int ParseCmdLine(const int, char**); static int ExtractOutputFileNames(char*); static int CalculateInputImageHash(uint64_t*, uint64_t*); /* * Info file */ static int InitInfoFile(); /* * Lib related */ static int LoadLibs(); static int FindInputLib(pts_InputImage); static int FindMorphingLib(); static int FindOutputLib(); static int SplitLibraryParameters(char*, uint32_t*, pts_LibXmountOptions**); /* * 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 exported to LibXmount_Output */ static int LibXmount_Output_Size(uint64_t*); static int LibXmount_Output_Read(char*, off_t, size_t, size_t*); static int LibXmount_Output_Write(char*, off_t, size_t, size_t*); /******************************************************************************* * Helper functions ******************************************************************************/ //! 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; int ret; printf("\n" XMOUNT_COPYRIGHT_NOTICE "\n",XMOUNT_VERSION); printf("\nUsage:\n"); printf(" %s [fopts] \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(" xopts:\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 first=1; 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 details.\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 "); // List supported morphing functions first=1; for(uint32_t i=0;ip_supported_morphing_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(" --morphopts : Specify morphing library specific " "options.\n"); printf(" specifies a comma separated list of key=value options. " "See below for details.\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("\"raw\".\n"); #endif printf(" can be "); // List supported output formats first=1; for(uint32_t i=0;ip_supported_output_formats; 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(" --outopts : Specify output library specific " "options.\n"); printf(" --owcache : Same as --cache but overwrites " "existing cache file.\n"); printf(" --sizelimit : The data end of input image(s) is set to no " "more than bytes after the data start.\n"); printf(" --version : Same as --info.\n"); printf("\n"); printf(" mntp:\n"); printf(" Mount point where output image should be located.\n"); printf("\n"); printf("Infos:\n"); printf(" * One --in option and a mount point are mandatory!\n"); printf(" * If you specify --in multiple times, data from all images is " "morphed into one output image using the specified morphing " "function.\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 / Morphing library specific options:\n"); printf(" Input / Morphing libraries might support an own set of " "options to configure / tune their behaviour.\n"); printf(" Libraries supporting this feature (if any) and their " "options are listed below.\n"); printf("\n"); // List input, morphing and output lib options for(uint32_t i=0;i lib_functions.OptionsHelp((const char**)&p_buf); if(ret!=0) { LOG_ERROR("Unable to get options help for library '%s': %s!\n", glob_xmount.input.pp_libs[i]->p_name, glob_xmount.input.pp_libs[i]-> lib_functions.GetErrorMessage(ret)); } if(p_buf==NULL) continue; printf(" - %s\n",glob_xmount.input.pp_libs[i]->p_name); printf("%s",p_buf); printf("\n"); ret=glob_xmount.input.pp_libs[i]->lib_functions.FreeBuffer(p_buf); if(ret!=0) { LOG_ERROR("Unable to free options help text from library '%s': %s!\n", glob_xmount.input.pp_libs[i]->p_name, glob_xmount.input.pp_libs[i]-> lib_functions.GetErrorMessage(ret)); } } for(uint32_t i=0;i lib_functions.OptionsHelp((const char**)&p_buf); if(ret!=0) { LOG_ERROR("Unable to get options help for library '%s': %s!\n", glob_xmount.morphing.pp_libs[i]->p_name, glob_xmount.morphing.pp_libs[i]-> lib_functions.GetErrorMessage(ret)); } if(p_buf==NULL) continue; printf(" - %s\n",glob_xmount.morphing.pp_libs[i]->p_name); printf("%s",p_buf); printf("\n"); } for(uint32_t i=0;i lib_functions.OptionsHelp((const char**)&p_buf); if(ret!=0) { LOG_ERROR("Unable to get options help for library '%s': %s!\n", glob_xmount.output.pp_libs[i]->p_name, glob_xmount.output.pp_libs[i]-> lib_functions.GetErrorMessage(ret)); } if(p_buf==NULL) continue; printf(" - %s\n",glob_xmount.output.pp_libs[i]->p_name); printf("%s",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 * \return TRUE on success, FALSE on error */ static int ParseCmdLine(const int argc, char **pp_argv) { int i=1; int FuseMinusOControl=TRUE; int FuseAllowOther=TRUE; int first; char *p_buf; pts_InputImage p_input_image=NULL; int ret; // add pp_argv[0] to FUSE's argv XMOUNT_MALLOC(glob_xmount.pp_fuse_argv,char**,sizeof(char*)); XMOUNT_STRSET(glob_xmount.pp_fuse_argv[0],pp_argv[0]); glob_xmount.fuse_argc=1; // 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 XMOUNT_REALLOC(glob_xmount.pp_fuse_argv, char**, (glob_xmount.fuse_argc+1)*sizeof(char*)); XMOUNT_STRSET(glob_xmount.pp_fuse_argv[glob_xmount.fuse_argc], pp_argv[i]) glob_xmount.fuse_argc++; 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 mount options if((i+1)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.input.pp_images, pts_InputImage*, glob_xmount.input.images_count* sizeof(pts_InputImage)); 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) { // Set input lib options if((i+1)p_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_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(" loaded output libraries:\n"); for(uint32_t ii=0;iip_name); p_buf=glob_xmount.output.pp_libs[ii]->p_supported_output_formats; 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 { LOG_ERROR("Unknown command line option \"%s\"\n",pp_argv[i]); return FALSE; } } i++; } // Extract mountpoint if(i==(argc-1)) { XMOUNT_STRSET(glob_xmount.p_mountpoint,pp_argv[argc-1]) XMOUNT_REALLOC(glob_xmount.pp_fuse_argv, char**, (glob_xmount.fuse_argc+1)*sizeof(char*)); XMOUNT_STRSET(glob_xmount.pp_fuse_argv[glob_xmount.fuse_argc], glob_xmount.p_mountpoint); glob_xmount.fuse_argc++; } else { LOG_ERROR("No mountpoint specified!\n") return FALSE; } if(FuseMinusOControl==TRUE) { // We control the -o flag, set subtype, fsname and allow_other options glob_xmount.fuse_argc+=2; XMOUNT_REALLOC(glob_xmount.pp_fuse_argv, char**, glob_xmount.fuse_argc*sizeof(char*)); XMOUNT_STRSET(glob_xmount.pp_fuse_argv[glob_xmount.fuse_argc-2],"-o"); XMOUNT_STRSET(glob_xmount.pp_fuse_argv[glob_xmount.fuse_argc-1], "subtype=xmount"); if(glob_xmount.input.images_count!=0) { // Set name of first source file as fsname XMOUNT_STRAPP(glob_xmount.pp_fuse_argv[glob_xmount.fuse_argc-1], ",fsname='"); // If possible, use full path p_buf=realpath(glob_xmount.input.pp_images[0]->pp_files[0],NULL); if(p_buf==NULL) { XMOUNT_STRSET(p_buf,glob_xmount.input.pp_images[0]->pp_files[0]); } // Make sure fsname does not include some forbidden chars for(uint32_t i=0;ip_functions-> GetInfofileContent(glob_xmount.input.pp_images[i]->p_handle,(const char**)&p_buf); if(ret!=0) { LOG_ERROR("Unable to get info file content for image '%s': %s!\n", glob_xmount.input.pp_images[i]->pp_files[0], glob_xmount.input.pp_images[i]->p_functions-> GetErrorMessage(ret)); return FALSE; } // Add infos to main buffer and free p_buf XMOUNT_STRAPP(glob_xmount.output.p_info_file,"\n--> "); XMOUNT_STRAPP(glob_xmount.output.p_info_file, glob_xmount.input.pp_images[i]->pp_files[0]); XMOUNT_STRAPP(glob_xmount.output.p_info_file," <--\n"); if(p_buf!=NULL) { XMOUNT_STRAPP(glob_xmount.output.p_info_file,p_buf); glob_xmount.input.pp_images[i]->p_functions->FreeBuffer(p_buf); } else { XMOUNT_STRAPP(glob_xmount.output.p_info_file,"None\n"); } } // Add static morphing header XMOUNT_STRAPP(glob_xmount.output.p_info_file,IMAGE_INFO_MORPHING_HEADER); // Get and add infos from morphing lib ret=glob_xmount.morphing.p_functions-> GetInfofileContent(glob_xmount.morphing.p_handle,(const char**)&p_buf); if(ret!=0) { LOG_ERROR("Unable to get info file content from morphing lib: %s!\n", glob_xmount.morphing.p_functions->GetErrorMessage(ret)); return FALSE; } if(p_buf!=NULL) { XMOUNT_STRAPP(glob_xmount.output.p_info_file,p_buf); glob_xmount.morphing.p_functions->FreeBuffer(p_buf); } else { XMOUNT_STRAPP(glob_xmount.output.p_info_file,"None\n"); } 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; t_LibXmount_Output_GetApiVersion pfun_output_GetApiVersion; t_LibXmount_Output_GetSupportedFormats pfun_output_GetSupportedFormats; t_LibXmount_Output_GetFunctions pfun_output_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; pts_OutputLib p_output_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 memory!\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_xmount.input.pp_libs, pts_InputLib*, 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)); if(p_morphing_lib->lib_functions.CreateHandle==NULL || p_morphing_lib->lib_functions.DestroyHandle==NULL || p_morphing_lib->lib_functions.Morph==NULL || p_morphing_lib->lib_functions.Size==NULL || p_morphing_lib->lib_functions.Read==NULL || p_morphing_lib->lib_functions.OptionsHelp==NULL || p_morphing_lib->lib_functions.OptionsParse==NULL || p_morphing_lib->lib_functions.GetInfofileContent==NULL || p_morphing_lib->lib_functions.GetErrorMessage==NULL || p_morphing_lib->lib_functions.FreeBuffer==NULL) { LOG_DEBUG("Missing implemention of one or more functions in lib %s!\n", p_dirent->d_name); free(p_morphing_lib->p_supported_morphing_types); free(p_morphing_lib->p_name); free(p_morphing_lib); dlclose(p_libxmount); continue; } // Add entry to the input library list XMOUNT_REALLOC(glob_xmount.morphing.pp_libs, pts_MorphingLib*, 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); } if(strncmp(p_dirent->d_name,"libxmount_output_",17)==0) { // Found possible output lib. Try to load it LIBXMOUNT_LOAD(p_library_path); // Load library symbols LIBXMOUNT_LOAD_SYMBOL("LibXmount_Output_GetApiVersion", pfun_output_GetApiVersion); // Check library's API version if(pfun_output_GetApiVersion()!=LIBXMOUNT_OUTPUT_API_VERSION) { LOG_DEBUG("Failed! Wrong API version.\n"); LOG_ERROR("Unable to load output library '%s'. Wrong API version\n", p_library_path); dlclose(p_libxmount); continue; } LIBXMOUNT_LOAD_SYMBOL("LibXmount_Output_GetSupportedFormats", pfun_output_GetSupportedFormats); LIBXMOUNT_LOAD_SYMBOL("LibXmount_Output_GetFunctions", pfun_output_GetFunctions); // Construct new entry for our library list XMOUNT_MALLOC(p_output_lib,pts_OutputLib,sizeof(ts_OutputLib)); // Initialize lib_functions structure to NULL memset(&(p_output_lib->lib_functions), 0, sizeof(ts_LibXmountOutput_Functions)); // Set name and handle XMOUNT_STRSET(p_output_lib->p_name,p_dirent->d_name); p_output_lib->p_lib=p_libxmount; // Get and set supported types p_supported_formats=pfun_output_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_output_lib->p_supported_output_formats, char*, supported_formats_len); memcpy(p_output_lib->p_supported_output_formats, p_supported_formats, supported_formats_len); // Get, set and check lib_functions pfun_output_GetFunctions(&(p_output_lib->lib_functions)); if(p_output_lib->lib_functions.CreateHandle==NULL || p_output_lib->lib_functions.DestroyHandle==NULL || p_output_lib->lib_functions.Transform==NULL || p_output_lib->lib_functions.Size==NULL || p_output_lib->lib_functions.Read==NULL || p_output_lib->lib_functions.Write==NULL || p_output_lib->lib_functions.OptionsHelp==NULL || p_output_lib->lib_functions.OptionsParse==NULL || p_output_lib->lib_functions.GetInfofileContent==NULL || p_output_lib->lib_functions.GetErrorMessage==NULL || p_output_lib->lib_functions.FreeBuffer==NULL) { LOG_DEBUG("Missing implemention of one or more functions in lib %s!\n", p_dirent->d_name); free(p_output_lib->p_supported_output_formats); free(p_output_lib->p_name); free(p_output_lib); dlclose(p_libxmount); continue; } // Add entry to the input library list XMOUNT_REALLOC(glob_xmount.output.pp_libs, pts_OutputLib*, sizeof(pts_OutputLib)* (glob_xmount.output.libs_count+1)); glob_xmount.output.pp_libs[glob_xmount.output.libs_count++]= p_output_lib; LOG_DEBUG("Output 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, %u morphing libs and %u output libs " "were loaded.\n", glob_xmount.input.libs_count, glob_xmount.morphing.libs_count, glob_xmount.output.libs_count); free(p_library_path); closedir(p_dir); return ((glob_xmount.input.libs_count>0 && glob_xmount.morphing.libs_count>0 && glob_xmount.output.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_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_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.morphing.p_morph_type); // Loop over all loaded libs for(uint32_t i=0;ip_name); p_buf=glob_xmount.morphing.pp_libs[i]->p_supported_morphing_types; while(*p_buf!='\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_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 morph type found return FALSE; } //! Search an appropriate output lib for the specified output format /*! * \return TRUE on success, FALSE on error */ static int FindOutputLib() { char *p_buf; LOG_DEBUG("Trying to find suitable library for output format '%s'.\n", glob_xmount.output.p_output_format); // Loop over all loaded output libs for(uint32_t i=0;ip_name); p_buf=glob_xmount.output.pp_libs[i]->p_supported_output_formats; while(*p_buf!='\0') { if(strcmp(p_buf,glob_xmount.output.p_output_format)==0) { // Library supports output type, set lib functions LOG_DEBUG("Output library '%s' pretends to handle that output format.\n", glob_xmount.output.pp_libs[i]->p_name); glob_xmount.output.p_functions= &(glob_xmount.output.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 output format found return FALSE; } static void InitResources() { + // Args + glob_xmount.args.overwrite_cache=FALSE; + glob_xmount.args.p_cache_file=NULL; + // Input glob_xmount.input.libs_count=0; glob_xmount.input.pp_libs=NULL; glob_xmount.input.lib_params_count=0; glob_xmount.input.pp_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_size_limit=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.lib_params_count=0; 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; // Cache - glob_xmount.cache.overwrite_cache=FALSE; - glob_xmount.cache.p_cache_file=NULL; - glob_xmount.cache.h_cache_file=NULL; - glob_xmount.cache.h_block_cache=NULL; - glob_xmount.cache.h_block_cache_index=NULL; - glob_xmount.cache.p_block_cache_index=NULL; - glob_xmount.cache.block_cache_index_len=0; + glob_xmount.h_cache=NULL; // Output glob_xmount.output.libs_count=0; glob_xmount.output.pp_libs=NULL; glob_xmount.output.p_output_format=NULL; glob_xmount.output.lib_params_count=0; glob_xmount.output.pp_lib_params=NULL; glob_xmount.output.p_handle=NULL; glob_xmount.output.p_functions=NULL; glob_xmount.output.input_functions.Size=&LibXmount_Output_Size; glob_xmount.output.input_functions.Read=&LibXmount_Output_Read; glob_xmount.output.input_functions.Write=&LibXmount_Output_Write; glob_xmount.output.image_size=0; glob_xmount.output.writable=FALSE; glob_xmount.output.p_virtual_image_path=NULL; glob_xmount.output.p_info_path=NULL; glob_xmount.output.p_info_file=NULL; // Misc data glob_xmount.debug=FALSE; glob_xmount.may_set_fuse_allow_other=FALSE; glob_xmount.fuse_argc=0; glob_xmount.pp_fuse_argv=NULL; glob_xmount.p_mountpoint=NULL; } /* * FreeResources */ static void FreeResources() { int ret; - teGidaFsError gidafs_ret=eGidaFsError_None; + te_XmountCache_Error cache_ret=e_XmountCache_Error_None; LOG_DEBUG("Freeing all resources\n"); // Misc if(glob_xmount.pp_fuse_argv!=NULL) { for(int i=0;i DestroyHandle(&(glob_xmount.output.p_handle)); if(ret!=0) { LOG_ERROR("Unable to destroy output handle: %s!\n", glob_xmount.output.p_functions->GetErrorMessage(ret)); } } } if(glob_xmount.output.pp_lib_params!=NULL) { for(uint32_t i=0;ip_supported_output_formats!=NULL) free(glob_xmount.output.pp_libs[i]->p_supported_output_formats); if(glob_xmount.output.pp_libs[i]->p_lib!=NULL) dlclose(glob_xmount.output.pp_libs[i]->p_lib); if(glob_xmount.output.pp_libs[i]->p_name!=NULL) free(glob_xmount.output.pp_libs[i]->p_name); free(glob_xmount.output.pp_libs[i]); } free(glob_xmount.output.pp_libs); } if(glob_xmount.output.p_info_path!=NULL) free(glob_xmount.output.p_info_path); if(glob_xmount.output.p_info_file!=NULL) free(glob_xmount.output.p_info_file); if(glob_xmount.output.p_virtual_image_path!=NULL) free(glob_xmount.output.p_virtual_image_path); // Cache - if(glob_xmount.cache.h_cache_file!=NULL) { - if(glob_xmount.cache.p_block_cache_index!=NULL) - free(glob_xmount.cache.p_block_cache_index); - if(glob_xmount.cache.h_block_cache_index!=NULL) { - gidafs_ret=GidaFsLib_CloseFile(glob_xmount.cache.h_cache_file, - &(glob_xmount.cache.h_block_cache_index)); - if(gidafs_ret!=eGidaFsError_None) { - LOG_ERROR("Unable to close block cache index file: Error code %u: " - "Ignoring!\n", - gidafs_ret) - } - } - if(glob_xmount.cache.h_block_cache!=NULL) { - gidafs_ret=GidaFsLib_CloseFile(glob_xmount.cache.h_cache_file, - &(glob_xmount.cache.h_block_cache)); - if(gidafs_ret!=eGidaFsError_None) { - LOG_ERROR("Unable to close block cache file: Error code %u: " - "Ignoring!\n", - gidafs_ret) - } - } - gidafs_ret=GidaFsLib_CloseFs(&(glob_xmount.cache.h_cache_file)); - if(gidafs_ret!=eGidaFsError_None) { + if(glob_xmount.h_cache!=NULL) { + cache_ret=XmountCache_Close(&(glob_xmount.h_cache)); + if(cache_ret!=e_XmountCache_Error_None) { LOG_ERROR("Unable to close cache file: Error code %u: Ignoring!\n", - gidafs_ret) + cache_ret); } } - if(glob_xmount.cache.p_cache_file!=NULL) free(glob_xmount.cache.p_cache_file); // 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) { 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.pp_lib_params!=NULL) { 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); } + // Args + if(glob_xmount.args.p_cache_file!=NULL) { + XMOUNT_FREE(glob_xmount.args.p_cache_file); + } + // Before we return, initialize everything in case ReleaseResources would be // called again. InitResources(); } //! Function to split given library options static int SplitLibraryParameters(char *p_params, uint32_t *p_ret_opts_count, pts_LibXmountOptions **ppp_ret_opt) { pts_LibXmountOptions p_opts=NULL; pts_LibXmountOptions *pp_opts=NULL; uint32_t params_len; uint32_t opts_count=0; uint32_t sep_pos=0; char *p_buf=p_params; if(p_params==NULL) return FALSE; // Get params length params_len=strlen(p_params); // Return if no params specified if(params_len==0) { *ppp_ret_opt=NULL; p_ret_opts_count=0; return TRUE; } // Split params while(*p_buf!='\0') { XMOUNT_MALLOC(p_opts,pts_LibXmountOptions,sizeof(ts_LibXmountOptions)); p_opts->valid=0; #define FREE_PP_OPTS() { \ if(pp_opts!=NULL) { \ for(uint32_t i=0;ip_key,p_buf,sep_pos); p_buf+=(sep_pos+1); // Search next separator sep_pos=0; while(p_buf[sep_pos]!='\0' && p_buf[sep_pos]!=',') sep_pos++; if(sep_pos==0) { LOG_ERROR("Library parameter '%s' is not of format key=value!\n", p_opts->p_key); free(p_opts->p_key); free(p_opts); FREE_PP_OPTS(); return FALSE; } // Save option value XMOUNT_STRNSET(p_opts->p_value,p_buf,sep_pos); p_buf+=sep_pos; LOG_DEBUG("Extracted library option: '%s' = '%s'\n", p_opts->p_key, p_opts->p_value); #undef FREE_PP_OPTS // Add current option to return array XMOUNT_REALLOC(pp_opts, pts_LibXmountOptions*, sizeof(pts_LibXmountOptions)*(opts_count+1)); pp_opts[opts_count++]=p_opts; // If we're not at the end of p_params, skip over separator for next run if(*p_buf!='\0') p_buf++; } LOG_DEBUG("Extracted a total of %" PRIu32 " library options\n",opts_count); *p_ret_opts_count=opts_count; *ppp_ret_opt=pp_opts; return TRUE; } /******************************************************************************* * 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 ReadInputImageData(glob_xmount.input.pp_images[image], p_buf, offset, count, p_read); } /******************************************************************************* * LibXmount_Output function implementation ******************************************************************************/ //! Function to get the size of the morphed image /*! * \param p_size Pointer to store morphed image's size to * \return 0 on success */ static int LibXmount_Output_Size(uint64_t *p_size) { int ret=0; ret=GetMorphedImageSize(p_size); return ret==TRUE ? 0 : ret; } //! Function to read data from the morphed 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 */ static int LibXmount_Output_Read(char *p_buf, off_t offset, size_t count, size_t *p_read) { return ReadMorphedImageData(p_buf,offset,count,p_read); } //! Function to write data to the morphed image /*! * \param p_buf Buffer with data to write * \param offset Position at which to start writing * \param count Amount of bytes to write * \param p_written Number of written bytes on success * \return 0 on success or negated error code on error */ static int LibXmount_Output_Write(char *p_buf, off_t offset, size_t count, size_t *p_written) { return WriteMorphedImageData(p_buf,offset,count,p_written); } /******************************************************************************* * Main ******************************************************************************/ int main(int argc, char *argv[]) { struct stat file_stat; int ret; int fuse_ret; char *p_err_msg; + te_XmountCache_Error cache_ret=e_XmountCache_Error_None; // Set implemented FUSE functions struct fuse_operations xmount_operations = { //.access=FuseAccess, .getattr=Xmount_FuseGetAttr, .mkdir=Xmount_FuseMkDir, .mknod=Xmount_FuseMkNod, .open=Xmount_FuseOpen, .readdir=Xmount_FuseReadDir, .read=Xmount_FuseRead, .rename=Xmount_FuseRename, .rmdir=Xmount_FuseRmDir, //.statfs=FuseStatFs, .unlink=Xmount_FuseUnlink, .write=Xmount_FuseWrite }; // Disable std output / input buffering setbuf(stdout,NULL); setbuf(stderr,NULL); // Init glob_xmount InitResources(); - // Load input and morphing libs + // Load input, morphing and output 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)!=TRUE) { PrintUsage(argv[0]); FreeResources(); return 1; } // Check command line options if(glob_xmount.input.images_count==0) { LOG_ERROR("No --in command line option specified!\n") PrintUsage(argv[0]); FreeResources(); return 1; } if(glob_xmount.fuse_argc<2) { LOG_ERROR("Couldn't parse command line options!\n") PrintUsage(argv[0]); FreeResources(); return 1; } if(glob_xmount.morphing.p_morph_type==NULL) { XMOUNT_STRSET(glob_xmount.morphing.p_morph_type,"combine"); } + // TODO: Add default output image format here // Check if mountpoint is a valid dir if(stat(glob_xmount.p_mountpoint,&file_stat)!=0) { LOG_ERROR("Unable to stat mount point '%s'!\n",glob_xmount.p_mountpoint); PrintUsage(argv[0]); FreeResources(); return 1; } if(!S_ISDIR(file_stat.st_mode)) { LOG_ERROR("Mount point '%s' is not a directory!\n", glob_xmount.p_mountpoint); PrintUsage(argv[0]); FreeResources(); return 1; } if(glob_xmount.debug==TRUE) { LOG_DEBUG("Options passed to FUSE: ") for(int i=0;ifiles_count==1) { LOG_DEBUG("Loading image file \"%s\"...\n", glob_xmount.input.pp_images[i]->pp_files[0]) } else { LOG_DEBUG("Loading image files \"%s .. %s\"...\n", 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.input.pp_images[i])) { LOG_ERROR("Unknown input image type '%s' for input image '%s'!\n", glob_xmount.input.pp_images[i]->p_type, glob_xmount.input.pp_images[i]->pp_files[0]) PrintUsage(argv[0]); FreeResources(); return 1; } // Init input image handle 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, glob_xmount.debug); if(ret!=0) { LOG_ERROR("Unable to init input handle for input image '%s': %s!\n", glob_xmount.input.pp_images[i]->pp_files[0], glob_xmount.input.pp_images[i]->p_functions-> GetErrorMessage(ret)); FreeResources(); return 1; } // Parse input lib specific options if(glob_xmount.input.pp_lib_params!=NULL) { ret=glob_xmount.input.pp_images[i]->p_functions-> OptionsParse(glob_xmount.input.pp_images[i]->p_handle, glob_xmount.input.lib_params_count, glob_xmount.input.pp_lib_params, (const char**)&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.input.pp_images[i]->pp_files[0], glob_xmount.input.pp_images[i]->p_functions-> GetErrorMessage(ret), p_err_msg); 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.input.pp_images[i]->pp_files[0], glob_xmount.input.pp_images[i]->p_functions-> GetErrorMessage(ret)); FreeResources(); return 1; } } } // Open input image ret= glob_xmount.input.pp_images[i]-> p_functions-> 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.input.pp_images[i]->pp_files[0], glob_xmount.input.pp_images[i]->p_functions-> GetErrorMessage(ret)); FreeResources(); return 1; } // Determine input image size ret=glob_xmount.input.pp_images[i]-> p_functions-> 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.input.pp_images[i]->pp_files[0], glob_xmount.input.pp_images[i]-> p_functions->GetErrorMessage(ret)); FreeResources(); return 1; } // If an offset was specified, check it against offset and change 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 than the size of the input " "image '%s'! (%" PRIu64 " > %" PRIu64 ")\n", 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.input.pp_images[i]->size-=glob_xmount.input.image_offset; } // If a size limit was specified, check it and change size if(glob_xmount.input.image_size_limit!=0) { if(glob_xmount.input.pp_images[i]->size< glob_xmount.input.image_size_limit) { LOG_ERROR("The specified size limit is larger than the size of the " "input image '%s'! (%" PRIu64 " > %" PRIu64 ")\n", glob_xmount.input.pp_images[i]->pp_files[0], glob_xmount.input.image_size_limit, glob_xmount.input.pp_images[i]->size); FreeResources(); return 1; } glob_xmount.input.pp_images[i]->size=glob_xmount.input.image_size_limit; } 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.morphing.p_morph_type); FreeResources(); return 1; } // Init morphing 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_xmount.morphing.p_functions->GetErrorMessage(ret)); FreeResources(); return 1; } // Parse morphing lib specific options if(glob_xmount.morphing.pp_lib_params!=NULL) { p_err_msg=NULL; ret=glob_xmount.morphing.p_functions-> OptionsParse(glob_xmount.morphing.p_handle, glob_xmount.morphing.lib_params_count, glob_xmount.morphing.pp_lib_params, (const char**)&p_err_msg); if(ret!=0) { if(p_err_msg!=NULL) { LOG_ERROR("Unable to parse morphing library specific options: %s: %s!\n", glob_xmount.morphing.p_functions->GetErrorMessage(ret), p_err_msg); glob_xmount.morphing.p_functions->FreeBuffer(p_err_msg); FreeResources(); return 1; } else { LOG_ERROR("Unable to parse morphing library specific options: %s!\n", glob_xmount.morphing.p_functions->GetErrorMessage(ret)); FreeResources(); return 1; } } } // Morph image 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_xmount.morphing.p_functions->GetErrorMessage(ret)); FreeResources(); return 1; } // Init random generator srand(time(NULL)); // Calculate partial MD5 hash of input image file 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.image_hash_lo)))+i)); for(int i=0;i<8;i++) printf("%02hhx",*(((char*)(&(glob_xmount.input.image_hash_hi)))+i)); printf("\n"); } if(!ExtractOutputFileNames(glob_xmount.input.pp_images[0]->pp_files[0])) { LOG_ERROR("Couldn't extract virtual file names!\n"); FreeResources(); return 1; } LOG_DEBUG("Virtual file names extracted successfully\n") // Find output lib if(FindOutputLib()!=TRUE) { LOG_ERROR("Unable to find a library supporting the output format '%s'!\n", glob_xmount.output.p_output_format); FreeResources(); return 1; } // Init output ret=glob_xmount.output.p_functions-> CreateHandle(&glob_xmount.output.p_handle, glob_xmount.output.p_output_format, glob_xmount.debug); if(ret!=0) { LOG_ERROR("Unable to create output handle: %s!\n", glob_xmount.output.p_functions->GetErrorMessage(ret)); FreeResources(); return 1; } // Parse output lib specific options if(glob_xmount.output.pp_lib_params!=NULL) { p_err_msg=NULL; ret=glob_xmount.output.p_functions-> OptionsParse(glob_xmount.output.p_handle, glob_xmount.output.lib_params_count, glob_xmount.output.pp_lib_params, (const char**)&p_err_msg); if(ret!=0) { if(p_err_msg!=NULL) { LOG_ERROR("Unable to parse output library specific options: %s: %s!\n", glob_xmount.output.p_functions->GetErrorMessage(ret), p_err_msg); glob_xmount.output.p_functions->FreeBuffer(p_err_msg); FreeResources(); return 1; } else { LOG_ERROR("Unable to parse output library specific options: %s!\n", glob_xmount.output.p_functions->GetErrorMessage(ret)); FreeResources(); return 1; } } } // Morph image 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_xmount.morphing.p_functions->GetErrorMessage(ret)); FreeResources(); return 1; } // Gather infos for info file if(!InitInfoFile()) { LOG_ERROR("Couldn't gather infos for virtual image info file!\n") FreeResources(); return 1; } LOG_DEBUG("Virtual image info file build successfully\n") if(glob_xmount.output.writable) { // Init cache file and cache file block index + // TODO: Add cache file creration / opening +/* + if(glob_xmount.args.overwrite_cache==TRUE) { + cache_ret=XmountCache_Create(&(glob_xmount.h_cache), + glob_xmount.args.p_cache_file, + + + } else { + cache_ret=XmountCache_Open + } + + + te_XmountCache_Error cache_ret=e_XmountCache_Error_None; if(!InitCacheFile()) { LOG_ERROR("Couldn't initialize cache file!\n") FreeResources(); return 1; } +*/ LOG_DEBUG("Cache file initialized successfully\n") } // Call fuse_main to do the fuse magic fuse_ret=fuse_main(glob_xmount.fuse_argc, glob_xmount.pp_fuse_argv, &xmount_operations, NULL); // Destroy mutexes pthread_mutex_destroy(&(glob_xmount.mutex_image_rw)); pthread_mutex_destroy(&(glob_xmount.mutex_info_read)); // Free allocated memory FreeResources(); return fuse_ret; } diff --git a/src/xmount.h b/src/xmount.h index 58bfb58..36340ec 100755 --- a/src/xmount.h +++ b/src/xmount.h @@ -1,83 +1,93 @@ /******************************************************************************* * xmount Copyright (c) 2008-2016 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 . * *******************************************************************************/ #ifndef XMOUNT_H #define XMOUNT_H #include "xmount_input.h" #include "xmount_morphing.h" #include "xmount_cache.h" #include "xmount_output.h" #undef FALSE #undef TRUE #define FALSE 0 #define TRUE 1 /* * Constants */ #define IMAGE_INFO_INPUT_HEADER \ "------> The following values are supplied by the used input library(ies) " \ "<------\n" #define IMAGE_INFO_MORPHING_HEADER \ "\n------> The following values are supplied by the used morphing library " \ "<------\n\n" /******************************************************************************* * Xmount specific structures ******************************************************************************/ +//! Structure containing xmount arguments +typedef struct s_XmountArgs { + //! Cache file + char *p_cache_file; + //! If set to 1, overwrite any existing cache file + uint8_t overwrite_cache; +} ts_XmountArgs; + //! Structure containing global xmount runtime infos typedef struct s_XmountData { + //! Parsed xmount arguments + ts_XmountArgs args; //! Input image related data ts_InputData input; //! Morphing related data ts_MorphingData morphing; - //! Cache file related data - ts_CacheData cache; + //! Cache file handle + pts_XmountCacheHandle h_cache; //! Output image related data ts_OutputData output; //! Enable debug output uint8_t debug; //! Set if we are allowed to set fuse's allow_other option uint8_t may_set_fuse_allow_other; //! Argv for FUSE int fuse_argc; //! Argv for FUSE char **pp_fuse_argv; //! Mount point char *p_mountpoint; //! Mutex to control concurrent read & write access on output image pthread_mutex_t mutex_image_rw; //! Mutex to control concurrent read access on info file pthread_mutex_t mutex_info_read; } ts_XmountData; /******************************************************************************* * Global vars ******************************************************************************/ //! Struct that contains various runtime configuration options ts_XmountData glob_xmount; /******************************************************************************* * Xmount functions ******************************************************************************/ #endif // XMOUNT_H diff --git a/src/xmount_cache.c b/src/xmount_cache.c index 576b3ef..9538c28 100644 --- a/src/xmount_cache.c +++ b/src/xmount_cache.c @@ -1,957 +1,798 @@ /******************************************************************************* * xmount Copyright (c) 2008-2016 by Gillen Daniel * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #include #include #include #include #include #include "xmount_cache.h" #include "xmount.h" #include "macros.h" /******************************************************************************* * Private definitions / macros ******************************************************************************/ #define LOG_WARNING(...) { \ LIBXMOUNT_LOG_WARNING(__VA_ARGS__); \ } #define LOG_ERROR(...) { \ LIBXMOUNT_LOG_ERROR(__VA_ARGS__); \ } #define LOG_DEBUG(...) { \ LIBXMOUNT_LOG_DEBUG(glob_xmount.debug,__VA_ARGS__); \ } #ifndef __LP64__ //! Value used to indicate an uncached block entry #define XMOUNT_CACHE_INVALID_INDEX 0xFFFFFFFFFFFFFFFFLL #else #define XMOUNT_CACHE_INVALID_INDEX 0xFFFFFFFFFFFFFFFF #endif //! Structures and vars needed for write access #define XMOUNT_CACHE_FOLDER "/.xmount" #define XMOUNT_CACHE_BLOCK_FILE XMOUNT_CACHE_FOLDER "/blocks.data" #define XMOUNT_CACHE_BLOCK_INDEX_FILE XMOUNT_CACHE_FOLDER "/blocks.index" /******************************************************************************* * Private types / structures / enums ******************************************************************************/ typedef struct s_XmountCacheHandle { //! Cache file to save changes to char *p_cache_file; //! Handle to cache file hGidaFs h_cache_file; //! Handle to block cache hGidaFsFile h_block_cache; //! Handle to block cache index hGidaFsFile h_block_cache_index; //! In-memory copy of cache index uint64_t *p_block_cache_index; //! Length (in elements) of in-memory block cache index uint64_t block_cache_index_len; // TODO: Move to s_XmountData //! Overwrite existing cache uint8_t overwrite_cache; } ts_XmountCacheHandle, *pts_XmountCacheHandle; /******************************************************************************* * Private functions declarations ******************************************************************************/ /*! * \brief Create a new xmount cache handle * * \param pp_h Pointer to an xmount cache handle * \param p_file Cache file path / name * \return e_XmountCache_Error_None on success */ te_XmountCache_Error XmountCache_CreateHandle(pts_XmountCacheHandle *pp_h, const char *p_file); /*! * \brief Destroy an xmount cache handle * * \param pp_h Pointer to an xmount cache handle * \return e_XmountCache_Error_None on success */ te_XmountCache_Error XmountCache_DestroyHandle(pts_XmountCacheHandle *pp_h); /*! * \brief Check if a file exists * * Checks if the given file p_file exists. * * \param p_file File to check for * \return e_XmountCache_Error_None if file exists */ te_XmountCache_Error XmountCache_FileExists(const char *p_file); +/*! + * \brief Updates a block cache entry + * + * Updates the given block cache index entry on disk. If the special value + * XMOUNT_CACHE_INVALID_INDEX is given as entry value, the whole block cache + * index is updated. + * + * \param p_h Cache handle + * \param entry Block cache entry number to update + * \return e_XmountCache_Error_None on success + */ +te_XmountCache_Error XmountCache_UpdateIndex(pts_XmountCacheHandle p_h, + uint64_t entry); + /******************************************************************************* * Public functions implementations ******************************************************************************/ /* * XmountCache_Create */ te_XmountCache_Error XmountCache_Create(pts_XmountCacheHandle *pp_h, const char *p_file, uint64_t image_size, uint8_t overwrite) { pts_XmountCacheHandle p_h=NULL; teGidaFsError gidafs_ret=eGidaFsError_None; te_XmountCache_Error ret=e_XmountCache_Error_None; - // Check params + // Params check if(pp_h==NULL) return e_XmountCache_Error_InvalidHandlePointer; if(p_file==NULL) return e_XmountCache_Error_InvalidString; if(strlen(p_file)==0) return e_XmountCache_Error_InvalidFile; // Make sure file does not exist when overwrite was not specified if(overwrite==0 && XmountCache_FileExists(p_file)==e_XmountCache_Error_None) { // Given file exists and overwrite was not specified. This is fatal! return e_XmountCache_Error_ExistingFile; } // Create new handle ret=XmountCache_CreateHandle(&p_h,p_file); if(ret!=e_XmountCache_Error_None) return ret; #define XMOUNTCACHE_CREATE__DESTROY_HANDLE do { \ ret=XmountCache_DestroyHandle(&p_h); \ if(ret!=e_XmountCache_Error_None) { \ LOG_ERROR("Unable to destroy cache handle: Error code %u: Ignoring!\n", \ ret); \ } \ *pp_h=NULL; \ } while(0) // Create new cache file gidafs_ret=GidaFsLib_NewFs(&(p_h->h_cache_file), p_h->p_cache_file, 0); if(gidafs_ret!=eGidaFsError_None) { LOG_ERROR("Unable to create new xmount cache file '%s': Error code %u!\n", p_h->p_cache_file, gidafs_ret); XMOUNTCACHE_CREATE__DESTROY_HANDLE; return FALSE; } #define XMOUNTCACHE_CREATE__CLOSE_CACHE do { \ gidafs_ret=GidaFsLib_CloseFs(&(p_h->h_cache_file)); \ if(gidafs_ret!=eGidaFsError_None) { \ LOG_ERROR("Unable to close xmount cache file '%s': " \ "Error code %u: Ignoring!\n", \ p_h->p_cache_file, \ gidafs_ret); \ } \ } while(0) // TODO: Check if cache file uses same block size as we do // Create needed xmount subdirectory gidafs_ret=GidaFsLib_CreateDir(p_h->h_cache_file, XMOUNT_CACHE_FOLDER, eGidaFsNodeFlag_RWXu); if(gidafs_ret!=eGidaFsError_None) { LOG_ERROR("Unable to create cache file directory '%s': Error code %u!\n", XMOUNT_CACHE_FOLDER, gidafs_ret); XMOUNTCACHE_CREATE__CLOSE_CACHE; XMOUNTCACHE_CREATE__DESTROY_HANDLE; return e_XmountCache_Error_FailedCacheInit; } // Create block cache file gidafs_ret=GidaFsLib_OpenFile(p_h->h_cache_file, XMOUNT_CACHE_BLOCK_FILE, &(p_h->h_block_cache), eGidaFsOpenFileFlag_ReadWrite | eGidaFsOpenFileFlag_CreateAlways, eGidaFsNodeFlag_Rall | eGidaFsNodeFlag_Wusr); if(gidafs_ret!=eGidaFsError_None) { LOG_ERROR("Unable to create block cache file '%s': Error code %u!\n", XMOUNT_CACHE_BLOCK_FILE, gidafs_ret); XMOUNTCACHE_CREATE__CLOSE_CACHE; XMOUNTCACHE_CREATE__DESTROY_HANDLE; + // TODO: Own error code needed here return e_XmountCache_Error_FailedCacheInit; } #define XMOUNTCACHE_CREATE__CLOSE_BLOCK_CACHE do { \ gidafs_ret=GidaFsLib_CloseFile(p_h->h_cache_file, \ &(p_h->h_block_cache)); \ if(gidafs_ret!=eGidaFsError_None) { \ LOG_ERROR("Unable to close block cache file: Error code %u: " \ "Ignoring!\n", \ gidafs_ret); \ } \ } while(0) // Create block cache index file gidafs_ret=GidaFsLib_OpenFile(p_h->h_cache_file, XMOUNT_CACHE_BLOCK_INDEX_FILE, &(p_h->h_block_cache_index), eGidaFsOpenFileFlag_ReadWrite | eGidaFsOpenFileFlag_CreateAlways, eGidaFsNodeFlag_Rall | eGidaFsNodeFlag_Wusr); if(gidafs_ret!=eGidaFsError_None) { LOG_ERROR("Unable to create block cache index file '%s': Error code %u!\n", XMOUNT_CACHE_BLOCK_FILE, gidafs_ret); XMOUNTCACHE_CREATE__CLOSE_BLOCK_CACHE; XMOUNTCACHE_CREATE__CLOSE_CACHE; XMOUNTCACHE_CREATE__DESTROY_HANDLE; + // TODO: Own error code needed here return e_XmountCache_Error_FailedCacheInit; } #define XMOUNTCACHE_CREATE__CLOSE_BLOCK_CACHE_INDEX do { \ gidafs_ret=GidaFsLib_CloseFile(p_h->h_cache_file, \ &(p_h->h_block_cache_index)); \ if(gidafs_ret!=eGidaFsError_None) { \ LOG_ERROR("Unable to close block cache index file: Error code %u: " \ "Ignoring!\n", \ gidafs_ret); \ } \ } while(0) // Calculate how many cache blocks are needed and how big the cache block // index must be p_h->block_cache_index_len=image_size/XMOUNT_CACHE_BLOCK_SIZE; if((image_size%XMOUNT_CACHE_BLOCK_SIZE)!=0) p_h->block_cache_index_len++; LOG_DEBUG("Cache blocks: %" PRIu64 " entries using %" PRIu64 " bytes\n", p_h->block_cache_index_len, p_h->block_cache_index_len*sizeof(uint8_t)); // Prepare in-memory buffer for block cache index p_h->p_block_cache_index= (uint64_t*)calloc(1,p_h->block_cache_index_len*sizeof(uint64_t)); if(p_h->p_block_cache_index==NULL) { XMOUNTCACHE_CREATE__CLOSE_BLOCK_CACHE_INDEX; XMOUNTCACHE_CREATE__CLOSE_BLOCK_CACHE; XMOUNTCACHE_CREATE__CLOSE_CACHE; XMOUNTCACHE_CREATE__DESTROY_HANDLE; return e_XmountCache_Error_Alloc; } // Generate initial block cache index for(uint64_t i=0;iblock_cache_index_len;i++) { p_h->p_block_cache_index[i]=XMOUNT_CACHE_INVALID_INDEX; } // Write initial block cache index to cache file - if(UpdateBlockCacheIndex(XMOUNT_BLOCK_CACHE_INVALID_INDEX,0)!=TRUE) { - LOG_ERROR("Unable to generate initial block cache index file '%s': " + ret=XmountCache_UpdateIndex(p_h,XMOUNT_CACHE_INVALID_INDEX); + if(ret!=e_XmountCache_Error_None) { + LOG_ERROR("Unable to update initial block cache index file: " "Error code %u!\n", XMOUNT_CACHE_BLOCK_FILE, - gidafs_ret); + ret); XMOUNTCACHE_CREATE__CLOSE_BLOCK_CACHE_INDEX; XMOUNTCACHE_CREATE__CLOSE_BLOCK_CACHE; XMOUNTCACHE_CREATE__CLOSE_CACHE; XMOUNTCACHE_CREATE__DESTROY_HANDLE; - return e_XmountCache_Error_FailedCacheInit; + return ret; } #undef XMOUNTCACHE_CREATE__CLOSE_BLOCK_CACHE_INDEX #undef XMOUNTCACHE_CREATE__CLOSE_BLOCK_CACHE #undef XMOUNTCACHE_CREATE__CLOSE_CACHE #undef XMOUNTCACHE_CREATE__DESTROY_HANDLE *pp_h=p_h; return e_XmountCache_Error_None; } /* * XmountCache_Open */ te_XmountCache_Error XmountCache_Open(pts_XmountCacheHandle *pp_h, const char *p_file, uint64_t image_size) { + uint64_t blockindex_size=0; + uint64_t read=0; pts_XmountCacheHandle p_h=NULL; teGidaFsError gidafs_ret=eGidaFsError_None; te_XmountCache_Error ret=e_XmountCache_Error_None; - // Check params + // Params check if(pp_h==NULL) return e_XmountCache_Error_InvalidHandlePointer; if(p_file==NULL) return e_XmountCache_Error_InvalidString; if(strlen(p_file)==0) return e_XmountCache_Error_InvalidFile; // Make sure file exists if(XmountCache_FileExists(p_file)!=e_XmountCache_Error_None) { // Given file does not exist. This is fatal! return e_XmountCache_Error_InexistingFile; } // Create new handle ret=XmountCache_CreateHandle(&p_h,p_file); if(ret!=e_XmountCache_Error_None) return ret; #define XMOUNTCACHE_OPEN__DESTROY_HANDLE do { \ ret=XmountCache_DestroyHandle(&p_h); \ if(ret!=e_XmountCache_Error_None) { \ LOG_ERROR("Unable to destroy cache handle: Error code %u: Ignoring!\n", \ ret); \ } \ *pp_h=NULL; \ } while(0) // Open cache file gidafs_ret=GidaFsLib_OpenFs(&(p_h->h_cache_file),p_h->p_cache_file); if(gidafs_ret!=eGidaFsError_None) { // TODO: Check for old cache file type and inform user it isn't supported // anymore! LOG_ERROR("Couldn't open xmount cache file '%s': Error code %u!\n", p_h->p_cache_file, gidafs_ret) return e_XmountCache_Error_FailedOpeningCache; } #define XMOUNTCACHE_OPEN__CLOSE_CACHE do { \ gidafs_ret=GidaFsLib_CloseFs(&(p_h->h_cache_file)); \ if(gidafs_ret!=eGidaFsError_None) { \ LOG_ERROR("Unable to close xmount cache file '%s': " \ "Error code %u: Ignoring!\n", \ p_h->p_cache_file, \ gidafs_ret); \ } \ } while(0) + // Open block cache file + gidafs_ret=GidaFsLib_OpenFile(p_h->h_cache_file, + XMOUNT_CACHE_BLOCK_FILE, + &(p_h->h_block_cache), + eGidaFsOpenFileFlag_ReadWrite, + 0); + if(gidafs_ret!=eGidaFsError_None) { + LOG_ERROR("Unable to open block cache file '%s': Error code %u!\n", + XMOUNT_CACHE_BLOCK_FILE, + gidafs_ret); + XMOUNTCACHE_OPEN__CLOSE_CACHE; + XMOUNTCACHE_OPEN__DESTROY_HANDLE; + // TODO: Own error code needed here + return e_XmountCache_Error_FailedOpeningCache; + } + #define XMOUNTCACHE_OPEN__CLOSE_BLOCK_CACHE do { \ gidafs_ret=GidaFsLib_CloseFile(p_h->h_cache_file, \ &(p_h->h_block_cache)); \ if(gidafs_ret!=eGidaFsError_None) { \ LOG_ERROR("Unable to close block cache file: Error code %u: " \ "Ignoring!\n", \ gidafs_ret); \ } \ } while(0) + // Open block cache index file + gidafs_ret=GidaFsLib_OpenFile(p_h->h_cache_file, + XMOUNT_CACHE_BLOCK_INDEX_FILE, + &(p_h->h_block_cache_index), + eGidaFsOpenFileFlag_ReadWrite, + 0); + if(gidafs_ret!=eGidaFsError_None) { + LOG_ERROR("Unable to open block cache index file '%s': Error code %u!\n", + XMOUNT_CACHE_BLOCK_FILE, + gidafs_ret); + XMOUNTCACHE_OPEN__CLOSE_BLOCK_CACHE; + XMOUNTCACHE_OPEN__CLOSE_CACHE; + XMOUNTCACHE_OPEN__DESTROY_HANDLE; + // TODO: Own error code needed here + return e_XmountCache_Error_FailedOpeningCache; + } + #define XMOUNTCACHE_OPEN__CLOSE_BLOCK_CACHE_INDEX do { \ gidafs_ret=GidaFsLib_CloseFile(p_h->h_cache_file, \ &(p_h->h_block_cache_index)); \ if(gidafs_ret!=eGidaFsError_None) { \ LOG_ERROR("Unable to close block cache index file: Error code %u: " \ "Ignoring!\n", \ gidafs_ret); \ } \ } while(0) + // Calculate how many cache blocks are needed and how big the cache block + // index must be + p_h->block_cache_index_len=image_size/XMOUNT_CACHE_BLOCK_SIZE; + if((image_size%XMOUNT_CACHE_BLOCK_SIZE)!=0) p_h->block_cache_index_len++; + LOG_DEBUG("Cache blocks: %" PRIu64 " entries using %" PRIu64 " bytes\n", + p_h->block_cache_index_len, + p_h->block_cache_index_len*sizeof(uint8_t)); - - - - - - -/* - - -#define INITCACHEFILE__CLOSE_CACHE do { \ - gidafs_ret=GidaFsLib_CloseFs(&(glob_xmount.cache.h_cache_file)); \ - if(gidafs_ret!=eGidaFsError_None) { \ - LOG_ERROR("Unable to close cache file: Error code %u: Ignoring!\n", \ - gidafs_ret) \ - } \ -} while(0) - -#define INITCACHEFILE__CLOSE_BLOCK_CACHE do { \ - gidafs_ret=GidaFsLib_CloseFile(glob_xmount.cache.h_cache_file, \ - &(glob_xmount.cache.h_block_cache)); \ - if(gidafs_ret!=eGidaFsError_None) { \ - LOG_ERROR("Unable to close block cache file: Error code %u: Ignoring!\n", \ - gidafs_ret) \ - } \ -} while(0) - -#define INITCACHEFILE__CLOSE_BLOCK_CACHE_INDEX do { \ - gidafs_ret=GidaFsLib_CloseFile(glob_xmount.cache.h_cache_file, \ - &(glob_xmount.cache.h_block_cache_index)); \ - if(gidafs_ret!=eGidaFsError_None) { \ - LOG_ERROR("Unable to close block cache index file: Error code %u: " \ - "Ignoring!\n", \ - gidafs_ret) \ - } \ -} while(0) - - // TODO: Check if cache file uses same block size as we do - - if(is_new_cache_file==1) { - // New cache file, create needed xmount subdirectory - gidafs_ret=GidaFsLib_CreateDir(glob_xmount.cache.h_cache_file, - XMOUNT_CACHE_FOLDER, - eGidaFsNodeFlag_RWXu); - if(gidafs_ret!=eGidaFsError_None) { - LOG_ERROR("Unable to create cache file directory '%s': Error code %u!\n", - XMOUNT_CACHE_FOLDER, - gidafs_ret) - INITCACHEFILE__CLOSE_CACHE; - return FALSE; - } - } - - // Open / Create block cache file - gidafs_ret=GidaFsLib_OpenFile(glob_xmount.cache.h_cache_file, - XMOUNT_CACHE_BLOCK_FILE, - &(glob_xmount.cache.h_block_cache), - eGidaFsOpenFileFlag_ReadWrite | - (is_new_cache_file==1 ? - eGidaFsOpenFileFlag_CreateAlways : 0), - eGidaFsNodeFlag_Rall | - eGidaFsNodeFlag_Wusr); - if(gidafs_ret!=eGidaFsError_None) { - LOG_ERROR("Unable to open / create block cache file '%s': Error code %u!\n", - XMOUNT_CACHE_BLOCK_FILE, - gidafs_ret) - INITCACHEFILE__CLOSE_CACHE; - return FALSE; - } - - // Open / Create block cache index file - gidafs_ret=GidaFsLib_OpenFile(glob_xmount.cache.h_cache_file, - XMOUNT_CACHE_BLOCK_INDEX_FILE, - &(glob_xmount.cache.h_block_cache_index), - eGidaFsOpenFileFlag_ReadWrite | - (is_new_cache_file==1 ? - eGidaFsOpenFileFlag_CreateAlways : 0), - eGidaFsNodeFlag_Rall | - eGidaFsNodeFlag_Wusr); + // Make sure block cache index has correct size + gidafs_ret=GidaFsLib_GetFileSize(p_h->h_cache_file, + p_h->h_block_cache_index, + &blockindex_size); if(gidafs_ret!=eGidaFsError_None) { - LOG_ERROR("Unable to open / create block cache index file '%s': " - "Error code %u!\n", - XMOUNT_CACHE_BLOCK_FILE, + LOG_ERROR("Unable to get block cache index file size: Error code %u!\n", gidafs_ret) - INITCACHEFILE__CLOSE_BLOCK_CACHE; - INITCACHEFILE__CLOSE_CACHE; - return FALSE; + XMOUNTCACHE_OPEN__CLOSE_BLOCK_CACHE_INDEX; + XMOUNTCACHE_OPEN__CLOSE_BLOCK_CACHE; + XMOUNTCACHE_OPEN__CLOSE_CACHE; + XMOUNTCACHE_OPEN__DESTROY_HANDLE; + return e_XmountCache_Error_FailedGettingIndexSize; } - - // Calculate how many cache blocks are needed and how big the cache block - // index must be - glob_xmount.cache.block_cache_index_len=image_size/CACHE_BLOCK_SIZE; - if((image_size%CACHE_BLOCK_SIZE)!=0) { - glob_xmount.cache.block_cache_index_len++; + if(blockindex_size%sizeof(uint64_t)!=0 || + (blockindex_size/sizeof(uint64_t))!=p_h->block_cache_index_len) + { + // TODO: Be more helpfull in error message + LOG_ERROR("Block cache index size is incorrect for given input image!\n") + XMOUNTCACHE_OPEN__CLOSE_BLOCK_CACHE_INDEX; + XMOUNTCACHE_OPEN__CLOSE_BLOCK_CACHE; + XMOUNTCACHE_OPEN__CLOSE_CACHE; + XMOUNTCACHE_OPEN__DESTROY_HANDLE; + return e_XmountCache_Error_InvalidIndexSize; } - LOG_DEBUG("Cache blocks: %u (0x%04X) entries using %zd (0x%08zX) bytes\n", - glob_xmount.cache.block_cache_index_len, - glob_xmount.cache.block_cache_index_len, - glob_xmount.cache.block_cache_index_len* - sizeof(t_CacheFileBlockIndex), - glob_xmount.cache.block_cache_index_len* - sizeof(t_CacheFileBlockIndex)) - // Prepare in-memory buffer for block cache index - XMOUNT_MALLOC(glob_xmount.cache.p_block_cache_index, - t_CacheFileBlockIndex*, - glob_xmount.cache.block_cache_index_len* - sizeof(t_CacheFileBlockIndex)) - - if(is_new_cache_file==1) { - // Generate initial block cache index - for(uint64_t i=0;ip_block_cache_index= + (uint64_t*)calloc(1,p_h->block_cache_index_len*sizeof(uint64_t)); + if(p_h->p_block_cache_index==NULL) { + XMOUNTCACHE_OPEN__CLOSE_BLOCK_CACHE_INDEX; + XMOUNTCACHE_OPEN__CLOSE_BLOCK_CACHE; + XMOUNTCACHE_OPEN__CLOSE_CACHE; + XMOUNTCACHE_OPEN__DESTROY_HANDLE; + return e_XmountCache_Error_Alloc; } -#undef INITCACHEFILE__CLOSE_BLOCK_CACHE_INDEX -#undef INITCACHEFILE__CLOSE_BLOCK_CACHE -#undef INITCACHEFILE__CLOSE_CACHE -*/ + // Read block cache index into memory + gidafs_ret=GidaFsLib_ReadFile(p_h->h_cache_file, + p_h->h_block_cache_index, + 0, + blockindex_size, + p_h->p_block_cache_index, + &read); + if(gidafs_ret!=eGidaFsError_None || read!=blockindex_size) { + LOG_ERROR("Unable to read block cache index: Error code %u!\n", + gidafs_ret); + XMOUNTCACHE_OPEN__CLOSE_BLOCK_CACHE_INDEX; + XMOUNTCACHE_OPEN__CLOSE_BLOCK_CACHE; + XMOUNTCACHE_OPEN__CLOSE_CACHE; + XMOUNTCACHE_OPEN__DESTROY_HANDLE; + return e_XmountCache_Error_FailedReadingIndex; + } #undef XMOUNTCACHE_OPEN__CLOSE_BLOCK_CACHE_INDEX #undef XMOUNTCACHE_OPEN__CLOSE_BLOCK_CACHE #undef XMOUNTCACHE_OPEN__CLOSE_CACHE #undef XMOUNTCACHE_OPEN__DESTROY_HANDLE *pp_h=p_h; return e_XmountCache_Error_None; } /* * XmountCache_Close */ te_XmountCache_Error XmountCache_Close(pts_XmountCacheHandle *pp_h) { pts_XmountCacheHandle p_h=NULL; + teGidaFsError gidafs_ret=eGidaFsError_None; + te_XmountCache_Error final_ret=e_XmountCache_Error_None; te_XmountCache_Error ret=e_XmountCache_Error_None; + // Params check + if(pp_h==NULL) return e_XmountCache_Error_InvalidHandlePointer; + if(*pp_h==NULL) return e_XmountCache_Error_InvalidHandle; + p_h=*pp_h; + + // Close block cache index + gidafs_ret=GidaFsLib_CloseFile(p_h->h_cache_file,&(p_h->h_block_cache_index)); + if(gidafs_ret!=eGidaFsError_None) { + LOG_ERROR("Unable to close block cache index file: Error code %u: " + "Ignoring!\n", + gidafs_ret); + if(final_ret==e_XmountCache_Error_None) { + final_ret=e_XmountCache_Error_FailedClosingIndex; + } + } + + // Close block cache + gidafs_ret=GidaFsLib_CloseFile(p_h->h_cache_file,&(p_h->h_block_cache)); + if(gidafs_ret!=eGidaFsError_None) { + LOG_ERROR("Unable to close block cache file: Error code %u: " + "Ignoring!\n", + gidafs_ret); + if(final_ret==e_XmountCache_Error_None) { + final_ret=e_XmountCache_Error_FailedClosingBlockCache; + } + } + + // Close cache file + gidafs_ret=GidaFsLib_CloseFs(&(p_h->h_cache_file)); + if(gidafs_ret!=eGidaFsError_None) { + LOG_ERROR("Unable to close xmount cache file '%s': Error code %u: " + "Ignoring!\n", + p_h->p_cache_file, + gidafs_ret); + if(final_ret==e_XmountCache_Error_None) { + final_ret=e_XmountCache_Error_FailedClosingCache; + } + } + // Destroy handle + ret=XmountCache_DestroyHandle(&p_h); + if(ret!=e_XmountCache_Error_None) { + LOG_ERROR("Unable to destroy cache handle: Error code %u: Ignoring!\n", + ret); + if(final_ret==e_XmountCache_Error_None) { + final_ret=ret; + } + } *pp_h=NULL; - return e_XmountCache_Error_None; + return final_ret; } /* * XmountCache_BlockCacheRead */ te_XmountCache_Error XmountCache_BlockCacheRead(pts_XmountCacheHandle p_h, char *p_buf, uint64_t block, uint64_t block_offset, uint64_t count) { - te_XmountCache_Error ret=e_XmountCache_Error_None; + uint64_t read=0; + teGidaFsError gidafs_ret=eGidaFsError_None; + + // Params check + if(p_h==NULL) return e_XmountCache_Error_InvalidHandle; + if(p_buf==NULL) return e_XmountCache_Error_InvalidBuffer; + if(block>=p_h->block_cache_index_len) return e_XmountCache_Error_InvalidIndex; + if(block_offset>XMOUNT_CACHE_BLOCK_SIZE || + block_offset+count>XMOUNT_CACHE_BLOCK_SIZE) + { + return e_XmountCache_Error_ReadBeyondBlockBounds; + } + if(p_h->p_block_cache_index[block]==XMOUNT_CACHE_INVALID_INDEX) { + return e_XmountCache_Error_UncachedBlock; + } + + // Read requested data from block cache file + gidafs_ret=GidaFsLib_ReadFile(p_h->h_cache_file, + p_h->h_block_cache, + p_h->p_block_cache_index[block]+block_offset, + count, + p_buf, + &read); + if(gidafs_ret!=eGidaFsError_None || read!=count) { + LOG_ERROR("Unable to read cached data from block %" PRIu64 + ": Error code %u!\n", + block, + gidafs_ret); + return e_XmountCache_Error_FailedReadingBlockCache; + } + + return e_XmountCache_Error_None; } /* * XmountCache_BlockCacheWrite */ te_XmountCache_Error XmountCache_BlockCacheWrite(pts_XmountCacheHandle p_h, char *p_buf, uint64_t block, uint64_t block_offset, uint64_t count) { - te_XmountCache_Error ret=e_XmountCache_Error_None; + uint64_t written=0; + teGidaFsError gidafs_ret=eGidaFsError_None; + + // Params check + if(p_h==NULL) return e_XmountCache_Error_InvalidHandle; + if(p_buf==NULL) return e_XmountCache_Error_InvalidBuffer; + if(block>=p_h->block_cache_index_len) return e_XmountCache_Error_InvalidIndex; + if(block_offset>XMOUNT_CACHE_BLOCK_SIZE || + block_offset+count>XMOUNT_CACHE_BLOCK_SIZE) + { + return e_XmountCache_Error_ReadBeyondBlockBounds; + } + if(p_h->p_block_cache_index[block]==XMOUNT_CACHE_INVALID_INDEX) { + return e_XmountCache_Error_UncachedBlock; + } + + gidafs_ret=GidaFsLib_WriteFile(p_h->h_cache_file, + p_h->h_block_cache, + p_h->p_block_cache_index[block]+block_offset, + count, + p_buf, + &written); + if(gidafs_ret!=eGidaFsError_None || written!=count) { + LOG_ERROR("Unable to write data to cached block %" PRIu64 + ": Error code %u!\n", + block, + gidafs_ret); + return e_XmountCache_Error_FailedWritingBlockCache; + } + + return e_XmountCache_Error_None; } /* * XmountCache_BlockCacheAppend */ te_XmountCache_Error XmountCache_BlockCacheAppend(pts_XmountCacheHandle p_h, char *p_buf, uint64_t block) { + uint64_t written=0; + teGidaFsError gidafs_ret=eGidaFsError_None; te_XmountCache_Error ret=e_XmountCache_Error_None; + + // Params check + if(p_h==NULL) return e_XmountCache_Error_InvalidHandle; + if(p_buf==NULL) return e_XmountCache_Error_InvalidBuffer; + if(block>=p_h->block_cache_index_len) return e_XmountCache_Error_InvalidIndex; + + // Get current block cache size + gidafs_ret=GidaFsLib_GetFileSize(p_h->h_cache_file, + p_h->h_block_cache, + &(p_h->p_block_cache_index[block])); + if(gidafs_ret!=eGidaFsError_None) { + LOG_ERROR("Unable to get current block cache size: Error code %u!\n", + gidafs_ret); + return e_XmountCache_Error_FailedGettingIndexSize; + } + + // Append new block + gidafs_ret=GidaFsLib_WriteFile(p_h->h_cache_file, + p_h->h_block_cache, + p_h->p_block_cache_index[block], + XMOUNT_CACHE_BLOCK_SIZE, + p_buf, + &written); + if(gidafs_ret!=eGidaFsError_None || written!=XMOUNT_CACHE_BLOCK_SIZE) { + LOG_ERROR("Unable to write data to cached block %" PRIu64 + ": Error code %u!\n", + block, + gidafs_ret); + return e_XmountCache_Error_FailedWritingBlockCache; + } + + // Update on-disk block cache index + ret=XmountCache_UpdateIndex(p_h,block); + if(ret!=e_XmountCache_Error_None) { + LOG_ERROR("Unable to update block cache index %" PRIu64 + ": Error code %u!\n", + block, + ret); + return ret; + } + + return e_XmountCache_Error_None; } /* * XmountCache_IsBlockCached */ te_XmountCache_Error XmountCache_IsBlockCached(pts_XmountCacheHandle p_h, uint64_t block) { - te_XmountCache_Error ret=e_XmountCache_Error_None; + // Params check + if(p_h==NULL) return e_XmountCache_Error_InvalidHandle; + if(block>=p_h->block_cache_index_len) return e_XmountCache_Error_InvalidIndex; + + // Check if given block has been cached previously + if(p_h->p_block_cache_index[block]==XMOUNT_CACHE_INVALID_INDEX) { + // Block is not in cache + return e_XmountCache_Error_UncachedBlock; + } + + return e_XmountCache_Error_None; } /******************************************************************************* * Private functions implementations ******************************************************************************/ /* * XmountCache_CreateHandle */ te_XmountCache_Error XmountCache_CreateHandle(pts_XmountCacheHandle *pp_h, const char *p_file) { pts_XmountCacheHandle p_h=NULL; - // Check given handle pointer - if(pp_h==NULL) return e_XmountCache_Error_InvalidHandlePointer; - // Alloc memory for handle p_h=(pts_XmountCacheHandle)calloc(1,sizeof(ts_XmountCacheHandle)); if(p_h==NULL) { *pp_h=NULL; return e_XmountCache_Error_Alloc; } // Init values. The p_cache_file alloc and memcpy works corrently as strlen() // counts the amount of bytes, not chars. No UTF8-issue here. p_h->p_cache_file=(char*)calloc(1,strlen(p_file)+1); if(p_h->p_cache_file==NULL) { free(p_h); *pp_h=NULL; return e_XmountCache_Error_Alloc; } memcpy(p_h->p_cache_file,p_file,strlen(p_file)); p_h->h_cache_file=NULL; p_h->h_block_cache=NULL; p_h->h_block_cache_index=NULL; p_h->p_block_cache_index=NULL; // Return new handle *pp_h=p_h; return e_XmountCache_Error_None; } /* * XmountCache_DestroyHandle */ te_XmountCache_Error XmountCache_DestroyHandle(pts_XmountCacheHandle *pp_h) { - pts_XmountCacheHandle p_h=NULL; - - // Check given handle pointer - if(pp_h==NULL) return e_XmountCache_Error_InvalidHandlePointer; - if(*pp_h==NULL) return e_XmountCache_Error_InvalidHandle; - p_h=*pp_h; + pts_XmountCacheHandle p_h=*pp_h; // Free handle if(p_h->p_cache_file!=NULL) free(p_h->p_cache_file); if(p_h->p_block_cache_index!=NULL) free(p_h->p_block_cache_index); free(p_h); // Return destroyed handle *pp_h=NULL; return e_XmountCache_Error_None; } /* * XmountCache_FileExists */ te_XmountCache_Error XmountCache_FileExists(const char *p_file) { struct stat statbuf; if(lstat(p_file,&statbuf)==0) return e_XmountCache_Error_None; else return e_XmountCache_Error_InexistingFile; } - - - - - - - - - - - - - - - - - - - - - -//! Create / load cache file to enable virtual write support -/*! - * \return TRUE on success, FALSE on error - */ -int InitCacheFile() { - uint64_t blockindex_size=0; - uint64_t image_size=0; - uint64_t read=0; - uint8_t is_new_cache_file=0; - teGidaFsError gidafs_ret=eGidaFsError_None; - - // Get input image size for later use - if(!GetMorphedImageSize(&image_size)) { - LOG_ERROR("Couldn't get morphed image size!\n") - return FALSE; - } - - if(!glob_xmount.cache.overwrite_cache) { - // Try to open an existing cache file or create a new one - gidafs_ret=GidaFsLib_OpenFs(&(glob_xmount.cache.h_cache_file), - glob_xmount.cache.p_cache_file); - if(gidafs_ret!=eGidaFsError_None && - gidafs_ret!=eGidaFsError_FailedOpeningFsFile) - { - // TODO: Check for old cache file type and inform user it isn't supported - // anymore! - LOG_ERROR("Couldn't open cache file '%s': Error code %u!\n", - glob_xmount.cache.p_cache_file, - gidafs_ret) - return FALSE; - } else if(gidafs_ret==eGidaFsError_FailedOpeningFsFile) { - // Unable to open cache file. It might simply not exist. - LOG_DEBUG("Cache file '%s' does not exist. Creating new one\n", - glob_xmount.cache.p_cache_file) - gidafs_ret=GidaFsLib_NewFs(&(glob_xmount.cache.h_cache_file), - glob_xmount.cache.p_cache_file, - 0); - if(gidafs_ret!=eGidaFsError_None) { - // There is really a problem opening/creating the file - LOG_ERROR("Couldn't open cache file '%s': Error code %u!\n", - glob_xmount.cache.p_cache_file, - gidafs_ret) - return FALSE; - } - is_new_cache_file=1; - } - } else { - // Overwrite existing cache file or create a new one - gidafs_ret=GidaFsLib_NewFs(&(glob_xmount.cache.h_cache_file), - glob_xmount.cache.p_cache_file, - 0); - if(gidafs_ret!=eGidaFsError_None) { - // There is really a problem opening/creating the file - LOG_ERROR("Couldn't open cache file '%s': Error code %u!\n", - glob_xmount.cache.p_cache_file, - gidafs_ret) - return FALSE; - } - is_new_cache_file=1; - } - -#define INITCACHEFILE__CLOSE_CACHE do { \ - gidafs_ret=GidaFsLib_CloseFs(&(glob_xmount.cache.h_cache_file)); \ - if(gidafs_ret!=eGidaFsError_None) { \ - LOG_ERROR("Unable to close cache file: Error code %u: Ignoring!\n", \ - gidafs_ret) \ - } \ -} while(0) - -#define INITCACHEFILE__CLOSE_BLOCK_CACHE do { \ - gidafs_ret=GidaFsLib_CloseFile(glob_xmount.cache.h_cache_file, \ - &(glob_xmount.cache.h_block_cache)); \ - if(gidafs_ret!=eGidaFsError_None) { \ - LOG_ERROR("Unable to close block cache file: Error code %u: Ignoring!\n", \ - gidafs_ret) \ - } \ -} while(0) - -#define INITCACHEFILE__CLOSE_BLOCK_CACHE_INDEX do { \ - gidafs_ret=GidaFsLib_CloseFile(glob_xmount.cache.h_cache_file, \ - &(glob_xmount.cache.h_block_cache_index)); \ - if(gidafs_ret!=eGidaFsError_None) { \ - LOG_ERROR("Unable to close block cache index file: Error code %u: " \ - "Ignoring!\n", \ - gidafs_ret) \ - } \ -} while(0) - - // TODO: Check if cache file uses same block size as we do - - if(is_new_cache_file==1) { - // New cache file, create needed xmount subdirectory - gidafs_ret=GidaFsLib_CreateDir(glob_xmount.cache.h_cache_file, - XMOUNT_CACHE_FOLDER, - eGidaFsNodeFlag_RWXu); - if(gidafs_ret!=eGidaFsError_None) { - LOG_ERROR("Unable to create cache file directory '%s': Error code %u!\n", - XMOUNT_CACHE_FOLDER, - gidafs_ret) - INITCACHEFILE__CLOSE_CACHE; - return FALSE; - } - } - - // Open / Create block cache file - gidafs_ret=GidaFsLib_OpenFile(glob_xmount.cache.h_cache_file, - XMOUNT_CACHE_BLOCK_FILE, - &(glob_xmount.cache.h_block_cache), - eGidaFsOpenFileFlag_ReadWrite | - (is_new_cache_file==1 ? - eGidaFsOpenFileFlag_CreateAlways : 0), - eGidaFsNodeFlag_Rall | - eGidaFsNodeFlag_Wusr); - if(gidafs_ret!=eGidaFsError_None) { - LOG_ERROR("Unable to open / create block cache file '%s': Error code %u!\n", - XMOUNT_CACHE_BLOCK_FILE, - gidafs_ret) - INITCACHEFILE__CLOSE_CACHE; - return FALSE; - } - - // Open / Create block cache index file - gidafs_ret=GidaFsLib_OpenFile(glob_xmount.cache.h_cache_file, - XMOUNT_CACHE_BLOCK_INDEX_FILE, - &(glob_xmount.cache.h_block_cache_index), - eGidaFsOpenFileFlag_ReadWrite | - (is_new_cache_file==1 ? - eGidaFsOpenFileFlag_CreateAlways : 0), - eGidaFsNodeFlag_Rall | - eGidaFsNodeFlag_Wusr); - if(gidafs_ret!=eGidaFsError_None) { - LOG_ERROR("Unable to open / create block cache index file '%s': " - "Error code %u!\n", - XMOUNT_CACHE_BLOCK_FILE, - gidafs_ret) - INITCACHEFILE__CLOSE_BLOCK_CACHE; - INITCACHEFILE__CLOSE_CACHE; - return FALSE; - } - - // Calculate how many cache blocks are needed and how big the cache block - // index must be - glob_xmount.cache.block_cache_index_len=image_size/CACHE_BLOCK_SIZE; - if((image_size%CACHE_BLOCK_SIZE)!=0) { - glob_xmount.cache.block_cache_index_len++; - } - - LOG_DEBUG("Cache blocks: %u (0x%04X) entries using %zd (0x%08zX) bytes\n", - glob_xmount.cache.block_cache_index_len, - glob_xmount.cache.block_cache_index_len, - glob_xmount.cache.block_cache_index_len* - sizeof(t_CacheFileBlockIndex), - glob_xmount.cache.block_cache_index_len* - sizeof(t_CacheFileBlockIndex)) - - // Prepare in-memory buffer for block cache index - XMOUNT_MALLOC(glob_xmount.cache.p_block_cache_index, - t_CacheFileBlockIndex*, - glob_xmount.cache.block_cache_index_len* - sizeof(t_CacheFileBlockIndex)) - - if(is_new_cache_file==1) { - // Generate initial block cache index - for(uint64_t i=0;ip_block_cache_index+entry; + update_size=sizeof(uint64_t); } else { // Dump whole block cache index to cache file - p_buf=glob_xmount.cache.p_block_cache_index; - update_size=glob_xmount.cache.block_cache_index_len* - sizeof(t_CacheFileBlockIndex); + p_buf=p_h->p_block_cache_index; + update_size=p_h->block_cache_index_len*sizeof(uint64_t); } // Update cache file - gidafs_ret=GidaFsLib_WriteFile(glob_xmount.cache.h_cache_file, - glob_xmount.cache.h_block_cache_index, + gidafs_ret=GidaFsLib_WriteFile(p_h->h_cache_file, + p_h->h_block_cache_index, 0, update_size, p_buf, &written); if(gidafs_ret!=eGidaFsError_None || written!=update_size) { - LOG_ERROR("Unable to update block cache index file '%s': " - "Error code %u!\n", - XMOUNT_CACHE_BLOCK_INDEX_FILE, + LOG_ERROR("Unable to update block cache index: Error code %u!\n", gidafs_ret) - return FALSE; + return e_XmountCache_Error_FailedUpdatingIndex; } - return TRUE; + return e_XmountCache_Error_None; } diff --git a/src/xmount_cache.h b/src/xmount_cache.h index cfc318a..5431e3f 100644 --- a/src/xmount_cache.h +++ b/src/xmount_cache.h @@ -1,176 +1,203 @@ /******************************************************************************* * xmount Copyright (c) 2008-2016 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 XMOUNT_CACHE_H #define XMOUNT_CACHE_H #include /******************************************************************************* * Public definitions / macros ******************************************************************************/ //! Default block cache block size to use (1 megabyte) #define XMOUNT_CACHE_BLOCK_SIZE (1024*1024) /******************************************************************************* * Public types / structures / enums ******************************************************************************/ typedef struct s_XmountCacheHandle *pts_XmountCacheHandle; typedef enum e_XmountCache_Error { //! No error e_XmountCache_Error_None=0, //! Error to allocate memory e_XmountCache_Error_Alloc, //! Invalid cache handle e_XmountCache_Error_InvalidHandle, //! Invalid pointer to a cache handle e_XmountCache_Error_InvalidHandlePointer, //! A given string is invalid e_XmountCache_Error_InvalidString, //! A given file path / name is invalid e_XmountCache_Error_InvalidFile, //! A given file does not exist e_XmountCache_Error_InexistingFile, //! A given file exists e_XmountCache_Error_ExistingFile, //! Unable to create needed xmount structures inside cache file e_XmountCache_Error_FailedCacheInit, //! Unable to open xmount cache file e_XmountCache_Error_FailedOpeningCache, + //! Failed to get block cache index size + e_XmountCache_Error_FailedGettingIndexSize, + //! Invalid block cache index size + e_XmountCache_Error_InvalidIndexSize, + //! Unable to read block cache index + e_XmountCache_Error_FailedReadingIndex, + //! Failed closing cache block index + e_XmountCache_Error_FailedClosingIndex, + //! Failed closing cache block index + e_XmountCache_Error_FailedClosingBlockCache, + //! Failed closing cache block index + e_XmountCache_Error_FailedClosingCache, + //! Failed to update block cache index + e_XmountCache_Error_FailedUpdatingIndex, + //! Invalid block cache index specified + e_XmountCache_Error_InvalidIndex, + //! Block has not yet been cached + e_XmountCache_Error_UncachedBlock, + //! Invalid buffer specified + e_XmountCache_Error_InvalidBuffer, + //! Request would read beyond a single cache block + e_XmountCache_Error_ReadBeyondBlockBounds, + //! Failed reading cached data + e_XmountCache_Error_FailedReadingBlockCache, + //! Failed writing cached data + e_XmountCache_Error_FailedWritingBlockCache, } te_XmountCache_Error; /******************************************************************************* * Public functions declarations ******************************************************************************/ /*! * \brief Create new xmount cache file * * Creates a new xmount cache file in the given file. If the given file already * exists, this function will fail except if overwrite is set to 1. * * \param pp_handle Pointer to an xmount cache handle * \param p_file File to use as cache file * \param image_size Size of image in bytes for which this cache will be used * \param overwrite If set to 1, overwrites existig cache file * \return e_XmountCache_Error_None on success */ te_XmountCache_Error XmountCache_Create(pts_XmountCacheHandle *pp_h, const char *p_file, uint64_t image_size, uint8_t overwrite); /*! * \brief Open an existing xmount cache file * * Opens the given xmount cache file. * * \param pp_handle Pointer to an xmount cache handle * \param p_file File to use as cache file * \param image_size Size of image in bytes for which this cache will be used * \return e_XmountCache_Error_None on success */ te_XmountCache_Error XmountCache_Open(pts_XmountCacheHandle *pp_h, const char *p_file, uint64_t image_size); /*! * \brief Closes a previously openend xmount cache file * * Closes the given xmount cache file and frees any used resources. * * \param pp_handle Pointer to an xmount cache handle * \return e_XmountCache_Error_None on success */ te_XmountCache_Error XmountCache_Close(pts_XmountCacheHandle *pp_h); /*! * \brief Read data from block cache * * Reads count bytes at offset block_offset from block number block and writes * the read data into the pre-allocated buffer p_buf. The given block has to * have been previously cached by a call to XmountCache_BlockCacheAppend(). * * WARNING: This function does only work on single blocks. It is not possible to * read beyond a block end. * * \param p_handle Xmount cache handle * \param p_buf Buffer to store read data into * \param block Number of block to read data from * \param block_offset Offset inside block to start reading from * \param count Amount of bytes to read * \return e_XmountCache_Error_None on success */ te_XmountCache_Error XmountCache_BlockCacheRead(pts_XmountCacheHandle p_h, char *p_buf, uint64_t block, uint64_t block_offset, uint64_t count); /*! * \brief Write data to block cache * * Writes count bytes from buffer p_buf to block number block at offset * block_offset. The given block has to have been previously cached by a call to * XmountCache_BlockCacheAppend(). * * WARNING: This function does only work on single blocks. It is not possible to * write beyond a block end. * * \param p_handle Xmount cache handle * \param p_buf Buffer with data to write * \param block Number of block to write data to * \param block_offset Offset inside block to start writing from * \param count Amount of bytes to write * \return e_XmountCache_Error_None on success */ te_XmountCache_Error XmountCache_BlockCacheWrite(pts_XmountCacheHandle p_h, char *p_buf, uint64_t block, uint64_t block_offset, uint64_t count); /*! * \brief Add a new block to the cache * * Adds the data inside p_buf to the cache file and saves it under the block * number block. Every block must contain exactly XMOUNT_CACHE_BLOCK_SIZE bytes. * Every block can only be cached once. Appending the same block twice will * fail. * * \param p_handle Xmount cache handle * \param p_buf Buffer with block data * \param block Number of block under which to save given data * \return e_XmountCache_Error_None on success */ te_XmountCache_Error XmountCache_BlockCacheAppend(pts_XmountCacheHandle p_h, char *p_buf, uint64_t block); /*! * \brief Chech if a block has previously been chached * - * Checks if the given block has previously been cached. + * Checks if the given block has previously been cached. If it hasn't, + * e_XmountCache_Error_UncachedBlock is returned. * * \param p_handle Xmount cache handle * \param block Number of block to check * \return e_XmountCache_Error_None on success */ te_XmountCache_Error XmountCache_IsBlockCached(pts_XmountCacheHandle p_h, uint64_t block); #endif // XMOUNT_CACHE_H diff --git a/src/xmount_morphing.c b/src/xmount_morphing.c index bcc5a44..ea3fb42 100644 --- a/src/xmount_morphing.c +++ b/src/xmount_morphing.c @@ -1,324 +1,316 @@ /******************************************************************************* * xmount Copyright (c) 2008-2016 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 // For memcpy #include "xmount_morphing.h" #include "xmount.h" #include "macros.h" #define LOG_WARNING(...) { \ LIBXMOUNT_LOG_WARNING(__VA_ARGS__); \ } #define LOG_ERROR(...) { \ LIBXMOUNT_LOG_ERROR(__VA_ARGS__); \ } #define LOG_DEBUG(...) { \ LIBXMOUNT_LOG_DEBUG(glob_xmount.debug,__VA_ARGS__); \ } //! Get size of morphed image /*! * \param p_size Buf to save size to * \return TRUE on success, FALSE on error */ int GetMorphedImageSize(uint64_t *p_size) { int ret; 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_xmount.morphing.p_functions->GetErrorMessage(ret)); return FALSE; } return TRUE; } //! 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 */ int ReadMorphedImageData(char *p_buf, off_t offset, size_t size, size_t *p_read) { uint64_t block_off=0; uint64_t cur_block=0; uint64_t cur_to_read=0; uint64_t image_size=0; size_t read=0; size_t to_read=0; int ret; - teGidaFsError gidafs_ret=eGidaFsError_None; + uint8_t is_block_cached=FALSE; + te_XmountCache_Error cache_ret=e_XmountCache_Error_None; // 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 FALSE; } 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 %" PRIu64 ".\n", size, to_read); } else to_read=size; // Calculate block to start reading data from - cur_block=offset/CACHE_BLOCK_SIZE; - block_off=offset%CACHE_BLOCK_SIZE; + cur_block=offset/XMOUNT_CACHE_BLOCK_SIZE; + block_off=offset%XMOUNT_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; + if(block_off+to_read>XMOUNT_CACHE_BLOCK_SIZE) { + cur_to_read=XMOUNT_CACHE_BLOCK_SIZE-block_off; } else cur_to_read=to_read; + // Determine if we have to read cached data + is_block_cached=FALSE; + if(glob_xmount.output.writable==TRUE) { + cache_ret=XmountCache_IsBlockCached(glob_xmount.h_cache,cur_block); + if(cache_ret==e_XmountCache_Error_None) is_block_cached=TRUE; + else if(cache_ret!=e_XmountCache_Error_UncachedBlock) { + LOG_ERROR("Unable to determine if block %" PRIu64 " is cached: " + "Error code %u!\n", + cur_block, + cache_ret); + return -EIO; + } + } + // Check if block is cached - if(glob_xmount.output.writable==TRUE && - glob_xmount.cache.p_block_cache_index[cur_block]!=CACHE_BLOCK_FREE) - { + if(is_block_cached==TRUE) { // Write support enabled and need to read altered data from cachefile - LOG_DEBUG("Reading %zu bytes at offset %" PRIu64 - " from block cache file\n", - cur_to_read, - glob_xmount.cache.p_block_cache_index[cur_block]+block_off) + LOG_DEBUG("Reading %zu bytes from block cache file\n",cur_to_read); - gidafs_ret=GidaFsLib_ReadFile(glob_xmount.cache.h_cache_file, - glob_xmount.cache.h_block_cache, - glob_xmount.cache. - p_block_cache_index[cur_block]+block_off, - cur_to_read, - p_buf, - &read); - if(gidafs_ret!=eGidaFsError_None || read!=cur_to_read) { - LOG_ERROR("Unable to read cached data from block %" PRIu64 - ": Error code %u!\n", + cache_ret=XmountCache_BlockCacheRead(glob_xmount.h_cache, + p_buf, + cur_block, + block_off, + cur_to_read); + if(cache_ret!=e_XmountCache_Error_None) { + LOG_ERROR("Unable to read %" PRIu64 + " bytes of cached data from cache block %" PRIu64 + " at cache block offset %" PRIu64 ": Error code %u!\n", + cur_to_read, cur_block, - gidafs_ret); + block_off, + cache_ret); return -EIO; } } else { // No write support or data not cached ret=glob_xmount.morphing.p_functions->Read(glob_xmount.morphing.p_handle, p_buf, - (cur_block*CACHE_BLOCK_SIZE)+ + (cur_block*XMOUNT_CACHE_BLOCK_SIZE)+ block_off, cur_to_read, &read); if(ret!=0 || read!=cur_to_read) { LOG_ERROR("Couldn't read %zu bytes at offset %zu from morphed image: " "%s!\n", cur_to_read, offset, glob_xmount.morphing.p_functions->GetErrorMessage(ret)); return -EIO; } LOG_DEBUG("Read %" PRIu64 " bytes at offset %" PRIu64 " from morphed image file\n", cur_to_read, - (cur_block*CACHE_BLOCK_SIZE)+block_off); + (cur_block*XMOUNT_CACHE_BLOCK_SIZE)+block_off); } cur_block++; block_off=0; p_buf+=cur_to_read; to_read-=cur_to_read; } *p_read=to_read; return TRUE; } //! Write data to morphed image /*! * \param p_buf Buffer with data to write * \param offset Offset to start writing at * \param count Amount of bytes to write * \param p_written Amount of successfully written bytes * \return TRUE on success, negated error code on error */ int WriteMorphedImageData(const char *p_buf, off_t offset, size_t count, size_t *p_written) { uint64_t block_off=0; uint64_t cur_block=0; uint64_t cur_to_read=0; uint64_t cur_to_write=0; uint64_t image_size=0; uint64_t read=0; size_t written=0; size_t to_write=0; int ret; - teGidaFsError gidafs_ret=eGidaFsError_None; char *p_buf2=NULL; + uint8_t is_block_cached=FALSE; + te_XmountCache_Error cache_ret=e_XmountCache_Error_None; // Make sure we aren't writing 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_written=0; return 0; } if(offset+count>image_size) { // Attempt to write data past EOF of morphed image file to_write=image_size-offset; LOG_DEBUG("Attempt to write data past EOF of morphed image. Corrected size " "from %zu to %" PRIu64 ".\n", count, to_write); } else to_write=count; // Calculate block to start writing data to - cur_block=offset/CACHE_BLOCK_SIZE; - block_off=offset%CACHE_BLOCK_SIZE; + cur_block=offset/XMOUNT_CACHE_BLOCK_SIZE; + block_off=offset%XMOUNT_CACHE_BLOCK_SIZE; while(to_write!=0) { // Calculate how many bytes we have to write to this block - if(block_off+to_write>CACHE_BLOCK_SIZE) { - cur_to_write=CACHE_BLOCK_SIZE-block_off; + if(block_off+to_write>XMOUNT_CACHE_BLOCK_SIZE) { + cur_to_write=XMOUNT_CACHE_BLOCK_SIZE-block_off; } else cur_to_write=to_write; + // Determine if block is already in cache + is_block_cached=FALSE; + cache_ret=XmountCache_IsBlockCached(glob_xmount.h_cache,cur_block); + if(cache_ret==e_XmountCache_Error_None) is_block_cached=TRUE; + else if(cache_ret!=e_XmountCache_Error_UncachedBlock) { + LOG_ERROR("Unable to determine if block %" PRIu64 " is cached: " + "Error code %u!\n", + cur_block, + cache_ret); + return -EIO; + } + // Check if block is cached - if(glob_xmount.cache.p_block_cache_index[cur_block]!=CACHE_BLOCK_FREE) { + if(is_block_cached==TRUE) { // Block is cached - gidafs_ret=GidaFsLib_WriteFile(glob_xmount.cache.h_cache_file, - glob_xmount.cache.h_block_cache, - glob_xmount.cache. - p_block_cache_index[cur_block]+block_off, - cur_to_write, - p_buf, - &written); - if(gidafs_ret!=eGidaFsError_None || written!=cur_to_write) { - LOG_ERROR("Unable to write data to cached block %" PRIu64 - ": Error code %u!\n", + cache_ret=XmountCache_BlockCacheWrite(glob_xmount.h_cache, + p_buf, + cur_block, + block_off, + cur_to_write); + if(cache_ret!=e_XmountCache_Error_None) { + LOG_ERROR("Unable to write %" PRIu64 + " bytes of data to cache block %" PRIu64 + " at cache block offset %" PRIu64 ": Error code %u!\n", + cur_to_write, cur_block, - gidafs_ret); + block_off, + cache_ret); return -EIO; } - LOG_DEBUG("Wrote %" PRIu64 " bytes at offset %" PRIu64 - " to block cache file\n", - cur_to_write, - glob_xmount.cache.p_block_cache_index[cur_block]+block_off); + LOG_DEBUG("Wrote %" PRIu64 " bytes to block cache\n",cur_to_write); } else { // Uncached block. Need to cache entire new block // Prepare new write buffer - XMOUNT_MALLOC(p_buf2,char*,CACHE_BLOCK_SIZE); - memset(p_buf2,0x00,CACHE_BLOCK_SIZE); + XMOUNT_MALLOC(p_buf2,char*,XMOUNT_CACHE_BLOCK_SIZE); + memset(p_buf2,0x00,XMOUNT_CACHE_BLOCK_SIZE); // Read full block from morphed image - cur_to_read=CACHE_BLOCK_SIZE; - if((cur_block*CACHE_BLOCK_SIZE)+CACHE_BLOCK_SIZE>image_size) { - cur_to_read=CACHE_BLOCK_SIZE-(((cur_block*CACHE_BLOCK_SIZE)+ - CACHE_BLOCK_SIZE)-image_size); + cur_to_read=XMOUNT_CACHE_BLOCK_SIZE; + if((cur_block*XMOUNT_CACHE_BLOCK_SIZE)+XMOUNT_CACHE_BLOCK_SIZE>image_size) { + cur_to_read=XMOUNT_CACHE_BLOCK_SIZE-(((cur_block*XMOUNT_CACHE_BLOCK_SIZE)+ + XMOUNT_CACHE_BLOCK_SIZE)-image_size); } ret=glob_xmount.morphing.p_functions->Read(glob_xmount.morphing.p_handle, p_buf2, - cur_block*CACHE_BLOCK_SIZE, + cur_block*XMOUNT_CACHE_BLOCK_SIZE, cur_to_read, &read); if(ret!=0 || read!=cur_to_read) { LOG_ERROR("Couldn't read %" PRIu64 " bytes at offset %zu " "from morphed image: %s!\n", cur_to_read, offset, glob_xmount.morphing.p_functions->GetErrorMessage(ret)); XMOUNT_FREE(p_buf2); return -EIO; } // Set changed data memcpy(p_buf2+block_off,p_buf,cur_to_write); - // Write new block to block cache - // Get current block cache size - gidafs_ret=GidaFsLib_GetFileSize(glob_xmount.cache.h_cache_file, - glob_xmount.cache.h_block_cache, - &(glob_xmount.cache. - p_block_cache_index[cur_block])); - if(gidafs_ret!=eGidaFsError_None) { - LOG_ERROR("Unable to get current block cache size: Error code %u!\n", - gidafs_ret); - XMOUNT_FREE(p_buf2); - return -EIO; - } - // Append new block - gidafs_ret=GidaFsLib_WriteFile(glob_xmount.cache.h_cache_file, - glob_xmount.cache.h_block_cache, - glob_xmount.cache. - p_block_cache_index[cur_block], - CACHE_BLOCK_SIZE, - p_buf2, - &written); - if(gidafs_ret!=eGidaFsError_None || written!=cur_to_write) { - LOG_ERROR("Unable to write data to cached block %" PRIu64 + cache_ret=XmountCache_BlockCacheAppend(glob_xmount.h_cache, + p_buf, + cur_block); + if(cache_ret!=e_XmountCache_Error_None) { + LOG_ERROR("Unable to append new block cache block %" PRIu64 ": Error code %u!\n", cur_block, - gidafs_ret); + cache_ret); XMOUNT_FREE(p_buf2); return -EIO; } XMOUNT_FREE(p_buf2); - // Update on-disk block cache index - ret=UpdateBlockCacheIndex(cur_block, - glob_xmount.cache.p_block_cache_index[ - cur_block]); - if(ret!=TRUE) { - LOG_ERROR("Unable to update block cache index %" PRIu64 - ": Error code %u!\n", - cur_block, - gidafs_ret); - return -EIO; - } - LOG_DEBUG("Updated cache file block index: Number=%" PRIu64 - ", Data offset=%" PRIu64 "\n", - cur_block, - glob_xmount.cache.p_block_cache_index[cur_block]); + LOG_DEBUG("Appended new block cache block %" PRIu64 "\n",cur_block); } block_off=0; cur_block++; p_buf+=cur_to_write; to_write-=cur_to_write; } *p_written=to_write; return TRUE; }