diff --git a/trunk/libxmount_input/CMakeLists.txt b/trunk/libxmount_input/CMakeLists.txt index 43df12f..80cd466 100644 --- a/trunk/libxmount_input/CMakeLists.txt +++ b/trunk/libxmount_input/CMakeLists.txt @@ -1,28 +1,28 @@ add_subdirectory(libxmount_input_dd) if(NOT STATIC_EWF) find_package(LibEWF) if(LIBEWF_FOUND) add_subdirectory(libxmount_input_ewf) endif(LIBEWF_FOUND) else(NOT STATIC_EWF) message(STATUS "Not checking for LibEWF (STATIC_EWF = 1)") add_subdirectory(libxmount_input_ewf) endif(NOT STATIC_EWF) if(NOT STATIC_AFF) find_package(LibAFF) if(LIBAFF_FOUND) add_subdirectory(libxmount_input_aff) endif(LIBAFF_FOUND) else(NOT STATIC_AFF) message(STATUS "Not checking for LibAFF (STATIC_AFF = 1)") add_subdirectory(libxmount_input_aff) endif(NOT STATIC_AFF) find_package(LibZ) if(LIBZ_FOUND) add_subdirectory(libxmount_input_aewf) -# add_subdirectory(libxmount_input_aaff) + add_subdirectory(libxmount_input_aaff) endif(LIBZ_FOUND) 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 813e4ff..c35b76f 100644 --- a/trunk/libxmount_input/libxmount_input_aaff/libxmount_input_aaff.c +++ b/trunk/libxmount_input/libxmount_input_aaff/libxmount_input_aaff.c @@ -1,738 +1,1050 @@ /******************************************************************************* * 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 . * *******************************************************************************/ +// Please don't touch source code formatting! + +#ifdef LINTING +// #define _LARGEFILE_SOURCE +// #define _FILE_OFFSET_BITS 64 + #define AAFF_STANDALONE +#endif + +#ifdef AAFF_STANDALONE + #define LOG_STDOUT TRUE +#endif #include #include #include #include #include #include #include #include +#include +#include + #include "../libxmount_input.h" -//#define AAFF_DEBUG #include "libxmount_input_aaff.h" -/******************************************************************************* - * Forward declarations - ******************************************************************************/ -int AaffCreateHandle(void **pp_handle); -int AaffDestroyHandle(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, - 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; -} +static int AaffClose (void *pHandle); +static const char* AaffGetErrorMessage (int ErrNum); -/* - * LibXmount_Input_GetSupportedFormats - */ -const char* LibXmount_Input_GetSupportedFormats() { - return "aaff\0\0"; -} +#define AAFF_OPTION_MAXPAGEARRMEM "aaffmaxmem" +#define AAFF_OPTION_LOG "aafflog" -/* - * LibXmount_Input_GetFunctions - */ -void LibXmount_Input_GetFunctions(ts_LibXmountInputFunctions *p_functions) { - p_functions->CreateHandle=&AaffCreateHandle; - p_functions->DestroyHandle=&AaffDestroyHandle; - p_functions->Open=&AaffOpen; - p_functions->Close=&AaffClose; - p_functions->Size=&AaffSize; - p_functions->Read=&AaffRead; - p_functions->OptionsHelp=&AaffOptionsHelp; - p_functions->OptionsParse=&AaffOptionsParse; - p_functions->GetInfofileContent=&AaffGetInfofileContent; - p_functions->FreeBuffer=&AaffFreeBuffer; -} +// ---------------------------- +// Logging and error handling +// ---------------------------- -/******************************************************************************* - * Private - ******************************************************************************/ - -// --------------------------- -// Internal static functions -// --------------------------- +#define LOG_HEADER_LEN 80 -static int AaffCreateHandle0 (t_pAaff *ppAaff) +int LogvEntry (const char *pLogFileName, uint8_t LogStdout, const char *pFileName, const char *pFunctionName, int LineNr, const char *pFormat, va_list pArguments) { - t_pAaff pAaff; + time_t NowT; + struct tm *pNowTM; + FILE *pFile; + int wr; + char *pFullLogFileName = NULL; + const char *pBase; + char LogLineHeader[1024]; + pid_t OwnPID; + + if (!LogStdout && (pLogFileName==NULL)) + return AAFF_OK; - pAaff = (t_pAaff) malloc (sizeof(t_Aaff)); - if (pAaff == NULL) - return AAFF_MEMALLOC_FAILED; + time (&NowT); + pNowTM = localtime (&NowT); + OwnPID = getpid(); // pthread_self() + wr = (int) strftime (&LogLineHeader[0] , sizeof(LogLineHeader) , "%a %d.%b.%Y %H:%M:%S ", pNowTM); //lint !e713 + wr += snprintf (&LogLineHeader[wr], sizeof(LogLineHeader)-wr, "%5d ", OwnPID); //lint !e737 - memset (pAaff, 0, sizeof(t_Aaff)); - *ppAaff = pAaff; + if (pFileName && pFunctionName) + { + pBase = strrchr(pFileName, '/'); + if (pBase) + pFileName = pBase+1; + wr += snprintf (&LogLineHeader[wr], sizeof(LogLineHeader)-wr, "%s %s %d ", pFileName, pFunctionName, LineNr); //lint !e737 + } +// while (wr < LOG_HEADER_LEN) +// LogLineHeader[wr++] = ' '; + + if (pLogFileName) + { + wr = asprintf (&pFullLogFileName, "%s_%d", pLogFileName, OwnPID); + if ((wr <= 0) || (pFullLogFileName == NULL)) + { + if (LogStdout) + printf ("\nLog file error: Can't build filename"); + return AAFF_MEMALLOC_FAILED; + } + else + { + pFile = fopen64 (pFullLogFileName, "a"); + if (pFile == NULL) + { + if (LogStdout) + printf ("\nLog file error: Can't be opened"); + return AAFF_CANNOT_OPEN_LOGFILE; + } + else + { + fprintf (pFile, "%-*s", LOG_HEADER_LEN, &LogLineHeader[0]); + vfprintf (pFile, pFormat, pArguments); + fprintf (pFile, "\n"); + fclose (pFile); + } + free (pFullLogFileName); + } + } + if (LogStdout) + { + printf ("%s", &LogLineHeader[0]); + vprintf (pFormat, pArguments); + printf ("\n"); + } return AAFF_OK; } -static int AaffDestroyHandle0 (t_pAaff *ppAaff) +int LogEntry (const char *pLogFileName, uint8_t LogStdout, const char *pFileName, const char *pFunctionName, int LineNr, const char *pFormat, ...) { - t_pAaff pAaff = *ppAaff; + va_list VaList; + int rc; - 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); + if (!LogStdout && (pLogFileName==NULL)) + return AAFF_OK; - memset (pAaff, 0, sizeof(t_Aaff)); - free (pAaff); - *ppAaff = NULL; + va_start (VaList, pFormat); //lint !e530 Symbol 'VaList' not initialized + rc = LogvEntry (pLogFileName, LogStdout, pFileName, pFunctionName, LineNr, pFormat, VaList); + va_end(VaList); + return rc; +} - return AAFF_OK; +// CHK requires existance of pAaff handle + +#ifdef AAFF_STANDALONE + #define LOG_ERRORS_ON_STDOUT TRUE +#else + #define LOG_ERRORS_ON_STDOUT pAaff->LogStdout +#endif + +#define CHK(ChkVal) \ +{ \ + int ChkValRc; \ + if ((ChkValRc=(ChkVal)) != AAFF_OK) \ + { \ + const char *pErr = AaffGetErrorMessage (ChkValRc); \ + LogEntry (pAaff->pLogFilename, LOG_ERRORS_ON_STDOUT, __FILE__, __FUNCTION__, __LINE__, "Error %d (%s) occured", ChkValRc, pErr); \ + return ChkValRc; \ + } \ +} + +#define LOG(...) \ + LogEntry (pAaff->pLogFilename, pAaff->LogStdout, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__); + + +// AaffCheckError is called before exiting AaffRead. It should not +// be called elsewehere or else the statistics would become wrong. +static void AaffCheckError (t_pAaff pAaff, int Ret, int *pErrno) +{ + *pErrno = 0; + if (Ret != AAFF_OK) + { + if ((Ret >= AAFF_ERROR_ENOMEM_START) && (Ret <= AAFF_ERROR_ENOMEM_END)) *pErrno = ENOMEM; + else if ((Ret >= AAFF_ERROR_EINVAL_START) && (Ret <= AAFF_ERROR_EINVAL_END)) *pErrno = EINVAL; + else *pErrno = EIO; // all other errors + } } +// ------------------------------------ +// Internal functions +// ------------------------------------ + 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 - // -------------------------------------------- + // Set the seek position for starting 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; + uint64_t FoundPage=0; int rc; - DEBUG_PRINTF ("\nSearching for page %" PRIu64 ", MaxHops=%d -- ", Page, MaxHops); + LOG ("Searching 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); + rc = AaffReadSegmentPage (pAaff, Page, &FoundPage, ppBuffer, pLen); + if (rc != AAFF_FOUND) + CHK (rc) + LOG (" %" 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) + if (MaxHops < 0) return AAFF_PAGE_NOT_FOUND; return AAFF_OK; } // --------------- // API functions // --------------- -/* - * AaffCreateHandle - */ -int AaffCreateHandle(void **pp_handle) { - *pp_handle=NULL; - CHK(AaffCreateHandle0((t_pAaff*)pp_handle)) - return AAFF_OK; +static int AaffCreateHandle (void **ppHandle, const char *pFormat, uint8_t Debug) +{ + t_pAaff pAaff; + + *ppHandle = NULL; + + pAaff = (t_pAaff) malloc (sizeof(t_Aaff)); + if (pAaff == NULL) + return AAFF_MEMALLOC_FAILED; + + memset (pAaff, 0, sizeof(t_Aaff)); + pAaff->MaxPageArrMem = AAFF_DEFAULT_MAX_PAGE_ARR_MEM; + pAaff->LogStdout = Debug; + + *ppHandle = (void*) pAaff; + + return AAFF_OK; } -/* - * AaffDestroyHandle - */ -int AaffDestroyHandle(void **pp_handle) { - // TODO: Implement -/* - CHK(AaffDestroyHandle0((t_pAaff*)pp_handle)) - *pp_handle=NULL; -*/ - return AAFF_OK; +static int AaffDestroyHandle (void **ppHandle) +{ + t_pAaff pAaff = (t_pAaff) *ppHandle; + + 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); + *ppHandle = NULL; + + return AAFF_OK; } -/* - * AaffOpen - */ -int AaffOpen(void **pp_handle, - const char **pp_filename_arr, - uint64_t filename_arr_len) + +int AaffOpen (void *pHandle, const char **ppFilenameArr, uint64_t FilenameArrLen) { - 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) { - AaffDestroyHandle0(&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; - - return AAFF_OK; + t_pAaff pAaff = (t_pAaff) pHandle; + char Signature[strlen(AFF_HEADER)+1]; + uint64_t Seek; + + LOG ("Called - Files=%" PRIu64, FilenameArrLen); + + if (FilenameArrLen != 1) + CHK (AAFF_SPLIT_IMAGES_NOT_SUPPORTED) + + pAaff->pFilename = strdup (ppFilenameArr[0]); + pAaff->pFile = fopen (ppFilenameArr[0],"r"); + if(pAaff->pFile==NULL) + { + (void) AaffDestroyHandle ((void**) &pAaff); + CHK (AAFF_FILE_OPEN_FAILED) + } + + // Check signature + // --------------- + CHK (AaffReadFile (pAaff, &Signature, sizeof(Signature))) + if (memcmp (Signature, AFF_HEADER, sizeof(Signature)) !=0) + { + (void)AaffClose((void**)&pAaff); + CHK (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 = (char *) malloc (AaffInfoBuffLen); + pAaff->pInfoBuff = (char *) 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); + CHK (AAFF_TOO_MANY_HEADER_SEGEMENTS) + } + + if (strstr (pAaff->pLibVersion, "Guymager") == NULL) + { + (void) AaffClose ((void**)&pAaff); + CHK (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++; + + MaxEntries = (pAaff->MaxPageArrMem*1024*1024) / (sizeof (unsigned long long *) + 1); // +1 in order not to risk a result of 0 + 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); + CHK (AAFF_UNEXPECTED_PAGE_NUMBER) + } + pAaff->pPageSeekArr[0] = Seek; + + // Alloc Buffers + // ------------- + pAaff->pPageBuff = (char *) malloc (pAaff->PageSize); + pAaff->CurrentPage = AAFF_CURRENTPAGE_NOTSET; + + LOG ("Ret"); + return AAFF_OK; +} + +static int AaffClose (void *pHandle) +{ + t_pAaff pAaff = (t_pAaff) pHandle; + int rc = AAFF_OK; + + LOG ("Called"); + + if (fclose (pAaff->pFile)) + rc = AAFF_CANNOT_CLOSE_FILE; + + LOG ("Ret"); + return rc; +} + +static int AaffSize (void *pHandle, uint64_t *pSize) +{ + t_pAaff pAaff = (t_pAaff) pHandle; + + LOG ("Called"); + *pSize = pAaff->ImageSize; + + LOG ("Ret - Size=%" PRIu64, *pSize); + return AAFF_OK; } -/* - * AaffClose - */ -int AaffClose(void **pp_handle) { - int rc=AAFF_OK; +static int AaffRead (void *pHandle, char *pBuf, off_t Seek, size_t Count, size_t *pRead, int *pErrno) +{ + t_pAaff pAaff = (t_pAaff) pHandle; + char *pPageBuffer; + uint64_t Page; + uint64_t Seek64; + uint64_t Remaining; + uint32_t PageLen, Ofs, ToCopy; + int Ret = AAFF_OK; + + LOG ("Called - Seek=%'" PRIu64 ",Count=%'" PRIu64, Seek, Count); + *pRead = 0; + *pErrno = 0; + + if (Seek < 0) + { + Ret = AAFF_NEGATIVE_SEEK; + goto Leave; + } + Seek64 = Seek; + + if (Seek64 >= pAaff->ImageSize) // If calling function asks + goto Leave; // for data beyond end of + if ((Seek64+Count) > pAaff->ImageSize) // image simply return what + Count = pAaff->ImageSize - Seek64; // is possible. + + Page = Seek64 / pAaff->PageSize; + Ofs = Seek64 % pAaff->PageSize; + Remaining = Count; + + while (Count) + { + Ret = AaffReadPage (pAaff, Page, &pPageBuffer, &PageLen); + if (Ret) + goto Leave; + if (PageLen == 0) + { + Ret = AAFF_PAGE_LENGTH_ZERO; + goto Leave; + } + ToCopy = GETMIN (PageLen-Ofs, Remaining); + memcpy (pBuf, pPageBuffer+Ofs, ToCopy); + Remaining -= ToCopy; + pBuf += ToCopy; + *pRead += ToCopy; + Ofs=0; + Page++; + } + +Leave: + AaffCheckError (pAaff, Ret, pErrno); + LOG ("Ret %d - Read=%" PRIu32, Ret, *pRead); + return Ret; +} - if(fclose((*(t_pAaff*)pp_handle)->pFile)) rc=AAFF_CANNOT_CLOSE_FILE; - CHK(AaffDestroyHandle0((t_pAaff*)pp_handle)) +static int AaffOptionsHelp (const char **ppHelp) +{ + char *pHelp=NULL; + int wr; + + wr = asprintf (&pHelp, " %-12s : Maximum amount of RAM cache, in MiB, for image seek offsets. Default: %"PRIu64" MiB\n" + " %-12s : Log file name.\n" + " Specify full path for %s. The given file name is extended by _.\n", + AAFF_OPTION_MAXPAGEARRMEM, AAFF_DEFAULT_MAX_PAGE_ARR_MEM, + AAFF_OPTION_LOG, + AAFF_OPTION_LOG); + if ((pHelp == NULL) || (wr<=0)) + return AAFF_MEMALLOC_FAILED; - return rc; + *ppHelp = pHelp; + return AAFF_OK; } -/* - * AaffGetInfofileContent - */ -int AaffGetInfofileContent(void *p_handle, char **pp_info_buf) { +static int AaffOptionsParse (void *pHandle, uint32_t OptionCount, const pts_LibXmountOptions *ppOptions, const char **ppError) +{ + pts_LibXmountOptions pOption; + t_pAaff pAaff = (t_pAaff) pHandle; + const char *pError = NULL; + int rc = AAFF_OK; + int Ok; + + LOG ("Called - OptionCount=%" PRIu32, OptionCount); + *ppError = NULL; + + #define TEST_OPTION_UINT64(Opt,DestField) \ + if (strcmp (pOption->p_key, Opt) == 0) \ + { \ + pAaff->DestField = StrToUint64 (pOption->p_value, &Ok); \ + if (!Ok) \ + { \ + pError = "Error in option %s: Invalid value"; \ + break; \ + } \ + LOG ("Option %s set to %" PRIu64, Opt, pAaff->DestField) \ + } + + for (uint32_t i=0; ip_key, AAFF_OPTION_LOG) == 0) + { + pAaff->pLogFilename = strdup (pOption->p_value); + rc = LOG ("Logging for libxmount_input_aaff started") + if (rc != AAFF_OK) + { + pError = "Write test to log file failed"; + break; + } + pOption->valid = TRUE; + LOG ("Option %s set to %s", AAFF_OPTION_LOG, pAaff->pLogFilename); + } + else TEST_OPTION_UINT64 (AAFF_OPTION_MAXPAGEARRMEM, MaxPageArrMem) + } + #undef TEST_OPTION_UINT64 + + if (pError) + *ppError = strdup (pError); + LOG ("Ret - rc=%d,Error=%s", rc, *ppError); + return rc; +} + +static int AaffGetInfofileContent (void *pHandle, const char **ppInfoBuf) +{ + t_pAaff pAaff = (t_pAaff) pHandle; uint64_t i; uint64_t Entries = 0; int Pos = 0; + + LOG ("Called"); + #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]) + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "AFF IMAGE INFORMATION"); + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\n---------------------"); + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\nAFF file %s" , pAaff->pFilename ); + + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\nPage size %u" , pAaff->PageSize ); + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\nSector size %d" , pAaff->SectorSize ); + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\nSectors %" PRIu64, pAaff->Sectors); + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\nImage size %" PRIu64 " (%0.1f GiB)", pAaff->ImageSize, pAaff->ImageSize/(1024.0*1024.0*1024.0)); + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\nTotal pages %" PRIu64, pAaff->TotalPages); + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\n"); + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\n%s", pAaff->pInfoBuffConst); + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\n"); + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\nCurrent page "); + if (pAaff->CurrentPage == AAFF_CURRENTPAGE_NOTSET) + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "not set"); + else Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "%" PRIu64, pAaff->CurrentPage); + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\nSeek array length %" PRIu64, pAaff->PageSeekArrLen); + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\nSeek interleave %" PRIu64, pAaff->Interleave); + + for (i=0; iPageSeekArrLen; i++) + { + if (pAaff->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"); + } + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\nSeek array entries %" PRIu64, Entries); + Pos += snprintf (&pAaff->pInfoBuff[Pos], REM, "\n"); #undef REM - // TODO: Rather then copying, generate info text once here. It won't be used - // after this anymore. - *pp_info_buf=(char*)malloc(strlen(((t_pAaff)p_handle)->pInfoBuff)+1); - if(*pp_info_buf==NULL) { - return AAFF_MEMALLOC_FAILED; - } + *ppInfoBuf = strdup (pAaff->pInfoBuff); + if (*ppInfoBuf == NULL) + CHK (AAFF_MEMALLOC_FAILED) - strcpy(*pp_info_buf,((t_pAaff)p_handle)->pInfoBuff); - return AAFF_OK; + LOG ("Ret - %d bytes of info", strlen(*ppInfoBuf)+1); + return AAFF_OK; } -/* - * AaffSize - */ -int AaffSize(void *p_handle, uint64_t *p_size) { - *p_size=((t_pAaff)p_handle)->ImageSize; - return AAFF_OK; +static const char* AaffGetErrorMessage (int ErrNum) +{ + const char *pMsg; + #define ADD_ERR(ErrCode) \ + case ErrCode: pMsg = #ErrCode; \ + break; + + switch (ErrNum) + { + ADD_ERR (AAFF_OK) + ADD_ERR (AAFF_FOUND) + ADD_ERR (AAFF_MEMALLOC_FAILED) + ADD_ERR (AAFF_OPTIONS_ERROR) + ADD_ERR (AAFF_SPLIT_IMAGES_NOT_SUPPORTED) + ADD_ERR (AAFF_INVALID_SIGNATURE) + ADD_ERR (AAFF_NOT_CREATED_BY_GUYMAGER) + ADD_ERR (AAFF_CANNOT_OPEN_LOGFILE) + ADD_ERR (AAFF_FILE_OPEN_FAILED) + ADD_ERR (AAFF_CANNOT_READ_DATA) + ADD_ERR (AAFF_INVALID_HEADER) + ADD_ERR (AAFF_INVALID_FOOTER) + ADD_ERR (AAFF_TOO_MANY_HEADER_SEGEMENTS) + ADD_ERR (AAFF_INVALID_PAGE_NUMBER) + ADD_ERR (AAFF_UNEXPECTED_PAGE_NUMBER) + ADD_ERR (AAFF_CANNOT_CLOSE_FILE) + ADD_ERR (AAFF_CANNOT_SEEK) + ADD_ERR (AAFF_WRONG_SEGMENT) + ADD_ERR (AAFF_UNCOMPRESS_FAILED) + ADD_ERR (AAFF_INVALID_PAGE_ARGUMENT) + ADD_ERR (AAFF_SEEKARR_CORRUPT) + ADD_ERR (AAFF_PAGE_NOT_FOUND) + ADD_ERR (AAFF_READ_BEYOND_IMAGE_LENGTH) + ADD_ERR (AAFF_READ_BEYOND_LAST_PAGE) + ADD_ERR (AAFF_PAGE_LENGTH_ZERO) + ADD_ERR (AAFF_NEGATIVE_SEEK) + default: + pMsg = "Unknown error"; + } + #undef ARR_ERR + return pMsg; } -/* - * AaffRead - */ -int AaffRead(void *p_handle, - uint64_t seek, - char *p_buf, - uint32_t count) +static int AaffFreeBuffer (void *pBuf) { - 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; + free (pBuf); + return AAFF_OK; } -/* - * AaffOptionsHelp - */ -const char* AaffOptionsHelp() { - return NULL; + +// ------------------------------------ +// LibXmount_Input API implementation +// ------------------------------------ + +uint8_t LibXmount_Input_GetApiVersion () +{ + return LIBXMOUNT_INPUT_API_VERSION; } -/* - * AaffOptionsParse - */ -int AaffOptionsParse(void *p_handle, char *p_options, char **pp_error) { - return AAFF_OK; +const char* LibXmount_Input_GetSupportedFormats () +{ + return "aaff\0\0"; } -/* - * AaffFreeBuffer - */ -void AaffFreeBuffer(void *p_buf) { - free(p_buf); +void LibXmount_Input_GetFunctions(ts_LibXmountInputFunctions *pFunctions) +{ + pFunctions->CreateHandle = &AaffCreateHandle; + pFunctions->DestroyHandle = &AaffDestroyHandle; + pFunctions->Open = &AaffOpen; + pFunctions->Close = &AaffClose; + pFunctions->Size = &AaffSize; + pFunctions->Read = &AaffRead; + pFunctions->OptionsHelp = &AaffOptionsHelp; + pFunctions->OptionsParse = &AaffOptionsParse; + pFunctions->GetInfofileContent = &AaffGetInfofileContent; + pFunctions->GetErrorMessage = &AaffGetErrorMessage; + pFunctions->FreeBuffer = &AaffFreeBuffer; } // ----------------------------------------------------- // Small main routine for testing // It converts an aff file to dd // ----------------------------------------------------- -#ifdef AAFF_MAIN_FOR_TESTING +#ifdef AAFF_STANDALONE + +#define PRINT_ERROR_AND_EXIT(...) \ +{ \ + printf (__VA_ARGS__); \ + exit (1); \ +} -int main(int argc, char *argv[]) +int ParseOptions (t_pAaff pAaff, char *pOptions) { - t_pAaff pAaff; - char *pInfoBuff; - uint64_t Remaining; - uint64_t CurrentPos=0; - int rc; - int Percent; - int PercentOld; + pts_LibXmountOptions pOptionArr; + pts_LibXmountOptions *ppOptionArr; + int OptionCount; + char *pSep; + char *pEqual; + char *pTmp; + const char *pError; + char *pOpt; + int rc; + + if (pOptions == NULL) + return AAFF_OK; + + if (*pOptions == '\0') + return AAFF_OK; + + if (*pOptions == ',') + return AAFF_OPTIONS_ERROR; + + if (pOptions[strlen(pOptions)-1] == ',') + return AAFF_OPTIONS_ERROR; + + pOpt = strdup (pOptions); + + // Count number of comma separated options + OptionCount = 1; + pTmp = pOpt; + while ((pTmp = strchr (pTmp, ',')) != NULL) + { + OptionCount++; + pTmp++; + } + + // Create and fill option array + pOptionArr = (pts_LibXmountOptions) malloc (OptionCount * sizeof(ts_LibXmountOptions)); + if (pOptionArr == NULL) + PRINT_ERROR_AND_EXIT ("Cannot allocate pOptionArr"); + memset (pOptionArr, 0, OptionCount * sizeof(ts_LibXmountOptions)); + pTmp = pOpt; + for (int i=0; i
\n", argv[0]); + (void) AaffOptionsHelp (&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"); + CHK (AaffFreeBuffer ((void*) pHelp)) exit (1); } - -// rc = AaffOpen (&pAaff, argv[1], 1024); // weird seek array size for testing - rc = AaffOpen (&pAaff, argv[1], 1); - if (rc) + if (argv[argc-1][0] == '-') { - printf ("Error %d while opening file %s\n", rc, argv[1]); - exit (2); + pOptions = strdup (&(argv[argc-1][1])); + argc--; } - rc = AaffInfo (pAaff, &pInfoBuff); + rc = AaffCreateHandle ((void**) &pAaff, "aaff", LOG_STDOUT); + if (rc != AAFF_OK) + PRINT_ERROR_AND_EXIT ("Cannot create handle, rc=%d\n", rc) + if (pOptions) + CHK (ParseOptions(pAaff, pOptions)) + + rc = AaffOpen (pAaff, &argv[1], 1); if (rc) { - printf ("Could not retrieve info\n"); + printf ("Error %d while opening file %s\n", rc, argv[1]); exit (2); } - printf ("%s", pInfoBuff); + + CHK (AaffGetInfofileContent ((void*) pAaff, &pInfoBuff)) + if (pInfoBuff) + printf ("Contents of info buffer:\n%s\n", pInfoBuff); + CHK (AaffFreeBuffer ((void*)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; + const unsigned BuffSize = 65536; + char *pBuff; + uint64_t ToRead; + uint64_t Read; + Remaining = pAaff->ImageSize; - pBuff = malloc (BuffSize); + pBuff = (char *) malloc (BuffSize); CurrentPos=0; + Errno = 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) + ToRead = GETMIN (Remaining, BuffSize); + rc = AaffRead ((void*) pAaff, pBuff, CurrentPos, ToRead, &Read, &Errno); + if ((rc != AAFF_OK) || (Errno != 0)) + PRINT_ERROR_AND_EXIT("Error %d while calling AewfRead (Errno %d)\n", rc, Errno); + if (Read != ToRead) + PRINT_ERROR_AND_EXIT("Only %" PRIu64 " out of %" PRIu64 " bytes read\n", Read, ToRead); + + if (fwrite (pBuff, Read, 1, pFile) != 1) { printf ("Could not write to destinationfile\n"); exit (2); } - CurrentPos += Bytes; - Remaining -= Bytes; + CurrentPos += Read; + Remaining -= Read; 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_aaff/libxmount_input_aaff.h b/trunk/libxmount_input/libxmount_input_aaff/libxmount_input_aaff.h index 08af468..07f3cbd 100644 --- a/trunk/libxmount_input/libxmount_input_aaff/libxmount_input_aaff.h +++ b/trunk/libxmount_input/libxmount_input_aaff/libxmount_input_aaff.h @@ -1,187 +1,183 @@ /******************************************************************************* * xmount Copyright (c) 2008-2013 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 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 . * *******************************************************************************/ +// Please don't touch source code formatting! + #ifndef AAFF_H #define AAFF_H typedef struct _t_Aaff *t_pAaff; // ---------------------- // Constant definitions // ---------------------- -#define AAFF_DEFAULT_PAGE_SEEK_MAX_ENTRIES 1000000 // Default max. number of cached seek points for fast page access - -#define AAFF_CURRENTPAGE_NOTSET ULONG_LONG_MAX - #define GETMAX(a,b) ((a)>(b)?(a):(b)) #define GETMIN(a,b) ((a)<(b)?(a):(b)) +#define FALSE 0 +#define TRUE 1 + +const uint64_t AAFF_DEFAULT_MAX_PAGE_ARR_MEM = 10; // Default max. memory for caching seek points for fast page access (MiB) +const uint64_t AAFF_CURRENTPAGE_NOTSET = UINT64_MAX; + // ----------------- // AFF definitions // ----------------- #define AFF_GID_LENGTH 16 #define AFF_SEGARG_U64 2 // Used as argument for segments that contain a 64 bit unsigned in the data field #define AFF_HEADER "AFF10\r\n" #define AFF_SEGMENT_HEADER_MAGIC "AFF" #define AFF_SEGMENT_FOOTER_MAGIC "ATT" #define AFF_BADSECTOR_HEADER "BAD SECTOR" #define AFF_FILE_TYPE "AFF" #define AFF_SEGNAME_BADFLAG "badflag" #define AFF_SEGNAME_AFFLIB_VERSION "afflib_version" #define AFF_SEGNAME_FILETYPE "aff_file_type" #define AFF_SEGNAME_GID "image_gid" #define AFF_SEGNAME_SECTORS "devicesectors" #define AFF_SEGNAME_SECTORSIZE "sectorsize" #define AFF_SEGNAME_IMAGESIZE "imagesize" #define AFF_SEGNAME_PAGESIZE "pagesize" #define AFF_SEGNAME_BADSECTORS "badsectors" #define AFF_SEGNAME_MD5 "md5" #define AFF_SEGNAME_SHA256 "sha256" #define AFF_SEGNAME_DURATION "acquisition_seconds" #define AFF_SEGNAME_PAGE "page" #define AAFF_SEGNAME_COMMAND_LINE "acquisition_commandline" #define AAFF_SEGNAME_MACADDR "acquisition_macaddr" #define AAFF_SEGNAME_DATE "acquisition_date" // Format: YYYY-MM-DD HH:MM:SS TZT #define AAFF_SEGNAME_DEVICE "acquisition_device" #define AAFF_SEGNAME_MODEL "device_model" #define AAFF_SEGNAME_SN "device_sn" #define AFF_PAGEFLAGS_UNCOMPRESSED 0x0000 #define AFF_PAGEFLAGS_COMPRESSED_ZLIB 0x0001 #define AFF_PAGEFLAGS_COMPRESSED_ZERO 0x0033 #define AAFF_MD5_LEN 16 #define AAFF_SHA256_LEN 32 #define AAFF_BADSECTORMARKER_MAXLEN 65536 typedef struct { char Magic[4]; unsigned int NameLen; unsigned int DataLen; unsigned int Argument; // Named "flags" in original aff source, named "arg" in afinfo output. char Name[]; //lint !e1501 } __attribute__ ((packed)) t_AffSegmentHeader; typedef t_AffSegmentHeader *t_pAffSegmentHeader; // Between header and footer lie the segment name and the data typedef struct { char Magic[4]; unsigned int SegmentLen; } __attribute__ ((packed)) t_AffSegmentFooter; const int AaffInfoBuffLen = 1024*1024; typedef struct _t_Aaff { - char *pFilename; - FILE *pFile; - - char *pLibVersion; // AFF File Header info - char *pFileType; - unsigned int PageSize; - unsigned int SectorSize; - uint64_t Sectors; - uint64_t ImageSize; - uint64_t TotalPages; - - char *pNameBuff; // Buffers - char *pDataBuff; - unsigned int NameBuffLen; - unsigned int DataBuffLen; - - uint64_t CurrentPage; - char *pPageBuff; // Length is PageSize, contains data of CurrentPage - unsigned int PageBuffDataLen; // Length of current data in PageBuff (the same for all pages, but the last one might contain less data) - - char *pInfoBuff; - char *pInfoBuffConst; - - uint64_t *pPageSeekArr; - uint64_t PageSeekArrLen; - uint64_t Interleave; // The number of pages lying between 2 entries in the PageSeekArr + char *pFilename; + FILE *pFile; + + char *pLibVersion; // AFF File Header info + char *pFileType; + unsigned int PageSize; + unsigned int SectorSize; + uint64_t Sectors; + uint64_t ImageSize; + uint64_t TotalPages; + + char *pNameBuff; // Buffers + char *pDataBuff; + unsigned int NameBuffLen; + unsigned int DataBuffLen; + + uint64_t CurrentPage; + char *pPageBuff; // Length is PageSize, contains data of CurrentPage + unsigned int PageBuffDataLen; // Length of current data in PageBuff (the same for all pages, but the last one might contain less data) + + char *pInfoBuff; + char *pInfoBuffConst; + + uint64_t *pPageSeekArr; + uint64_t PageSeekArrLen; + uint64_t Interleave; // The number of pages lying between 2 entries in the PageSeekArr + + // Options + char *pLogFilename; + uint64_t MaxPageArrMem; // Maximum amount of memory (in MiB) for storing page pointers + uint8_t LogStdout; } t_Aaff; -// ---------------- -// Error handling -// ---------------- - -#ifdef AAFF_DEBUG - #define CHK(ChkVal) \ - { \ - int ChkValRc; \ - if ((ChkValRc=(ChkVal)) != AAFF_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)) != AAFF_OK) \ - return ChkValRc; \ - } - #define DEBUG_PRINTF(...) -#endif - // Possible error codes enum { - AAFF_OK = 0, - AAFF_FOUND, - - AAFF_MEMALLOC_FAILED=100, - AAFF_FILE_OPEN_FAILED, - AAFF_INVALID_SIGNATURE, - AAFF_CANNOT_READ_DATA, - AAFF_INVALID_HEADER, - AAFF_INVALID_FOOTER, // 105 - AAFF_TOO_MANY_HEADER_SEGEMENTS, - AAFF_NOT_CREATED_BY_GUYMAGER, - AAFF_INVALID_PAGE_NUMBER, - AAFF_UNEXPECTED_PAGE_NUMBER, - AAFF_CANNOT_CLOSE_FILE, // 110 - AAFF_CANNOT_SEEK, - AAFF_WRONG_SEGMENT, - AAFF_UNCOMPRESS_FAILED, - AAFF_INVALID_PAGE_ARGUMENT, - AAFF_SEEKARR_CORRUPT, // 115 - AAFF_PAGE_NOT_FOUND, - AAFF_READ_BEYOND_IMAGE_LENGTH, - AAFF_READ_BEYOND_LAST_PAGE + AAFF_OK = 0, + AAFF_FOUND, + + AAFF_ERROR_ENOMEM_START=1000, + AAFF_MEMALLOC_FAILED, + AAFF_ERROR_ENOMEM_END, + + AAFF_ERROR_EINVAL_START=2000, + AAFF_OPTIONS_ERROR, + AAFF_SPLIT_IMAGES_NOT_SUPPORTED, + AAFF_INVALID_SIGNATURE, + AAFF_NOT_CREATED_BY_GUYMAGER, + AAFF_CANNOT_OPEN_LOGFILE, + AAFF_ERROR_EINVAL_END, + + AAFF_ERROR_EIO_START=3000, + AAFF_FILE_OPEN_FAILED, + AAFF_CANNOT_READ_DATA, + AAFF_INVALID_HEADER, + AAFF_INVALID_FOOTER, + AAFF_TOO_MANY_HEADER_SEGEMENTS, + AAFF_INVALID_PAGE_NUMBER, + AAFF_UNEXPECTED_PAGE_NUMBER, + AAFF_CANNOT_CLOSE_FILE, + AAFF_CANNOT_SEEK, + AAFF_WRONG_SEGMENT, + AAFF_UNCOMPRESS_FAILED, + AAFF_INVALID_PAGE_ARGUMENT, + AAFF_SEEKARR_CORRUPT, + AAFF_PAGE_NOT_FOUND, + AAFF_READ_BEYOND_IMAGE_LENGTH, + AAFF_READ_BEYOND_LAST_PAGE, + AAFF_PAGE_LENGTH_ZERO, + AAFF_NEGATIVE_SEEK, + AAFF_ERROR_EIO_END, }; #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 f68ea98..8447319 100644 --- a/trunk/libxmount_input/libxmount_input_aewf/libxmount_input_aewf.c +++ b/trunk/libxmount_input/libxmount_input_aewf/libxmount_input_aewf.c @@ -1,1527 +1,1530 @@ /******************************************************************************* * 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. // Please don't touch source code formatting! #ifdef LINTING // #define _LARGEFILE_SOURCE // #define _FILE_OFFSET_BITS 64 #define AEWF_STANDALONE #endif #include #include #include #include #include #include //lint !e537 !e451 Include file messages #include #include //lint !e537 #include //lint !e537 !e451 #include //lint !e537 !e451 #include //lint !e537 !e451 #include #include "../libxmount_input.h" #include "libxmount_input_aewf.h" #ifdef AEWF_STANDALONE #define CREATE_REVERSE_FILE // #define REVERSE_FILE_USES_SEPARATE_HANDLE #define LOG_STDOUT TRUE #endif //#ifdef AEWF_STANDALONE // #define _GNU_SOURCE //#endif #define AEWF_OPTION_TABLECACHE "aewfmaxmem" #define AEWF_OPTION_MAXOPENSEGMENTS "aewfmaxfiles" #define AEWF_OPTION_STATS "aewfstats" #define AEWF_OPTION_STATSREFRESH "aewfrefresh" #define AEWF_OPTION_LOG "aewflog" static int AewfClose (void *pHandle); static const char* AewfGetErrorMessage (int ErrNum); -#define AEWF_DEFAULT_TABLECACHE 10LLU // MiB -#define AEWF_DEFAULT_MAXOPENSEGMENTS 10LLU -#define AEWF_DEFAULT_STATSREFRESH 10LLU +const uint64_t AEWF_DEFAULT_TABLECACHE = 10; // MiB +const uint64_t AEWF_DEFAULT_MAXOPENSEGMENTS = 10; +const uint64_t AEWF_DEFAULT_STATSREFRESH = 10; // ---------------------------- // Logging and error handling // ---------------------------- #define LOG_HEADER_LEN 80 int LogvEntry (const char *pLogFileName, uint8_t LogStdout, const char *pFileName, const char *pFunctionName, int LineNr, const char *pFormat, va_list pArguments) { time_t NowT; struct tm *pNowTM; FILE *pFile; int wr; char *pFullLogFileName = NULL; const char *pBase; char LogLineHeader[1024]; pid_t OwnPID; if (!LogStdout && (pLogFileName==NULL)) return AEWF_OK; time (&NowT); pNowTM = localtime (&NowT); OwnPID = getpid(); // pthread_self() wr = (int) strftime (&LogLineHeader[0] , sizeof(LogLineHeader) , "%a %d.%b.%Y %H:%M:%S ", pNowTM); //lint !e713 wr += snprintf (&LogLineHeader[wr], sizeof(LogLineHeader)-wr, "%5d ", OwnPID); //lint !e737 if (pFileName && pFunctionName) { pBase = strrchr(pFileName, '/'); if (pBase) pFileName = pBase+1; wr += snprintf (&LogLineHeader[wr], sizeof(LogLineHeader)-wr, "%s %s %d ", pFileName, pFunctionName, LineNr); //lint !e737 } // while (wr < LOG_HEADER_LEN) // LogLineHeader[wr++] = ' '; if (pLogFileName) { wr = asprintf (&pFullLogFileName, "%s_%d", pLogFileName, OwnPID); if ((wr <= 0) || (pFullLogFileName == NULL)) { if (LogStdout) printf ("\nLog file error: Can't build filename"); return AEWF_MEMALLOC_FAILED; } else { pFile = fopen64 (pFullLogFileName, "a"); if (pFile == NULL) { if (LogStdout) printf ("\nLog file error: Can't be opened"); return AEWF_CANNOT_OPEN_LOGFILE; } else { fprintf (pFile, "%-*s", LOG_HEADER_LEN, &LogLineHeader[0]); vfprintf (pFile, pFormat, pArguments); fprintf (pFile, "\n"); fclose (pFile); } free (pFullLogFileName); } } if (LogStdout) { printf ("%s", &LogLineHeader[0]); vprintf (pFormat, pArguments); printf ("\n"); } return AEWF_OK; } int LogEntry (const char *pLogFileName, uint8_t LogStdout, const char *pFileName, const char *pFunctionName, int LineNr, const char *pFormat, ...) { va_list VaList; int rc; if (!LogStdout && (pLogFileName==NULL)) return AEWF_OK; va_start (VaList, pFormat); //lint !e530 Symbol 'VaList' not initialized rc = LogvEntry (pLogFileName, LogStdout, pFileName, pFunctionName, LineNr, pFormat, VaList); va_end(VaList); return rc; } // CHK requires existance of pAewf handle #ifdef AEWF_STANDALONE #define LOG_ERRORS_ON_STDOUT TRUE #else #define LOG_ERRORS_ON_STDOUT pAewf->LogStdout #endif #define CHK(ChkVal) \ { \ int ChkValRc; \ if ((ChkValRc=(ChkVal)) != AEWF_OK) \ { \ const char *pErr = AewfGetErrorMessage (ChkValRc); \ LogEntry (pAewf->pLogFilename, LOG_ERRORS_ON_STDOUT, __FILE__, __FUNCTION__, __LINE__, "Error %d (%s) occured", ChkValRc, pErr); \ return ChkValRc; \ } \ } #define LOG(...) \ LogEntry (pAewf->pLogFilename, pAewf->LogStdout, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__); // AewfCheckError is called before exiting AewfRead. It should not // be called elsewehere or else the statistics would become wrong. static void AewfCheckError (t_pAewf pAewf, int Ret, int *pErrno) { *pErrno = 0; if (Ret != AEWF_OK) { pAewf->Errors++; pAewf->LastError = Ret; if ((Ret >= AEWF_ERROR_ENOMEM_START) && (Ret <= AEWF_ERROR_ENOMEM_END)) *pErrno = ENOMEM; else if ((Ret >= AEWF_ERROR_EINVAL_START) && (Ret <= AEWF_ERROR_EINVAL_END)) *pErrno = EINVAL; else *pErrno = EIO; // all other errors } } // ------------------------------------ // Internal functions // ------------------------------------ -static int AewfLogStdout (t_pAewf pAewf, uint8_t Flag) -{ - pAewf->LogStdout = Flag; - return AEWF_OK; -} - 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; } #define NO_SEEK ULLONG_MAX static int ReadFilePos (t_pAewf pAewf, FILE *pFile, void *pMem, unsigned int Size, uint64_t Pos) { if (Size == 0) return AEWF_OK; if (Pos != NO_SEEK) { if (fseeko64 (pFile, Pos, SEEK_SET)) return AEWF_FILE_SEEK_FAILED; } if (fread (pMem, Size, 1UL, pFile) != 1) return AEWF_FILE_READ_FAILED; return AEWF_OK; } static int ReadFileAllocPos (t_pAewf pAewf, FILE *pFile, void **ppMem, unsigned int Size, uint64_t Pos) { *ppMem = (void*) malloc (Size); if (*ppMem == NULL) return AEWF_MEMALLOC_FAILED; CHK (ReadFilePos (pAewf, pFile, *ppMem, Size, Pos)) return AEWF_OK; } static int ReadFileAlloc (t_pAewf pAewf, FILE *pFile, void **ppMem, unsigned int Size) { CHK (ReadFileAllocPos (pAewf, pFile, ppMem, Size, NO_SEEK)) 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; } static int CreateInfoData (t_pAewf pAewf, t_pAewfSectionVolume pVolume, char *pHeader , unsigned HeaderLen, char *pHeader2, unsigned Header2Len) { char *pInfo1 = NULL; char *pInfo2 = NULL; char *pInfo3 = NULL; char *pInfo4 = NULL; char *pInfo5 = NULL; 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; int rc = AEWF_OK; #define RET_ERR(ErrCode) \ { \ rc = ErrCode; \ goto CleanUp; \ } #define ASPRINTF(...) \ { \ if (asprintf(__VA_ARGS__) < 0) \ RET_ERR (AEWF_ASPRINTF_FAILED) \ } 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[ 4], 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; } if (pHdr) { pText = (char *) malloc (MaxTextSize); if (pText == NULL) RET_ERR (AEWF_MEMALLOC_FAILED) DstLen0 = MaxTextSize; zrc = uncompress ((unsigned char *)pText, &DstLen0, (const Bytef*)pHdr, HdrLen); UncompressedLen = DstLen0; if (zrc != Z_OK) RET_ERR (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) RET_ERR (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); (void) 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 (pCurDesc && pCurData) { pInfo3 = (char *) malloc (strlen (pCurData) + 4096); if (pInfo3 == NULL) RET_ERR (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, "%"PRIu64" segment file: %s\n", pAewf->Segments, pAewf->pSegmentArr[0].pName) else ASPRINTF (&pInfo4, "%"PRIu64" segment files\n First: %s\n Last: %s\n", pAewf->Segments, pAewf->pSegmentArr[0 ].pName, pAewf->pSegmentArr[pAewf->Segments-1].pName); ASPRINTF (&pInfo5, "%"PRIu64" 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) #undef RET_ERR #undef ASPRINTF CleanUp: if (pText ) free (pText ); if (pInfo1) free (pInfo1); if (pInfo2) free (pInfo2); if (pInfo3) free (pInfo3); if (pInfo4) free (pInfo4); if (pInfo5) free (pInfo5); return rc; } 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; LOG ("Closing %s\n", pOldestSegment->pName); CHK (CloseFile (&pOldestSegment->pFile)) pAewf->OpenSegments--; } // Read the desired table into RAM // ------------------------------- LOG ("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; LOG ("Releasing table %" PRIu64 " (%lu bytes)\n", pOldestTable->Nr, pOldestTable->Size); } // Read the desired table into RAM // ------------------------------- LOG ("Loading table %" PRIu64 " (%lu bytes)", pTable->Nr, pTable->Size); CHK (AewfOpenSegment (pAewf, pTable)); CHK (ReadFileAllocPos (pAewf, pTable->pSegment->pFile, (void**) &pTable->pEwfTable, pTable->Size, pTable->Offset)) pAewf->TableCache += pTable->Size; pAewf->TablesReadFromImage = pTable->Size; return AEWF_OK; } // AewfReadChunk0 reads exactly one chunk. It expects the EWF table be present // in memory and the required segment be 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; int Ret = AEWF_OK; 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) { LOG ("Chunk too big %u / %u", ReadLen, pAewf->ChunkBuffSize); return AEWF_CHUNK_TOO_BIG; } ChunkSize = pAewf->ChunkSize; if (AbsoluteChunk == (pAewf->Chunks-1)) // The very last chunk of the image may be smaller than the default { // chunk size if the image isn't a multiple of the chunk size. ChunkSize = pAewf->ImageSize % pAewf->ChunkSize; if (ChunkSize == 0) ChunkSize = pAewf->ChunkSize; } if (Compressed) { CHK (ReadFilePos (pAewf, 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) Ret = AEWF_UNCOMPRESS_FAILED; if (DstLen0 != ChunkSize) Ret = AEWF_BAD_UNCOMPRESSED_LENGTH; } else { CHK (ReadFilePos (pAewf, pTable->pSegment->pFile, pAewf->pChunkBuffUncompressed, ReadLen, SeekPos)) CalcCRC = adler32 (1, (const Bytef *) pAewf->pChunkBuffUncompressed, ChunkSize); pStoredCRC = (uint *) (pAewf->pChunkBuffUncompressed + ChunkSize); //lint !e826 Suspicious pointer-to-pointer conversion (area too small) if (CalcCRC != *pStoredCRC) Ret = AEWF_CHUNK_CRC_ERROR; } pAewf->DataReadFromImage += ReadLen; pAewf->DataReadFromImageRaw += ChunkSize; if (Ret == AEWF_OK) { pAewf->ChunkInBuff = AbsoluteChunk; pAewf->ChunkBuffUncompressedDataLen = ChunkSize; } else { pAewf->ChunkInBuff = AEWF_NONE; pAewf->ChunkBuffUncompressedDataLen = 0; } return Ret; } 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; *pLen = 0; 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) CHK (AEWF_CHUNK_NOT_FOUND) // Load corresponding table and get chunk // -------------------------------------- pTable->LastUsed = time(NULL); //lint !e771 pTable' (line 640) conceivably not initialized pTable->pSegment->LastUsed = pTable->LastUsed; // Update LastUsed here, in order not to remove the required data from cache CHK (AewfLoadEwfTable (pAewf, pTable)) CHK (AewfOpenSegment (pAewf, pTable)); if ((AbsoluteChunk - pTable->ChunkFrom) > UINT_MAX) CHK (AEWF_ERROR_IN_CHUNK_NUMBER) TableChunk = AbsoluteChunk - pTable->ChunkFrom; // LOG ("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; if (pAewf->pStatsFilename) { time (&NowT); if (((NowT - pAewf->LastStatsUpdate) >= (int)pAewf->StatsRefresh) || Force) { pAewf->LastStatsUpdate = NowT; pid = getpid (); if (asprintf (&pFilename, "%s_%d", pAewf->pStatsFilename, pid) < 0) return AEWF_MEMALLOC_FAILED; 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, "Image segment files %6"PRIu64"\n" , pAewf->Segments); fprintf (pFile, "Image tables %6"PRIu64"\n" , pAewf->Tables); fprintf (pFile, "\n"); 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, "Errors %10" PRIu64 "\n", pAewf->Errors); fprintf (pFile, "Open segment files %10" PRIu64"\n" , pAewf->OpenSegments); fprintf (pFile, "Last error %10d (%s)\n" , pAewf->LastError, AewfGetErrorMessage (pAewf->LastError)); 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)); fprintf (pFile, "RAM used as table cache %10.1f MiB\n", pAewf->TableCache/ (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; } // --------------- // API functions // --------------- static int AewfCreateHandle (void **ppHandle, const char *pFormat, uint8_t Debug) { t_pAewf pAewf; *ppHandle = NULL; // Create handle and clear it // -------------------------- - pAewf=(t_pAewf)malloc(sizeof(t_Aewf)); + pAewf = (t_pAewf) malloc (sizeof(t_Aewf)); if (pAewf == NULL) return AEWF_MEMALLOC_FAILED; memset(pAewf,0,sizeof(t_Aewf)); pAewf->ChunkInBuff = AEWF_NONE; pAewf->pErrorText = 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; pAewf->ChunksRead = 0; pAewf->BytesRead = 0; pAewf->Errors = 0; pAewf->LastError = AEWF_OK; pAewf->MaxTableCache = 0; pAewf->MaxOpenSegments = 0; pAewf->pStatsFilename = NULL; pAewf->StatsRefresh = 0; pAewf->pLogFilename = NULL; - pAewf->LogStdout = FALSE; + pAewf->LogStdout = Debug; pAewf->MaxTableCache = AEWF_DEFAULT_TABLECACHE * 1024*1024; pAewf->MaxOpenSegments = AEWF_DEFAULT_MAXOPENSEGMENTS; pAewf->StatsRefresh = AEWF_DEFAULT_STATSREFRESH; - CHK (AewfLogStdout (pAewf, Debug)) - *ppHandle = (void*) pAewf; return AEWF_OK; } int AewfDestroyHandle(void **ppHandle) { t_pAewf pAewf = (t_pAewf) *ppHandle; LOG ("Called"); LOG ("Remark: 'Ret' won't be logged"); // Handle gets destroyed, 'ret' logging not possible if (pAewf->pLogFilename) free(pAewf->pLogFilename); if (pAewf->pStatsFilename) free(pAewf->pStatsFilename); memset (pAewf, 0, sizeof(t_Aewf)); free (pAewf); *ppHandle = NULL; return AEWF_OK; } -int AewfOpen(void *pHandle, const char **ppFilenameArr, uint64_t FilenameArrLen) +int AewfOpen (void *pHandle, const char **ppFilenameArr, uint64_t FilenameArrLen) { t_pAewf pAewf = (t_pAewf) pHandle; 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; LOG ("Called - Files=%" PRIu64, FilenameArrLen); // Create pSegmentArr and put the segment files in it // -------------------------------------------------- int SegmentArrLen = FilenameArrLen * sizeof(t_Segment); pAewf->pSegmentArr = (t_pSegment) malloc (SegmentArrLen); pAewf->Segments = FilenameArrLen; if (pAewf->pSegmentArr == NULL) return AEWF_MEMALLOC_FAILED; memset (pAewf->pSegmentArr, 0, SegmentArrLen); for (unsigned i=0; ipSegmentArr[i]; pSegment->pName = canonicalize_file_name (ppFilenameArr[i]); // canonicalize_file_name allocates a buffer CHK (OpenFile (&pFile, pSegment->pName)) CHK (ReadFilePos (pAewf, pFile, (void*)&FileHeader, sizeof(FileHeader), 0)) // LOG ("Segment %s - %d \n", ppFilenameArr[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; LOG ("Reading tables\n"); for (unsigned i=0; iSegments; i++) { pSegment = &pAewf->pSegmentArr[i]; CHK (OpenFile (&pFile, pSegment->pName)) CHK (ReadFilePos (pAewf, pFile, &FileHeader, sizeof(FileHeader), 0)) Pos = sizeof (FileHeader); LOG ("Segment %s ", pSegment->pName); do { CHK (ReadFilePos (pAewf, 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 (pAewf, 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; free (pEwfTable); pEwfTable = NULL; SectionSectorsSize = 0; } else if ((strcasecmp ((char *)Section.Type, "header") == 0) && (pHeader==NULL)) { HeaderLen = Section.Size - sizeof(t_AewfSection); CHK (ReadFileAlloc (pAewf, pFile, (void**) &pHeader, HeaderLen)) } else if ((strcasecmp ((char *)Section.Type, "header2") == 0) && (pHeader2==NULL)) { Header2Len = Section.Size - sizeof(t_AewfSection); CHK (ReadFileAlloc (pAewf, pFile, (void**) &pHeader2, Header2Len)) } else if ((strcasecmp ((char *)Section.Type, "volume") == 0) && (pVolume==NULL)) { CHK (ReadFileAlloc (pAewf, pFile, (void**) &pVolume, sizeof(t_AewfSectionVolume))) pAewf->Sectors = pVolume->SectorCount; pAewf->SectorSize = pVolume->BytesPerSector; pAewf->ChunkSize = pVolume->SectorsPerChunk * pVolume->BytesPerSector; //lint !e647 Suspicious truncation pAewf->ImageSize = pAewf->Sectors * pAewf->SectorSize; } LastSection = (Pos == Section.OffsetNextSection); Pos = Section.OffsetNextSection; } while (!LastSection); CHK (CloseFile (&pFile)) } if (pVolume == NULL) return AEWF_VOLUME_MISSING; if (pAewf->Chunks != pVolume->ChunkCount) { LOG ("Error: Wrong chunk count: %"PRIu64" / %"PRIu64, pAewf->Chunks, pVolume->ChunkCount); LOG ("Maybe some segment files are missing. Perhaps you specified E01 instead of E?? or the segments continue beyond extension .EZZ."); 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->TableCache = 0; pAewf->OpenSegments = 0; CHK (CreateInfoData (pAewf, pVolume, pHeader, HeaderLen, pHeader2, Header2Len)) free (pVolume); free (pHeader); free (pHeader2); LOG ("Ret"); return AEWF_OK; } static int AewfClose (void *pHandle) { t_pAewf pAewf = (t_pAewf) pHandle; t_pTable pTable; t_pSegment pSegment; LOG ("Called"); CHK (UpdateStats (pAewf,TRUE)) for (unsigned i=0; iTables; i++) { pTable = &pAewf->pTableArr[i]; if (pTable->pEwfTable) free (pTable->pEwfTable); } for (unsigned i=0;iSegments;i++) { pSegment = &pAewf->pSegmentArr[i]; if (pSegment->pFile) CHK (CloseFile (&pSegment->pFile)); free (pSegment->pName); } free (pAewf->pTableArr); free (pAewf->pSegmentArr); free (pAewf->pChunkBuffCompressed); free (pAewf->pChunkBuffUncompressed); LOG ("Ret"); return AEWF_OK; } static int AewfSize (void *pHandle, uint64_t *pSize) { t_pAewf pAewf = (t_pAewf) pHandle; LOG ("Called"); *pSize = pAewf->ImageSize; LOG ("Ret - Size=%" PRIu64, *pSize); return AEWF_OK; } static int AewfRead (void *pHandle, char *pBuf, off_t Seek, size_t Count, size_t *pRead, int *pErrno) { t_pAewf pAewf = (t_pAewf) pHandle; char *pChunkBuffer; + uint64_t Seek64; uint64_t Chunk; uint64_t Remaining; unsigned int ChunkLen, Ofs, ToCopy; int Ret = AEWF_OK; - LOG ("Called - Seek=%" PRIu64 ",Count=%" PRIu64, Seek, Count); + LOG ("Called - Seek=%'" PRIu64 ",Count=%'" PRIu64, Seek, Count); *pRead = 0; *pErrno = 0; + if (Seek < 0) + { + Ret = AEWF_NEGATIVE_SEEK; + goto Leave; + } + Seek64 = Seek; + pAewf->ReadOperations++; pAewf->DataRequestedByCaller+=Count; - if (Seek >= pAewf->ImageSize) // If calling function asks - goto Leave; // for data beyond end of - if ((Seek+Count) > pAewf->ImageSize) // image simply return what - Count = pAewf->ImageSize - Seek; // is possible. + if (Seek64 >= pAewf->ImageSize) // If calling function asks + goto Leave; // for data beyond end of + if ((Seek64+Count) > pAewf->ImageSize) // image simply return what + Count = pAewf->ImageSize - Seek64; // is possible. - Ofs = Seek % pAewf->ChunkSize; - Chunk = Seek / pAewf->ChunkSize; + Ofs = Seek64 % pAewf->ChunkSize; + Chunk = Seek64 / pAewf->ChunkSize; Remaining = Count; while (Remaining) { Ret = AewfReadChunk (pAewf, Chunk, &pChunkBuffer, &ChunkLen); if (Ret) goto Leave; if (ChunkLen == 0) { Ret = AEWF_CHUNK_LENGTH_ZERO; goto Leave; } ToCopy = GETMIN (ChunkLen-Ofs, Remaining); memcpy (pBuf, pChunkBuffer+Ofs, ToCopy); Remaining -= ToCopy; pBuf += ToCopy; *pRead += ToCopy; Ofs = 0; Chunk++; } Leave: AewfCheckError (pAewf, Ret, pErrno); CHK (UpdateStats (pAewf, (Ret != AEWF_OK))) LOG ("Ret %d - Read=%" PRIu32, Ret, *pRead); return Ret; } static int AewfOptionsHelp (const char **ppHelp) { char *pHelp=NULL; int wr; - wr = asprintf (&pHelp, " %-8s : Maximum amount of RAM cache, in MiB, for image offset tables. Default: %llu MiB\n" - " %-8s : Maximum number of concurrently opened image segment files. Default: %llu\n" - " %-8s : Output statistics at regular intervals to this file.\n" - " %-8s : The update interval, in seconds, for the statistics. Ignored if %s is not set. Default: %llus.\n" - " %-8s : Log file name.\n" + wr = asprintf (&pHelp, " %-12s : Maximum amount of RAM cache, in MiB, for image offset tables. Default: %"PRIu64" MiB\n" + " %-12s : Maximum number of concurrently opened image segment files. Default: %"PRIu64"\n" + " %-12s : Output statistics at regular intervals to this file.\n" + " %-12s : The update interval, in seconds, for the statistics. Ignored if %s is not set. Default: %"PRIu64"s.\n" + " %-12s : Log file name.\n" " Specify full paths for %s and %s options. The given file names are extended by _.\n", AEWF_OPTION_TABLECACHE, AEWF_DEFAULT_TABLECACHE, AEWF_OPTION_MAXOPENSEGMENTS, AEWF_DEFAULT_MAXOPENSEGMENTS, AEWF_OPTION_STATS, AEWF_OPTION_STATSREFRESH, AEWF_OPTION_STATS, AEWF_DEFAULT_STATSREFRESH, AEWF_OPTION_LOG, AEWF_OPTION_STATS, AEWF_OPTION_LOG); if ((pHelp == NULL) || (wr<=0)) return AEWF_MEMALLOC_FAILED; *ppHelp = pHelp; return AEWF_OK; } static int AewfOptionsParse (void *pHandle, uint32_t OptionCount, const pts_LibXmountOptions *ppOptions, const char **ppError) { pts_LibXmountOptions pOption; t_pAewf pAewf = (t_pAewf) pHandle; const char *pError = NULL; int rc = AEWF_OK; int Ok; LOG ("Called - OptionCount=%" PRIu32, OptionCount); *ppError = NULL; #define TEST_OPTION_UINT64(Opt,DestField) \ if (strcmp (pOption->p_key, Opt) == 0) \ { \ pAewf->DestField = StrToUint64 (pOption->p_value, &Ok); \ if (!Ok) \ { \ pError = "Error in option %s: Invalid value"; \ break; \ } \ LOG ("Option %s set to %" PRIu64, Opt, pAewf->DestField) \ } for (uint32_t i=0; ip_key, AEWF_OPTION_LOG) == 0) { pAewf->pLogFilename = strdup (pOption->p_value); rc = LOG ("Logging for libxmount_input_aewf started") if (rc != AEWF_OK) { pError = "Write test to log file failed"; break; } pOption->valid = TRUE; LOG ("Option %s set to %s", AEWF_OPTION_LOG, pAewf->pLogFilename); } if (strcmp (pOption->p_key, AEWF_OPTION_STATS) == 0) { pAewf->pStatsFilename = strdup (pOption->p_value); pOption->valid = TRUE; LOG ("Option %s set to %s", AEWF_OPTION_LOG, pAewf->pLogFilename); } else TEST_OPTION_UINT64 (AEWF_OPTION_MAXOPENSEGMENTS, MaxOpenSegments) else TEST_OPTION_UINT64 (AEWF_OPTION_TABLECACHE , MaxTableCache) else TEST_OPTION_UINT64 (AEWF_OPTION_STATSREFRESH , StatsRefresh) } #undef TEST_OPTION_UINT64 if (pError) *ppError = strdup (pError); LOG ("Ret - rc=%d,Error=%s", rc, *ppError); return rc; } static int AewfGetInfofileContent (void *pHandle, const char **ppInfoBuf) { t_pAewf pAewf = (t_pAewf) pHandle; char *pInfo; LOG ("Called"); pInfo = strdup (pAewf->pInfo); if (pInfo == NULL) return AEWF_MEMALLOC_FAILED; *ppInfoBuf = pInfo; LOG ("Ret - %d bytes of info", strlen(pInfo)+1); return AEWF_OK; } static const char* AewfGetErrorMessage (int ErrNum) { const char *pMsg; #define ADD_ERR(AewfErrCode) \ case AewfErrCode: pMsg = #AewfErrCode; \ break; switch (ErrNum) { ADD_ERR (AEWF_OK) ADD_ERR (AEWF_MEMALLOC_FAILED) ADD_ERR (AEWF_READ_BEYOND_END_OF_IMAGE) ADD_ERR (AEWF_OPTIONS_ERROR) + ADD_ERR (AEWF_CANNOT_OPEN_LOGFILE) ADD_ERR (AEWF_FILE_OPEN_FAILED) ADD_ERR (AEWF_FILE_CLOSE_FAILED) ADD_ERR (AEWF_FILE_SEEK_FAILED) ADD_ERR (AEWF_FILE_READ_FAILED) ADD_ERR (AEWF_READFILE_BAD_MEM) ADD_ERR (AEWF_INVALID_SEGMENT_NUMBER) ADD_ERR (AEWF_WRONG_SEGMENT_FILE_COUNT) ADD_ERR (AEWF_VOLUME_MUST_PRECEDE_TABLES) ADD_ERR (AEWF_SECTORS_MUST_PRECEDE_TABLES) ADD_ERR (AEWF_WRONG_CHUNK_COUNT) ADD_ERR (AEWF_CHUNK_NOT_FOUND) ADD_ERR (AEWF_VOLUME_MISSING) ADD_ERR (AEWF_ERROR_EWF_TABLE_NOT_READY) ADD_ERR (AEWF_ERROR_EWF_SEGMENT_NOT_READY) ADD_ERR (AEWF_CHUNK_TOO_BIG) ADD_ERR (AEWF_UNCOMPRESS_FAILED) ADD_ERR (AEWF_BAD_UNCOMPRESSED_LENGTH) ADD_ERR (AEWF_CHUNK_CRC_ERROR) ADD_ERR (AEWF_ERROR_IN_CHUNK_NUMBER) ADD_ERR (AEWF_VASPRINTF_FAILED) ADD_ERR (AEWF_UNCOMPRESS_HEADER_FAILED) ADD_ERR (AEWF_ASPRINTF_FAILED) - ADD_ERR (AEWF_CANNOT_OPEN_LOGFILE) - ADD_ERR (AEWF_ERROR_EIO_END) + ADD_ERR (AEWF_CHUNK_LENGTH_ZERO) + ADD_ERR (AEWF_NEGATIVE_SEEK) default: pMsg = "Unknown error"; } #undef ARR_ERR return pMsg; } static int AewfFreeBuffer (void *pBuf) { free (pBuf); return AEWF_OK; } // ------------------------------------ // LibXmount_Input API implementation // ------------------------------------ uint8_t LibXmount_Input_GetApiVersion () { return LIBXMOUNT_INPUT_API_VERSION; } const char* LibXmount_Input_GetSupportedFormats () { return "aewf\0\0"; //lint !e840 Use of nul character in a string literal } void LibXmount_Input_GetFunctions (ts_LibXmountInputFunctions *pFunctions) { pFunctions->CreateHandle = &AewfCreateHandle; pFunctions->DestroyHandle = &AewfDestroyHandle; pFunctions->Open = &AewfOpen; pFunctions->Close = &AewfClose; pFunctions->Size = &AewfSize; pFunctions->Read = &AewfRead; pFunctions->OptionsHelp = &AewfOptionsHelp; pFunctions->OptionsParse = &AewfOptionsParse; pFunctions->GetInfofileContent = &AewfGetInfofileContent; pFunctions->GetErrorMessage = &AewfGetErrorMessage; pFunctions->FreeBuffer = &AewfFreeBuffer; } // ----------------------------------------------------- // Small main routine for testing // It converts an EWF file into dd // ----------------------------------------------------- #ifdef AEWF_STANDALONE #define PRINT_ERROR_AND_EXIT(...) \ { \ printf (__VA_ARGS__); \ exit (1); \ } int ParseOptions (t_pAewf pAewf, char *pOptions) { pts_LibXmountOptions pOptionArr; pts_LibXmountOptions *ppOptionArr; int OptionCount; char *pSep; char *pEqual; char *pTmp; const char *pError; char *pOpt; int rc; if (pOptions == NULL) return AEWF_OK; if (*pOptions == '\0') return AEWF_OK; if (*pOptions == ',') return AEWF_OPTIONS_ERROR; if (pOptions[strlen(pOptions)-1] == ',') return AEWF_OPTIONS_ERROR; pOpt = strdup (pOptions); // Count number of comma separated options OptionCount = 1; pTmp = pOpt; while ((pTmp = strchr (pTmp, ',')) != NULL) { OptionCount++; pTmp++; } // Create and fill option array pOptionArr = (pts_LibXmountOptions) malloc (OptionCount * sizeof(ts_LibXmountOptions)); if (pOptionArr == NULL) PRINT_ERROR_AND_EXIT ("Cannot allocate pOptionArr"); memset (pOptionArr, 0, OptionCount * sizeof(ts_LibXmountOptions)); pTmp = pOpt; for (int i=0; i <...> [-comma_separated_options]\n", argv[0]); printf ("Possible options:\n%s\n", pHelp); printf ("The output file will be named dd.\n"); + CHK (AewfFreeBuffer ((void*) pHelp)) exit (1); } if (argv[argc-1][0] == '-') { pOptions = strdup (&(argv[argc-1][1])); argc--; } - rc = AewfCreateHandle ((void**) &pAewf, "aewf"); + rc = AewfCreateHandle ((void**) &pAewf, "aewf", LOG_STDOUT); if (rc != AEWF_OK) PRINT_ERROR_AND_EXIT ("Cannot create handle, rc=%d\n", rc) - CHK (AewfLogStdout (pAewf, LOG_STDOUT)) - if (pOptions) CHK (ParseOptions(pAewf, pOptions)) rc = AewfOpen (pAewf, &argv[1], argc-1); if (rc != AEWF_OK) PRINT_ERROR_AND_EXIT ("Cannot open EWF files, rc=%d\n", rc) #if defined(CREATE_REVERSE_FILE) && defined(REVERSE_FILE_USES_SEPARATE_HANDLE) - rc = AewfCreateHandle ((void**) &pAewfRev, "aewf"); + rc = AewfCreateHandle ((void**) &pAewfRev, "aewf", LOG_STDOUT); if (rc != AEWF_OK) PRINT_ERROR_AND_EXIT ("Cannot create reverse handle, rc=%d\n", rc) - CHK (AewfLogStdout (pAewfRev, LOG_STDOUT)) if (pOptions) CHK (ParseOptions (pAewfRev, pOptions)) rc = AewfOpen (pAewfRev, &argv[1], argc-1); if (rc != AEWF_OK) PRINT_ERROR_AND_EXIT ("Cannot open EWF files, rc=%d\n", rc) #endif CHK (AewfGetInfofileContent ((void*) pAewf, &pInfoBuff)) if (pInfoBuff) printf ("Contents of info buffer:\n%s\n", pInfoBuff); + CHK (AewfFreeBuffer ((void*) pInfoBuff)) CHK (AewfSize (pAewf, &TotalSize)) printf ("Total size: %" PRIu64 " bytes\n", 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; + Errno = 0; while (Remaining) { // LOG ("Pos %" PRIu64 " -- Remaining %" PRIu64 " ", Pos, Remaining); ToRead = GETMIN (Remaining, BuffSize); - rc = AewfRead ((void*) pAewf, &Buff[0], Pos, ToRead, &Read); - if (rc != AEWF_OK) - PRINT_ERROR_AND_EXIT("Error %d while calling AewfRead\n", rc); + + rc = AewfRead ((void*) pAewf, &Buff[0], Pos, ToRead, &Read, &Errno); + if ((rc != AEWF_OK) || (Errno != 0)) + PRINT_ERROR_AND_EXIT("Error %d while calling AewfRead (Errno %d)\n", rc, Errno); if (Read != ToRead) PRINT_ERROR_AND_EXIT("Only %" PRIu64 " out of %" PRIu64 " bytes read\n", Read, ToRead); if (fwrite (Buff, Read, 1, pFile) != 1) PRINT_ERROR_AND_EXIT("Could not write to destination file\n"); Remaining -= ToRead; Pos += ToRead; #ifdef CREATE_REVERSE_FILE PosRev -= ToRead; - rc = AewfRead ((void*) pAewfRev, &Buff[0], PosRev, ToRead, &Read); - if (rc != AEWF_OK) - PRINT_ERROR_AND_EXIT("Error %d while reverse calling AewfRead\n", rc); + rc = AewfRead ((void*) pAewfRev, &Buff[0], PosRev, ToRead, &Read, &Errno); + if ((rc != AEWF_OK) || (Errno != 0)) + PRINT_ERROR_AND_EXIT("Error %d while reverse calling AewfRead (Errno %d)\n", rc, Errno); if (Read != ToRead) PRINT_ERROR_AND_EXIT("Only %" PRIu64 " out of %" PRIu64 " bytes read from rev file\n", Read, ToRead); 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 AEWF files\n"); if (AewfDestroyHandle ((void**)&pAewf)) PRINT_ERROR_AND_EXIT("Error while destroying AEWF handle\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 AEWF files\n"); if (AewfDestroyHandle ((void**)&pAewfRev)) PRINT_ERROR_AND_EXIT("Error while destroying reverse AEWF handle\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_aewf/libxmount_input_aewf.h b/trunk/libxmount_input/libxmount_input_aewf/libxmount_input_aewf.h index 65f7f34..33818cb 100644 --- a/trunk/libxmount_input/libxmount_input_aewf/libxmount_input_aewf.h +++ b/trunk/libxmount_input/libxmount_input_aewf/libxmount_input_aewf.h @@ -1,246 +1,248 @@ /******************************************************************************* * 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 . * *******************************************************************************/ // Please don't touch source code formatting! #ifndef AEWF_H #define AEWF_H typedef struct _t_Aewf *t_pAewf; // ---------------------- // Constant definitions // ---------------------- #define GETMAX(a,b) ((a)>(b)?(a):(b)) #define GETMIN(a,b) ((a)<(b)?(a):(b)) #define FALSE 0 #define TRUE 1 // --------------------- // Types and strutures // --------------------- typedef struct { unsigned char Signature[8]; unsigned char StartOfFields; // 0x01; unsigned short int SegmentNumber; unsigned short int EndOfFields; // 0x0000 } __attribute__ ((packed)) t_AewfFileHeader, *t_AewfpFileHeader; typedef struct { unsigned char Type[16]; uint64_t OffsetNextSection; uint64_t Size; unsigned char Padding[40]; uint32_t Checksum; char Data[]; //lint !e1501 data member has zero size } __attribute__ ((packed)) t_AewfSection, *t_pAewfSection; typedef struct { unsigned char MediaType; unsigned char Unknown1[3]; // contains 0x00 uint32_t ChunkCount; uint32_t SectorsPerChunk; uint32_t BytesPerSector; uint64_t SectorCount; uint32_t CHS_Cylinders; uint32_t CHS_Heads; uint32_t CHS_Sectors; unsigned char MediaFlags; unsigned char Unknown2[3]; // contains 0x00 uint32_t PalmVolumeStartSector; unsigned char Padding1[4]; // contains 0x00 uint32_t SmartLogsStartSector; unsigned char CompressionLevel; unsigned char Unknown3[3]; // contains 0x00 uint32_t ErrorBlockSize; unsigned char Unknown4[4]; unsigned char AcquirySystemGUID[16]; unsigned char Padding2[963]; unsigned char Reserved [5]; uint32_t Checksum; } __attribute__ ((packed)) t_AewfSectionVolume, *t_pAewfSectionVolume; typedef struct { uint32_t ChunkCount; unsigned char Padding1 [4]; uint64_t TableBaseOffset; unsigned char Padding2 [4]; uint32_t Checksum; uint32_t OffsetArray[]; //lint !e1501 data member has zero size } __attribute__ ((packed)) t_AewfSectionTable, *t_pAewfSectionTable; const uint32_t AEWF_COMPRESSED = 0x80000000; typedef struct { uint32_t FirstSector; uint32_t NumberOfSectors; } __attribute__ ((packed)) t_AewfSectionErrorEntry, *t_pAewfSectionErrorEntry; typedef struct { uint32_t NumberOfErrors; unsigned char Padding[512]; uint32_t Checksum; t_AewfSectionErrorEntry ErrorArr[0]; //lint !e1501 data member 'ErrorArr' has zero size uint32_t ChecksumArr; } __attribute__ ((packed)) t_AewfSectionError, *t_pAewfSectionError; typedef struct { unsigned char MD5[16]; unsigned char Unknown[16]; uint32_t Checksum; } __attribute__ ((packed)) t_AewfSectionHash, *t_pAewfSectionHash; typedef struct { char *pName; unsigned Number; FILE *pFile; // NULL if file is not opened (never read or kicked out form cache) time_t LastUsed; } t_Segment, *t_pSegment; typedef struct { uint64_t Nr; // The table's position in the pAewf->pTableArr, for debug output only uint64_t ChunkFrom; // Number of the chunk referred to by the first entry of this table (very first chunk has number 0) uint64_t ChunkTo; // Number of the chunk referred to by the last entry of this table t_pSegment pSegment; // The file segment where the table is located uint64_t Offset; // The offset of the table inside the segment file (start of t_AewfSectionTable, not of the preceding t_AewfSection) unsigned long Size; // The length of the table (same as allocated length for pEwfTable) uint32_t ChunkCount; // The number of chunk; this is the same as pTableData->Chunkcount, however, pTableData might not be available (NULL) uint32_t SectionSectorsSize; // Silly EWF format has no clean way of knowing size of the last (possibly compressed) chunk of a table time_t LastUsed; // Last usage of this table, for cache management t_pAewfSectionTable pEwfTable; // Contains the original EWF table section or NULL, if never read or kicked out from cache } t_Table, *t_pTable; -#define AEWF_NONE ULONG_LONG_MAX +#define AEWF_NONE UINT64_MAX + typedef struct _t_Aewf { - t_pSegment pSegmentArr; // Array of all segment files (in correct order) - t_pTable pTableArr; // Array of all chunk offset tables found in the segment files (in correct order) - uint64_t Segments; - uint64_t Tables; - uint64_t Chunks; // Total number of chunks in all tables - uint64_t TableCache; // Current amount RAM used by tables, in bytes - uint64_t OpenSegments; // Current number of open segment files - uint64_t SectorSize; - uint64_t Sectors; - uint64_t ChunkSize; - uint64_t ImageSize; // Equals to Sectors * SectorSize - char *pChunkBuffCompressed; - char *pChunkBuffUncompressed; - uint64_t ChunkBuffUncompressedDataLen; // This normally always is equal to the chunk size (32K), except maybe for the last chunk, if the image's total size is not a multiple of the chunk size - uint32_t ChunkBuffSize; - uint64_t ChunkInBuff; // Chunk currently residing in pChunkBuffUncompressed (AEWF_NONE if none) - char *pErrorText; // Used for assembling error text during option parsing - time_t LastStatsUpdate; - char *pInfo; + t_pSegment pSegmentArr; // Array of all segment files (in correct order) + t_pTable pTableArr; // Array of all chunk offset tables found in the segment files (in correct order) + uint64_t Segments; + uint64_t Tables; + uint64_t Chunks; // Total number of chunks in all tables + uint64_t TableCache; // Current amount RAM used by tables, in bytes + uint64_t OpenSegments; // Current number of open segment files + uint64_t SectorSize; + uint64_t Sectors; + uint64_t ChunkSize; + uint64_t ImageSize; // Equals to Sectors * SectorSize + char *pChunkBuffCompressed; + char *pChunkBuffUncompressed; + uint64_t ChunkBuffUncompressedDataLen; // This normally always is equal to the chunk size (32K), except maybe for the last chunk, if the image's total size is not a multiple of the chunk size + uint32_t ChunkBuffSize; + uint64_t ChunkInBuff; // Chunk currently residing in pChunkBuffUncompressed (AEWF_NONE if none) + char *pErrorText; // Used for assembling error text during option parsing + time_t LastStatsUpdate; + char *pInfo; // Statistics uint64_t SegmentCacheHits; uint64_t SegmentCacheMisses; uint64_t TableCacheHits; uint64_t TableCacheMisses; uint64_t ChunkCacheHits; uint64_t ChunkCacheMisses; uint64_t ReadOperations; // How many times did xmount call the function AewfRead uint64_t DataReadFromImage; // The data (in bytes) read from the image uint64_t DataReadFromImageRaw; // The same data (in bytes), after uncompression (if any) uint64_t DataRequestedByCaller; // How much data was given back to the caller uint64_t TablesReadFromImage; // The overhead of the table read operations (in bytes) uint64_t ChunksRead; uint64_t BytesRead; uint64_t Errors; int LastError; // Options uint64_t MaxTableCache; // Max. amount of bytes in pTableArr[x].pTableData, in bytes uint64_t MaxOpenSegments; // Max. number of open files in pSegmentArr char *pStatsFilename; // Statistics file uint64_t StatsRefresh; // The time in seconds between update of the stats file char *pLogFilename; uint8_t LogStdout; } t_Aewf; // ---------------- // Error codes // ---------------- // AEWF Error codes are automatically mapped to errno codes by means of the groups // below. AEWF uses these errno codes: // ENOMEM memory allocation errors // EINVAL wrong parameter(s) passed to an AEWF function // EIO all others: AEWF function errors, EWF image errors enum { AEWF_OK = 0, AEWF_ERROR_ENOMEM_START=1000, AEWF_MEMALLOC_FAILED, AEWF_ERROR_ENOMEM_END, AEWF_ERROR_EINVAL_START=2000, AEWF_READ_BEYOND_END_OF_IMAGE, AEWF_OPTIONS_ERROR, AEWF_CANNOT_OPEN_LOGFILE, AEWF_ERROR_EINVAL_END, AEWF_ERROR_EIO_START=3000, AEWF_FILE_OPEN_FAILED, AEWF_FILE_CLOSE_FAILED, AEWF_FILE_SEEK_FAILED, AEWF_FILE_READ_FAILED, AEWF_READFILE_BAD_MEM, AEWF_INVALID_SEGMENT_NUMBER, AEWF_WRONG_SEGMENT_FILE_COUNT, AEWF_VOLUME_MUST_PRECEDE_TABLES, AEWF_SECTORS_MUST_PRECEDE_TABLES, AEWF_WRONG_CHUNK_COUNT, AEWF_CHUNK_NOT_FOUND, AEWF_VOLUME_MISSING, AEWF_ERROR_EWF_TABLE_NOT_READY, AEWF_ERROR_EWF_SEGMENT_NOT_READY, AEWF_CHUNK_TOO_BIG, AEWF_UNCOMPRESS_FAILED, AEWF_BAD_UNCOMPRESSED_LENGTH, AEWF_CHUNK_CRC_ERROR, AEWF_ERROR_IN_CHUNK_NUMBER, AEWF_VASPRINTF_FAILED, AEWF_UNCOMPRESS_HEADER_FAILED, AEWF_ASPRINTF_FAILED, AEWF_CHUNK_LENGTH_ZERO, + AEWF_NEGATIVE_SEEK, AEWF_ERROR_EIO_END, }; #endif