diff --git a/.gitignore b/.gitignore index 4ef444723c814b20e290f20554e496003145669f..b1d3f4ab63882b0c936cf211f07f0b8371189cb6 100755 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ framework/SampleConfig/to_install/ framework/modules/*/win32/Debug/ framework/modules/*/win32/Release/ framework/modules/*/win32/*.user +framework/modules/c_InterestingFilesModule/tsk # Release files release/sleuthkit-* diff --git a/man/img_cat.1 b/man/img_cat.1 index 06f348bc7e80959f314c9de64b553d286bbf93a9..e0fdf92e3e85fdfc53fae0cfef7705c6ae444bc1 100644 --- a/man/img_cat.1 +++ b/man/img_cat.1 @@ -2,7 +2,7 @@ .SH NAME img_cat \- Output contents of an image file. .SH SYNOPSIS -.B img_cat [-i imgtype] [-b dev_sector_size] [-b start_sector] [-e stop_sector] [-vV] +.B img_cat [-i imgtype] [-b dev_sector_size] [-s start_sector] [-e stop_sector] [-vV] .I image [images] .SH DESCRIPTION .B img_cat diff --git a/tools/fiwalk/src/fiwalk.cpp b/tools/fiwalk/src/fiwalk.cpp index 8bfba32f36c5a322d8dc74aa2c78b62557a23d9f..e32c55aa3cd33994ae9567e981cca16dc51295be 100644 --- a/tools/fiwalk/src/fiwalk.cpp +++ b/tools/fiwalk/src/fiwalk.cpp @@ -664,13 +664,6 @@ int main(int argc, char * const *argv1) fprintf(stderr,"ERROR: fiwalk was compiled without AFF support.\n"); exit(0); #else -#if 0 - if((tsk_img_type_supported() & TSK_IMG_TYPE_AFF_AFF)==0){ - fprintf(stderr,"ERROR: fiwalk was compiled with AFF support but the TSK library is not.\n"); - fprintf(stderr,"tsk_img_type_supported=0x%x\n",tsk_img_type_supported()); - exit(0); - } -#endif #endif } diff --git a/tsk/auto/auto_db.cpp b/tsk/auto/auto_db.cpp index 37190621f3e335155071d2f228bca0812bebee1a..e87d17891dd9128584b9db890d66bc02dd338b56 100644 --- a/tsk/auto/auto_db.cpp +++ b/tsk/auto/auto_db.cpp @@ -193,19 +193,20 @@ uint8_t uint8_t TskAutoDb::addImageDetails(const char *const img_ptrs[], int a_num) { - string md5 = ""; -#if HAVE_LIBEWF - if (m_img_info->itype == TSK_IMG_TYPE_EWF_EWF) { +// string md5 = ""; +//#if HAVE_LIBEWF +// if (m_img_info->itype == TSK_IMG_TYPE_EWF_EWF) { // @@@ This shoudl really probably be inside of a tsk_img_ method - IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)m_img_info; - if (ewf_info->md5hash_isset) { - md5 = ewf_info->md5hash; - } - } -#endif +// IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)m_img_info; +// if (ewf_info->md5hash_isset) { +// md5 = ewf_info->md5hash; +// } +// } +//#endif if (m_db->addImageInfo(m_img_info->itype, m_img_info->sector_size, - m_curImgId, m_curImgTZone, m_img_info->size, md5)) { + // m_curImgId, m_curImgTZone, m_img_info->size, md5)) { + m_curImgId, m_curImgTZone)) { return 1; } diff --git a/tsk/auto/db_sqlite.cpp b/tsk/auto/db_sqlite.cpp index 39a1c132a1079425b7a0aeeedc88150a859e564a..4679b0a003abd594842859bbfbef976cd1d98ec9 100644 --- a/tsk/auto/db_sqlite.cpp +++ b/tsk/auto/db_sqlite.cpp @@ -26,7 +26,8 @@ using std::sort; using std::for_each; -#define TSK_SCHEMA_VER 3 +//#define TSK_SCHEMA_VER 3 +#define TSK_SCHEMA_VER 2 /** * Set the locations and logging object. Must call @@ -238,7 +239,9 @@ int "Error creating tsk_objects table: %s\n") || attempt_exec - ("CREATE TABLE tsk_image_info (obj_id INTEGER PRIMARY KEY, type INTEGER, ssize INTEGER, tzone TEXT, size INTEGER, md5 TEXT);", +// ("CREATE TABLE tsk_image_info (obj_id INTEGER PRIMARY KEY, type INTEGER, ssize INTEGER, tzone TEXT, size INTEGER, md5 TEXT);", + + ("CREATE TABLE tsk_image_info (obj_id INTEGER PRIMARY KEY, type INTEGER, ssize INTEGER, tzone TEXT);", "Error creating tsk_image_info table: %s\n") || attempt_exec @@ -421,9 +424,14 @@ int objId = sqlite3_last_insert_rowid(m_db); +// snprintf(stmt, 1024, +// "INSERT INTO tsk_image_info (obj_id, type, ssize, tzone, size, md5) VALUES (%lld, %d, %d, '%s', %"PRIuOFF", '%s');", +// objId, type, ssize, timezone.c_str(), size, md5.c_str()); + snprintf(stmt, 1024, - "INSERT INTO tsk_image_info (obj_id, type, ssize, tzone, size, md5) VALUES (%lld, %d, %d, '%s', %"PRIuOFF", '%s');", - objId, type, ssize, timezone.c_str(), size, md5.c_str()); + "INSERT INTO tsk_image_info (obj_id, type, ssize, tzone) VALUES (%lld, %d, %d, '%s');", + objId, type, ssize, timezone.c_str()); + return attempt_exec(stmt, "Error adding data to tsk_image_info table: %s\n"); } diff --git a/tsk/fs/tsk_yaffs.h b/tsk/fs/tsk_yaffs.h index bd29dd220a968632cb0a30cc824a57423baa9582..93ccc1bceb0386eae2fbb7eaab4035c7916a1e42 100644 --- a/tsk/fs/tsk_yaffs.h +++ b/tsk/fs/tsk_yaffs.h @@ -34,6 +34,30 @@ extern "C" { #define YAFFS_DEFAULT_MAX_TEST_BLOCKS 400 // Maximum number of blocks to test looking for Yaffs2 spare under auto-detect +#define YAFFS_HELP_MESSAGE "See http://wiki.sleuthkit.org/index.php?title=YAFFS2 for help on Yaffs2 configuration" + +/* + * Yaffs config file constants and return values + */ +#ifdef TSK_WIN32 +#define YAFFS_CONFIG_FILE_SUFFIX L"-yaffs2.config" +#else +#define YAFFS_CONFIG_FILE_SUFFIX "-yaffs2.config" +#endif + +#define YAFFS_CONFIG_SEQ_NUM_STR "spare_seq_num_offset" +#define YAFFS_CONFIG_OBJ_ID_STR "spare_obj_id_offset" +#define YAFFS_CONFIG_CHUNK_ID_STR "spare_chunk_id_offset" +#define YAFFS_CONFIG_PAGE_SIZE_STR "flash_page_size" +#define YAFFS_CONFIG_SPARE_SIZE_STR "flash_spare_size" +#define YAFFS_CONFIG_CHUNKS_PER_BLOCK_STR "flash_chunks_per_block" + +typedef enum { + YAFFS_CONFIG_OK, + YAFFS_CONFIG_FILE_NOT_FOUND, + YAFFS_CONFIG_ERROR +} YAFFS_CONFIG_STATUS; + /* ** Yaffs Object Flags */ diff --git a/tsk/fs/yaffs.cpp b/tsk/fs/yaffs.cpp index fdb61921f916b2dba26fce6990ea365bf5ddbfcd..0984cabee43e74913ce46eda69469afdedc3da74 100644 --- a/tsk/fs/yaffs.cpp +++ b/tsk/fs/yaffs.cpp @@ -28,6 +28,10 @@ v** Copyright (c) 2002-2003 Brian Carrier, @stake Inc. All rights reserved --*/ #include <vector> +#include <map> +#include <algorithm> +#include <string> +#include <set> #include "tsk_fs_i.h" #include "tsk_yaffs.h" @@ -611,34 +615,44 @@ static void static void yaffscache_objects_free(YAFFSFS_INFO *yfs) { - YaffsCacheObject *obj = yfs->cache_objects; - while(obj != NULL) { - YaffsCacheObject *to_free = obj; + if((yfs != NULL) && (yfs->cache_objects != NULL)){ + YaffsCacheObject *obj = yfs->cache_objects; + while(obj != NULL) { + YaffsCacheObject *to_free = obj; + + YaffsCacheVersion *ver = obj->yco_latest; + while(ver != NULL) { + YaffsCacheVersion *v_to_free = ver; + ver = ver->ycv_prior; + free(v_to_free); + } - YaffsCacheVersion *ver = obj->yco_latest; - while(ver != NULL) { - YaffsCacheVersion *v_to_free = ver; - ver = ver->ycv_prior; - free(v_to_free); + obj = obj->yco_next; + free(to_free); } - - obj = obj->yco_next; - free(to_free); } } static void yaffscache_chunks_free(YAFFSFS_INFO *yfs) { - std::map<unsigned int,YaffsCacheChunkGroup>::iterator iter; - for( iter = yfs->chunkMap->begin(); iter != yfs->chunkMap->end(); ++iter ) { - YaffsCacheChunk *chunk = yfs->chunkMap->operator[](iter->first).cache_chunks_head; - while(chunk != NULL) { - YaffsCacheChunk *to_free = chunk; - chunk = chunk->ycc_next; - free(to_free); + if((yfs != NULL) && (yfs->chunkMap != NULL)){ + // Free the YaffsCacheChunks in each ChunkGroup + std::map<unsigned int,YaffsCacheChunkGroup>::iterator iter; + for( iter = yfs->chunkMap->begin(); iter != yfs->chunkMap->end(); ++iter ) { + YaffsCacheChunk *chunk = yfs->chunkMap->operator[](iter->first).cache_chunks_head; + while(chunk != NULL) { + YaffsCacheChunk *to_free = chunk; + chunk = chunk->ycc_next; + free(to_free); + } } + + // Free the map + yfs->chunkMap->clear(); + delete yfs->chunkMap; } + } @@ -649,6 +663,212 @@ static void * */ +/* Function to parse config file + * + * @param img_info Image info for this image + * @param map<string, int> Stores values from config file indexed on parameter name + * @returns YAFFS_CONFIG_STATUS One of YAFFS_CONFIG_OK, YAFFS_CONFIG_FILE_NOT_FOUND, or YAFFS_CONFIG_ERROR + */ +static YAFFS_CONFIG_STATUS +yaffs_load_config_file(TSK_IMG_INFO * a_img_info, std::map<std::string, std::string> & results){ + const TSK_TCHAR ** image_names; + int num_imgs; + size_t config_file_name_len; + TSK_TCHAR * config_file_name; + FILE* config_file; + char buf[1001]; + + // Get the image name(s) + image_names = tsk_img_get_names(a_img_info, &num_imgs); + if(num_imgs < 1){ + return YAFFS_CONFIG_ERROR; + } + + // Construct the name of the config file from the first image name + config_file_name_len = TSTRLEN(image_names[0]); + config_file_name_len += TSTRLEN(YAFFS_CONFIG_FILE_SUFFIX); + config_file_name = (TSK_TCHAR *) tsk_malloc(sizeof(TSK_TCHAR) * (config_file_name_len + 1)); + + TSTRNCPY(config_file_name, image_names[0], TSTRLEN(image_names[0]) + 1); + TSTRNCAT(config_file_name, YAFFS_CONFIG_FILE_SUFFIX, TSTRLEN(YAFFS_CONFIG_FILE_SUFFIX) + 1); + +#ifdef TSK_WIN32 + HANDLE hWin; + + if ((hWin = CreateFile(config_file_name, GENERIC_READ, + FILE_SHARE_READ, 0, OPEN_EXISTING, 0, + 0)) == INVALID_HANDLE_VALUE) { + + // For the moment, assume that the file just doesn't exist, which isn't an error + free(config_file_name); + return YAFFS_CONFIG_FILE_NOT_FOUND; + } + config_file = _fdopen(_open_osfhandle((intptr_t) hWin, _O_RDONLY), "r"); + if (config_file == NULL) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS); + tsk_error_set_errstr( + "yaffs_load_config: Error converting Windows handle to C handle"); + free(config_file_name); + CloseHandle(hWin); + return YAFFS_CONFIG_ERROR; + } +#else + if (NULL == (config_file = fopen(config_file_name, "r"))) { + free(config_file_name); + return YAFFS_CONFIG_FILE_NOT_FOUND; + } +#endif + + while(fgets(buf, 1000, config_file) != NULL){ + + // Is it a comment? + if((buf[0] == '#') || (buf[0] == ';')){ + continue; + } + + // Is there a '=' ? + if(strchr(buf, '=') == NULL){ + continue; + } + + // Copy to strings while removing whitespace and converting to lower case + std::string paramName(""); + std::string paramVal(""); + + const char * paramNamePtr = strtok(buf, "="); + while(*paramNamePtr != '\0'){ + if(! isspace((char)(*paramNamePtr))){ + paramName += tolower((char)(*paramNamePtr)); + } + paramNamePtr++; + } + + const char * paramValPtr = strtok(NULL, "="); + while(*paramValPtr != '\0'){ + if(! isspace(*paramValPtr)){ + paramVal += tolower((char)(*paramValPtr)); + } + paramValPtr++; + } + + // Make sure this parameter is not already in the map + if(results.find(paramName) != results.end()){ + // Duplicate parameter - return an error + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS); + tsk_error_set_errstr( + "yaffs_load_config: Duplicate parameter name in config file (\"%s\"). %s", paramName.c_str(), YAFFS_HELP_MESSAGE); + fclose(config_file); + free(config_file_name); + return YAFFS_CONFIG_ERROR; + } + + // Add this entry to the map + results[paramName] = paramVal; + } + + fclose(config_file); + free(config_file_name); + return YAFFS_CONFIG_OK; +} + +/* + * Helper function for yaffs_validate_config + * Tests that a string consists only of digits and has at least one digit + * (Can modify later if we want negative fields to be valid) + * + * @param numStr String to test + * @returns 1 on error, 0 on success + */ +static int +yaffs_validate_integer_field(std::string numStr){ + unsigned int i; + + // Test if empty + if(numStr.length() == 0){ + return 1; + } + + // Test each character + for(i = 0;i < numStr.length();i++){ + if(! isdigit(numStr[i])){ + return 1; + } + } + + return 0; +} + +/* + * Function to validate the contents of the config file + * Currently testing: + * All YAFFS_CONFIG fields should be integers (if they exist) + * Either need all three of YAFFS_CONFIG_SEQ_NUM_STR, YAFFS_CONFIG_OBJ_ID_STR, YAFFS_CONFIG_CHUNK_ID_STR + * or none of them + * + * @param paramMap Holds mapping of parameter name to parameter value + * @returns 1 on error (invalid parameters), 0 on success + */ +static int +yaffs_validate_config_file(std::map<std::string, std::string> & paramMap){ + int offset_field_count; + + // Make a list of all fields to test + std::set<std::string> integerParams; + integerParams.insert(YAFFS_CONFIG_SEQ_NUM_STR); + integerParams.insert(YAFFS_CONFIG_OBJ_ID_STR); + integerParams.insert(YAFFS_CONFIG_CHUNK_ID_STR); + integerParams.insert(YAFFS_CONFIG_PAGE_SIZE_STR); + integerParams.insert(YAFFS_CONFIG_SPARE_SIZE_STR); + integerParams.insert(YAFFS_CONFIG_CHUNKS_PER_BLOCK_STR); + + // If the parameter is set, verify that the value is an int + for(std::set<std::string>::iterator it = integerParams.begin();it != integerParams.end();it++){ + if((paramMap.find(*it) != paramMap.end()) && + (0 != yaffs_validate_integer_field(paramMap[*it]))){ + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS); + tsk_error_set_errstr( + "yaffs_validate_config_file: Empty or non-integer value for Yaffs2 parameter \"%s\". %s", (*it).c_str(), YAFFS_HELP_MESSAGE); + return 1; + } + } + + // Check that we have all three spare offset fields, or none of the three + offset_field_count = 0; + if(paramMap.find(YAFFS_CONFIG_SEQ_NUM_STR) != paramMap.end()){ + offset_field_count++; + } + if(paramMap.find(YAFFS_CONFIG_OBJ_ID_STR) != paramMap.end()){ + offset_field_count++; + } + if(paramMap.find(YAFFS_CONFIG_CHUNK_ID_STR) != paramMap.end()){ + offset_field_count++; + } + + if(! ((offset_field_count == 0) || (offset_field_count == 3))){ + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS); + tsk_error_set_errstr( + "yaffs_validate_config_file: Require either all three spare offset fields or none. %s", YAFFS_HELP_MESSAGE); + return 1; + } + + // Make sure there aren't any unexpected fields present + for(std::map<std::string, std::string>::iterator it = paramMap.begin(); it != paramMap.end();it++){ + if(integerParams.find(it->first) == integerParams.end()){ + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS); + tsk_error_set_errstr( + "yaffs_validate_config_file: Found unexpected field in config file (\"%s\"). %s", it->first.c_str(), YAFFS_HELP_MESSAGE); + return 1; + } + } + + return 0; +} + /* * Function to attempt to determine the layout of the yaffs spare area. * Results of the analysis (if the format could be determined) will be stored @@ -666,6 +886,8 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ unsigned int chunksToTest = 10; // Number of chunks to test in each block unsigned int minChunksRead = 10; // Minimum number of chunks we require to run the test (we might not get the full number we want to test for a very small file) + unsigned int chunkSize = yfs->page_size + yfs->spare_size; + unsigned int blockSize = yfs->chunks_per_block * chunkSize; TSK_FS_INFO *fs = &(yfs->fs_info); unsigned int cnt; @@ -677,6 +899,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ unsigned int currentOffset; unsigned char * allSpares; + unsigned int allSparesLength; TSK_OFF_T offset; TSK_OFF_T maxBlocks; @@ -696,19 +919,23 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ int thisChunkBase; int lastChunkBase; - if ((spareBuffer = (unsigned char*) tsk_malloc(yfs->spare_size)) == NULL) { + // The spare area needs to be at least 16 bytes to run the test + if(yfs->spare_size < 16){ + if(tsk_verbose && (! yfs->autoDetect)){ + tsk_fprintf(stderr, + "yaffs_initialize_spare_format failed - given spare size (%d) is not large enough to contain needed fields\n", yfs->spare_size); + } return TSK_ERR; } - if ((allSpares = (unsigned char*) tsk_malloc(yfs->spare_size * blocksToTest * chunksToTest)) == NULL) { - free(spareBuffer); + if ((spareBuffer = (unsigned char*) tsk_malloc(yfs->spare_size)) == NULL) { return TSK_ERR; } - // Initialize the array containing the spares so that uninitialized entries won't cause failure if we don't have enough - // data to fill it. - for(i = 0;i < yfs->spare_size * blocksToTest * chunksToTest;i++){ - allSpares[i] = 0x01; + allSparesLength = yfs->spare_size * blocksToTest * chunksToTest; + if ((allSpares = (unsigned char*) tsk_malloc(allSparesLength)) == NULL) { + free(spareBuffer); + return TSK_ERR; } // Initialize the pointers to one of the configurations we've seen (thought these defaults should not get used) @@ -727,7 +954,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ // observed exception. // Calculate the number of blocks in the image - maxBlocks = yfs->fs_info.img_info->size / (yfs->chunks_per_block * (yfs->page_size + yfs->spare_size)); + maxBlocks = yfs->fs_info.img_info->size / (yfs->chunks_per_block * chunkSize); // If maxBlocksToTest = 0 (unlimited), set it to the total number of blocks // Also reduce the number of blocks to test if it is larger than the total number of blocks @@ -740,7 +967,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ for(TSK_OFF_T blockIndex = 0;blockIndex < maxBlocksToTest;blockIndex++){ // Read the last spare area that we want to test first - offset = (TSK_OFF_T)blockIndex * yfs->chunks_per_block * (yfs->page_size + yfs->spare_size) + (chunksToTest - 1) * (yfs->page_size + yfs->spare_size) + yfs->page_size; + offset = (TSK_OFF_T)blockIndex * blockSize + (chunksToTest - 1) * chunkSize + yfs->page_size; cnt = tsk_img_read(fs->img_info, offset, (char *) spareBuffer, yfs->spare_size); if (cnt == -1 || cnt < yfs->spare_size) { @@ -774,7 +1001,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ // Copy all earlier spare areas in the block for(chunkIndex = 0;chunkIndex < chunksToTest - 1;chunkIndex++){ - offset = blockIndex * yfs->chunks_per_block * (yfs->page_size + yfs->spare_size) + chunkIndex * (yfs->page_size + yfs->spare_size) + yfs->page_size; + offset = blockIndex * blockSize + chunkIndex * chunkSize + yfs->page_size; cnt = tsk_img_read(fs->img_info, offset, (char *) spareBuffer, yfs->spare_size); if (cnt == -1 || cnt < yfs->spare_size) { @@ -817,7 +1044,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ // Print out the collected spare areas if we're in verbose mode if(tsk_verbose && (! yfs->autoDetect)){ - for(blockIndex = 0;blockIndex < blocksToTest;blockIndex++){ + for(blockIndex = 0;blockIndex < nBlocksTested;blockIndex++){ for(chunkIndex = 0;chunkIndex < chunksToTest;chunkIndex++){ for(i = 0;i < yfs->spare_size;i++){ fprintf(stderr, "%02x", allSpares[blockIndex * yfs->spare_size * chunksToTest + chunkIndex * yfs->spare_size + i]); @@ -828,9 +1055,9 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ } // Test all indices into the spare area (that leave enough space for all 16 bytes) - for(currentOffset = 0;currentOffset < yfs->spare_size - 16;currentOffset++){ + for(currentOffset = 0;currentOffset <= yfs->spare_size - 16;currentOffset++){ goodOffset = 1; - for(blockIndex = 0;blockIndex < blocksToTest;blockIndex++){ + for(blockIndex = 0;blockIndex < nBlocksTested;blockIndex++){ for(chunkIndex = 1;chunkIndex < chunksToTest;chunkIndex++){ lastChunkBase = blockIndex * yfs->spare_size * chunksToTest + (chunkIndex - 1) * yfs->spare_size; @@ -901,7 +1128,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ break; } } - if(allSameByte && (allSpares[thisChunkBase + currentOffset] != 0x01)){ // allSpares was initialized with all 0x01 - there might be lines of it left + if(allSameByte){ if(tsk_verbose && (! yfs->autoDetect)){ tsk_fprintf(stderr, "yaffs_initialize_spare_format: Elimimating offset %d - all repeated bytes\n", @@ -937,7 +1164,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ // We probably don't want the first byte to always be 0xff int firstByteFF = 1; - for(blockIndex = 0;blockIndex < blocksToTest;blockIndex++){ + for(blockIndex = 0;blockIndex < nBlocksTested;blockIndex++){ for(chunkIndex = 1;chunkIndex < chunksToTest;chunkIndex++){ if(allSpares[blockIndex * yfs->spare_size * chunksToTest + chunkIndex * yfs->spare_size + currentOffset] != 0xff){ firstByteFF = 0; @@ -993,6 +1220,8 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ tsk_fprintf(stderr, "yaffs_initialize_spare_format: Final offsets: %d (sequence number), %d (object id), %d (chunk id), %d (n bytes)\n", bestOffset, bestOffset+4, bestOffset+8, bestOffset+12); + tsk_fprintf(stderr, + "If these do not seem valid: %s\n", YAFFS_HELP_MESSAGE); } return TSK_OK; } @@ -1079,11 +1308,18 @@ static uint8_t uint32_t object_id; uint32_t chunk_id; + // Should have checked this by now, but just in case + if((yfs->spare_seq_offset + 4 > yfs->spare_size) || + (yfs->spare_obj_id_offset + 4 > yfs->spare_size) || + (yfs->spare_chunk_id_offset + 4 > yfs->spare_size)){ + return 1; + } + if ((spr = (unsigned char*) tsk_malloc(yfs->spare_size)) == NULL) { return 1; } - if (yfs->spare_size < 46) { + if (yfs->spare_size < 46) { // Why is this 46? tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("yaffsfs_read_spare: spare size is too small"); @@ -1528,7 +1764,6 @@ static uint8_t uint8_t type; char *real_name; - if (a_fs_file == NULL) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("yaffsfs_inode_lookup: fs_file is NULL"); @@ -1569,6 +1804,10 @@ static uint8_t return 1; } + if(version->ycv_header_chunk == NULL){ + return 1; + } + if (yaffsfs_read_chunk(yfs, &header, &spare, version->ycv_header_chunk->ycc_offset) != TSK_OK) { if (tsk_verbose) tsk_fprintf(stderr, "yaffs_inode_lookup: yaffsfs_read_chunk failed!\n"); @@ -2218,17 +2457,18 @@ static uint8_t static void yaffsfs_close(TSK_FS_INFO *fs) { - YAFFSFS_INFO *yfs = (YAFFSFS_INFO *)fs; + if(fs != NULL){ + YAFFSFS_INFO *yfs = (YAFFSFS_INFO *)fs; - fs->tag = 0; + fs->tag = 0; - // TODO: Walk and free the cache structures - yaffscache_objects_free(yfs); - yaffscache_chunks_free(yfs); + // Walk and free the cache structures + yaffscache_objects_free(yfs); + yaffscache_chunks_free(yfs); - //tsk_deinit_lock(&yaffsfs->lock); - - tsk_fs_free(fs); + //tsk_deinit_lock(&yaffsfs->lock); + tsk_fs_free(fs); + } } typedef struct _dir_open_cb_args { @@ -2384,6 +2624,7 @@ static TSK_RETVAL_ENUM return TSK_ERR; } + if ((fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) { tsk_error_errstr2_concat(" - yaffs_dir_open_meta"); @@ -2661,12 +2902,14 @@ TSK_FS_INFO * yaffs2_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, TSK_FS_TYPE_ENUM ftype, uint8_t test) { - YAFFSFS_INFO *yaffsfs; - TSK_FS_INFO *fs; + YAFFSFS_INFO *yaffsfs = NULL; + TSK_FS_INFO *fs = NULL; const unsigned int psize = img_info->page_size; const unsigned int ssize = img_info->spare_size; YaffsHeader * first_header = NULL; TSK_FS_DIR *test_dir; + std::map<std::string, std::string> configParams; + YAFFS_CONFIG_STATUS config_file_status; // clean up any error messages that are lying around tsk_error_reset(); @@ -2680,13 +2923,47 @@ TSK_FS_INFO * if ((yaffsfs = (YAFFSFS_INFO *) tsk_fs_malloc(sizeof(YAFFSFS_INFO))) == NULL) return NULL; + yaffsfs->cache_objects = NULL; + yaffsfs->chunkMap = NULL; + + // Read config file (if it exists) + config_file_status = yaffs_load_config_file(img_info, configParams); + if(config_file_status == YAFFS_CONFIG_ERROR){ + // tsk_error was set by yaffs_load_config + goto on_error; + } + else if(config_file_status == YAFFS_CONFIG_OK){ + // Validate the input + // If it fails validation, return (tsk_error will be set up already) + if(1 == yaffs_validate_config_file(configParams)){ + goto on_error; + } + } + + // If we read these fields from the config file, use those values. Otherwise use the defaults + if(configParams.find(YAFFS_CONFIG_PAGE_SIZE_STR) != configParams.end()){ + yaffsfs->page_size = atoi(configParams[YAFFS_CONFIG_PAGE_SIZE_STR].c_str()); + } + else{ + yaffsfs->page_size = psize == 0 ? YAFFS_DEFAULT_PAGE_SIZE : psize; + } + + if(configParams.find(YAFFS_CONFIG_SPARE_SIZE_STR) != configParams.end()){ + yaffsfs->spare_size = atoi(configParams[YAFFS_CONFIG_SPARE_SIZE_STR].c_str()); + } + else{ + yaffsfs->spare_size = ssize == 0 ? YAFFS_DEFAULT_SPARE_SIZE : ssize; + } + + if(configParams.find(YAFFS_CONFIG_CHUNKS_PER_BLOCK_STR) != configParams.end()){ + yaffsfs->chunks_per_block = atoi(configParams[YAFFS_CONFIG_CHUNKS_PER_BLOCK_STR].c_str()); + } + else{ + yaffsfs->chunks_per_block = 64; + } - yaffsfs->page_size = psize == 0 ? YAFFS_DEFAULT_PAGE_SIZE : psize; - yaffsfs->spare_size = ssize == 0 ? YAFFS_DEFAULT_SPARE_SIZE : ssize; - yaffsfs->chunks_per_block = 64; // TODO: Why are 2 different memory allocation methods used in the same code? // This makes things unnecessary complex. - yaffsfs->chunkMap = new std::map<uint32_t, YaffsCacheChunkGroup>; yaffsfs->max_obj_id = 1; yaffsfs->max_version = 0; @@ -2708,23 +2985,45 @@ TSK_FS_INFO * fs->endian = TSK_LIT_ENDIAN; // Determine the layout of the spare area + // If it was specified in the config file, use those values. Otherwise do the auto-detection + if(configParams.find(YAFFS_CONFIG_SEQ_NUM_STR) != configParams.end()){ + // In the validation step, we ensured that if one of the offsets was set, we have all of them + yaffsfs->spare_seq_offset = atoi(configParams[YAFFS_CONFIG_SEQ_NUM_STR].c_str()); + yaffsfs->spare_obj_id_offset = atoi(configParams[YAFFS_CONFIG_OBJ_ID_STR].c_str()); + yaffsfs->spare_chunk_id_offset = atoi(configParams[YAFFS_CONFIG_CHUNK_ID_STR].c_str()); + + // Check that the offsets are valid for the given spare area size (fields are 4 bytes long) + if((yaffsfs->spare_seq_offset + 4 > yaffsfs->spare_size) || + (yaffsfs->spare_obj_id_offset + 4 > yaffsfs->spare_size) || + (yaffsfs->spare_chunk_id_offset + 4 > yaffsfs->spare_size)){ + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS); + tsk_error_set_errstr("yaffs2_open: Offset(s) in config file too large for spare area (size %d). %s", yaffsfs->spare_size, YAFFS_HELP_MESSAGE); + goto on_error; + } - // Decide how many blocks to test. If we're not doing auto-detection, set to zero (no limit) - unsigned int maxBlocksToTest; - if(yaffsfs->autoDetect){ - maxBlocksToTest = YAFFS_DEFAULT_MAX_TEST_BLOCKS; + + // nBytes isn't currently used, so just set to zero + yaffsfs->spare_nbytes_offset = 0; } else{ - maxBlocksToTest = 0; - } + // Decide how many blocks to test. If we're not doing auto-detection, set to zero (no limit) + unsigned int maxBlocksToTest; + if(yaffsfs->autoDetect){ + maxBlocksToTest = YAFFS_DEFAULT_MAX_TEST_BLOCKS; + } + else{ + maxBlocksToTest = 0; + } - if(yaffs_initialize_spare_format(yaffsfs, maxBlocksToTest) != TSK_OK){ - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_FS_MAGIC); - tsk_error_set_errstr("not a YAFFS file system (bad spare format)"); - if (tsk_verbose) - fprintf(stderr, "yaffsfs_open: could not find valid spare area format\n"); - goto on_error; + if(yaffs_initialize_spare_format(yaffsfs, maxBlocksToTest) != TSK_OK){ + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS_MAGIC); + tsk_error_set_errstr("not a YAFFS file system (bad spare format). %s", YAFFS_HELP_MESSAGE); + if (tsk_verbose) + fprintf(stderr, "yaffsfs_open: could not find valid spare area format\n%s\n", YAFFS_HELP_MESSAGE); + goto on_error; + } } /* @@ -2736,9 +3035,9 @@ TSK_FS_INFO * if (yaffsfs_read_header(yaffsfs, &first_header, 0)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); - tsk_error_set_errstr("not a YAFFS file system (first record)"); + tsk_error_set_errstr("not a YAFFS file system (first record). %s", YAFFS_HELP_MESSAGE); if (tsk_verbose) - fprintf(stderr, "yaffsfs_open: invalid first record\n"); + fprintf(stderr, "yaffsfs_open: invalid first record\n%s\n", YAFFS_HELP_MESSAGE); goto on_error; } free(first_header); @@ -2798,44 +3097,34 @@ TSK_FS_INFO * * cache is shared among threads. */ //tsk_init_lock(&yaffsfs->lock); - yaffsfs->cache_objects = NULL; + yaffsfs->chunkMap = new std::map<uint32_t, YaffsCacheChunkGroup>; yaffsfs_cache_fs(yaffsfs); if (tsk_verbose) { fprintf(stderr, "yaffsfs_open: done building cache!\n"); //yaffscache_objects_dump(yaffsfs, stderr); } - fflush(stderr); // Update the number of inums now that we've read in the file system fs->inum_count = fs->last_inum - 1; test_dir = tsk_fs_dir_open_meta(fs, fs->root_inum); if (test_dir == NULL) { - yaffsfs_close(fs); - tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); - tsk_error_set_errstr("not a YAFFS file system (no root directory)"); + tsk_error_set_errstr("not a YAFFS file system (no root directory). %s", YAFFS_HELP_MESSAGE); if (tsk_verbose) - fprintf(stderr, "yaffsfs_open: invalid file system\n"); - return NULL; + fprintf(stderr, "yaffsfs_open: invalid file system\n%s\n", YAFFS_HELP_MESSAGE); + goto on_error; } tsk_fs_dir_close(test_dir); return fs; on_error: - // Make sure to free yaffsfs here otherwise it will leak - if( yaffsfs != NULL ) { - // TODO: where is chunkMap freed in normal operations? - if( yaffsfs->chunkMap != NULL ) { - yaffsfs->chunkMap->clear(); + // yaffsfs_close frees all the cache objects + yaffsfs_close(fs); - delete yaffsfs->chunkMap; - } - free( yaffsfs ); - } return NULL; }