diff --git a/bindings/java/jni/auto_db_java.cpp b/bindings/java/jni/auto_db_java.cpp index 56b2d758106400ae7f574ef3ad438078a9db2e86..969d87ba117d3a9b4959ad27feb27c116d42b62f 100644 --- a/bindings/java/jni/auto_db_java.cpp +++ b/bindings/java/jni/auto_db_java.cpp @@ -110,16 +110,11 @@ TskAutoDbJava::initializeJni(JNIEnv * jniEnv, jobject jobj) { return TSK_ERR; } - m_addFileMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addFile", "(JJJIIILjava/lang/String;JJIIIIJJJJJIIILjava/lang/String;Ljava/lang/String;)J"); + m_addFileMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addFile", "(JJJIIILjava/lang/String;JJIIIIJJJJJIIILjava/lang/String;Ljava/lang/String;JJJ)J"); if (m_addFileMethodID == NULL) { return TSK_ERR; } - m_getParentIdMethodID = m_jniEnv->GetMethodID(m_callbackClass, "findParentObjId", "(JJLjava/lang/String;Ljava/lang/String;)J"); - if (m_getParentIdMethodID == NULL) { - return TSK_ERR; - } - m_addUnallocParentMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addUnallocFsBlockFilesParent", "(JLjava/lang/String;)J"); if (m_addUnallocParentMethodID == NULL) { return TSK_ERR; @@ -427,7 +422,7 @@ TskAutoDbJava::addFsFile(TSK_FS_FILE* fs_file, if (fs_file->name == NULL) return TSK_ERR; - // Find the object id for the parent folder. + // The object id for the parent folder. Will stay as zero if not the root folder int64_t parObjId = 0; // Root directory's parent should be the file system object. @@ -436,16 +431,10 @@ TskAutoDbJava::addFsFile(TSK_FS_FILE* fs_file, ((fs_file->name->name == NULL) || (strlen(fs_file->name->name) == 0))) { // File is in the root directory parObjId = fsObjId; - } else { - // Look up parent object ID - parObjId = findParObjId(fs_file, path, fsObjId); - if (parObjId == -1) { - return TSK_ERR; - } } // Add the file to the database - return addFile(fs_file, fs_attr, path, fsObjId, parObjId, objId, dataSourceObjId); + return addFile(fs_file, fs_attr, path, fsObjId, parObjId, dataSourceObjId); } /** @@ -471,61 +460,15 @@ void extractExtension(char *name, char *extension) { } /** -* Store info about a directory in a complex map structure as a cache for the -* files who are a child of this directory and want to know its object id. -* -* @param fsObjId fs id of this directory -* @param fs_file File for the directory to store -* @param path Full path (parent and this file) of the directory -* @param objId object id of the directory -*/ -void TskAutoDbJava::storeObjId(const int64_t& fsObjId, const TSK_FS_FILE* fs_file, const char* path, const int64_t& objId) -{ - // skip the . and .. entries - if ((fs_file->name) && (fs_file->name->name) && (TSK_FS_ISDOT(fs_file->name->name))) - { - return; - } - - uint32_t seq; - uint32_t path_hash = hash((const unsigned char *)path); - - /* NTFS uses sequence, otherwise we hash the path. We do this to map to the - * correct parent folder if there are two from the root dir that eventually point to - * the same folder (one deleted and one allocated) or two hard links. */ - if (TSK_FS_TYPE_ISNTFS(fs_file->fs_info->ftype)) { - /* Use the sequence stored in meta (which could be one larger than the name value - * if the directory is deleted. We do this because the par_seq gets added to the - * name structure when it is added to the directory based on teh value stored in - * meta. */ - seq = fs_file->meta->seq; - } - else { - seq = path_hash; - } - - map<TSK_INUM_T, map<uint32_t, map<uint32_t, int64_t> > >& fsMap = m_parentDirIdCache[fsObjId]; - if (fsMap.count(fs_file->name->meta_addr) == 0) { - fsMap[fs_file->name->meta_addr][seq][path_hash] = objId; - } - else { - map<uint32_t, map<uint32_t, int64_t> >& fileMap = fsMap[fs_file->name->meta_addr]; - if (fileMap.count(seq) == 0) { - fileMap[seq][path_hash] = objId; - } - } -} - - -/** -* Adds a file and its associated slack file to database. Object ID for new file stored in objId. +* Adds a file and its associated slack file to database. +* Does not learn object ID for new files, and files may +* not be added to the database immediately. * * @param fs_file * @param fs_attr * @param path File path * @param fsObjId Object ID of the file system -* @param parObjId Parent object ID -* @param objId Object ID of new file +* @param parObjId Parent object ID if known, 0 otherwise * @param dataSourceObjId Object ID of the data source * @returns TSK_ERR on error, TSK_OK on success */ @@ -533,7 +476,7 @@ TSK_RETVAL_ENUM TskAutoDbJava::addFile(TSK_FS_FILE* fs_file, const TSK_FS_ATTR* fs_attr, const char* path, int64_t fsObjId, int64_t parObjId, - int64_t& objId, int64_t dataSourceObjId) + int64_t dataSourceObjId) { time_t mtime = 0; time_t crtime = 0; @@ -543,6 +486,7 @@ TskAutoDbJava::addFile(TSK_FS_FILE* fs_file, int meta_type = 0; int meta_flags = 0; int meta_mode = 0; + int meta_seq = 0; int gid = 0; int uid = 0; int type = TSK_FS_ATTR_TYPE_NOT_FOUND; @@ -561,6 +505,7 @@ TskAutoDbJava::addFile(TSK_FS_FILE* fs_file, meta_mode = fs_file->meta->mode; gid = fs_file->meta->gid; uid = fs_file->meta->uid; + meta_seq = fs_file->meta->seq; } size_t attr_nlen = 0; @@ -616,9 +561,22 @@ TskAutoDbJava::addFile(TSK_FS_FILE* fs_file, jstring namej = m_jniEnv->NewStringUTF(name); jstring pathj = m_jniEnv->NewStringUTF(escaped_path); jstring extj = m_jniEnv->NewStringUTF(extension); + + /* NTFS uses sequence, otherwise we hash the path. We do this to map to the + * correct parent folder if there are two from the root dir that eventually point to + * the same folder (one deleted and one allocated) or two hard links. */ + jlong par_seqj; + if (TSK_FS_TYPE_ISNTFS(fs_file->fs_info->ftype)) + { + par_seqj = fs_file->name->par_seq; + } + else { + par_seqj = -1; + } + TSK_INUM_T par_meta_addr = fs_file->name->par_addr; // Add the file to the database - jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addFileMethodID, + jlong ret_val = m_jniEnv->CallLongMethod(m_javaDbObj, m_addFileMethodID, parObjId, fsObjId, dataSourceObjId, TSK_DB_FILES_TYPE_FS, @@ -628,19 +586,13 @@ TskAutoDbJava::addFile(TSK_FS_FILE* fs_file, size, (unsigned long long)crtime, (unsigned long long)ctime, (unsigned long long) atime, (unsigned long long) mtime, meta_mode, gid, uid, - pathj, extj); - objId = (int64_t)objIdj; + pathj, extj, + (uint64_t)meta_seq, par_meta_addr, par_seqj); - if (objId < 0) { + if (ret_val < 0) { return TSK_ERR; } - // If dir, update parent ID cache - if (TSK_FS_IS_DIR_META(meta_type)){ - std::string fullPath = std::string(path) + fs_file->name->name; - storeObjId(fsObjId, fs_file, fullPath.c_str(), objId); - } - // Add entry for the slack space. // Current conditions for creating a slack file: // - File name is not empty, "." or ".." @@ -662,7 +614,7 @@ TskAutoDbJava::addFile(TSK_FS_FILE* fs_file, TSK_OFF_T slackSize = fs_attr->nrd.allocsize - fs_attr->nrd.initsize; // Add slack file to database - jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addFileMethodID, + jlong ret_val = m_jniEnv->CallLongMethod(m_javaDbObj, m_addFileMethodID, parObjId, fsObjId, dataSourceObjId, TSK_DB_FILES_TYPE_SLACK, @@ -672,10 +624,10 @@ TskAutoDbJava::addFile(TSK_FS_FILE* fs_file, slackSize, (unsigned long long)crtime, (unsigned long long)ctime, (unsigned long long) atime, (unsigned long long) mtime, meta_mode, gid, uid, // md5TextPtr, known, - pathj, slackExtj); - int64_t slackObjId = (int64_t)objIdj; + pathj, slackExtj, + (uint64_t)meta_seq, par_meta_addr, par_seqj); - if (slackObjId < 0) { + if (ret_val < 0) { return TSK_ERR; } } @@ -875,161 +827,6 @@ TskAutoDbJava::addUnallocFsBlockFilesParent(const int64_t fsObjId, int64_t& objI return TSK_OK; } -/** -* Return a hash of the passed in string. We use this -* for full paths. -* From: http://www.cse.yorku.ca/~oz/hash.html -*/ -uint32_t -TskAutoDbJava::hash(const unsigned char* str) -{ - uint32_t hash = 5381; - int c; - - while ((c = *str++)) { - // skip slashes -> normalizes leading/ending/double slashes - if (c == '/') - continue; - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - } - - return hash; -} - -/* -* Utility method to break up path into parent folder and folder/file name. -* @param path Path of folder that we want to analyze -* @param ret_parent_path pointer to parent path (begins and ends with '/') -* @param ret_name pointer to final folder/file name -* @returns 0 on success, 1 on error -*/ -bool -TskAutoDbJava::getParentPathAndName(const char *path, const char **ret_parent_path, const char **ret_name) { - // Need to break up 'path' in to the parent folder to match in 'parent_path' and the folder - // name to match with the 'name' column in tsk_files table - - // reset all arrays - parent_name[0] = '\0'; - parent_path[0] = '\0'; - - size_t path_len = strlen(path); - if (path_len >= MAX_PATH_LENGTH_JAVA_DB_LOOKUP) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_AUTO_DB); - tsk_error_set_errstr("TskDb::getParentPathAndName: Path is too long. Length = %zd, Max length = %d", path_len, MAX_PATH_LENGTH_JAVA_DB_LOOKUP); - // assign return values to pointers - *ret_parent_path = ""; - *ret_name = ""; - return 1; - } - - // check if empty path or just "/" were passed in - if (path_len == 0 || (strcmp(path, "/") == 0)) { - *ret_name = ""; - *ret_parent_path = "/"; - return 0; - } - - - // step 1, copy everything into parent_path and clean it up - // add leading slash if its not in input. - if (path[0] != '/') { - sprintf(parent_path, "%s", "/"); - } - - strncat(parent_path, path, MAX_PATH_LENGTH_JAVA_DB_LOOKUP); - - // remove trailing slash - if (parent_path[strlen(parent_path) - 1] == '/') { - parent_path[strlen(parent_path) - 1] = '\0'; - } - - // replace all non-UTF8 characters - tsk_cleanupUTF8(parent_path, '^'); - - // Step 2, move the final folder/file to parent_file - - // Find the last '/' - char *chptr = strrchr(parent_path, '/'); - if (chptr) { - // character found in the string - size_t position = chptr - parent_path; - - sprintf(parent_name, "%s", chptr + 1); // copy everything after slash into parent_name - *ret_name = parent_name; - - parent_path[position + 1] = '\0'; // add terminating null after last "/" - *ret_parent_path = parent_path; - } - else { - // "/" character not found. the entire path is parent file name. parent path is "/" - *ret_name = parent_path; - *ret_parent_path = "/"; - } - return 0; -} - -/** -* Find parent object id of TSK_FS_FILE. Use local cache map, if not found, fall back to SQL -* @param fs_file file to find parent obj id for -* @param parentPath Path of parent folder that we want to match -* @param fsObjId fs id of this file -* @returns parent obj id ( > 0), -1 on error -*/ -int64_t -TskAutoDbJava::findParObjId(const TSK_FS_FILE* fs_file, const char* parentPath, const int64_t& fsObjId) -{ - uint32_t seq; - uint32_t path_hash = hash((const unsigned char *)parentPath); - - /* NTFS uses sequence, otherwise we hash the path. We do this to map to the - * correct parent folder if there are two from the root dir that eventually point to - * the same folder (one deleted and one allocated) or two hard links. */ - if (TSK_FS_TYPE_ISNTFS(fs_file->fs_info->ftype)) - { - seq = fs_file->name->par_seq; - } - else - { - seq = path_hash; - } - - //get from cache by parent meta addr, if available - map<TSK_INUM_T, map<uint32_t, map<uint32_t, int64_t> > >& fsMap = m_parentDirIdCache[fsObjId]; - if (fsMap.count(fs_file->name->par_addr) > 0) - { - map<uint32_t, map<uint32_t, int64_t> >& fileMap = fsMap[fs_file->name->par_addr]; - if (fileMap.count(seq) > 0) { - map<uint32_t, int64_t>& pathMap = fileMap[seq]; - if (pathMap.count(path_hash) > 0) { - return pathMap[path_hash]; - } - } - } - - // Need to break up 'path' in to the parent folder to match in 'parent_path' and the folder - // name to match with the 'name' column in tsk_files table - const char *parent_name = ""; - const char *parent_path = ""; - if (getParentPathAndName(parentPath, &parent_path, &parent_name)) { - return -1; - } - - jstring jpath = m_jniEnv->NewStringUTF(parent_path); - jstring jname = m_jniEnv->NewStringUTF(parent_name); - - // Look up in the database - jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_getParentIdMethodID, - fs_file->name->par_addr, fsObjId, jpath, jname); - int64_t objId = (int64_t)objIdj; - - if (objId < 0) { - return -1; - } - - return objId; -} - /** * Adds a new volume that will hold the unallocated blocks for the pool. * diff --git a/bindings/java/jni/auto_db_java.h b/bindings/java/jni/auto_db_java.h index 85a9dc69eb1d006032ef6a2ba7470dfd563f95ae..e6c5e68dd7217d784695749cf53c24df090db75d 100644 --- a/bindings/java/jni/auto_db_java.h +++ b/bindings/java/jni/auto_db_java.h @@ -143,16 +143,6 @@ class TskAutoDbJava :public TskAuto { std::map<int64_t, int64_t> m_poolOffsetToParentId; std::map<int64_t, int64_t> m_poolOffsetToVsId; - // Used to look up object IDs for files - #define MAX_PATH_LENGTH_JAVA_DB_LOOKUP 2048 - char parent_name[MAX_PATH_LENGTH_JAVA_DB_LOOKUP]; - char parent_path[MAX_PATH_LENGTH_JAVA_DB_LOOKUP + 2]; // +2 is for leading slash and trailing slash - map<int64_t, map<TSK_INUM_T, map<uint32_t, map<uint32_t, int64_t> > > > m_parentDirIdCache; //maps a file system ID to a map, which maps a directory file system meta address to a map, which maps a sequence ID to a map, which maps a hash of a path to its object ID in the database - int64_t findParObjId(const TSK_FS_FILE* fs_file, const char* parentPath, const int64_t& fsObjId); - bool getParentPathAndName(const char *path, const char **ret_parent_path, const char **ret_name); - void storeObjId(const int64_t& fsObjId, const TSK_FS_FILE* fs_file, const char* path, const int64_t& objId); - uint32_t hash(const unsigned char* str); - // JNI data JNIEnv * m_jniEnv = NULL; jclass m_callbackClass = NULL; @@ -164,7 +154,6 @@ class TskAutoDbJava :public TskAuto { jmethodID m_addPoolMethodID = NULL; jmethodID m_addFileSystemMethodID = NULL; jmethodID m_addFileMethodID = NULL; - jmethodID m_getParentIdMethodID = NULL; jmethodID m_addUnallocParentMethodID = NULL; jmethodID m_addLayoutFileMethodID = NULL; jmethodID m_addLayoutFileRangeMethodID = NULL; @@ -228,7 +217,7 @@ class TskAutoDbJava :public TskAuto { TSK_RETVAL_ENUM addFile(TSK_FS_FILE* fs_file, const TSK_FS_ATTR* fs_attr, const char* path, int64_t fsObjId, int64_t parObjId, - int64_t& objId, int64_t dataSourceObjId); + int64_t dataSourceObjId); TSK_RETVAL_ENUM 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, diff --git a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java index a091dcfe60676ce95ef0f050f287aa26b4715d90..7cbe2b0f91f40d0661f6a38ef671c67d87d92d50 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java +++ b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java @@ -180,11 +180,14 @@ private void createFileTables(Statement stmt) throws SQLException { + "time_zone TEXT NOT NULL, acquisition_details TEXT, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)"); stmt.execute("CREATE TABLE tsk_fs_info (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, " + + "data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " + "img_offset " + dbQueryHelper.getBigIntType() + " NOT NULL, fs_type INTEGER NOT NULL, " + "block_size " + dbQueryHelper.getBigIntType() + " NOT NULL, " + "block_count " + dbQueryHelper.getBigIntType() + " NOT NULL, root_inum " + dbQueryHelper.getBigIntType() + " NOT NULL, " + "first_inum " + dbQueryHelper.getBigIntType() + " NOT NULL, last_inum " + dbQueryHelper.getBigIntType() + " NOT NULL, " - + "display_name TEXT, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)"); + + "display_name TEXT, " + + "FOREIGN KEY(data_source_obj_id) REFERENCES data_source_info(obj_id) ON DELETE CASCADE, " + + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE)"); stmt.execute("CREATE TABLE tsk_files (obj_id " + dbQueryHelper.getPrimaryKey() + " PRIMARY KEY, " + "fs_obj_id " + dbQueryHelper.getBigIntType() + ", data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " diff --git a/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java b/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java index 2e9bc72106ebea19430ba617dee1f9a232a306f3..b65ad77b136adda0578a7bce53ed9b2f56945902 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java +++ b/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java @@ -18,8 +18,12 @@ */ package org.sleuthkit.datamodel; +import org.apache.commons.lang3.StringUtils; +import java.util.List; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; @@ -38,6 +42,12 @@ class JniDbHelper { private CaseDbTransaction trans = null; private final Map<Long, Long> fsIdToRootDir = new HashMap<>(); + private final Map<Long, TskData.TSK_FS_TYPE_ENUM> fsIdToFsType = new HashMap<>(); + private final Map<ParentCacheKey, Long> parentDirCache = new HashMap<>(); + + private static final long BATCH_FILE_THRESHOLD = 500; + private final List<FileInfo> batchedFiles = new ArrayList<>(); + private final List<LayoutRangeInfo> batchedLayoutRanges = new ArrayList<>(); JniDbHelper(SleuthkitCase caseDb) { this.caseDb = caseDb; @@ -45,34 +55,47 @@ class JniDbHelper { } /** - * Start the add image transaction + * Start a transaction * * @throws TskCoreException */ - void beginTransaction() throws TskCoreException { + private void beginTransaction() throws TskCoreException { trans = caseDb.beginTransaction(); + trans.acquireSingleUserCaseWriteLock(); } /** - * Commit the add image transaction + * Commit the current transaction * * @throws TskCoreException */ - void commitTransaction() throws TskCoreException { + private void commitTransaction() throws TskCoreException { trans.commit(); trans = null; } /** - * Revert the add image transaction - * - * @throws TskCoreException + * Revert the current transaction */ - void revertTransaction() throws TskCoreException { - trans.rollback(); - trans = null; + private void revertTransaction() { + try { + if (trans != null) { + trans.rollback(); + trans = null; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error rolling back transaction", ex); + } } + /** + * Add any remaining files to the database. + */ + void finish() { + addBatchedFilesToDb(); + addBatchedLayoutRangesToDb(); + } + /** * Add a new image to the database. * Intended to be called from the native code during the add image process. @@ -91,14 +114,18 @@ void revertTransaction() throws TskCoreException { */ long addImageInfo(int type, long ssize, String timezone, long size, String md5, String sha1, String sha256, String deviceId, - String collectionDetails) { + String collectionDetails) { try { - return caseDb.addImageJNI(TskData.TSK_IMG_TYPE_ENUM.valueOf(type), ssize, size, + beginTransaction(); + long objId = caseDb.addImageJNI(TskData.TSK_IMG_TYPE_ENUM.valueOf(type), ssize, size, timezone, md5, sha1, sha256, deviceId, collectionDetails, trans); + commitTransaction(); + return objId; } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding image to the database", ex); + revertTransaction(); return -1; - } + } } /** @@ -113,11 +140,14 @@ long addImageInfo(int type, long ssize, String timezone, */ int addImageName(long objId, String name, long sequence) { try { + beginTransaction(); caseDb.addImageNameJNI(objId, name, sequence, trans); + commitTransaction(); return 0; } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding image name to the database - image obj ID: " + objId + ", image name: " + name + ", sequence: " + sequence, ex); + revertTransaction(); return -1; } } @@ -135,11 +165,14 @@ int addImageName(long objId, String name, long sequence) { */ long addVsInfo(long parentObjId, int vsType, long imgOffset, long blockSize) { try { + beginTransaction(); VolumeSystem vs = caseDb.addVolumeSystem(parentObjId, TskData.TSK_VS_TYPE_ENUM.valueOf(vsType), imgOffset, blockSize, trans); + commitTransaction(); return vs.getId(); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding volume system to the database - parent obj ID: " + parentObjId + ", image offset: " + imgOffset, ex); + revertTransaction(); return -1; } } @@ -160,11 +193,14 @@ long addVsInfo(long parentObjId, int vsType, long imgOffset, long blockSize) { long addVolume(long parentObjId, long addr, long start, long length, String desc, long flags) { try { + beginTransaction(); Volume vol = caseDb.addVolume(parentObjId, addr, start, length, desc, flags, trans); + commitTransaction(); return vol.getId(); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding volume to the database - parent object ID: " + parentObjId + ", addr: " + addr, ex); + revertTransaction(); return -1; } } @@ -180,10 +216,13 @@ long addVolume(long parentObjId, long addr, long start, long length, String desc */ long addPool(long parentObjId, int poolType) { try { + beginTransaction(); Pool pool = caseDb.addPool(parentObjId, TskData.TSK_POOL_TYPE_ENUM.valueOf(poolType), trans); + commitTransaction(); return pool.getId(); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding pool to the database - parent object ID: " + parentObjId, ex); + revertTransaction(); return -1; } } @@ -206,21 +245,26 @@ long addPool(long parentObjId, int poolType) { long addFileSystem(long parentObjId, long imgOffset, int fsType, long blockSize, long blockCount, long rootInum, long firstInum, long lastInum) { try { + beginTransaction(); FileSystem fs = caseDb.addFileSystem(parentObjId, imgOffset, TskData.TSK_FS_TYPE_ENUM.valueOf(fsType), blockSize, blockCount, rootInum, firstInum, lastInum, null, trans); + commitTransaction(); + fsIdToFsType.put(fs.getId(), TskData.TSK_FS_TYPE_ENUM.valueOf(fsType)); return fs.getId(); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding file system to the database - parent object ID: " + parentObjId + ", offset: " + imgOffset, ex); + revertTransaction(); return -1; } } /** - * Add a file to the database. + * Add a file to the database. + * File inserts are batched so the file may not be added immediately. * Intended to be called from the native code during the add image process. * - * @param parentObjId The parent of the file. + * @param parentObjId The parent of the file if known or 0 if unknown. * @param fsObjId The object ID of the file system. * @param dataSourceObjId The data source object ID. * @param fsType The type. @@ -228,7 +272,7 @@ long addFileSystem(long parentObjId, long imgOffset, int fsType, long blockSize, * @param attrId The type id given to the file by the file system. * @param name The name of the file. * @param metaAddr The meta address of the file. - * @param metaSeq The meta sequence number of the file. + * @param metaSeq The meta sequence number of the file from fs_file->name->meta_seq. * @param dirType The type of the file, usually as reported in * the name structure of the file system. * @param metaType The type of the file, usually as reported in @@ -249,8 +293,11 @@ long addFileSystem(long parentObjId, long imgOffset, int fsType, long blockSize, * @param known The file known status. * @param escaped_path The escaped path to the file. * @param extension The file extension. + * @param seq The sequence number from fs_file->meta->seq. + * @param parMetaAddr The metadata address of the parent + * @param parSeq The parent sequence number if NTFS, -1 otherwise. * - * @return The object ID of the new file or -1 if an error occurred + * @return 0 if successful, -1 if not */ long addFile(long parentObjId, long fsObjId, long dataSourceObjId, @@ -261,9 +308,11 @@ long addFile(long parentObjId, long size, long crtime, long ctime, long atime, long mtime, int meta_mode, int gid, int uid, - String escaped_path, String extension) { - try { - long objId = caseDb.addFileJNI(parentObjId, + String escaped_path, String extension, + long seq, long parMetaAddr, long parSeq) { + + // Add the new file to the list + batchedFiles.add(new FileInfo(parentObjId, fsObjId, dataSourceObjId, fsType, attrType, attrId, name, @@ -272,21 +321,105 @@ long addFile(long parentObjId, size, crtime, ctime, atime, mtime, meta_mode, gid, uid, - null, TskData.FileKnown.UNKNOWN, - escaped_path, extension, - false, trans); - - // If we're adding the root directory for the file system, cache it - if (parentObjId == fsObjId) { - fsIdToRootDir.put(fsObjId, objId); + escaped_path, extension, + seq, parMetaAddr, parSeq)); + + // Add the current files to the database if we've exceeded the threshold or if we + // have the root folder. + if ((fsObjId == parentObjId) + || (batchedFiles.size() > BATCH_FILE_THRESHOLD)) { + return addBatchedFilesToDb(); + } + return 0; + } + + /** + * Add the current set of files to the database. + * + * @return 0 if successful, -1 if not + */ + private long addBatchedFilesToDb() { + try { + beginTransaction(); + for (FileInfo fileInfo : batchedFiles) { + long computedParentObjId = fileInfo.parentObjId; + try { + // If we weren't given the parent object ID, look it up + if (fileInfo.parentObjId == 0) { + computedParentObjId = getParentObjId(fileInfo); + } + + long objId = caseDb.addFileJNI(computedParentObjId, + fileInfo.fsObjId, fileInfo.dataSourceObjId, + fileInfo.fsType, + fileInfo.attrType, fileInfo.attrId, fileInfo.name, + fileInfo.metaAddr, fileInfo.metaSeq, + fileInfo.dirType, fileInfo.metaType, fileInfo.dirFlags, fileInfo.metaFlags, + fileInfo.size, + fileInfo.crtime, fileInfo.ctime, fileInfo.atime, fileInfo.mtime, + fileInfo.meta_mode, fileInfo.gid, fileInfo.uid, + null, TskData.FileKnown.UNKNOWN, + fileInfo.escaped_path, fileInfo.extension, + false, trans); + + // If we're adding the root directory for the file system, cache it + if (fileInfo.parentObjId == fileInfo.fsObjId) { + fsIdToRootDir.put(fileInfo.fsObjId, objId); + } + + // If the file is a directory, cache the object ID + if ((fileInfo.metaType == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue() + || (fileInfo.metaType == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT_DIR.getValue())) + && (fileInfo.name != null) + && ! fileInfo.name.equals(".") + && ! fileInfo.name.equals("..")) { + String dirName = fileInfo.escaped_path + fileInfo.name; + ParentCacheKey key = new ParentCacheKey(fileInfo.fsObjId, fileInfo.metaAddr, fileInfo.seq, dirName); + parentDirCache.put(key, objId); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding file to the database - parent object ID: " + computedParentObjId + + ", file system object ID: " + fileInfo.fsObjId + ", name: " + fileInfo.name, ex); + revertTransaction(); + return -1; + } } - return objId; + commitTransaction(); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding file to the database - parent object ID: " + parentObjId - + ", file system object ID: " + fsObjId + ", name: " + name, ex); + logger.log(Level.SEVERE, "Error adding batched files to database", ex); + revertTransaction(); return -1; } + batchedFiles.clear(); + return 0; } + + /** + * Look up the parent object ID for a file using the cache or the database. + * + * @param fileInfo The file to find the parent of + * + * @return Parent object ID + * + * @throws TskCoreException + */ + private long getParentObjId(FileInfo fileInfo) throws TskCoreException { + // Remove the final slash from the path unless we're in the root folder + String parentPath = fileInfo.escaped_path; + if(parentPath.endsWith("/") && ! parentPath.equals("/")) { + parentPath = parentPath.substring(0, parentPath.lastIndexOf('/')); + } + + // Look up the parent + ParentCacheKey key = new ParentCacheKey(fileInfo.fsObjId, fileInfo.parMetaAddr, fileInfo.parSeq, parentPath); + if (parentDirCache.containsKey(key)) { + return parentDirCache.get(key); + } else { + // The parent wasn't found in the cache so do a database query + java.io.File parentAsFile = new java.io.File(parentPath); + return caseDb.findParentObjIdJNI(fileInfo.parMetaAddr, fileInfo.fsObjId, parentAsFile.getPath(), parentAsFile.getName(), trans); + } + } /** * Add a layout file to the database. @@ -306,13 +439,15 @@ long addLayoutFile(long parentObjId, int fileType, String name, long size) { try { + // The file system may be null for layout files Long fsObjIdForDb = fsObjId; if (fsObjId == 0) { fsObjIdForDb = null; } - return caseDb.addFileJNI(parentObjId, + beginTransaction(); + long objId = caseDb.addFileJNI(parentObjId, fsObjIdForDb, dataSourceObjId, fileType, null, null, name, @@ -327,9 +462,12 @@ long addLayoutFile(long parentObjId, null, TskData.FileKnown.UNKNOWN, null, null, true, trans); + commitTransaction(); + return objId; } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding layout file to the database - parent object ID: " + parentObjId + ", file system object ID: " + fsObjId + ", name: " + name, ex); + revertTransaction(); return -1; } } @@ -346,32 +484,39 @@ long addLayoutFile(long parentObjId, * @return 0 if successful, -1 if not */ long addLayoutFileRange(long objId, long byteStart, long byteLen, long seq) { - try { - caseDb.addLayoutFileRangeJNI(objId, byteStart, byteLen, seq, trans); - return 0; - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding layout file range to the database - layout file ID: " + objId - + ", byte start: " + byteStart, ex); - return -1; + batchedLayoutRanges.add(new LayoutRangeInfo(objId, byteStart, byteLen, seq)); + + if (batchedLayoutRanges.size() > BATCH_FILE_THRESHOLD) { + return addBatchedLayoutRangesToDb(); } + return 0; } /** - * Look up the parent of a file based on metadata address and name/path. - * Intended to be called from the native code during the add image process. - * - * @param metaAddr - * @param fsObjId - * @param path - * @param name + * Add the current set of layout ranges to the database. * - * @return The object ID of the parent or -1 if not found + * @return 0 if successful, -1 if not */ - long findParentObjId(long metaAddr, long fsObjId, String path, String name) { + private long addBatchedLayoutRangesToDb() { try { - return caseDb.findParentObjIdJNI(metaAddr, fsObjId, path, name, trans); + beginTransaction(); + + for (LayoutRangeInfo range : batchedLayoutRanges) { + try { + caseDb.addLayoutFileRangeJNI(range.objId, range.byteStart, range.byteLen, range.seq, trans); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding layout file range to the database - layout file ID: " + range.objId + + ", byte start: " + range.byteStart, ex); + revertTransaction(); + return -1; + } + } + commitTransaction(); + batchedLayoutRanges.clear(); + return 0; } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error looking up parent with meta addr: " + metaAddr + " and name " + name, ex); + logger.log(Level.SEVERE, "Error adding batched files to database", ex); + revertTransaction(); return -1; } } @@ -391,10 +536,163 @@ long addUnallocFsBlockFilesParent(long fsObjId, String name) { logger.log(Level.SEVERE, "Error - root directory for file system ID {0} not found", fsObjId); return -1; } - return caseDb.addVirtualDirectoryJNI(fsIdToRootDir.get(fsObjId), name, trans); + beginTransaction(); + VirtualDirectory dir = caseDb.addVirtualDirectory(fsIdToRootDir.get(fsObjId), name, trans); + commitTransaction(); + return dir.getId(); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error creating virtual directory " + name + " under file system ID " + fsObjId, ex); + revertTransaction(); return -1; } } + + /** + * Class to use as a key into the parent object ID map + */ + private class ParentCacheKey { + long fsObjId; + long metaAddr; + long seqNum; + String path; + + /** + * Create the key into the parent dir cache. + * Only NTFS uses the seqNum of the parent. For all file systems set to zero. + * + * @param fsObjId The file system object ID. + * @param metaAddr The metadata address of the directory. + * @param seqNum The sequence number of the directory. Unused unless file system is NTFS. + * @param path The path to the directory (should not include final slash unless root dir). + */ + ParentCacheKey(long fsObjId, long metaAddr, long seqNum, String path) { + this.fsObjId = fsObjId; + this.metaAddr = metaAddr; + if (fsIdToFsType.containsKey(fsObjId) + && (fsIdToFsType.get(fsObjId).equals(TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_NTFS) + || fsIdToFsType.get(fsObjId).equals(TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_NTFS_DETECT))) { + this.seqNum = seqNum; + } else { + this.seqNum = 0; + } + this.path = path; + } + + @Override + public boolean equals(Object obj) { + if (! (obj instanceof ParentCacheKey)) { + return false; + } + + ParentCacheKey otherKey = (ParentCacheKey) obj; + if (this.fsObjId != otherKey.fsObjId + || this.metaAddr != otherKey.metaAddr + || this.seqNum != otherKey.seqNum) { + return false; + } + + return StringUtils.equals(this.path, otherKey.path); + } + + @Override + public int hashCode() { + int hash = 3; + hash = 31 * hash + (int) (this.fsObjId ^ (this.fsObjId >>> 32)); + hash = 31 * hash + (int) (this.metaAddr ^ (this.metaAddr >>> 32)); + hash = 31 * hash + (int) (this.seqNum ^ (this.seqNum >>> 32)); + hash = 31 * hash + Objects.hashCode(this.path); + return hash; + } + } + + /** + * Utility class to hold data for layout ranges waiting + * to be added to the database. + */ + private class LayoutRangeInfo { + long objId; + long byteStart; + long byteLen; + long seq; + + LayoutRangeInfo(long objId, long byteStart, long byteLen, long seq) { + this.objId = objId; + this.byteStart = byteStart; + this.byteLen = byteLen; + this.seq = seq; + } + } + + /** + * Utility class to hold data for files waiting to be + * added to the database. + */ + private class FileInfo { + long parentObjId; + long fsObjId; + long dataSourceObjId; + int fsType; + int attrType; + int attrId; + String name; + long metaAddr; + long metaSeq; + int dirType; + int metaType; + int dirFlags; + int metaFlags; + long size; + long crtime; + long ctime; + long atime; + long mtime; + int meta_mode; + int gid; + int uid; + String escaped_path; + String extension; + long seq; + long parMetaAddr; + long parSeq; + + FileInfo(long parentObjId, + long fsObjId, long dataSourceObjId, + int fsType, + int attrType, int attrId, String name, + long metaAddr, long metaSeq, + int dirType, int metaType, int dirFlags, int metaFlags, + long size, + long crtime, long ctime, long atime, long mtime, + int meta_mode, int gid, int uid, + String escaped_path, String extension, + long seq, long parMetaAddr, long parSeq) { + + this.parentObjId = parentObjId; + this.fsObjId = fsObjId; + this.dataSourceObjId = dataSourceObjId; + this.fsType = fsType; + this.attrType = attrType; + this.attrId = attrId; + this.name = name; + this.metaAddr = metaAddr; + this.metaSeq = metaSeq; + this.dirType = dirType; + this.metaType = metaType; + this.dirFlags = dirFlags; + this.metaFlags = metaFlags; + this.size = size; + this.crtime = crtime; + this.ctime = ctime; + this.atime = atime; + this.mtime = mtime; + this.meta_mode = meta_mode; + this.gid = gid; + this.uid = uid; + this.escaped_path = escaped_path; + this.extension = extension; + this.seq = seq; + this.parMetaAddr = parMetaAddr; + this.parSeq = parSeq; + } + } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 8c898af384c9787a68a9e1d2542344cf45beb8ce..bb9dd9e8334fc5c045e84611ed28526bc0035696 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -2120,7 +2120,7 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot4toSchema8dot5(CaseDbSchem statement.execute("ALTER TABLE tag_names ADD COLUMN rank INTEGER"); - String insertStmt = "INSERT INTO tsk_tag_sets (name) VALUES ('Project VIC (United States)')"; + String insertStmt = "INSERT INTO tsk_tag_sets (name) VALUES ('Project VIC')"; if (getDatabaseType() == DbType.POSTGRESQL) { statement.execute(insertStmt, Statement.RETURN_GENERATED_KEYS); } else { @@ -2130,17 +2130,48 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot4toSchema8dot5(CaseDbSchem if (resultSet != null && resultSet.next()) { int tagSetId = resultSet.getInt(1); - String updateQuery = "UPDATE tag_names SET tag_set_id = %d, color = '%s', rank = %d WHERE display_name = '%s'"; - statement.executeUpdate(String.format(updateQuery, tagSetId, "Red", 1, "CAT-1: Child Exploitation (Illegal)")); - statement.executeUpdate(String.format(updateQuery, tagSetId, "Lime", 2, "CAT-2: Child Exploitation (Non-Illegal/Age Difficult)")); - statement.executeUpdate(String.format(updateQuery, tagSetId, "Yellow", 3, "CAT-3: CGI/Animation (Child Exploitive)")); - statement.executeUpdate(String.format(updateQuery, tagSetId, "Purple", 4, "CAT-4: Exemplar/Comparison (Internal Use Only)")); - statement.executeUpdate(String.format(updateQuery, tagSetId, "Green", 5, "CAT-5: Non-pertinent")); - statement.executeUpdate(String.format(updateQuery, tagSetId, "Silver", 0, "CAT-0: Uncategorized", 0)); + String updateQuery = "UPDATE tag_names SET tag_set_id = %d, color = '%s', rank = %d, display_name = '%s' WHERE display_name = '%s'"; + statement.executeUpdate(String.format(updateQuery, tagSetId, "Red", 1, "Child Exploitation (Illegal)", "CAT-1: Child Exploitation (Illegal)")); + statement.executeUpdate(String.format(updateQuery, tagSetId, "Lime", 2, "Child Exploitation (Non-Illegal/Age Difficult)", "CAT-2: Child Exploitation (Non-Illegal/Age Difficult)")); + statement.executeUpdate(String.format(updateQuery, tagSetId, "Yellow", 3, "CGI/Animation (Child Exploitive)", "CAT-3: CGI/Animation (Child Exploitive)")); + statement.executeUpdate(String.format(updateQuery, tagSetId, "Purple", 4, "Exemplar/Comparison (Internal Use Only)", "CAT-4: Exemplar/Comparison (Internal Use Only)")); + statement.executeUpdate(String.format(updateQuery, tagSetId, "Fuchsia", 5, "Non-pertinent", "CAT-5: Non-pertinent")); + + String deleteContentTag = "DELETE FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id from tag_names WHERE display_name LIKE 'CAT-0: Uncategorized')"; + String deleteArtifactTag = "DELETE FROM blackboard_artifact_tags WHERE tag_name_id IN (SELECT tag_name_id from tag_names WHERE display_name LIKE 'CAT-0: Uncategorized')"; + String deleteCat0 = "DELETE FROM tag_names WHERE display_name = 'CAT-0: Uncategorized'"; + statement.executeUpdate(deleteContentTag); + statement.executeUpdate(deleteArtifactTag); + statement.executeUpdate(deleteCat0); + } else { throw new TskCoreException("Failed to retrieve the default tag_set_id from DB"); } } + + // Add data_source_obj_id column to the tsk_files table. For newly created cases + // this column will have a foreign key constraint on the data_source_info table. + // There does not seem to be a reasonable way to do this in an upgrade, + // so upgraded cases will be missing the foreign key. + switch (getDatabaseType()) { + case POSTGRESQL: + statement.execute("ALTER TABLE tsk_fs_info ADD COLUMN data_source_obj_id BIGINT NOT NULL DEFAULT -1;"); + break; + case SQLITE: + statement.execute("ALTER TABLE tsk_fs_info ADD COLUMN data_source_obj_id INTEGER NOT NULL DEFAULT -1;"); + break; + } + Statement updateStatement = connection.createStatement(); + try (ResultSet resultSet = statement.executeQuery("SELECT obj_id FROM tsk_fs_info")) { + while (resultSet.next()) { + long fsId = resultSet.getLong("obj_id"); + long dataSourceId = getDataSourceObjectId(connection, fsId); + updateStatement.executeUpdate("UPDATE tsk_fs_info SET data_source_obj_id = " + dataSourceId + " WHERE obj_id = " + fsId + ";"); + } + } finally { + closeStatement(updateStatement); + } + return new CaseDbSchemaVersionNumber(8, 5); } finally { @@ -6065,19 +6096,23 @@ public FileSystem addFileSystem(long parentObjId, long imgOffset, TskData.TSK_FS CaseDbConnection connection = transaction.getConnection(); long newObjId = addObject(parentObjId, TskData.ObjectType.FS.getObjectType(), connection); + // Get the data source object ID + long dataSourceId = getDataSourceObjectId(connection, newObjId); + // Add a row to tsk_fs_info - // INSERT INTO tsk_fs_info (obj_id, img_offset, fs_type, block_size, block_count, root_inum, first_inum, last_inum, display_name) + // INSERT INTO tsk_fs_info (obj_id, data_source_obj_id, img_offset, fs_type, block_size, block_count, root_inum, first_inum, last_inum, display_name) PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FS_INFO); preparedStatement.clearParameters(); preparedStatement.setLong(1, newObjId); - preparedStatement.setLong(2, imgOffset); - preparedStatement.setInt(3, type.getValue()); - preparedStatement.setLong(4, blockSize); - preparedStatement.setLong(5, blockCount); - preparedStatement.setLong(6, rootInum); - preparedStatement.setLong(7, firstInum); - preparedStatement.setLong(8, lastInum); - preparedStatement.setString(9, displayName); + preparedStatement.setLong(2, dataSourceId); + preparedStatement.setLong(3, imgOffset); + preparedStatement.setInt(4, type.getValue()); + preparedStatement.setLong(5, blockSize); + preparedStatement.setLong(6, blockCount); + preparedStatement.setLong(7, rootInum); + preparedStatement.setLong(8, firstInum); + preparedStatement.setLong(9, lastInum); + preparedStatement.setString(10, displayName); connection.executeUpdate(preparedStatement); // Create the new FileSystem object @@ -7871,70 +7906,30 @@ Directory getDirectoryById(long id, FileSystem parentFs) throws TskCoreException * @param image Image to lookup FileSystem for * * @return Collection of FileSystems in the image + * + * @throws TskCoreException */ - public Collection<FileSystem> getFileSystems(Image image) { - List<FileSystem> fileSystems = new ArrayList<FileSystem>(); - CaseDbConnection connection; - try { - connection = connections.getConnection(); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting file systems for image " + image.getId(), ex); //NON-NLS - return fileSystems; - } + public Collection<FileSystem> getImageFileSystems(Image image) throws TskCoreException { + List<FileSystem> fileSystems = new ArrayList<>(); + CaseDbConnection connection = connections.getConnection(); + acquireSingleUserCaseReadLock(); Statement s = null; ResultSet rs = null; + String queryStr = "SELECT * FROM tsk_fs_info WHERE data_source_obj_id = " + image.getId(); try { s = connection.createStatement(); - - // Get all the file systems. - List<FileSystem> allFileSystems = new ArrayList<FileSystem>(); - try { - rs = connection.executeQuery(s, "SELECT * FROM tsk_fs_info"); //NON-NLS - while (rs.next()) { - TskData.TSK_FS_TYPE_ENUM fsType = TskData.TSK_FS_TYPE_ENUM.valueOf(rs.getInt("fs_type")); //NON-NLS - FileSystem fs = new FileSystem(this, rs.getLong("obj_id"), "", rs.getLong("img_offset"), //NON-NLS - fsType, rs.getLong("block_size"), rs.getLong("block_count"), //NON-NLS - rs.getLong("root_inum"), rs.getLong("first_inum"), rs.getLong("last_inum")); //NON-NLS - fs.setParent(null); - allFileSystems.add(fs); - } - } catch (SQLException ex) { - logger.log(Level.SEVERE, "There was a problem while trying to obtain all file systems", ex); //NON-NLS - } finally { - closeResultSet(rs); - rs = null; - } - - // For each file system, find the image to which it belongs by iteratively - // climbing the tsk_ojbects hierarchy only taking those file systems - // that belong to this image. - for (FileSystem fs : allFileSystems) { - Long imageID = null; - Long currentObjID = fs.getId(); - while (imageID == null) { - try { - rs = connection.executeQuery(s, "SELECT * FROM tsk_objects WHERE tsk_objects.obj_id = " + currentObjID); //NON-NLS - rs.next(); - currentObjID = rs.getLong("par_obj_id"); //NON-NLS - if (rs.getInt("type") == TskData.ObjectType.IMG.getObjectType()) { //NON-NLS - imageID = rs.getLong("obj_id"); //NON-NLS - } - } catch (SQLException ex) { - logger.log(Level.SEVERE, "There was a problem while trying to obtain this image's file systems", ex); //NON-NLS - } finally { - closeResultSet(rs); - rs = null; - } - } - - // see if imageID is this image's ID - if (imageID == image.getId()) { - fileSystems.add(fs); - } + rs = connection.executeQuery(s, queryStr); //NON-NLS + while (rs.next()) { + TskData.TSK_FS_TYPE_ENUM fsType = TskData.TSK_FS_TYPE_ENUM.valueOf(rs.getInt("fs_type")); //NON-NLS + FileSystem fs = new FileSystem(this, rs.getLong("obj_id"), "", rs.getLong("img_offset"), //NON-NLS + fsType, rs.getLong("block_size"), rs.getLong("block_count"), //NON-NLS + rs.getLong("root_inum"), rs.getLong("first_inum"), rs.getLong("last_inum")); //NON-NLS + fs.setParent(null); + fileSystems.add(fs); } } catch (SQLException ex) { - logger.log(Level.SEVERE, "Error getting case database connection", ex); //NON-NLS + throw new TskCoreException("Error looking up files systems. Query: " + queryStr, ex); //NON-NLS } finally { closeResultSet(rs); closeStatement(s); @@ -11115,10 +11110,10 @@ long findParentObjIdJNI(long metaAddr, long fsObjId, String path, String name, C if (resultSet.next()) { return resultSet.getLong("obj_id"); } else { - throw new TskCoreException(String.format("Error looking up parent meta addr %d", metaAddr)); + throw new TskCoreException(String.format("Error looking up parent - meta addr: %d, path: %s, name: %s", metaAddr, path, name)); } } catch (SQLException ex) { - throw new TskCoreException(String.format("Error looking up parent meta addr %d", metaAddr), ex); + throw new TskCoreException(String.format("Error looking up parent - meta addr: %d, path: %s, name: %s", metaAddr, path, name), ex); } finally { closeResultSet(resultSet); } @@ -11328,122 +11323,7 @@ void addLayoutFileRangeJNI(long objId, long byteStart, long byteLen, releaseSingleUserCaseWriteLock(); } } - - /** - * Adds a virtual directory to the database and returns a VirtualDirectory - * object representing it. For use with the JNI callbacks associated with - * the add image process. - * - * @param parentId the ID of the parent, or 0 if NULL - * @param directoryName the name of the virtual directory to create - * @param transaction the transaction in the scope of which the operation - * is to be performed, managed by the caller - * - * @return The object ID of the new virtual directory - * - * @throws TskCoreException - */ - long addVirtualDirectoryJNI(long parentId, String directoryName, CaseDbTransaction transaction) throws TskCoreException { - acquireSingleUserCaseWriteLock(); - ResultSet resultSet = null; - try { - // Get the parent path. - CaseDbConnection connection = transaction.getConnection(); - - String parentPath; - Content parent = this.getAbstractFileById(parentId, connection); - if (parent instanceof AbstractFile) { - if (isRootDirectory((AbstractFile) parent, transaction)) { - parentPath = "/"; - } else { - parentPath = ((AbstractFile) parent).getParentPath() + parent.getName() + "/"; //NON-NLS - } - } else { - // The parent was either null or not an abstract file - parentPath = "/"; - } - - // Insert a row for the virtual directory into the tsk_objects table. - long newObjId = addObject(parentId, TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection); - - // Insert a row for the virtual directory into the tsk_files table. - // INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, has_path, dir_type, meta_type, - // dir_flags, meta_flags, size, ctime, crtime, atime, mtime, md5, known, mime_type, parent_path, data_source_obj_id,extension) - // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?) - PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE); - statement.clearParameters(); - statement.setLong(1, newObjId); - - // If the parent is part of a file system, grab its file system ID - if (0 != parentId) { - long parentFs = this.getFileSystemId(parentId, connection); - if (parentFs != -1) { - statement.setLong(2, parentFs); - } else { - statement.setNull(2, java.sql.Types.BIGINT); - } - } else { - statement.setNull(2, java.sql.Types.BIGINT); - } - - // name - statement.setString(3, directoryName); - - //type - statement.setShort(4, TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()); - statement.setShort(5, (short) 1); - - //flags - final TSK_FS_NAME_TYPE_ENUM dirType = TSK_FS_NAME_TYPE_ENUM.DIR; - statement.setShort(6, dirType.getValue()); - final TSK_FS_META_TYPE_ENUM metaType = TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR; - statement.setShort(7, metaType.getValue()); - - //allocated - final TSK_FS_NAME_FLAG_ENUM dirFlag = TSK_FS_NAME_FLAG_ENUM.ALLOC; - statement.setShort(8, dirFlag.getValue()); - final short metaFlags = (short) (TSK_FS_META_FLAG_ENUM.ALLOC.getValue() - | TSK_FS_META_FLAG_ENUM.USED.getValue()); - statement.setShort(9, metaFlags); - - //size - statement.setLong(10, 0); - - // nulls for params 11-14 - statement.setNull(11, java.sql.Types.BIGINT); - statement.setNull(12, java.sql.Types.BIGINT); - statement.setNull(13, java.sql.Types.BIGINT); - statement.setNull(14, java.sql.Types.BIGINT); - - statement.setNull(15, java.sql.Types.VARCHAR); // MD5 - statement.setByte(16, FileKnown.UNKNOWN.getFileKnownValue()); // Known - statement.setNull(17, java.sql.Types.VARCHAR); // MIME type - - // parent path - statement.setString(18, parentPath); - - // data source object id (same as object id if this is a data source) - long dataSourceObjectId; - if (0 == parentId) { - dataSourceObjectId = newObjId; - } else { - dataSourceObjectId = getDataSourceObjectId(connection, parentId); - } - statement.setLong(19, dataSourceObjectId); - - //extension, since this is not really file we just set it to null - statement.setString(20, null); - connection.executeUpdate(statement); - - return newObjId; - } catch (SQLException e) { - throw new TskCoreException("Error creating virtual directory '" + directoryName + "'", e); - } finally { - closeResultSet(resultSet); - releaseSingleUserCaseWriteLock(); - } - } - + /** * Stores a pair of object ID and its type */ @@ -11660,8 +11540,8 @@ private enum PREPARED_STATEMENT { INSERT_VS_PART_SQLITE("INSERT INTO tsk_vs_parts (obj_id, addr, start, length, desc, flags) VALUES (?, ?, ?, ?, ?, ?)"), INSERT_VS_PART_POSTGRESQL("INSERT INTO tsk_vs_parts (obj_id, addr, start, length, descr, flags) VALUES (?, ?, ?, ?, ?, ?)"), INSERT_POOL_INFO("INSERT INTO tsk_pool_info (obj_id, pool_type) VALUES (?, ?)"), - INSERT_FS_INFO("INSERT INTO tsk_fs_info (obj_id, img_offset, fs_type, block_size, block_count, root_inum, first_inum, last_inum, display_name)" - + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"), + INSERT_FS_INFO("INSERT INTO tsk_fs_info (obj_id, data_source_obj_id, img_offset, fs_type, block_size, block_count, root_inum, first_inum, last_inum, display_name)" + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), SELECT_OBJ_ID_BY_META_ADDR_AND_PATH("SELECT obj_id FROM tsk_files WHERE meta_addr = ? AND fs_obj_id = ? AND parent_path = ? AND name = ?"), SELECT_TAG_NAME_BY_ID("SELECT * FROM tag_names where tag_name_id = ?"); @@ -13015,6 +12895,25 @@ public LocalFile addLocalFile(String fileName, String localPath, public AddImageProcess makeAddImageProcess(String timezone, boolean addUnallocSpace, boolean noFatFsOrphans) { return this.caseHandle.initAddImageProcess(timezone, addUnallocSpace, noFatFsOrphans, "", this); } + + /** + * Helper to return FileSystems in an Image + * + * @param image Image to lookup FileSystem for + * + * @return Collection of FileSystems in the image + * + * @deprecated Use getImageFileSystems which throws an exception if an error occurs. + */ + @Deprecated + public Collection<FileSystem> getFileSystems(Image image) { + try { + return getImageFileSystems(image); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error loading all file systems for image with ID {0}", image.getId()); + return new ArrayList<>(); + } + } /** * Acquires a write lock, but only if this is a single-user case. Always diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java index eaf0cba9dcbaca06b0049cd4361fc3f673ce3221..4fe44e69095ea9e9a78621ed570f2a5b3417896d 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java @@ -426,15 +426,13 @@ void free() throws TskCoreException { long addImageInfo(long deviceObjId, List<String> imageFilePaths, String timeZone, SleuthkitCase skCase) throws TskCoreException { JniDbHelper dbHelper = new JniDbHelper(skCase); try { - dbHelper.beginTransaction(); long tskAutoDbPointer = initializeAddImgNat(dbHelper, timezoneLongToShort(timeZone), false, false, false); runOpenAndAddImgNat(tskAutoDbPointer, UUID.randomUUID().toString(), imageFilePaths.toArray(new String[0]), imageFilePaths.size(), timeZone); long id = finishAddImgNat(tskAutoDbPointer); - dbHelper.commitTransaction(); + dbHelper.finish(); skCase.addDataSourceToHasChildrenMap(); return id; } catch (TskDataException ex) { - dbHelper.revertTransaction(); throw new TskCoreException("Error adding image to case database", ex); } } @@ -526,7 +524,6 @@ public void run(String deviceId, String[] imageFilePaths, int sectorSize) throws } if (!isCanceled) { //with isCanceled being guarded by this it will have the same value everywhere in this synchronized block imageHandle = openImage(imageFilePaths, sectorSize, false, caseDbIdentifier); - dbHelper.beginTransaction(); tskAutoDbPointer = initAddImgNat(dbHelper, timezoneLongToShort(timeZone), addUnallocSpace, skipFatFsOrphans); } if (0 == tskAutoDbPointer) { @@ -576,8 +573,6 @@ public synchronized void revert() throws TskCoreException { throw new TskCoreException("AddImgProcess::revert: AutoDB pointer is NULL"); } - dbHelper.revertTransaction(); - // Delete the object in the native code finishAddImgNat(tskAutoDbPointer); tskAutoDbPointer = 0; @@ -602,7 +597,7 @@ public synchronized long commit() throws TskCoreException { throw new TskCoreException("AddImgProcess::commit: AutoDB pointer is NULL"); } - dbHelper.commitTransaction(); + dbHelper.finish(); // Get the image ID and delete the object in the native code long id = finishAddImgNat(tskAutoDbPointer); diff --git a/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java b/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java index 10e002d5d0cd993d238abb40465efe373011287b..284a86cde5a985048eccf073a8ad0662380b597b 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TaggingManager.java @@ -136,11 +136,12 @@ public TagSet addTagSet(String name, List<TagName> tagNames) throws TskCoreExcep } /** - * Remove a row from the tag set table. The TagNames in the TagSet will not - * be deleted, nor will any tags with the TagNames from the deleted tag set - * be deleted. + * Remove a row from the tag set table. If the given TagSet has a valid list + * of TagNames the TagNames will be removed from the tag_name table if there + * are not references to the TagNames in the content_tag or + * blackboard_artifact_tag table. * - * @param tagSet TagSet to be deleted + * @param tagSet TagSet to be deleted. * * @throws TskCoreException */ @@ -149,19 +150,26 @@ public void deleteTagSet(TagSet tagSet) throws TskCoreException { throw new IllegalArgumentException("Error adding deleting TagSet, TagSet object was null"); } - CaseDbConnection connection = skCase.getConnection(); - skCase.acquireSingleUserCaseWriteLock(); - try (Statement stmt = connection.createStatement()) { - connection.beginTransaction(); - String queryTemplate = "DELETE FROM tsk_tag_sets WHERE tag_set_id = '%d'"; - stmt.execute(String.format(queryTemplate, tagSet.getId())); - connection.commitTransaction(); - } catch (SQLException ex) { - connection.rollbackTransaction(); - throw new TskCoreException(String.format("Error deleting tag set where id = %d.", tagSet.getId()), ex); - } finally { - connection.close(); - skCase.releaseSingleUserCaseWriteLock(); + if (isTagSetInUse(tagSet)) { + throw new TskCoreException("Unable to delete TagSet (%d). TagSet TagName list contains TagNames that are currently in use."); + } + + try (CaseDbConnection connection = skCase.getConnection()) { + skCase.acquireSingleUserCaseWriteLock(); + try (Statement stmt = connection.createStatement()) { + connection.beginTransaction(); + String queryTemplate = "DELETE FROM tag_names WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE tag_set_id = %d)"; + stmt.execute(String.format(queryTemplate, tagSet.getId())); + + queryTemplate = "DELETE FROM tsk_tag_sets WHERE tag_set_id = '%d'"; + stmt.execute(String.format(queryTemplate, tagSet.getId())); + connection.commitTransaction(); + } catch (SQLException ex) { + connection.rollbackTransaction(); + throw new TskCoreException(String.format("Error deleting tag set where id = %d.", tagSet.getId()), ex); + } finally { + skCase.releaseSingleUserCaseWriteLock(); + } } } @@ -414,6 +422,48 @@ public ContentTagChange addContentTag(Content content, TagName tagName, String c } } + /** + * Determine if the given TagSet contains TagNames that are currently in + * use, ie there is an existing ContentTag or ArtifactTag that uses TagName. + * + * @param tagSet The Tagset to check. + * + * @return Return true if the TagSet is in use. + * + * @throws TskCoreException + */ + private boolean isTagSetInUse(TagSet tagSet) throws TskCoreException { + try (CaseDbConnection connection = skCase.getConnection()) { + List<TagName> tagNameList = tagSet.getTagNames(); + if (tagNameList != null && !tagNameList.isEmpty()) { + skCase.acquireSingleUserCaseReadLock(); + try { + String statement = String.format("SELECT tag_id FROM content_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE tag_set_id = %d)", tagSet.getId()); + try (Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(statement)) { + if (resultSet.next()) { + return true; + } + } catch (SQLException ex) { + throw new TskCoreException(String.format("Failed to determine if TagSet is in use (%s)", tagSet.getId()), ex); + } + + statement = String.format("SELECT tag_id FROM blackboard_artifact_tags WHERE tag_name_id IN (SELECT tag_name_id FROM tag_names WHERE tag_set_id = %d)", tagSet.getId()); + try (Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery(statement)) { + if (resultSet.next()) { + return true; + } + } catch (SQLException ex) { + throw new TskCoreException(String.format("Failed to determine if TagSet is in use (%s)", tagSet.getId()), ex); + } + } finally { + skCase.releaseSingleUserCaseReadLock(); + } + } + } + + return false; + } + /** * Returns a list of all of the TagNames that are apart of the given TagSet. * diff --git a/tools/autotools/tsk_gettimes.cpp b/tools/autotools/tsk_gettimes.cpp index ce411a00a8053502ecf307a7d1c68e1d8c253715..3686ecad541b14af9ab02dffe7fd612364051542 100644 --- a/tools/autotools/tsk_gettimes.cpp +++ b/tools/autotools/tsk_gettimes.cpp @@ -46,6 +46,7 @@ class TskGetTimes:public TskAuto { TskGetTimes(int32_t, bool); virtual TSK_RETVAL_ENUM processFile(TSK_FS_FILE * fs_file, const char *path); 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 uint8_t handleError(); @@ -125,6 +126,14 @@ TskGetTimes::filterVol(const TSK_VS_PART_INFO * vs_part) return TSK_FILTER_CONT; } +TSK_FILTER_ENUM +TskGetTimes::filterPool(const TSK_POOL_INFO * pool_info) +{ + // There's nothing to do, but we need to override this to allow the pool + // to be processed. + return TSK_FILTER_CONT; +} + TSK_FILTER_ENUM TskGetTimes::filterPoolVol(const TSK_POOL_VOLUME_INFO * pool_vol) diff --git a/tsk/auto/auto.cpp b/tsk/auto/auto.cpp index e20b1dac8b4520117cb88a7ab7c784c684fa07f2..2114f0b243830954d67e6e27ca030ad51cf1cd9d 100755 --- a/tsk/auto/auto.cpp +++ b/tsk/auto/auto.cpp @@ -220,7 +220,7 @@ TSK_FILTER_ENUM TskAuto::filterPool(const TSK_POOL_INFO * /*pool_info*/) { /* Most of our tools can't handle pool volumes yet */ if (tsk_verbose) - fprintf(stderr, "filterPoolVol: Pool handling is not yet implemented for this tool\n"); + fprintf(stderr, "filterPool: Pool handling is not yet implemented for this tool\n"); return TSK_FILTER_SKIP; } diff --git a/tsk/base/tsk_base.h b/tsk/base/tsk_base.h index 91daa425392cd9526b9d9ca770d69b908d42c267..310ec6cc98e049b302861052c37cca7d6832ae59 100644 --- a/tsk/base/tsk_base.h +++ b/tsk/base/tsk_base.h @@ -56,6 +56,10 @@ // get some other TSK / OS settings #include "tsk_os.h" +#ifdef TSK_WIN32 +#define strncasecmp _strnicmp +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/tsk/fs/fs_dir.c b/tsk/fs/fs_dir.c index 5abd9b8c140350ac93cb59ae5cf739fd8e9fa7a8..199f56aaa530a81c135e6d9f6f6ca3e569d2393d 100644 --- a/tsk/fs/fs_dir.c +++ b/tsk/fs/fs_dir.c @@ -17,7 +17,6 @@ #include "tsk_fs_i.h" #include "tsk_fatfs.h" - /** \internal * Allocate a FS_DIR structure to load names into. * @@ -541,6 +540,74 @@ save_inum_named(TSK_FS_INFO *a_fs, DENT_DINFO *dinfo) { tsk_release_lock(&a_fs->list_inum_named_lock); } +/** + * Prioritize folders in the root directory based on which are expected to contain user content. + */ +static TSK_RETVAL_ENUM +prioritizeDirNames(TSK_FS_NAME * names, size_t count, int * indexToOrderedIndex) { + const int HIGH = 0; + const int MED = 1; + const int LOW = 2; + const int LAST = 3; + int * scores; + int i, currentScore; + + scores = (int *)tsk_malloc(count * sizeof(int)); + if (scores == NULL) { + return TSK_ERR; + } + + // Default level is medium for any files/folders that do not match one of the patterns below. + // This includes the Program Files and Applications folders. + for (i = 0; i < count; i++) { + scores[i] = MED; + } + + // Get the score for each name. Currnetly all patterns match the beginning of the name. + for (i = 0; i < count; i++) { + TSK_FS_NAME* name = &(names[i]); + if (name->name != NULL) { + if (0 == strncasecmp(name->name, "Users", strlen("Users"))) { + scores[i] = HIGH; + } + else if (0 == strncasecmp(name->name, "Documents and Settings", strlen("Documents and Settings"))) { + scores[i] = HIGH; + } + else if (0 == strncasecmp(name->name, "home", strlen("home"))) { + scores[i] = HIGH; + } + else if (0 == strncasecmp(name->name, "ProgramData", strlen("ProgramData"))) { + scores[i] = HIGH; + } + else if (0 == strncasecmp(name->name, "Windows", strlen("Windows"))) { + scores[i] = LOW; + } + else if (0 == strncasecmp(name->name, "$Orphan", strlen("$Orphan"))) { + scores[i] = LOW; + } + else if (0 == strncasecmp(name->name, "pagefile", strlen("pagefile"))) { + scores[i] = LAST; + } + else if (0 == strncasecmp(name->name, "hiberfil", strlen("hiberfil"))) { + scores[i] = LAST; + } + } + } + + // Order the name entries based on the scores + int orderedIndex = 0; + for (currentScore = HIGH; currentScore <= LAST; currentScore++) { + for (i = 0; i < count; i++) { + if (scores[i] == currentScore) { + indexToOrderedIndex[orderedIndex] = i; + orderedIndex++; + } + } + } + free(scores); + return TSK_OK; +} + /* dir_walk local function that is used for recursive calls. Callers * should initially call the non-local version. */ static TSK_WALK_RET_ENUM @@ -551,17 +618,34 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo, TSK_FS_DIR *fs_dir; TSK_FS_FILE *fs_file; size_t i; + int* indexToOrderedIndex = NULL; // get the list of entries in the directory if ((fs_dir = tsk_fs_dir_open_meta(a_fs, a_addr)) == NULL) { return TSK_WALK_ERROR; } + // If we're in the root folder, sort the files/folders to prioritize user content + if (a_addr == a_fs->root_inum) { + indexToOrderedIndex = (int *)tsk_malloc(fs_dir->names_used * sizeof(int)); + if (indexToOrderedIndex == NULL) { + tsk_fs_dir_close(fs_dir); + return TSK_WALK_ERROR; + } + if (TSK_OK != prioritizeDirNames(fs_dir->names, fs_dir->names_used, indexToOrderedIndex)) { + tsk_fs_dir_close(fs_dir); + return TSK_WALK_ERROR; + } + } + /* Allocate a file structure for the callbacks. We * will allocate fs_meta structures as needed and * point into the fs_dir structure for the names. */ if ((fs_file = tsk_fs_file_alloc(a_fs)) == NULL) { tsk_fs_dir_close(fs_dir); + if (indexToOrderedIndex != NULL) { + free(indexToOrderedIndex); + } return TSK_WALK_ERROR; } @@ -570,7 +654,13 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo, /* Point name to the buffer of names. We need to be * careful about resetting this before we free fs_file */ - fs_file->name = (TSK_FS_NAME *) & fs_dir->names[i]; + if (indexToOrderedIndex != NULL) { + // If we have a priortized list, use it + fs_file->name = (TSK_FS_NAME *)& fs_dir->names[indexToOrderedIndex[i]]; + } + else { + fs_file->name = (TSK_FS_NAME *)& fs_dir->names[i]; + } /* load the fs_meta structure if possible. * Must have non-zero inode addr or have allocated name (if inode is 0) */ @@ -596,6 +686,10 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo, fs_file->name = NULL; tsk_fs_file_close(fs_file); + if (indexToOrderedIndex != NULL) { + free(indexToOrderedIndex); + } + /* free the list -- fs_dir_walk has no way * of knowing that we stopped early w/out error. */ @@ -610,6 +704,9 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo, tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); + if (indexToOrderedIndex != NULL) { + free(indexToOrderedIndex); + } return TSK_WALK_ERROR; } } @@ -674,6 +771,9 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo, tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); + if (indexToOrderedIndex != NULL) { + free(indexToOrderedIndex); + } return TSK_WALK_ERROR; } @@ -688,6 +788,10 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo, "tsk_fs_dir_walk_lcl: directory : %" PRIuINUM " exceeded max length / depth\n", fs_file->name->meta_addr); } + + if (indexToOrderedIndex != NULL) { + free(indexToOrderedIndex); + } return TSK_WALK_ERROR; } @@ -728,6 +832,10 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo, tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); + + if (indexToOrderedIndex != NULL) { + free(indexToOrderedIndex); + } return TSK_WALK_STOP; } @@ -763,6 +871,10 @@ tsk_fs_dir_walk_lcl(TSK_FS_INFO * a_fs, DENT_DINFO * a_dinfo, tsk_fs_dir_close(fs_dir); fs_file->name = NULL; tsk_fs_file_close(fs_file); + + if (indexToOrderedIndex != NULL) { + free(indexToOrderedIndex); + } return TSK_WALK_CONT; } diff --git a/win32/BUILDING.txt b/win32/BUILDING.txt index ca0aa844fce21b4188ffbef1279ba16a3a3ae87a..a39f9ab6b77195a3411404aa312940d27c52ec79 100755 --- a/win32/BUILDING.txt +++ b/win32/BUILDING.txt @@ -19,7 +19,7 @@ XP executables. Building -There are six build targets: +There are four build targets: - Debug_NoLibs and Release_NoLibs do not depend on any third-party libraries. - Debug and Release depend on libewf, libvmdk, libvhdi, and zlib to be built so that E01 images as well as VMDK and VHD virtual machine formats are supported.