diff --git a/trunk/libxmount_input/libxmount_input.h b/trunk/libxmount_input/libxmount_input.h index ab7d04b..77bf350 100644 --- a/trunk/libxmount_input/libxmount_input.h +++ b/trunk/libxmount_input/libxmount_input.h @@ -1,147 +1,176 @@ /******************************************************************************* * xmount Copyright (c) 2008-2014 by Gillen Daniel * * * * xmount is a small tool to "fuse mount" various image formats and enable * * virtual write access. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #ifndef LIBXMOUNT_INPUT_H #define LIBXMOUNT_INPUT_H #define LIBXMOUNT_INPUT_API_VERSION 1 #include // For int*_t and uint*_t #include // For PRI* //! Structure containing pointers to the lib's functions typedef struct s_LibXmountInputFunctions { + //! Function to initialize handle /*! - * Function to initialize handle + * This function is called once to allow the lib to alloc any needed + * structures before other functions that rely upon a valid handle are called + * (for ex. OptionsParse or Open). * - * \param oo_handle Pointer to store handle + * \param pp_handle Pointer to store handle to * \return 0 on success or error code */ - int (*InitHandle)(void **pp_handle); + int (*CreateHandle)(void **pp_handle); + //! Function to destroy handle /*! - * Function to open input image + * In this function, any structures allocated with CreateHandle should be + * freed. It is generally the last function called before unloading of lib + * happens. + * + * By convention, after this function has been called, *pp_handle must be + * NULL. + * + * \param pp_handle Pointer to store handle to + * \return 0 on success or error code + */ + int (*DestroyHandle)(void **pp_handle); + + //! Function to open input image + /*! + * Opens the specified image for reading. * * \param pp_handle Pointer to store handle of opened image to * \param pp_filename_arr Array containing all specified input images * \param filename_arr_len Length of pp_filename_arr * \return 0 on success or error code */ int (*Open)(void **pp_handle, const char **pp_filename_arr, uint64_t filename_arr_len); + //! Function to close an opened input image /*! - * Function to get the input image's size + * Closes the input image and frees any memory allocaed during opening but + * does not invalidate the main handle. Further calls to for ex. Open must + * be possible without first calling CreateHandle again! + * + * \param pp_handle Pointer to the handle of the opened image + * \return 0 on success or error code + */ + int (*Close)(void **pp_handle); + + //! Function to get the input image's size + /*! + * Returns the real size of the input image. Real means the size of the + * uncompressed or otherwise made available data contained inside the input + * image. * * \param p_handle Handle to the opened image * \param p_size Pointer to store input image's size to * \return 0 on success or error code */ int (*Size)(void *p_handle, uint64_t *p_size); + //! Function to read data from input image /*! - * Function to read data from input image + * Reads count bytes at offset from input image and copies them into memory + * starting at the address of p_buf. Memory is pre-allocated to as much bytes + * as should be read. * * \param p_handle Handle to the opened image * \param offset Position at which to start reading * \param p_buf Buffer to store read data to * \param count Amount of bytes to read * \return 0 on success or error code */ int (*Read)(void *p_handle, uint64_t offset, char *p_buf, uint32_t count); - /*! - * Function to close an opened input image - * - * \param pp_handle Pointer to the handle of the opened image - * \return 0 on success or error code - */ - int (*Close)(void **pp_handle); - /*! * Function to return a string containing help messages for any supported - * lib-specific options + * lib-specific options. + * + * Returned string must be constant. It won't be freed! * - * \param pp_help Pointer to a string to store null-terminated help text + * \return Pointer to a null-terminated string containing the help text */ const char* (*OptionsHelp)(); /*! * Function to parse any lib-specific options * * \param p_handle Handle to the opened image * \param p_options String with specified options * \param pp_error Pointer to a string with error message * \return 0 on success or error code and error message */ int (*OptionsParse)(void *p_handle, char *p_options, char **pp_error); /*! * Function to get content to add to the info file * * \param p_handle Handle to the opened image * \param pp_info_buf Pointer to store the null-terminated content * \return 0 on success or error code */ int (*GetInfofileContent)(void *p_handle, const char **pp_info_buf); /*! * Function to free buffers that were allocated by lib * * \param p_buf Buffer to free */ void (*FreeBuffer)(void *p_buf); } ts_LibXmountInputFunctions, *pts_LibXmountInputFunctions; //! Get library API version /*! * \param p_ver Supported version */ uint8_t LibXmount_Input_GetApiVersion(); typedef uint8_t (*t_LibXmount_Input_GetApiVersion)(); //! Get a list of supported formats /*! * Gets a list of supported input image formats. These are the strings * specified with xmount's --in command line option. * * \param ppp_arr Array containing supported format strings * \return Length of ppp_arr */ const char* LibXmount_Input_GetSupportedFormats(); typedef const char* (*t_LibXmount_Input_GetSupportedFormats)(); //! Get the lib's s_LibXmountInputFunctions structure /*! * \param pp_functions Functions */ void LibXmount_Input_GetFunctions(ts_LibXmountInputFunctions *p_functions); typedef void (*t_LibXmount_Input_GetFunctions)(ts_LibXmountInputFunctions*); #endif // LIBXMOUNT_INPUT_H diff --git a/trunk/libxmount_input/libxmount_input_aaff/libxmount_input_aaff.c b/trunk/libxmount_input/libxmount_input_aaff/libxmount_input_aaff.c index 75a3114..aa5f189 100644 --- a/trunk/libxmount_input/libxmount_input_aaff/libxmount_input_aaff.c +++ b/trunk/libxmount_input/libxmount_input_aaff/libxmount_input_aaff.c @@ -1,722 +1,718 @@ /******************************************************************************* * xmount Copyright (c) 2008,2009, 2010, 2011, 2012 * * by Gillen Daniel * * * * This module has been written by Guy Voncken. It contains the functions for * * accessing simple AFF images created by Guymager. * * * * 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 #include #include #include #include #include #include #include #include "../libxmount_input.h" //#define AAFF_DEBUG #include "libxmount_input_aaff.h" /******************************************************************************* * Forward declarations ******************************************************************************/ int AaffInitHandle(void **pp_handle); int AaffOpen(void **pp_handle, const char **pp_filename_arr, uint64_t filename_arr_len); int AaffSize(void *p_handle, uint64_t *p_size); int AaffRead(void *p_handle, uint64_t seek, char *p_buf, uint32_t count); int AaffClose(void **pp_handle); const char* AaffOptionsHelp(); int AaffOptionsParse(void *p_handle, char *p_options, char **pp_error); int AaffGetInfofileContent(void *p_handle, const char **pp_info_buf); void AaffFreeBuffer(void *p_buf); /******************************************************************************* * LibXmount_Input API implementation ******************************************************************************/ /* * LibXmount_Input_GetApiVersion */ uint8_t LibXmount_Input_GetApiVersion() { return LIBXMOUNT_INPUT_API_VERSION; } /* * LibXmount_Input_GetSupportedFormats */ const char* LibXmount_Input_GetSupportedFormats() { return "aaff\0\0"; } /* * LibXmount_Input_GetFunctions */ void LibXmount_Input_GetFunctions(ts_LibXmountInputFunctions *p_functions) { p_functions->InitHandle=&AaffInitHandle; p_functions->Open=&AaffOpen; p_functions->Size=&AaffSize; p_functions->Read=&AaffRead; p_functions->Close=&AaffClose; p_functions->OptionsHelp=&AaffOptionsHelp; p_functions->OptionsParse=&AaffOptionsParse; p_functions->GetInfofileContent=&AaffGetInfofileContent; p_functions->FreeBuffer=&AaffFreeBuffer; } /******************************************************************************* * Private ******************************************************************************/ // --------------------------- // Internal static functions // --------------------------- static int AaffCreateHandle (t_pAaff *ppAaff) { t_pAaff pAaff; pAaff = (t_pAaff) malloc (sizeof(t_Aaff)); if (pAaff == NULL) return AAFF_MEMALLOC_FAILED; memset (pAaff, 0, sizeof(t_Aaff)); *ppAaff = pAaff; return AAFF_OK; } static int AaffDestroyHandle (t_pAaff *ppAaff) { t_pAaff pAaff = *ppAaff; if (pAaff->pFilename) free (pAaff->pFilename); if (pAaff->pPageSeekArr) free (pAaff->pPageSeekArr); if (pAaff->pLibVersion) free (pAaff->pLibVersion); if (pAaff->pFileType) free (pAaff->pFileType); if (pAaff->pNameBuff) free (pAaff->pNameBuff); if (pAaff->pDataBuff) free (pAaff->pDataBuff); if (pAaff->pPageBuff) free (pAaff->pPageBuff); if (pAaff->pInfoBuffConst) free (pAaff->pInfoBuffConst); if (pAaff->pInfoBuff) free (pAaff->pInfoBuff); memset (pAaff, 0, sizeof(t_Aaff)); free (pAaff); *ppAaff = NULL; return AAFF_OK; } uint64_t AaffU64 (char *pData) { uint64_t Val=0; int i; for (i=4; i<8; i++) Val = (Val << 8) | pData[i]; for (i=0; i<4; i++) Val = (Val << 8) | pData[i]; return Val; } static int AaffPageNumberFromSegmentName (char *pSegmentName, uint64_t *pPageNumber) { char *pSegmentNamePageNumber; char *pTail; pSegmentNamePageNumber = &pSegmentName[strlen(AFF_SEGNAME_PAGE)]; *pPageNumber = strtoull (pSegmentNamePageNumber, &pTail, 10); if (*pTail != '\0') return AAFF_INVALID_PAGE_NUMBER; // There should be no extra chars after the number return AAFF_OK; } static inline uint64_t AaffGetCurrentSeekPos (t_Aaff *pAaff) { return ftello (pAaff->pFile); } static inline uint64_t AaffSetCurrentSeekPos (t_Aaff *pAaff, uint64_t Val, int Whence) { if (fseeko (pAaff->pFile, Val, Whence) != 0) return AAFF_CANNOT_SEEK; return AAFF_OK; } static int AaffReadFile (t_Aaff *pAaff, void *pData, uint32_t DataLen) { if (fread (pData, DataLen, 1, pAaff->pFile) != 1) return AAFF_CANNOT_READ_DATA; return AAFF_OK; } static int AaffRealloc (void **ppBuff, uint32_t *pCurrentLen, uint32_t NewLen) { if (NewLen > *pCurrentLen) { *ppBuff = realloc (*ppBuff, NewLen); if (*ppBuff == NULL) return AAFF_MEMALLOC_FAILED; *pCurrentLen = NewLen; } return AAFF_OK; } static int AaffReadSegment (t_pAaff pAaff, char **ppName, uint32_t *pArg, char **ppData, uint32_t *pDataLen) { t_AffSegmentHeader Header; t_AffSegmentFooter Footer; CHK (AaffReadFile (pAaff, &Header, offsetof(t_AffSegmentHeader, Name))) if (strcmp (&Header.Magic[0], AFF_SEGMENT_HEADER_MAGIC) != 0) return AAFF_INVALID_HEADER; Header.NameLen = ntohl (Header.NameLen ); Header.DataLen = ntohl (Header.DataLen ); Header.Argument = ntohl (Header.Argument); CHK (AaffRealloc ((void**)&pAaff->pNameBuff, &pAaff->NameBuffLen, Header.NameLen+1)) // alloc +1, as is might be a string which can be more CHK (AaffRealloc ((void**)&pAaff->pDataBuff, &pAaff->DataBuffLen, Header.DataLen+1)) // easily handled by the calling fn when adding a \0 CHK (AaffReadFile (pAaff, pAaff->pNameBuff, Header.NameLen)) if (Header.DataLen) CHK (AaffReadFile (pAaff, pAaff->pDataBuff, Header.DataLen)) pAaff->pNameBuff[Header.NameLen] = '\0'; pAaff->pDataBuff[Header.DataLen] = '\0'; if (ppName) *ppName = pAaff->pNameBuff; if (pArg ) *pArg = Header.Argument; if (ppData) *ppData = pAaff->pDataBuff; if (pDataLen) *pDataLen = Header.DataLen; // Read footer and position file pointer to next segemnt at the same time // ---------------------------------------------------------------------- CHK (AaffReadFile (pAaff, &Footer, sizeof(Footer))) if (strcmp (&Footer.Magic[0], AFF_SEGMENT_FOOTER_MAGIC) != 0) return AAFF_INVALID_FOOTER; return AAFF_OK; } static int AaffReadSegmentPage (t_pAaff pAaff, uint64_t SearchPage, uint64_t *pFoundPage, char **ppData, uint32_t *pDataLen) { t_AffSegmentHeader Header; t_AffSegmentFooter Footer; char SearchPageStr[128]; int rc = AAFF_OK; *ppData = NULL; *pDataLen = 0; sprintf (SearchPageStr, "page%" PRIu64, SearchPage); CHK (AaffReadFile (pAaff, &Header, offsetof(t_AffSegmentHeader, Name))) if (strcmp (&Header.Magic[0], AFF_SEGMENT_HEADER_MAGIC) != 0) return AAFF_INVALID_HEADER; Header.NameLen = ntohl (Header.NameLen ); Header.DataLen = ntohl (Header.DataLen ); Header.Argument = ntohl (Header.Argument); CHK (AaffRealloc ((void**)&pAaff->pNameBuff, &pAaff->NameBuffLen, Header.NameLen+1)) CHK (AaffReadFile (pAaff, pAaff->pNameBuff, Header.NameLen)) pAaff->pNameBuff[Header.NameLen] = '\0'; if (strncmp (pAaff->pNameBuff, AFF_SEGNAME_PAGE, strlen(AFF_SEGNAME_PAGE)) != 0) return AAFF_WRONG_SEGMENT; CHK (AaffPageNumberFromSegmentName (pAaff->pNameBuff, pFoundPage)) if (*pFoundPage == SearchPage) { unsigned int Len; uLongf ZLen; int zrc; switch (Header.Argument) { case AFF_PAGEFLAGS_UNCOMPRESSED: DEBUG_PRINTF ("\nuncompressed"); CHK (AaffReadFile (pAaff, pAaff->pPageBuff, Header.DataLen)) pAaff->PageBuffDataLen = Header.DataLen; break; case AFF_PAGEFLAGS_COMPRESSED_ZERO: DEBUG_PRINTF ("\nzero"); CHK (AaffReadFile (pAaff, &Len, sizeof(Len))) Len = ntohl (Len); memset (pAaff->pPageBuff, 0, Len); pAaff->PageBuffDataLen = Len; break; case AFF_PAGEFLAGS_COMPRESSED_ZLIB: DEBUG_PRINTF ("\ncompressed"); CHK (AaffRealloc ((void**)&pAaff->pDataBuff, &pAaff->DataBuffLen, Header.DataLen)); CHK (AaffReadFile (pAaff, pAaff->pDataBuff, Header.DataLen)) // read into pDataBuff ZLen = pAaff->PageSize; // size of pPageBuff zrc = uncompress ((unsigned char*)(pAaff->pPageBuff), &ZLen, (unsigned char*)(pAaff->pDataBuff), Header.DataLen); // uncompress into pPageBuff pAaff->PageBuffDataLen = ZLen; if (zrc != Z_OK) return AAFF_UNCOMPRESS_FAILED; break; default: return AAFF_INVALID_PAGE_ARGUMENT; } *ppData = pAaff->pPageBuff; *pDataLen = pAaff->PageBuffDataLen; pAaff->CurrentPage = *pFoundPage; rc = AAFF_FOUND; } else { CHK (AaffSetCurrentSeekPos (pAaff, Header.DataLen, SEEK_CUR)) } // Read footer and position file pointer to next segemnt at the same time // ---------------------------------------------------------------------- CHK (AaffReadFile (pAaff, &Footer, sizeof(Footer))) if (strcmp (&Footer.Magic[0], AFF_SEGMENT_FOOTER_MAGIC) != 0) return AAFF_INVALID_FOOTER; return rc; } static int AaffReadPage (t_pAaff pAaff, uint64_t Page, char **ppBuffer, uint32_t *pLen) { if (Page >= pAaff->TotalPages) return AAFF_READ_BEYOND_LAST_PAGE; // Check if it's the current page // ------------------------------ if (Page == pAaff->CurrentPage) { *ppBuffer = pAaff->pPageBuff; *pLen = pAaff->PageBuffDataLen; return AAFF_OK; } // Set the seek position for startig the search // -------------------------------------------- int MaxHops; if ((pAaff->CurrentPage != AAFF_CURRENTPAGE_NOTSET) && (pAaff->CurrentPage+1 == Page)) // The current seek pos already is the correct one { MaxHops = 1; } else // Find the closest entry in PageSeekArr { int64_t Entry; Entry = Page / pAaff->Interleave; while (pAaff->pPageSeekArr[Entry] == 0) { Entry--; if (Entry<0) return AAFF_SEEKARR_CORRUPT; } AaffSetCurrentSeekPos (pAaff, pAaff->pPageSeekArr[Entry], SEEK_SET); MaxHops = Page - (Entry * pAaff->Interleave) +1; } // Run through segment list until page is found // -------------------------------------------- uint64_t Seek; uint64_t FoundPage; int rc; DEBUG_PRINTF ("\nSearching for page %" PRIu64 ", MaxHops=%d -- ", Page, MaxHops); while (MaxHops--) { Seek = AaffGetCurrentSeekPos (pAaff); rc = AaffReadSegmentPage (pAaff, Page, &FoundPage, ppBuffer, pLen); DEBUG_PRINTF (" %" PRIu64 " (%d)", FoundPage, rc); if ((FoundPage % pAaff->Interleave) == 0) pAaff->pPageSeekArr[FoundPage/pAaff->Interleave] = Seek; if (rc == AAFF_FOUND) break; } DEBUG_PRINTF ("\n"); if (MaxHops<0) return AAFF_PAGE_NOT_FOUND; return AAFF_OK; } // --------------- // API functions // --------------- /* * AaffInitHandle */ int AaffInitHandle(void **pp_handle) { *pp_handle=NULL; - CHK(AaffCreateHandle((t_pAaff*)pp_handle)) - return AAFF_OK; } /* * AaffOpen */ int AaffOpen(void **pp_handle, const char **pp_filename_arr, uint64_t filename_arr_len) { t_pAaff pAaff=(t_pAaff)*pp_handle; char Signature[strlen(AFF_HEADER)+1]; uint64_t Seek; if(filename_arr_len!=1) { // Split aff files are not supported // TODO: Set correct error return 1; } pAaff->pFilename=strdup(pp_filename_arr[0]); pAaff->pFile=fopen(pp_filename_arr[0],"r"); if(pAaff->pFile==NULL) { AaffDestroyHandle(&pAaff); return AAFF_FILE_OPEN_FAILED; } // Check signature // --------------- CHK(AaffReadFile(pAaff,&Signature,sizeof(Signature))) if(memcmp(Signature,AFF_HEADER,sizeof(Signature))!=0) { (void)AaffClose((void**)&pAaff); return AAFF_INVALID_SIGNATURE; } // Read header segments // -------------------- char *pName; uint32_t Arg; char *pData; uint32_t DataLen; const int MAX_HEADER_SEGMENTS = 100; int Seg; unsigned int i; int wr; int Pos = 0; const unsigned HexStrLen = 32; char HexStr[HexStrLen+1]; #define REM (AaffInfoBuffLen-Pos) pAaff->pInfoBuffConst = malloc (AaffInfoBuffLen); pAaff->pInfoBuff = malloc (AaffInfoBuffLen*2); // Search for known segments at the image start for (Seg=0; SegPageSize=Arg; } else if(strcmp(pName,AFF_SEGNAME_SECTORSIZE)==0) { pAaff->SectorSize=Arg; } else if(strcmp(pName,AFF_SEGNAME_SECTORS)==0) { pAaff->Sectors=AaffU64(pData); } else if(strcmp(pName,AFF_SEGNAME_IMAGESIZE)==0) { pAaff->ImageSize=AaffU64(pData); } else if(strcmp(pName,AFF_SEGNAME_AFFLIB_VERSION)==0) { pAaff->pLibVersion=strdup((char*)pData); } else if(strcmp(pName,AFF_SEGNAME_FILETYPE)==0) { pAaff->pFileType=strdup((char*)pData); } else if((strcmp(pName,AFF_SEGNAME_GID)==0) || (strcmp(pName, AFF_SEGNAME_BADFLAG)==0)) { wr=0; for (i=0; ipInfoBuffConst[Pos]), REM,"%-25s %s", pName, HexStr); if (ipInfoBuffConst[Pos]), REM,"..."); Pos += snprintf (&(pAaff->pInfoBuffConst[Pos]), REM,"\n"); } else if(strncmp(pName,AFF_SEGNAME_PAGE,strlen(AFF_SEGNAME_PAGE))==0) { break; } else { if ((Arg == 0) && DataLen) Pos += snprintf (&(pAaff->pInfoBuffConst[Pos]), REM,"%-25s %s\n", pName, pData); } } #undef REM if (Seg >= MAX_HEADER_SEGMENTS) { (void) AaffClose ((void**)&pAaff); return AAFF_TOO_MANY_HEADER_SEGEMENTS; } if (strstr (pAaff->pLibVersion, "Guymager") == NULL) { (void) AaffClose ((void**)&pAaff); return AAFF_NOT_CREATED_BY_GUYMAGER; } // Prepare page seek array // ----------------------- uint64_t MaxEntries; int ArrBytes; pAaff->TotalPages = pAaff->ImageSize / pAaff->PageSize; if (pAaff->ImageSize % pAaff->PageSize) pAaff->TotalPages++; // TODO: MaxPageArrMem was a uint64_t parameter of this function MaxEntries = AAFF_DEFAULT_PAGE_SEEK_MAX_ENTRIES; /* if (MaxPageArrMem) { // +1 in order not to risk a result of 0 MaxEntries = (MaxPageArrMem / sizeof (unsigned long long *)) + 1; } else MaxEntries = AAFF_DEFAULT_PAGE_SEEK_MAX_ENTRIES; */ MaxEntries = GETMIN (MaxEntries, pAaff->TotalPages); pAaff->Interleave = pAaff->TotalPages / MaxEntries; if (pAaff->TotalPages % MaxEntries) pAaff->Interleave++; pAaff->PageSeekArrLen = pAaff->TotalPages / pAaff->Interleave; ArrBytes = pAaff->PageSeekArrLen * sizeof(uint64_t *); pAaff->pPageSeekArr = (uint64_t*)malloc (ArrBytes); memset (pAaff->pPageSeekArr, 0, ArrBytes); CHK (AaffPageNumberFromSegmentName (pName, &pAaff->CurrentPage)); if (pAaff->CurrentPage != 0) { (void) AaffClose ((void**)&pAaff); return AAFF_UNEXPECTED_PAGE_NUMBER; } pAaff->pPageSeekArr[0] = Seek; // Alloc Buffers // ------------- pAaff->pPageBuff = malloc (pAaff->PageSize); pAaff->CurrentPage = AAFF_CURRENTPAGE_NOTSET; - *pp_handle=(void*)pAaff; - return AAFF_OK; } /* * AaffClose */ int AaffClose(void **pp_handle) { int rc=AAFF_OK; if(fclose((*(t_pAaff*)pp_handle)->pFile)) rc=AAFF_CANNOT_CLOSE_FILE; CHK(AaffDestroyHandle((t_pAaff*)pp_handle)) return rc; } /* * AaffGetInfofileContent */ int AaffGetInfofileContent(void *p_handle, const char **pp_info_buf) { uint64_t i; uint64_t Entries = 0; int Pos = 0; #define REM (AaffInfoBuffLen-Pos) Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "AFF IMAGE INFORMATION"); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\n---------------------"); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\nAFF file %s" , ((t_pAaff)p_handle)->pFilename ); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\nPage size %u" , ((t_pAaff)p_handle)->PageSize ); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\nSector size %d" , ((t_pAaff)p_handle)->SectorSize ); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\nSectors %" PRIu64, ((t_pAaff)p_handle)->Sectors ); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\nImage size %" PRIu64 " (%0.1f GiB)", ((t_pAaff)p_handle)->ImageSize, ((t_pAaff)p_handle)->ImageSize/(1024.0*1024.0*1024.0)); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\nTotal pages %" PRIu64, ((t_pAaff)p_handle)->TotalPages ); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\n"); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\n%s", ((t_pAaff)p_handle)->pInfoBuffConst); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\n"); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\nCurrent page "); if (((t_pAaff)p_handle)->CurrentPage == AAFF_CURRENTPAGE_NOTSET) Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "not set"); else Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "%" PRIu64, ((t_pAaff)p_handle)->CurrentPage); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\nSeek array length %" PRIu64, ((t_pAaff)p_handle)->PageSeekArrLen); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\nSeek interleave %" PRIu64, ((t_pAaff)p_handle)->Interleave); for (i=0; i<((t_pAaff)p_handle)->PageSeekArrLen; i++) if (((t_pAaff)p_handle)->pPageSeekArr[i]) Entries++; Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\nSeek array entries %" PRIu64, Entries); Pos += snprintf (&((t_pAaff)p_handle)->pInfoBuff[Pos], REM, "\n"); #undef REM *pp_info_buf = ((t_pAaff)p_handle)->pInfoBuff; return AAFF_OK; } /* * AaffSize */ int AaffSize(void *p_handle, uint64_t *p_size) { *p_size=((t_pAaff)p_handle)->ImageSize; return AAFF_OK; } /* * AaffRead */ int AaffRead(void *p_handle, uint64_t seek, char *p_buf, uint32_t count) { uint64_t page; char *p_page_buffer; uint32_t page_len, ofs, to_copy; if((seek+count)>((t_pAaff)p_handle)->ImageSize) { return AAFF_READ_BEYOND_IMAGE_LENGTH; } page=seek/((t_pAaff)p_handle)->PageSize; ofs=seek%((t_pAaff)p_handle)->PageSize; while(count) { CHK(AaffReadPage((t_pAaff)p_handle,page,&p_page_buffer,&page_len)) to_copy=GETMIN(page_len-ofs,count); memcpy(p_buf,p_page_buffer+ofs,to_copy); count-=to_copy; p_buf+=to_copy; ofs=0; page++; } return AAFF_OK; } /* * AaffOptionsHelp */ const char* AaffOptionsHelp() { return NULL; } /* * AaffOptionsParse */ int AaffOptionsParse(void *p_handle, char *p_options, char **pp_error) { return AAFF_OK; } /* * AaffFreeBuffer */ void AaffFreeBuffer(void *p_buf) { free(p_buf); } // ----------------------------------------------------- // Small main routine for testing // It converts an aff file to dd // ----------------------------------------------------- #ifdef AAFF_MAIN_FOR_TESTING int main(int argc, char *argv[]) { t_pAaff pAaff; char *pInfoBuff; uint64_t Remaining; uint64_t CurrentPos=0; int rc; int Percent; int PercentOld; setbuf (stdout, NULL); setbuf (stderr, NULL); setlocale (LC_ALL, ""); printf ("AFF to DD converter\n"); if (argc != 3) { printf ("Usage: %s
\n", argv[0]); exit (1); } // rc = AaffOpen (&pAaff, argv[1], 1024); // weird seek array size for testing rc = AaffOpen (&pAaff, argv[1], 1); if (rc) { printf ("Error %d while opening file %s\n", rc, argv[1]); exit (2); } rc = AaffInfo (pAaff, &pInfoBuff); if (rc) { printf ("Could not retrieve info\n"); exit (2); } printf ("%s", pInfoBuff); // Create destination file and fill it with data from aff // ------------------------------------------------------ FILE *pFile; pFile = fopen (argv[2], "w"); // const unsigned BuffSize = 13; // weird Buffsize for testing const unsigned BuffSize = 65536; unsigned char *pBuff; unsigned int Bytes; Remaining = pAaff->ImageSize; pBuff = malloc (BuffSize); CurrentPos=0; PercentOld = -1; while (Remaining) { Bytes = GETMIN (Remaining, BuffSize); rc = AaffRead (pAaff, CurrentPos, pBuff, Bytes); if (rc != AAFF_OK) { printf ("Could not read data from aff file, error %d\n", rc); exit (2); } if (fwrite (pBuff, Bytes, 1, pFile) != 1) { printf ("Could not write to destinationfile\n"); exit (2); } CurrentPos += Bytes; Remaining -= Bytes; Percent = (100*CurrentPos) / pAaff->ImageSize; if (Percent != PercentOld) { printf ("\r%d%% done...", Percent); PercentOld = Percent; } } free (pBuff); fclose (pFile); return 0; } #endif diff --git a/trunk/libxmount_input/libxmount_input_aewf/libxmount_input_aewf.c b/trunk/libxmount_input/libxmount_input_aewf/libxmount_input_aewf.c index 8d4607b..51d2f77 100644 --- a/trunk/libxmount_input/libxmount_input_aewf/libxmount_input_aewf.c +++ b/trunk/libxmount_input/libxmount_input_aewf/libxmount_input_aewf.c @@ -1,1237 +1,1235 @@ /******************************************************************************* * xmount Copyright (c) 2008-2014 by Gillen Daniel * * * * This module has been written by Guy Voncken. It contains the functions for * * accessing EWF images created by Guymager and others. * * * * xmount is a small tool to "fuse mount" various harddisk image formats as dd, * * vdi, vhd or vmdk files and enable virtual write access to them. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ // Aewf has been written in order to reduce xmount's memory footprint when // operating on large EWF images. Before Aewf, xmount exclusively relied on // libewf for accessing EWF images, resulting in enormous memory consumption. // // Aewf uses 2 main structures for handling image access: pAewf->pSegmentArr // contains everything about the image files (segments) and pAewf->pTableArr // handles the EWF chunk offset tables. // // At the same time, those structures serve as caches for the two most vital // ressouces, namely the number of segment files opened in parallel and the // memory consumed by the chunk offset tables. // // The max. values for both are configurable, see pAewf->MaxOpenSegments and // pAewf->MaxTableCache. #include #include #include #include #include #include //lint !e537 !e451 Repeated include #include #include //lint !e537 !e451 Repeated include #include //lint !e537 !e451 Repeated include #include //lint !e537 !e451 Repeated include #include //lint !e537 !e451 Repeated include #include "../libxmount_input.h" //#define AEWF_DEBUG #include "libxmount_input_aewf.h" //#define AEWF_MAIN_FOR_TESTING #ifdef AEWF_MAIN_FOR_TESTING #define CREATE_REVERSE_FILE // #define REVERSE_FILE_USES_SEPARATE_HANDLE #endif #ifdef AEWF_MAIN_FOR_TESTING #define _GNU_SOURCE #endif #ifdef LINT_CODE_CHECK #define _LARGEFILE_SOURCE #define _FILE_OFFSET_BITS 64 #endif /******************************************************************************* * Forward declarations ******************************************************************************/ int AewfInitHandle(void **pp_handle); int AewfOpen(void **pp_handle, const char **pp_filename_arr, uint64_t filename_arr_len); int AewfSize(void *p_handle, uint64_t *p_size); int AewfRead(void *p_handle, uint64_t seek, char *p_buf, uint32_t count); int AewfClose(void **pp_handle); const char* AewfOptionsHelp(); int AewfOptionsParse(void *p_handle, char *p_options, char **pp_error); int AewfGetInfofileContent(void *p_handle, const char **pp_info_buf); void AewfFreeBuffer(void *p_buf); /******************************************************************************* * LibXmount_Input API implementation ******************************************************************************/ /* * LibXmount_Input_GetApiVersion */ uint8_t LibXmount_Input_GetApiVersion() { return LIBXMOUNT_INPUT_API_VERSION; } /* * LibXmount_Input_GetSupportedFormats */ const char* LibXmount_Input_GetSupportedFormats() { return "aewf\0\0"; } /* * LibXmount_Input_GetFunctions */ void LibXmount_Input_GetFunctions(ts_LibXmountInputFunctions *p_functions) { p_functions->InitHandle=&AewfInitHandle; p_functions->Open=&AewfOpen; p_functions->Size=&AewfSize; p_functions->Read=&AewfRead; p_functions->Close=&AewfClose; p_functions->OptionsHelp=&AewfOptionsHelp; p_functions->OptionsParse=&AewfOptionsParse; p_functions->GetInfofileContent=&AewfGetInfofileContent; p_functions->FreeBuffer=&AewfFreeBuffer; } /******************************************************************************* * Private ******************************************************************************/ // --------------------------- // Internal static functions // --------------------------- static int OpenFile (FILE **ppFile, const char *pFilename) { *ppFile = fopen (pFilename, "r"); if (*ppFile == NULL) return AEWF_FILE_OPEN_FAILED; return AEWF_OK; } static int CloseFile (FILE **ppFile) { if (fclose (*ppFile)) return AEWF_FILE_CLOSE_FAILED; *ppFile = NULL; return AEWF_OK; } static int ReadFilePos (FILE *pFile, void *pMem, unsigned int Size, uint64_t Pos) { if (Size == 0) return AEWF_OK; if (Pos != ULLONG_MAX) { if (fseeko (pFile, Pos, SEEK_SET)) return AEWF_FILE_SEEK_FAILED; } if (fread (pMem, Size, 1, pFile) != 1) return AEWF_FILE_READ_FAILED; return AEWF_OK; } //static int ReadFile (FILE *pFile, void *pMem, unsigned int Size) //{ // CHK (ReadFilePos (pFile, pMem, Size, ULLONG_MAX)) // // return AEWF_OK; //} static int ReadFileAllocPos (FILE *pFile, void **ppMem, unsigned int Size, uint64_t Pos) { *ppMem = (void*) malloc (Size); if (*ppMem == NULL) return AEWF_MEMALLOC_FAILED; CHK (ReadFilePos (pFile, *ppMem, Size, Pos)) return AEWF_OK; } static int ReadFileAlloc (FILE *pFile, void **ppMem, unsigned int Size) { CHK (ReadFileAllocPos (pFile, ppMem, Size, ULLONG_MAX)) return AEWF_OK; } static int QsortCompareSegments (const void *pA, const void *pB) { const t_pSegment pSegmentA = ((const t_pSegment)pA); //lint !e1773 Attempt to cast way const const t_pSegment pSegmentB = ((const t_pSegment)pB); //lint !e1773 Attempt to cast way const return (int)pSegmentA->Number - (int)pSegmentB->Number; } // --------------- // API functions // --------------- -/* - * AewfInitHandle - */ -int AewfInitHandle(void **pp_handle) { - -} - static int CreateInfoData (t_pAewf pAewf, t_pAewfSectionVolume pVolume, char *pHeader , unsigned HeaderLen, char *pHeader2, unsigned Header2Len) { char *pInfo1; char *pInfo2; char *pInfo3 = NULL; char *pInfo4; char *pInfo5; char *pHdr = NULL; unsigned HdrLen= 0; char *pText = NULL; char *pCurrent; char *pDesc = NULL; char *pData = NULL; char *pEnd; uLongf DstLen0; int zrc; const int MaxTextSize = 65536; unsigned UncompressedLen; ASPRINTF(&pInfo1, "Image size %" PRIu64 " (%0.2f GiB)\n" "Bytes per sector %u\n" "Sector count %" PRIu64 "\n" "Sectors per chunk %u\n" "Chunk count %u\n" "Error block size %u\n" "Compression level %u\n" "Media type %02X\n" "Cylinders/Heads/Sectors %u/%u/%u\n" "Media flags %02X\n" "Palm volume start sector %u\n" "Smart logs start sector %u\n", pAewf->ImageSize, pAewf->ImageSize / (1024.0 * 1024.0* 1024.0), pVolume->BytesPerSector, pVolume->SectorCount, pVolume->SectorsPerChunk, pVolume->ChunkCount, pVolume->ErrorBlockSize, pVolume->CompressionLevel, pVolume->MediaType, pVolume->CHS_Cylinders, pVolume->CHS_Heads, pVolume->CHS_Sectors, pVolume->MediaFlags, pVolume->PalmVolumeStartSector, pVolume->SmartLogsStartSector); ASPRINTF (&pInfo2, "AcquirySystemGUID %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", pVolume->AcquirySystemGUID[ 0], pVolume->AcquirySystemGUID[ 1], pVolume->AcquirySystemGUID[ 2], pVolume->AcquirySystemGUID[ 3], pVolume->AcquirySystemGUID[ 4], pVolume->AcquirySystemGUID[ 5], pVolume->AcquirySystemGUID[ 6], pVolume->AcquirySystemGUID[ 7], pVolume->AcquirySystemGUID[ 8], pVolume->AcquirySystemGUID[ 9], pVolume->AcquirySystemGUID[10], pVolume->AcquirySystemGUID[11], pVolume->AcquirySystemGUID[12], pVolume->AcquirySystemGUID[13], pVolume->AcquirySystemGUID[14], pVolume->AcquirySystemGUID[15]); if (pHeader2) { pHdr = pHeader2; HdrLen = Header2Len; } else if (pHeader ) { pHdr = pHeader; HdrLen = HeaderLen; } // pHdr = pHeader; HdrLen = HeaderLen; if (pHdr) { pText = (char *) malloc (MaxTextSize); if (pText == NULL) return AEWF_MEMALLOC_FAILED; DstLen0 = MaxTextSize; zrc = uncompress ((unsigned char *)pText, &DstLen0, (const Bytef*)pHdr, HdrLen); UncompressedLen = DstLen0; if (zrc != Z_OK) return AEWF_UNCOMPRESS_HEADER_FAILED; if (pHeader2) // We must convert from silly Windows 2 byte wchar_t to { // correct Unix 4 byte wchar_t, before we can convert to UTF8 wchar_t *pTemp = (wchar_t*) malloc ((UncompressedLen/2)*sizeof(wchar_t)); if (pTemp == NULL) return AEWF_MEMALLOC_FAILED; for (unsigned i=0; i<(UncompressedLen/2); i++) pTemp[i] = (wchar_t) (((unsigned char*)pText)[2*i ]) | (((wchar_t) (((unsigned char*)pText)[2*i+1])) << 8); wcstombs(pText, pTemp, UncompressedLen/2); free (pTemp); } // Extract descriptor and data lines // --------------------------------- pCurrent = pText; // pText may start with BOM (Header2), but that's no problem as while (pCurrent) // first line anyway never is the "main" line. { if (strcasestr(pCurrent, "main") == pCurrent) // The header line is the one that break; // follows the line beginning with "main" pCurrent = strstr (pCurrent, "\n"); if (pCurrent) pCurrent++; } if (pCurrent) { pDesc = strstr (pCurrent, "\n"); if (pDesc) { *pDesc++ = '\0'; pData = strstr (pDesc, "\n"); if (pData) { *pData++ = '\0'; pEnd = strstr (pData, "\n"); if (pEnd) *pEnd = '\0'; } } } // Scan descriptor and data lines // ------------------------------ char *pCurDesc = pDesc; char *pCurData = pData; const char *pField; char *pTabDesc; char *pTabData; char *pValue; int wr = 0; time_t Time; struct tm *pTM; char TimeBuff[64]; if (pDesc && pData) { pInfo3 = (char *) malloc (strlen (pData) + 4096); if (pInfo3 == NULL) return AEWF_MEMALLOC_FAILED; while (*pCurDesc && *pCurData) { pTabDesc = strstr (pCurDesc, "\t"); pTabData = strstr (pCurData, "\t"); if (pTabDesc) *pTabDesc = '\0'; if (pTabData) *pTabData = '\0'; if (strcasecmp(pCurDesc, "a" ) == 0) pField = "Description"; else if (strcasecmp(pCurDesc, "c" ) == 0) pField = "Case"; else if (strcasecmp(pCurDesc, "n" ) == 0) pField = "Evidence"; else if (strcasecmp(pCurDesc, "e" ) == 0) pField = "Examiner"; else if (strcasecmp(pCurDesc, "t" ) == 0) pField = "Notes"; else if (strcasecmp(pCurDesc, "md") == 0) pField = "Model"; else if (strcasecmp(pCurDesc, "sn") == 0) pField = "Serial number"; else if (strcasecmp(pCurDesc, "av") == 0) pField = "Imager version"; else if (strcasecmp(pCurDesc, "ov") == 0) pField = "OS version"; else if (strcasecmp(pCurDesc, "m" ) == 0) pField = "Acquired time"; else if (strcasecmp(pCurDesc, "u" ) == 0) pField = "System time"; else if (strcasecmp(pCurDesc, "p" ) == 0) pField = NULL; else if (strcasecmp(pCurDesc, "dc") == 0) pField = NULL; else pField = "--"; if (pField) { pValue = pCurData; if (strstr (pField, "time")) { size_t w; Time = atoll (pCurData); pTM = localtime (&Time); pValue = &TimeBuff[0]; w = strftime (pValue, sizeof(TimeBuff), "%Y-%m-%d %H:%M:%S (%z)", pTM); sprintf (&pValue[w], " (epoch %s)", pCurData); } wr += sprintf (&pInfo3[wr], "%-17s %s\n", pField, pValue); } if (!pTabDesc || !pTabData) break; pCurDesc = pTabDesc+1; pCurData = pTabData+1; } } } if (pAewf->Segments == 1) ASPRINTF (&pInfo4, "%u segment file: %s\n", pAewf->Segments, pAewf->pSegmentArr[0].pName) else ASPRINTF (&pInfo4, "%u segment files\n First: %s\n Last: %s\n", pAewf->Segments, pAewf->pSegmentArr[0 ].pName, pAewf->pSegmentArr[pAewf->Segments-1].pName); ASPRINTF (&pInfo5, "%u tables\n", pAewf->Tables); if (pInfo3) ASPRINTF (&pAewf->pInfo, "%s%s\n%s\n%s%s", pInfo1, pInfo2, pInfo3, pInfo4, pInfo5) else ASPRINTF (&pAewf->pInfo, "%s%s%s%s" , pInfo1, pInfo2, pInfo4, pInfo5) free (pInfo1); free (pInfo2); free (pInfo4); free (pInfo5); if (pText ) free (pText ); if (pInfo3) free (pInfo3); return AEWF_OK; } +/* + * AewfInitHandle + */ +int AewfInitHandle(void **pp_handle) { + t_pAewf p_aewf=(t_pAewf)*pp_handle; + + // Create handle and clear it + // -------------------------- + p_aewf=(t_pAewf)malloc(sizeof(t_Aewf)); + if(p_aewf == NULL) return AEWF_MEMALLOC_FAILED; + + memset(p_aewf,0,sizeof(t_Aewf)); + p_aewf->ChunkInBuff=ULONG_LONG_MAX; + p_aewf->pErrorText=NULL; + p_aewf->pStatsFilename=NULL; + p_aewf->StatsRefresh=10; + p_aewf->SegmentCacheHits=0; + p_aewf->SegmentCacheMisses=0; + p_aewf->TableCacheHits=0; + p_aewf->TableCacheMisses=0; + p_aewf->ChunkCacheHits=0; + p_aewf->ChunkCacheMisses=0; + p_aewf->ReadOperations=0; + p_aewf->DataReadFromImage=0; + p_aewf->DataReadFromImageRaw=0; + p_aewf->DataRequestedByCaller=0; + p_aewf->TablesReadFromImage=0; + + return AEWF_OK; +} + /* * AewfOpen */ int AewfOpen(void **pp_handle, const char **pp_filename_arr, uint64_t filename_arr_len) { - t_pAewf pAewf; + t_pAewf pAewf=(t_pAewf)*pp_handle; t_AewfFileHeader FileHeader; t_AewfSection Section; FILE *pFile; t_pSegment pSegment; t_pTable pTable; uint64_t Pos; t_pAewfSectionTable pEwfTable = NULL; t_pAewfSectionVolume pVolume = NULL; char *pHeader = NULL; char *pHeader2 = NULL; int LastSection; unsigned int SectionSectorsSize; unsigned HeaderLen = 0; unsigned Header2Len = 0; - // Create handle and clear it - // -------------------------- - *pp_handle = NULL; - pAewf = (t_pAewf) malloc (sizeof(t_Aewf)); - if (pAewf == NULL) - return AEWF_MEMALLOC_FAILED; - memset (pAewf, 0, sizeof(t_Aewf)); - pAewf->ChunkInBuff = ULONG_LONG_MAX; - pAewf->pErrorText = NULL; - pAewf->pStatsFilename = NULL; - pAewf->StatsRefresh = 10; - - pAewf->SegmentCacheHits = 0; - pAewf->SegmentCacheMisses = 0; - pAewf->TableCacheHits = 0; - pAewf->TableCacheMisses = 0; - pAewf->ChunkCacheHits = 0; - pAewf->ChunkCacheMisses = 0; - pAewf->ReadOperations = 0; - pAewf->DataReadFromImage = 0; - pAewf->DataReadFromImageRaw = 0; - pAewf->DataRequestedByCaller= 0; - pAewf->TablesReadFromImage = 0; - // Create pSegmentArr and put the segment files in it // -------------------------------------------------- int SegmentArrLen = filename_arr_len * sizeof(t_Segment); pAewf->pSegmentArr = (t_pSegment) malloc (SegmentArrLen); pAewf->Segments = filename_arr_len; if (pAewf->pSegmentArr == NULL) return AEWF_MEMALLOC_FAILED; memset (pAewf->pSegmentArr, 0, SegmentArrLen); for (unsigned i=0; ipSegmentArr[i]; pSegment->pName = canonicalize_file_name (pp_filename_arr[i]); // canonicalize_file_name allocates a buffer CHK (OpenFile (&pFile, pSegment->pName)) CHK (ReadFilePos (pFile, (void*)&FileHeader, sizeof(FileHeader), 0)) // DEBUG_PRINTF ("Segment %s - %d \n", pp_filename_arr[i], FileHeader.SegmentNumber); pSegment->Number = FileHeader.SegmentNumber; pSegment->LastUsed = 0; pSegment->pFile = NULL; CHK (CloseFile (&pFile)) } // Put segment array into correct sequence and check if segment number are correct // ------------------------------------------------------------------------------- qsort (pAewf->pSegmentArr, pAewf->Segments, sizeof (t_Segment), &QsortCompareSegments); for (unsigned i=0; iSegments; i++) { if ((i+1) != pAewf->pSegmentArr[i].Number) return AEWF_INVALID_SEGMENT_NUMBER; } // Find all tables in the segment files // ------------------------------------ pAewf->pTableArr = NULL; pAewf->Tables = 0; pAewf->Chunks = 0; SectionSectorsSize = 0; DEBUG_PRINTF ("Reading tables\n"); for (unsigned i=0; iSegments; i++) { pSegment = &pAewf->pSegmentArr[i]; CHK (OpenFile (&pFile, pSegment->pName)) CHK (ReadFilePos (pFile, &FileHeader, sizeof(FileHeader), 0)) Pos = sizeof (FileHeader); DEBUG_PRINTF ("Segment %s ", pSegment->pName); do { CHK (ReadFilePos (pFile, &Section, sizeof (t_AewfSection), Pos)) if (strcasecmp ((char *)Section.Type, "sectors") == 0) { SectionSectorsSize = Section.Size; } else if (strcasecmp ((char *)Section.Type, "table") == 0) { if (pVolume == NULL) return AEWF_VOLUME_MUST_PRECEDE_TABLES; if (SectionSectorsSize == 0) return AEWF_SECTORS_MUST_PRECEDE_TABLES; pAewf->Tables++; pAewf->pTableArr = (t_pTable) realloc (pAewf->pTableArr, pAewf->Tables * sizeof (t_Table)); CHK (ReadFileAlloc (pFile, (void**) &pEwfTable, sizeof(t_AewfSectionTable))) // No need to read the actual offset table pTable = &pAewf->pTableArr[pAewf->Tables-1]; pTable->Nr = pAewf->Tables-1; pTable->pSegment = pSegment; pTable->Offset = Pos + sizeof (t_AewfSection); pTable->Size = Section.Size; pTable->ChunkCount = pEwfTable->ChunkCount; pTable->LastUsed = 0; pTable->pEwfTable = NULL; pTable->ChunkFrom = pAewf->Chunks; pTable->SectionSectorsSize = SectionSectorsSize; pAewf->Chunks += pTable->ChunkCount; pTable->ChunkTo = pAewf->Chunks-1; DEBUG_PRINTF ("t%d", pTable->ChunkCount) free (pEwfTable); pEwfTable = NULL; SectionSectorsSize = 0; } else if ((strcasecmp ((char *)Section.Type, "header") == 0) && (pHeader==NULL)) { HeaderLen = Section.Size - sizeof(t_AewfSection); CHK (ReadFileAlloc (pFile, (void**) &pHeader, HeaderLen)) } else if ((strcasecmp ((char *)Section.Type, "header2") == 0) && (pHeader2==NULL)) { Header2Len = Section.Size - sizeof(t_AewfSection); CHK (ReadFileAlloc (pFile, (void**) &pHeader2, Header2Len)) } else if ((strcasecmp ((char *)Section.Type, "volume") == 0) && (pVolume==NULL)) { CHK (ReadFileAlloc (pFile, (void**) &pVolume, sizeof(t_AewfSectionVolume))) pAewf->Sectors = pVolume->SectorCount; pAewf->SectorSize = pVolume->BytesPerSector; pAewf->ChunkSize = pVolume->SectorsPerChunk * pVolume->BytesPerSector; pAewf->ImageSize = pAewf->Sectors * pAewf->SectorSize; DEBUG_PRINTF ("%lld sectors à %lld bytes", pAewf->Sectors, pAewf->SectorSize) } LastSection = (Pos == Section.OffsetNextSection); Pos = Section.OffsetNextSection; } while (!LastSection); DEBUG_PRINTF ("\n"); CHK (CloseFile (&pFile)) } if (pVolume == NULL) return AEWF_VOLUME_MISSING; if (pAewf->Chunks != pVolume->ChunkCount) return AEWF_WRONG_CHUNK_COUNT; pAewf->ChunkBuffSize = pAewf->ChunkSize + 4096; // reserve some extra space (for CRC and as compressed data might be slightly larger than uncompressed data with some imagers) pAewf->pChunkBuffCompressed = (char *) malloc (pAewf->ChunkBuffSize); pAewf->pChunkBuffUncompressed = (char *) malloc (pAewf->ChunkBuffSize); if ((pAewf->pChunkBuffCompressed == NULL) || (pAewf->pChunkBuffUncompressed == NULL)) return AEWF_MEMALLOC_FAILED; pAewf->MaxTableCache = 10*1024*1024; pAewf->MaxOpenSegments = 10; pAewf->TableCache = 0; pAewf->OpenSegments = 0; - *((t_pAewf**)pp_handle)=(void*)pAewf; - CHK (CreateInfoData (pAewf, pVolume, pHeader, HeaderLen, pHeader2, Header2Len)) free (pVolume); free (pHeader); free (pHeader2); return AEWF_OK; } /* * AewfInfo */ int AewfGetInfofileContent(void *p_handle, const char **pp_info_buf) { *pp_info_buf=((t_pAewf)p_handle)->pInfo; return AEWF_OK; } /* * AewfSize */ int AewfSize(void *p_handle, uint64_t *p_size) { *p_size = ((t_pAewf)p_handle)->ImageSize; return AEWF_OK; } static int AewfOpenSegment (t_pAewf pAewf, t_pTable pTable) { t_pSegment pOldestSegment; if (pTable->pSegment->pFile != NULL) // is already opened ? { pAewf->SegmentCacheHits++; return AEWF_OK; } pAewf->SegmentCacheMisses++; // Check if another segment file must be closed first // -------------------------------------------------- while (pAewf->OpenSegments >= pAewf->MaxOpenSegments) { pOldestSegment = NULL; for (unsigned i=0; iSegments; i++) { if (pAewf->pSegmentArr[i].pFile == NULL) continue; if (pOldestSegment == NULL) { pOldestSegment = &pAewf->pSegmentArr[i]; } else { if (pAewf->pSegmentArr[i].LastUsed < pOldestSegment->LastUsed) pOldestSegment = &pAewf->pSegmentArr[i]; } } if (pOldestSegment == NULL) break; DEBUG_PRINTF ("Closing %s\n", pOldestSegment->pName); CHK (CloseFile (&pOldestSegment->pFile)) pAewf->OpenSegments--; } // Read the desired table into RAM // ------------------------------- DEBUG_PRINTF ("Opening %s\n", pTable->pSegment->pName); CHK (OpenFile(&pTable->pSegment->pFile, pTable->pSegment->pName)) pAewf->OpenSegments++; return AEWF_OK; } static int AewfLoadEwfTable (t_pAewf pAewf, t_pTable pTable) { t_pTable pOldestTable = NULL; if (pTable->pEwfTable != NULL) // is already loaded? { pAewf->TableCacheHits++; return AEWF_OK; } pAewf->TableCacheMisses++; // Check if another pEwfTable must be given up first // ------------------------------------------------- while ((pAewf->TableCache + pTable->Size) > pAewf->MaxTableCache) { pOldestTable = NULL; for (unsigned i=0; iTables; i++) { if (pAewf->pTableArr[i].pEwfTable == NULL) continue; if (pOldestTable == NULL) { pOldestTable = &pAewf->pTableArr[i]; } else { if (pAewf->pTableArr[i].LastUsed < pOldestTable->LastUsed) pOldestTable = &pAewf->pTableArr[i]; } } if (pOldestTable == NULL) break; pAewf->TableCache -= pOldestTable->Size; free (pOldestTable->pEwfTable); pOldestTable->pEwfTable = NULL; DEBUG_PRINTF ("Releasing table %" PRIu64 " (%lu bytes)\n", pOldestTable->Nr, pOldestTable->Size); } // Read the desired table into RAM // ------------------------------- DEBUG_PRINTF ("Loading table %" PRIu64 " (%lu bytes)\n", pTable->Nr, pTable->Size); CHK (AewfOpenSegment (pAewf, pTable)); CHK (ReadFileAllocPos (pTable->pSegment->pFile, (void**) &pTable->pEwfTable, pTable->Size, pTable->Offset)) pAewf->TableCache += pTable->Size; pAewf->TablesReadFromImage = pTable->Size; return AEWF_OK; } // AewfReadChunk0 reads one chunk. It expects that the EWF table is present // in memory and the required segment file is opened. static int AewfReadChunk0 (t_pAewf pAewf, t_pTable pTable, uint64_t AbsoluteChunk, unsigned TableChunk) { int Compressed; uint64_t SeekPos; t_pAewfSectionTable pEwfTable; unsigned int Offset; unsigned int ReadLen; uLongf DstLen0; int zrc; uint CalcCRC; uint *pStoredCRC; uint64_t ChunkSize; pEwfTable = pTable->pEwfTable; if (pEwfTable == NULL) return AEWF_ERROR_EWF_TABLE_NOT_READY; if (pTable->pSegment->pFile == NULL) return AEWF_ERROR_EWF_SEGMENT_NOT_READY; Compressed = pEwfTable->OffsetArray[TableChunk] & AEWF_COMPRESSED; Offset = pEwfTable->OffsetArray[TableChunk] & ~AEWF_COMPRESSED; SeekPos = pEwfTable->TableBaseOffset + Offset; if (TableChunk < (pEwfTable->ChunkCount-1)) ReadLen = (pEwfTable->OffsetArray[TableChunk+1] & ~AEWF_COMPRESSED) - Offset; else ReadLen = (pTable->SectionSectorsSize - sizeof(t_AewfSection)) - (Offset - (pEwfTable->OffsetArray[0] & ~AEWF_COMPRESSED)); // else ReadLen = pAewf->ChunkBuffSize; // This also works! It looks as if uncompress is able to find out by itself the real size of the input data. if (ReadLen > pAewf->ChunkBuffSize) return AEWF_CHUNK_TOO_BIG; if (Compressed) { CHK (ReadFilePos (pTable->pSegment->pFile, pAewf->pChunkBuffCompressed, ReadLen, SeekPos)) DstLen0 = pAewf->ChunkBuffSize; zrc = uncompress ((unsigned char*)pAewf->pChunkBuffUncompressed, &DstLen0, (const Bytef*)pAewf->pChunkBuffCompressed, ReadLen); if (zrc != Z_OK) return AEWF_UNCOMPRESS_FAILED; else if (DstLen0 != pAewf->ChunkSize) return AEWF_BAD_UNCOMPRESSED_LENGTH; ChunkSize = DstLen0; } else { ChunkSize = pAewf->ChunkSize; if (AbsoluteChunk == (pAewf->Chunks-1)) { ChunkSize = pAewf->ImageSize % pAewf->ChunkSize; if (ChunkSize == 0) ChunkSize = pAewf->ChunkSize; printf ("Last chunk size %" PRIu64 "\n", ChunkSize); printf ("ReadLen %u\n", ReadLen); } CHK (ReadFilePos (pTable->pSegment->pFile, pAewf->pChunkBuffUncompressed, ReadLen, SeekPos)) CalcCRC = adler32 (1, (const Bytef *) pAewf->pChunkBuffUncompressed, ChunkSize); pStoredCRC = (uint *) (pAewf->pChunkBuffUncompressed + ChunkSize); if (CalcCRC != *pStoredCRC) return AEWF_CHUNK_CRC_ERROR; } pAewf->ChunkInBuff = AbsoluteChunk; pAewf->ChunkBuffUncompressedDataLen = ChunkSize; pAewf->DataReadFromImage += ReadLen; pAewf->DataReadFromImageRaw += ChunkSize; return AEWF_OK; } static int AewfReadChunk (t_pAewf pAewf, uint64_t AbsoluteChunk, char **ppBuffer, unsigned int *pLen) { t_pTable pTable; int Found=FALSE; unsigned TableChunk; unsigned TableNr; *ppBuffer = pAewf->pChunkBuffUncompressed; if (pAewf->ChunkInBuff == AbsoluteChunk) { *pLen = pAewf->ChunkBuffUncompressedDataLen; pAewf->ChunkCacheHits++; return AEWF_OK; } pAewf->ChunkCacheMisses++; // Find table containing desired chunk // ----------------------------------- for (TableNr=0; TableNrTables; TableNr++) { pTable = &pAewf->pTableArr[TableNr]; Found = (AbsoluteChunk >= pTable->ChunkFrom) && (AbsoluteChunk <= pTable->ChunkTo); if (Found) break; } if (!Found) return AEWF_CHUNK_NOT_FOUND; // Load corresponding table and get chunk // -------------------------------------- pTable->LastUsed = time(NULL); // Update LastUsed here, in order not to pTable->pSegment->LastUsed = pTable->LastUsed; // remove the required data from cache CHK (AewfLoadEwfTable (pAewf, pTable)) CHK (AewfOpenSegment (pAewf, pTable)); if ((AbsoluteChunk - pTable->ChunkFrom) > ULONG_MAX) return AEWF_ERROR_IN_CHUNK_NUMBER; TableChunk = AbsoluteChunk - pTable->ChunkFrom; // DEBUG_PRINTF ("table %d / entry %" PRIu64 " (%s)\n", TableNr, TableChunk, pTable->pSegment->pName) CHK (AewfReadChunk0 (pAewf, pTable, AbsoluteChunk, TableChunk)) *pLen = pAewf->ChunkBuffUncompressedDataLen; return AEWF_OK; } static int UpdateStats (t_pAewf pAewf, int Force) { time_t NowT; pid_t pid; FILE *pFile; char *pFilename = NULL; char *pCurrentWorkDir = NULL; time (&NowT); if (pAewf->pStatsFilename) { if (((NowT - pAewf->LastStatsUpdate) >= (int)pAewf->StatsRefresh) || Force) { pAewf->LastStatsUpdate = NowT; pid = getpid (); ASPRINTF (&pFilename, "%s_%d", pAewf->pStatsFilename, pid) pFile = fopen (pFilename, "w"); if (pFile == NULL) // May be the file is locked by someone else, let's retry in 1 second { pAewf->LastStatsUpdate = NowT - pAewf->StatsRefresh + 1; return AEWF_OK; } fprintf (pFile, "Cache hits misses ratio\n"); fprintf (pFile, "-------------------------------------\n"); fprintf (pFile, "Segment %10" PRIu64 " %10" PRIu64 " %5.1f%%\n", pAewf->SegmentCacheHits, pAewf->SegmentCacheMisses, (100.0*pAewf->SegmentCacheHits)/(pAewf->SegmentCacheHits+pAewf->SegmentCacheMisses)); fprintf (pFile, "Table %10" PRIu64 " %10" PRIu64 " %5.1f%%\n", pAewf->TableCacheHits , pAewf->TableCacheMisses , (100.0*pAewf->TableCacheHits) /(pAewf->TableCacheHits +pAewf->TableCacheMisses )); fprintf (pFile, "Chunk %10" PRIu64 " %10" PRIu64 " %5.1f%%\n", pAewf->ChunkCacheHits , pAewf->ChunkCacheMisses , (100.0*pAewf->ChunkCacheHits) /(pAewf->ChunkCacheHits +pAewf->ChunkCacheMisses )); fprintf (pFile, "\n"); fprintf (pFile, "Read operations %10" PRIu64 "\n", pAewf->ReadOperations); fprintf (pFile, "Data read from image %10.1f MiB (compressed)\n", pAewf->DataReadFromImage / (1024.0*1024.0)); fprintf (pFile, "Data read from image %10.1f MiB (raw)\n" , pAewf->DataReadFromImageRaw / (1024.0*1024.0)); fprintf (pFile, "Data requested by caller %10.1f MiB\n" , pAewf->DataRequestedByCaller/ (1024.0*1024.0)); fprintf (pFile, "Tables read from image %10.1f MiB\n" , pAewf->TablesReadFromImage / (1024.0*1024.0)); pCurrentWorkDir = getcwd (NULL, 0); if (pCurrentWorkDir == NULL) return AEWF_MEMALLOC_FAILED; fprintf (pFile, "\nCurrent working directory: %s\n", pCurrentWorkDir); free (pCurrentWorkDir); (void) fclose (pFile); free (pFilename); return AEWF_OK; } } return AEWF_OK; } /* * AewfRead */ int AewfRead(void *p_handle, uint64_t seek, char *p_buf, uint32_t count) { uint64_t chunk; char *p_chunk_buffer; unsigned int chunk_len, ofs, to_copy; ((t_pAewf)p_handle)->ReadOperations++; ((t_pAewf)p_handle)->DataRequestedByCaller+=count; if((seek+count)>((t_pAewf)p_handle)->ImageSize) { return AEWF_READ_BEYOND_IMAGE_LENGTH; } chunk=seek/((t_pAewf)p_handle)->ChunkSize; ofs=seek%((t_pAewf)p_handle)->ChunkSize; while(count) { CHK(AewfReadChunk((t_pAewf)p_handle,chunk,&p_chunk_buffer,&chunk_len)) to_copy=GETMIN(chunk_len-ofs,count); memcpy(p_buf,p_chunk_buffer+ofs,to_copy); count-=to_copy; p_buf+=to_copy; ofs=0; chunk++; } CHK(UpdateStats((t_pAewf)p_handle,FALSE)) return AEWF_OK; } /* * AewfClose */ int AewfClose(void **pp_handle) { t_pTable p_table; t_pSegment p_segment; t_pAewf p_aewf=*((t_pAewf*)pp_handle); CHK(UpdateStats(p_aewf,TRUE)) for(unsigned i=0;iTables;i++) { p_table=&p_aewf->pTableArr[i]; if(p_table->pEwfTable) free(p_table->pEwfTable); } for(unsigned i=0;iSegments;i++) { p_segment=&p_aewf->pSegmentArr[i]; if(p_segment->pFile) CloseFile(&p_segment->pFile); free(p_segment->pName); } free(p_aewf->pTableArr); free(p_aewf->pSegmentArr); free(p_aewf->pChunkBuffCompressed); free(p_aewf->pChunkBuffUncompressed); if(p_aewf->pStatsFilename) free(p_aewf->pStatsFilename); memset(p_aewf,0,sizeof(t_Aewf)); free(p_aewf); *pp_handle=NULL; return AEWF_OK; } // Option handling // --------------- static const char *pOptionPrefix = "aewf_"; static const char OptionSeparator = ','; static int SetError (t_pAewf pAewf, char **ppError, const char *pFormat, ...) { va_list VaList; if (pAewf->pErrorText) free (pAewf->pErrorText); va_start(VaList, pFormat); if (vasprintf (&pAewf->pErrorText, pFormat, VaList) < 0) return AEWF_VASPRINTF_FAILED; va_end(VaList); *ppError = pAewf->pErrorText; return AEWF_OK; } static int CheckOption (const char *pOption, int OptionLen, const char *pOptionName, const char **ppValue, int *pValueLen) { int Found; *ppValue = NULL; *pValueLen = 0; Found = (strcasestr (pOption, pOptionName) == pOption); if (Found) { *ppValue = pOption + strlen (pOptionName); *pValueLen = OptionLen - strlen (pOptionName); } return Found; } static int ValueToInt (t_pAewf pAewf, const char *pValue, int ValueLen, char **ppError) { char *pTail; int Value; *ppError = NULL; Value = strtoll (pValue, &pTail, 10); if (pTail != (pValue + ValueLen)) CHK (SetError(pAewf, ppError, "Invalid option value %s", pValue)) return Value; } static char *ValueToStr (t_pAewf pAewf, const char *pValue, int ValueLen, char **ppError) { *ppError = NULL; return strndup (pValue, ValueLen); } static int ReadOption (t_pAewf pAewf, char *pOption, int OptionLen, char **ppError) { const char *pValue; int ValueLen; *ppError = NULL; if (CheckOption (pOption, OptionLen, "maxfiles=", &pValue, &ValueLen)) pAewf->MaxOpenSegments = ValueToInt (pAewf, pValue, ValueLen, ppError); else if (CheckOption (pOption, OptionLen, "maxmem=" , &pValue, &ValueLen)) pAewf->MaxTableCache = ValueToInt (pAewf, pValue, ValueLen, ppError)*1024*1024; else if (CheckOption (pOption, OptionLen, "stats=" , &pValue, &ValueLen)) pAewf->pStatsFilename = ValueToStr (pAewf, pValue, ValueLen, ppError); else if (CheckOption (pOption, OptionLen, "refresh=" , &pValue, &ValueLen)) pAewf->StatsRefresh = ValueToInt (pAewf, pValue, ValueLen, ppError); else CHK (SetError(pAewf, ppError, "Unknown option %s%s", pOptionPrefix, pOption)) return AEWF_OK; } /* * AewfOptionsParse */ int AewfOptionsParse(void *p_handle, char *p_options, char **pp_error) { char *pCurrent; char *pOption; char *pSep; int Found; pCurrent = p_options; while (*pCurrent) { pSep = strchr (pCurrent, OptionSeparator); if (pSep == NULL) pSep = pCurrent + strlen(pCurrent); Found = FALSE; if ((pSep - pCurrent) >= (int)strlen(pOptionPrefix)) // Check for options starting with our prefix { Found = (strncasecmp (pCurrent, pOptionPrefix, strlen(pOptionPrefix)) == 0); if (Found) { pOption = pCurrent + strlen(pOptionPrefix); CHK (ReadOption ((t_pAewf)p_handle, pOption, pSep-pOption, pp_error)) if (*pp_error) break; memmove (pCurrent, pSep+1, strlen(pSep)+1); } } if (!Found) { if (*pSep) pCurrent = pSep+1; else pCurrent = pSep; } } if (p_options[strlen(p_options)-1] == OptionSeparator) // Remove trailing separator if there is one p_options[strlen(p_options)-1] = '\0'; DEBUG_PRINTF ("Max open segment files %" PRIu64 "\n" , ((t_pAewf)p_handle)->MaxOpenSegments) DEBUG_PRINTF ("Max table cache %" PRIu64 " bytes (%0.1f MiB)\n", ((t_pAewf)p_handle)->MaxTableCache, ((t_pAewf)p_handle)->MaxTableCache / (1024.0*1024.0)) DEBUG_PRINTF ("Stats file %s\n" , ((t_pAewf)p_handle)->pStatsFilename ? ((t_pAewf)p_handle)->pStatsFilename : "-none-") DEBUG_PRINTF ("Stats refresh %" PRIu64 "s\n" , ((t_pAewf)p_handle)->StatsRefresh); DEBUG_PRINTF ("Unused options %s\n" , pOptions); return AEWF_OK; } const char* AewfOptionsHelp() { return " aewf_maxmem : The maximum amount of memory (in MiB) used for caching image offset tables.\n" " aewf_maxfiles : The maximum number of image segment files opened at the same time.\n" " aewf_stats : A filename that will be used for outputting statistical data at regular\n" " intervals. The process id is automatically appended to the given filename.\n" " aewf_refresh : The update interval, in seconds, for the statistical data output.\n" " Ignored if aewf_stats is not set. The default value is 10.\n" " Example: aewf_maxmem=64,aewf_stats=mystats,aewf_refresh=2"; } void AewfFreeBuffer(void *p_buf) { free(p_buf); } // ----------------------------------------------------- // Small main routine for testing // It converts an EWF file into dd // ----------------------------------------------------- #ifdef AEWF_MAIN_FOR_TESTING int main(int argc, const char *argv[]) { t_pAewf pAewf; uint64_t TotalSize; uint64_t Remaining; uint64_t Read; uint64_t Pos; unsigned int BuffSize = 13*65536; // A multiple of chunk size for good performance char Buff[BuffSize]; FILE *pFile; int Percent; int PercentOld; int rc; char *pOptions = NULL; char *pError = NULL; const char *pHelp; const char *pInfoBuff; #ifdef CREATE_REVERSE_FILE FILE *pFileRev; uint64_t PosRev; #ifdef REVERSE_FILE_USES_SEPARATE_HANDLE t_pAewf pAewfRev; #else #define pAewfRev pAewf #endif #endif setbuf(stdout, NULL); setbuf(stderr, NULL); setlocale (LC_ALL, ""); #define PRINT_ERROR_AND_EXIT(...) \ { \ printf (__VA_ARGS__); \ exit (1); \ } printf ("EWF to DD converter - result file is named dd\n"); printf (" Result file is named dd"); #ifdef CREATE_REVERSE_FILE printf ("; Also creates a backwards read file named rev"); #ifdef REVERSE_FILE_USES_SEPARATE_HANDLE printf ("; Uses separate AEWF handle for reverse file"); #else printf ("; Uses the same AEWF handle for reverse file"); #endif #endif printf ("\n"); if (argc < 2) { (void) AewfOptionHelp (&pHelp); printf ("Usage: %s <...> [-comma_separated_options]\n", argv[0]); printf ("Possible options:\n%s\n", pHelp); printf ("The output file will be named dd.\n"); exit (1); } if (argv[argc-1][0] == '-') { pOptions = strdup (&(argv[argc-1][1])); argc--; } rc = AewfOpen (&pAewf, argc-1, &argv[1]); if (rc != AEWF_OK) PRINT_ERROR_AND_EXIT ("Cannot open EWF files, rc=%d\n", rc) if (pOptions) CHK (AewfOptions(pAewf, pOptions, &pError)) if (pError) PRINT_ERROR_AND_EXIT ("Error while setting options: %s", pError); #if defined(CREATE_REVERSE_FILE) && defined(REVERSE_FILE_USES_SEPARATE_HANDLE) rc = AewfOpen (&pAewfRev, argc-1, &argv[1]); if (rc != AEWF_OK) PRINT_ERROR_AND_EXIT ("Cannot open EWF files, rc=%d\n", rc) if (pOptions) CHK (AewfOptions(pAewfRev, pOptions, &pError)) if (pError) PRINT_ERROR_AND_EXIT ("Error while setting options: %s", pError); #endif CHK (AewfInfo (pAewf, &pInfoBuff)) if (pInfoBuff) printf ("Contents of info buffer:\n%s\n", pInfoBuff); CHK (AewfSize (pAewf, &TotalSize)) printf ("Total size: %" PRIu64 " bytes\n", TotalSize); Remaining = TotalSize; pFile = fopen ("dd", "w"); if (pFile == NULL) PRINT_ERROR_AND_EXIT("Cannot open destination file\n"); #ifdef CREATE_REVERSE_FILE pFileRev = fopen ("rev", "w"); if (pFileRev == NULL) PRINT_ERROR_AND_EXIT("Cannot open reverse destination file\n"); PosRev = TotalSize; #endif Remaining = TotalSize; Pos = 0; PercentOld = -1; while (Remaining) { // DEBUG_PRINTF ("Pos %" PRIu64 " -- Remaining %" PRIu64 " ", Pos, Remaining); Read = GETMIN (Remaining, BuffSize); rc = AewfRead (pAewf, Pos, &Buff[0], Read); if (rc != AEWF_OK) PRINT_ERROR_AND_EXIT("Error %d while calling AewfRead\n", rc); if (fwrite (Buff, Read, 1, pFile) != 1) PRINT_ERROR_AND_EXIT("Could not write to destination file\n"); Remaining -= Read; Pos += Read; #ifdef CREATE_REVERSE_FILE PosRev -= Read; rc = AewfRead (pAewf, PosRev, &Buff[0], Read); if (rc != AEWF_OK) PRINT_ERROR_AND_EXIT("Error %d while reverse calling AewfRead\n", rc); if (fseeko (pFileRev, PosRev, SEEK_SET)) return AEWF_FILE_SEEK_FAILED; if (fwrite (Buff, Read, 1, pFileRev) != 1) PRINT_ERROR_AND_EXIT("Could not write to reverse destination file\n"); #endif Percent = (100*Pos) / TotalSize; if (Percent != PercentOld) { printf ("\r%d%% done...", Percent); PercentOld = Percent; } } if (AewfClose (&pAewf)) PRINT_ERROR_AND_EXIT("Error while closing EWF files\n"); if (fclose (pFile)) PRINT_ERROR_AND_EXIT ("Error while closing destination file\n"); #ifdef CREATE_REVERSE_FILE #ifdef REVERSE_FILE_USES_SEPARATE_HANDLE if (AewfClose (&pAewfRev)) PRINT_ERROR_AND_EXIT("Error while closing reverse EWF files\n"); #endif if (fclose (pFileRev)) PRINT_ERROR_AND_EXIT ("Error while closing reverse destination file\n"); #endif printf ("\n"); return 0; } #endif diff --git a/trunk/libxmount_input/libxmount_input_aff/libxmount_input_aff.c b/trunk/libxmount_input/libxmount_input_aff/libxmount_input_aff.c index e49f394..45f32be 100644 --- a/trunk/libxmount_input/libxmount_input_aff/libxmount_input_aff.c +++ b/trunk/libxmount_input/libxmount_input_aff/libxmount_input_aff.c @@ -1,204 +1,196 @@ /******************************************************************************* * xmount Copyright (c) 2008-2014 by Gillen Daniel * * * * xmount is a small tool to "fuse mount" various image formats and enable * * virtual write access. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #undef HAVE_LIBAFF_STATIC #include #include #include "../libxmount_input.h" #ifndef HAVE_LIBAFF_STATIC #include #else #include "libaff/lib/afflib.h" #endif /******************************************************************************* * Forward declarations ******************************************************************************/ +int AffInitHandle(void **pp_handle); int AffOpen(void **pp_handle, const char **pp_filename_arr, uint64_t filename_arr_len); int AffSize(void *p_handle, uint64_t *p_size); int AffRead(void *p_handle, uint64_t seek, unsigned char *p_buf, uint32_t count); int AffClose(void **pp_handle); const char* AffOptionsHelp(); int AffOptionsParse(void *p_handle, char *p_options, char **pp_error); int AffGetInfofileContent(void *p_handle, const char **pp_info_buf); void AffFreeBuffer(void *p_buf); /******************************************************************************* * LibXmount_Input API implementation ******************************************************************************/ /* * LibXmount_Input_GetApiVersion */ -void LibXmount_Input_GetApiVersion(uint8_t *p_ver) { - *p_ver=LIBXMOUNT_INPUT_API_VERSION; +uint8_t LibXmount_Input_GetApiVersion() { + return LIBXMOUNT_INPUT_API_VERSION; } /* * LibXmount_Input_GetSupportedFormats */ -void LibXmount_Input_GetSupportedFormats(char ***ppp_arr, uint8_t *p_arr_len) { - // Alloc array containing 1 element with content "aff" - *ppp_arr=(char**)malloc(sizeof(char*)); - if(*ppp_arr==NULL) { - *p_arr_len=0; - return; - } - **ppp_arr=(char*)malloc(sizeof(char)*4); - if(**ppp_arr==NULL) { - free(*ppp_arr); - *ppp_arr=NULL; - *p_arr_len=0; - return; - } - strcpy(**ppp_arr,"aff"); - *p_arr_len=1; +const char* LibXmount_Input_GetSupportedFormats() { + return "aff\0\0"; } /* * LibXmount_Input_GetFunctions */ -void LibXmount_Input_GetFunctions(ts_LibXmountInputFunctions **pp_functions) { - *pp_functions= - (pts_LibXmountInputFunctions)malloc(sizeof(ts_LibXmountInputFunctions)); - if(*pp_functions==NULL) return; - - (*pp_functions)->Open=&AffOpen; - (*pp_functions)->Size=&AffSize; - (*pp_functions)->Read=&AffRead; - (*pp_functions)->Close=&AffClose; - (*pp_functions)->OptionsHelp=&AffOptionsHelp; - (*pp_functions)->OptionsParse=&AffOptionsParse; - (*pp_functions)->GetInfofileContent=&AffGetInfofileContent; - (*pp_functions)->FreeBuffer=&AffFreeBuffer; +void LibXmount_Input_GetFunctions(ts_LibXmountInputFunctions *p_functions) { + p_functions->InitHandle=&AffInitHandle; + p_functions->Open=&AffOpen; + p_functions->Size=&AffSize; + p_functions->Read=&AffRead; + p_functions->Close=&AffClose; + p_functions->OptionsHelp=&AffOptionsHelp; + p_functions->OptionsParse=&AffOptionsParse; + p_functions->GetInfofileContent=&AffGetInfofileContent; + p_functions->FreeBuffer=&AffFreeBuffer; } /******************************************************************************* * Private ******************************************************************************/ +/* + * AffInitHandle + */ +int AffInitHandle(void **pp_handle) { + *pp_handle=NULL; + return 0; +} + /* * AffOpen */ int AffOpen(void **pp_handle, const char **pp_filename_arr, uint64_t filename_arr_len) { // We need at least one file if(filename_arr_len==0) return 1; // Open AFF file *pp_handle=(void*)af_open(pp_filename_arr[0],O_RDONLY,0); if(!*pp_handle) { // LOG_ERROR("Couldn't open AFF file!\n") return 1; } // Encrypted images aren't supported for now if(af_cannot_decrypt((AFFILE*)*p_handle)) { // LOG_ERROR("Encrypted AFF input images aren't supported yet!\n") return 1; } return 0; } /* * AffSize */ int AffSize(void *p_handle, uint64_t *p_size) { *p_size=af_seek((AFFILE*)p_handle,0,SEEK_END); // TODO: Check for error return 0; } /* * AffRead */ int AffRead(void *p_handle, uint64_t offset, unsigned char *p_buf, uint32_t count) { af_seek((AFFILE*)p_handle,offset,SEEK_SET); // TODO: Check for error if(af_read((AFFILE*)p_handle,p_buf,count)!=count) { // LOG_ERROR("Couldn't read %zd bytes from offset %" PRIu64 // "!\n",ToRead,offset); return 1; } return 0; } /* * AffClose */ int AffClose(void **pp_handle) { af_close((AFFILE*)*p_handle); // TODO: Check for error return 0; } /* * AffOptionsHelp */ const char* AffOptionsHelp() { return NULL; } /* * AffOptionsParse */ int AffOptionsParse(void *p_handle, char *p_options, char **pp_error) { return 0; } /* * AffGetInfofileContent */ int AffGetInfofileContent(void *p_handle, const char **pp_info_buf) { // TODO *pp_info_buf=NULL; return 0; } /* * AffFreeBuffer */ void AffFreeBuffer(void *p_buf) { free(p_buf); } /* ----- Change history ----- 20140724: * Initial version implementing AffOpen, AffSize, AffRead, AffClose, AffOptionsHelp, AffOptionsParse and AffFreeBuffer */ diff --git a/trunk/libxmount_input/libxmount_input_dd/libxmount_input_dd.c b/trunk/libxmount_input/libxmount_input_dd/libxmount_input_dd.c index 5513279..6634396 100644 --- a/trunk/libxmount_input/libxmount_input_dd/libxmount_input_dd.c +++ b/trunk/libxmount_input/libxmount_input_dd/libxmount_input_dd.c @@ -1,443 +1,447 @@ /******************************************************************************* * xmount Copyright (c) 2008-2013 by Gillen Daniel * * * * This module has been written by Guy Voncken. It contains the functions for * * accessing dd images. Split dd is supported as well. * * * * xmount is a small tool to "fuse mount" various harddisk image formats as dd, * * vdi, vhd or vmdk files and enable virtual write access to them. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #include #include #include #include #include #include #include "../libxmount_input.h" #include "libxmount_input_dd.h" /******************************************************************************* * Forward declarations ******************************************************************************/ +int DdInitHandle(void **pp_handle); int DdOpen(void **pp_handle, const char **pp_filename_arr, uint64_t filename_arr_len); int DdSize(void *p_handle, uint64_t *p_size); int DdRead(void *p_handle, uint64_t seek, char *p_buf, uint32_t count); int DdClose(void **pp_handle); const char* DdOptionsHelp(); int DdOptionsParse(void *p_handle, char *p_options, char **pp_error); int DdGetInfofileContent(void *p_handle, const char **pp_info_buf); void DdFreeBuffer(void *p_buf); /******************************************************************************* * LibXmount_Input API implementation ******************************************************************************/ /* * LibXmount_Input_GetApiVersion */ uint8_t LibXmount_Input_GetApiVersion() { return LIBXMOUNT_INPUT_API_VERSION; } /* * LibXmount_Input_GetSupportedFormats */ const char* LibXmount_Input_GetSupportedFormats() { return "dd\0\0"; } /* * LibXmount_Input_GetFunctions */ void LibXmount_Input_GetFunctions(ts_LibXmountInputFunctions *p_functions) { + p_functions->InitHandle=&DdInitHandle; p_functions->Open=&DdOpen; p_functions->Size=&DdSize; p_functions->Read=&DdRead; p_functions->Close=&DdClose; p_functions->OptionsHelp=&DdOptionsHelp; p_functions->OptionsParse=&DdOptionsParse; p_functions->GetInfofileContent=&DdGetInfofileContent; p_functions->FreeBuffer=&DdFreeBuffer; } /******************************************************************************* * Private ******************************************************************************/ // ---------------------- // Constant definitions // ---------------------- #define GETMAX(a,b) ((a)>(b)?(a):(b)) #define GETMIN(a,b) ((a)<(b)?(a):(b)) // --------------------- // Types and strutures // --------------------- typedef struct { char *pFilename; unsigned long long FileSize; FILE *pFile; } t_Piece, *t_pPiece; typedef struct _t_dd { t_pPiece pPieceArr; unsigned int Pieces; unsigned long long TotalSize; char *pInfo; } t_dd; // ---------------- // Error handling // ---------------- #ifdef DD_DEBUG #define CHK(ChkVal) \ { \ int ChkValRc; \ if ((ChkValRc=(ChkVal)) != DD_OK) \ { \ printf ("Err %d in %s, %d\n", ChkValRc, __FILE__, __LINE__); \ return ChkValRc; \ } \ } #define DEBUG_PRINTF(pFormat, ...) \ printf (pFormat, ##__VA_ARGS__); #else #define CHK(ChkVal) \ { \ int ChkValRc; \ if ((ChkValRc=(ChkVal)) != DD_OK) \ return ChkValRc; \ } #define DEBUG_PRINTF(...) #endif // --------------------------- // Internal static functions // --------------------------- static inline unsigned long long DdGetCurrentSeekPos (t_pPiece pPiece) { return ftello (pPiece->pFile); } -static inline int DdSetCurrentSeekPos (t_pPiece pPiece, unsigned long long Val, int Whence) +static inline int DdSetCurrentSeekPos (t_pPiece pPiece, uint64_t Val, int Whence) { if (fseeko (pPiece->pFile, Val, Whence) != 0) return DD_CANNOT_SEEK; return DD_OK; } int DdDestroyHandle (t_pdd *ppdd) { t_pdd pdd = *ppdd; t_pPiece pPiece; int CloseErrors = 0; if (pdd->pPieceArr) { for (int i=0; i < pdd->Pieces; i++) { pPiece = &pdd->pPieceArr[i]; if (pPiece->pFile) if (fclose (pPiece->pFile)) CloseErrors++; if (pPiece->pFilename) free (pPiece->pFilename); } free (pdd->pPieceArr); } if (pdd->pInfo) free (pdd->pInfo); free (pdd); *ppdd = NULL; if (CloseErrors) return DD_CANNOT_CLOSE_FILE; return DD_OK; } -static int DdCreateHandle (t_pdd *ppdd, unsigned FilenameArrLen, const char **ppFilenameArr) +static int DdCreateHandle (t_pdd pdd, unsigned FilenameArrLen, const char **ppFilenameArr) { - t_pdd pdd; t_pPiece pPiece; - *ppdd = NULL; - pdd = (t_pdd) malloc (sizeof(t_dd)); - if (pdd == NULL) - return DD_MEMALLOC_FAILED; - - memset (pdd, 0, sizeof(t_dd)); pdd->Pieces = FilenameArrLen; pdd->pPieceArr = (t_pPiece) malloc (pdd->Pieces * sizeof(t_Piece)); if (pdd->pPieceArr == NULL) { (void) DdDestroyHandle (&pdd); return DD_MEMALLOC_FAILED; } pdd->TotalSize = 0; for (int i=0; i < pdd->Pieces; i++) { pPiece = &pdd->pPieceArr[i]; pPiece->pFilename = strdup (ppFilenameArr[i]); if (pPiece->pFilename == NULL) { (void) DdDestroyHandle (&pdd); return DD_MEMALLOC_FAILED; } pPiece->pFile = fopen (pPiece->pFilename, "r"); if (pPiece->pFile == NULL) { (void) DdDestroyHandle (&pdd); return DD_FILE_OPEN_FAILED; } CHK(DdSetCurrentSeekPos(pPiece, 0, SEEK_END)) pPiece->FileSize = DdGetCurrentSeekPos (pPiece); pdd->TotalSize += pPiece->FileSize; } asprintf (&pdd->pInfo, "dd image made of %u pieces, %llu bytes in total (%0.3f GiB)", pdd->Pieces, pdd->TotalSize, pdd->TotalSize / (1024.0*1024.0*1024.0)); - *ppdd = pdd; - return DD_OK; } // --------------- // API functions // --------------- +/* + * DdInitHandle + */ +int DdInitHandle(void **pp_handle) { + t_pdd p_dd=(t_pdd)*pp_handle; + + p_dd=(t_pdd)malloc(sizeof(t_dd)); + if(p_dd==NULL) return DD_MEMALLOC_FAILED; + + memset(p_dd,0,sizeof(t_dd)); + + return DD_OK; +} + /* * DdOpen */ int DdOpen(void **pp_handle, const char **pp_filename_arr, uint64_t filename_arr_len) { - CHK(DdCreateHandle((t_pdd*)pp_handle,filename_arr_len,pp_filename_arr)) + CHK(DdCreateHandle((t_pdd)*pp_handle,filename_arr_len,pp_filename_arr)) return DD_OK; } /* * DdSize */ int DdSize(void *p_handle, uint64_t *p_size) { *p_size=((t_pdd)p_handle)->TotalSize; return DD_OK; } -/* - * DdRead0 - */ int DdRead0 (t_pdd pdd, uint64_t Seek, char *pBuffer, uint32_t *pCount) { t_pPiece pPiece; int i; // Find correct piece to read from // ------------------------------- for (i=0; iPieces; i++) { pPiece = &pdd->pPieceArr[i]; if (Seek < pPiece->FileSize) break; Seek -= pPiece->FileSize; } if (i >= pdd->Pieces) return DD_READ_BEYOND_END_OF_IMAGE; // Read from this piece // -------------------- CHK (DdSetCurrentSeekPos (pPiece, Seek, SEEK_SET)) *pCount = GETMIN (*pCount, pPiece->FileSize - Seek); if (fread (pBuffer, *pCount, 1, pPiece->pFile) != 1) return DD_CANNOT_READ_DATA; return DD_OK; } /* * DdRead */ int DdRead(void *p_handle, uint64_t seek, char *p_buf, uint32_t count) { uint32_t remaining=count; uint32_t read; if((seek+count)>((t_pdd)p_handle)->TotalSize) { return DD_READ_BEYOND_END_OF_IMAGE; } do { read=remaining; CHK(DdRead0((t_pdd)p_handle,seek,p_buf,&read)) remaining-=read; p_buf+=read; seek+=read; } while(remaining); return DD_OK; } /* * DdInfo */ int DdGetInfofileContent(void *p_handle, const char **pp_info_buf) { *pp_info_buf=((t_pdd)p_handle)->pInfo; return DD_OK; } /* * DdClose */ int DdClose(void **pp_handle) { CHK (DdDestroyHandle((t_pdd*)pp_handle)) return DD_OK; } /* * DdOptionsHelp */ const char* DdOptionsHelp() { return NULL; } /* * DdOptionsParse */ int DdOptionsParse(void *p_handle, char *p_options, char **pp_error) { return DD_OK; } /* * DdFreeBuffer */ void DdFreeBuffer(void *p_buf) { free(p_buf); } // ----------------------------------------------------- // Small main routine for testing // It a split dd file to non-split dd // ----------------------------------------------------- #ifdef DD_MAIN_FOR_TESTING int main(int argc, const char *argv[]) { t_pdd pdd; uint64_t TotalSize; uint64_t Remaining; uint64_t Read; uint64_t Pos; uint32_t BuffSize = 1024; char Buff[BuffSize]; FILE *pFile; int Percent; int PercentOld; int rc; printf ("Split DD to DD converter\n"); if (argc < 3) { printf ("Usage: %s
<...>
\n", argv[0]); exit (1); } if (DdOpen ((void**)&pdd, argc-2, &argv[1]) != DD_OK) { printf ("Cannot open split dd file\n"); exit (1); } CHK (DdSize ((void*)pdd, &TotalSize)) printf ("Total size: %llu bytes\n", TotalSize); Remaining = TotalSize; pFile = fopen (argv[argc-1], "w"); if (pFile == NULL) { printf ("Cannot open destination file\n"); exit (1); } Remaining = TotalSize; Pos = 0; PercentOld = -1; while (Remaining) { Read = GETMIN (Remaining, BuffSize); rc = DdRead ((void*)pdd, Pos, &Buff[0], Read); if (rc != DD_OK) { printf ("Error %d while calling DdRead\n", rc); exit (1); } if (fwrite (Buff, Read, 1, pFile) != 1) { printf ("Could not write to destinationfile\n"); exit (2); } Remaining -= Read; Pos += Read; Percent = (100*Pos) / TotalSize; if (Percent != PercentOld) { printf ("\r%d%% done...", Percent); PercentOld = Percent; } } if (fclose (pFile)) { printf ("Error while closing destinationfile\n"); exit (3); } printf ("\n"); return 0; } #endif diff --git a/trunk/libxmount_input/libxmount_input_ewf/libxmount_input_ewf.c b/trunk/libxmount_input/libxmount_input_ewf/libxmount_input_ewf.c index 4677370..a53bf0f 100644 --- a/trunk/libxmount_input/libxmount_input_ewf/libxmount_input_ewf.c +++ b/trunk/libxmount_input/libxmount_input_ewf/libxmount_input_ewf.c @@ -1,330 +1,339 @@ /******************************************************************************* * xmount Copyright (c) 2008-2014 by Gillen Daniel * * * * xmount is a small tool to "fuse mount" various image formats and enable * * virtual write access. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ #undef HAVE_LIBEWF_STATIC #include #include #include "../libxmount_input.h" #ifndef HAVE_LIBEWF_STATIC #include #else #include "libewf/include/libewf.h" #endif #if !defined(LIBEWF_HANDLE) // libewf version 2 no longer defines LIBEWF_HANDLE #define HAVE_LIBEWF_V2_API #endif /******************************************************************************* * Forward declarations ******************************************************************************/ +int EwfInitHandle(void **pp_handle); int EwfOpen(void **pp_handle, const char **pp_filename_arr, uint64_t filename_arr_len); int EwfSize(void *p_handle, uint64_t *p_size); int EwfRead(void *p_handle, uint64_t seek, char *p_buf, uint32_t count); int EwfClose(void **pp_handle); const char* EwfOptionsHelp(); int EwfOptionsParse(void *p_handle, char *p_options, char **pp_error); int EwfGetInfofileContent(void *p_handle, const char **pp_info_buf); void EwfFreeBuffer(void *p_buf); /******************************************************************************* * LibXmount_Input API implementation ******************************************************************************/ /* * LibXmount_Input_GetApiVersion */ uint8_t LibXmount_Input_GetApiVersion() { return LIBXMOUNT_INPUT_API_VERSION; } /* * LibXmount_Input_GetSupportedFormats */ const char* LibXmount_Input_GetSupportedFormats() { return "ewf\0\0"; } /* * LibXmount_Input_GetFunctions */ void LibXmount_Input_GetFunctions(ts_LibXmountInputFunctions *p_functions) { + p_functions->InitHandle=&EwfInitHandle; p_functions->Open=&EwfOpen; p_functions->Size=&EwfSize; p_functions->Read=&EwfRead; p_functions->Close=&EwfClose; p_functions->OptionsHelp=&EwfOptionsHelp; p_functions->OptionsParse=&EwfOptionsParse; p_functions->GetInfofileContent=&EwfGetInfofileContent; p_functions->FreeBuffer=&EwfFreeBuffer; } /******************************************************************************* * Private ******************************************************************************/ +/* + * EwfInitHandle + */ +int EwfInitHandle(void **pp_handle) { + *pp_handle=NULL; + +#ifdef HAVE_LIBEWF_V2_API + if(libewf_handle_initialize((libewf_handle_t**)pp_handle,NULL)!=1) { + // LOG_ERROR("Couldn't create EWF handle!\n") + return 1; + } +#endif + + return 0; +} + /* * EwfOpen */ int EwfOpen(void **pp_handle, const char **pp_filename_arr, uint64_t filename_arr_len) { // We need at least one file if(filename_arr_len==0) return 1; // Make sure all files are EWF files for(uint64_t i=0;i * * * * xmount is a small tool to "fuse mount" various harddisk image formats as dd, * * vdi, vhd or vmdk files and enable virtual write access to them. * * * * This program is free software: you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the Free * * Software Foundation, either version 3 of the License, or (at your option) * * any later version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * *******************************************************************************/ //#include "config.h" //#ifndef HAVE_LIBZ // #undef WITH_LIBAEWF //#endif //#define XMOUNT_LIBRARY_PATH "/usr/local/lib/xmount" #include #include #include #include #include //#include #include // For dlopen, dlclose, dlsym #include // For opendir, readdir, closedir #include #include #ifndef __APPLE__ #include #endif #include #include #include "xmount.h" #include "md5.h" /******************************************************************************* * Global vars ******************************************************************************/ // Struct that contains various runtime configuration options static ts_XmountConfData glob_xmount_cfg; // Struct containing pointers to the libxmount_input functions static pts_InputLib *glob_pp_input_libs=NULL; static uint32_t glob_input_libs_count=0; static pts_LibXmountInputFunctions glob_p_input_functions=NULL; // Handle for input image static void *glob_p_input_image=NULL; // Pointer to virtual info file static char *glob_p_info_file=NULL; // Vars needed for VDI emulation static pts_VdiFileHeader glob_p_vdi_header=NULL; static uint32_t glob_vdi_header_size=0; static char *glob_p_vdi_block_map=NULL; static uint32_t glob_p_vdi_block_map_size=0; // Vars needed for VHD emulation static ts_VhdFileHeader *glob_p_vhd_header=NULL; // Vars needed for VMDK emulation static char *glob_p_vmdk_file=NULL; static int glob_vmdk_file_size=0; static char *glob_p_vmdk_lockdir1=NULL; static char *glob_p_vmdk_lockdir2=NULL; static char *glob_p_vmdk_lockfile_data=NULL; static int glob_vmdk_lockfile_size=0; static char *glob_p_vmdk_lockfile_name=NULL; // Vars needed for virtual write access static FILE *glob_p_cache_file=NULL; static pts_CacheFileHeader glob_p_cache_header=NULL; static pts_CacheFileBlockIndex glob_p_cache_blkidx=NULL; // Mutexes to control concurrent read & write access static pthread_mutex_t glob_mutex_image_rw; static pthread_mutex_t glob_mutex_info_read; /* * LogMessage: * Print error and debug messages to stdout * * Params: * p_msg_type: "ERROR" or "DEBUG" * p_calling_fun: Name of calling function * line: Line number of call * p_msg: Message string * ...: Variable params with values to include in message string * * Returns: * n/a */ static void LogMessage(char *p_msg_type, char *p_calling_fun, int line, char *p_msg, ...) { va_list var_list; // Print message "header" printf("%s: %s.%s@%u : ",p_msg_type,p_calling_fun,XMOUNT_VERSION,line); // Print message with variable parameters va_start(var_list,p_msg); vprintf(p_msg,var_list); va_end(var_list); } /* * LogWarnMessage: * Print warning messages to stdout * * Params: * p_msg: Message string * ...: Variable params with values to include in message string * * Returns: * n/a */ static void LogWarnMessage(char *p_msg,...) { va_list var_list; // Print message "header" printf("WARNING: "); // Print message with variable parameters va_start(var_list,p_msg); vprintf(p_msg,var_list); va_end(var_list); } /* * PrintUsage: * Print usage instructions (cmdline options etc..) * * Params: * p_prog_name: Program name (argv[0]) * * Returns: * n/a */ static void PrintUsage(char *p_prog_name) { char *p_buf; int first=1; printf("\nxmount v%s copyright (c) 2008-2014 by Gillen Daniel " "\n",XMOUNT_VERSION); printf("\nUsage:\n"); printf(" %s [[fopts] [mopts]] [ [...]] \n\n",p_prog_name); printf("Options:\n"); printf(" fopts:\n"); printf(" -d : Enable FUSE's and xmount's debug mode.\n"); printf(" -h : Display this help message.\n"); printf(" -s : Run single threaded.\n"); printf(" -o no_allow_other : Disable automatic addition of FUSE's allow_other option.\n"); printf(" -o : Specify fuse mount options. Will also disable automatic\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("\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 "); // List supported input types for(uint32_t i=0;ip_supported_input_types; while(*p_buf!='\0') { if(first==1) { printf("\"%s\"",p_buf); first=0; } else printf(", \"%s\"",p_buf); p_buf+=(strlen(p_buf)+1); } } printf(".\n"); printf(" --inopts : Specify input library specific options.\n"); printf(" --info : Print out some infos about used compiler and libraries.\n"); printf(" --offset : Move the output image data start bytes into the input image.\n"); printf(" --out : Output image format. can be \"dd\", \"dmg\", \"vdi\", \"vhd\", \"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"); #ifndef __APPLE__ printf(" INFO: Input and output image type defaults to \"dd\" if not specified.\n"); #else printf(" INFO: Input image type defaults to \"dd\" and output image type defaults to \"dmg\" if not specified.\n"); #endif printf(" WARNING: Output image type \"vmdk(s)\" should be considered experimental!\n"); printf("\n"); printf(" ifile:\n"); printf(" Input image file. If your input image is split into multiple files, you have to specify them all!\n"); printf("\n"); printf(" mntp:\n"); printf(" Mount point where virtual files should be located.\n"); printf("\n"); printf(" iopts:\n"); printf(" Some input libraries might support an own set of options to configure / tune their behaviour.\n"); printf(" Input libraries supporting this feature (if any) and and their options are listed below.\n"); printf("\n"); // List input lib options first=1; for(uint32_t i=0;ilib_functions.OptionsHelp(); if(p_buf==NULL) continue; first=0; printf(" - %s\n",glob_pp_input_libs[i]->p_name); printf("%s\n",p_buf); printf("\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*)FOPEN("/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,FuseMinusOControl=TRUE,FuseAllowOther=TRUE,first; char *p_buf; // 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]) glob_xmount_cfg.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 / lib mount options if((argc+1)>i) { i++; // As the user specified the -o option, we assume he knows what he is // doing. We won't append allow_other automatically. And we allow him // to disable allow_other by passing a single "-o no_allow_other" // which won't be passed to FUSE as it is xmount specific. if(strcmp(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]) FuseMinusOControl=FALSE; } else FuseAllowOther=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(glob_xmount_cfg.pCacheFile,argv[i]) glob_xmount_cfg.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", glob_xmount_cfg.pCacheFile) } else if(strcmp(argv[i],"--in")==0) { // Specify input image type // Next parameter must be image type if((argc+1)>i) { i++; if(glob_xmount_cfg.p_orig_image_type==NULL) { XMOUNT_STRSET(glob_xmount_cfg.p_orig_image_type,argv[i]); LOG_DEBUG("Setting input image type to '%s'\n",argv[i]); } else { LOG_ERROR("You can only specify --in once!") 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],"--inopts")==0) { if((argc+1)>i) { i++; XMOUNT_STRSET(glob_xmount_cfg.p_lib_params,argv[i]); } else { LOG_ERROR("You must specify special options!\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) { glob_xmount_cfg.VirtImageType=VirtImageType_DD; LOG_DEBUG("Setting virtual image type to DD\n") } else if(strcmp(argv[i],"dmg")==0) { glob_xmount_cfg.VirtImageType=VirtImageType_DMG; LOG_DEBUG("Setting virtual image type to DMG\n") } else if(strcmp(argv[i],"vdi")==0) { glob_xmount_cfg.VirtImageType=VirtImageType_VDI; LOG_DEBUG("Setting virtual image type to VDI\n") } else if(strcmp(argv[i],"vhd")==0) { glob_xmount_cfg.VirtImageType=VirtImageType_VHD; LOG_DEBUG("Setting virtual image type to VHD\n") } else if(strcmp(argv[i],"vmdk")==0) { glob_xmount_cfg.VirtImageType=VirtImageType_VMDK; LOG_DEBUG("Setting virtual image type to VMDK\n") } else if(strcmp(argv[i],"vmdks")==0) { glob_xmount_cfg.VirtImageType=VirtImageType_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(glob_xmount_cfg.pCacheFile,argv[i]) glob_xmount_cfg.Writable=TRUE; glob_xmount_cfg.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", glob_xmount_cfg.pCacheFile) } else if(strcmp(argv[i],"--version")==0 || strcmp(argv[i],"--info")==0) { printf("xmount v%s copyright (c) 2008-2014 by Gillen Daniel " "\n\n",XMOUNT_VERSION); #ifdef __GNUC__ printf(" compile timestamp: %s %s\n",__DATE__,__TIME__); printf(" gcc version: %s\n",__VERSION__); #endif printf(" loaded input libraries:\n"); for(uint32_t ii=0;iip_name); p_buf=glob_pp_input_libs[ii]->p_supported_input_types; first=TRUE; while(*p_buf!='\0') { if(first) { printf("\"%s\"",p_buf); first=FALSE; } else printf(", \"%s\"",p_buf); p_buf+=(strlen(p_buf)+1); } printf("\n"); } printf("\n"); exit(0); } else if(strcmp(argv[i],"--offset")==0) { if((argc+1)>i) { i++; glob_xmount_cfg.orig_img_offset=strtoull(argv[i],NULL,10); } else { LOG_ERROR("You must specify an offset!\n") PrintUsage(argv[0]); exit(1); } LOG_DEBUG("Setting input image offset to \"%" PRIu64 "\"\n", glob_xmount_cfg.orig_img_offset) } else { LOG_ERROR("Unknown command line option \"%s\"\n",argv[i]); PrintUsage(argv[0]); exit(1); } } i++; } // 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); } if(FuseMinusOControl==TRUE) { // We control the -o flag, set subtype, fsname and allow_other options opts+=2; XMOUNT_REALLOC(*pppNargv,char**,opts*sizeof(char*)) XMOUNT_STRSET((*pppNargv)[opts-2],"-o") XMOUNT_STRSET((*pppNargv)[opts-1],"subtype=xmount,fsname=") XMOUNT_STRAPP((*pppNargv)[opts-1],(*pppFilenames)[0]) if(FuseAllowOther==TRUE) { // Try to add "allow_other" to FUSE's cmd-line params if(CheckFuseAllowOther()==TRUE) { XMOUNT_STRAPP((*pppNargv)[opts-1],",allow_other") } } } *pNargc=opts; return TRUE; } /* * ExtractVirtFileNames: * Extract virtual file name from input image name * * Params: * p_orig_name: Name of input image (Can include a path) * * Returns: * "TRUE" on success, "FALSE" on error */ static int ExtractVirtFileNames(char *p_orig_name) { char *tmp; // Truncate any leading path tmp=strrchr(p_orig_name,'/'); if(tmp!=NULL) p_orig_name=tmp+1; // Extract file extension tmp=strrchr(p_orig_name,'.'); // Set leading '/' XMOUNT_STRSET(glob_xmount_cfg.pVirtualImagePath,"/") XMOUNT_STRSET(glob_xmount_cfg.pVirtualImageInfoPath,"/") if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) { XMOUNT_STRSET(glob_xmount_cfg.pVirtualVmdkPath,"/") } // Copy filename if(tmp==NULL) { // Input image filename has no extension XMOUNT_STRAPP(glob_xmount_cfg.pVirtualImagePath,p_orig_name) XMOUNT_STRAPP(glob_xmount_cfg.pVirtualImageInfoPath,p_orig_name) if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) { XMOUNT_STRAPP(glob_xmount_cfg.pVirtualVmdkPath,p_orig_name) } XMOUNT_STRAPP(glob_xmount_cfg.pVirtualImageInfoPath,".info") } else { XMOUNT_STRNAPP(glob_xmount_cfg.pVirtualImagePath,p_orig_name, strlen(p_orig_name)-strlen(tmp)) XMOUNT_STRNAPP(glob_xmount_cfg.pVirtualImageInfoPath,p_orig_name, strlen(p_orig_name)-strlen(tmp)) if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) { XMOUNT_STRNAPP(glob_xmount_cfg.pVirtualVmdkPath,p_orig_name, strlen(p_orig_name)-strlen(tmp)) } XMOUNT_STRAPP(glob_xmount_cfg.pVirtualImageInfoPath,".info") } // Add virtual file extensions switch(glob_xmount_cfg.VirtImageType) { case VirtImageType_DD: XMOUNT_STRAPP(glob_xmount_cfg.pVirtualImagePath,".dd") break; case VirtImageType_DMG: XMOUNT_STRAPP(glob_xmount_cfg.pVirtualImagePath,".dmg") break; case VirtImageType_VDI: XMOUNT_STRAPP(glob_xmount_cfg.pVirtualImagePath,".vdi") break; case VirtImageType_VHD: XMOUNT_STRAPP(glob_xmount_cfg.pVirtualImagePath,".vhd") break; case VirtImageType_VMDK: case VirtImageType_VMDKS: XMOUNT_STRAPP(glob_xmount_cfg.pVirtualImagePath,".dd") XMOUNT_STRAPP(glob_xmount_cfg.pVirtualVmdkPath,".vmdk") break; default: LOG_ERROR("Unknown virtual image type!\n") return FALSE; } LOG_DEBUG("Set virtual image name to \"%s\"\n", glob_xmount_cfg.pVirtualImagePath) LOG_DEBUG("Set virtual image info name to \"%s\"\n", glob_xmount_cfg.pVirtualImageInfoPath) if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) { LOG_DEBUG("Set virtual vmdk name to \"%s\"\n", glob_xmount_cfg.pVirtualVmdkPath) } return TRUE; } /* * GetOrigImageSize: * Get size of original image * * Params: * p_size: Pointer to an uint64_t to which the size will be written to * without_offset: If set to TRUE, returns the real size without substracting * a given offset. * * Returns: * "TRUE" on success, "FALSE" on error */ static int GetOrigImageSize(uint64_t *p_size, int without_offset) { // Make sure to return correct values when dealing with only 32bit file sizes *p_size=0; // When size was already queryed, use old value rather than regetting value // from disk if(glob_xmount_cfg.OrigImageSize!=0 && !without_offset) { *p_size=glob_xmount_cfg.OrigImageSize; return TRUE; } // Get size of original image if(glob_p_input_functions->Size(glob_p_input_image,p_size)!=0) { LOG_ERROR("Unable to determine input image size\n"); return FALSE; } if(!without_offset) { // Substract given offset (*p_size)-=glob_xmount_cfg.orig_img_offset; // Save size so we have not to reget it from disk next time glob_xmount_cfg.OrigImageSize=*p_size; } return TRUE; } /* * GetVirtImageSize: * Get size of the emulated image * * Params: * p_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 *p_size) { if(glob_xmount_cfg.VirtImageSize!=0) { *p_size=glob_xmount_cfg.VirtImageSize; return TRUE; } switch(glob_xmount_cfg.VirtImageType) { case VirtImageType_DD: case VirtImageType_DMG: case VirtImageType_VMDK: case VirtImageType_VMDKS: // Virtual image is a DD, DMG or VMDK file. Just return the size of the // original image if(!GetOrigImageSize(p_size,FALSE)) { LOG_ERROR("Couldn't get size of input image!\n") return FALSE; } break; case VirtImageType_VDI: // Virtual image is a VDI file. Get size of original image and add size // of VDI header etc. if(!GetOrigImageSize(p_size,FALSE)) { LOG_ERROR("Couldn't get size of input image!\n") return FALSE; } (*p_size)+=(sizeof(ts_VdiFileHeader)+glob_p_vdi_block_map_size); break; case VirtImageType_VHD: // Virtual image is a VHD file. Get size of original image and add size // of VHD footer. if(!GetOrigImageSize(p_size,FALSE)) { LOG_ERROR("Couldn't get size of input image!\n") return FALSE; } (*p_size)+=sizeof(ts_VhdFileHeader); break; default: LOG_ERROR("Unsupported image type!\n") return FALSE; } glob_xmount_cfg.VirtImageSize=*p_size; return TRUE; } /* * GetOrigImageData: * Read data from original image * * Params: * p_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 *p_buf, off_t offset, size_t size) { size_t to_read=0; uint64_t image_size=0; // Make sure we aren't reading past EOF of image file if(!GetOrigImageSize(&image_size,FALSE)) { LOG_ERROR("Couldn't get image size!\n") return -1; } if(offset>=image_size) { // Offset is beyond image size LOG_DEBUG("Offset is beyond image size.\n") return 0; } if(offset+size>image_size) { // Attempt to read data past EOF of image file to_read=image_size-offset; LOG_DEBUG("Attempt to read data past EOF. Corrected size from %zd" " to %zd.\n",size,to_read) } else to_read=size; // Read data from image file (adding input image offset if one was specified) if(glob_p_input_functions->Read(glob_p_input_image, offset+glob_xmount_cfg.orig_img_offset, p_buf, to_read)!=0) { LOG_ERROR("Couldn't read %zd bytes from offset %" PRIu64 "!\n", to_read, offset); return -1; } return to_read; } /* * 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(glob_p_vmdk_file); if(offsetlen) { size=len-offset; LOG_DEBUG("Attempt to read past EOF of virtual vmdk file\n") } if(glob_xmount_cfg.Writable==TRUE && glob_p_cache_header->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,glob_p_vmdk_file+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: * p_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 *p_buf, off_t offset, size_t size) { uint32_t cur_block=0; uint64_t orig_image_size, virt_image_size; size_t to_read=0, cur_to_read=0; off_t file_off=offset, block_off=0; size_t to_read_later=0; // Get virtual image size if(!GetVirtImageSize(&virt_image_size)) { LOG_ERROR("Couldn't get virtual image size!\n") return -1; } if(offset>=virt_image_size) { LOG_ERROR("Attempt to read beyond virtual image EOF!\n") return -1; } if(offset+size>virt_image_size) { LOG_DEBUG("Attempt to read pas EOF of virtual image file\n") size=virt_image_size-offset; } to_read=size; if(!GetOrigImageSize(&orig_image_size,FALSE)) { LOG_ERROR("Couldn't get original image size!") return 0; } // Read virtual image type specific data preceeding original image data switch(glob_xmount_cfg.VirtImageType) { case VirtImageType_DD: case VirtImageType_DMG: case VirtImageType_VMDK: case VirtImageType_VMDKS: break; case VirtImageType_VDI: if(file_offglob_vdi_header_size) { cur_to_read=glob_vdi_header_size-file_off; } else { cur_to_read=to_read; } if(glob_xmount_cfg.Writable==TRUE && glob_p_cache_header->VdiFileHeaderCached==TRUE) { // VDI header was already cached if(fseeko(glob_p_cache_file, glob_p_cache_header->pVdiFileHeader+file_off, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to cached VDI header at offset %" PRIu64 "\n",glob_p_cache_header->pVdiFileHeader+file_off) return 0; } if(fread(p_buf,cur_to_read,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't read %zu bytes from cache file at offset %" PRIu64 "\n",cur_to_read, glob_p_cache_header->pVdiFileHeader+file_off) return 0; } LOG_DEBUG("Read %zd bytes from cached VDI header at offset %" PRIu64 " at cache file offset %" PRIu64 "\n", cur_to_read,file_off, glob_p_cache_header->pVdiFileHeader+file_off) } else { // VDI header isn't cached memcpy(p_buf,((char*)glob_p_vdi_header)+file_off,cur_to_read); LOG_DEBUG("Read %zd bytes at offset %" PRIu64 " from virtual VDI header\n",cur_to_read, file_off) } if(to_read==cur_to_read) return to_read; else { // Adjust values to read from original image to_read-=cur_to_read; p_buf+=cur_to_read; file_off=0; } } else file_off-=glob_vdi_header_size; break; case VirtImageType_VHD: // When emulating VHD, make sure the while loop below only reads data // available in the original image. Any VHD footer data must be read // afterwards. if(file_off>=orig_image_size) { to_read_later=to_read; to_read=0; } else if((file_off+to_read)>orig_image_size) { to_read_later=(file_off+to_read)-orig_image_size; to_read-=to_read_later; } break; } // Calculate block to read data from cur_block=file_off/CACHE_BLOCK_SIZE; block_off=file_off%CACHE_BLOCK_SIZE; // Read image data while(to_read!=0) { // Calculate how many bytes we have to read from this block if(block_off+to_read>CACHE_BLOCK_SIZE) { cur_to_read=CACHE_BLOCK_SIZE-block_off; } else cur_to_read=to_read; if(glob_xmount_cfg.Writable==TRUE && glob_p_cache_blkidx[cur_block].Assigned==TRUE) { // Write support enabled and need to read altered data from cachefile if(fseeko(glob_p_cache_file, glob_p_cache_blkidx[cur_block].off_data+block_off, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to offset %" PRIu64 " in cache file\n") return -1; } if(fread(p_buf,cur_to_read,1,glob_p_cache_file)!=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",cur_to_read,file_off) } else { // No write support or data not cached if(GetOrigImageData(p_buf, file_off, cur_to_read)!=cur_to_read) { 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",cur_to_read, file_off) } cur_block++; block_off=0; p_buf+=cur_to_read; to_read-=cur_to_read; file_off+=cur_to_read; } if(to_read_later!=0) { // Read virtual image type specific data following original image data switch(glob_xmount_cfg.VirtImageType) { case VirtImageType_DD: case VirtImageType_DMG: case VirtImageType_VMDK: case VirtImageType_VMDKS: case VirtImageType_VDI: break; case VirtImageType_VHD: // Micro$oft has choosen to use a footer rather then a header. if(glob_xmount_cfg.Writable==TRUE && glob_p_cache_header->VhdFileHeaderCached==TRUE) { // VHD footer was already cached if(fseeko(glob_p_cache_file, glob_p_cache_header->pVhdFileHeader+(file_off-orig_image_size), SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to cached VHD footer at offset %" PRIu64 "\n", glob_p_cache_header->pVhdFileHeader+ (file_off-orig_image_size)) return 0; } if(fread(p_buf,to_read_later,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't read %zu bytes from cache file at offset %" PRIu64 "\n",to_read_later, glob_p_cache_header->pVhdFileHeader+ (file_off-orig_image_size)) return 0; } LOG_DEBUG("Read %zd bytes from cached VHD footer at offset %" PRIu64 " at cache file offset %" PRIu64 "\n", to_read_later,(file_off-orig_image_size), glob_p_cache_header->pVhdFileHeader+(file_off-orig_image_size)) } else { // VHD header isn't cached memcpy(p_buf, ((char*)glob_p_vhd_header)+(file_off-orig_image_size), to_read_later); LOG_DEBUG("Read %zd bytes at offset %" PRIu64 " from virtual VHD header\n", to_read_later, (file_off-orig_image_size)) } break; } } return size; } /* * SetVdiFileHeaderData: * Write data to virtual VDI file header * * Params: * p_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 *p_buf,off_t offset,size_t size) { if(offset+size>glob_vdi_header_size) size=glob_vdi_header_size-offset; LOG_DEBUG("Need to cache %zu bytes at offset %" PRIu64 " from VDI header\n",size,offset) if(glob_p_cache_header->VdiFileHeaderCached==1) { // Header was already cached if(fseeko(glob_p_cache_file, glob_p_cache_header->pVdiFileHeader+offset, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to cached VDI header at address %" PRIu64 "\n",glob_p_cache_header->pVdiFileHeader+offset) return -1; } if(fwrite(p_buf,size,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" PRIu64 "\n",size, glob_p_cache_header->pVdiFileHeader+offset) return -1; } LOG_DEBUG("Wrote %zd bytes at offset %" PRIu64 " to cache file\n", size,glob_p_cache_header->pVdiFileHeader+offset) } else { // Header wasn't already cached. if(fseeko(glob_p_cache_file, 0, SEEK_END)!=0) { LOG_ERROR("Couldn't seek to end of cache file!") return -1; } glob_p_cache_header->pVdiFileHeader=ftello(glob_p_cache_file); LOG_DEBUG("Caching whole VDI header\n") if(offset>0) { // Changes do not begin at offset 0, need to prepend with data from // VDI header if(fwrite((char*)glob_p_vdi_header,offset,1,glob_p_cache_file)!=1) { LOG_ERROR("Error while writing %" PRIu64 " bytes " "to cache file at offset %" PRIu64 "!\n", offset, glob_p_cache_header->pVdiFileHeader); return -1; } LOG_DEBUG("Prepended changed data with %" PRIu64 " bytes at cache file offset %" PRIu64 "\n", offset,glob_p_cache_header->pVdiFileHeader) } // Cache changed data if(fwrite(p_buf,size,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" PRIu64 "\n",size, glob_p_cache_header->pVdiFileHeader+offset) return -1; } LOG_DEBUG("Wrote %zu bytes of changed data to cache file offset %" PRIu64 "\n",size, glob_p_cache_header->pVdiFileHeader+offset) if(offset+size!=glob_vdi_header_size) { // Need to append data from VDI header to cache whole data struct if(fwrite(((char*)glob_p_vdi_header)+offset+size, glob_vdi_header_size-(offset+size), 1, glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" PRIu64 "\n",glob_vdi_header_size-(offset+size), (uint64_t)(glob_p_cache_header->pVdiFileHeader+offset+size)) return -1; } LOG_DEBUG("Appended %" PRIu32 " bytes to changed data at cache file offset %" PRIu64 "\n",glob_vdi_header_size-(offset+size), glob_p_cache_header->pVdiFileHeader+offset+size) } // Mark header as cached and update header in cache file glob_p_cache_header->VdiFileHeaderCached=1; if(fseeko(glob_p_cache_file,0,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to offset 0 of cache file!\n") return -1; } if(fwrite((char*)glob_p_cache_header,sizeof(ts_CacheFileHeader),1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write changed cache file header!\n") return -1; } } // All important data has been written, now flush all buffers to make // sure data is written to cache file fflush(glob_p_cache_file); #ifndef __APPLE__ ioctl(fileno(glob_p_cache_file),BLKFLSBUF,0); #endif return size; } /* * SetVhdFileHeaderData: * Write data to virtual VHD file footer * * Params: * p_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 SetVhdFileHeaderData(char *p_buf,off_t offset,size_t size) { LOG_DEBUG("Need to cache %zu bytes at offset %" PRIu64 " from VHD footer\n",size,offset) if(glob_p_cache_header->VhdFileHeaderCached==1) { // Header has already been cached if(fseeko(glob_p_cache_file, glob_p_cache_header->pVhdFileHeader+offset, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to cached VHD header at address %" PRIu64 "\n",glob_p_cache_header->pVhdFileHeader+offset) return -1; } if(fwrite(p_buf,size,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" PRIu64 "\n",size, glob_p_cache_header->pVhdFileHeader+offset) return -1; } LOG_DEBUG("Wrote %zd bytes at offset %" PRIu64 " to cache file\n", size,glob_p_cache_header->pVhdFileHeader+offset) } else { // Header hasn't been cached yet. if(fseeko(glob_p_cache_file, 0, SEEK_END)!=0) { LOG_ERROR("Couldn't seek to end of cache file!") return -1; } glob_p_cache_header->pVhdFileHeader=ftello(glob_p_cache_file); LOG_DEBUG("Caching whole VHD header\n") if(offset>0) { // Changes do not begin at offset 0, need to prepend with data from // VHD header if(fwrite((char*)glob_p_vhd_header,offset,1,glob_p_cache_file)!=1) { LOG_ERROR("Error while writing %" PRIu64 " bytes " "to cache file at offset %" PRIu64 "!\n", offset, glob_p_cache_header->pVhdFileHeader); return -1; } LOG_DEBUG("Prepended changed data with %" PRIu64 " bytes at cache file offset %" PRIu64 "\n", offset,glob_p_cache_header->pVhdFileHeader) } // Cache changed data if(fwrite(p_buf,size,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" PRIu64 "\n",size, glob_p_cache_header->pVhdFileHeader+offset) return -1; } LOG_DEBUG("Wrote %zu bytes of changed data to cache file offset %" PRIu64 "\n",size, glob_p_cache_header->pVhdFileHeader+offset) if(offset+size!=sizeof(ts_VhdFileHeader)) { // Need to append data from VHD header to cache whole data struct if(fwrite(((char*)glob_p_vhd_header)+offset+size, sizeof(ts_VhdFileHeader)-(offset+size), 1, glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write %zu bytes to cache file at offset %" PRIu64 "\n",sizeof(ts_VhdFileHeader)-(offset+size), (uint64_t)(glob_p_cache_header->pVhdFileHeader+offset+size)) return -1; } LOG_DEBUG("Appended %" PRIu32 " bytes to changed data at cache file offset %" PRIu64 "\n",sizeof(ts_VhdFileHeader)-(offset+size), glob_p_cache_header->pVhdFileHeader+offset+size) } // Mark header as cached and update header in cache file glob_p_cache_header->VhdFileHeaderCached=1; if(fseeko(glob_p_cache_file,0,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to offset 0 of cache file!\n") return -1; } if(fwrite((char*)glob_p_cache_header,sizeof(ts_CacheFileHeader),1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't write changed cache file header!\n") return -1; } } // All important data has been written, now flush all buffers to make // sure data is written to cache file fflush(glob_p_cache_file); #ifndef __APPLE__ ioctl(fileno(glob_p_cache_file),BLKFLSBUF,0); #endif return size; } /* * SetVirtImageData: * Write data to virtual image * * Params: * p_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 *p_buf, off_t offset, size_t size) { uint64_t CurBlock=0; uint64_t VirtImageSize; uint64_t OrigImageSize; size_t ToWrite=0; size_t to_write_later=0; size_t CurToWrite=0; off_t FileOff=offset; off_t BlockOff=0; char *WriteBuf=(char*)p_buf; char *p_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; // Get original image size if(!GetOrigImageSize(&OrigImageSize,FALSE)) { LOG_ERROR("Couldn't get original image size!\n") return -1; } // Cache virtual image type specific data preceeding original image data switch(glob_xmount_cfg.VirtImageType) { case VirtImageType_DD: case VirtImageType_DMG: case VirtImageType_VMDK: case VirtImageType_VMDKS: break; case VirtImageType_VDI: if(FileOff=OrigImageSize) { to_write_later=ToWrite; ToWrite=0; } else if((FileOff+ToWrite)>OrigImageSize) { to_write_later=(FileOff+ToWrite)-OrigImageSize; ToWrite-=to_write_later; } break; } // Calculate block to write data to CurBlock=FileOff/CACHE_BLOCK_SIZE; BlockOff=FileOff%CACHE_BLOCK_SIZE; while(ToWrite!=0) { // Calculate how many bytes we have to write to this block if(BlockOff+ToWrite>CACHE_BLOCK_SIZE) { CurToWrite=CACHE_BLOCK_SIZE-BlockOff; } else CurToWrite=ToWrite; if(glob_p_cache_blkidx[CurBlock].Assigned==1) { // Block was already cached // Seek to data offset in cache file if(fseeko(glob_p_cache_file, glob_p_cache_blkidx[CurBlock].off_data+BlockOff, SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to cached block at address %" PRIu64 "\n", glob_p_cache_blkidx[CurBlock].off_data+BlockOff) return -1; } if(fwrite(WriteBuf,CurToWrite,1,glob_p_cache_file)!=1) { LOG_ERROR("Error while writing %zu bytes " "to cache file at offset %" PRIu64 "!\n", CurToWrite, glob_p_cache_blkidx[CurBlock].off_data+BlockOff); return -1; } LOG_DEBUG("Wrote %zd bytes at offset %" PRIu64 " to cache file\n",CurToWrite, glob_p_cache_blkidx[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(glob_p_cache_file,0,SEEK_END); glob_p_cache_blkidx[CurBlock].off_data=ftello(glob_p_cache_file); if(BlockOff!=0) { // Changed data does not begin at block boundry. Need to prepend // with data from virtual image file XMOUNT_MALLOC(p_buf2,char*,BlockOff*sizeof(char)) if(GetOrigImageData(p_buf2,FileOff-BlockOff,BlockOff)!=BlockOff) { LOG_ERROR("Couldn't read data from original image file!\n") return -1; } if(fwrite(p_buf2,BlockOff,1,glob_p_cache_file)!=1) { LOG_ERROR("Couldn't writing %" PRIu64 " bytes " "to cache file at offset %" PRIu64 "!\n", BlockOff, glob_p_cache_blkidx[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(p_buf2); } if(fwrite(WriteBuf,CurToWrite,1,glob_p_cache_file)!=1) { LOG_ERROR("Error while writing %zd bytes " "to cache file at offset %" PRIu64 "!\n", CurToWrite, glob_p_cache_blkidx[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(p_buf2,char*,(CACHE_BLOCK_SIZE- (BlockOff+CurToWrite))*sizeof(char)) memset(p_buf2,0,CACHE_BLOCK_SIZE-(BlockOff+CurToWrite)); if((FileOff-BlockOff)+CACHE_BLOCK_SIZE>OrigImageSize) { // Original image is smaller than full cache block if(GetOrigImageData(p_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(p_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(p_buf2, CACHE_BLOCK_SIZE-(BlockOff+CurToWrite), 1, glob_p_cache_file)!=1) { LOG_ERROR("Error while writing %zd bytes " "to cache file at offset %" PRIu64 "!\n", CACHE_BLOCK_SIZE-(BlockOff+CurToWrite), glob_p_cache_blkidx[CurBlock].off_data+ BlockOff+CurToWrite); return -1; } free(p_buf2); } // All important data for this cache block has been written, // flush all buffers and mark cache block as assigned fflush(glob_p_cache_file); #ifndef __APPLE__ ioctl(fileno(glob_p_cache_file),BLKFLSBUF,0); #endif glob_p_cache_blkidx[CurBlock].Assigned=1; // Update cache block index entry in cache file fseeko(glob_p_cache_file, sizeof(ts_CacheFileHeader)+(CurBlock*sizeof(ts_CacheFileBlockIndex)), SEEK_SET); if(fwrite(&(glob_p_cache_blkidx[CurBlock]), sizeof(ts_CacheFileBlockIndex), 1, glob_p_cache_file)!=1) { LOG_ERROR("Couldn't update cache file block index!\n"); return -1; } LOG_DEBUG("Updated cache file block index: Number=%" PRIu64 ", Data offset=%" PRIu64 "\n",CurBlock, glob_p_cache_blkidx[CurBlock].off_data); } // Flush buffers fflush(glob_p_cache_file); #ifndef __APPLE__ ioctl(fileno(glob_p_cache_file),BLKFLSBUF,0); #endif BlockOff=0; CurBlock++; WriteBuf+=CurToWrite; ToWrite-=CurToWrite; FileOff+=CurToWrite; } if(to_write_later!=0) { // Cache virtual image type specific data preceeding original image data switch(glob_xmount_cfg.VirtImageType) { case VirtImageType_DD: case VirtImageType_DMG: case VirtImageType_VMDK: case VirtImageType_VMDKS: case VirtImageType_VDI: break; case VirtImageType_VHD: // Micro$oft has choosen to use a footer rather then a header. ret=SetVhdFileHeaderData(WriteBuf,FileOff-OrigImageSize,to_write_later); if(ret==-1) { LOG_ERROR("Couldn't write data to virtual VHD file footer!\n") return -1; } break; } } 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 * p_stat: 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 *p_stat) { memset(p_stat,0,sizeof(struct stat)); if(strcmp(path,"/")==0) { // Attributes of mountpoint p_stat->st_mode=S_IFDIR | 0777; p_stat->st_nlink=2; } else if(strcmp(path,glob_xmount_cfg.pVirtualImagePath)==0) { // Attributes of virtual image if(!glob_xmount_cfg.Writable) p_stat->st_mode=S_IFREG | 0444; else p_stat->st_mode=S_IFREG | 0666; p_stat->st_nlink=1; // Get virtual image file size if(!GetVirtImageSize((uint64_t*)&(p_stat->st_size))) { LOG_ERROR("Couldn't get image size!\n"); return -ENOENT; } if(glob_xmount_cfg.VirtImageType==VirtImageType_VHD) { // Make sure virtual image seems to be fully allocated (not sparse file). // Without this, Windows won't attach the vhd file! p_stat->st_blocks=p_stat->st_size/512; if(p_stat->st_size%512!=0) p_stat->st_blocks++; } } else if(strcmp(path,glob_xmount_cfg.pVirtualImageInfoPath)==0) { // Attributes of virtual image info file p_stat->st_mode=S_IFREG | 0444; p_stat->st_nlink=1; // Get virtual image info file size if(glob_p_info_file!=NULL) { p_stat->st_size=strlen(glob_p_info_file); } else p_stat->st_size=0; } else if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) { // Some special files only present when emulating VMDK files if(strcmp(path,glob_xmount_cfg.pVirtualVmdkPath)==0) { // Attributes of virtual vmdk file if(!glob_xmount_cfg.Writable) p_stat->st_mode=S_IFREG | 0444; else p_stat->st_mode=S_IFREG | 0666; p_stat->st_nlink=1; // Get virtual image info file size if(glob_p_vmdk_file!=NULL) { p_stat->st_size=glob_vmdk_file_size; } else p_stat->st_size=0; } else if(glob_p_vmdk_lockdir1!=NULL && strcmp(path,glob_p_vmdk_lockdir1)==0) { p_stat->st_mode=S_IFDIR | 0777; p_stat->st_nlink=2; } else if(glob_p_vmdk_lockdir2!=NULL && strcmp(path,glob_p_vmdk_lockdir2)==0) { p_stat->st_mode=S_IFDIR | 0777; p_stat->st_nlink=2; } else if(glob_p_vmdk_lockfile_name!=NULL && strcmp(path,glob_p_vmdk_lockfile_name)==0) { p_stat->st_mode=S_IFREG | 0666; if(glob_p_vmdk_lockfile_name!=NULL) { p_stat->st_size=strlen(glob_p_vmdk_lockfile_name); } else p_stat->st_size=0; } else return -ENOENT; } else return -ENOENT; // Set uid and gid of all files to uid and gid of current process p_stat->st_uid=getuid(); p_stat->st_gid=getgid(); return 0; } /* * CreateVirtDir: * FUSE mkdir implementation * * Params: * p_path: Directory path * mode: Directory permissions * * Returns: * "0" on success, negated error code on error */ static int CreateVirtDir(const char *p_path, mode_t mode) { // Only allow creation of VMWare's lock directories if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) { if(glob_p_vmdk_lockdir1==NULL) { char aVmdkLockDir[strlen(glob_xmount_cfg.pVirtualVmdkPath)+5]; sprintf(aVmdkLockDir,"%s.lck",glob_xmount_cfg.pVirtualVmdkPath); if(strcmp(p_path,aVmdkLockDir)==0) { LOG_DEBUG("Creating virtual directory \"%s\"\n",aVmdkLockDir) XMOUNT_STRSET(glob_p_vmdk_lockdir1,aVmdkLockDir) return 0; } else { LOG_ERROR("Attempt to create illegal directory \"%s\"!\n",p_path) LOG_DEBUG("Supposed: %s\n",aVmdkLockDir) return -1; } } else if(glob_p_vmdk_lockdir2==NULL && strncmp(p_path,glob_p_vmdk_lockdir1,strlen(glob_p_vmdk_lockdir1))==0) { LOG_DEBUG("Creating virtual directory \"%s\"\n",p_path) XMOUNT_STRSET(glob_p_vmdk_lockdir2,p_path) return 0; } else { LOG_ERROR("Attempt to create illegal directory \"%s\"!\n",p_path) LOG_DEBUG("Compared to first %u chars of \"%s\"\n",strlen(glob_p_vmdk_lockdir1),glob_p_vmdk_lockdir1) return -1; } } LOG_ERROR("Attempt to create directory \"%s\" " "on read-only filesystem!\n",p_path) return -1; } /* * CreateVirtFile: * FUSE create implementation. * Only allows to create VMWare's lock file! * * Params: * p_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 *p_path, mode_t mode, dev_t dev) { if((glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) && glob_p_vmdk_lockdir1!=NULL && glob_p_vmdk_lockfile_name==NULL) { LOG_DEBUG("Creating virtual file \"%s\"\n",p_path) XMOUNT_STRSET(glob_p_vmdk_lockfile_name,p_path); return 0; } else { LOG_ERROR("Attempt to create illegal file \"%s\"\n",p_path) return -1; } } /* * GetVirtFiles: * FUSE readdir implementation * * Params: * p_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 *p_path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { (void)offset; (void)fi; if(strcmp(p_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,glob_xmount_cfg.pVirtualImagePath+1,NULL,0); filler(buf,glob_xmount_cfg.pVirtualImageInfoPath+1,NULL,0); if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) { // For VMDK's, we use an additional descriptor file filler(buf,glob_xmount_cfg.pVirtualVmdkPath+1,NULL,0); // And there could also be a lock directory if(glob_p_vmdk_lockdir1!=NULL) { filler(buf,glob_p_vmdk_lockdir1+1,NULL,0); } } } else if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) { // For VMDK emulation, there could be a lock directory if(glob_p_vmdk_lockdir1!=NULL && strcmp(p_path,glob_p_vmdk_lockdir1)==0) { filler(buf,".",NULL,0); filler(buf,"..",NULL,0); if(glob_p_vmdk_lockfile_name!=NULL) { filler(buf,glob_p_vmdk_lockfile_name+strlen(glob_p_vmdk_lockdir1)+1,NULL,0); } } else if(glob_p_vmdk_lockdir2!=NULL && strcmp(p_path,glob_p_vmdk_lockdir2)==0) { filler(buf,".",NULL,0); filler(buf,"..",NULL,0); } else return -ENOENT; } else return -ENOENT; return 0; } /* * OpenVirtFile: * FUSE open implementation * * Params: * p_path: Path to file to open * p_fi: File info struct * * Returns: * "0" on success, negated error code on error */ static int OpenVirtFile(const char *p_path, struct fuse_file_info *p_fi) { if(strcmp(p_path,glob_xmount_cfg.pVirtualImagePath)==0 || strcmp(p_path,glob_xmount_cfg.pVirtualImageInfoPath)==0) { // Check open permissions if(!glob_xmount_cfg.Writable && (p_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",p_path) return -EACCES; } return 0; } else if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) { if(strcmp(p_path,glob_xmount_cfg.pVirtualVmdkPath)==0) { // Check open permissions if(!glob_xmount_cfg.Writable && (p_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",p_path) return -EACCES; } return 0; } else if(glob_p_vmdk_lockfile_name!=NULL && strcmp(p_path,glob_p_vmdk_lockfile_name)==0) { // Check open permissions if(!glob_xmount_cfg.Writable && (p_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",p_path) return -EACCES; } return 0; } else { // Attempt to open a non existant file LOG_DEBUG("Attempt to open non existant file \"%s\".\n",p_path) return -ENOENT; } } else { // Attempt to open a non existant file LOG_DEBUG("Attempt to open non existant file \"%s\".\n",p_path) return -ENOENT; } } /* * ReadVirtFile: * FUSE read implementation * * Params: * p_path: Path (relative to mount folder) of file to read data from * p_buf: Pre-allocated buffer where read data should be 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 *p_path, char *p_buf, size_t size, off_t offset, struct fuse_file_info *fi) { uint64_t len; if(strcmp(p_path,glob_xmount_cfg.pVirtualImagePath)==0) { // Wait for other threads to end reading/writing data pthread_mutex_lock(&glob_mutex_image_rw); // Get virtual image file size if(!GetVirtImageSize(&len)) { LOG_ERROR("Couldn't get virtual image size!\n") pthread_mutex_unlock(&glob_mutex_image_rw); return 0; } if(offsetlen) size=len-offset; if(GetVirtImageData(p_buf,offset,size)!=size) { LOG_ERROR("Couldn't read data from virtual image file!\n") pthread_mutex_unlock(&glob_mutex_image_rw); return 0; } } else { LOG_DEBUG("Attempt to read past EOF of virtual image file\n"); pthread_mutex_unlock(&glob_mutex_image_rw); return 0; } // Allow other threads to read/write data again pthread_mutex_unlock(&glob_mutex_image_rw); } else if(strcmp(p_path,glob_xmount_cfg.pVirtualImageInfoPath)==0) { // Read data from virtual image info file len=strlen(glob_p_info_file); if(offsetlen) { size=len-offset; LOG_DEBUG("Attempt to read past EOF of virtual image info file\n") } pthread_mutex_lock(&glob_mutex_info_read); memcpy(p_buf,glob_p_info_file+offset,size); pthread_mutex_unlock(&glob_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(p_path,glob_xmount_cfg.pVirtualVmdkPath)==0) { // Read data from virtual vmdk file len=glob_vmdk_file_size; 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(&glob_mutex_image_rw); memcpy(p_buf,glob_p_vmdk_file+offset,size); pthread_mutex_unlock(&glob_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(glob_p_vmdk_lockfile_name!=NULL && strcmp(p_path,glob_p_vmdk_lockfile_name)==0) { // Read data from virtual lock file len=glob_vmdk_lockfile_size; 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(&glob_mutex_image_rw); memcpy(p_buf,glob_p_vmdk_lockfile_data+offset,size); pthread_mutex_unlock(&glob_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",p_path) return -ENOENT; } return size; } /* * RenameVirtFile: * FUSE rename implementation * * Params: * p_path: File to rename * p_npath: New filename * * Returns: * "0" on error, negated error code on error */ static int RenameVirtFile(const char *p_path, const char *p_npath) { if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) { if(glob_p_vmdk_lockfile_name!=NULL && strcmp(p_path,glob_p_vmdk_lockfile_name)==0) { LOG_DEBUG("Renaming virtual lock file from \"%s\" to \"%s\"\n", glob_p_vmdk_lockfile_name, p_npath) XMOUNT_REALLOC(glob_p_vmdk_lockfile_name,char*, (strlen(p_npath)+1)*sizeof(char)); strcpy(glob_p_vmdk_lockfile_name,p_npath); return 0; } } return -ENOENT; } /* * DeleteVirtDir: * FUSE rmdir implementation * * Params: * p_path: Directory to delete * * Returns: * "0" on success, negated error code on error */ static int DeleteVirtDir(const char *p_path) { // Only VMWare's lock directories can be deleted if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) { if(glob_p_vmdk_lockdir1!=NULL && strcmp(p_path,glob_p_vmdk_lockdir1)==0) { LOG_DEBUG("Deleting virtual lock dir \"%s\"\n",glob_p_vmdk_lockdir1) free(glob_p_vmdk_lockdir1); glob_p_vmdk_lockdir1=NULL; return 0; } else if(glob_p_vmdk_lockdir2!=NULL && strcmp(p_path,glob_p_vmdk_lockdir2)==0) { LOG_DEBUG("Deleting virtual lock dir \"%s\"\n",glob_p_vmdk_lockdir1) free(glob_p_vmdk_lockdir2); glob_p_vmdk_lockdir2=NULL; return 0; } } return -1; } /* * DeleteVirtFile: * FUSE unlink implementation * * Params: * p_path: File to delete * * Returns: * "0" on success, negated error code on error */ static int DeleteVirtFile(const char *p_path) { // Only VMWare's lock file can be deleted if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) { if(glob_p_vmdk_lockfile_name!=NULL && strcmp(p_path,glob_p_vmdk_lockfile_name)==0) { LOG_DEBUG("Deleting virtual file \"%s\"\n",glob_p_vmdk_lockfile_name) free(glob_p_vmdk_lockfile_name); free(glob_p_vmdk_lockfile_data); glob_p_vmdk_lockfile_name=NULL; glob_p_vmdk_lockfile_data=NULL; glob_vmdk_lockfile_size=0; return 0; } } return -1; } /* * GetVirtFsStats: * FUSE statfs implementation * * Params: * p_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 *p_path, struct statvfs *stats) { struct statvfs CacheFileFsStats; int ret; if(glob_xmount_cfg.Writable==TRUE) { // If write support is enabled, return stats of fs upon which cache file // resides in if((ret=statvfs(glob_xmount_cfg.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", glob_xmount_cfg.pCacheFile) return ret; } } else { // TODO: Return read only return 0; } } */ /* * WriteVirtFile: * FUSE write implementation * * Params: * p_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 *p_path, const char *p_buf, size_t size, off_t offset, struct fuse_file_info *fi) { uint64_t len; if(strcmp(p_path,glob_xmount_cfg.pVirtualImagePath)==0) { // Wait for other threads to end reading/writing data pthread_mutex_lock(&glob_mutex_image_rw); // Get virtual image file size if(!GetVirtImageSize(&len)) { LOG_ERROR("Couldn't get virtual image size!\n") pthread_mutex_unlock(&glob_mutex_image_rw); return 0; } if(offsetlen) size=len-offset; if(SetVirtImageData(p_buf,offset,size)!=size) { LOG_ERROR("Couldn't write data to virtual image file!\n") pthread_mutex_unlock(&glob_mutex_image_rw); return 0; } } else { LOG_DEBUG("Attempt to write past EOF of virtual image file\n") pthread_mutex_unlock(&glob_mutex_image_rw); return 0; } // Allow other threads to read/write data again pthread_mutex_unlock(&glob_mutex_image_rw); } else if(strcmp(p_path,glob_xmount_cfg.pVirtualVmdkPath)==0) { pthread_mutex_lock(&glob_mutex_image_rw); len=glob_vmdk_file_size; if((offset+size)>len) { // Enlarge or create buffer if needed if(len==0) { len=offset+size; XMOUNT_MALLOC(glob_p_vmdk_file,char*,len*sizeof(char)) } else { len=offset+size; XMOUNT_REALLOC(glob_p_vmdk_file,char*,len*sizeof(char)) } glob_vmdk_file_size=offset+size; } // Copy data to buffer memcpy(glob_p_vmdk_file+offset,p_buf,size); pthread_mutex_unlock(&glob_mutex_image_rw); } else if(glob_p_vmdk_lockfile_name!=NULL && strcmp(p_path,glob_p_vmdk_lockfile_name)==0) { pthread_mutex_lock(&glob_mutex_image_rw); if((offset+size)>glob_vmdk_lockfile_size) { // Enlarge or create buffer if needed if(glob_vmdk_lockfile_size==0) { glob_vmdk_lockfile_size=offset+size; XMOUNT_MALLOC(glob_p_vmdk_lockfile_data,char*, glob_vmdk_lockfile_size*sizeof(char)) } else { glob_vmdk_lockfile_size=offset+size; XMOUNT_REALLOC(glob_p_vmdk_lockfile_data,char*, glob_vmdk_lockfile_size*sizeof(char)) } } // Copy data to buffer memcpy(glob_p_vmdk_lockfile_data+offset,p_buf,size); pthread_mutex_unlock(&glob_mutex_image_rw); } else if(strcmp(p_path,glob_xmount_cfg.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",p_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 *p_buf; XMOUNT_MALLOC(p_buf,char*,HASH_AMOUNT*sizeof(char)) size_t read_data=GetOrigImageData(p_buf,0,HASH_AMOUNT); if(read_data>0) { // Calculate MD5 hash md5_init(&md5_state); md5_append(&md5_state,(const md5_byte_t*)p_buf,HASH_AMOUNT); md5_finish(&md5_state,(md5_byte_t*)hash); // Convert MD5 hash into two 64bit integers *pHashLow=*((uint64_t*)hash); *pHashHigh=*((uint64_t*)(hash+8)); free(p_buf); return TRUE; } else { LOG_ERROR("Couldn't read data from original image file!\n") free(p_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,FALSE)) { 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++; glob_p_vdi_block_map_size=BlockEntries*sizeof(uint32_t); LOG_DEBUG("BlockMap: %d (%08X) entries, %d (%08X) bytes!\n", BlockEntries, BlockEntries, glob_p_vdi_block_map_size, glob_p_vdi_block_map_size) // Allocate memory for vdi header and block map glob_vdi_header_size=sizeof(ts_VdiFileHeader)+glob_p_vdi_block_map_size; XMOUNT_MALLOC(glob_p_vdi_header,pts_VdiFileHeader,glob_vdi_header_size) memset(glob_p_vdi_header,0,glob_vdi_header_size); glob_p_vdi_block_map=((void*)glob_p_vdi_header)+sizeof(ts_VdiFileHeader); // Init header values strncpy(glob_p_vdi_header->szFileInfo,VDI_FILE_COMMENT, strlen(VDI_FILE_COMMENT)+1); glob_p_vdi_header->u32Signature=VDI_IMAGE_SIGNATURE; glob_p_vdi_header->u32Version=VDI_IMAGE_VERSION; glob_p_vdi_header->cbHeader=0x00000180; // No idea what this is for! Testimage had same value glob_p_vdi_header->u32Type=VDI_IMAGE_TYPE_FIXED; glob_p_vdi_header->fFlags=VDI_IMAGE_FLAGS; strncpy(glob_p_vdi_header->szComment,VDI_HEADER_COMMENT, strlen(VDI_HEADER_COMMENT)+1); glob_p_vdi_header->offData=glob_vdi_header_size; glob_p_vdi_header->offBlocks=sizeof(ts_VdiFileHeader); glob_p_vdi_header->cCylinders=0; // Legacy info glob_p_vdi_header->cHeads=0; // Legacy info glob_p_vdi_header->cSectors=0; // Legacy info glob_p_vdi_header->cbSector=512; // Legacy info glob_p_vdi_header->u32Dummy=0; glob_p_vdi_header->cbDisk=ImageSize; // Seems as VBox is always using a 1MB blocksize glob_p_vdi_header->cbBlock=VDI_IMAGE_BLOCK_SIZE; glob_p_vdi_header->cbBlockExtra=0; glob_p_vdi_header->cBlocks=BlockEntries; glob_p_vdi_header->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. glob_p_vdi_header->uuidCreate_l=glob_xmount_cfg.InputHashLo; glob_p_vdi_header->uuidCreate_h=glob_xmount_cfg.InputHashHi; //*((uint32_t*)(&(glob_p_vdi_header->uuidCreate_l)))=rand(); //*((uint32_t*)(&(glob_p_vdi_header->uuidCreate_l))+4)=rand(); //*((uint32_t*)(&(glob_p_vdi_header->uuidCreate_h)))=rand(); //*((uint32_t*)(&(glob_p_vdi_header->uuidCreate_h))+4)=rand(); #define rand64(var) { \ *((uint32_t*)&(var))=rand(); \ *(((uint32_t*)&(var))+1)=rand(); \ } rand64(glob_p_vdi_header->uuidModify_l); rand64(glob_p_vdi_header->uuidModify_h); #undef rand64 // Generate block map i=0; for(offset=0;offsetcookie=VHD_IMAGE_HVAL_COOKIE; glob_p_vhd_header->features=VHD_IMAGE_HVAL_FEATURES; glob_p_vhd_header->file_format_version=VHD_IMAGE_HVAL_FILE_FORMAT_VERSION; glob_p_vhd_header->data_offset=VHD_IMAGE_HVAL_DATA_OFFSET; glob_p_vhd_header->creation_time=htobe32(time(NULL)- VHD_IMAGE_TIME_CONVERSION_OFFSET); glob_p_vhd_header->creator_app=VHD_IMAGE_HVAL_CREATOR_APPLICATION; glob_p_vhd_header->creator_ver=VHD_IMAGE_HVAL_CREATOR_VERSION; glob_p_vhd_header->creator_os=VHD_IMAGE_HVAL_CREATOR_HOST_OS; glob_p_vhd_header->size_original=htobe64(orig_image_size); glob_p_vhd_header->size_current=glob_p_vhd_header->size_original; // Convert size to sectors if(orig_image_size>136899993600) { // image is larger then CHS values can address. // Set sectors to max (C65535*H16*S255). geom_tot_s=267382800; } else { // Calculate actual sectors geom_tot_s=orig_image_size/512; if((orig_image_size%512)!=0) geom_tot_s++; } // Calculate CHS values. This is done according to the VHD specs if(geom_tot_s>=66059280) { // C65535 * H16 * S63 geom_s=255; geom_h=16; geom_c_x_h=geom_tot_s/geom_s; } else { geom_s=17; geom_c_x_h=geom_tot_s/geom_s; geom_h=(geom_c_x_h+1023)/1024; if(geom_h<4) geom_h=4; if(geom_c_x_h>=(geom_h*1024) || geom_h>16) { geom_s=31; geom_h=16; geom_c_x_h=geom_tot_s/geom_s; } if(geom_c_x_h>=(geom_h*1024)) { geom_s=63; geom_h=16; geom_c_x_h=geom_tot_s/geom_s; } } geom_c=geom_c_x_h/geom_h; glob_p_vhd_header->disk_geometry_c=htobe16(geom_c); glob_p_vhd_header->disk_geometry_h=geom_h; glob_p_vhd_header->disk_geometry_s=geom_s; glob_p_vhd_header->disk_type=VHD_IMAGE_HVAL_DISK_TYPE; glob_p_vhd_header->uuid_l=glob_xmount_cfg.InputHashLo; glob_p_vhd_header->uuid_h=glob_xmount_cfg.InputHashHi; glob_p_vhd_header->saved_state=0x00; // Calculate footer checksum for(i=0;ichecksum=htobe32(~checksum); LOG_DEBUG("VHD header size = %u\n",sizeof(ts_VhdFileHeader)); return TRUE; } /* * InitVirtualVmdkFile: * Init the virtual VMDK file * * Params: * n/a * * Returns: * "TRUE" on success, "FALSE" on error */ static int InitVirtualVmdkFile() { uint64_t ImageSize=0; uint64_t ImageBlocks=0; char buf[500]; // Get original image size if(!GetOrigImageSize(&ImageSize,FALSE)) { LOG_ERROR("Couldn't get original image size!\n") return FALSE; } ImageBlocks=ImageSize/512; if(ImageSize%512!=0) ImageBlocks++; #define VMDK_DESC_FILE "# Disk DescriptorFile\n" \ "version=1\n" \ "CID=fffffffe\n" \ "parentCID=ffffffff\n" \ "createType=\"monolithicFlat\"\n\n" \ "# Extent description\n" \ "RW %" PRIu64 " FLAT \"%s\" 0\n\n" \ "# The Disk Data Base\n" \ "#DDB\n" \ "ddb.virtualHWVersion = \"3\"\n" \ "ddb.adapterType = \"%s\"\n" \ "ddb.geometry.cylinders = \"0\"\n" \ "ddb.geometry.heads = \"0\"\n" \ "ddb.geometry.sectors = \"0\"\n" if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK) { // VMDK with IDE bus sprintf(buf, VMDK_DESC_FILE, ImageBlocks, (glob_xmount_cfg.pVirtualImagePath)+1, "ide"); } else if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS){ // VMDK with SCSI bus sprintf(buf, VMDK_DESC_FILE, ImageBlocks, (glob_xmount_cfg.pVirtualImagePath)+1, "scsi"); } else { LOG_ERROR("Unknown virtual VMDK file format!\n") return FALSE; } #undef VMDK_DESC_FILE // Do not use XMOUNT_STRSET here to avoid adding '\0' to the buffer! XMOUNT_MALLOC(glob_p_vmdk_file,char*,strlen(buf)) strncpy(glob_p_vmdk_file,buf,strlen(buf)); glob_vmdk_file_size=strlen(buf); return TRUE; } /* * InitVirtImageInfoFile: * Create virtual image info file * * Params: * n/a * * Returns: * "TRUE" on success, "FALSE" on error */ static int InitVirtImageInfoFile() { // char buf[200]; // int ret; // Add static header to file XMOUNT_MALLOC(glob_p_info_file,char*,(strlen(IMAGE_INFO_HEADER)+1)) strncpy(glob_p_info_file,IMAGE_INFO_HEADER,strlen(IMAGE_INFO_HEADER)+1); // TODO /* switch(glob_xmount_cfg.OrigImageType) { case TOrigImageType_DD: // Original image is a DD file. There isn't much info to extract. Perhaps // just add image size // TODO: Add infos to virtual image info file break; #ifdef WITH_LIBEWF #define M_SAVE_VALUE(DESC,SHORT_DESC) { \ if(ret==1) { \ XMOUNT_REALLOC(glob_p_info_file,char*, \ (strlen(glob_p_info_file)+strlen(buf)+strlen(DESC)+2)) \ strncpy((glob_p_info_file+strlen(glob_p_info_file)),DESC,strlen(DESC)+1); \ strncpy((glob_p_info_file+strlen(glob_p_info_file)),buf,strlen(buf)+1); \ strncpy((glob_p_info_file+strlen(glob_p_info_file)),"\n",2); \ } else if(ret==-1) { \ LOG_WARNING("Couldn't query EWF image header value '%s'\n",SHORT_DESC) \ } \ } case TOrigImageType_EWF: // Original image is an EWF file. Extract various infos from ewf file and // add them to the virtual image info file content. #if defined( HAVE_LIBEWF_V2_API ) ret=libewf_handle_get_utf8_header_value_case_number(hEwfFile,buf,sizeof(buf),NULL); M_SAVE_VALUE("Case number: ","Case number") ret=libewf_handle_get_utf8_header_value_description(hEwfFile,buf,sizeof(buf),NULL); M_SAVE_VALUE("Description: ","Description") ret=libewf_handle_get_utf8_header_value_examiner_name(hEwfFile,buf,sizeof(buf),NULL); M_SAVE_VALUE("Examiner: ","Examiner") ret=libewf_handle_get_utf8_header_value_evidence_number(hEwfFile,buf,sizeof(buf),NULL); M_SAVE_VALUE("Evidence number: ","Evidence number") ret=libewf_handle_get_utf8_header_value_notes(hEwfFile,buf,sizeof(buf),NULL); M_SAVE_VALUE("Notes: ","Notes") ret=libewf_handle_get_utf8_header_value_acquiry_date(hEwfFile,buf,sizeof(buf),NULL); M_SAVE_VALUE("Acquiry date: ","Acquiry date") ret=libewf_handle_get_utf8_header_value_system_date(hEwfFile,buf,sizeof(buf),NULL); M_SAVE_VALUE("System date: ","System date") ret=libewf_handle_get_utf8_header_value_acquiry_operating_system(hEwfFile,buf,sizeof(buf),NULL); M_SAVE_VALUE("Acquiry os: ","Acquiry os") ret=libewf_handle_get_utf8_header_value_acquiry_software_version(hEwfFile,buf,sizeof(buf),NULL); M_SAVE_VALUE("Acquiry sw version: ","Acquiry sw version") ret=libewf_handle_get_utf8_hash_value_md5(hEwfFile,buf,sizeof(buf),NULL); M_SAVE_VALUE("MD5 hash: ","MD5 hash") ret=libewf_handle_get_utf8_hash_value_sha1(hEwfFile,buf,sizeof(buf),NULL); M_SAVE_VALUE("SHA1 hash: ","SHA1 hash") #else ret=libewf_get_header_value_case_number(hEwfFile,buf,sizeof(buf)); M_SAVE_VALUE("Case number: ","Case number") ret=libewf_get_header_value_description(hEwfFile,buf,sizeof(buf)); M_SAVE_VALUE("Description: ","Description") ret=libewf_get_header_value_examiner_name(hEwfFile,buf,sizeof(buf)); M_SAVE_VALUE("Examiner: ","Examiner") ret=libewf_get_header_value_evidence_number(hEwfFile,buf,sizeof(buf)); M_SAVE_VALUE("Evidence number: ","Evidence number") ret=libewf_get_header_value_notes(hEwfFile,buf,sizeof(buf)); M_SAVE_VALUE("Notes: ","Notes") ret=libewf_get_header_value_acquiry_date(hEwfFile,buf,sizeof(buf)); M_SAVE_VALUE("Acquiry date: ","Acquiry date") ret=libewf_get_header_value_system_date(hEwfFile,buf,sizeof(buf)); M_SAVE_VALUE("System date: ","System date") ret=libewf_get_header_value_acquiry_operating_system(hEwfFile,buf,sizeof(buf)); M_SAVE_VALUE("Acquiry os: ","Acquiry os") ret=libewf_get_header_value_acquiry_software_version(hEwfFile,buf,sizeof(buf)); M_SAVE_VALUE("Acquiry sw version: ","Acquiry sw version") ret=libewf_get_hash_value_md5(hEwfFile,buf,sizeof(buf)); M_SAVE_VALUE("MD5 hash: ","MD5 hash") ret=libewf_get_hash_value_sha1(hEwfFile,buf,sizeof(buf)); M_SAVE_VALUE("SHA1 hash: ","SHA1 hash") #endif break; #undef M_SAVE_VALUE #endif #ifdef WITH_LIBAEWF case TOrigImageType_AEWF: if((ret=AewfInfo(hAewfFile,(const char**)&glob_p_info_file))!=AEWF_OK) { LOG_ERROR("Unable to get EWF image infos using AewfInfo. Return code %d!\n",ret) return FALSE; } break; #endif #ifdef WITH_LIBAFF case TOrigImageType_AFF: // TODO: Extract some infos from AFF file to add to our info file break; #endif #ifdef WITH_LIBAAFF case TOrigImageType_AAFF: if((ret=AaffInfo(hAaffFile,&glob_p_info_file))!=AAFF_OK) { LOG_ERROR("Unable to get AAF image infos using AaffInfo. Return code %d!\n",ret) return FALSE; } break; #endif default: LOG_ERROR("Unsupported input image type!\n") return FALSE; } */ return TRUE; } /* * InitCacheFile: * Create / load cache file to enable virtual write support * * Params: * n/a * * Returns: * "TRUE" on success, "FALSE" on error */ static int InitCacheFile() { uint64_t ImageSize=0; uint64_t BlockIndexSize=0; uint64_t CacheFileHeaderSize=0; uint64_t CacheFileSize=0; uint32_t NeededBlocks=0; uint64_t buf; if(!glob_xmount_cfg.OverwriteCache) { // Try to open an existing cache file or create a new one glob_p_cache_file=(FILE*)FOPEN(glob_xmount_cfg.pCacheFile,"rb+"); if(glob_p_cache_file==NULL) { // As the c lib seems to have no possibility to open a file rw wether it // exists or not (w+ does not work because it truncates an existing file), // when r+ returns NULL the file could simply not exist LOG_DEBUG("Cache file does not exist. Creating new one\n") glob_p_cache_file=(FILE*)FOPEN(glob_xmount_cfg.pCacheFile,"wb+"); if(glob_p_cache_file==NULL) { // There is really a problem opening the file LOG_ERROR("Couldn't open cache file \"%s\"!\n", glob_xmount_cfg.pCacheFile) return FALSE; } } } else { // Overwrite existing cache file or create a new one glob_p_cache_file=(FILE*)FOPEN(glob_xmount_cfg.pCacheFile,"wb+"); if(glob_p_cache_file==NULL) { LOG_ERROR("Couldn't open cache file \"%s\"!\n", glob_xmount_cfg.pCacheFile) return FALSE; } } // Get input image size if(!GetOrigImageSize(&ImageSize,FALSE)) { LOG_ERROR("Couldn't get input image size!\n") return FALSE; } // Calculate how many blocks are needed and how big the buffers must be // for the actual cache file version NeededBlocks=ImageSize/CACHE_BLOCK_SIZE; if((ImageSize%CACHE_BLOCK_SIZE)!=0) NeededBlocks++; BlockIndexSize=NeededBlocks*sizeof(ts_CacheFileBlockIndex); CacheFileHeaderSize=sizeof(ts_CacheFileHeader)+BlockIndexSize; LOG_DEBUG("Cache blocks: %u (%04X) entries, %zd (%08zX) bytes\n", NeededBlocks, NeededBlocks, BlockIndexSize, BlockIndexSize) // Get cache file size // fseeko64 had massive problems! if(fseeko(glob_p_cache_file,0,SEEK_END)!=0) { LOG_ERROR("Couldn't seek to end of cache file!\n") return FALSE; } // Same here, ftello64 didn't work at all and returned 0 all the times CacheFileSize=ftello(glob_p_cache_file); LOG_DEBUG("Cache file has %zd bytes\n",CacheFileSize) if(CacheFileSize>0) { // Cache file isn't empty, parse block header LOG_DEBUG("Cache file not empty. Parsing block header\n") if(fseeko(glob_p_cache_file,0,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to beginning of cache file!\n") return FALSE; } // Read and check file signature if(fread(&buf,8,1,glob_p_cache_file)!=1 || buf!=CACHE_FILE_SIGNATURE) { free(glob_p_cache_header); LOG_ERROR("Not an xmount cache file or cache file corrupt!\n") return FALSE; } // Now get cache file version (Has only 32bit!) if(fread(&buf,4,1,glob_p_cache_file)!=1) { free(glob_p_cache_header); LOG_ERROR("Not an xmount cache file or cache file corrupt!\n") return FALSE; } switch((uint32_t)buf) { case 0x00000001: // Old v1 cache file. LOG_ERROR("Unsupported cache file version!\n") LOG_ERROR("Please use xmount-tool to upgrade your cache file.\n") return FALSE; case CUR_CACHE_FILE_VERSION: // Current version if(fseeko(glob_p_cache_file,0,SEEK_SET)!=0) { LOG_ERROR("Couldn't seek to beginning of cache file!\n") return FALSE; } // Alloc memory for header and block index XMOUNT_MALLOC(glob_p_cache_header,pts_CacheFileHeader,CacheFileHeaderSize) memset(glob_p_cache_header,0,CacheFileHeaderSize); // Read header and block index from file if(fread(glob_p_cache_header,CacheFileHeaderSize,1,glob_p_cache_file)!=1) { // Cache file isn't big enough free(glob_p_cache_header); LOG_ERROR("Cache file corrupt!\n") return FALSE; } break; default: LOG_ERROR("Unknown cache file version!\n") return FALSE; } // Check if cache file has same block size as we do if(glob_p_cache_header->BlockSize!=CACHE_BLOCK_SIZE) { LOG_ERROR("Cache file does not use default cache block size!\n") return FALSE; } // Set pointer to block index glob_p_cache_blkidx=(pts_CacheFileBlockIndex)((void*)glob_p_cache_header+ glob_p_cache_header->pBlockIndex); } else { // New cache file, generate a new block header LOG_DEBUG("Cache file is empty. Generating new block header\n"); // Alloc memory for header and block index XMOUNT_MALLOC(glob_p_cache_header,pts_CacheFileHeader,CacheFileHeaderSize) memset(glob_p_cache_header,0,CacheFileHeaderSize); glob_p_cache_header->FileSignature=CACHE_FILE_SIGNATURE; glob_p_cache_header->CacheFileVersion=CUR_CACHE_FILE_VERSION; glob_p_cache_header->BlockSize=CACHE_BLOCK_SIZE; glob_p_cache_header->BlockCount=NeededBlocks; //glob_p_cache_header->UsedBlocks=0; // The following pointer is only usuable when reading data from cache file glob_p_cache_header->pBlockIndex=sizeof(ts_CacheFileHeader); glob_p_cache_blkidx=(pts_CacheFileBlockIndex)((void*)glob_p_cache_header+ sizeof(ts_CacheFileHeader)); glob_p_cache_header->VdiFileHeaderCached=FALSE; glob_p_cache_header->pVdiFileHeader=0; glob_p_cache_header->VmdkFileCached=FALSE; glob_p_cache_header->VmdkFileSize=0; glob_p_cache_header->pVmdkFile=0; glob_p_cache_header->VhdFileHeaderCached=FALSE; glob_p_cache_header->pVhdFileHeader=0; // Write header to file if(fwrite(glob_p_cache_header,CacheFileHeaderSize,1,glob_p_cache_file)!=1) { free(glob_p_cache_header); LOG_ERROR("Couldn't write cache file header to file!\n"); return FALSE; } } return TRUE; } /* * LoadInputLibs */ static int LoadInputLibs() { DIR *p_dir=NULL; struct dirent *p_dirent=NULL; int base_library_path_len=0; char *p_library_path=NULL; void *p_libxmount_in=NULL; t_LibXmount_Input_GetApiVersion pfun_GetApiVersion; t_LibXmount_Input_GetSupportedFormats pfun_GetSupportedFormats; t_LibXmount_Input_GetFunctions pfun_GetFunctions; const char *p_supported_formats=NULL; const char *p_buf; uint32_t supported_formats_len=0; pts_InputLib p_input_lib=NULL; LOG_DEBUG("Searching for input libraries in '%s'.\n", XMOUNT_LIBRARY_PATH); // Open lib dir p_dir=opendir(XMOUNT_LIBRARY_PATH); if(p_dir==NULL) { LOG_ERROR("Unable to access xmount library directory '%s'!\n", XMOUNT_LIBRARY_PATH); return FALSE; } // Construct base library path base_library_path_len=strlen(XMOUNT_LIBRARY_PATH); XMOUNT_STRSET(p_library_path,XMOUNT_LIBRARY_PATH); if(XMOUNT_LIBRARY_PATH[base_library_path_len]!='/') { base_library_path_len++; XMOUNT_STRAPP(p_library_path,"/"); } // Loop over lib dir while((p_dirent=readdir(p_dir))!=NULL) { if(strncmp(p_dirent->d_name,"libxmount_input_",16)!=0) { LOG_DEBUG("Ignoring '%s'.\n",p_dirent->d_name); continue; } LOG_DEBUG("Trying to load '%s'\n",p_dirent->d_name); // Found an input lib, construct full path to it and load it p_library_path=realloc(p_library_path, base_library_path_len+strlen(p_dirent->d_name)+1); if(p_library_path==NULL) { LOG_ERROR("Couldn't allocate memmory!\n"); exit(1); } strcpy(p_library_path+base_library_path_len,p_dirent->d_name); p_libxmount_in=dlopen(p_library_path,RTLD_NOW); if(p_libxmount_in==NULL) { LOG_ERROR("Unable to load input library '%s'!\n",p_library_path); LOG_DEBUG("DLOPEN returned '%s'.\n",dlerror()); continue; } // Load library symbols #define LIBXMOUNT_LOAD_SYMBOL(name,pfun) { \ if((pfun=dlsym(p_libxmount_in,name))==NULL) { \ LOG_ERROR("Unable to load symbol '%s' from library '%s'!\n", \ name, \ p_library_path); \ dlclose(p_libxmount_in); \ p_libxmount_in=NULL; \ continue; \ } \ } LIBXMOUNT_LOAD_SYMBOL("LibXmount_Input_GetApiVersion",pfun_GetApiVersion); LIBXMOUNT_LOAD_SYMBOL("LibXmount_Input_GetSupportedFormats", pfun_GetSupportedFormats); LIBXMOUNT_LOAD_SYMBOL("LibXmount_Input_GetFunctions",pfun_GetFunctions); #undef LIBXMOUNT_LOAD_SYMBOL // Check library's API version if(pfun_GetApiVersion()!=LIBXMOUNT_INPUT_API_VERSION) { LOG_DEBUG("Failed! Wrong API version.\n"); LOG_ERROR("Unable to load input library '%s'. Wrong API version\n", p_library_path); dlclose(p_libxmount_in); continue; } // Construct new entry for our library list XMOUNT_MALLOC(p_input_lib,pts_InputLib,sizeof(ts_InputLib)); XMOUNT_STRSET(p_input_lib->p_name,p_dirent->d_name); p_input_lib->p_lib=p_libxmount_in; p_supported_formats=pfun_GetSupportedFormats(); supported_formats_len=0; p_buf=p_supported_formats; while(*p_buf!='\0') { supported_formats_len+=(strlen(p_buf)+1); p_buf+=(strlen(p_buf)+1); } supported_formats_len++; XMOUNT_MALLOC(p_input_lib->p_supported_input_types, char*, supported_formats_len); memcpy(p_input_lib->p_supported_input_types, p_supported_formats, supported_formats_len); // TODO: Maybe check if all functions are available pfun_GetFunctions(&(p_input_lib->lib_functions)); // Add entry to our input library list XMOUNT_REALLOC(glob_pp_input_libs, pts_InputLib*, sizeof(pts_InputLib)*(glob_input_libs_count+1)); glob_pp_input_libs[glob_input_libs_count++]=p_input_lib; LOG_DEBUG("%s loaded successfully\n",p_dirent->d_name); } LOG_DEBUG("A total of %u input libs were loaded.\n",glob_input_libs_count); free(p_library_path); closedir(p_dir); return (glob_input_libs_count>0 ? TRUE : FALSE); } /* * UnloadInputLibs */ static void UnloadInputLibs() { LOG_DEBUG("Unloading all input libs.\n"); for(uint32_t i=0;ip_name); dlclose(glob_pp_input_libs[i]->p_lib); free(glob_pp_input_libs[i]->p_supported_input_types); free(glob_pp_input_libs[i]); } free(glob_pp_input_libs); glob_pp_input_libs=NULL; glob_input_libs_count=0; } /* * FindInputLib */ static int FindInputLib() { char *p_buf; LOG_DEBUG("Trying to find suitable library for input type '%s'.\n", glob_xmount_cfg.p_orig_image_type); // Loop over all loaded libs for(uint32_t i=0;ip_name); p_buf=glob_pp_input_libs[i]->p_supported_input_types; while(*p_buf!='\0') { if(strcmp(p_buf,glob_xmount_cfg.p_orig_image_type)==0) { // Library supports input type, set lib functions LOG_DEBUG("Input library '%s' pretends to handle that input type.\n", glob_pp_input_libs[i]->p_name); glob_p_input_functions=&(glob_pp_input_libs[i]->lib_functions); return TRUE; } p_buf+=(strlen(p_buf)+1); } } LOG_DEBUG("Couldn't find any suitable library.\n"); // No library supporting input type found return FALSE; } /* * 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[]) -{ +int main(int argc, char *argv[]) { char **ppInputFilenames=NULL; int InputFilenameCount=0; int nargc=0; char **ppNargv=NULL; char *pMountpoint=NULL; int ret=1; + char *p_err_msg; + // Disable std output / std input buffering setbuf(stdout,NULL); setbuf(stderr,NULL); // Init glob_xmount_cfg glob_xmount_cfg.p_orig_image_type=NULL; #ifndef __APPLE__ glob_xmount_cfg.VirtImageType=VirtImageType_DD; #else glob_xmount_cfg.VirtImageType=VirtImageType_DMG; #endif glob_xmount_cfg.Debug=FALSE; glob_xmount_cfg.pVirtualImagePath=NULL; glob_xmount_cfg.pVirtualVmdkPath=NULL; glob_xmount_cfg.pVirtualImageInfoPath=NULL; glob_xmount_cfg.Writable=FALSE; glob_xmount_cfg.OverwriteCache=FALSE; glob_xmount_cfg.pCacheFile=NULL; glob_xmount_cfg.OrigImageSize=0; glob_xmount_cfg.VirtImageSize=0; glob_xmount_cfg.InputHashLo=0; glob_xmount_cfg.InputHashHi=0; glob_xmount_cfg.orig_img_offset=0; glob_xmount_cfg.p_lib_params=NULL; // Load input libs if(!LoadInputLibs()) { LOG_ERROR("Unable to load any input libraries!\n") return 1; } // Parse command line options if(!ParseCmdLine(argc, argv, &nargc, &ppNargv, &InputFilenameCount, &ppInputFilenames, &pMountpoint)) { LOG_ERROR("Error parsing command line options!\n") //PrintUsage(argv[0]); UnloadInputLibs(); 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]); UnloadInputLibs(); return 1; } + // TODO: Check if mountpoint is a valid dir + // If no input type was specified, default to "dd" if(glob_xmount_cfg.p_orig_image_type==NULL) { XMOUNT_STRSET(glob_xmount_cfg.p_orig_image_type,"dd"); } // Find an input lib for the specified input type if(!FindInputLib()) { LOG_ERROR("Unknown input image type \"%s\"!\n", glob_xmount_cfg.p_orig_image_type) PrintUsage(argv[0]); UnloadInputLibs(); return 1; } -/* + // Init input image handle + if(glob_p_input_functions->InitHandle(&glob_p_input_image)!=0) { + LOG_ERROR("Unable to init input handle!\n"); + return 1; + } + // Parse input lib specific options if(lob_xmount_cfg.p_lib_params!=NULL) { - glob_p_input_functions->OptionsParse( + if(glob_p_input_functions->OptionsParse(glob_p_input_image, + glob_xmount_cfg.p_lib_params, + &p_err_msg)!=0) + { + if(p_err_msg!=NULL) { + LOG_ERROR("Unable to parse input library specific options: %s!\n", + p_err_msg); + glob_p_input_functions->FreeBuffer(p_err_msg); + } else { + LOG_ERROR("Unable to parse input library specific options!\n"); + } + } } -*/ if(glob_xmount_cfg.Debug==TRUE) { LOG_DEBUG("Options passed to FUSE: ") for(int i=0;iOpen(&glob_p_input_image, (const char**)ppInputFilenames, InputFilenameCount)!=0) { LOG_ERROR("Unable to open input image file!"); UnloadInputLibs(); return 1; } LOG_DEBUG("Input image file opened successfully\n") // If an offset was specified, make sure it is within limits if(glob_xmount_cfg.orig_img_offset!=0) { uint64_t size; if(!GetOrigImageSize(&size,TRUE)) { LOG_ERROR("Couldn't get original image's size!\n"); return 1; } if(glob_xmount_cfg.orig_img_offset>size) { LOG_ERROR("The specified offset is larger then the size of the input " "image! (%" PRIu64 " > %" PRIu64 ")\n", glob_xmount_cfg.orig_img_offset, size); return 1; } } // Calculate partial MD5 hash of input image file if(CalculateInputImageHash(&(glob_xmount_cfg.InputHashLo), &(glob_xmount_cfg.InputHashHi))==FALSE) { LOG_ERROR("Couldn't calculate partial hash of input image file!\n") return 1; } if(glob_xmount_cfg.Debug==TRUE) { LOG_DEBUG("Partial MD5 hash of input image file: ") for(int i=0;i<8;i++) printf("%02hhx", *(((char*)(&(glob_xmount_cfg.InputHashLo)))+i)); for(int i=0;i<8;i++) printf("%02hhx", *(((char*)(&(glob_xmount_cfg.InputHashHi)))+i)); printf("\n"); } if(!ExtractVirtFileNames(ppInputFilenames[0])) { LOG_ERROR("Couldn't extract virtual file names!\n"); UnloadInputLibs(); return 1; } LOG_DEBUG("Virtual file names extracted successfully\n") // Gather infos for info file if(!InitVirtImageInfoFile()) { LOG_ERROR("Couldn't gather infos for virtual image info file!\n") UnloadInputLibs(); return 1; } LOG_DEBUG("Virtual image info file build successfully\n") // Do some virtual image type specific initialisations switch(glob_xmount_cfg.VirtImageType) { case VirtImageType_DD: case VirtImageType_DMG: break; case VirtImageType_VDI: // When mounting as VDI, we need to construct a vdi header if(!InitVirtVdiHeader()) { LOG_ERROR("Couldn't initialize virtual VDI file header!\n") UnloadInputLibs(); return 1; } LOG_DEBUG("Virtual VDI file header build successfully\n") break; case VirtImageType_VHD: // When mounting as VHD, we need to construct a vhd footer if(!InitVirtVhdHeader()) { LOG_ERROR("Couldn't initialize virtual VHD file footer!\n") UnloadInputLibs(); return 1; } LOG_DEBUG("Virtual VHD file footer build successfully\n") break; case VirtImageType_VMDK: case VirtImageType_VMDKS: // When mounting as VMDK, we need to construct the VMDK descriptor file if(!InitVirtualVmdkFile()) { LOG_ERROR("Couldn't initialize virtual VMDK file!\n") UnloadInputLibs(); return 1; } break; } if(glob_xmount_cfg.Writable) { // Init cache file and cache file block index if(!InitCacheFile()) { LOG_ERROR("Couldn't initialize cache file!\n") UnloadInputLibs(); return 1; } LOG_DEBUG("Cache file initialized successfully\n") } // Call fuse_main to do the fuse magic ret=fuse_main(nargc,ppNargv,&xmount_operations,NULL); // Destroy mutexes pthread_mutex_destroy(&glob_mutex_image_rw); pthread_mutex_destroy(&glob_mutex_info_read); // Close input image if(glob_p_input_functions->Close(&glob_p_input_image)!=0) { LOG_ERROR("Unable to close input image file!"); } if(glob_xmount_cfg.Writable) { // Write support was enabled, close cache file fclose(glob_p_cache_file); free(glob_p_cache_header); } // Free allocated memory if(glob_xmount_cfg.VirtImageType==VirtImageType_VDI) { // Free constructed VDI header free(glob_p_vdi_header); } if(glob_xmount_cfg.VirtImageType==VirtImageType_VHD) { // Free constructed VHD header free(glob_p_vhd_header); } if(glob_xmount_cfg.VirtImageType==VirtImageType_VMDK || glob_xmount_cfg.VirtImageType==VirtImageType_VMDKS) { // Free constructed VMDK file free(glob_p_vmdk_file); free(glob_xmount_cfg.pVirtualVmdkPath); if(glob_p_vmdk_lockfile_name!=NULL) free(glob_p_vmdk_lockfile_name); if(glob_p_vmdk_lockfile_data!=NULL) free(glob_p_vmdk_lockfile_data); if(glob_p_vmdk_lockdir1!=NULL) free(glob_p_vmdk_lockdir1); if(glob_p_vmdk_lockdir2!=NULL) free(glob_p_vmdk_lockdir2); } for(int i=0;i32bit 20090308: * Added SetVdiFileHeaderData function to handle virtual image type specific data to be cached. This makes cache files independent from virtual image type 20090316: v0.2.0 released 20090327: v0.2.1 released * Fixed a bug in virtual write support. Checking whether data is cached didn't use semaphores. This could corrupt cache files when running multi-threaded. * Added IsVdiFileHeaderCached function to check whether VDI file header was already cached * Added IsBlockCached function to check whether a block was already cached 20090331: v0.2.2 released (Internal release) * Further changes to semaphores to fix write support bug. 20090410: v0.2.3 released * Reverted most of the fixes from v0.2.1 and v0.2.2 as those did not solve the write support bug. * Removed all semaphores * Added two pthread mutexes to protect virtual image and virtual info file. 20090508: * Configure script will now exit when needed libraries aren't found * Added support for newest libewf beta version 20090506 as it seems to reduce memory usage when working with EWF files by about 1/2. * Added LIBEWF_BETA define to adept source to new libewf API. * Added function InitVirtualVmdkFile to build a VmWare virtual disk descriptor file. 20090519: * Added function CreateVirtDir implementing FUSE's mkdir to allow VMWare to create his .vmdk.lck lock folder. Function does not allow to create other folders! * Changed cache file handling as VMDK caching will need new cache file structure incompatible to the old one. 20090522: v0.3.0 released * Added function DeleteVirtFile and DeleteVirtDir so VMWare can remove his lock directories and files. * Added function RenameVirtFile because VMWare needs to rename his lock files. * VMDK support should work now but descriptor file won't get cached as I didn't implement it yet. 20090604: * Added --cache commandline parameter doing the same as --rw. * Added --owcache commandline parameter doing the same as --rw but overwrites any existing cache data. This can be handy for debugging and testing purposes. * Added "vmdks" output type. Same as "vmdk" but generates a disk connected to the SCSI bus rather than the IDE bus. 20090710: v0.3.1 released 20090721: * Added function CheckFuseAllowOther to check wether FUSE supports the "-o allow_other" option. It is supported when "user_allow_other" is set in /etc/fuse.conf or when running xmount as root. * Automatic addition of FUSE's "-o allow_other" option if it is supported. * Added special "-o no_allow_other" command line parameter to disable automatic addition of the above option. * Reorganisation of FUSE's and xmount's command line options processing. * Added LogWarnMessage function to output a warning message. 20090722: * Added function CalculateInputImageHash to calculate an MD5 hash of the first input image's HASH_AMOUNT bytes of data. This hash is used as VDI creation UUID and will later be used to match cache files to input images. 20090724: v0.3.2 released 20090725: v0.4.0 released * Added AFF input image support. * Due to various problems with libewf and libaff packages (Mainly in Debian and Ubuntu), I decided to include them into xmount's source tree and link them in statically. This has the advantage that I can use whatever version I want. 20090727: v0.4.1 released * Added again the ability to compile xmount with shared libs as the Debian folks don't like the static ones :) 20090812: * Added TXMountConfData.OrigImageSize and TXMountConfData.VirtImageSize to save the size of the input and output image in order to avoid regetting it always from disk. 20090814: * Replaced all malloc and realloc occurences with the two macros XMOUNT_MALLOC and XMOUNT_REALLOC. 20090816: * Replaced where applicable all occurences of str(n)cpy or alike with their corresponding macros XMOUNT_STRSET, XMOUNT_STRCPY and XMOUNT_STRNCPY pendants. 20090907: v0.4.2 released * Fixed a bug in VMDK lock file access. glob_vmdk_lockfile_size wasn't reset to 0 when the file was deleted. * Fixed a bug in VMDK descriptor file access. Had to add glob_vmdk_file_size to track the size of this file as strlen was a bad idea :). 20100324: v0.4.3 released * Changed all header structs to prevent different sizes on i386 and amd64. See xmount.h for more details. 20100810: v0.4.4 released * Found a bug in InitVirtVdiHeader(). The 64bit values were addressed incorrectly while filled with rand(). This leads to an error message when trying to add a VDI file to VirtualBox 3.2.8. 20110210: * Adding subtype and fsname FUSE options in order to display mounted source in mount command output. 20110211: v0.4.5 released 20111011: * Changes to deal with libewf v2 API (thx to Joachim Metz) 20111109: v0.4.6 released * Added support for DMG output type (actually a DD with .dmg file extension). This type is used as default output type when using xmount under Mac OS X. 20120130: v0.4.7 released * Made InitVirtImageInfoFile less picky about missing EWF infos. 20120507: * Added support for VHD output image as requested by various people. * Statically linked libs updated to 20120504 (libewf) and 3.7.0 (afflib). 20120510: v0.5.0 released * Added stbuf->st_blocks calculation for VHD images in function GetVirtFileAttr. This makes Windows not think the emulated file would be a sparse file. Sparse vhd files are not attachable in Windows. 20130726: v0.6.0 released * Added libaaff to replace libaff (thx to Guy Voncken). * Added libdd to replace raw dd input file handling and finally support split dd files (thx to Guy Voncken). 20140311: * Added libaewf (thx to Guy Voncken). 20140726: * Added support for dynamically loading of input libs. This should ease adding support for new input image formats in the future. * Moved input image functions to their corresponding dynamically loadable libs. * Prepended "glob_" to all global vars for better identification. 20140731: * Added --offset option as requested by HPM. * Began massive code cleanup */