diff --git a/bindings/java/jni/auto_db_java.cpp b/bindings/java/jni/auto_db_java.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f85b9cd418ec8b6d62c1c27ee187bea79d2627c8 --- /dev/null +++ b/bindings/java/jni/auto_db_java.cpp @@ -0,0 +1,1305 @@ +/* + ** The Sleuth Kit + ** + ** Brian Carrier [carrier <at> sleuthkit [dot] org] + ** Copyright (c) 2010-2013 Brian Carrier. All Rights reserved + ** + ** This software is distributed under the Common Public License 1.0 + ** + */ + +/** + * \file auto_db.cpp + * Contains code to populate SQLite 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; + +/** + * @param a_db Database to add an image to + * @param a_NSRLDb Database of "known" files (can be NULL) + * @param a_knownBadDb Database of "known bad" files (can be NULL) + */ +TskAutoDb::TskAutoDb(TskDb * a_db, TSK_HDB_INFO * a_NSRLDb, TSK_HDB_INFO * a_knownBadDb) +{ + m_db = a_db; + m_curImgId = 0; + m_curVsId = 0; + m_curVolId = 0; + m_curFsId = 0; + m_curFileId = 0; + m_curUnallocDirId = 0; + m_curDirAddr = 0; + m_curDirPath = ""; + m_blkMapFlag = false; + m_vsFound = false; + m_volFound = false; + m_poolFound = false; + m_stopped = false; + m_foundStructure = false; + m_imgTransactionOpen = false; + m_attributeAdded = false; + m_NSRLDb = a_NSRLDb; + m_knownBadDb = a_knownBadDb; + if ((m_NSRLDb) || (m_knownBadDb)) { + m_fileHashFlag = true; + } + else { + m_fileHashFlag = false; + } + m_addFileSystems = true; + m_noFatFsOrphans = false; + m_addUnallocSpace = false; + m_minChunkSize = -1; + m_maxChunkSize = -1; + tsk_init_lock(&m_curDirPathLock); +} + +TskAutoDb::~TskAutoDb() +{ + // if they didn't commit / revert, then revert + if (m_imgTransactionOpen) { + revertAddImage(); + } + + closeImage(); + tsk_deinit_lock(&m_curDirPathLock); +} + +void + TskAutoDb::closeImage() +{ + TskAuto::closeImage(); + m_NSRLDb = NULL; + m_knownBadDb = NULL; +} + + +void + TskAutoDb::createBlockMap(bool flag) +{ + m_blkMapFlag = flag; +} + +void + TskAutoDb::hashFiles(bool flag) +{ + m_fileHashFlag = flag; +} + +void TskAutoDb::setAddFileSystems(bool addFileSystems) +{ + m_addFileSystems = addFileSystems; +} + +void TskAutoDb::setNoFatFsOrphans(bool noFatFsOrphans) +{ + m_noFatFsOrphans = noFatFsOrphans; +} + +void TskAutoDb::setAddUnallocSpace(bool addUnallocSpace) +{ + setAddUnallocSpace(addUnallocSpace, -1); +} + +void TskAutoDb::setAddUnallocSpace(bool addUnallocSpace, int64_t minChunkSize) +{ + m_addUnallocSpace = addUnallocSpace; + m_minChunkSize = minChunkSize; + m_maxChunkSize = -1; +} + +void TskAutoDb::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 + TskAutoDb::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 + TskAutoDb::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 +TskAutoDb::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 +TskAutoDb::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 (m_db->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 (m_db->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 TskAutoDb::filterVs(const TSK_VS_INFO * vs_info) +{ + m_vsFound = true; + if (m_db->addVsInfo(vs_info, m_curImgId, m_curVsId)) { + registerError(); + return TSK_FILTER_STOP; + } + + return TSK_FILTER_CONT; +} + +TSK_FILTER_ENUM +TskAutoDb::filterPool(const TSK_POOL_INFO * pool_info) +{ + m_poolFound = true; + + if (m_volFound && m_vsFound) { + // there's a volume system and volume + if (m_db->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 (m_db->addPoolInfoAndVS(pool_info, m_curImgId, m_curPoolVs)) { + registerError(); + return TSK_FILTER_STOP; + } + } + + + + return TSK_FILTER_CONT; +} + +TSK_FILTER_ENUM +TskAutoDb::filterPoolVol(const TSK_POOL_VOLUME_INFO * pool_vol) +{ + + if (m_db->addPoolVolumeInfo(pool_vol, m_curPoolVs, m_curPoolVol)) { + registerError(); + return TSK_FILTER_STOP; + } + + return TSK_FILTER_CONT; +} + +TSK_FILTER_ENUM +TskAutoDb::filterVol(const TSK_VS_PART_INFO * vs_part) +{ + m_volFound = true; + m_foundStructure = true; + m_poolFound = false; + + if (m_db->addVolumeInfo(vs_part, m_curVsId, m_curVolId)) { + registerError(); + return TSK_FILTER_STOP; + } + + return TSK_FILTER_CONT; +} + + +TSK_FILTER_ENUM +TskAutoDb::filterFs(TSK_FS_INFO * fs_info) +{ + TSK_FS_FILE *file_root; + m_foundStructure = true; + + if (m_poolFound) { + // there's a pool + if (m_db->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 (m_db->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 (m_db->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 + TskAutoDb::insertFileData(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) +{ + + if (m_db->addFsFile(fs_file, fs_attr, path, md5, known, 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 TskAutoDb::addFilesInImgToDb() +{ + if (m_db == NULL || m_db->isDbOpen() == false) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("addFilesInImgToDb: m_db not open"); + registerError(); + return 1; + } + + // @@@ 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 + TskAutoDb::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, "TskAutoDb::startAddImage: Starting add image process\n"); + + if (m_db->releaseSavepoint(TSK_ADD_IMAGE_SAVEPOINT) == 0) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("TskAutoDb::startAddImage(): An add-image savepoint already exists"); + registerError(); + return 1; + } + + // @@@ This check is a bit paranoid, and may need to be removed in the future + if (m_db->inTransaction()) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("TskAutoDb::startAddImage(): Already in a transaction, image might not be committed"); + registerError(); + return 1; + } + + if (m_db->createSavepoint(TSK_ADD_IMAGE_SAVEPOINT)) { + registerError(); + return 1; + } + + m_imgTransactionOpen = true; + if (openImage(numImg, imagePaths, imgType, sSize, deviceId)) { + tsk_error_set_errstr2("TskAutoDb::startAddImage"); + registerError(); + if (revertAddImage()) + 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 +TskAutoDb::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, "TskAutoDb::startAddImage: Starting add image process\n"); + + if (m_db->releaseSavepoint(TSK_ADD_IMAGE_SAVEPOINT) == 0) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("TskAutoDb::startAddImage(): An add-image savepoint already exists"); + registerError(); + return 1; + } + + // @@@ This check is a bit paranoid, and may need to be removed in the future + if (m_db->inTransaction()) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("TskAutoDb::startAddImage(): Already in a transaction, image might not be committed"); + registerError(); + return 1; + } + + if (m_db->createSavepoint(TSK_ADD_IMAGE_SAVEPOINT)) { + registerError(); + return 1; + } + + m_imgTransactionOpen = true; + if (openImage(deviceId)) { + tsk_error_set_errstr2("TskAutoDb::startAddImage"); + registerError(); + if (revertAddImage()) + 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 + TskAutoDb::startAddImage(int numImg, const char *const imagePaths[], + TSK_IMG_TYPE_ENUM imgType, unsigned int sSize, const char* deviceId) +{ + if (tsk_verbose) + tsk_fprintf(stderr, "TskAutoDb::startAddImage_utf8: Starting add image process\n"); + + + if (m_db->releaseSavepoint(TSK_ADD_IMAGE_SAVEPOINT) == 0) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("TskAutoDb::startAddImage(): An add-image savepoint already exists"); + registerError(); + return 1; + } + + // @@@ This check is a bit paranoid, and may need to be removed in the future + if (m_db->inTransaction()) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("TskAutoDb::startAddImage(): Already in a transaction, image might not be committed"); + registerError(); + return 1; + } + + + if (m_db->createSavepoint(TSK_ADD_IMAGE_SAVEPOINT)) { + registerError(); + return 1; + } + + m_imgTransactionOpen = true; + + if (openImageUtf8(numImg, imagePaths, imgType, sSize, deviceId)) { + tsk_error_set_errstr2("TskAutoDb::startAddImage"); + registerError(); + if (revertAddImage()) + 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 + TskAutoDb::stopAddImage() +{ + if (tsk_verbose) + tsk_fprintf(stderr, "TskAutoDb::stopAddImage: Stop request received\n"); + + m_stopped = true; + setStopProcessing(); + // flag is checked every time processFile() is called +} + +/** + * Revert all changes after the startAddImage() process has run successfully. + * @returns 1 on error (error was NOT registered in list), 0 on success + */ +int + TskAutoDb::revertAddImage() +{ + if (tsk_verbose) + tsk_fprintf(stderr, "TskAutoDb::revertAddImage: Reverting add image process\n"); + + if (m_imgTransactionOpen == false) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("revertAddImage(): transaction is already closed"); + return 1; + } + + int retval = m_db->revertSavepoint(TSK_ADD_IMAGE_SAVEPOINT); + if (retval == 0) { + if (m_db->inTransaction()) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("TskAutoDb::revertAddImage(): Image reverted, but still in a transaction."); + retval = 1; + } + } + m_imgTransactionOpen = false; + return retval; +} + +/** + * Finish the transaction after the startAddImage is finished. + * @returns Id of the image that was added or -1 on error (error was NOT registered in list) + */ +int64_t +TskAutoDb::commitAddImage() +{ + if (tsk_verbose) + tsk_fprintf(stderr, "TskAutoDb::commitAddImage: Committing add image process\n"); + + if (m_imgTransactionOpen == false) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("commitAddImage(): transaction is already closed"); + return -1; + } + + int retval = m_db->releaseSavepoint(TSK_ADD_IMAGE_SAVEPOINT); + m_imgTransactionOpen = false; + if (retval == 1) { + return -1; + } else { + if (m_db->inTransaction()) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("TskAutoDb::revertAddImage(): Image savepoint released, but still in a transaction."); + return -1; + } + } + + return m_curImgId; +} + +/** + * Set the current image's timezone + */ +void +TskAutoDb::setTz(string tzone) +{ + m_curImgTZone = tzone; +} + +TSK_RETVAL_ENUM +TskAutoDb::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, "TskAutoDb::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, NULL, TSK_DB_FILES_KNOWN_UNKNOWN); + } + + // 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 +TskAutoDb::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)) { + + // calculate the MD5 hash if the attribute is a file + unsigned char hash[16]; + unsigned char *md5 = NULL; + memset(hash, 0, 16); + + TSK_DB_FILES_KNOWN_ENUM file_known = TSK_DB_FILES_KNOWN_UNKNOWN; + + if (m_fileHashFlag && isFile(fs_file)) { + if (md5HashAttr(hash, fs_attr)) { + // error was registered + return TSK_OK; + } + md5 = hash; + + if (m_NSRLDb != NULL) { + int8_t retval = tsk_hdb_lookup_raw(m_NSRLDb, hash, 16, TSK_HDB_FLAG_QUICK, NULL, NULL); + if (retval == -1) { + registerError(); + return TSK_OK; + } + else if (retval) { + file_known = TSK_DB_FILES_KNOWN_KNOWN; + } + } + + if (m_knownBadDb != NULL) { + int8_t retval = tsk_hdb_lookup_raw(m_knownBadDb, hash, 16, TSK_HDB_FLAG_QUICK, NULL, NULL); + if (retval == -1) { + registerError(); + return TSK_OK; + } + else if (retval) { + file_known = TSK_DB_FILES_KNOWN_KNOWN_BAD; + } + } + } + + if (insertFileData(fs_attr->fs_file, fs_attr, path, md5, file_known) == TSK_ERR) { + registerError(); + return TSK_OK; + } + else { + m_attributeAdded = true; + } + + // add the block map, if requested and the file is non-resident + if ((m_blkMapFlag) && (isNonResident(fs_attr)) + && (isDotDir(fs_file) == 0)) { + TSK_FS_ATTR_RUN *run; + int sequence = 0; + + for (run = fs_attr->nrd.run; run != NULL; run = run->next) { + unsigned int block_size = fs_file->fs_info->block_size; + + // ignore sparse blocks + if (run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE) + continue; + + // @@@ We probably want to keep on going here + if (m_db->addFileLayoutRange(m_curFileId, + run->addr * block_size, run->len * block_size, sequence++)) { + registerError(); + return TSK_OK; + } + } + } + } + + return TSK_OK; +} + + +/** + * Helper for md5HashAttr + */ +TSK_WALK_RET_ENUM +TskAutoDb::md5HashCallback(TSK_FS_FILE * /*file*/, TSK_OFF_T /*offset*/, + TSK_DADDR_T /*addr*/, char *buf, size_t size, + TSK_FS_BLOCK_FLAG_ENUM /*a_flags*/, void *ptr) +{ + TSK_MD5_CTX *md = (TSK_MD5_CTX *) ptr; + if (md == NULL) + return TSK_WALK_CONT; + + TSK_MD5_Update(md, (unsigned char *) buf, (unsigned int) size); + + return TSK_WALK_CONT; +} + + + +/** + * MD5 hash an attribute and put the result in the given array + * @param md5Hash array to write the hash to + * @param fs_attr attribute to hash the data of + * @return Returns 1 on error (message has been registered) + */ +int +TskAutoDb::md5HashAttr(unsigned char md5Hash[16], const TSK_FS_ATTR * fs_attr) +{ + TSK_MD5_CTX md; + + TSK_MD5_Init(&md); + + if (tsk_fs_attr_walk(fs_attr, TSK_FS_FILE_WALK_FLAG_NONE, + md5HashCallback, (void *) &md)) { + registerError(); + return 1; + } + + TSK_MD5_Final(md5Hash, &md); + return 0; +} + +/** +* 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 TskAutoDb::fsWalkUnallocBlocksCb(const TSK_FS_BLOCK *a_block, void *a_ptr) { + UNALLOC_BLOCK_WLK_TRACK * unallocBlockWlkTrack = (UNALLOC_BLOCK_WLK_TRACK *) a_ptr; + + if (unallocBlockWlkTrack->tskAutoDb.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 (unallocBlockWlkTrack->tskAutoDb.m_db->addUnallocBlockFile(unallocBlockWlkTrack->tskAutoDb.m_curUnallocDirId, + unallocBlockWlkTrack->fsObjId, unallocBlockWlkTrack->size, unallocBlockWlkTrack->ranges, fileObjId, unallocBlockWlkTrack->tskAutoDb.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 TskAutoDb::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("TskAutoDb::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 (m_db->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 << "TskAutoDb::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 (m_db->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 TskAutoDb::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 TskAutoDb::addUnallocFsSpaceToDb(size_t & numFs) { + + vector<TSK_DB_FS_INFO> fsInfos; + + if(m_stopAllProcessing) { + return TSK_OK; + } + + 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; +} + +/** +* 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 TskAutoDb::addUnallocVsSpaceToDb(size_t & numVsP) { + + vector<TSK_DB_VS_PART_INFO> vsPartInfos; + + 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 TskAutoDb::addUnallocImageSpaceToDb() { + TSK_RETVAL_ENUM retImgFile = TSK_OK; + + 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(); + retImgFile = 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; + retImgFile = m_db->addUnallocBlockFile(m_curImgId, 0, imgSize, ranges, fileObjId, m_curImgId); + } + return retImgFile; +} + +/** +* 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 TskAutoDb::getCurDir() { + string curDirPath; + tsk_take_lock(&m_curDirPathLock); + curDirPath = m_curDirPath; + tsk_release_lock(&m_curDirPathLock); + return curDirPath; +} + + +bool TskAutoDb::isDbOpen() { + if(m_db!=NULL) { + return m_db->isDbOpen(); + } + return false; +} diff --git a/bindings/java/jni/auto_db_java.h b/bindings/java/jni/auto_db_java.h new file mode 100644 index 0000000000000000000000000000000000000000..ae64b92601a6ed2482584a2334cebc0398ef1c39 --- /dev/null +++ b/bindings/java/jni/auto_db_java.h @@ -0,0 +1,230 @@ +/* + ** 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.h + * Contains the class that creates a case-level database of file system + * data from the JNI code. + */ + +#ifndef _AUTO_DB_JAVA_H +#define _AUTO_DB_JAVA_H + +#include <string> +using std::string; + +#include "tsk_auto_i.h" + + +/** \internal + * C++ class that implements TskAuto to load file metadata into a database. + * This is used by the TskCaseDb class. + */ +class TskAutoDb:public TskAuto { + public: + TskAutoDb(TskDb * a_db, TSK_HDB_INFO * a_NSRLDb, TSK_HDB_INFO * a_knownBadDb); + virtual ~ TskAutoDb(); + virtual uint8_t openImage(int, const TSK_TCHAR * const images[], + TSK_IMG_TYPE_ENUM, unsigned int a_ssize, const char* deviceId = NULL); + virtual uint8_t openImage(const char* a_deviceId = NULL); + virtual uint8_t openImageUtf8(int, const char *const images[], + TSK_IMG_TYPE_ENUM, unsigned int a_ssize, const char* deviceId = NULL); + virtual void closeImage(); + virtual void setTz(string tzone); + + virtual TSK_FILTER_ENUM filterVs(const TSK_VS_INFO * vs_info); + virtual TSK_FILTER_ENUM filterVol(const TSK_VS_PART_INFO * vs_part); + virtual TSK_FILTER_ENUM filterPool(const TSK_POOL_INFO * pool_info); + virtual TSK_FILTER_ENUM filterPoolVol(const TSK_POOL_VOLUME_INFO * pool_vol); + virtual TSK_FILTER_ENUM filterFs(TSK_FS_INFO * fs_info); + virtual TSK_RETVAL_ENUM processFile(TSK_FS_FILE * fs_file, + const char *path); + virtual void createBlockMap(bool flag); + const std::string getCurDir(); + + /** + * Check if we can talk to the database. + * Returns true if the database is reachable with current credentials, false otherwise. + */ + bool isDbOpen(); + + /** + * Calculate hash values of files and add them to database. + * Default is false. Will be set to true if a Hash DB is configured. + * + * @param flag True to calculate hash values and look them up. + */ + virtual void hashFiles(bool flag); + + /** + * Sets whether or not the file systems for an image should be added when + * the image is added to the case database. The default value is true. + */ + void setAddFileSystems(bool addFileSystems); + + /** + * Skip processing of orphans on FAT filesystems. + * This will make the loading of the database much faster + * but you will not have all deleted files. Default value is false. + * @param noFatFsOrphans flag set to true if to skip processing orphans on FAT fs + */ + virtual void setNoFatFsOrphans(bool noFatFsOrphans); + + /** + * When enabled, records for unallocated file system space will be added to the database. Default value is false. + * @param addUnallocSpace If true, create records for contiguous unallocated file system sectors. + */ + virtual void setAddUnallocSpace(bool addUnallocSpace); + + /** + * When enabled, records for unallocated file system space will be added to the database. Default value is false. + * @param addUnallocSpace If true, create records for contiguous unallocated file system sectors. + * @param minChunkSize the number of bytes to group unallocated data into. A value of 0 will create + * one large chunk and group only on volume boundaries. A value of -1 will group each consecutive + * chunk. + */ + virtual void setAddUnallocSpace(bool addUnallocSpace, int64_t minChunkSize); + + /** + * When enabled, records for unallocated file system space will be added to the database with the given parameters. + * Automatically sets the flag to create records for contiguous unallocated file system sectors. + * @param minChunkSize the number of bytes to group unallocated data into. A value of 0 will create + * one large chunk and group only on volume boundaries. A value of -1 will group each consecutive + * chunk. + * @param maxChunkSize the maximum number of bytes in one record of unallocated data. A value of -1 will not + * split the records based on size + */ + virtual void setAddUnallocSpace(int64_t minChunkSize, int64_t maxChunkSize); + + uint8_t addFilesInImgToDb(); + + /** + * + */ + uint8_t startAddImage(int numImg, const TSK_TCHAR * const imagePaths[], + TSK_IMG_TYPE_ENUM imgType, unsigned int sSize, const char* deviceId = NULL); + uint8_t startAddImage(TSK_IMG_INFO * img_info, const char* deviceId = NULL); +#ifdef WIN32 + uint8_t startAddImage(int numImg, const char *const imagePaths[], + TSK_IMG_TYPE_ENUM imgType, unsigned int sSize, const char* deviceId = NULL); +#endif + void stopAddImage(); + int revertAddImage(); + int64_t commitAddImage(); + + private: + TskDb * m_db; + int64_t m_curImgId; ///< Object ID of image currently being processed + int64_t m_curVsId; ///< Object ID of volume system currently being processed + int64_t m_curVolId; ///< Object ID of volume currently being processed + int64_t m_curPoolVol; ///< Object ID of the pool volume currently being processed + int64_t m_curPoolVs; ///< Object ID of the pool volume system currently being processed + int64_t m_curFsId; ///< Object ID of file system currently being processed + int64_t m_curFileId; ///< Object ID of file currently being processed + TSK_INUM_T m_curDirAddr; ///< Meta address the directory currently being processed + int64_t m_curUnallocDirId; + string m_curDirPath; //< Path of the current directory being processed + tsk_lock_t m_curDirPathLock; //< protects concurrent access to m_curDirPath + string m_curImgTZone; + bool m_blkMapFlag; + bool m_fileHashFlag; + bool m_vsFound; + bool m_volFound; + bool m_poolFound; + bool m_stopped; + bool m_imgTransactionOpen; + TSK_HDB_INFO * m_NSRLDb; + TSK_HDB_INFO * m_knownBadDb; + bool m_addFileSystems; + bool m_noFatFsOrphans; + bool m_addUnallocSpace; + int64_t m_minChunkSize; ///< -1 for no minimum, 0 for no chunking at all, greater than 0 to wait for that number of chunks before writing to the database + int64_t m_maxChunkSize; ///< Max number of unalloc bytes to process before writing to the database, even if there is no natural break. -1 for no chunking + bool m_foundStructure; ///< Set to true when we find either a volume or file system + bool m_attributeAdded; ///< Set to true when an attribute was added by processAttributes + + // prevent copying until we add proper logic to handle it + TskAutoDb(const TskAutoDb&); + TskAutoDb & operator=(const TskAutoDb&); + + //internal structure to keep track of temp. unalloc block range + typedef struct _UNALLOC_BLOCK_WLK_TRACK { + _UNALLOC_BLOCK_WLK_TRACK(const TskAutoDb & tskAutoDb, const TSK_FS_INFO & fsInfo, const int64_t fsObjId, int64_t minChunkSize, int64_t maxChunkSize) + : tskAutoDb(tskAutoDb),fsInfo(fsInfo),fsObjId(fsObjId),curRangeStart(0), minChunkSize(minChunkSize), maxChunkSize(maxChunkSize), prevBlock(0), isStart(true), nextSequenceNo(0) {} + const TskAutoDb & tskAutoDb; + const TSK_FS_INFO & fsInfo; + const int64_t fsObjId; + vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; + TSK_DADDR_T curRangeStart; + int64_t size; + const int64_t minChunkSize; + const int64_t maxChunkSize; + TSK_DADDR_T prevBlock; + bool isStart; + uint32_t nextSequenceNo; + } UNALLOC_BLOCK_WLK_TRACK; + + uint8_t addImageDetails(const char *); + TSK_RETVAL_ENUM insertFileData(TSK_FS_FILE * fs_file, + const TSK_FS_ATTR *, const char *path, + const unsigned char *const md5, + const TSK_DB_FILES_KNOWN_ENUM known); + virtual TSK_RETVAL_ENUM processAttribute(TSK_FS_FILE *, + const TSK_FS_ATTR * fs_attr, const char *path); + static TSK_WALK_RET_ENUM md5HashCallback(TSK_FS_FILE * file, + TSK_OFF_T offset, TSK_DADDR_T addr, char *buf, size_t size, + TSK_FS_BLOCK_FLAG_ENUM a_flags, void *ptr); + int md5HashAttr(unsigned char md5Hash[16], const TSK_FS_ATTR * fs_attr); + + static TSK_WALK_RET_ENUM fsWalkUnallocBlocksCb(const TSK_FS_BLOCK *a_block, void *a_ptr); + TSK_RETVAL_ENUM addFsInfoUnalloc(const TSK_DB_FS_INFO & dbFsInfo); + TSK_RETVAL_ENUM addUnallocFsSpaceToDb(size_t & numFs); + TSK_RETVAL_ENUM addUnallocVsSpaceToDb(size_t & numVsP); + TSK_RETVAL_ENUM addUnallocImageSpaceToDb(); + TSK_RETVAL_ENUM addUnallocSpaceToDb(); + +}; + + +#define TSK_CASE_DB_TAG 0xB0551A33 + +/** + * Stores case-level information in a database on one or more disk images. + */ +class TskCaseDb { + public: + unsigned int m_tag; + + ~TskCaseDb(); + + static TskCaseDb *newDb(const TSK_TCHAR * path); + static TskCaseDb *newDb(const TSK_TCHAR * const path, CaseDbConnectionInfo * info); + static TskCaseDb *openDb(const TSK_TCHAR * path); + static TskCaseDb *openDb(const TSK_TCHAR * path, CaseDbConnectionInfo * info); + + void clearLookupDatabases(); + uint8_t setNSRLHashDb(TSK_TCHAR * const indexFile); + uint8_t setKnownBadHashDb(TSK_TCHAR * const indexFile); + + uint8_t addImage(int numImg, const TSK_TCHAR * const imagePaths[], + TSK_IMG_TYPE_ENUM imgType, unsigned int sSize); + TskAutoDb *initAddImage(); + + private: + // prevent copying until we add proper logic to handle it + TskCaseDb(const TskCaseDb&); + TskCaseDb & operator=(const TskCaseDb&); + TskCaseDb(TskDb * a_db); + TskDb *m_db; + TSK_HDB_INFO * m_NSRLDb; + TSK_HDB_INFO * m_knownBadDb; +}; + +#endif