/* ** The Sleuth Kit ** ** Brian Carrier [carrier <at> sleuthkit [dot] org] ** Copyright (c) 2020 Brian Carrier. All Rights reserved ** ** This software is distributed under the Common Public License 1.0 ** */ /** * \file auto_db_java.cpp * Contains code to populate database with volume and file system information from a specific image. */ #include "auto_db_java.h" #include "tsk/img/img_writer.h" #if HAVE_LIBEWF #include "tsk/img/ewf.h" #include "tsk/img/tsk_img_i.h" #endif #include <string.h> #include <algorithm> #include <sstream> using std::stringstream; using std::for_each; /** */ TskAutoDbJava::TskAutoDbJava() { m_curImgId = 0; m_curVsId = 0; m_curVolId = 0; m_curFsId = 0; m_curFileId = 0; m_curUnallocDirId = 0; m_curDirAddr = 0; m_curDirPath = ""; m_vsFound = false; m_volFound = false; m_poolFound = false; m_stopped = false; m_foundStructure = false; m_attributeAdded = false; m_addFileSystems = true; m_noFatFsOrphans = false; m_addUnallocSpace = false; m_minChunkSize = -1; m_maxChunkSize = -1; tsk_init_lock(&m_curDirPathLock); } TskAutoDbJava::~TskAutoDbJava() { closeImage(); tsk_deinit_lock(&m_curDirPathLock); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// int64_t addImageInfo(int type, TSK_OFF_T ssize, int64_t & objId, const string & timezone, TSK_OFF_T size, const string &md5, const string& sha1, const string& sha256, const string& deviceId, const string& collectionDetails) { printf("addImageInfo2\n"); return 2; } int64_t addImageName(int64_t objId, char const* imgName, int sequence) { printf("addImageName\n"); return 3; } int64_t addVsInfo(const TSK_VS_INFO* vs_info, int64_t parObjId, int64_t& objId) { printf("addImageName\n"); return 4; } int64_t addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& objId) { printf("addPoolInfoAndVS\n"); return 5; } int64_t addPoolVolumeInfo(const TSK_POOL_VOLUME_INFO* pool_vol, int64_t parObjId, int64_t& objId) { printf("addPoolVolumeInfo\n"); return 6; } int64_t addVolumeInfo(const TSK_VS_PART_INFO* vs_part, int64_t parObjId, int64_t& objId) { printf("addVolumeInfo\n"); return 7; } int64_t addFsInfo(const TSK_FS_INFO* fs_info, int64_t parObjId, int64_t& objId) { printf("addFsInfo\n"); return 8; } int64_t addFsFile(TSK_FS_FILE* fs_file, const TSK_FS_ATTR* fs_attr, const char* path, const unsigned char*const md5, const TSK_DB_FILES_KNOWN_ENUM known, int64_t fsObjId, int64_t& objId, int64_t dataSourceObjId) { printf("addFsFile\n"); return 9; } int64_t addFileWithLayoutRange(const TSK_DB_FILES_TYPE_ENUM dbFileType, const int64_t parentObjId, const int64_t fsObjId, const uint64_t size, vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId, int64_t dataSourceObjId) { printf("addFileWithLayoutRange\n"); return 10; } int64_t addUnallocBlockFile(const int64_t parentObjId, const int64_t fsObjId, const uint64_t size, vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId, int64_t dataSourceObjId) { printf("addUnallocBlockFile\n"); return addFileWithLayoutRange(TSK_DB_FILES_TYPE_UNALLOC_BLOCKS, parentObjId, fsObjId, size, ranges, objId, dataSourceObjId); } int64_t addUnusedBlockFile(const int64_t parentObjId, const int64_t fsObjId, const uint64_t size, vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId, int64_t dataSourceObjId) { printf("addUnusedBlockFile\n"); return addFileWithLayoutRange(TSK_DB_FILES_TYPE_UNUSED_BLOCKS, parentObjId, fsObjId, size, ranges, objId, dataSourceObjId); } int64_t addUnallocFsBlockFilesParent(const int64_t fsObjId, int64_t& objId, int64_t dataSourceObjId) { printf("addUnallocFsBlockfilesParent\n"); return 11; } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// void TskAutoDbJava::closeImage() { TskAuto::closeImage(); } void TskAutoDbJava::setAddFileSystems(bool addFileSystems) { m_addFileSystems = addFileSystems; } void TskAutoDbJava::setNoFatFsOrphans(bool noFatFsOrphans) { m_noFatFsOrphans = noFatFsOrphans; } void TskAutoDbJava::setAddUnallocSpace(bool addUnallocSpace) { setAddUnallocSpace(addUnallocSpace, -1); } void TskAutoDbJava::setAddUnallocSpace(bool addUnallocSpace, int64_t minChunkSize) { m_addUnallocSpace = addUnallocSpace; m_minChunkSize = minChunkSize; m_maxChunkSize = -1; } void TskAutoDbJava::setAddUnallocSpace(int64_t minChunkSize, int64_t maxChunkSize) { m_addUnallocSpace = true; m_minChunkSize = minChunkSize; m_maxChunkSize = maxChunkSize; } /** * Adds an image to the database. * * @param a_num Number of image parts * @param a_images Array of paths to the image parts * @param a_type Image type * @param a_ssize Size of device sector in bytes (or 0 for default) * @param a_deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID). * @return 0 for success, 1 for failure */ uint8_t TskAutoDbJava::openImageUtf8(int a_num, const char *const a_images[], TSK_IMG_TYPE_ENUM a_type, unsigned int a_ssize, const char* a_deviceId) { uint8_t retval = TskAuto::openImageUtf8(a_num, a_images, a_type, a_ssize); if (retval != 0) { return retval; } if (addImageDetails(a_deviceId)) { return 1; } return 0; } /** * Adds an image to the database. * * @param a_num Number of image parts * @param a_images Array of paths to the image parts * @param a_type Image type * @param a_ssize Size of device sector in bytes (or 0 for default) * @param a_deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID). * @return 0 for success, 1 for failure */ uint8_t TskAutoDbJava::openImage(int a_num, const TSK_TCHAR * const a_images[], TSK_IMG_TYPE_ENUM a_type, unsigned int a_ssize, const char* a_deviceId) { // make name of database #ifdef TSK_WIN32 uint8_t retval = TskAuto::openImage(a_num, a_images, a_type, a_ssize); if (retval != 0) { return retval; } return (addImageDetails(a_deviceId)); #else return openImageUtf8(a_num, a_images, a_type, a_ssize, a_deviceId); #endif } /** * Adds an image to the database. Requires that m_img_info is already initialized * * @param a_deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID). * @return 0 for success, 1 for failure */ uint8_t TskAutoDbJava::openImage(const char* a_deviceId) { if (m_img_info == NULL) { return 1; } return(addImageDetails(a_deviceId)); } /** * Adds image details to the existing database tables. * * @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID). * @return Returns 0 for success, 1 for failure */ uint8_t TskAutoDbJava::addImageDetails(const char* deviceId) { string md5 = ""; string sha1 = ""; string collectionDetails = ""; #if HAVE_LIBEWF if (m_img_info->itype == TSK_IMG_TYPE_EWF_EWF) { // @@@ This should 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; } if (ewf_info->sha1hash_isset) { sha1 = ewf_info->sha1hash; } collectionDetails = ewf_get_details(ewf_info); } #endif string devId; if (NULL != deviceId) { devId = deviceId; } else { devId = ""; } if (-1 == addImageInfo(m_img_info->itype, m_img_info->sector_size, m_curImgId, m_curImgTZone, m_img_info->size, md5, sha1, "", devId, collectionDetails)) { registerError(); return 1; } char **img_ptrs; #ifdef TSK_WIN32 // convert image paths to UTF-8 img_ptrs = (char **)tsk_malloc(m_img_info->num_img * sizeof(char *)); if (img_ptrs == NULL) { return 1; } for (int i = 0; i < m_img_info->num_img; i++) { char * img2 = (char*)tsk_malloc(1024 * sizeof(char)); UTF8 *ptr8; UTF16 *ptr16; ptr8 = (UTF8 *)img2; ptr16 = (UTF16 *)m_img_info->images[i]; uint8_t retval = tsk_UTF16toUTF8_lclorder((const UTF16 **)&ptr16, (UTF16 *) & ptr16[TSTRLEN(m_img_info->images[i]) + 1], &ptr8, (UTF8 *)((uintptr_t)ptr8 + 1024), TSKlenientConversion); if (retval != TSKconversionOK) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_AUTO_UNICODE); tsk_error_set_errstr("Error converting image to UTF-8\n"); return 1; } img_ptrs[i] = img2; } #else img_ptrs = m_img_info->images; #endif // Add the image names for (int i = 0; i < m_img_info->num_img; i++) { const char *img_ptr = img_ptrs[i]; if (-1 == addImageName(m_curImgId, img_ptr, i)) { registerError(); return 1; } } #ifdef TSK_WIN32 //cleanup for (int i = 0; i < m_img_info->num_img; ++i) { free(img_ptrs[i]); } free(img_ptrs); #endif return 0; } TSK_FILTER_ENUM TskAutoDbJava::filterVs(const TSK_VS_INFO * vs_info) { m_vsFound = true; if (-1 == addVsInfo(vs_info, m_curImgId, m_curVsId)) { registerError(); return TSK_FILTER_STOP; } return TSK_FILTER_CONT; } TSK_FILTER_ENUM TskAutoDbJava::filterPool(const TSK_POOL_INFO * pool_info) { m_poolFound = true; if (m_volFound && m_vsFound) { // there's a volume system and volume if (-1 == addPoolInfoAndVS(pool_info, m_curVolId, m_curPoolVs)) { registerError(); return TSK_FILTER_STOP; } } else { // pool doesn't live in a volume, use image as parent if (-1 == addPoolInfoAndVS(pool_info, m_curImgId, m_curPoolVs)) { registerError(); return TSK_FILTER_STOP; } } return TSK_FILTER_CONT; } TSK_FILTER_ENUM TskAutoDbJava::filterPoolVol(const TSK_POOL_VOLUME_INFO * pool_vol) { if (-1 == addPoolVolumeInfo(pool_vol, m_curPoolVs, m_curPoolVol)) { registerError(); return TSK_FILTER_STOP; } return TSK_FILTER_CONT; } TSK_FILTER_ENUM TskAutoDbJava::filterVol(const TSK_VS_PART_INFO * vs_part) { m_volFound = true; m_foundStructure = true; m_poolFound = false; if (-1 == addVolumeInfo(vs_part, m_curVsId, m_curVolId)) { registerError(); return TSK_FILTER_STOP; } return TSK_FILTER_CONT; } TSK_FILTER_ENUM TskAutoDbJava::filterFs(TSK_FS_INFO * fs_info) { TSK_FS_FILE *file_root; m_foundStructure = true; if (m_poolFound) { // there's a pool if (-1 != addFsInfo(fs_info, m_curPoolVol, m_curFsId)) { registerError(); return TSK_FILTER_STOP; } } else if (m_volFound && m_vsFound) { // there's a volume system and volume if (-1 != addFsInfo(fs_info, m_curVolId, m_curFsId)) { registerError(); return TSK_FILTER_STOP; } } else { // file system doesn't live in a volume, use image as parent if (-1 != addFsInfo(fs_info, m_curImgId, m_curFsId)) { registerError(); return TSK_FILTER_STOP; } } // We won't hit the root directory on the walk, so open it now if ((file_root = tsk_fs_file_open(fs_info, NULL, "/")) != NULL) { processFile(file_root, ""); tsk_fs_file_close(file_root); file_root = NULL; } // make sure that flags are set to get all files -- we need this to // find parent directory TSK_FS_DIR_WALK_FLAG_ENUM filterFlags = (TSK_FS_DIR_WALK_FLAG_ENUM) (TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_UNALLOC); //check if to skip processing of FAT orphans if (m_noFatFsOrphans && TSK_FS_TYPE_ISFAT(fs_info->ftype) ) { filterFlags = (TSK_FS_DIR_WALK_FLAG_ENUM) (filterFlags | TSK_FS_DIR_WALK_FLAG_NOORPHAN); } setFileFilterFlags(filterFlags); return TSK_FILTER_CONT; } /* Insert the file data into the file table. * @param md5 Binary MD5 value (i.e. 16 bytes) or NULL * Returns TSK_ERR on error. */ TSK_RETVAL_ENUM TskAutoDbJava::insertFileData(TSK_FS_FILE * fs_file, const TSK_FS_ATTR * fs_attr, const char *path) { if (-1 == addFsFile(fs_file, fs_attr, path, NULL, TSK_DB_FILES_KNOWN_UNKNOWN, m_curFsId, m_curFileId, m_curImgId)) { registerError(); return TSK_ERR; } return TSK_OK; } /** * Analyzes the open image and adds image info to a database. * Does not deal with transactions and such. Refer to startAddImage() * for more control. * @returns 1 if a critical error occurred (DB doesn't exist, no file system, etc.), 2 if errors occurred at some point adding files to the DB (corrupt file, etc.), and 0 otherwise. Errors will have been registered. */ uint8_t TskAutoDbJava::addFilesInImgToDb() { // @@@ This seems bad because we are overriding what the user may // have set. We should remove the public API if we are going to // override it -- presumably this was added so that we always have // unallocated volume space... setVolFilterFlags((TSK_VS_PART_FLAG_ENUM) (TSK_VS_PART_FLAG_ALLOC | TSK_VS_PART_FLAG_UNALLOC)); uint8_t retVal = 0; if (findFilesInImg()) { // map the boolean return value from findFiles to the three-state return value we use // @@@ findFiles should probably return this three-state enum too if (m_foundStructure == false) { retVal = 1; } else { retVal = 2; } } TSK_RETVAL_ENUM addUnallocRetval = TSK_OK; if (m_addUnallocSpace) addUnallocRetval = addUnallocSpaceToDb(); // findFiles return value trumps unalloc since it can return either 2 or 1. if (retVal) { return retVal; } else if (addUnallocRetval == TSK_ERR) { return 2; } else { return 0; } } /** * Start the process to add image/file metadata to database inside of a transaction. * User must call either commitAddImage() to commit the changes, * or revertAddImage() to revert them. * * @param numImg Number of image parts * @param imagePaths Array of paths to the image parts * @param imgType Image type * @param sSize Size of device sector in bytes (or 0 for default) * @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID) * @return 0 for success, 1 for failure */ uint8_t TskAutoDbJava::startAddImage(int numImg, const TSK_TCHAR * const imagePaths[], TSK_IMG_TYPE_ENUM imgType, unsigned int sSize, const char* deviceId) { if (tsk_verbose) tsk_fprintf(stderr, "TskAutoDbJava::startAddImage: Starting add image process\n"); if (openImage(numImg, imagePaths, imgType, sSize, deviceId)) { tsk_error_set_errstr2("TskAutoDbJava::startAddImage"); registerError(); return 1; } if (m_imageWriterEnabled) { tsk_img_writer_create(m_img_info, m_imageWriterPath); } if (m_addFileSystems) { return addFilesInImgToDb(); } else { return 0; } } /** * Start the process to add image/file metadata to database inside of a transaction. * User must call either commitAddImage() to commit the changes, * or revertAddImage() to revert them. * * @param img_info Previously initialized TSK_IMG_INFO object * @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID) * @return 0 for success, 1 for failure */ uint8_t TskAutoDbJava::startAddImage(TSK_IMG_INFO * img_info, const char* deviceId) { openImageHandle(img_info); if (m_img_info == NULL) { return 1; } if (tsk_verbose) tsk_fprintf(stderr, "TskAutoDbJava::startAddImage: Starting add image process\n"); if (openImage(deviceId)) { tsk_error_set_errstr2("TskAutoDbJava::startAddImage"); registerError(); return 1; } if (m_imageWriterEnabled) { if (tsk_img_writer_create(m_img_info, m_imageWriterPath)) { registerError(); return 1; } } if (m_addFileSystems) { return addFilesInImgToDb(); } else { return 0; } } #ifdef WIN32 /** * Start the process to add image/file metadata to database inside of a transaction. * Same functionality as addFilesInImgToDb(). Reverts * all changes on error. User must call either commitAddImage() to commit the changes, * or revertAddImage() to revert them. * * @param numImg Number of image parts * @param imagePaths Array of paths to the image parts * @param imgType Image type * @param sSize Size of device sector in bytes (or 0 for default) * @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID) * @return 0 for success 1, for failure */ uint8_t TskAutoDbJava::startAddImage(int numImg, const char *const imagePaths[], TSK_IMG_TYPE_ENUM imgType, unsigned int sSize, const char* deviceId) { if (tsk_verbose) tsk_fprintf(stderr, "TskAutoDbJava::startAddImage_utf8: Starting add image process\n"); if (openImageUtf8(numImg, imagePaths, imgType, sSize, deviceId)) { tsk_error_set_errstr2("TskAutoDbJava::startAddImage"); registerError(); return 1; } if (m_imageWriterEnabled) { tsk_img_writer_create(m_img_info, m_imageWriterPath); } if (m_addFileSystems) { return addFilesInImgToDb(); } else { return 0; } } #endif /** * Cancel the running process. Will not be handled immediately. */ void TskAutoDbJava::stopAddImage() { if (tsk_verbose) tsk_fprintf(stderr, "TskAutoDbJava::stopAddImage: Stop request received\n"); m_stopped = true; setStopProcessing(); // flag is checked every time processFile() is called } /** * Set the current image's timezone */ void TskAutoDbJava::setTz(string tzone) { m_curImgTZone = tzone; } TSK_RETVAL_ENUM TskAutoDbJava::processFile(TSK_FS_FILE * fs_file, const char *path) { // Check if the process has been canceled if (m_stopped) { if (tsk_verbose) tsk_fprintf(stderr, "TskAutoDbJava::processFile: Stop request detected\n"); return TSK_STOP; } /* Update the current directory, which can be used to show * progress. If we get a directory, then use its name. We * do this so that when we are searching for orphan files, then * we at least show $OrphanFiles as status. The secondary check * is to grab the parent folder from files once we return back * into a folder when we are doing our depth-first recursion. */ if (isDir(fs_file)) { m_curDirAddr = fs_file->name->meta_addr; tsk_take_lock(&m_curDirPathLock); m_curDirPath = string(path) + fs_file->name->name; tsk_release_lock(&m_curDirPathLock); } else if (m_curDirAddr != fs_file->name->par_addr) { m_curDirAddr = fs_file->name->par_addr; tsk_take_lock(&m_curDirPathLock); m_curDirPath = path; tsk_release_lock(&m_curDirPathLock); } /* process the attributes. The case of having 0 attributes can occur * with virtual / sparse files and HFS directories. * At some point, this can probably be cleaned * up if TSK is more consistent about if there should always be an * attribute or not. Sometimes, none of the attributes are added * because of their type and we always want to add a reference to * every file. */ TSK_RETVAL_ENUM retval = TSK_OK; m_attributeAdded = false; if (tsk_fs_file_attr_getsize(fs_file) > 0) { retval = processAttributes(fs_file, path); } // insert a general row if we didn't add a specific attribute one if ((retval == TSK_OK) && (m_attributeAdded == false)) { retval = insertFileData(fs_file, NULL, path); } // reset the file id m_curFileId = 0; if (retval == TSK_STOP) return TSK_STOP; else return TSK_OK; } // we return only OK or STOP -- errors are registered only and OK is returned. TSK_RETVAL_ENUM TskAutoDbJava::processAttribute(TSK_FS_FILE * fs_file, const TSK_FS_ATTR * fs_attr, const char *path) { // add the file metadata for the default attribute type if (isDefaultType(fs_file, fs_attr)) { if (insertFileData(fs_attr->fs_file, fs_attr, path) == TSK_ERR) { registerError(); return TSK_OK; } else { m_attributeAdded = true; } } return TSK_OK; } /** * Callback invoked per every unallocated block in the filesystem * Creates file ranges and file entries * A single file entry per consecutive range of blocks * @param a_block block being walked * @param a_ptr a pointer to an UNALLOC_BLOCK_WLK_TRACK struct * @returns TSK_WALK_CONT if continue, otherwise TSK_WALK_STOP if stop processing requested */ TSK_WALK_RET_ENUM TskAutoDbJava::fsWalkUnallocBlocksCb(const TSK_FS_BLOCK *a_block, void *a_ptr) { UNALLOC_BLOCK_WLK_TRACK * unallocBlockWlkTrack = (UNALLOC_BLOCK_WLK_TRACK *) a_ptr; if (unallocBlockWlkTrack->tskAutoDbJava.m_stopAllProcessing) return TSK_WALK_STOP; // initialize if this is the first block if (unallocBlockWlkTrack->isStart) { unallocBlockWlkTrack->isStart = false; unallocBlockWlkTrack->curRangeStart = a_block->addr; unallocBlockWlkTrack->prevBlock = a_block->addr; unallocBlockWlkTrack->size = unallocBlockWlkTrack->fsInfo.block_size; unallocBlockWlkTrack->nextSequenceNo = 0; return TSK_WALK_CONT; } // We want to keep consecutive blocks in the same run, so simply update prevBlock and the size // if this one is consecutive with the last call. But, if we have hit the max chunk // size, then break up this set of consecutive blocks. if ((a_block->addr == unallocBlockWlkTrack->prevBlock + 1) && ((unallocBlockWlkTrack->maxChunkSize <= 0) || (unallocBlockWlkTrack->size < unallocBlockWlkTrack->maxChunkSize))) { unallocBlockWlkTrack->prevBlock = a_block->addr; unallocBlockWlkTrack->size += unallocBlockWlkTrack->fsInfo.block_size; return TSK_WALK_CONT; } // this block is not contiguous with the previous one or we've hit the maximum size; create and add a range object const uint64_t rangeStartOffset = unallocBlockWlkTrack->curRangeStart * unallocBlockWlkTrack->fsInfo.block_size + unallocBlockWlkTrack->fsInfo.offset; const uint64_t rangeSizeBytes = (1 + unallocBlockWlkTrack->prevBlock - unallocBlockWlkTrack->curRangeStart) * unallocBlockWlkTrack->fsInfo.block_size; unallocBlockWlkTrack->ranges.push_back(TSK_DB_FILE_LAYOUT_RANGE(rangeStartOffset, rangeSizeBytes, unallocBlockWlkTrack->nextSequenceNo++)); // Return (instead of adding this run) if we are going to: // a) Make one big file with all unallocated space (minChunkSize == 0) // or // b) Only make an unallocated file once we have at least chunkSize bytes // of data in our current run (minChunkSize > 0) // In either case, reset the range pointers and add this block to the size if ((unallocBlockWlkTrack->minChunkSize == 0) || ((unallocBlockWlkTrack->minChunkSize > 0) && (unallocBlockWlkTrack->size < unallocBlockWlkTrack->minChunkSize))) { unallocBlockWlkTrack->size += unallocBlockWlkTrack->fsInfo.block_size; unallocBlockWlkTrack->curRangeStart = a_block->addr; unallocBlockWlkTrack->prevBlock = a_block->addr; return TSK_WALK_CONT; } // at this point we are either chunking and have reached the chunk limit // or we're not chunking. Either way we now add what we've got to the DB int64_t fileObjId = 0; if (-1 == addUnallocBlockFile(unallocBlockWlkTrack->tskAutoDbJava.m_curUnallocDirId, unallocBlockWlkTrack->fsObjId, unallocBlockWlkTrack->size, unallocBlockWlkTrack->ranges, fileObjId, unallocBlockWlkTrack->tskAutoDbJava.m_curImgId) == TSK_ERR) { // @@@ Handle error -> Don't have access to registerError() though... } // reset unallocBlockWlkTrack->curRangeStart = a_block->addr; unallocBlockWlkTrack->prevBlock = a_block->addr; unallocBlockWlkTrack->size = unallocBlockWlkTrack->fsInfo.block_size; // The current block is part of the new range unallocBlockWlkTrack->ranges.clear(); unallocBlockWlkTrack->nextSequenceNo = 0; //we don't know what the last unalloc block is in advance //and will handle the last range in addFsInfoUnalloc() return TSK_WALK_CONT; } /** * Add unallocated space for the given file system to the database. * Create files for consecutive unalloc block ranges. * @param dbFsInfo fs to process * @returns TSK_OK on success, TSK_ERR on error */ TSK_RETVAL_ENUM TskAutoDbJava::addFsInfoUnalloc(const TSK_DB_FS_INFO & dbFsInfo) { // Unalloc space is not yet implemented for APFS if (dbFsInfo.fType == TSK_FS_TYPE_APFS) { return TSK_OK; } //open the fs we have from database TSK_FS_INFO * fsInfo = tsk_fs_open_img(m_img_info, dbFsInfo.imgOffset, dbFsInfo.fType); if (fsInfo == NULL) { tsk_error_set_errstr2("TskAutoDbJava::addFsInfoUnalloc: error opening fs at offset %" PRIdOFF, dbFsInfo.imgOffset); registerError(); return TSK_ERR; } //create a "fake" dir to hold the unalloc files for the fs if (-1 == addUnallocFsBlockFilesParent(dbFsInfo.objId, m_curUnallocDirId, m_curImgId) == TSK_ERR) { tsk_error_set_errstr2("addFsInfoUnalloc: error creating dir for unallocated space"); registerError(); return TSK_ERR; } //walk unalloc blocks on the fs and process them //initialize the unalloc block walk tracking UNALLOC_BLOCK_WLK_TRACK unallocBlockWlkTrack(*this, *fsInfo, dbFsInfo.objId, m_minChunkSize, m_maxChunkSize); uint8_t block_walk_ret = tsk_fs_block_walk(fsInfo, fsInfo->first_block, fsInfo->last_block, (TSK_FS_BLOCK_WALK_FLAG_ENUM)(TSK_FS_BLOCK_WALK_FLAG_UNALLOC | TSK_FS_BLOCK_WALK_FLAG_AONLY), fsWalkUnallocBlocksCb, &unallocBlockWlkTrack); if (block_walk_ret == 1) { stringstream errss; tsk_fs_close(fsInfo); errss << "TskAutoDbJava::addFsInfoUnalloc: error walking fs unalloc blocks, fs id: "; errss << unallocBlockWlkTrack.fsObjId; tsk_error_set_errstr2("%s", errss.str().c_str()); registerError(); return TSK_ERR; } if(m_stopAllProcessing) { tsk_fs_close(fsInfo); return TSK_OK; } // handle creation of the last range // make range inclusive from curBlockStart to prevBlock const uint64_t byteStart = unallocBlockWlkTrack.curRangeStart * fsInfo->block_size + fsInfo->offset; const uint64_t byteLen = (1 + unallocBlockWlkTrack.prevBlock - unallocBlockWlkTrack.curRangeStart) * fsInfo->block_size; unallocBlockWlkTrack.ranges.push_back(TSK_DB_FILE_LAYOUT_RANGE(byteStart, byteLen, unallocBlockWlkTrack.nextSequenceNo++)); int64_t fileObjId = 0; if (-1 == addUnallocBlockFile(m_curUnallocDirId, dbFsInfo.objId, unallocBlockWlkTrack.size, unallocBlockWlkTrack.ranges, fileObjId, m_curImgId) == TSK_ERR) { registerError(); tsk_fs_close(fsInfo); return TSK_ERR; } //cleanup tsk_fs_close(fsInfo); return TSK_OK; } /** * Process all unallocated space for this disk image and create "virtual" files with layouts * @returns TSK_OK on success, TSK_ERR on error */ TSK_RETVAL_ENUM TskAutoDbJava::addUnallocSpaceToDb() { if (m_stopAllProcessing) { return TSK_OK; } size_t numVsP = 0; size_t numFs = 0; TSK_RETVAL_ENUM retFsSpace = addUnallocFsSpaceToDb(numFs); TSK_RETVAL_ENUM retVsSpace = addUnallocVsSpaceToDb(numVsP); //handle case when no fs and no vs partitions TSK_RETVAL_ENUM retImgFile = TSK_OK; if (numVsP == 0 && numFs == 0) { retImgFile = addUnallocImageSpaceToDb(); } if (retFsSpace == TSK_ERR || retVsSpace == TSK_ERR || retImgFile == TSK_ERR) return TSK_ERR; else return TSK_OK; } /** * Process each file system in the database and add its unallocated sectors to virtual files. * @param numFs (out) number of filesystems found * @returns TSK_OK on success, TSK_ERR on error (if some or all fs could not be processed) */ TSK_RETVAL_ENUM TskAutoDbJava::addUnallocFsSpaceToDb(size_t & numFs) { vector<TSK_DB_FS_INFO> fsInfos; if(m_stopAllProcessing) { return TSK_OK; } printf("SKIPPING addUnallocFsSpaceToDb!!!!\n"); // TODO TODO /* uint16_t ret = m_db->getFsInfos(m_curImgId, fsInfos); if (ret) { tsk_error_set_errstr2("addUnallocFsSpaceToDb: error getting fs infos from db"); registerError(); return TSK_ERR; } numFs = fsInfos.size(); TSK_RETVAL_ENUM allFsProcessRet = TSK_OK; for (vector<TSK_DB_FS_INFO>::iterator it = fsInfos.begin(); it!= fsInfos.end(); ++it) { if (m_stopAllProcessing) { break; } if (addFsInfoUnalloc(*it) == TSK_ERR) allFsProcessRet = TSK_ERR; } //TODO set parent_path for newly created virt dir/file hierarchy for consistency return allFsProcessRet;*/ return TSK_OK; } /** * Process each volume in the database and add its unallocated sectors to virtual files. * @param numVsP (out) number of vs partitions found * @returns TSK_OK on success, TSK_ERR on error */ TSK_RETVAL_ENUM TskAutoDbJava::addUnallocVsSpaceToDb(size_t & numVsP) { vector<TSK_DB_VS_PART_INFO> vsPartInfos; printf("SKIPPING addUnallocVsSpaceToDb!!!!\n"); // TODO TODO /* TSK_RETVAL_ENUM retVsPartInfos = m_db->getVsPartInfos(m_curImgId, vsPartInfos); if (retVsPartInfos == TSK_ERR) { tsk_error_set_errstr2("addUnallocVsSpaceToDb: error getting vs part infos from db"); registerError(); return TSK_ERR; } numVsP = vsPartInfos.size(); //get fs infos to see if this vspart has fs vector<TSK_DB_FS_INFO> fsInfos; uint16_t retFsInfos = m_db->getFsInfos(m_curImgId, fsInfos); if (retFsInfos) { tsk_error_set_errstr2("addUnallocVsSpaceToDb: error getting fs infos from db"); registerError(); return TSK_ERR; } for (vector<TSK_DB_VS_PART_INFO>::const_iterator it = vsPartInfos.begin(); it != vsPartInfos.end(); ++it) { if (m_stopAllProcessing) { break; } const TSK_DB_VS_PART_INFO &vsPart = *it; //interested in unalloc, meta, or alloc and no fs if ( (vsPart.flags & (TSK_VS_PART_FLAG_UNALLOC | TSK_VS_PART_FLAG_META)) == 0 ) { //check if vspart has no fs bool hasFs = false; for (vector<TSK_DB_FS_INFO>::const_iterator itFs = fsInfos.begin(); itFs != fsInfos.end(); ++itFs) { const TSK_DB_FS_INFO & fsInfo = *itFs; TSK_DB_OBJECT fsObjInfo; if (m_db->getObjectInfo(fsInfo.objId, fsObjInfo) == TSK_ERR ) { stringstream errss; errss << "addUnallocVsSpaceToDb: error getting object info for fs from db, objId: " << fsInfo.objId; tsk_error_set_errstr2("%s", errss.str().c_str()); registerError(); return TSK_ERR; } if (fsObjInfo.parObjId == vsPart.objId) { hasFs = true; break; } } if (hasFs == true) { //skip processing this vspart continue; } } //end checking vspart flags //get sector size and image offset from parent vs info //get parent id of this vs part TSK_DB_OBJECT vsPartObj; if (m_db->getObjectInfo(vsPart.objId, vsPartObj) == TSK_ERR) { stringstream errss; errss << "addUnallocVsSpaceToDb: error getting object info for vs part from db, objId: " << vsPart.objId; tsk_error_set_errstr2("%s", errss.str().c_str()); registerError(); return TSK_ERR; } TSK_DB_VS_INFO vsInfo; if (m_db->getVsInfo(vsPartObj.parObjId, vsInfo) ) { stringstream errss; errss << "addUnallocVsSpaceToDb: error getting volume system info from db, objId: " << vsPartObj.parObjId; tsk_error_set_errstr2("%s", errss.str().c_str()); registerError(); return TSK_ERR; } //create an unalloc file with unalloc part, with vs part as parent vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; const uint64_t byteStart = vsInfo.offset + vsInfo.block_size * vsPart.start; const uint64_t byteLen = vsInfo.block_size * vsPart.len; TSK_DB_FILE_LAYOUT_RANGE tempRange(byteStart, byteLen, 0); ranges.push_back(tempRange); int64_t fileObjId = 0; if (m_db->addUnallocBlockFile(vsPart.objId, 0, tempRange.byteLen, ranges, fileObjId, m_curImgId) == TSK_ERR) { registerError(); return TSK_ERR; } }*/ return TSK_OK; } /** * Adds unalloc space for the image if there is no volumes and no file systems. * * @returns TSK_OK on success, TSK_ERR on error */ TSK_RETVAL_ENUM TskAutoDbJava::addUnallocImageSpaceToDb() { const TSK_OFF_T imgSize = getImageSize(); if (imgSize == -1) { tsk_error_set_errstr("addUnallocImageSpaceToDb: error getting current image size, can't create unalloc block file for the image."); registerError(); return TSK_ERR; } else { TSK_DB_FILE_LAYOUT_RANGE tempRange(0, imgSize, 0); //add unalloc block file for the entire image vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; ranges.push_back(tempRange); int64_t fileObjId = 0; if (-1 == addUnallocBlockFile(m_curImgId, 0, imgSize, ranges, fileObjId, m_curImgId)) { return TSK_ERR; } } return TSK_OK; } /** * Returns the directory currently being analyzed by processFile(). * Safe to use from another thread than processFile(). * * @returns curDirPath string representing currently analyzed directory */ const std::string TskAutoDbJava::getCurDir() { string curDirPath; tsk_take_lock(&m_curDirPathLock); curDirPath = m_curDirPath; tsk_release_lock(&m_curDirPathLock); return curDirPath; }