diff --git a/trunk/xmount.c b/trunk/xmount.c index 41b1886..19dc036 100755 --- a/trunk/xmount.c +++ b/trunk/xmount.c @@ -1,2732 +1,2732 @@ /******************************************************************************* -* xmount Copyright (c) 2008,2009 by Gillen Daniel * +* xmount Copyright (c) 2008-2010 by Gillen Daniel * * * * xmount is a small tool to "fuse mount" various harddisk image formats as dd, * * vdi 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 . * *******************************************************************************/ #define HAVE_LIBAFF_STATIC #define HAVE_LIBEWF_STATIC #ifdef HAVE_LIBEWF #define WITH_LIBEWF #endif #ifdef HAVE_LIBAFF_STATIC #define WITH_LIBEWF #endif #ifdef HAVE_LIBAFF #define WITH_LIBAFF #endif #ifdef HAVE_LIBAFF_STATIC #define WITH_LIBAFF #endif #include "config.h" #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBEWF #include #endif #ifdef HAVE_LIBEWF_STATIC #include "libewf/include/libewf.h" #endif #ifdef HAVE_LIBAFF #include #endif #ifdef HAVE_LIBAFF_STATIC #include "libaff/lib/afflib.h" #endif #include "xmount.h" #include "md5.h" // Some constant values #define IMAGE_INFO_HEADER "The following values have been extracted from " \ "the mounted image file:\n\n" #define VDI_FILE_COMMENT "<<< This is a virtual VDI image >>>" #define VDI_HEADER_COMMENT "This VDI was emulated using xmount v" \ PACKAGE_VERSION // Struct that contains various runtime configuration options static TXMountConfData XMountConfData; // Handles for input image types static FILE *hDdFile=NULL; #ifdef WITH_LIBEWF static LIBEWF_HANDLE *hEwfFile=NULL; #endif #ifdef WITH_LIBAFF static AFFILE *hAffFile=NULL; #endif // Pointer to virtual info file static char *pVirtualImageInfoFile=NULL; // Vars needed for VDI emulation static TVdiFileHeader *pVdiFileHeader=NULL; static uint32_t VdiFileHeaderSize=0; static char *pVdiBlockMap=NULL; static uint32_t VdiBlockMapSize=0; // Vars needed for VMDK emulation static char *pVirtualVmdkFile=NULL; static int VirtualVmdkFileSize=0; static char *pVirtualVmdkLockDir=NULL; static char *pVirtualVmdkLockDir2=NULL; static char *pVirtualVmdkLockFileData=NULL; static int VirtualVmdkLockFileDataSize=0; static char *pVirtualVmdkLockFileName=NULL; // Vars needed for virtual write access static FILE *hCacheFile=NULL; static pTCacheFileHeader pCacheFileHeader=NULL; static pTCacheFileBlockIndex pCacheFileBlockIndex=NULL; // Mutexes to control concurrent read & write access static pthread_mutex_t mutex_image_rw; static pthread_mutex_t mutex_info_read; /* * LogMessage: * Print error and debug messages to stdout * * Params: * pMessageType: "ERROR" or "DEBUG" * pCallingFunction: Name of calling function * line: Line number of call * pMessage: Message string * ...: Variable params with values to include in message string * * Returns: * n/a */ static void LogMessage(char *pMessageType, char *pCallingFunction, int line, char *pMessage, ...) { va_list VaList; // Print message "header" printf("%s: %s.%s@%u : ",pMessageType,pCallingFunction,PACKAGE_VERSION,line); // Print message with variable parameters va_start(VaList,pMessage); vprintf(pMessage,VaList); va_end(VaList); } /* * LogWarnMessage: * Print warning messages to stdout * * Params: * pMessage: Message string * ...: Variable params with values to include in message string * * Returns: * n/a */ static void LogWarnMessage(char *pMessage,...) { va_list VaList; // Print message "header" printf("WARNING: "); // Print message with variable parameters va_start(VaList,pMessage); vprintf(pMessage,VaList); va_end(VaList); } /* * PrintUsage: * Print usage instructions (cmdline options etc..) * * Params: * pProgramName: Program name (argv[0]) * * Returns: * n/a */ static void PrintUsage(char *pProgramName) { printf("\nxmount v%s copyright (c) 2008-2010 by Gillen Daniel " "\n",PACKAGE_VERSION); printf("\nUsage:\n"); printf(" %s [[fopts] [mopts]] [ [...]] \n\n",pProgramName); 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\n"); printf(" addition of FUSE's allow_other option!\n"); printf(" INFO: For VMDK emulation, you have to uncomment \"user_allow_other\" in\n"); printf(" /etc/fuse.conf or run xmount as root.\n"); printf(" mopts:\n"); printf(" --cache : Enable virtual write support and set cachefile to use.\n"); // printf(" --debug : Enable xmount's debug mode.\n"); printf(" --in : Input image format. can be \"dd\""); #ifdef WITH_LIBEWF printf(", \"ewf\""); #endif #ifdef WITH_LIBAFF printf(", \"aff\""); #endif printf(".\n"); printf(" --info : Print out some infos about used compiler and libraries.\n"); printf(" --out : Output image format. can be \"dd\", \"vdi\", \"vmdk(s)\".\n"); printf(" --owcache : Same as --cache but overwrites existing cache.\n"); printf(" --rw : Same as --cache .\n"); printf(" --version : Same as --info.\n"); printf(" INFO: Input and output image type defaults to \"dd\" if not specified.\n"); printf(" WARNING: Output image type \"vmdk(s)\" should be considered experimental!\n"); printf(" ifile:\n"); printf(" Input image file."); #ifdef WITH_LIBEWF printf(" If you use EWF files, you have to specify all image\n"); printf(" segments! (If your shell supports it, you can use .E?? as file extension\n"); printf(" to specify them all)\n"); #else printf("\n"); #endif printf(" mntp:\n"); printf(" Mount point where virtual files should be located.\n"); } /* * CheckFuseAllowOther: * 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. * * Params: * n/a * * Returns: * TRUE on success, FALSE on error */ static int CheckFuseAllowOther() { if(geteuid()!=0) { // Not running xmount as root. Try to read FUSE's config file /etc/fuse.conf FILE *hFuseConf=(FILE*)fopen64("/etc/fuse.conf","r"); if(hFuseConf==NULL) { LogWarnMessage("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"); return FALSE; } // Search conf file for set user_allow_others char line[256]; int PermSet=FALSE; while(fgets(line,sizeof(line),hFuseConf)!=NULL && PermSet!=TRUE) { // TODO: This works as long as there is no other parameter beginning with // "user_allow_other" :) if(strncmp(line,"user_allow_other",strlen("user_allow_other"))==0) { PermSet=TRUE; } } fclose(hFuseConf); if(PermSet==FALSE) { LogWarnMessage("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"); return FALSE; } } // Running xmount as root or user_allow_other is set in /etc/fuse.conf return TRUE; } /* * ParseCmdLine: * Parse command line options * * Params: * argc: Number of cmdline params * argv: Array containing cmdline params * pNargv: Number of FUSE options is written to this var * pppNargv: FUSE options are written to this array * pFilenameCount: Number of input image files is written to this var * pppFilenames: Input image filenames are written to this array * ppMountpoint: Mountpoint is written to this var * * Returns: * "TRUE" on success, "FALSE" on error */ static int ParseCmdLine(const int argc, char **argv, int *pNargc, char ***pppNargv, int *pFilenameCount, char ***pppFilenames, char **ppMountpoint) { int i=1,files=0,opts=0,AllowOther=TRUE; // add argv[0] to pppNargv opts++; XMOUNT_MALLOC(*pppNargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*pppNargv)[opts-1],argv[0]) // Parse options while(i1 && *(argv[i]+1)!='-') { // Options beginning with - are mostly FUSE specific if(strcmp(argv[i],"-d")==0) { // Enable FUSE's and xmount's debug mode opts++; XMOUNT_REALLOC(*pppNargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*pppNargv)[opts-1],argv[i]) XMountConfData.Debug=TRUE; } else if(strcmp(argv[i],"-h")==0) { // Print help message PrintUsage(argv[0]); exit(1); } else if(strcmp(argv[i],"-o")==0) { // Next parameter specifies fuse mount options if((argc+1)>i) { i++; // As the user specified the -o option, we assume he knows what he is // doing. We won't append allow_other automatically. And we allow him // to disable allow_other by passing a single "-o no_allow_other" // which won't be passed to FUSE as it is xmount specific. if(strcmp(argv[i],"no_allow_other")!=0) { opts+=2; XMOUNT_REALLOC(*pppNargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*pppNargv)[opts-2],argv[i-1]) XMOUNT_STRSET((*pppNargv)[opts-1],argv[i]) } AllowOther=FALSE; } else { LOG_ERROR("Couldn't parse mount options!\n") PrintUsage(argv[0]); exit(1); } } else if(strcmp(argv[i],"-s")==0) { // Enable FUSE's single threaded mode opts++; XMOUNT_REALLOC(*pppNargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*pppNargv)[opts-1],argv[i]) } else if(strcmp(argv[i],"-V")==0) { // Display FUSE version info opts++; XMOUNT_REALLOC(*pppNargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*pppNargv)[opts-1],argv[i]) } else { LOG_ERROR("Unknown command line option \"%s\"\n",argv[i]); PrintUsage(argv[0]); exit(1); } } else { // Options beginning with -- are xmount specific if(strcmp(argv[i],"--cache")==0 || strcmp(argv[i],"--rw")==0) { // Emulate writable access to mounted image // Next parameter must be cache file to read/write changes from/to if((argc+1)>i) { i++; XMOUNT_STRSET(XMountConfData.pCacheFile,argv[i]) XMountConfData.Writable=TRUE; } else { LOG_ERROR("You must specify a cache file to read/write data from/to!\n") PrintUsage(argv[0]); exit(1); } LOG_DEBUG("Enabling virtual write support using cache file \"%s\"\n", XMountConfData.pCacheFile) } else if(strcmp(argv[i],"--in")==0) { // Specify input image type // Next parameter must be image type if((argc+1)>i) { i++; if(strcmp(argv[i],"dd")==0) { XMountConfData.OrigImageType=TOrigImageType_DD; LOG_DEBUG("Setting input image type to DD\n") #ifdef WITH_LIBEWF } else if(strcmp(argv[i],"ewf")==0) { XMountConfData.OrigImageType=TOrigImageType_EWF; LOG_DEBUG("Setting input image type to EWF\n") #endif #ifdef WITH_LIBAFF } else if(strcmp(argv[i],"aff")==0) { XMountConfData.OrigImageType=TOrigImageType_AFF; LOG_DEBUG("Setting input image type to AFF\n") #endif } else { LOG_ERROR("Unknown input image type \"%s\"!\n",argv[i]) PrintUsage(argv[0]); exit(1); } } else { LOG_ERROR("You must specify an input image type!\n"); PrintUsage(argv[0]); exit(1); } } else if(strcmp(argv[i],"--out")==0) { // Specify output image type // Next parameter must be image type if((argc+1)>i) { i++; if(strcmp(argv[i],"dd")==0) { XMountConfData.VirtImageType=TVirtImageType_DD; LOG_DEBUG("Setting virtual image type to DD\n") } else if(strcmp(argv[i],"vdi")==0) { XMountConfData.VirtImageType=TVirtImageType_VDI; LOG_DEBUG("Setting virtual image type to VDI\n") } else if(strcmp(argv[i],"vmdk")==0) { XMountConfData.VirtImageType=TVirtImageType_VMDK; LOG_DEBUG("Setting virtual image type to VMDK\n") } else if(strcmp(argv[i],"vmdks")==0) { XMountConfData.VirtImageType=TVirtImageType_VMDKS; LOG_DEBUG("Setting virtual image type to VMDKS\n") } else { LOG_ERROR("Unknown output image type \"%s\"!\n",argv[i]) PrintUsage(argv[0]); exit(1); } } else { LOG_ERROR("You must specify an output image type!\n"); PrintUsage(argv[0]); exit(1); } } else if(strcmp(argv[i],"--owcache")==0) { // Enable writable access to mounted image and overwrite existing cache // Next parameter must be cache file to read/write changes from/to if((argc+1)>i) { i++; XMOUNT_STRSET(XMountConfData.pCacheFile,argv[i]) XMountConfData.Writable=TRUE; XMountConfData.OverwriteCache=TRUE; } else { LOG_ERROR("You must specify a cache file to read/write data from/to!\n") PrintUsage(argv[0]); exit(1); } LOG_DEBUG("Enabling virtual write support overwriting cache file \"%s\"\n", XMountConfData.pCacheFile) } else if(strcmp(argv[i],"--version")==0 || strcmp(argv[i],"--info")==0) { printf("xmount v%s copyright (c) 2008, 2009 by Gillen Daniel " "\n\n",PACKAGE_VERSION); #ifdef __GNUC__ printf(" compile timestamp: %s %s\n",__DATE__,__TIME__); printf(" gcc version: %s\n",__VERSION__); #endif #ifdef WITH_LIBEWF printf(" libewf support: YES (version %s)\n",LIBEWF_VERSION_STRING); #else printf(" libewf support: NO\n"); #endif #ifdef WITH_LIBAFF printf(" libaff support: YES (version %s)\n",af_version()); #else printf(" libaaf support: NO\n"); #endif printf("\n"); exit(0); } else { LOG_ERROR("Unknown command line option \"%s\"\n",argv[i]); PrintUsage(argv[0]); exit(1); } } i++; } if(AllowOther==TRUE) { // Try to add "-o allow_other" to FUSE's cmd-line params if(CheckFuseAllowOther()==TRUE) { opts+=2; XMOUNT_REALLOC(*pppNargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*pppNargv)[opts-2],"-o") XMOUNT_STRSET((*pppNargv)[opts-1],"allow_other") } } // Parse input image filename(s) while(i<(argc-1)) { files++; XMOUNT_REALLOC(*pppFilenames,char**,files*sizeof(char*)) XMOUNT_STRSET((*pppFilenames)[files-1],argv[i]) i++; } if(files==0) { LOG_ERROR("No input files specified!\n") PrintUsage(argv[0]); exit(1); } *pFilenameCount=files; // Extract mountpoint if(i==(argc-1)) { XMOUNT_STRSET(*ppMountpoint,argv[argc-1]) opts++; XMOUNT_REALLOC(*pppNargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*pppNargv)[opts-1],*ppMountpoint) } else { LOG_ERROR("No mountpoint specified!\n") PrintUsage(argv[0]); exit(1); } *pNargc=opts; return TRUE; } /* * ExtractVirtFileNames: * Extract virtual file name from input image name * * Params: * pOrigName: Name of input image (Can include a path) * * Returns: * "TRUE" on success, "FALSE" on error */ static int ExtractVirtFileNames(char *pOrigName) { char *tmp; // Truncate any leading path tmp=strrchr(pOrigName,'/'); if(tmp!=NULL) pOrigName=tmp+1; // Extract file extension tmp=strrchr(pOrigName,'.'); // Set leading '/' XMOUNT_STRSET(XMountConfData.pVirtualImagePath,"/") XMOUNT_STRSET(XMountConfData.pVirtualImageInfoPath,"/") if(XMountConfData.VirtImageType==TVirtImageType_VMDK || XMountConfData.VirtImageType==TVirtImageType_VMDKS) { XMOUNT_STRSET(XMountConfData.pVirtualVmdkPath,"/") } // Copy filename if(tmp==NULL) { // Input image filename has no extension XMOUNT_STRAPP(XMountConfData.pVirtualImagePath,pOrigName) XMOUNT_STRAPP(XMountConfData.pVirtualImageInfoPath,pOrigName) if(XMountConfData.VirtImageType==TVirtImageType_VMDK || XMountConfData.VirtImageType==TVirtImageType_VMDKS) { XMOUNT_STRAPP(XMountConfData.pVirtualVmdkPath,pOrigName) } XMOUNT_STRAPP(XMountConfData.pVirtualImageInfoPath,".info") } else { XMOUNT_STRNAPP(XMountConfData.pVirtualImagePath,pOrigName, strlen(pOrigName)-strlen(tmp)) XMOUNT_STRNAPP(XMountConfData.pVirtualImageInfoPath,pOrigName, strlen(pOrigName)-strlen(tmp)) if(XMountConfData.VirtImageType==TVirtImageType_VMDK || XMountConfData.VirtImageType==TVirtImageType_VMDKS) { XMOUNT_STRNAPP(XMountConfData.pVirtualVmdkPath,pOrigName, strlen(pOrigName)-strlen(tmp)) } XMOUNT_STRAPP(XMountConfData.pVirtualImageInfoPath,".info") } // Add virtual file extensions switch(XMountConfData.VirtImageType) { case TVirtImageType_DD: XMOUNT_STRAPP(XMountConfData.pVirtualImagePath,".dd") break; case TVirtImageType_VDI: XMOUNT_STRAPP(XMountConfData.pVirtualImagePath,".vdi") break; case TVirtImageType_VMDK: case TVirtImageType_VMDKS: XMOUNT_STRAPP(XMountConfData.pVirtualImagePath,".dd") XMOUNT_STRAPP(XMountConfData.pVirtualVmdkPath,".vmdk") break; default: LOG_ERROR("Unknown virtual image type!\n") return FALSE; } LOG_DEBUG("Set virtual image name to \"%s\"\n", XMountConfData.pVirtualImagePath) LOG_DEBUG("Set virtual image info name to \"%s\"\n", XMountConfData.pVirtualImageInfoPath) if(XMountConfData.VirtImageType==TVirtImageType_VMDK || XMountConfData.VirtImageType==TVirtImageType_VMDKS) { LOG_DEBUG("Set virtual vmdk name to \"%s\"\n", XMountConfData.pVirtualVmdkPath) } return TRUE; } /* * GetOrigImageSize: * Get size of original image * * Params: * size: Pointer to an uint64_t to which the size will be written to * * Returns: * "TRUE" on success, "FALSE" on error */ static int GetOrigImageSize(uint64_t *size) { // Make sure to return correct values when dealing with only 32bit file sizes *size=0; // When size was already queryed, use old value rather than regetting value // from disk if(XMountConfData.OrigImageSize!=0) { *size=XMountConfData.OrigImageSize; return TRUE; } // Now get size of original image switch(XMountConfData.OrigImageType) { case TOrigImageType_DD: // Original image is a DD file. Seek to end to get size. if(fseeko(hDdFile,0,SEEK_END)!=0) { LOG_ERROR("Couldn't seek to end of image file!\n") return FALSE; } *size=ftello(hDdFile); break; #ifdef WITH_LIBEWF case TOrigImageType_EWF: // Original image is an EWF file. Just query media size. if(libewf_get_media_size(hEwfFile,size)!=1) { LOG_ERROR("Couldn't get ewf media size!\n") return FALSE; } break; #endif #ifdef WITH_LIBAFF case TOrigImageType_AFF: // Original image is an AFF file. *size=af_seek(hAffFile,0,SEEK_END); break; #endif default: LOG_ERROR("Unsupported image type!\n") return FALSE; } // Save size so we have not to reget it from disk next time XMountConfData.OrigImageSize=*size; return TRUE; } /* * GetVirtImageSize: * Get size of the emulated image * * Params: * size: Pointer to an uint64_t to which the size will be written to * * Returns: * "TRUE" on success, "FALSE" on error */ static int GetVirtImageSize(uint64_t *size) { if(XMountConfData.VirtImageSize!=0) { *size=XMountConfData.VirtImageSize; return TRUE; } switch(XMountConfData.VirtImageType) { case TVirtImageType_VMDK: case TVirtImageType_VMDKS: case TVirtImageType_DD: // Virtual image is a DD or VMDK file. Just return the size of the // original image if(!GetOrigImageSize(size)) { LOG_ERROR("Couldn't get size of input image!\n") return FALSE; } break; case TVirtImageType_VDI: // Virtual image is a VDI file. Get size of original image and add size // of VDI header etc. if(!GetOrigImageSize(size)) { LOG_ERROR("Couldn't get size of input image!\n") return FALSE; } (*size)+=(sizeof(TVdiFileHeader)+VdiBlockMapSize); break; default: LOG_ERROR("Unsupported image type!\n") return FALSE; } XMountConfData.VirtImageSize=*size; return TRUE; } /* * GetOrigImageData: * Read data from original image * * Params: * buf: Pointer to buffer to write read data to (Must be preallocated!) * offset: Offset at which data should be read * size: Size of data which should be read (Size of buffer) * * Returns: * Number of read bytes on success or "-1" on error */ static int GetOrigImageData(char *buf, off_t offset, size_t size) { size_t ToRead=0; uint64_t ImageSize=0; // Make sure we aren't reading past EOF of image file if(!GetOrigImageSize(&ImageSize)) { LOG_ERROR("Couldn't get image size!\n") return -1; } if(offset>=ImageSize) { // Offset is beyond image size LOG_DEBUG("Offset is beyond image size.\n") return 0; } if(offset+size>ImageSize) { // Attempt to read data past EOF of image file ToRead=ImageSize-offset; LOG_DEBUG("Attempt to read data past EOF. Corrected size from %zd" " to %zd.\n",size,ToRead) } else ToRead=size; // Now read data from image file switch(XMountConfData.OrigImageType) { case TOrigImageType_DD: // Original image is a DD file. Seek to offset and read ToRead bytes. // TODO: Perhaps check whether it is cheaper to seek from current position // to offset than seeking from beginning of the file if(fseeko(hDdFile,offset,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to offset %" PRIu64 "!\n",offset) return -1; } if(fread(buf,ToRead,1,hDdFile)!=1) { LOG_ERROR("Couldn't read %zd bytes from offset %" PRIu64 "!\n",ToRead,offset) return -1; } LOG_DEBUG("Read %zd bytes at offset %" PRIu64 " from DD file\n", ToRead,offset) break; #ifdef WITH_LIBEWF case TOrigImageType_EWF: // Original image is an EWF file. Seek to offset and read ToRead bytes. if(libewf_seek_offset(hEwfFile,offset)!=-1) { if(libewf_read_buffer(hEwfFile,buf,ToRead)!=ToRead) { LOG_ERROR("Couldn't read %zd bytes from offset %" PRIu64 "!\n",ToRead,offset) return -1; } } else { LOG_ERROR("Couldn't seek to offset %" PRIu64 "!\n",offset) return -1; } LOG_DEBUG("Read %zd bytes at offset %" PRIu64 " from EWF file\n", ToRead,offset) break; #endif #ifdef WITH_LIBAFF case TOrigImageType_AFF: // Original image is an AFF file. af_seek(hAffFile,offset,SEEK_SET); if(af_read(hAffFile,buf,ToRead)!=ToRead) { LOG_ERROR("Couldn't read %zd bytes from offset %" PRIu64 "!\n",ToRead,offset) return -1; } LOG_DEBUG("Read %zd bytes at offset %" PRIu64 " from AFF file\n", ToRead,offset) break; #endif default: LOG_ERROR("Unsupported image type!\n") return -1; } return ToRead; } /* * GetVirtVmdkData: * Read data from virtual VMDK file * * Params: * buf: Pointer to buffer to write read data to (Must be preallocated!) * offset: Offset at which data should be read * size: Size of data which should be read (Size of buffer) * * Returns: * Number of read bytes on success or "-1" on error */ /* static int GetVirtualVmdkData(char *buf, off_t offset, size_t size) { uint32_t len; len=strlen(pVirtualVmdkFile); if(offsetlen) { size=len-offset; LOG_DEBUG("Attempt to read past EOF of virtual vmdk file\n") } if(XMountConfData.Writable==TRUE && pCacheFileHeader->VmdkFileCached==TRUE) { // VMDK file is cached. Read data from cache file // TODO: Read data from cache file } else { // No write support or VMDK file not cached. memcpy(buf,pVirtualVmdkFile+offset,size); LOG_DEBUG("Read %" PRIu64 " bytes at offset %" PRIu64 " from virtual vmdk file\n",size,offset) } } else { LOG_DEBUG("Attempt to read past EOF of virtual vmdk file\n"); return -1; } return size; } */ /* * GetVirtImageData: * Read data from virtual image * * Params: * buf: Pointer to buffer to write read data to (Must be preallocated!) * offset: Offset at which data should be read * size: Size of data which should be read (Size of buffer) * * Returns: * Number of read bytes on success or "-1" on error */ static int GetVirtImageData(char *buf, off_t offset, size_t size) { uint32_t CurBlock=0; uint64_t VirtImageSize; size_t ToRead=0; size_t CurToRead=0; off_t FileOff=offset; off_t BlockOff=0; // Get virtual image size if(!GetVirtImageSize(&VirtImageSize)) { LOG_ERROR("Couldn't get virtual image size!\n") return -1; } if(offset>=VirtImageSize) { LOG_ERROR("Attempt to read beyond virtual image EOF!\n") return -1; } if(offset+size>VirtImageSize) { LOG_DEBUG("Attempt to read pas EOF of virtual image file\n") size=VirtImageSize-offset; } ToRead=size; // Read virtual image type specific data switch(XMountConfData.VirtImageType) { case TVirtImageType_DD: case TVirtImageType_VMDK: case TVirtImageType_VMDKS: break; case TVirtImageType_VDI: if(FileOffVdiFileHeaderSize) CurToRead=VdiFileHeaderSize-FileOff; else CurToRead=ToRead; if(XMountConfData.Writable==TRUE && pCacheFileHeader->VdiFileHeaderCached==TRUE) { // VDI header was already cached if(fseeko(hCacheFile, pCacheFileHeader->pVdiFileHeader+FileOff, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to cached VDI header at offset %" PRIu64 "\n",pCacheFileHeader->pVdiFileHeader+FileOff) return 0; } if(fread(buf,CurToRead,1,hCacheFile)!=1) { LOG_ERROR("Couldn't read %zu bytes from cache file at offset %" PRIu64 "\n",CurToRead, pCacheFileHeader->pVdiFileHeader+FileOff) return 0; } LOG_DEBUG("Read %zd bytes from cached VDI header at offset %" PRIu64 " at cache file offset %" PRIu64 "\n", CurToRead,FileOff, pCacheFileHeader->pVdiFileHeader+FileOff) } else { // VDI header isn't cached memcpy(buf,((char*)pVdiFileHeader)+FileOff,CurToRead); LOG_DEBUG("Read %zd bytes at offset %" PRIu64 " from virtual VDI header\n",CurToRead, FileOff) } if(ToRead==CurToRead) return ToRead; else { // Adjust values to read from original image ToRead-=CurToRead; buf+=CurToRead; FileOff=0; } } else FileOff-=VdiFileHeaderSize; break; } // Calculate block to read data from CurBlock=FileOff/CACHE_BLOCK_SIZE; BlockOff=FileOff%CACHE_BLOCK_SIZE; // Read image data while(ToRead!=0) { // Calculate how many bytes we have to read from this block if(BlockOff+ToRead>CACHE_BLOCK_SIZE) { CurToRead=CACHE_BLOCK_SIZE-BlockOff; } else CurToRead=ToRead; if(XMountConfData.Writable==TRUE && pCacheFileBlockIndex[CurBlock].Assigned==TRUE) { // Write support enabled and need to read altered data from cachefile if(fseeko(hCacheFile, pCacheFileBlockIndex[CurBlock].off_data+BlockOff, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to offset %" PRIu64 " in cache file\n") return -1; } if(fread(buf,CurToRead,1,hCacheFile)!=1) { LOG_ERROR("Couldn't read data from cache file!\n") return -1; } LOG_DEBUG("Read %zd bytes at offset %" PRIu64 " from cache file\n",CurToRead,FileOff) } else { // No write support or data not cached if(GetOrigImageData(buf, FileOff, CurToRead)!=CurToRead) { LOG_ERROR("Couldn't read data from input image!\n") return -1; } LOG_DEBUG("Read %zd bytes at offset %" PRIu64 " from original image file\n",CurToRead, FileOff) } CurBlock++; BlockOff=0; buf+=CurToRead; ToRead-=CurToRead; FileOff+=CurToRead; } return size; } /* * SetVdiFileHeaderData: * Write data to virtual VDI file header * * Params: * buf: Buffer containing data to write * offset: Offset of changes * size: Amount of bytes to write * * Returns: * Number of written bytes on success or "-1" on error */ static int SetVdiFileHeaderData(char *buf,off_t offset,size_t size) { if(offset+size>VdiFileHeaderSize) size=VdiFileHeaderSize-offset; LOG_DEBUG("Need to cache %zu bytes at offset %" PRIu64 " from VDI header\n",size,offset) if(pCacheFileHeader->VdiFileHeaderCached==1) { // Header was already cached if(fseeko(hCacheFile, pCacheFileHeader->pVdiFileHeader+offset, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to cached VDI header at address %" PRIu64 "\n",pCacheFileHeader->pVdiFileHeader+offset) return -1; } if(fwrite(buf,size,1,hCacheFile)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" PRIu64 "\n",size, pCacheFileHeader->pVdiFileHeader+offset) return -1; } LOG_DEBUG("Wrote %zd bytes at offset %" PRIu64 " to cache file\n", size,pCacheFileHeader->pVdiFileHeader+offset) } else { // Header wasn't already cached. if(fseeko(hCacheFile, 0, SEEK_END)!=0) { LOG_ERROR("Couldn't seek to end of cache file!") return -1; } pCacheFileHeader->pVdiFileHeader=ftello(hCacheFile); LOG_DEBUG("Caching whole VDI header\n") if(offset>0) { // Changes do not begin at offset 0, need to prepend with data from // VDI header if(fwrite((char*)pVdiFileHeader,offset,1,hCacheFile)!=1) { LOG_ERROR("Error while writing %" PRIu64 " bytes " "to cache file at offset %" PRIu64 "!\n", offset, pCacheFileHeader->pVdiFileHeader); return -1; } LOG_DEBUG("Prepended changed data with %" PRIu64 " bytes at cache file offset %" PRIu64 "\n", offset,pCacheFileHeader->pVdiFileHeader) } // Cache changed data if(fwrite(buf,size,1,hCacheFile)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" PRIu64 "\n",size, pCacheFileHeader->pVdiFileHeader+offset) return -1; } LOG_DEBUG("Wrote %zu bytes of changed data to cache file offset %" PRIu64 "\n",size, pCacheFileHeader->pVdiFileHeader+offset) if(offset+size!=VdiFileHeaderSize) { // Need to append data from VDI header to cache whole data struct if(fwrite(((char*)pVdiFileHeader)+offset+size, VdiFileHeaderSize-(offset+size), 1, hCacheFile)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" PRIu64 "\n",VdiFileHeaderSize-(offset+size), (uint64_t)(pCacheFileHeader->pVdiFileHeader+offset+size)) return -1; } LOG_DEBUG("Appended %" PRIu32 " bytes to changed data at cache file offset %" PRIu64 "\n",VdiFileHeaderSize-(offset+size), pCacheFileHeader->pVdiFileHeader+offset+size) } // Mark header as cached and update header in cache file pCacheFileHeader->VdiFileHeaderCached=1; if(fseeko(hCacheFile,0,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to offset 0 of cache file!\n") return -1; } if(fwrite((char*)pCacheFileHeader,sizeof(TCacheFileHeader),1,hCacheFile)!=1) { LOG_ERROR("Couldn't write changed cache file header!\n") return -1; } } // All important data has been written, now flush all buffers to make // sure data is written to cache file fflush(hCacheFile); ioctl(fileno(hCacheFile),BLKFLSBUF,0); return size; } /* * SetVirtImageData: * Write data to virtual image * * Params: * buf: Buffer containing data to write * offset: Offset to start writing at * size: Size of data to be written * * Returns: * Number of written bytes on success or "-1" on error */ static int SetVirtImageData(const char *buf, off_t offset, size_t size) { uint64_t CurBlock=0; uint64_t VirtImageSize; uint64_t OrigImageSize; size_t ToWrite=0; size_t CurToWrite=0; off_t FileOff=offset; off_t BlockOff=0; char *WriteBuf=(char*)buf; char *buf2; ssize_t ret; // Get virtual image size if(!GetVirtImageSize(&VirtImageSize)) { LOG_ERROR("Couldn't get virtual image size!\n") return -1; } if(offset>=VirtImageSize) { LOG_ERROR("Attempt to write beyond EOF of virtual image file!\n") return -1; } if(offset+size>VirtImageSize) { LOG_DEBUG("Attempt to write past EOF of virtual image file\n") size=VirtImageSize-offset; } ToWrite=size; // Cache virtual image type specific data if(XMountConfData.VirtImageType==TVirtImageType_VDI) { if(FileOffCACHE_BLOCK_SIZE) { CurToWrite=CACHE_BLOCK_SIZE-BlockOff; } else CurToWrite=ToWrite; if(pCacheFileBlockIndex[CurBlock].Assigned==1) { // Block was already cached // Seek to data offset in cache file if(fseeko(hCacheFile, pCacheFileBlockIndex[CurBlock].off_data+BlockOff, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to cached block at address %" PRIu64 "\n", pCacheFileBlockIndex[CurBlock].off_data+BlockOff) return -1; } if(fwrite(WriteBuf,CurToWrite,1,hCacheFile)!=1) { LOG_ERROR("Error while writing %zu bytes " "to cache file at offset %" PRIu64 "!\n", CurToWrite, pCacheFileBlockIndex[CurBlock].off_data+BlockOff); return -1; } LOG_DEBUG("Wrote %zd bytes at offset %" PRIu64 " to cache file\n",CurToWrite, pCacheFileBlockIndex[CurBlock].off_data+BlockOff) } else { // Uncached block. Need to cache entire new block // Seek to end of cache file to append new cache block fseeko(hCacheFile,0,SEEK_END); pCacheFileBlockIndex[CurBlock].off_data=ftello(hCacheFile); if(BlockOff!=0) { // Changed data does not begin at block boundry. Need to prepend // with data from virtual image file XMOUNT_MALLOC(buf2,char*,BlockOff*sizeof(char)) if(GetOrigImageData(buf2,FileOff-BlockOff,BlockOff)!=BlockOff) { LOG_ERROR("Couldn't read data from original image file!\n") return -1; } if(fwrite(buf2,BlockOff,1,hCacheFile)!=1) { LOG_ERROR("Couldn't writing %" PRIu64 " bytes " "to cache file at offset %" PRIu64 "!\n", BlockOff, pCacheFileBlockIndex[CurBlock].off_data); return -1; } LOG_DEBUG("Prepended changed data with %" PRIu64 " bytes from virtual image file at offset %" PRIu64 "\n",BlockOff,FileOff-BlockOff) free(buf2); } if(fwrite(WriteBuf,CurToWrite,1,hCacheFile)!=1) { LOG_ERROR("Error while writing %zd bytes " "to cache file at offset %" PRIu64 "!\n", CurToWrite, pCacheFileBlockIndex[CurBlock].off_data+BlockOff); return -1; } if(BlockOff+CurToWrite!=CACHE_BLOCK_SIZE) { // Changed data does not end at block boundry. Need to append // with data from virtual image file XMOUNT_MALLOC(buf2,char*,(CACHE_BLOCK_SIZE- (BlockOff+CurToWrite))*sizeof(char)) memset(buf2,0,CACHE_BLOCK_SIZE-(BlockOff+CurToWrite)); if((FileOff-BlockOff)+CACHE_BLOCK_SIZE>OrigImageSize) { // Original image is smaller than full cache block if(GetOrigImageData(buf2, FileOff+CurToWrite, OrigImageSize-(FileOff+CurToWrite))!= OrigImageSize-(FileOff+CurToWrite)) { LOG_ERROR("Couldn't read data from virtual image file!\n") return -1; } } else { if(GetOrigImageData(buf2, FileOff+CurToWrite, CACHE_BLOCK_SIZE-(BlockOff+CurToWrite))!= CACHE_BLOCK_SIZE-(BlockOff+CurToWrite)) { LOG_ERROR("Couldn't read data from virtual image file!\n") return -1; } } if(fwrite(buf2, CACHE_BLOCK_SIZE-(BlockOff+CurToWrite), 1, hCacheFile)!=1) { LOG_ERROR("Error while writing %zd bytes " "to cache file at offset %" PRIu64 "!\n", CACHE_BLOCK_SIZE-(BlockOff+CurToWrite), pCacheFileBlockIndex[CurBlock].off_data+BlockOff+CurToWrite); return -1; } free(buf2); } // All important data for this cache block has been written, // flush all buffers and mark cache block as assigned fflush(hCacheFile); ioctl(fileno(hCacheFile),BLKFLSBUF,0); pCacheFileBlockIndex[CurBlock].Assigned=1; // Update cache block index entry in cache file fseeko(hCacheFile, sizeof(TCacheFileHeader)+(CurBlock*sizeof(TCacheFileBlockIndex)), SEEK_SET); if(fwrite(&(pCacheFileBlockIndex[CurBlock]), sizeof(TCacheFileBlockIndex), 1, hCacheFile)!=1) { LOG_ERROR("Couldn't update cache file block index!\n"); return -1; } LOG_DEBUG("Updated cache file block index: Number=%" PRIu64 ", Data offset=%" PRIu64 "\n",CurBlock, pCacheFileBlockIndex[CurBlock].off_data); } // Flush buffers fflush(hCacheFile); ioctl(fileno(hCacheFile),BLKFLSBUF,0); BlockOff=0; CurBlock++; WriteBuf+=CurToWrite; ToWrite-=CurToWrite; FileOff+=CurToWrite; } return size; } /* * GetVirtFileAccess: * FUSE access implementation * * Params: * path: Path of file to get attributes from * perm: Requested permissisons * * Returns: * "0" on success, negated error code on error */ /* static int GetVirtFileAccess(const char *path, int perm) { // TODO: Implement propper file permission handling // http://www.cs.cf.ac.uk/Dave/C/node20.html // Values for the second argument to access. // These may be OR'd together. //#define R_OK 4 // Test for read permission. //#define W_OK 2 // Test for write permission. //#define X_OK 1 // Test for execute permission. //#define F_OK 0 // Test for existence. return 0; } */ /* * GetVirtFileAttr: * FUSE getattr implementation * * Params: * path: Path of file to get attributes from * stbuf: Pointer to stat structure to save attributes to * * Returns: * "0" on success, negated error code on error */ static int GetVirtFileAttr(const char *path, struct stat *stbuf) { memset(stbuf,0,sizeof(struct stat)); if(strcmp(path,"/")==0) { // Attributes of mountpoint stbuf->st_mode=S_IFDIR | 0777; stbuf->st_nlink=2; } else if(strcmp(path,XMountConfData.pVirtualImagePath)==0) { // Attributes of virtual image if(!XMountConfData.Writable) stbuf->st_mode=S_IFREG | 0444; else stbuf->st_mode=S_IFREG | 0666; stbuf->st_nlink=1; // Get virtual image file size if(!GetVirtImageSize(&(stbuf->st_size))) { LOG_ERROR("Couldn't get image size!\n"); return -ENOENT; } } else if(strcmp(path,XMountConfData.pVirtualImageInfoPath)==0) { // Attributes of virtual image info file stbuf->st_mode=S_IFREG | 0444; stbuf->st_nlink=1; // Get virtual image info file size if(pVirtualImageInfoFile!=NULL) { stbuf->st_size=strlen(pVirtualImageInfoFile); } else stbuf->st_size=0; } else if(XMountConfData.VirtImageType==TVirtImageType_VMDK || XMountConfData.VirtImageType==TVirtImageType_VMDKS) { // Some special files only present when emulating VMDK files if(strcmp(path,XMountConfData.pVirtualVmdkPath)==0) { // Attributes of virtual vmdk file if(!XMountConfData.Writable) stbuf->st_mode=S_IFREG | 0444; else stbuf->st_mode=S_IFREG | 0666; stbuf->st_nlink=1; // Get virtual image info file size if(pVirtualVmdkFile!=NULL) { stbuf->st_size=VirtualVmdkFileSize; } else stbuf->st_size=0; } else if(pVirtualVmdkLockDir!=NULL && strcmp(path,pVirtualVmdkLockDir)==0) { stbuf->st_mode=S_IFDIR | 0777; stbuf->st_nlink=2; } else if(pVirtualVmdkLockDir2!=NULL && strcmp(path,pVirtualVmdkLockDir2)==0) { stbuf->st_mode=S_IFDIR | 0777; stbuf->st_nlink=2; } else if(pVirtualVmdkLockFileName!=NULL && strcmp(path,pVirtualVmdkLockFileName)==0) { stbuf->st_mode=S_IFREG | 0666; if(pVirtualVmdkLockFileName!=NULL) { stbuf->st_size=strlen(pVirtualVmdkLockFileName); } else stbuf->st_size=0; } else return -ENOENT; } else return -ENOENT; // Set uid and gid of all files to uid and gid of current process stbuf->st_uid=getuid(); stbuf->st_gid=getgid(); return 0; } /* * CreateVirtDir: * FUSE mkdir implementation * * Params: * path: Directory path * mode: Directory permissions * * Returns: * "0" on success, negated error code on error */ static int CreateVirtDir(const char *path, mode_t mode) { // Only allow creation of VMWare's lock directories if(XMountConfData.VirtImageType==TVirtImageType_VMDK || XMountConfData.VirtImageType==TVirtImageType_VMDKS) { if(pVirtualVmdkLockDir==NULL) { char aVmdkLockDir[strlen(XMountConfData.pVirtualVmdkPath)+5]; sprintf(aVmdkLockDir,"%s.lck",XMountConfData.pVirtualVmdkPath); if(strcmp(path,aVmdkLockDir)==0) { LOG_DEBUG("Creating virtual directory \"%s\"\n",aVmdkLockDir) XMOUNT_STRSET(pVirtualVmdkLockDir,aVmdkLockDir) return 0; } else { LOG_ERROR("Attempt to create illegal directory \"%s\"!\n",path) LOG_DEBUG("Supposed: %s\n",aVmdkLockDir) return -1; } } else if(pVirtualVmdkLockDir2==NULL && strncmp(path,pVirtualVmdkLockDir,strlen(pVirtualVmdkLockDir))==0) { LOG_DEBUG("Creating virtual directory \"%s\"\n",path) XMOUNT_STRSET(pVirtualVmdkLockDir2,path) return 0; } else { LOG_ERROR("Attempt to create illegal directory \"%s\"!\n",path) LOG_DEBUG("Compared to first %u chars of \"%s\"\n",strlen(pVirtualVmdkLockDir),pVirtualVmdkLockDir) return -1; } } LOG_ERROR("Attempt to create directory \"%s\" " "on read-only filesystem!\n",path) return -1; } /* * CreateVirtFile: * FUSE create implementation. * Only allows to create VMWare's lock file! * * Params: * path: File to create * mode: File mode * dev: ??? but not used * * Returns: * "0" on success, negated error code on error */ static int CreateVirtFile(const char *path, mode_t mode, dev_t dev) { if((XMountConfData.VirtImageType==TVirtImageType_VMDK || XMountConfData.VirtImageType==TVirtImageType_VMDKS) && pVirtualVmdkLockDir!=NULL && pVirtualVmdkLockFileName==NULL) { LOG_DEBUG("Creating virtual file \"%s\"\n",path) XMOUNT_STRSET(pVirtualVmdkLockFileName,path); return 0; } else { LOG_ERROR("Attempt to create illegal file \"%s\"\n",path) return -1; } } /* * GetVirtFiles: * FUSE readdir implementation * * Params: * path: Path from where files should be listed * buf: Buffer to write file entrys to * filler: Function to write file entrys to buffer * offset: ??? but not used * fi: ??? but not used * * Returns: * "0" on success, negated error code on error */ static int GetVirtFiles(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { (void)offset; (void)fi; if(strcmp(path,"/")==0) { // Add std . and .. entrys filler(buf,".",NULL,0); filler(buf,"..",NULL,0); // Add our virtual files (p+1 to ignore starting "/") filler(buf,XMountConfData.pVirtualImagePath+1,NULL,0); filler(buf,XMountConfData.pVirtualImageInfoPath+1,NULL,0); if(XMountConfData.VirtImageType==TVirtImageType_VMDK || XMountConfData.VirtImageType==TVirtImageType_VMDKS) { // For VMDK's, we use an additional descriptor file filler(buf,XMountConfData.pVirtualVmdkPath+1,NULL,0); // And there could also be a lock directory if(pVirtualVmdkLockDir!=NULL) { filler(buf,pVirtualVmdkLockDir+1,NULL,0); } } } else if(XMountConfData.VirtImageType==TVirtImageType_VMDK || XMountConfData.VirtImageType==TVirtImageType_VMDKS) { // For VMDK emulation, there could be a lock directory if(pVirtualVmdkLockDir!=NULL && strcmp(path,pVirtualVmdkLockDir)==0) { filler(buf,".",NULL,0); filler(buf,"..",NULL,0); if(pVirtualVmdkLockFileName!=NULL) { filler(buf,pVirtualVmdkLockFileName+strlen(pVirtualVmdkLockDir)+1,NULL,0); } } else if(pVirtualVmdkLockDir2!=NULL && strcmp(path,pVirtualVmdkLockDir2)==0) { filler(buf,".",NULL,0); filler(buf,"..",NULL,0); } else return -ENOENT; } else return -ENOENT; return 0; } /* * OpenVirtFile: * FUSE open implementation * * Params: * path: Path to file to open * fi: ??? but not used * * Returns: * "0" on success, negated error code on error */ static int OpenVirtFile(const char *path, struct fuse_file_info *fi) { if(strcmp(path,XMountConfData.pVirtualImagePath)==0 || strcmp(path,XMountConfData.pVirtualImageInfoPath)==0) { // Check open permissions if(!XMountConfData.Writable && (fi->flags & 3)!=O_RDONLY) { // Attempt to open a read-only file for writing LOG_DEBUG("Attempt to open the read-only file \"%s\" for writing.\n",path) return -EACCES; } return 0; } else if(XMountConfData.VirtImageType==TVirtImageType_VMDK || XMountConfData.VirtImageType==TVirtImageType_VMDKS) { if(strcmp(path,XMountConfData.pVirtualVmdkPath)==0) { // Check open permissions if(!XMountConfData.Writable && (fi->flags & 3)!=O_RDONLY) { // Attempt to open a read-only file for writing LOG_DEBUG("Attempt to open the read-only file \"%s\" for writing.\n",path) return -EACCES; } return 0; } else if(pVirtualVmdkLockFileName!=NULL && strcmp(path,pVirtualVmdkLockFileName)==0) { // Check open permissions if(!XMountConfData.Writable && (fi->flags & 3)!=O_RDONLY) { // Attempt to open a read-only file for writing LOG_DEBUG("Attempt to open the read-only file \"%s\" for writing.\n",path) return -EACCES; } return 0; } else { // Attempt to open a non existant file LOG_DEBUG("Attempt to open non existant file \"%s\".\n",path) return -ENOENT; } } else { // Attempt to open a non existant file LOG_DEBUG("Attempt to open non existant file \"%s\".\n",path) return -ENOENT; } } /* * ReadVirtFile: * FUSE read implementation * * Params: * buf: Buffer where read data is written to * size: Number of bytes to read * offset: Offset to start reading at * fi: ?? but not used * * Returns: * Read bytes on success, negated error code on error */ static int ReadVirtFile(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { uint64_t len; if(strcmp(path,XMountConfData.pVirtualImagePath)==0) { // Wait for other threads to end reading/writing data pthread_mutex_lock(&mutex_image_rw); // Get virtual image file size if(!GetVirtImageSize(&len)) { LOG_ERROR("Couldn't get virtual image size!\n") pthread_mutex_unlock(&mutex_image_rw); return 0; } if(offsetlen) size=len-offset; if(GetVirtImageData(buf,offset,size)!=size) { LOG_ERROR("Couldn't read data from virtual image file!\n") pthread_mutex_unlock(&mutex_image_rw); return 0; } } else { LOG_DEBUG("Attempt to read past EOF of virtual image file\n"); pthread_mutex_unlock(&mutex_image_rw); return 0; } // Allow other threads to read/write data again pthread_mutex_unlock(&mutex_image_rw); } else if(strcmp(path,XMountConfData.pVirtualImageInfoPath)==0) { // Read data from virtual image info file len=strlen(pVirtualImageInfoFile); if(offsetlen) { size=len-offset; LOG_DEBUG("Attempt to read past EOF of virtual image info file\n") } pthread_mutex_lock(&mutex_info_read); memcpy(buf,pVirtualImageInfoFile+offset,size); pthread_mutex_unlock(&mutex_info_read); LOG_DEBUG("Read %" PRIu64 " bytes at offset %" PRIu64 " from virtual image info file\n",size,offset) } else { LOG_DEBUG("Attempt to read past EOF of virtual info file\n"); return 0; } } else if(strcmp(path,XMountConfData.pVirtualVmdkPath)==0) { // Read data from virtual vmdk file len=VirtualVmdkFileSize; if(offsetlen) { LOG_DEBUG("Attempt to read past EOF of virtual vmdk file\n") LOG_DEBUG("Adjusting read size from %u to %u\n",size,len-offset) size=len-offset; } pthread_mutex_lock(&mutex_image_rw); memcpy(buf,pVirtualVmdkFile+offset,size); pthread_mutex_unlock(&mutex_image_rw); LOG_DEBUG("Read %" PRIu64 " bytes at offset %" PRIu64 " from virtual vmdk file\n",size,offset) } else { LOG_DEBUG("Attempt to read behind EOF of virtual vmdk file\n"); return 0; } } else if(pVirtualVmdkLockFileName!=NULL && strcmp(path,pVirtualVmdkLockFileName)==0) { // Read data from virtual lock file len=VirtualVmdkLockFileDataSize; if(offsetlen) { LOG_DEBUG("Attempt to read past EOF of virtual vmdk lock file\n") LOG_DEBUG("Adjusting read size from %u to %u\n",size,len-offset) size=len-offset; } pthread_mutex_lock(&mutex_image_rw); memcpy(buf,pVirtualVmdkLockFileData+offset,size); pthread_mutex_unlock(&mutex_image_rw); LOG_DEBUG("Read %" PRIu64 " bytes at offset %" PRIu64 " from virtual vmdk lock file\n",size,offset) } else { LOG_DEBUG("Attempt to read past EOF of virtual vmdk lock file\n"); return 0; } } else { // Attempt to read non existant file LOG_DEBUG("Attempt to read from non existant file \"%s\"\n",path) return -ENOENT; } return size; } /* * RenameVirtFile: * FUSE rename implementation * * Params: * path: File to rename * npath: New filename * * Returns: * "0" on error, negated error code on error */ static int RenameVirtFile(const char *path, const char *npath) { if(XMountConfData.VirtImageType==TVirtImageType_VMDK || XMountConfData.VirtImageType==TVirtImageType_VMDKS) { if(pVirtualVmdkLockFileName!=NULL && strcmp(path,pVirtualVmdkLockFileName)==0) { LOG_DEBUG("Renaming virtual lock file from \"%s\" to \"%s\"\n", pVirtualVmdkLockFileName, npath) XMOUNT_REALLOC(pVirtualVmdkLockFileName,char*, (strlen(npath)+1)*sizeof(char)); strcpy(pVirtualVmdkLockFileName,npath); return 0; } } return -ENOENT; } /* * DeleteVirtDir: * FUSE rmdir implementation * * Params: * path: Directory to delete * * Returns: * "0" on success, negated error code on error */ static int DeleteVirtDir(const char *path) { // Only VMWare's lock directories can be deleted if(XMountConfData.VirtImageType==TVirtImageType_VMDK || XMountConfData.VirtImageType==TVirtImageType_VMDKS) { if(pVirtualVmdkLockDir!=NULL && strcmp(path,pVirtualVmdkLockDir)==0) { LOG_DEBUG("Deleting virtual lock dir \"%s\"\n",pVirtualVmdkLockDir) free(pVirtualVmdkLockDir); pVirtualVmdkLockDir=NULL; return 0; } else if(pVirtualVmdkLockDir2!=NULL && strcmp(path,pVirtualVmdkLockDir2)==0) { LOG_DEBUG("Deleting virtual lock dir \"%s\"\n",pVirtualVmdkLockDir) free(pVirtualVmdkLockDir2); pVirtualVmdkLockDir2=NULL; return 0; } } return -1; } /* * DeleteVirtFile: * FUSE unlink implementation * * Params: * path: File to delete * * Returns: * "0" on success, negated error code on error */ static int DeleteVirtFile(const char *path) { // Only VMWare's lock file can be deleted if(XMountConfData.VirtImageType==TVirtImageType_VMDK || XMountConfData.VirtImageType==TVirtImageType_VMDKS) { if(pVirtualVmdkLockFileName!=NULL && strcmp(path,pVirtualVmdkLockFileName)==0) { LOG_DEBUG("Deleting virtual file \"%s\"\n",pVirtualVmdkLockFileName) free(pVirtualVmdkLockFileName); free(pVirtualVmdkLockFileData); pVirtualVmdkLockFileName=NULL; pVirtualVmdkLockFileData=NULL; VirtualVmdkLockFileDataSize=0; return 0; } } return -1; } /* * GetVirtFsStats: * FUSE statfs implementation * * Params: * path: Get stats for fs that the specified file resides in * stats: Stats * * Returns: * "0" on success, negated error code on error */ /* static int GetVirtFsStats(const char *path, struct statvfs *stats) { struct statvfs CacheFileFsStats; int ret; if(XMountConfData.Writable==TRUE) { // If write support is enabled, return stats of fs upon which cache file // resides in if((ret=statvfs(XMountConfData.pCacheFile,&CacheFileFsStats))==0) { memcpy(stats,&CacheFileFsStats,sizeof(struct statvfs)); return 0; } else { LOG_ERROR("Couldn't get stats for fs upon which resides \"%s\"\n", XMountConfData.pCacheFile) return ret; } } else { // TODO: Return read only return 0; } } */ /* * WriteVirtFile: * FUSE write implementation * * Params: * buf: Buffer containing data to write * size: Number of bytes to write * offset: Offset to start writing at * fi: ?? but not used * * Returns: * Written bytes on success, negated error code on error */ static int WriteVirtFile(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { uint64_t len; if(strcmp(path,XMountConfData.pVirtualImagePath)==0) { // Wait for other threads to end reading/writing data pthread_mutex_lock(&mutex_image_rw); // Get virtual image file size if(!GetVirtImageSize(&len)) { LOG_ERROR("Couldn't get virtual image size!\n") pthread_mutex_unlock(&mutex_image_rw); return 0; } if(offsetlen) size=len-offset; if(SetVirtImageData(buf,offset,size)!=size) { LOG_ERROR("Couldn't write data to virtual image file!\n") pthread_mutex_unlock(&mutex_image_rw); return 0; } } else { LOG_DEBUG("Attempt to write past EOF of virtual image file\n") pthread_mutex_unlock(&mutex_image_rw); return 0; } // Allow other threads to read/write data again pthread_mutex_unlock(&mutex_image_rw); } else if(strcmp(path,XMountConfData.pVirtualVmdkPath)==0) { pthread_mutex_lock(&mutex_image_rw); len=VirtualVmdkFileSize; if((offset+size)>len) { // Enlarge or create buffer if needed if(len==0) { len=offset+size; XMOUNT_MALLOC(pVirtualVmdkFile,char*,len*sizeof(char)) } else { len=offset+size; XMOUNT_REALLOC(pVirtualVmdkFile,char*,len*sizeof(char)) } VirtualVmdkFileSize=offset+size; } // Copy data to buffer memcpy(pVirtualVmdkFile+offset,buf,size); pthread_mutex_unlock(&mutex_image_rw); } else if(pVirtualVmdkLockFileName!=NULL && strcmp(path,pVirtualVmdkLockFileName)==0) { pthread_mutex_lock(&mutex_image_rw); if((offset+size)>VirtualVmdkLockFileDataSize) { // Enlarge or create buffer if needed if(VirtualVmdkLockFileDataSize==0) { VirtualVmdkLockFileDataSize=offset+size; XMOUNT_MALLOC(pVirtualVmdkLockFileData,char*, VirtualVmdkLockFileDataSize*sizeof(char)) } else { VirtualVmdkLockFileDataSize=offset+size; XMOUNT_REALLOC(pVirtualVmdkLockFileData,char*, VirtualVmdkLockFileDataSize*sizeof(char)) } } // Copy data to buffer memcpy(pVirtualVmdkLockFileData+offset,buf,size); pthread_mutex_unlock(&mutex_image_rw); } else if(strcmp(path,XMountConfData.pVirtualImageInfoPath)==0) { // Attempt to write data to read only image info file LOG_DEBUG("Attempt to write data to virtual info file\n"); return -ENOENT; } else { // Attempt to write to non existant file LOG_DEBUG("Attempt to write to the non existant file \"%s\"\n",path) return -ENOENT; } return size; } /* * CalculateInputImageHash: * Calculates an MD5 hash of the first HASH_AMOUNT bytes of the input image. * * Params: * pHashLow : Pointer to the lower 64 bit of the hash * pHashHigh : Pointer to the higher 64 bit of the hash * * Returns: * TRUE on success, FALSE on error */ static int CalculateInputImageHash(uint64_t *pHashLow, uint64_t *pHashHigh) { char hash[16]; md5_state_t md5_state; char *buf; XMOUNT_MALLOC(buf,char*,HASH_AMOUNT*sizeof(char)) size_t read_data=GetOrigImageData(buf,0,HASH_AMOUNT); if(read_data>0) { // Calculate MD5 hash md5_init(&md5_state); md5_append(&md5_state,buf,HASH_AMOUNT); md5_finish(&md5_state,hash); // Convert MD5 hash into two 64bit integers *pHashLow=*((uint64_t*)hash); *pHashHigh=*((uint64_t*)(hash+8)); free(buf); return TRUE; } else { LOG_ERROR("Couldn't read data from original image file!\n") free(buf); return FALSE; } } /* * InitVirtVdiHeader: * Build and init virtual VDI file header * * Params: * n/a * * Returns: * "TRUE" on success, "FALSE" on error */ static int InitVirtVdiHeader() { // See http://forums.virtualbox.org/viewtopic.php?t=8046 for a // "description" of the various header fields uint64_t ImageSize; off_t offset; uint32_t i,BlockEntries; // Get input image size if(!GetOrigImageSize(&ImageSize)) { LOG_ERROR("Couldn't get input image size!\n") return FALSE; } // Calculate how many VDI blocks we need BlockEntries=ImageSize/VDI_IMAGE_BLOCK_SIZE; if((ImageSize%VDI_IMAGE_BLOCK_SIZE)!=0) BlockEntries++; VdiBlockMapSize=BlockEntries*sizeof(uint32_t); LOG_DEBUG("BlockMap: %d (%08X) entries, %d (%08X) bytes!\n", BlockEntries, BlockEntries, VdiBlockMapSize, VdiBlockMapSize) // Allocate memory for vdi header and block map VdiFileHeaderSize=sizeof(TVdiFileHeader)+VdiBlockMapSize; XMOUNT_MALLOC(pVdiFileHeader,pTVdiFileHeader,VdiFileHeaderSize) memset(pVdiFileHeader,0,VdiFileHeaderSize); pVdiBlockMap=((void*)pVdiFileHeader)+sizeof(TVdiFileHeader); // Init header values strncpy(pVdiFileHeader->szFileInfo,VDI_FILE_COMMENT, strlen(VDI_FILE_COMMENT)+1); pVdiFileHeader->u32Signature=VDI_IMAGE_SIGNATURE; pVdiFileHeader->u32Version=VDI_IMAGE_VERSION; pVdiFileHeader->cbHeader=0x00000180; // No idea what this is for! Testimage had same value pVdiFileHeader->u32Type=VDI_IMAGE_TYPE_FIXED; pVdiFileHeader->fFlags=VDI_IMAGE_FLAGS; strncpy(pVdiFileHeader->szComment,VDI_HEADER_COMMENT, strlen(VDI_HEADER_COMMENT)+1); pVdiFileHeader->offData=VdiFileHeaderSize; pVdiFileHeader->offBlocks=sizeof(TVdiFileHeader); pVdiFileHeader->cCylinders=0; // Legacy info pVdiFileHeader->cHeads=0; // Legacy info pVdiFileHeader->cSectors=0; // Legacy info pVdiFileHeader->cbSector=512; // Legacy info pVdiFileHeader->u32Dummy=0; pVdiFileHeader->cbDisk=ImageSize; // Seems as VBox is always using a 1MB blocksize pVdiFileHeader->cbBlock=VDI_IMAGE_BLOCK_SIZE; pVdiFileHeader->cbBlockExtra=0; pVdiFileHeader->cBlocks=BlockEntries; pVdiFileHeader->cBlocksAllocated=BlockEntries; // Use partial MD5 input file hash as creation UUID and generate a random // modification UUID. VBox won't accept immages where create and modify UUIDS // aren't set. pVdiFileHeader->uuidCreate_l=XMountConfData.InputHashLo; pVdiFileHeader->uuidCreate_h=XMountConfData.InputHashHi; //*((uint32_t*)(&(pVdiFileHeader->uuidCreate_l)))=rand(); //*((uint32_t*)(&(pVdiFileHeader->uuidCreate_l))+4)=rand(); //*((uint32_t*)(&(pVdiFileHeader->uuidCreate_h)))=rand(); //*((uint32_t*)(&(pVdiFileHeader->uuidCreate_h))+4)=rand(); #define rand64(var) { \ *((uint32_t*)&(var))=rand(); \ *(((uint32_t*)&(var))+1)=rand(); \ } rand64(pVdiFileHeader->uuidModify_l); rand64(pVdiFileHeader->uuidModify_h); #undef rand64 // Generate block map i=0; for(offset=0;offset0) { // Cache file isn't empty, parse block header LOG_DEBUG("Cache file not empty. Parsing block header\n") if(fseeko(hCacheFile,0,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to beginning of cache file!\n") return FALSE; } // Read and check file signature if(fread(&buf,8,1,hCacheFile)!=1 || buf!=CACHE_FILE_SIGNATURE) { free(pCacheFileHeader); LOG_ERROR("Not an xmount cache file or cache file corrupt!\n") return FALSE; } // Now get cache file version (Has only 32bit!) if(fread(&buf,4,1,hCacheFile)!=1) { free(pCacheFileHeader); LOG_ERROR("Not an xmount cache file or cache file corrupt!\n") return FALSE; } switch((uint32_t)buf) { case 0x00000001: // Old v1 cache file. LOG_ERROR("Unsupported cache file version!\n") LOG_ERROR("Please use xmount-tool to upgrade your cache file.\n") return FALSE; case CUR_CACHE_FILE_VERSION: // Current version if(fseeko(hCacheFile,0,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to beginning of cache file!\n") return FALSE; } // Alloc memory for header and block index XMOUNT_MALLOC(pCacheFileHeader,pTCacheFileHeader,CacheFileHeaderSize) memset(pCacheFileHeader,0,CacheFileHeaderSize); // Read header and block index from file if(fread(pCacheFileHeader,CacheFileHeaderSize,1,hCacheFile)!=1) { // Cache file isn't big enough free(pCacheFileHeader); LOG_ERROR("Cache file corrupt!\n") return FALSE; } break; default: LOG_ERROR("Unknown cache file version!\n") return FALSE; } // Check if cache file has same block size as we do if(pCacheFileHeader->BlockSize!=CACHE_BLOCK_SIZE) { LOG_ERROR("Cache file does not use default cache block size!\n") return FALSE; } // Set pointer to block index pCacheFileBlockIndex=(pTCacheFileBlockIndex)((void*)pCacheFileHeader+ pCacheFileHeader->pBlockIndex); } else { // New cache file, generate a new block header LOG_DEBUG("Cache file is empty. Generating new block header\n"); // Alloc memory for header and block index XMOUNT_MALLOC(pCacheFileHeader,pTCacheFileHeader,CacheFileHeaderSize) memset(pCacheFileHeader,0,CacheFileHeaderSize); pCacheFileHeader->FileSignature=CACHE_FILE_SIGNATURE; pCacheFileHeader->CacheFileVersion=CUR_CACHE_FILE_VERSION; pCacheFileHeader->BlockSize=CACHE_BLOCK_SIZE; pCacheFileHeader->BlockCount=NeededBlocks; //pCacheFileHeader->UsedBlocks=0; // The following pointer is only usuable when reading data from cache file pCacheFileHeader->pBlockIndex=sizeof(TCacheFileHeader); pCacheFileBlockIndex=(pTCacheFileBlockIndex)((void*)pCacheFileHeader+ sizeof(TCacheFileHeader)); pCacheFileHeader->VdiFileHeaderCached=FALSE; pCacheFileHeader->pVdiFileHeader=0; pCacheFileHeader->VmdkFileCached=FALSE; pCacheFileHeader->VmdkFileSize=0; pCacheFileHeader->pVmdkFile=0; // Write header to file if(fwrite(pCacheFileHeader,CacheFileHeaderSize,1,hCacheFile)!=1) { free(pCacheFileHeader); LOG_ERROR("Couldn't write cache file header to file!\n"); return FALSE; } } return TRUE; } /* * Struct containing implemented FUSE functions */ static struct fuse_operations xmount_operations = { // .access=GetVirtFileAccess, .getattr=GetVirtFileAttr, .mkdir=CreateVirtDir, .mknod=CreateVirtFile, .open=OpenVirtFile, .readdir=GetVirtFiles, .read=ReadVirtFile, .rename=RenameVirtFile, .rmdir=DeleteVirtDir, // .statfs=GetVirtFsStats, .unlink=DeleteVirtFile, .write=WriteVirtFile // .release=mountewf_release, }; /* * Main */ int main(int argc, char *argv[]) { char **ppInputFilenames=NULL; int InputFilenameCount=0; int nargc=0; char **ppNargv=NULL; char *pMountpoint=NULL; int ret=1; int i=0; setbuf(stdout,NULL); setbuf(stderr,NULL); // Init XMountConfData XMountConfData.OrigImageType=TOrigImageType_DD; XMountConfData.VirtImageType=TVirtImageType_DD; XMountConfData.Debug=FALSE; XMountConfData.pVirtualImagePath=NULL; XMountConfData.pVirtualVmdkPath=NULL; XMountConfData.pVirtualImageInfoPath=NULL; XMountConfData.Writable=FALSE; XMountConfData.OverwriteCache=FALSE; XMountConfData.pCacheFile=NULL; XMountConfData.OrigImageSize=0; XMountConfData.VirtImageSize=0; XMountConfData.InputHashLo=0; XMountConfData.InputHashHi=0; // Parse command line options if(!ParseCmdLine(argc, argv, &nargc, &ppNargv, &InputFilenameCount, &ppInputFilenames, &pMountpoint)) { LOG_ERROR("Error parsing command line options!\n") //PrintUsage(argv[0]); return 1; } // Check command line options if(nargc<2 /*|| InputFilenameCount==0 || pMountpoint==NULL*/) { LOG_ERROR("Couldn't parse command line options!\n") PrintUsage(argv[0]); return 1; } if(XMountConfData.Debug==TRUE) { LOG_DEBUG("Options passed to FUSE: ") for(i=0;i32bit 20090308: * Added SetVdiFileHeaderData function to handle virtual image type specific data to be cached. This makes cache files independent from virtual image type 20090316: v0.2.0 released 20090327: v0.2.1 released * Fixed a bug in virtual write support. Checking whether data is cached didn't use semaphores. This could corrupt cache files when running multi-threaded. * Added IsVdiFileHeaderCached function to check whether VDI file header was already cached * Added IsBlockCached function to check whether a block was already cached 20090331: v0.2.2 released (Internal release) * Further changes to semaphores to fix write support bug. 20090410: v0.2.3 released * Reverted most of the fixes from v0.2.1 and v0.2.2 as those did not solve the write support bug. * Removed all semaphores * Added two pthread mutexes to protect virtual image and virtual info file. 20090508: * Configure script will now exit when needed libraries aren't found * Added support for newest libewf beta version 20090506 as it seems to reduce memory usage when working with EWF files by about 1/2. * Added LIBEWF_BETA define to adept source to new libewf API. * Added function InitVirtualVmdkFile to build a VmWare virtual disk descriptor file. 20090519: * Added function CreateVirtDir implementing FUSE's mkdir to allow VMWare to create his .vmdk.lck lock folder. Function does not allow to create other folders! * Changed cache file handling as VMDK caching will need new cache file structure incompatible to the old one. 20090522: v0.3.0 released * Added function DeleteVirtFile and DeleteVirtDir so VMWare can remove his lock directories and files. * Added function RenameVirtFile because VMWare needs to rename his lock files. * VMDK support should work now but descriptor file won't get cached as I didn't implement it yet. 20090604: * Added --cache commandline parameter doing the same as --rw. * Added --owcache commandline parameter doing the same as --rw but overwrites any existing cache data. This can be handy for debugging and testing purposes. * Added "vmdks" output type. Same as "vmdk" but generates a disk connected to the SCSI bus rather than the IDE bus. 20090710: v0.3.1 released 20090721: * Added function CheckFuseAllowOther to check wether FUSE supports the "-o allow_other" option. It is supported when "user_allow_other" is set in /etc/fuse.conf or when running xmount as root. * Automatic addition of FUSE's "-o allow_other" option if it is supported. * Added special "-o no_allow_other" command line parameter to disable automatic addition of the above option. * Reorganisation of FUSE's and xmount's command line options processing. * Added LogWarnMessage function to output a warning message. 20090722: * Added function CalculateInputImageHash to calculate an MD5 hash of the first input image's HASH_AMOUNT bytes of data. This hash is used as VDI creation UUID and will later be used to match cache files to input images. 20090724: v0.3.2 released 20090725: v0.4.0 released * Added AFF input image support. * Due to various problems with libewf and libaff packages (Mainly in Debian and Ubuntu), I decided to include them into xmount's source tree and link them in statically. This has the advantage that I can use whatever version I want. 20090727: v0.4.1 released * Added again the ability to compile xmount with shared libs as the Debian folks don't like the static ones :) 20090812: * Added TXMountConfData.OrigImageSize and TXMountConfData.VirtImageSize to save the size of the input and output image in order to avoid regetting it always from disk. 20090814: * Replaced all malloc and realloc occurences with the two macros XMOUNT_MALLOC and XMOUNT_REALLOC. 20090816: * Replaced where applicable all occurences of str(n)cpy or alike with their corresponding macros XMOUNT_STRSET, XMOUNT_STRCPY and XMOUNT_STRNCPY pendants. 20090907: v0.4.2 released * Fixed a bug in VMDK lock file access. VirtualVmdkLockFileDataSize wasn't reset to 0 when the file was deleted. * Fixed a bug in VMDK descriptor file access. Had to add VirtualVmdkFileSize to track the size of this file as strlen was a bad idea :). 20100324: v0.4.3 released * Changed all header structs to prevent different sizes on i386 and amd64. See xmount.h for more details. 20100810: v0.4.4 released * Found a bug in InitVirtVdiHeader(). The 64bit values were addressed incorrectly while filled with rand(). This leads to an error message when trying to add a VDI file to VirtualBox 3.2.8. */ diff --git a/trunk/xmount.h b/trunk/xmount.h index 23ea8c7..0341425 100755 --- a/trunk/xmount.h +++ b/trunk/xmount.h @@ -1,322 +1,322 @@ /******************************************************************************* -* xmount Copyright (c) 2008,2009 by Gillen Daniel * +* xmount Copyright (c) 2008-2010 by Gillen Daniel * * * * xmount is a small tool to "fuse mount" various image formats as dd or vdi * * files 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 . * *******************************************************************************/ #define FUSE_USE_VERSION 26 #include #include #include #include #undef FALSE #undef TRUE #define FALSE 0 #define TRUE 1 /* * Virtual image types */ typedef enum TVirtImageType { /** Virtual image is a DD file */ TVirtImageType_DD, /** Virtual image is a VDI file */ TVirtImageType_VDI, /** Virtual image is a VMDK file (IDE bus)*/ TVirtImageType_VMDK, /** Virtual image is a VMDK file (SCSI bus)*/ TVirtImageType_VMDKS } TVirtImageType; /* * Input image types */ typedef enum TOrigImageType { /** Input image is a DD file */ TOrigImageType_DD, /** Input image is an EWF file */ TOrigImageType_EWF, /** Input image is an AFF file */ TOrigImageType_AFF } TOrigImageType; /* * Various mountimg runtime options */ typedef struct TXMountConfData { /** Input image type */ TOrigImageType OrigImageType; /** Virtual image type */ TVirtImageType VirtImageType; /** Enable debug output */ uint32_t Debug; /** Path of virtual image file */ char *pVirtualImagePath; /** Path of virtual VMDK file */ char *pVirtualVmdkPath; /** Path of virtual image info file */ char *pVirtualImageInfoPath; /** Enable virtual write support */ uint32_t Writable; /** Overwrite existing cache */ uint32_t OverwriteCache; /** Cache file to save changes to */ char *pCacheFile; /** Size of input image */ uint64_t OrigImageSize; /** Size of virtual image */ uint64_t VirtImageSize; /** Partial MD5 hash of input image */ uint64_t InputHashLo; uint64_t InputHashHi; } __attribute__ ((packed)) TXMountConfData; /* * VDI Binary File Header structure */ #define VDI_IMAGE_SIGNATURE 0xBEDA107F // 1:1 copy from hp #define VDI_IMAGE_VERSION 0x00010001 // Vers 1.1 #define VDI_IMAGE_TYPE_FIXED 0x00000002 // Type 2 (fixed size) #define VDI_IMAGE_FLAGS 0 #define VDI_IMAGE_BLOCK_SIZE (1024*1024) // 1 Megabyte typedef struct TVdiFileHeader { // ----- VDIPREHEADER ------ /** Just text info about image type, for eyes only. */ char szFileInfo[64]; /** The image signature (VDI_IMAGE_SIGNATURE). */ uint32_t u32Signature; /** The image version (VDI_IMAGE_VERSION). */ uint32_t u32Version; // ----- VDIHEADER1PLUS ----- /** Size of header structure in bytes. */ uint32_t cbHeader; /** The image type (VDI_IMAGE_TYPE_*). */ uint32_t u32Type; /** Image flags (VDI_IMAGE_FLAGS_*). */ uint32_t fFlags; /** Image comment. (UTF-8) */ char szComment[256]; /** Offset of Blocks array from the begining of image file. * Should be sector-aligned for HDD access optimization. */ uint32_t offBlocks; /** Offset of image data from the begining of image file. * Should be sector-aligned for HDD access optimization. */ uint32_t offData; /** Legacy image geometry (previous code stored PCHS there). */ /** Cylinders. */ uint32_t cCylinders; /** Heads. */ uint32_t cHeads; /** Sectors per track. */ uint32_t cSectors; /** Sector size. (bytes per sector) */ uint32_t cbSector; /** Was BIOS HDD translation mode, now unused. */ uint32_t u32Dummy; /** Size of disk (in bytes). */ uint64_t cbDisk; /** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) Should be a power of 2! */ uint32_t cbBlock; /** Size of additional service information of every data block. * Prepended before block data. May be 0. * Should be a power of 2 and sector-aligned for optimization reasons. */ uint32_t cbBlockExtra; /** Number of blocks. */ uint32_t cBlocks; /** Number of allocated blocks. */ uint32_t cBlocksAllocated; /** UUID of image. */ uint64_t uuidCreate_l; uint64_t uuidCreate_h; /** UUID of image's last modification. */ uint64_t uuidModify_l; uint64_t uuidModify_h; /** Only for secondary images - UUID of previous image. */ uint64_t uuidLinkage_l; uint64_t uuidLinkage_h; /** Only for secondary images - UUID of previous image's last modification. */ uint64_t uuidParentModify_l; uint64_t uuidParentModify_h; /** Padding to get 512 byte alignment */ uint64_t padding0; uint64_t padding1; uint64_t padding2; uint64_t padding3; uint64_t padding4; uint64_t padding5; uint64_t padding6; } __attribute__ ((packed)) TVdiFileHeader, *pTVdiFileHeader; // /** The way the UUID is declared by the DCE specification. */ // struct // { // uint32_t u32TimeLow; // uint16_t u16TimeMid; // uint16_t u16TimeHiAndVersion; // uint8_t u8ClockSeqHiAndReserved; // uint8_t u8ClockSeqLow; // uint8_t au8Node[6]; // } Gen; /* * Cache file block index array element */ #ifdef __LP64__ #define CACHE_BLOCK_FREE 0xFFFFFFFFFFFFFFFF #else #define CACHE_BLOCK_FREE 0xFFFFFFFFFFFFFFFFLL #endif typedef struct TCacheFileBlockIndex { /** Set to 1 if block is assigned (This block has data in cache file) */ uint32_t Assigned; /** Offset to data in cache file */ uint64_t off_data; } __attribute__ ((packed)) TCacheFileBlockIndex, *pTCacheFileBlockIndex; /* * Cache file header structures */ #define CACHE_BLOCK_SIZE (1024*1024) // 1 megabyte #ifdef __LP64__ #define CACHE_FILE_SIGNATURE 0xFFFF746E756F6D78 // "xmount\xFF\xFF" #else #define CACHE_FILE_SIGNATURE 0xFFFF746E756F6D78LL #endif #define CUR_CACHE_FILE_VERSION 0x00000002 // Current cache file version #define HASH_AMOUNT (1024*1024)*10 // Amount of data used to construct a // "unique" hash for every input image // (10MByte) // Current header typedef struct TCacheFileHeader { /** Simple signature to identify cache files */ uint64_t FileSignature; /** Cache file version */ uint32_t CacheFileVersion; /** Cache block size */ uint64_t BlockSize; /** Total amount of cache blocks */ uint64_t BlockCount; /** Offset to the first block index array element */ uint64_t pBlockIndex; /** Set to 1 if VDI file header is cached */ uint32_t VdiFileHeaderCached; /** Offset to cached VDI file header */ uint64_t pVdiFileHeader; /** Set to 1 if VMDK file is cached */ uint32_t VmdkFileCached; /** Size of VMDK file */ uint64_t VmdkFileSize; /** Offset to cached VMDK file */ uint64_t pVmdkFile; /** Padding until offset 512 to ease further additions */ char HeaderPadding[444]; } __attribute__ ((packed)) TCacheFileHeader, *pTCacheFileHeader; // Old v1 header typedef struct TCacheFileHeader_v1 { /** Simple signature to identify cache files */ uint64_t FileSignature; /** Cache file version */ uint32_t CacheFileVersion; /** Total amount of cache blocks */ uint64_t BlockCount; /** Offset to the first block index array element */ uint64_t pBlockIndex; /** Set to 1 if VDI file header is cached */ uint32_t VdiFileHeaderCached; /** Offset to cached VDI file header */ uint64_t pVdiFileHeader; /** Set to 1 if VMDK file is cached */ } TCacheFileHeader_v1, *pTCacheFileHeader_v1; /* * Macros to ease debugging and error reporting */ #define LOG_ERROR(...) \ LogMessage("ERROR",(char*)__FUNCTION__,__LINE__,__VA_ARGS__); #define LOG_DEBUG(...) { \ if(XMountConfData.Debug) \ LogMessage("DEBUG",(char*)__FUNCTION__,__LINE__,__VA_ARGS__); \ } /* * Macros to alloc or realloc memory and check whether it worked */ #define XMOUNT_MALLOC(var,var_type,size) { \ (var)=(var_type)malloc(size); \ if((var)==NULL) { \ LOG_ERROR("Couldn't allocate memmory!\n"); \ exit(1); \ } \ } #define XMOUNT_REALLOC(var,var_type,size) { \ (var)=(var_type)realloc((var),size); \ if((var)==NULL) { \ LOG_ERROR("Couldn't allocate memmory!\n"); \ exit(1); \ } \ } /* * Macros for some often used string functions */ #define XMOUNT_STRSET(var1,var2) { \ XMOUNT_MALLOC(var1,char*,strlen(var2)+1) \ strcpy(var1,var2); \ } #define XMOUNT_STRNSET(var1,var2,size) { \ XMOUNT_MALLOC(var1,char*,(size)+1) \ strncpy(var1,var2,size); \ (var1)[size]='\0'; \ } #define XMOUNT_STRAPP(var1,var2) { \ XMOUNT_REALLOC(var1,char*,strlen(var1)+strlen(var2)+1) \ strcpy((var1)+strlen(var1),var2); \ } #define XMOUNT_STRNAPP(var1,var2,size) { \ XMOUNT_REALLOC(var1,char*,strlen(var1)+(size)+1) \ (var1)[strlen(var1)+(size)]='\0'; \ strncpy((var1)+strlen(var1),var2,size); \ } /* ----- Change history ----- 20090226: * Added change history information to this file. * Added TVirtImageType enum to identify virtual image type. * Added TOrigImageType enum to identify input image type. * Added TMountimgConfData struct to hold various mountimg runtime options. * Renamed VDIFILEHEADER to TVdiFileHeader. 20090228: * Added LOG_ERROR and LOG_DEBUG macros * Added defines for various static VDI header values * Added defines for TRUE and FALSE 20090307: * Added defines for various static cache file header values * Added VdiFileHeaderCached and pVdiFileHeader values to be able to cache the VDI file header separatly. 20090519: * Added new cache file header structure and moved old one to TCacheFileHeader_v1. * New cache file structure includes VmdkFileCached and pVmdkFile to cache virtual VMDK file and makes room for further additions so current cache file version 2 cache files can be easily converted to newer ones. 20090814: * Added XMOUNT_MALLOC and XMOUNT_REALLOC macros. 20090816: * Added XMOUNT_STRSET, XMOUNT_STRNSET, XMOUNT_STRAPP and XMOUNT_STRNAPP macros. 20100324: * Added "__attribute__ ((packed))" to all header structs to prevent different sizes on i386 and amd64. */ diff --git a/trunk/xmount.project b/trunk/xmount.project index a0bbb38..6d947a5 100644 --- a/trunk/xmount.project +++ b/trunk/xmount.project @@ -1,109 +1,111 @@ + + ./configure make make clean make None . None diff --git a/trunk/xmount.tags b/trunk/xmount.tags index 503c4f8..4871e66 100644 Binary files a/trunk/xmount.tags and b/trunk/xmount.tags differ diff --git a/trunk/xmount_cache.c b/trunk/xmount_cache.c index f27f6a4..2cc29db 100755 --- a/trunk/xmount_cache.c +++ b/trunk/xmount_cache.c @@ -1,486 +1,486 @@ /******************************************************************************* * xmount Copyright (c) 2008,2009 by Gillen Daniel * * * * xmount is a small tool to "fuse mount" various image formats as dd or vdi * * files and enable virtual write access. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #include "xmount_cache.h" #include "xmount_log.h" #include "xmount_macros.h" #include "xmount_options.h" #include #include /******************************** xmcache_open ********************************/ int xmcache_open(pTXMCacheFile pCacheFile, char *pFileName) { uint64_t CacheFileSize=0; uint64_t NeededBlocks=0; uint64_t ImageMapSize=0; uint32_t tmp_vers; char tmp_sig[8]; if(XMOptions.OverwriteCache!=1) { // Try to open an existing cache file or create a new one LOG_DEBUG("Trying to open existing cache file \"%s\"\n",pFileName) pCacheFile->hCacheFile=(FILE*)fopen64(pFileName,"rb+"); if(pCacheFile->hCacheFile==NULL) { // As the c lib seems to have no possibility to open a file rw wether it // exists or not (w+ does not work because it truncates an existing file), // when r+ returns NULL the file could simply not exist LOG_DEBUG("Cache file does not exist. Trying to create a new one\n") pCacheFile->hCacheFile=(FILE*)fopen64(pFileName,"wb+"); if(pCacheFile->hCacheFile==NULL) { // There is really a problem opening the file LOG_ERROR("Couldn't open cache file \"%s\"!\n",pFileName) return 0; } LOG_DEBUG("Cache file created sucsessfully\n") } else { LOG_DEBUG("Cache file opened sucsessfully\n") } } else { // Overwrite existing cache file or create a new one LOG_DEBUG("Trying to create or overwrite the cache file \"%s\"\n", pFileName) pCacheFile->hCacheFile=(FILE*)fopen64(pFileName,"wb+"); if(pCacheFile->hCacheFile==NULL) { LOG_ERROR("Couldn't open cache file \"%s\"!\n",pFileName) return 0; } LOG_DEBUG("Cache file created sucsessfully\n") } // Get cache file size if(fseeko(pCacheFile->hCacheFile,0,SEEK_END)!=0) { LOG_ERROR("Couldn't seek to end of cache file!\n") return 0; } CacheFileSize=ftello(pCacheFile->hCacheFile); LOG_DEBUG("Cache file size: %" PRIu64 " bytes\n",CacheFileSize) if(CacheFileSize==0) { // Empty cache file LOG_DEBUG("Generating new header segments\n") // Calculate how many blocks are needed for caching the entire image file // and how big the ImageMap must be // TODO: Allow custom block size specified on cmd line here NeededBlocks=XMOptions.OrigImageSize/XM_CACHEFILE_BLOCKSIZE; if((XMOptions.OrigImageSize%XM_CACHEFILE_BLOCKSIZE)!=0) NeededBlocks++; ImageMapSize=NeededBlocks*sizeof(TXMImageMapEntry); // Alloc memory for cache all file segments uint64_t TotalToAlloc=sizeof(TXMCacheFileHeader)+ ImageMapSize+ sizeof(TXMFileIndex)+ sizeof(TXMFileMap); LOG_DEBUG("Size of all cache file header segments: %" PRIu64 " bytes\n", TotalToAlloc) LOG_DEBUG("Cache file header size: %u bytes\n",sizeof(TXMCacheFileHeader)) LOG_DEBUG("Image map size: %" PRIu64 " bytes (%" PRIu64 " addressable " \ "blocks holding %u bytes each)\n",ImageMapSize,NeededBlocks, XM_CACHEFILE_BLOCKSIZE) LOG_DEBUG("File index size: %u bytes\n",sizeof(TXMFileIndex)) LOG_DEBUG("File map size: %u bytes\n",sizeof(TXMFileMap)) XMOUNT_MALLOC(pCacheFile->pCacheFileHeader, pTXMCacheFileHeader, TotalToAlloc) // Set segment pointers and init with default values TotalToAlloc-=sizeof(TXMFileMap); pCacheFile->pFileMap= (pTXMFileMap)(((void*)(pCacheFile->pCacheFileHeader))+TotalToAlloc); memset(pCacheFile->pFileMap,0xffff,sizeof(TXMFileMap)); TotalToAlloc-=sizeof(TXMFileIndex); pCacheFile->pFileIndex= (pTXMFileIndex)(((void*)(pCacheFile->pCacheFileHeader))+TotalToAlloc); memset(pCacheFile->pFileIndex,0x0000,sizeof(TXMFileIndex)); TotalToAlloc-=ImageMapSize; pCacheFile->pImageMap= (pTXMImageMapEntry)(((void*)(pCacheFile->pCacheFileHeader))+TotalToAlloc); memset(pCacheFile->pImageMap,0xffff,ImageMapSize); memset(pCacheFile->pCacheFileHeader,0x0000,sizeof(TXMCacheFileHeader)); // Set header values strcpy(pCacheFile->pCacheFileHeader->signature,XM_CACHEFILE_SIGNATURE); pCacheFile->pCacheFileHeader->version=XM_CACHEFILE_CURVERSION; pCacheFile->pCacheFileHeader->blocksize=XM_CACHEFILE_BLOCKSIZE; pCacheFile->pCacheFileHeader->imagesize=XMOptions.OrigImageSize; pCacheFile->pCacheFileHeader->hashsize=XMOptions.OrigImageHashSize; strncpy(pCacheFile->pCacheFileHeader->imagehash, XMOptions.pOrigImageHash, 16); pCacheFile->pCacheFileHeader->off_imagemap=sizeof(TXMCacheFileHeader); LOG_DEBUG("Offset to image map: %u bytes\n",sizeof(TXMCacheFileHeader)) pCacheFile->pCacheFileHeader->off_fileindex= sizeof(TXMCacheFileHeader)+ImageMapSize; LOG_DEBUG("Offset to file index: %" PRIu64 " bytes\n", sizeof(TXMCacheFileHeader)+ImageMapSize) pCacheFile->pCacheFileHeader->off_filemap= sizeof(TXMCacheFileHeader)+ImageMapSize+sizeof(TXMFileIndex); LOG_DEBUG("Offset to file map: %" PRIu64 " bytes\n", sizeof(TXMCacheFileHeader)+ImageMapSize+sizeof(TXMFileIndex)) // Write all header segments to cache file if(fwrite(pCacheFile->pCacheFileHeader, sizeof(TXMCacheFileHeader)+ ImageMapSize+ sizeof(TXMFileIndex)+ sizeof(TXMFileMap), 1, pCacheFile->hCacheFile)!=1) { free(pCacheFile->hCacheFile); LOG_ERROR("Couldn't write cache file header segments to file!\n"); return 0; } // Now flush all buffers to make sure data is written to file fflush(pCacheFile->hCacheFile); ioctl(fileno(pCacheFile->hCacheFile),BLKFLSBUF,0); LOG_DEBUG("Successfully generated and written cache file header " \ "segments\n"); } else { // Trying to load existing cache file LOG_DEBUG("Trying to load existing cache file\n") if(fseeko(pCacheFile->hCacheFile,0,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to beginning of cache file!\n") return 0; } // Read and check file signature if(fread(&tmp_sig,8,1,pCacheFile->hCacheFile)!=1) { fclose(pCacheFile->hCacheFile); LOG_ERROR("Not an xmount cache file or cache file corrupt!\n") return 0; } else if(strcmp(tmp_sig,XM_CACHEFILE_SIGNATURE)!=0) { fclose(pCacheFile->hCacheFile); LOG_ERROR("Not an xmount cache file or cache file corrupt!\n") return 0; } // Read and check file version if(fread(&tmp_vers,4,1,pCacheFile->hCacheFile)!=1) { fclose(pCacheFile->hCacheFile); LOG_ERROR("Not an xmount cache file or cache file corrupt!\n") return 0; } else if(tmp_vershCacheFile); LOG_ERROR("Cache file version %u isn't supported any more! " \ "Please use xmount-tool to convert it to the actual version.\n", tmp_vers) return 0; } // It should now be reasonably secure to load the cache file header if(fseeko(pCacheFile->hCacheFile,0,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to beginning of cache file!\n") return 0; } XMOUNT_MALLOC(pCacheFile->pCacheFileHeader, pTXMCacheFileHeader, sizeof(TXMCacheFileHeader)) if(fread(pCacheFile->pCacheFileHeader, sizeof(TXMCacheFileHeader), 1, pCacheFile->hCacheFile)!=1) { fclose(pCacheFile->hCacheFile); free(pCacheFile->hCacheFile); LOG_ERROR("Couldn't read cache file header!\n") return 0; } // Compare image size with that saved in cache file if(pCacheFile->pCacheFileHeader->imagesize!=XMOptions.OrigImageSize) { fclose(pCacheFile->hCacheFile); LOG_ERROR("Input image size (%" PRIu64 " bytes) does not match the size " "saved in the cache file (%" PRIu64 " bytes)!\n", XMOptions.OrigImageSize,pCacheFile->pCacheFileHeader->imagesize) return 0; } // Calculate how many blocks are needed for caching the entire image file // and how big the ImageMap must be NeededBlocks= XMOptions.OrigImageSize/pCacheFile->pCacheFileHeader->blocksize; if((XMOptions.OrigImageSize%pCacheFile->pCacheFileHeader->blocksize)!=0) NeededBlocks++; ImageMapSize=NeededBlocks*sizeof(TXMImageMapEntry); // Alloc memory to cache all header segments uint64_t TotalToAlloc=sizeof(TXMCacheFileHeader)+ ImageMapSize+ sizeof(TXMFileIndex)+ sizeof(TXMFileMap); LOG_DEBUG("Size of all cache file header segments: %" PRIu64 " bytes\n", TotalToAlloc) LOG_DEBUG("Cache file header size: %u bytes\n",sizeof(TXMCacheFileHeader)) LOG_DEBUG("Image map size: %" PRIu64 " bytes (%" PRIu64 " addressable " \ "blocks holding %u bytes each)\n",ImageMapSize,NeededBlocks, XM_CACHEFILE_BLOCKSIZE) LOG_DEBUG("File index size: %u bytes\n",sizeof(TXMFileIndex)) LOG_DEBUG("File map size: %u bytes\n",sizeof(TXMFileMap)) XMOUNT_REALLOC(pCacheFile->pCacheFileHeader, pTXMCacheFileHeader, TotalToAlloc) // Set segment pointers and init with data from file TotalToAlloc-=sizeof(TXMFileMap); pCacheFile->pFileMap= (pTXMFileMap)(((void*)(pCacheFile->pCacheFileHeader))+TotalToAlloc); TotalToAlloc-=sizeof(TXMFileIndex); pCacheFile->pFileIndex= (pTXMFileIndex)(((void*)(pCacheFile->pCacheFileHeader))+TotalToAlloc); TotalToAlloc-=ImageMapSize; pCacheFile->pImageMap= (pTXMImageMapEntry)(((void*)(pCacheFile->pCacheFileHeader))+TotalToAlloc); // Read data from cache file into memory if(fread(pCacheFile->pImageMap, ImageMapSize, 1, pCacheFile->hCacheFile)!=1) { fclose(pCacheFile->hCacheFile); LOG_ERROR("Couldn't read image map from cache file!\n") return 0; } if(fread(pCacheFile->pFileIndex, sizeof(TXMFileIndex), 1, pCacheFile->hCacheFile)!=1) { fclose(pCacheFile->hCacheFile); LOG_ERROR("Couldn't read file index from cache file!\n") return 0; } if(fread(pCacheFile->pFileMap, sizeof(TXMFileMap), 1, pCacheFile->hCacheFile)!=1) { fclose(pCacheFile->hCacheFile); LOG_ERROR("Couldn't read file map from cache file!\n") return 0; } // TODO: Read all file maps into memory LOG_DEBUG("Successfully loaded cache file\n") } // Init cache mutex pthread_mutex_init(&(pCacheFile->mutex_rw),NULL); return 1; } /******************************* xmcache_close ********************************/ void xmcache_close(pTXMCacheFile pCacheFile) { // Flush all buffers to make sure data is written to file fflush(pCacheFile->hCacheFile); ioctl(fileno(pCacheFile->hCacheFile),BLKFLSBUF,0); // Close file handle fclose(pCacheFile->hCacheFile); // Destroy cache mutex pthread_mutex_destroy(&(pCacheFile->mutex_rw)); // Free buffers free(pCacheFile->pCacheFileHeader); // TODO: Free all entrys !!! //free(pCacheFile->pImageMap); //free(pCacheFile->pFileIndex); //free(pCacheFile->pFileMap); } /*************************** xmcache_get_blocksize ****************************/ uint64_t xmcache_get_blocksize(pTXMCacheFile pCacheFile) { return pCacheFile->pCacheFileHeader->blocksize; } /************************** xmcache_is_block_cached ***************************/ int xmcache_is_block_cached(pTXMCacheFile pCacheFile, uint64_t block) { if(pCacheFile->pImageMap[block].off_data== XM_CACHEFILE_IMAGEENTRY_UNASSIGNED) { LOG_DEBUG("Block %" PRIu64 " isn't assigned yet\n",block) return 0; } else { LOG_DEBUG("Block %" PRIu64 " is already assigned\n",block) return 0; } } /***************************** xmcache_image_read *****************************/ int xmcache_image_read(pTXMCacheFile pCacheFile, char *buf, uint64_t block, uint64_t offset, uint64_t size) { // Wait for other threads to end reading/writing data pthread_mutex_lock(&(pCacheFile->mutex_rw)); LOG_DEBUG("Trying to read %" PRIu64 " bytes at block offset %" PRIu64 " from block %" PRIu64 "\n",size,offset,block) if(pCacheFile->pImageMap[block].off_data!= XM_CACHEFILE_IMAGEENTRY_UNASSIGNED) { // Seek to correct place in cache file if(fseeko(pCacheFile->hCacheFile, pCacheFile->pImageMap[block].off_data+offset, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to offset %" PRIu64 " of block %" PRIu64 "!\n", offset,block) pthread_mutex_unlock(&(pCacheFile->mutex_rw)); return -1; } // Adjusting size if trying to read beyond block if((size+offset)>pCacheFile->pCacheFileHeader->blocksize) size=pCacheFile->pCacheFileHeader->blocksize-offset; // Read data from cache if(fread(buf,size,1,pCacheFile->hCacheFile)!=1) { LOG_ERROR("Couldn't read %" PRIu64 " bytes at block offset %" PRIu64 " from block %" PRIu64 " at file offset %" PRIu64 "!\n", size,offset,block,pCacheFile->pImageMap[block].off_data) pthread_mutex_unlock(&(pCacheFile->mutex_rw)); return -1; } LOG_DEBUG("Read %" PRIu64 " bytes\n",size) pthread_mutex_unlock(&(pCacheFile->mutex_rw)); return size; } else { LOG_ERROR("Attempt to read from non assigned block %" PRIu64 "!\n",block) pthread_mutex_unlock(&(pCacheFile->mutex_rw)); return -1; } } /**************************** xmcache_image_write *****************************/ int xmcache_image_write(pTXMCacheFile pCacheFile, char *buf, uint64_t block, uint64_t offset, uint64_t size) { // Wait for other threads to end reading/writing data pthread_mutex_lock(&(pCacheFile->mutex_rw)); LOG_DEBUG("Trying to write %" PRIu64 " bytes at block offset %" PRIu64 " to block %" PRIu64 "\n",size,offset,block) if(pCacheFile->pImageMap[block].off_data!= XM_CACHEFILE_IMAGEENTRY_UNASSIGNED) { // Seek to correct place in cache file if(fseeko(pCacheFile->hCacheFile, pCacheFile->pImageMap[block].off_data+offset, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to offset %" PRIu64 " of block %" PRIu64 "!\n", offset,block) pthread_mutex_unlock(&(pCacheFile->mutex_rw)); return -1; } // Adjusting size if trying to write beyond block if((size+offset)>pCacheFile->pCacheFileHeader->blocksize) size=pCacheFile->pCacheFileHeader->blocksize-offset; if(fwrite(buf,size,1,pCacheFile->hCacheFile)!=1) { LOG_ERROR("Couldn't write %" PRIu64 " bytes at block offset %" PRIu64 " to block %" PRIu64 " at file offset %" PRIu64 "!\n", size,offset,block,pCacheFile->pImageMap[block].off_data) pthread_mutex_unlock(&(pCacheFile->mutex_rw)); return -1; } LOG_DEBUG("Wrote %" PRIu64 " bytes\n",size) pthread_mutex_unlock(&(pCacheFile->mutex_rw)); return size; } else { // Allocate new block. When allocating new blocks, the first write must // fill the whole block with data! if(size!=pCacheFile->pCacheFileHeader->blocksize || offset!=0) { LOG_ERROR("Attempt to write partial data to unallocated block!\n") pthread_mutex_unlock(&(pCacheFile->mutex_rw)); return -1; } // Seek to end of cache file if(fseeko(pCacheFile->hCacheFile, 0, SEEK_END)!=0) { LOG_ERROR("Couldn't seek to end of cache file!\n") pthread_mutex_unlock(&(pCacheFile->mutex_rw)); return -1; } // Save offset for new block to image map pCacheFile->pImageMap[block].off_data=ftello(pCacheFile->hCacheFile); // Write new block if(fwrite(buf,size,1,pCacheFile->hCacheFile)!=1) { LOG_ERROR("Couldn't write %" PRIu64 " bytes at block offset %" PRIu64 " to block %" PRIu64 " at file offset %" PRIu64 "!\n", size,offset,block,pCacheFile->pImageMap[block].off_data) pthread_mutex_unlock(&(pCacheFile->mutex_rw)); return -1; } LOG_DEBUG("Wrote %" PRIu64 " bytes to new block at file offset %" PRIu64 "\n",size,pCacheFile->pImageMap[block].off_data) // Now that data has been written, update image map entry on disk LOG_DEBUG("Trying to update image map entry %" PRIu64 " at file offset %" \ PRIu64 "\n",block,pCacheFile->pCacheFileHeader->off_imagemap+ (block*sizeof(TXMImageMapEntry))) if(fseeko(pCacheFile->hCacheFile, pCacheFile->pCacheFileHeader->off_imagemap+ (block*sizeof(TXMImageMapEntry)), SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to image map entry %" PRIu64 "!\n",block) pthread_mutex_unlock(&(pCacheFile->mutex_rw)); return -1; } if(fwrite(&(pCacheFile->pImageMap[block].off_data), sizeof(TXMImageMapEntry), 1, pCacheFile->hCacheFile)!=1) { LOG_ERROR("Couldn't update image map entry!\n") pthread_mutex_unlock(&(pCacheFile->mutex_rw)); return -1; } LOG_DEBUG("Image map entry updated successfully\n") pthread_mutex_unlock(&(pCacheFile->mutex_rw)); return size; } } /********************************* xmcache_ls *********************************/ char **xmcache_ls(pTXMCacheFile pCacheFile, int ListInternal) { char **ppRes=NULL; uint64_t i; // Iterate over all file index entries for(i=0;ipFileIndex.FileIndexEntrys[i].filepath!='\0' || - pCacheFile->pFileIndex.FileIndexEntrys[i].filename!='\0') + if(pCacheFile->pFileIndex->FileIndexEntrys[i].filepath!='\0' || + pCacheFile->pFileIndex->FileIndexEntrys[i].filename!='\0') { // This entry holds data } } return ppRes; } diff --git a/trunk/xmount_options.c b/trunk/xmount_options.c index aa609f8..06bce0c 100755 --- a/trunk/xmount_options.c +++ b/trunk/xmount_options.c @@ -1,24 +1,33 @@ /******************************************************************************* -* xmount Copyright (c) 2008,2009 by Gillen Daniel * +* xmount Copyright (c) 2008-2010 by Gillen Daniel * * * * xmount is a small tool to "fuse mount" various image formats as dd or vdi * * files and enable virtual write access. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #include "xmount_options.h" +inline void InitXmountOptions() { + memset(&xmount_options,0,sizeof(tXmountOptions)); +} - +int ParseCommandLine(int argc, + char **pp_argv, + int *p_fuse_argc, + char **pp_fuse_argv) +{ + +} diff --git a/trunk/xmount_options.h b/trunk/xmount_options.h index e3ee537..8a820bc 100755 --- a/trunk/xmount_options.h +++ b/trunk/xmount_options.h @@ -1,95 +1,122 @@ /******************************************************************************* -* xmount Copyright (c) 2008,2009 by Gillen Daniel * +* xmount Copyright (c) 2008-2010 by Gillen Daniel * * * * xmount is a small tool to "fuse mount" various image formats as dd or vdi * * files 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_OPTIONS_H #define XMOUNT_OPTIONS_H #include -/* - * Virtual image types - */ -typedef enum TXVirtImageType { - /** Virtual image is a DD file */ - TVirtImageType_DD, - /** Virtual image is a VDI file */ - TVirtImageType_VDI, - /** Virtual image is a VMDK file (IDE bus)*/ - TVirtImageType_VMDK, - /** Virtual image is a VMDK file (SCSI bus)*/ - TVirtImageType_VMDKS -} TXVirtImageType; +/*! + @brief Input image types + Specifies the input image type. +*/ +typedef enum eInputImageType { + //! Input image is a DD file + eInputImageType_DD, + //! Input image is an EWF file + eInputImageType_EWF, + //! Input image is an AFF file + eInputImageType_AFF +} tInputImageType; -/* - * Input image types - */ -typedef enum TXOrigImageType { - /** Input image is a DD file */ - TOrigImageType_DD, - /** Input image is an EWF file */ - TOrigImageType_EWF, - /** Input image is an AFF file */ - TOrigImageType_AFF -} TXOrigImageType; +/*! + @brief Output (virtual) image types + Specifies the output image type. +*/ +typedef enum eOutputImageType { + //! Virtual image is a DD file + eOutputImageType_DD, + //! Virtual image is a VDI file + eOutputImageType_VDI, + //! Virtual image is a VMDK file (IDE bus) + eOutputImageType_VMDK, + //! Virtual image is a VMDK file (SCSI bus) + eOutputImageType_VMDKS +} tOutputImageType; -/* - * Various xmount runtime options - */ -typedef struct TXMOptions { - /** Input image type */ - TXOrigImageType OrigImageType; - /** Size of input image */ - uint64_t OrigImageSize; - /** Amount of data to use for following hash */ - uint64_t OrigImageHashSize; - /** MD5 hash of partial input image (16 byte) */ - char *pOrigImageHash; +/*! + @brief xmount runtime options + Structure to save various xmount runtime options. They are accessed trough + the global variable xmount_options defined in this header too. +*/ +typedef struct sXmountOptions { + /* Input image related */ + //! Input image type + tInputImageType input_image_type; + //! Size of input image + uint64_t input_image_size; + //! Amount of data to use for following hash + uint64_t input_image_hash_amount; + //! MD5 hash of partial input image (16 byte + '\0') + char orig_image_hash[17]; - /** Virtual image type */ - TXVirtImageType VirtImageType; - /** Size of virtual image */ - uint64_t VirtImageSize; + /* Output image related (general) */ + //! Output image type + tOutputImageType output_image_type; + //! Size of virtual image + uint64_t output_image_size; + //! Path and name of the output image file + char *p_output_image_path; + //! Cache file to save changes to + char *p_cache_file; - /** Enable debug output */ - unsigned char Debug; - /** Enable virtual write support */ - unsigned char Writable; - /** Overwrite existing cache */ - unsigned char OverwriteCache; - - /** Cache file to save changes to */ - char *pCacheFile; - - - - - - /** Path of virtual image file */ - char *pVirtualImagePath; - /** Path of virtual VMDK file */ + /* Output image related (VDI specific) */ + + + /* Output image related (VMDK(S) specific) */ + //! Path of virtual VMDK file char *pVirtualVmdkPath; - /** Path of virtual image info file */ + //! Path of virtual image info file char *pVirtualImageInfoPath; -} TXMOptions; -TXMOptions XMOptions; + /* "Real" options */ + //! Enable debug output + uint8_t debug; + //! Enable virtual write support + uint8_t writable; + //! Overwrite existing cache + uint8_t overwrite_cache; +} tXmountOptions; -#endif // #ifndef XMOUNT_OPTIONS_H +/* Global xmount options var */ +tXmountOptions xmount_options; +/* Functions to deal with the above defined global xmount option variable */ +/*! + @brief Init xmount options structure + Simply set everything to zero. +*/ +inline void InitXmountOptions(); + +/*! + @brief Parse command line options + Parse any options given on command line. + @param[in] argc Number of command line options + @param[in] pp_argv Array holding all command line options + @param[out] p_fuse_argc Number of command line options to pass to fuse + @param[out] pp_fuse_argv Array holding all command line options to pass to fuse + @return 1 on success, 0 on error +*/ +int ParseCommandLine(int argc, + char **pp_argv, + int *p_fuse_argc, + char **pp_fuse_argv); + +#endif // #ifndef XMOUNT_OPTIONS_H