diff --git a/bindings/java/jni/Makefile.am b/bindings/java/jni/Makefile.am index b85e68a191a1a657a69ebb63b14dc2184e1c2ecb..8057baf1cf0608e81604baf8b38ff88eb00a7950 100644 --- a/bindings/java/jni/Makefile.am +++ b/bindings/java/jni/Makefile.am @@ -3,7 +3,7 @@ AM_CXXFLAGS += -Wno-unused-command-line-argument -Wno-overloaded-virtual EXTRA_DIST = .indent.pro lib_LTLIBRARIES = libtsk_jni.la -libtsk_jni_la_SOURCES = dataModel_SleuthkitJNI.cpp dataModel_SleuthkitJNI.h +libtsk_jni_la_SOURCES = dataModel_SleuthkitJNI.cpp dataModel_SleuthkitJNI.h auto_db_java.h auto_db_java.cpp libtsk_jni_la_LIBADD = ../../../tsk/libtsk.la indent: diff --git a/bindings/java/jni/auto_db_java.cpp b/bindings/java/jni/auto_db_java.cpp new file mode 100644 index 0000000000000000000000000000000000000000..09bc0400a6ef6ac2f6c0a687745f9c213daff2d8 --- /dev/null +++ b/bindings/java/jni/auto_db_java.cpp @@ -0,0 +1,2096 @@ +/* + ** The Sleuth Kit + ** + ** Brian Carrier [carrier <at> sleuthkit [dot] org] + ** Copyright (c) 2020 Brian Carrier. All Rights reserved + ** + ** This software is distributed under the Common Public License 1.0 + ** + */ + +/** + * \file auto_db_java.cpp + * Contains code to populate database with volume and file system information from a specific image. + */ + +#include "auto_db_java.h" +#include "jni.h" +#include "tsk/img/img_writer.h" +#if HAVE_LIBEWF +#include "tsk/img/ewf.h" +#include "tsk/img/tsk_img_i.h" +#endif +#include <string.h> + +#include <algorithm> +#include <sstream> + +using std::stringstream; +using std::for_each; + +/** + */ +TskAutoDbJava::TskAutoDbJava() +{ + m_curImgId = 0; + m_curVsId = 0; + m_curVolId = 0; + m_curFsId = 0; + m_curFileId = 0; + m_curUnallocDirId = 0; + m_curDirAddr = 0; + m_curDirPath = ""; + m_vsFound = false; + m_volFound = false; + m_poolFound = false; + m_stopped = false; + m_foundStructure = false; + m_attributeAdded = false; + m_addFileSystems = true; + m_noFatFsOrphans = false; + m_addUnallocSpace = false; + m_minChunkSize = -1; + m_maxChunkSize = -1; + + m_jniEnv = NULL; + + tsk_init_lock(&m_curDirPathLock); +} + +TskAutoDbJava::~TskAutoDbJava() +{ + closeImage(); + tsk_deinit_lock(&m_curDirPathLock); +} + +/** +* Look up all callback method IDs +* @param jniEnv pointer to java environment this was called from +* @param jobj the JniDbHelper object this was called from +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::initializeJni(JNIEnv * jniEnv, jobject jobj) { + m_jniEnv = jniEnv; + m_javaDbObj = m_jniEnv->NewGlobalRef(jobj); + + jclass localCallbackClass = m_jniEnv->FindClass("org/sleuthkit/datamodel/JniDbHelper"); + if (localCallbackClass == NULL) { + return TSK_ERR; + } + m_callbackClass = (jclass)m_jniEnv->NewGlobalRef(localCallbackClass); + + m_addImageMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addImageInfo", "(IJLjava/lang/String;JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J"); + if (m_addImageMethodID == NULL) { + return TSK_ERR; + } + + m_addImageNameMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addImageName", "(JLjava/lang/String;J)I"); + if (m_addImageNameMethodID == NULL) { + return TSK_ERR; + } + + m_addVolumeSystemMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addVsInfo", "(JIJJ)J"); + if (m_addVolumeSystemMethodID == NULL) { + return TSK_ERR; + } + + m_addVolumeMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addVolume", "(JJJJLjava/lang/String;J)J"); + if (m_addVolumeMethodID == NULL) { + return TSK_ERR; + } + + m_addPoolMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addPool", "(JI)J"); + if (m_addPoolMethodID == NULL) { + return TSK_ERR; + } + + + m_addFileSystemMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addFileSystem", "(JJIJJJJJ)J"); + if (m_addFileSystemMethodID == NULL) { + return TSK_ERR; + } + + m_addFileMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addFile", "(JJJIIILjava/lang/String;JJIIIIJJJJJIIILjava/lang/String;Ljava/lang/String;)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; + } + + m_addLayoutFileMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addLayoutFile", "(JJJILjava/lang/String;J)J"); + if (m_addLayoutFileMethodID == NULL) { + return TSK_ERR; + } + + m_addLayoutFileRangeMethodID = m_jniEnv->GetMethodID(m_callbackClass, "addLayoutFileRange", "(JJJJ)J"); + if (m_addLayoutFileRangeMethodID == NULL) { + return TSK_ERR; + } + return TSK_OK; +} + +/** +* Cache a database object for later use. Should be called on image, volume system, volume, +* pool, and file system. +* @param objId The object ID of the new object +* @param parObjId The object ID of the new object's parent +* @param type The type of object +*/ +void +TskAutoDbJava::saveObjectInfo(uint64_t objId, uint64_t parObjId, TSK_DB_OBJECT_TYPE_ENUM type) { + TSK_DB_OBJECT objectInfo; + objectInfo.objId = objId; + objectInfo.parObjId = parObjId; + objectInfo.type = type; + m_savedObjects.push_back(objectInfo); +} + +/** +* Get a previously cached database object. +* @param objId The object ID of the object being loaded +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::getObjectInfo(uint64_t objId, TSK_DB_OBJECT** obj_info) { + for (vector<TSK_DB_OBJECT>::iterator itObjs = m_savedObjects.begin(); + itObjs != m_savedObjects.end(); ++itObjs) { + TSK_DB_OBJECT* tskDbObj = &(*itObjs); + if (tskDbObj->objId == objId) { + *obj_info = tskDbObj; + return TSK_OK; + } + } + + // Object not found + return TSK_ERR; +} + +/** +* Adds image details to the existing database tables. Object ID for new image stored in objId. +* +* @param type Image type +* @param ssize Size of device sector in bytes (or 0 for default) +* @param objId The object id assigned to the image (out param) +* @param timeZone The timezone the image is from +* @param size The size of the image in bytes. +* @param md5 MD5 hash of the image +* @param sha1 SHA1 hash of the image +* @param sha256 SHA256 hash of the image +* @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). +* @param collectionDetails collection details +* @returns TSK_ERR on error, TSK_OK on success +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addImageInfo(int type, TSK_OFF_T ssize, int64_t & objId, const string & timezone, TSK_OFF_T size, const string &md5, + const string& sha1, const string& sha256, const string& deviceId, const string& collectionDetails) { + + const char *tz_cstr = timezone.c_str(); + jstring tzj = m_jniEnv->NewStringUTF(tz_cstr); + + const char *md5_cstr = md5.c_str(); + jstring md5j = m_jniEnv->NewStringUTF(md5_cstr); + + const char *sha1_cstr = sha1.c_str(); + jstring sha1j = m_jniEnv->NewStringUTF(sha1_cstr); + + const char *sha256_cstr = sha256.c_str(); + jstring sha256j = m_jniEnv->NewStringUTF(sha256_cstr); + + const char *devId_cstr = deviceId.c_str(); + jstring devIdj = m_jniEnv->NewStringUTF(devId_cstr); + + const char *coll_cstr = collectionDetails.c_str(); + jstring collj = m_jniEnv->NewStringUTF(coll_cstr); + + jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addImageMethodID, + type, ssize, tzj, size, md5j, sha1j, sha256j, devIdj, collj); + objId = (int64_t)objIdj; + + if (objId < 0) { + return TSK_ERR; + } + + saveObjectInfo(objId, 0, TSK_DB_OBJECT_TYPE_IMG); + return TSK_OK; +} + +/** +* Adds one image name +* @param objId The object ID of the image +* @param imgName The image name +* @param sequence The sequence number for this image name +* @returns TSK_ERR on error, TSK_OK on success +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addImageName(int64_t objId, char const* imgName, int sequence) { + + jstring imgNamej = m_jniEnv->NewStringUTF(imgName); + + jint res = m_jniEnv->CallIntMethod(m_javaDbObj, m_addImageNameMethodID, + objId, imgNamej, (int64_t)sequence); + + if (res == 0) { + return TSK_OK; + } + else { + return TSK_ERR; + } +} + +/** +* Adds volume system to database. Object ID for new vs stored in objId. +* +* @param vs_info Struct containing info for this volume system +* @param parObjId Parent object ID for the volume system +* @param objId Object ID of new volume system +* @returns TSK_ERR on error, TSK_OK on success +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addVsInfo(const TSK_VS_INFO* vs_info, int64_t parObjId, int64_t& objId) { + + jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addVolumeSystemMethodID, + parObjId, vs_info->vstype, vs_info->offset, (uint64_t)vs_info->block_size); + objId = (int64_t)objIdj; + + if (objId < 0) { + return TSK_ERR; + } + + // Save the vs info to use for unallocated blocks later + TSK_DB_VS_INFO vs_db; + vs_db.objId = objId; + vs_db.offset = vs_info->offset; + vs_db.vstype = vs_info->vstype; + vs_db.block_size = vs_info->block_size; + m_savedVsInfo.push_back(vs_db); + + saveObjectInfo(objId, parObjId, TSK_DB_OBJECT_TYPE_VS); + return TSK_OK; +} + +/** +* Adds pool and pool volume system to database. Object ID for new pool vs stored in objId. +* +* @param pool_info Struct containing info for this pool +* @param parObjId Parent object ID for the pool +* @param objId Object ID of new pool volume system +* @returns TSK_ERR on error, TSK_OK on success +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& objId) { + + // Add the pool + jlong poolObjIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addPoolMethodID, + parObjId, pool_info->ctype); + int64_t poolObjId = (int64_t)poolObjIdj; + + if (poolObjId < 0) { + return TSK_ERR; + } + saveObjectInfo(poolObjId, parObjId, TSK_DB_OBJECT_TYPE_POOL); + + // Add the pool volume + jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addVolumeSystemMethodID, + poolObjIdj, TSK_VS_TYPE_APFS, pool_info->img_offset, (uint64_t)pool_info->block_size); + objId = (int64_t)objIdj; + + saveObjectInfo(objId, poolObjId, TSK_DB_OBJECT_TYPE_VS); + return TSK_OK; +} + +/** +* Adds a pool volume to database. Object ID for new pool volume stored in objId. +* +* @param pool_vol Struct containing info for this pool volume +* @param parObjId Parent object ID +* @param objId Object ID of new pool volume +* @returns TSK_ERR on error, TSK_OK on success +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addPoolVolumeInfo(const TSK_POOL_VOLUME_INFO* pool_vol, + int64_t parObjId, int64_t& objId) { + + jstring descj = m_jniEnv->NewStringUTF(pool_vol->desc); + + jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addVolumeMethodID, + parObjId, (int64_t)pool_vol->index, pool_vol->block, pool_vol->num_blocks, + descj, pool_vol->flags); + objId = (int64_t)objIdj; + + if (objId < 0) { + return TSK_ERR; + } + + saveObjectInfo(objId, parObjId, TSK_DB_OBJECT_TYPE_VOL); + return TSK_OK; +} + +/** +* Adds a volume to database. Object ID for new volume stored in objId. +* +* @param vs_part Struct containing info for this volume +* @param parObjId Parent object ID +* @param objId Object ID of new volume +* @returns TSK_ERR on error, TSK_OK on success +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addVolumeInfo(const TSK_VS_PART_INFO* vs_part, + int64_t parObjId, int64_t& objId) { + + jstring descj = m_jniEnv->NewStringUTF(vs_part->desc); + + jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addVolumeMethodID, + parObjId, (uint64_t)vs_part->addr, vs_part->start, vs_part->len, + descj, vs_part->flags); + objId = (int64_t)objIdj; + + if (objId < 0) { + return TSK_ERR; + } + + // Save the volume info for creating unallocated blocks later + TSK_DB_VS_PART_INFO vs_part_db; + vs_part_db.objId = objId; + vs_part_db.addr = vs_part->addr; + vs_part_db.start = vs_part->start; + vs_part_db.len = vs_part->len; + strncpy(vs_part_db.desc, vs_part->desc, TSK_MAX_DB_VS_PART_INFO_DESC_LEN - 1); + vs_part_db.flags = vs_part->flags; + m_savedVsPartInfo.push_back(vs_part_db); + + saveObjectInfo(objId, parObjId, TSK_DB_OBJECT_TYPE_VOL); + return TSK_OK; +} + +/** +* Adds a file system to database. Object ID for new file system stored in objId. +* +* @param fs_info Struct containing info for this file system +* @param parObjId Parent object ID +* @param objId Object ID of new file system +* @returns TSK_ERR on error, TSK_OK on success +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addFsInfo(const TSK_FS_INFO* fs_info, int64_t parObjId, + int64_t& objId) { + + jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addFileSystemMethodID, + parObjId, fs_info->offset, (int)fs_info->ftype, (uint64_t)fs_info->block_size, + fs_info->block_count, fs_info->root_inum, fs_info->first_inum, + fs_info->last_inum); + objId = (int64_t)objIdj; + + if (objId < 0) { + return TSK_ERR; + } + + // Save the file system info for creating unallocated blocks later + TSK_DB_FS_INFO fs_info_db; + fs_info_db.objId = objId; + fs_info_db.imgOffset = fs_info->offset; + fs_info_db.fType = fs_info->ftype; + fs_info_db.block_size = fs_info->block_size; + fs_info_db.block_count = fs_info->block_count; + fs_info_db.root_inum = fs_info->root_inum; + fs_info_db.first_inum = fs_info->first_inum; + fs_info_db.last_inum = fs_info->last_inum; + m_savedFsInfo.push_back(fs_info_db); + + saveObjectInfo(objId, parObjId, TSK_DB_OBJECT_TYPE_FS); + return TSK_OK; +} + +/** +* Adds a file to database. Object ID for new file stored in objId. +* +* @param fs_file +* @param fs_attr +* @param path File path +* @param parObjId Parent object ID +* @param fsObjId Object ID of the file system +* @param objId Object ID of new file +* @param dataSourceObjId Object ID of the data source +* @returns TSK_ERR on error, TSK_OK on success +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addFsFile(TSK_FS_FILE* fs_file, + const TSK_FS_ATTR* fs_attr, const char* path, + int64_t fsObjId, int64_t& objId, int64_t dataSourceObjId) { + + if (fs_file->name == NULL) + return TSK_ERR; + + // Find the object id for the parent folder. + int64_t parObjId = 0; + + // Root directory's parent should be the file system object. + // Make sure it doesn't have a name, so that we don't pick up ".." entries + if ((fs_file->fs_info->root_inum == fs_file->name->meta_addr) && + ((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); +} + +/** +* Extract the extension from the given file name and store it in the supplied string. +* @param name A file name +* @param extension The file name extension will be extracted to extension. +*/ +void extractExtension(char *name, char *extension) { + char *ext = strrchr(name, '.'); + + //if ext is not null and is not the entire filename... + if (ext && (name != ext)) { + size_t extLen = strlen(ext); + //... and doesn't only contain the '.' and isn't too long to be a real extension. + if ((1 < extLen) && (extLen < 15)) { + strncpy(extension, ext + 1, extLen - 1); + //normalize to lower case, only works for ascii + for (int i = 0; extension[i]; i++) { + extension[i] = tolower(extension[i]); + } + } + } +} + +/** +* 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. +* +* @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 dataSourceObjId Object ID of the data source +* @returns TSK_ERR on error, TSK_OK on success +*/ +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) +{ + time_t mtime = 0; + time_t crtime = 0; + time_t ctime = 0; + time_t atime = 0; + TSK_OFF_T size = 0; + int meta_type = 0; + int meta_flags = 0; + int meta_mode = 0; + int gid = 0; + int uid = 0; + int type = TSK_FS_ATTR_TYPE_NOT_FOUND; + int idx = 0; + + if (fs_file->name == NULL) + return TSK_OK; + + if (fs_file->meta) { + mtime = fs_file->meta->mtime; + atime = fs_file->meta->atime; + ctime = fs_file->meta->ctime; + crtime = fs_file->meta->crtime; + meta_type = fs_file->meta->type; + meta_flags = fs_file->meta->flags; + meta_mode = fs_file->meta->mode; + gid = fs_file->meta->gid; + uid = fs_file->meta->uid; + } + + size_t attr_nlen = 0; + if (fs_attr) { + type = fs_attr->type; + idx = fs_attr->id; + size = fs_attr->size; + if (fs_attr->name) { + if ((fs_attr->type != TSK_FS_ATTR_TYPE_NTFS_IDXROOT) || + (strcmp(fs_attr->name, "$I30") != 0)) { + attr_nlen = strlen(fs_attr->name); + } + } + } + + // sanity check + if (size < 0) { + size = 0; + } + + // combine name and attribute name + size_t len = strlen(fs_file->name->name); + char * name; + size_t nlen = len + attr_nlen + 11; // Extra space for possible colon and '-slack' + if ((name = (char *)tsk_malloc(nlen)) == NULL) { + return TSK_ERR; + } + + strncpy(name, fs_file->name->name, nlen); + + char extension[24] = ""; + extractExtension(name, extension); + + // Add the attribute name + if (attr_nlen > 0) { + strncat(name, ":", nlen - strlen(name)); + strncat(name, fs_attr->name, nlen - strlen(name)); + } + + // clean up path + // +2 = space for leading slash and terminating null + size_t path_len = strlen(path) + 2; + char* escaped_path; + if ((escaped_path = (char *)tsk_malloc(path_len)) == NULL) { + free(name); + return TSK_ERR; + } + strncpy(escaped_path, "/", path_len); + strncat(escaped_path, path, path_len - strlen(escaped_path)); + + jstring namej = m_jniEnv->NewStringUTF(name); + jstring pathj = m_jniEnv->NewStringUTF(escaped_path); + jstring extj = m_jniEnv->NewStringUTF(extension); + + // Add the file to the database + jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addFileMethodID, + parObjId, fsObjId, + dataSourceObjId, + TSK_DB_FILES_TYPE_FS, + type, idx, namej, + fs_file->name->meta_addr, (uint64_t)fs_file->name->meta_seq, + fs_file->name->type, meta_type, fs_file->name->flags, meta_flags, + 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; + + if (objId < 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 ".." + // - Data is non-resident + // - The allocated size is greater than the initialized file size + // See github issue #756 on why initsize and not size. + // - The data is not compressed + if ((fs_attr != NULL) + && ((strlen(name) > 0) && (!TSK_FS_ISDOT(name))) + && (!(fs_file->meta->flags & TSK_FS_META_FLAG_COMP)) + && (fs_attr->flags & TSK_FS_ATTR_NONRES) + && (fs_attr->nrd.allocsize > fs_attr->nrd.initsize)) { + strncat(name, "-slack", 6); + if (strlen(extension) > 0) { + strncat(extension, "-slack", 6); + } + jstring slackNamej = m_jniEnv->NewStringUTF(name); + jstring slackExtj = m_jniEnv->NewStringUTF(extension); + 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, + parObjId, fsObjId, + dataSourceObjId, + TSK_DB_FILES_TYPE_SLACK, + type, idx, slackNamej, + fs_file->name->meta_addr, (uint64_t)fs_file->name->meta_seq, + TSK_FS_NAME_TYPE_REG, TSK_FS_META_TYPE_REG, fs_file->name->flags, meta_flags, + 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; + + if (slackObjId < 0) { + return TSK_ERR; + } + } + + free(name); + free(escaped_path); + + return TSK_OK; +} + +// Internal function object to check for range overlap +typedef struct _checkFileLayoutRangeOverlap { + const vector<TSK_DB_FILE_LAYOUT_RANGE> & ranges; + bool hasOverlap; + + explicit _checkFileLayoutRangeOverlap(const vector<TSK_DB_FILE_LAYOUT_RANGE> & ranges) + : ranges(ranges), hasOverlap(false) {} + + bool getHasOverlap() const { return hasOverlap; } + void operator() (const TSK_DB_FILE_LAYOUT_RANGE & range) { + if (hasOverlap) + return; //no need to check other + + uint64_t start = range.byteStart; + uint64_t end = start + range.byteLen; + + vector<TSK_DB_FILE_LAYOUT_RANGE>::const_iterator it; + for (it = ranges.begin(); it != ranges.end(); ++it) { + const TSK_DB_FILE_LAYOUT_RANGE * otherRange = &(*it); + if (&range == otherRange) + continue; //skip, it's the same range + uint64_t otherStart = otherRange->byteStart; + uint64_t otherEnd = otherStart + otherRange->byteLen; + if (start <= otherEnd && end >= otherStart) { + hasOverlap = true; + break; + } + } + } + +} checkFileLayoutRangeOverlap; + +/** +* Internal helper method to add unalloc, unused and carved files with layout ranges to db +* Generates file_name and populates tsk_files, tsk_objects and tsk_file_layout tables +* Adds a single entry to tsk_files table with an auto-generated file name, tsk_objects table, and one or more entries to tsk_file_layout table +* @param dbFileType Type of file +* @param parentObjId Id of the parent object in the database (fs, volume, or image) +* @param fsObjId parent fs, or NULL if the file is not associated with fs +* @param size Number of bytes in file +* @param ranges vector containing one or more TSK_DB_FILE_LAYOUT_RANGE layout ranges (in) +* @param objId object id of the file object created (output) +* @param dataSourceObjId The object ID for the data source +* @returns TSK_OK on success or TSK_ERR on error. +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addFileWithLayoutRange(const TSK_DB_FILES_TYPE_ENUM dbFileType, const int64_t parentObjId, + const int64_t fsObjId, const uint64_t size, + vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId, + int64_t dataSourceObjId) { + + const size_t numRanges = ranges.size(); + + if (numRanges < 1) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("Error addFileWithLayoutRange() - no ranges present"); + return TSK_ERR; + } + + stringstream fileNameSs; + switch (dbFileType) { + case TSK_DB_FILES_TYPE_UNALLOC_BLOCKS: + fileNameSs << "Unalloc"; + break; + + case TSK_DB_FILES_TYPE_UNUSED_BLOCKS: + fileNameSs << "Unused"; + break; + + case TSK_DB_FILES_TYPE_CARVED: + fileNameSs << "Carved"; + break; + default: + stringstream sserr; + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + sserr << "Error addFileWithLayoutRange() - unsupported file type for file layout range: "; + sserr << (int)dbFileType; + tsk_error_set_errstr("%s", sserr.str().c_str()); + return TSK_ERR; + } + + //ensure layout ranges are sorted (to generate file name and to be inserted in sequence order) + sort(ranges.begin(), ranges.end()); + + //dome some checking + //ensure there is no overlap and each range has unique byte range + const checkFileLayoutRangeOverlap & overlapRes = + for_each(ranges.begin(), ranges.end(), checkFileLayoutRangeOverlap(ranges)); + if (overlapRes.getHasOverlap()) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("Error addFileWithLayoutRange() - overlap detected between ranges"); + return TSK_ERR; + } + + //construct filename with parent obj id, start byte of first range, end byte of last range + fileNameSs << "_" << parentObjId << "_" << ranges[0].byteStart; + fileNameSs << "_" << (ranges[numRanges - 1].byteStart + ranges[numRanges - 1].byteLen); + + jstring namej = m_jniEnv->NewStringUTF(fileNameSs.str().c_str()); + + // Insert into tsk files and tsk objects + jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addLayoutFileMethodID, + parentObjId, fsObjId, dataSourceObjId, dbFileType, namej, size); + objId = (int64_t)objIdj; + + if (objId < 0) { + return TSK_ERR; + } + + // Fill in fileObjId and insert ranges + for (vector<TSK_DB_FILE_LAYOUT_RANGE>::iterator it = ranges.begin(); + it != ranges.end(); ++it) { + TSK_DB_FILE_LAYOUT_RANGE & range = *it; + range.fileObjId = objId; + if (-1 == m_jniEnv->CallLongMethod(m_javaDbObj, m_addLayoutFileRangeMethodID, + objId, range.byteStart, range.byteLen, range.sequence)) { + return TSK_ERR; + } + } + + return TSK_OK; +} + +/** +* Adds information about a unallocated file with layout ranges into the database. +* Adds a single entry to tsk_files table with an auto-generated file name, tsk_objects table, and one or more entries to tsk_file_layout table +* @param parentObjId Id of the parent object in the database (fs, volume, or image) +* @param fsObjId parent fs, or NULL if the file is not associated with fs +* @param size Number of bytes in file +* @param ranges vector containing one or more TSK_DB_FILE_LAYOUT_RANGE layout ranges (in) +* @param objId object id of the file object created (output) +* @param dataSourceObjId The object ID for the data source +* @returns TSK_OK on success or TSK_ERR on error. +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addUnallocBlockFile(const int64_t parentObjId, const int64_t fsObjId, const uint64_t size, + vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId, + int64_t dataSourceObjId) { + return addFileWithLayoutRange(TSK_DB_FILES_TYPE_UNALLOC_BLOCKS, parentObjId, fsObjId, size, ranges, objId, + dataSourceObjId); +} + +/** +* Adds information about a unused file with layout ranges into the database. +* Adds a single entry to tsk_files table with an auto-generated file name, tsk_objects table, and one or more entries to tsk_file_layout table +* @param parentObjId Id of the parent object in the database (fs, volume, or image) +* @param fsObjId parent fs, or NULL if the file is not associated with fs +* @param size Number of bytes in file +* @param ranges vector containing one or more TSK_DB_FILE_LAYOUT_RANGE layout ranges (in) +* @param objId object id of the file object created (output) +* @param dataSourceObjId The object ID for the data source +* @returns TSK_OK on success or TSK_ERR on error. +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addUnusedBlockFile(const int64_t parentObjId, const int64_t fsObjId, const uint64_t size, + vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId, + int64_t dataSourceObjId) { + return addFileWithLayoutRange(TSK_DB_FILES_TYPE_UNUSED_BLOCKS, parentObjId, fsObjId, size, ranges, objId, + dataSourceObjId); +} + + + +/** +* Add a virtual dir to hold unallocated block files for this file system. +* @param fsObjId Object ID of the file system +* @param objId Object ID of the created virtual dir +* @param dataSourceObjId Object ID of the data source +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addUnallocFsBlockFilesParent(const int64_t fsObjId, int64_t& objId, + int64_t dataSourceObjId) { + + const char * const unallocDirName = "$Unalloc"; + jstring namej = m_jniEnv->NewStringUTF(unallocDirName); + + jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addUnallocParentMethodID, + fsObjId, namej); + objId = (int64_t)objIdj; + + if (objId < 0) { + return TSK_ERR; + } + 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. +* +* @param vol_index The index for the new volume (should be one higher than the number of pool volumes) +* @param parObjId The object ID of the parent volume system +* @param objId Will be set to the object ID of the new volume +* +* @returns TSK_ERR on error, TSK_OK on success +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addUnallocatedPoolVolume(int vol_index, int64_t parObjId, int64_t& objId) +{ + char *desc = "Unallocated Blocks"; + jstring descj = m_jniEnv->NewStringUTF(desc); + + jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addVolumeMethodID, + parObjId, vol_index, 0, 0, + descj, 0); + objId = (int64_t)objIdj; + + if (objId < 0) { + return TSK_ERR; + } + return TSK_OK; +} + +void TskAutoDbJava::close() { + if (m_jniEnv == NULL) { + return; + } + + if (m_javaDbObj != NULL) { + m_jniEnv->DeleteGlobalRef(m_javaDbObj); + } + + if (m_callbackClass != NULL) { + m_jniEnv->DeleteGlobalRef(m_callbackClass); + } +} + +int64_t TskAutoDbJava::getImageID() { + return m_curImgId; +} + +void TskAutoDbJava::closeImage() { + TskAuto::closeImage(); +} + +void TskAutoDbJava::setAddFileSystems(bool addFileSystems) { + m_addFileSystems = addFileSystems; +} + +void TskAutoDbJava::setNoFatFsOrphans(bool noFatFsOrphans) { + m_noFatFsOrphans = noFatFsOrphans; +} + +void TskAutoDbJava::setAddUnallocSpace(bool addUnallocSpace) { + setAddUnallocSpace(addUnallocSpace, -1); +} + +void TskAutoDbJava::setAddUnallocSpace(bool addUnallocSpace, int64_t minChunkSize) { + m_addUnallocSpace = addUnallocSpace; + m_minChunkSize = minChunkSize; + m_maxChunkSize = -1; +} + +void TskAutoDbJava::setAddUnallocSpace(int64_t minChunkSize, int64_t maxChunkSize) { + m_addUnallocSpace = true; + m_minChunkSize = minChunkSize; + m_maxChunkSize = maxChunkSize; +} + +/** + * Adds an image to the database. + * + * @param a_num Number of image parts + * @param a_images Array of paths to the image parts + * @param a_type Image type + * @param a_ssize Size of device sector in bytes (or 0 for default) + * @param a_deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID). + * @return 0 for success, 1 for failure + */ +uint8_t + TskAutoDbJava::openImageUtf8(int a_num, const char *const a_images[], + TSK_IMG_TYPE_ENUM a_type, unsigned int a_ssize, const char* a_deviceId) +{ + uint8_t retval = + TskAuto::openImageUtf8(a_num, a_images, a_type, a_ssize); + if (retval != 0) { + return retval; + } + + if (addImageDetails(a_deviceId)) { + return 1; + } + return 0; +} + +/** + * Adds an image to the database. + * + * @param a_num Number of image parts + * @param a_images Array of paths to the image parts + * @param a_type Image type + * @param a_ssize Size of device sector in bytes (or 0 for default) + * @param a_deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID). + * @return 0 for success, 1 for failure + */ +uint8_t + TskAutoDbJava::openImage(int a_num, const TSK_TCHAR * const a_images[], + TSK_IMG_TYPE_ENUM a_type, unsigned int a_ssize, const char* a_deviceId) +{ + +#ifdef TSK_WIN32 + + uint8_t retval = TskAuto::openImage(a_num, a_images, a_type, a_ssize); + + if (retval != 0) { + return retval; + } + + return (addImageDetails(a_deviceId)); +#else + return openImageUtf8(a_num, a_images, a_type, a_ssize, a_deviceId); +#endif +} + +/** +* Adds an image to the database. Requires that m_img_info is already initialized +* +* @param a_deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID). +* @return 0 for success, 1 for failure +*/ +uint8_t +TskAutoDbJava::openImage(const char* a_deviceId) +{ + if (m_img_info == NULL) { + return 1; + } + + return(addImageDetails(a_deviceId)); +} + +/** + * Adds image details to the existing database tables. + * + * @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID). + * @return Returns 0 for success, 1 for failure + */ +uint8_t +TskAutoDbJava::addImageDetails(const char* deviceId) +{ + string md5 = ""; + string sha1 = ""; + string collectionDetails = ""; +#if HAVE_LIBEWF + if (m_img_info->itype == TSK_IMG_TYPE_EWF_EWF) { + // @@@ This should really probably be inside of a tsk_img_ method + IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)m_img_info; + if (ewf_info->md5hash_isset) { + md5 = ewf_info->md5hash; + } + if (ewf_info->sha1hash_isset) { + sha1 = ewf_info->sha1hash; + } + + collectionDetails = ewf_get_details(ewf_info); + } +#endif + + string devId; + if (NULL != deviceId) { + devId = deviceId; + } else { + devId = ""; + } + if (TSK_OK != 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 (TSK_OK != addImageName(m_curImgId, img_ptr, i)) { + registerError(); + return 1; + } + } + +#ifdef TSK_WIN32 + //cleanup + for (int i = 0; i < m_img_info->num_img; ++i) { + free(img_ptrs[i]); + } + free(img_ptrs); +#endif + + return 0; +} + + +TSK_FILTER_ENUM +TskAutoDbJava::filterVs(const TSK_VS_INFO * vs_info) +{ + m_vsFound = true; + if (TSK_OK != addVsInfo(vs_info, m_curImgId, m_curVsId)) { + registerError(); + return TSK_FILTER_STOP; + } + + return TSK_FILTER_CONT; +} + +TSK_FILTER_ENUM +TskAutoDbJava::filterPool(const TSK_POOL_INFO * pool_info) +{ + m_poolFound = true; + + if (m_volFound && m_vsFound) { + // there's a volume system and volume + if (TSK_OK != addPoolInfoAndVS(pool_info, m_curVolId, m_curPoolVs)) { + registerError(); + return TSK_FILTER_STOP; + } + // Save the parent obj ID for the pool + m_poolOffsetToParentId[pool_info->img_offset] = m_curVolId; + } + else { + // pool doesn't live in a volume, use image as parent + if (TSK_OK != addPoolInfoAndVS(pool_info, m_curImgId, m_curPoolVs)) { + registerError(); + return TSK_FILTER_STOP; + } + // Save the parent obj ID for the pool + m_poolOffsetToParentId[pool_info->img_offset] = m_curImgId; + } + + // Store the volume system object ID for later use + m_poolOffsetToVsId[pool_info->img_offset] = m_curPoolVs; + + return TSK_FILTER_CONT; +} + +/** +* Adds unallocated pool blocks to a new volume. +* +* @param numPool Will be updated with the number of pools processed +* +* @return Returns 0 for success, 1 for failure +*/ +TSK_RETVAL_ENUM +TskAutoDbJava::addUnallocatedPoolBlocksToDb(size_t & numPool) { + + for (int i = 0; i < m_poolInfos.size(); i++) { + const TSK_POOL_INFO * pool_info = m_poolInfos[i]; + if (m_poolOffsetToVsId.find(pool_info->img_offset) == m_poolOffsetToVsId.end()) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("Error addUnallocatedPoolBlocksToDb() - could not find volume system object ID for pool at offset %lld", pool_info->img_offset); + return TSK_ERR; + } + int64_t curPoolVs = m_poolOffsetToVsId[pool_info->img_offset]; + + /* Make sure the pool_info is still allocated */ + if (pool_info->tag != TSK_POOL_INFO_TAG) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("Error addUnallocatedPoolBlocksToDb() - pool_info is not allocated"); + return TSK_ERR; + } + + /* Only APFS pools are currently supported */ + if (pool_info->ctype != TSK_POOL_TYPE_APFS) { + continue; + } + + /* Increment the count of pools found */ + numPool++; + + /* Create the volume */ + int64_t unallocVolObjId; + if (TSK_ERR == addUnallocatedPoolVolume(pool_info->num_vols, curPoolVs, unallocVolObjId)) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("Error addUnallocatedPoolBlocksToDb() - error createing unallocated space pool volume"); + return TSK_ERR; + } + + /* Create the unallocated space files */ + TSK_FS_ATTR_RUN * unalloc_runs = tsk_pool_unallocated_runs(pool_info); + TSK_FS_ATTR_RUN * current_run = unalloc_runs; + vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; + while (current_run != NULL) { + + TSK_DB_FILE_LAYOUT_RANGE tempRange(current_run->addr * pool_info->block_size, current_run->len * pool_info->block_size, 0); + + ranges.push_back(tempRange); + int64_t fileObjId = 0; + if (TSK_ERR == addUnallocBlockFile(unallocVolObjId, NULL, current_run->len * pool_info->block_size, ranges, fileObjId, m_curImgId)) { + registerError(); + tsk_fs_attr_run_free(unalloc_runs); + return TSK_ERR; + } + + current_run = current_run->next; + ranges.clear(); + } + tsk_fs_attr_run_free(unalloc_runs); + } + + return TSK_OK; +} + +TSK_FILTER_ENUM +TskAutoDbJava::filterPoolVol(const TSK_POOL_VOLUME_INFO * pool_vol) +{ + + if (TSK_OK != addPoolVolumeInfo(pool_vol, m_curPoolVs, m_curPoolVol)) { + registerError(); + return TSK_FILTER_STOP; + } + + return TSK_FILTER_CONT; +} + +TSK_FILTER_ENUM +TskAutoDbJava::filterVol(const TSK_VS_PART_INFO * vs_part) +{ + m_volFound = true; + m_foundStructure = true; + m_poolFound = false; + + if (TSK_OK != addVolumeInfo(vs_part, m_curVsId, m_curVolId)) { + registerError(); + return TSK_FILTER_STOP; + } + + return TSK_FILTER_CONT; +} + + +TSK_FILTER_ENUM +TskAutoDbJava::filterFs(TSK_FS_INFO * fs_info) +{ + TSK_FS_FILE *file_root; + m_foundStructure = true; + + if (m_poolFound) { + // there's a pool + if (TSK_OK != 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 (TSK_OK != 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 (TSK_OK != 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 fs_file + * @param fs_attr + * @param path + * Returns TSK_ERR on error. + */ +TSK_RETVAL_ENUM + TskAutoDbJava::insertFileData(TSK_FS_FILE * fs_file, + const TSK_FS_ATTR * fs_attr, const char *path) +{ + if (-1 == addFsFile(fs_file, fs_attr, path, m_curFsId, m_curFileId, + m_curImgId)) { + registerError(); + return TSK_ERR; + } + + return TSK_OK; +} + +/** + * Analyzes the open image and adds image info to a database. + * Does not deal with transactions and such. Refer to startAddImage() + * for more control. + * @returns 1 if a critical error occurred (DB doesn't exist, no file system, etc.), 2 if errors occurred at some point adding files to the DB (corrupt file, etc.), and 0 otherwise. Errors will have been registered. + */ +uint8_t TskAutoDbJava::addFilesInImgToDb() +{ + // @@@ This seems bad because we are overriding what the user may + // have set. We should remove the public API if we are going to + // override it -- presumably this was added so that we always have + // unallocated volume space... + setVolFilterFlags((TSK_VS_PART_FLAG_ENUM) (TSK_VS_PART_FLAG_ALLOC | + TSK_VS_PART_FLAG_UNALLOC)); + + uint8_t retVal = 0; + if (findFilesInImg()) { + // map the boolean return value from findFiles to the three-state return value we use + // @@@ findFiles should probably return this three-state enum too + if (m_foundStructure == false) { + retVal = 1; + } + else { + retVal = 2; + } + } + + TSK_RETVAL_ENUM addUnallocRetval = TSK_OK; + if (m_addUnallocSpace) + addUnallocRetval = addUnallocSpaceToDb(); + + // findFiles return value trumps unalloc since it can return either 2 or 1. + if (retVal) { + return retVal; + } + else if (addUnallocRetval == TSK_ERR) { + return 2; + } + else { + return 0; + } +} + + +/** + * Start the process to add image/file metadata to database inside of a transaction. + * User must call either commitAddImage() to commit the changes, + * or revertAddImage() to revert them. + * + * @param numImg Number of image parts + * @param imagePaths Array of paths to the image parts + * @param imgType Image type + * @param sSize Size of device sector in bytes (or 0 for default) + * @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID) + * @return 0 for success, 1 for failure + */ +uint8_t + TskAutoDbJava::startAddImage(int numImg, const TSK_TCHAR * const imagePaths[], + TSK_IMG_TYPE_ENUM imgType, unsigned int sSize, const char* deviceId) +{ + if (tsk_verbose) + tsk_fprintf(stderr, "TskAutoDbJava::startAddImage: Starting add image process\n"); + + if (openImage(numImg, imagePaths, imgType, sSize, deviceId)) { + tsk_error_set_errstr2("TskAutoDbJava::startAddImage"); + registerError(); + return 1; + } + + if (m_imageWriterEnabled) { + tsk_img_writer_create(m_img_info, m_imageWriterPath); + } + + if (m_addFileSystems) { + return addFilesInImgToDb(); + } else { + return 0; + } +} + +/** +* Start the process to add image/file metadata to database inside of a transaction. +* User must call either commitAddImage() to commit the changes, +* or revertAddImage() to revert them. +* +* @param img_info Previously initialized TSK_IMG_INFO object +* @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID) +* @return 0 for success, 1 for failure +*/ +uint8_t +TskAutoDbJava::startAddImage(TSK_IMG_INFO * img_info, const char* deviceId) +{ + openImageHandle(img_info); + + if (m_img_info == NULL) { + return 1; + } + + if (tsk_verbose) + tsk_fprintf(stderr, "TskAutoDbJava::startAddImage: Starting add image process\n"); + + if (openImage(deviceId)) { + tsk_error_set_errstr2("TskAutoDbJava::startAddImage"); + registerError(); + return 1; + } + + if (m_imageWriterEnabled) { + if (tsk_img_writer_create(m_img_info, m_imageWriterPath)) { + registerError(); + return 1; + } + } + + if (m_addFileSystems) { + return addFilesInImgToDb(); + } + else { + return 0; + } +} + + +#ifdef WIN32 +/** + * Start the process to add image/file metadata to database inside of a transaction. + * Same functionality as addFilesInImgToDb(). Reverts + * all changes on error. User must call either commitAddImage() to commit the changes, + * or revertAddImage() to revert them. + * + * @param numImg Number of image parts + * @param imagePaths Array of paths to the image parts + * @param imgType Image type + * @param sSize Size of device sector in bytes (or 0 for default) + * @param deviceId An ASCII-printable identifier for the device associated with the data source that is intended to be unique across multiple cases (e.g., a UUID) + * @return 0 for success 1, for failure + */ +uint8_t + TskAutoDbJava::startAddImage(int numImg, const char *const imagePaths[], + TSK_IMG_TYPE_ENUM imgType, unsigned int sSize, const char* deviceId) +{ + if (tsk_verbose) + tsk_fprintf(stderr, "TskAutoDbJava::startAddImage_utf8: Starting add image process\n"); + + if (openImageUtf8(numImg, imagePaths, imgType, sSize, deviceId)) { + tsk_error_set_errstr2("TskAutoDbJava::startAddImage"); + registerError(); + return 1; + } + if (m_imageWriterEnabled) { + tsk_img_writer_create(m_img_info, m_imageWriterPath); + } + + if (m_addFileSystems) { + return addFilesInImgToDb(); + } else { + return 0; + } +} +#endif + + +/** + * Cancel the running process. Will not be handled immediately. + */ +void + TskAutoDbJava::stopAddImage() +{ + if (tsk_verbose) + tsk_fprintf(stderr, "TskAutoDbJava::stopAddImage: Stop request received\n"); + + m_stopped = true; + setStopProcessing(); + // flag is checked every time processFile() is called +} + +/** + * Set the current image's timezone + */ +void +TskAutoDbJava::setTz(string tzone) +{ + m_curImgTZone = tzone; +} + +TSK_RETVAL_ENUM +TskAutoDbJava::processFile(TSK_FS_FILE * fs_file, const char *path) +{ + // Check if the process has been canceled + if (m_stopped) { + if (tsk_verbose) + tsk_fprintf(stderr, "TskAutoDbJava::processFile: Stop request detected\n"); + return TSK_STOP; + } + + /* Update the current directory, which can be used to show + * progress. If we get a directory, then use its name. We + * do this so that when we are searching for orphan files, then + * we at least show $OrphanFiles as status. The secondary check + * is to grab the parent folder from files once we return back + * into a folder when we are doing our depth-first recursion. */ + if (isDir(fs_file)) { + m_curDirAddr = fs_file->name->meta_addr; + tsk_take_lock(&m_curDirPathLock); + m_curDirPath = string(path) + fs_file->name->name; + tsk_release_lock(&m_curDirPathLock); + } + else if (m_curDirAddr != fs_file->name->par_addr) { + m_curDirAddr = fs_file->name->par_addr; + tsk_take_lock(&m_curDirPathLock); + m_curDirPath = path; + tsk_release_lock(&m_curDirPathLock); + } + + /* process the attributes. The case of having 0 attributes can occur + * with virtual / sparse files and HFS directories. + * At some point, this can probably be cleaned + * up if TSK is more consistent about if there should always be an + * attribute or not. Sometimes, none of the attributes are added + * because of their type and we always want to add a reference to + * every file. */ + TSK_RETVAL_ENUM retval = TSK_OK; + m_attributeAdded = false; + if (tsk_fs_file_attr_getsize(fs_file) > 0) { + retval = processAttributes(fs_file, path); + } + + // insert a general row if we didn't add a specific attribute one + if ((retval == TSK_OK) && (m_attributeAdded == false)) { + retval = insertFileData(fs_file, NULL, path); + } + + // reset the file id + m_curFileId = 0; + + if (retval == TSK_STOP) + return TSK_STOP; + else + return TSK_OK; +} + + +// we return only OK or STOP -- errors are registered only and OK is returned. +TSK_RETVAL_ENUM +TskAutoDbJava::processAttribute(TSK_FS_FILE * fs_file, + const TSK_FS_ATTR * fs_attr, const char *path) +{ + // add the file metadata for the default attribute type + if (isDefaultType(fs_file, fs_attr)) { + + if (insertFileData(fs_attr->fs_file, fs_attr, path) == TSK_ERR) { + registerError(); + return TSK_OK; + } + else { + m_attributeAdded = true; + } + } + + return TSK_OK; +} + + +/** +* Callback invoked per every unallocated block in the filesystem +* Creates file ranges and file entries +* A single file entry per consecutive range of blocks +* @param a_block block being walked +* @param a_ptr a pointer to an UNALLOC_BLOCK_WLK_TRACK struct +* @returns TSK_WALK_CONT if continue, otherwise TSK_WALK_STOP if stop processing requested +*/ +TSK_WALK_RET_ENUM TskAutoDbJava::fsWalkUnallocBlocksCb(const TSK_FS_BLOCK *a_block, void *a_ptr) { + UNALLOC_BLOCK_WLK_TRACK * unallocBlockWlkTrack = (UNALLOC_BLOCK_WLK_TRACK *) a_ptr; + + if (unallocBlockWlkTrack->tskAutoDbJava.m_stopAllProcessing) + return TSK_WALK_STOP; + + // initialize if this is the first block + if (unallocBlockWlkTrack->isStart) { + unallocBlockWlkTrack->isStart = false; + unallocBlockWlkTrack->curRangeStart = a_block->addr; + unallocBlockWlkTrack->prevBlock = a_block->addr; + unallocBlockWlkTrack->size = unallocBlockWlkTrack->fsInfo.block_size; + unallocBlockWlkTrack->nextSequenceNo = 0; + return TSK_WALK_CONT; + } + + // We want to keep consecutive blocks in the same run, so simply update prevBlock and the size + // if this one is consecutive with the last call. But, if we have hit the max chunk + // size, then break up this set of consecutive blocks. + if ((a_block->addr == unallocBlockWlkTrack->prevBlock + 1) && ((unallocBlockWlkTrack->maxChunkSize <= 0) || + (unallocBlockWlkTrack->size < unallocBlockWlkTrack->maxChunkSize))) { + unallocBlockWlkTrack->prevBlock = a_block->addr; + unallocBlockWlkTrack->size += unallocBlockWlkTrack->fsInfo.block_size; + return TSK_WALK_CONT; + } + + // this block is not contiguous with the previous one or we've hit the maximum size; create and add a range object + const uint64_t rangeStartOffset = unallocBlockWlkTrack->curRangeStart * unallocBlockWlkTrack->fsInfo.block_size + + unallocBlockWlkTrack->fsInfo.offset; + const uint64_t rangeSizeBytes = (1 + unallocBlockWlkTrack->prevBlock - unallocBlockWlkTrack->curRangeStart) + * unallocBlockWlkTrack->fsInfo.block_size; + unallocBlockWlkTrack->ranges.push_back(TSK_DB_FILE_LAYOUT_RANGE(rangeStartOffset, rangeSizeBytes, unallocBlockWlkTrack->nextSequenceNo++)); + + // Return (instead of adding this run) if we are going to: + // a) Make one big file with all unallocated space (minChunkSize == 0) + // or + // b) Only make an unallocated file once we have at least chunkSize bytes + // of data in our current run (minChunkSize > 0) + // In either case, reset the range pointers and add this block to the size + if ((unallocBlockWlkTrack->minChunkSize == 0) || + ((unallocBlockWlkTrack->minChunkSize > 0) && + (unallocBlockWlkTrack->size < unallocBlockWlkTrack->minChunkSize))) { + + unallocBlockWlkTrack->size += unallocBlockWlkTrack->fsInfo.block_size; + unallocBlockWlkTrack->curRangeStart = a_block->addr; + unallocBlockWlkTrack->prevBlock = a_block->addr; + return TSK_WALK_CONT; + } + + // at this point we are either chunking and have reached the chunk limit + // or we're not chunking. Either way we now add what we've got to the DB + int64_t fileObjId = 0; + TskAutoDbJava & tskAutoDbJava = unallocBlockWlkTrack->tskAutoDbJava; + if (-1 == tskAutoDbJava.addUnallocBlockFile(tskAutoDbJava.m_curUnallocDirId, + unallocBlockWlkTrack->fsObjId, unallocBlockWlkTrack->size, unallocBlockWlkTrack->ranges, fileObjId, tskAutoDbJava.m_curImgId) == TSK_ERR) { + // @@@ Handle error -> Don't have access to registerError() though... + } + + // reset + unallocBlockWlkTrack->curRangeStart = a_block->addr; + unallocBlockWlkTrack->prevBlock = a_block->addr; + unallocBlockWlkTrack->size = unallocBlockWlkTrack->fsInfo.block_size; // The current block is part of the new range + unallocBlockWlkTrack->ranges.clear(); + unallocBlockWlkTrack->nextSequenceNo = 0; + + //we don't know what the last unalloc block is in advance + //and will handle the last range in addFsInfoUnalloc() + + return TSK_WALK_CONT; +} + + +/** +* Add unallocated space for the given file system to the database. +* Create files for consecutive unalloc block ranges. +* @param dbFsInfo fs to process +* @returns TSK_OK on success, TSK_ERR on error +*/ +TSK_RETVAL_ENUM TskAutoDbJava::addFsInfoUnalloc(const TSK_DB_FS_INFO & dbFsInfo) { + + // Unalloc space is handled separately for APFS + if (dbFsInfo.fType == TSK_FS_TYPE_APFS) { + return TSK_OK; + } + + //open the fs we have from database + TSK_FS_INFO * fsInfo = tsk_fs_open_img(m_img_info, dbFsInfo.imgOffset, dbFsInfo.fType); + if (fsInfo == NULL) { + tsk_error_set_errstr2("TskAutoDbJava::addFsInfoUnalloc: error opening fs at offset %" PRIdOFF, dbFsInfo.imgOffset); + registerError(); + return TSK_ERR; + } + + //create a "fake" dir to hold the unalloc files for the fs + if (-1 == addUnallocFsBlockFilesParent(dbFsInfo.objId, m_curUnallocDirId, m_curImgId) == TSK_ERR) { + tsk_error_set_errstr2("addFsInfoUnalloc: error creating dir for unallocated space"); + registerError(); + return TSK_ERR; + } + + //walk unalloc blocks on the fs and process them + //initialize the unalloc block walk tracking + UNALLOC_BLOCK_WLK_TRACK unallocBlockWlkTrack(*this, *fsInfo, dbFsInfo.objId, m_minChunkSize, m_maxChunkSize); + uint8_t block_walk_ret = tsk_fs_block_walk(fsInfo, fsInfo->first_block, fsInfo->last_block, (TSK_FS_BLOCK_WALK_FLAG_ENUM)(TSK_FS_BLOCK_WALK_FLAG_UNALLOC | TSK_FS_BLOCK_WALK_FLAG_AONLY), + fsWalkUnallocBlocksCb, &unallocBlockWlkTrack); + + if (block_walk_ret == 1) { + stringstream errss; + tsk_fs_close(fsInfo); + errss << "TskAutoDbJava::addFsInfoUnalloc: error walking fs unalloc blocks, fs id: "; + errss << unallocBlockWlkTrack.fsObjId; + tsk_error_set_errstr2("%s", errss.str().c_str()); + registerError(); + return TSK_ERR; + } + + if(m_stopAllProcessing) { + tsk_fs_close(fsInfo); + return TSK_OK; + } + + // handle creation of the last range + // make range inclusive from curBlockStart to prevBlock + const uint64_t byteStart = unallocBlockWlkTrack.curRangeStart * fsInfo->block_size + fsInfo->offset; + const uint64_t byteLen = (1 + unallocBlockWlkTrack.prevBlock - unallocBlockWlkTrack.curRangeStart) * fsInfo->block_size; + unallocBlockWlkTrack.ranges.push_back(TSK_DB_FILE_LAYOUT_RANGE(byteStart, byteLen, unallocBlockWlkTrack.nextSequenceNo++)); + int64_t fileObjId = 0; + + if (-1 == addUnallocBlockFile(m_curUnallocDirId, dbFsInfo.objId, unallocBlockWlkTrack.size, unallocBlockWlkTrack.ranges, fileObjId, m_curImgId) == TSK_ERR) { + registerError(); + tsk_fs_close(fsInfo); + return TSK_ERR; + } + + //cleanup + tsk_fs_close(fsInfo); + + return TSK_OK; +} + +/** +* Process all unallocated space for this disk image and create "virtual" files with layouts +* @returns TSK_OK on success, TSK_ERR on error +*/ +TSK_RETVAL_ENUM TskAutoDbJava::addUnallocSpaceToDb() { + if (m_stopAllProcessing) { + return TSK_OK; + } + + size_t numVsP = 0; + size_t numFs = 0; + size_t numPool = 0; + + TSK_RETVAL_ENUM retFsSpace = addUnallocFsSpaceToDb(numFs); + TSK_RETVAL_ENUM retVsSpace = addUnallocVsSpaceToDb(numVsP); + TSK_RETVAL_ENUM retPoolSpace = addUnallocatedPoolBlocksToDb(numPool); + + //handle case when no fs and no vs partitions and no pools + TSK_RETVAL_ENUM retImgFile = TSK_OK; + if (numVsP == 0 && numFs == 0 && numPool == 0) { + retImgFile = addUnallocImageSpaceToDb(); + } + + + if (retFsSpace == TSK_ERR || retVsSpace == TSK_ERR || retPoolSpace == TSK_ERR || retImgFile == TSK_ERR) + return TSK_ERR; + else + return TSK_OK; +} + + +/** +* Process each file system in the database and add its unallocated sectors to virtual files. +* @param numFs (out) number of filesystems found +* @returns TSK_OK on success, TSK_ERR on error (if some or all fs could not be processed) +*/ +TSK_RETVAL_ENUM TskAutoDbJava::addUnallocFsSpaceToDb(size_t & numFs) { + + if(m_stopAllProcessing) { + return TSK_OK; + } + + numFs = m_savedFsInfo.size(); + TSK_RETVAL_ENUM allFsProcessRet = TSK_OK; + for (vector<TSK_DB_FS_INFO>::iterator it = m_savedFsInfo.begin(); it!= m_savedFsInfo.end(); ++it) { + if (m_stopAllProcessing) { + break; + } + if (addFsInfoUnalloc(*it) == TSK_ERR) + allFsProcessRet = TSK_ERR; + } + + //TODO set parent_path for newly created virt dir/file hierarchy for consistency + + return allFsProcessRet; + return TSK_OK; +} + +/** +* Process each volume in the database and add its unallocated sectors to virtual files. +* @param numVsP (out) number of vs partitions found +* @returns TSK_OK on success, TSK_ERR on error +*/ +TSK_RETVAL_ENUM TskAutoDbJava::addUnallocVsSpaceToDb(size_t & numVsP) { + + numVsP = m_savedVsPartInfo.size(); + + //get fs infos to see if this vspart has fs + for (vector<TSK_DB_VS_PART_INFO>::const_iterator it = m_savedVsPartInfo.begin(); + it != m_savedVsPartInfo.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 = m_savedFsInfo.begin(); + itFs != m_savedFsInfo.end(); ++itFs) { + const TSK_DB_FS_INFO & fsInfo = *itFs; + + TSK_DB_OBJECT* fsObjInfo = NULL; + if (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; + } + + // Check if the volume contains a pool + bool hasPool = false; + for (std::map<int64_t, int64_t>::iterator iter = m_poolOffsetToParentId.begin(); iter != m_poolOffsetToParentId.end(); ++iter) { + if (iter->second == vsPart.objId) { + hasPool = true; + } + } + if (hasPool) { + // Skip processing this vspart + continue; + } + + } + + // Get sector size and image offset from parent vs info + // Get parent id of this vs part + TSK_DB_OBJECT* vsPartObj = NULL; + if (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; + } + if (vsPartObj == NULL) { + return TSK_ERR; + } + + TSK_DB_VS_INFO* vsInfo = NULL; + for (vector<TSK_DB_VS_INFO>::iterator itVs = m_savedVsInfo.begin(); + itVs != m_savedVsInfo.end(); ++itVs) { + TSK_DB_VS_INFO* temp_vs_info = &(*itVs); + if (temp_vs_info->objId == vsPartObj->parObjId) { + vsInfo = temp_vs_info; + } + } + + if (vsInfo == NULL ) { + 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 (addUnallocBlockFile(vsPart.objId, 0, tempRange.byteLen, ranges, fileObjId, m_curImgId) == TSK_ERR) { + registerError(); + return TSK_ERR; + } + } + + return TSK_OK; +} + + +/** +* Adds unalloc space for the image if there is no volumes and no file systems. +* +* @returns TSK_OK on success, TSK_ERR on error +*/ +TSK_RETVAL_ENUM TskAutoDbJava::addUnallocImageSpaceToDb() { + + const TSK_OFF_T imgSize = getImageSize(); + if (imgSize == -1) { + tsk_error_set_errstr("addUnallocImageSpaceToDb: error getting current image size, can't create unalloc block file for the image."); + registerError(); + return TSK_ERR; + } + else { + TSK_DB_FILE_LAYOUT_RANGE tempRange(0, imgSize, 0); + //add unalloc block file for the entire image + vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; + ranges.push_back(tempRange); + int64_t fileObjId = 0; + if (-1 == addUnallocBlockFile(m_curImgId, 0, imgSize, ranges, fileObjId, m_curImgId)) { + return TSK_ERR; + } + } + return TSK_OK; +} + +/** +* Returns the directory currently being analyzed by processFile(). +* Safe to use from another thread than processFile(). +* +* @returns curDirPath string representing currently analyzed directory +*/ +const std::string TskAutoDbJava::getCurDir() { + string curDirPath; + tsk_take_lock(&m_curDirPathLock); + curDirPath = m_curDirPath; + tsk_release_lock(&m_curDirPathLock); + return curDirPath; +} diff --git a/bindings/java/jni/auto_db_java.h b/bindings/java/jni/auto_db_java.h new file mode 100644 index 0000000000000000000000000000000000000000..85a9dc69eb1d006032ef6a2ba7470dfd563f95ae --- /dev/null +++ b/bindings/java/jni/auto_db_java.h @@ -0,0 +1,247 @@ +/* + ** 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 <map> +using std::map; + +#include <string> +using std::string; + +#include "tsk/auto/tsk_auto_i.h" +#include "tsk/auto/tsk_db.h" +#include "jni.h" + + +/** \internal + * C++ class that implements TskAuto to load file metadata into a database. + * This is used by the TskCaseDb class. + */ +class TskAutoDbJava :public TskAuto { + public: + TskAutoDbJava(); + virtual ~TskAutoDbJava(); + 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(); + void close(); + 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); + const std::string getCurDir(); + + /** + * 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(); + + int64_t getImageID(); + + TSK_RETVAL_ENUM initializeJni(JNIEnv *, jobject); + + private: + 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_vsFound; + bool m_volFound; + bool m_poolFound; + bool m_stopped; + 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 + + // These are used to write unallocated blocks for pools at the end of the add image + // process. We can't load the pool_info objects directly from the database so we will + // store info about them here. + 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; + jobject m_javaDbObj = NULL; + jmethodID m_addImageMethodID = NULL; + jmethodID m_addImageNameMethodID = NULL; + jmethodID m_addVolumeSystemMethodID = NULL; + jmethodID m_addVolumeMethodID = NULL; + 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; + + // Cached objects + vector<TSK_DB_FS_INFO> m_savedFsInfo; + vector<TSK_DB_VS_INFO> m_savedVsInfo; + vector<TSK_DB_VS_PART_INFO> m_savedVsPartInfo; + vector<TSK_DB_OBJECT> m_savedObjects; + + void saveObjectInfo(uint64_t objId, uint64_t parObjId, TSK_DB_OBJECT_TYPE_ENUM type); + TSK_RETVAL_ENUM getObjectInfo(uint64_t objId, TSK_DB_OBJECT** obj_info); + + // prevent copying until we add proper logic to handle it + TskAutoDbJava(const TskAutoDbJava&); + TskAutoDbJava & operator=(const TskAutoDbJava&); + + //internal structure to keep track of temp. unalloc block range + typedef struct _UNALLOC_BLOCK_WLK_TRACK { + _UNALLOC_BLOCK_WLK_TRACK(TskAutoDbJava & tskAutoDbJava, const TSK_FS_INFO & fsInfo, const int64_t fsObjId, int64_t minChunkSize, int64_t maxChunkSize) + : tskAutoDbJava(tskAutoDbJava),fsInfo(fsInfo),fsObjId(fsObjId),curRangeStart(0), minChunkSize(minChunkSize), maxChunkSize(maxChunkSize), prevBlock(0), isStart(true), nextSequenceNo(0) {} + TskAutoDbJava & tskAutoDbJava; + 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); + virtual TSK_RETVAL_ENUM processAttribute(TSK_FS_FILE *, + const TSK_FS_ATTR * fs_attr, const char *path); + + TSK_RETVAL_ENUM addUnallocatedPoolBlocksToDb(size_t & numPool); + 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(); + + // JNI methods + TSK_RETVAL_ENUM addImageInfo(int type, TSK_OFF_T ssize, int64_t & objId, const string & timezone, TSK_OFF_T size, const string &md5, + const string& sha1, const string& sha256, const string& deviceId, const string& collectionDetails); + TSK_RETVAL_ENUM addImageName(int64_t objId, char const* imgName, int sequence); + TSK_RETVAL_ENUM addVsInfo(const TSK_VS_INFO* vs_info, int64_t parObjId, int64_t& objId); + TSK_RETVAL_ENUM addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& objId); + TSK_RETVAL_ENUM addPoolVolumeInfo(const TSK_POOL_VOLUME_INFO* pool_vol, int64_t parObjId, int64_t& objId); + TSK_RETVAL_ENUM addVolumeInfo(const TSK_VS_PART_INFO* vs_part, int64_t parObjId, int64_t& objId); + TSK_RETVAL_ENUM addFsInfo(const TSK_FS_INFO* fs_info, int64_t parObjId, int64_t& objId); + TSK_RETVAL_ENUM addFsFile(TSK_FS_FILE* fs_file, + const TSK_FS_ATTR* fs_attr, const char* path, + int64_t fsObjId, int64_t& objId, int64_t dataSourceObjId); + 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); + 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, + int64_t dataSourceObjId); + TSK_RETVAL_ENUM addUnallocBlockFile(const int64_t parentObjId, const int64_t fsObjId, const uint64_t size, + vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId, + int64_t dataSourceObjId); + TSK_RETVAL_ENUM addUnusedBlockFile(const int64_t parentObjId, const int64_t fsObjId, const uint64_t size, + vector<TSK_DB_FILE_LAYOUT_RANGE>& ranges, int64_t& objId, + int64_t dataSourceObjId); + TSK_RETVAL_ENUM addUnallocFsBlockFilesParent(const int64_t fsObjId, int64_t& objId, int64_t dataSourceObjId); + TSK_RETVAL_ENUM addUnallocatedPoolVolume(int vol_index, int64_t parObjId, int64_t& objId); + +}; + +#endif diff --git a/bindings/java/jni/dataModel_SleuthkitJNI.cpp b/bindings/java/jni/dataModel_SleuthkitJNI.cpp index 9572b510cb9f631f0f92886b2947c8b8b87c6dd3..b9160399769732c37d230ffeca93a1dc8bc428c9 100644 --- a/bindings/java/jni/dataModel_SleuthkitJNI.cpp +++ b/bindings/java/jni/dataModel_SleuthkitJNI.cpp @@ -14,6 +14,7 @@ #include "tsk/auto/tsk_is_image_supported.h" #include "tsk/img/img_writer.h" #include "tsk/img/raw.h" +#include "auto_db_java.h" #include "jni.h" #include "dataModel_SleuthkitJNI.h" #include <locale.h> @@ -957,12 +958,12 @@ JNIEXPORT jobject JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbLookup * @param addUnallocSpace Pass true to create virtual files for unallocated space. Ignored if addFileSystems is false. * @param skipFatFsOrphans Pass true to skip processing of orphan files for FAT file systems. Ignored if addFileSystems is false. * - * @return A pointer to the process (TskAutoDb object) or NULL on error. + * @return A pointer to the process (TskAutoDbJava object) or NULL on error. */ JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_initAddImgNat(JNIEnv * env, - jclass obj, jlong caseHandle, jstring timeZone, jboolean addUnallocSpace, jboolean skipFatFsOrphans) { - return Java_org_sleuthkit_datamodel_SleuthkitJNI_initializeAddImgNat(env, obj, caseHandle, timeZone, true, addUnallocSpace, skipFatFsOrphans); + jclass obj, jlong caseHandle, jobject callbackObj, jstring timeZone, jboolean addUnallocSpace, jboolean skipFatFsOrphans) { + return Java_org_sleuthkit_datamodel_SleuthkitJNI_initializeAddImgNat(env, obj, caseHandle, callbackObj, timeZone, true, addUnallocSpace, skipFatFsOrphans); } /* @@ -976,19 +977,13 @@ JNIEXPORT jlong JNICALL * @param addUnallocSpace Pass true to create virtual files for unallocated space. Ignored if addFileSystems is false. * @param skipFatFsOrphans Pass true to skip processing of orphan files for FAT file systems. Ignored if addFileSystems is false. * - * @return A pointer to the process (TskAutoDb object) or NULL on error. + * @return A pointer to the process (TskAutoDbJava object) or NULL on error. */ JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_initializeAddImgNat(JNIEnv * env, jclass obj, - jlong caseHandle, jstring timeZone, jboolean addFileSystems, jboolean addUnallocSpace, jboolean skipFatFsOrphans) { + jlong caseHandle, jobject callbackObj, jstring timeZone, jboolean addFileSystems, jboolean addUnallocSpace, jboolean skipFatFsOrphans) { jboolean isCopy; - TskCaseDb *tskCase = castCaseDb(env, caseHandle); - if (tskCase == 0) { - //exception already set - return 0; - } - if (env->GetStringUTFLength(timeZone) > 0) { const char *tzstr = env->GetStringUTFChars(timeZone, &isCopy); @@ -1016,35 +1011,35 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_initializeAddImgNat(JNIEnv * env, jcla TZSET(); } - TskAutoDb *tskAuto = tskCase->initAddImage(); - if (tskAuto == NULL) { - setThrowTskCoreError(env, "Error getting tskAuto handle from initAddImage"); + TskAutoDbJava *tskAutoJava = new TskAutoDbJava(); + if (tskAutoJava == NULL) { + setThrowTskCoreError(env, "Error creating TskAutoDbJava"); return 0; } // set the options flags - tskAuto->setAddFileSystems(addFileSystems?true:false); + tskAutoJava->setAddFileSystems(addFileSystems?true:false); if (addFileSystems) { if (addUnallocSpace) { // Minimum size of unalloc files: 500 MB, maximum size: 1 GB - tskAuto->setAddUnallocSpace((int64_t)500 * 1024 * 1024, (int64_t)1024 * 1024 * 1024); + tskAutoJava->setAddUnallocSpace((int64_t)500 * 1024 * 1024, (int64_t)1024 * 1024 * 1024); } else { - tskAuto->setAddUnallocSpace(false); + tskAutoJava->setAddUnallocSpace(false); } - tskAuto->setNoFatFsOrphans(skipFatFsOrphans?true:false); + tskAutoJava->setNoFatFsOrphans(skipFatFsOrphans?true:false); } else { - tskAuto->setAddUnallocSpace(false); - tskAuto->setNoFatFsOrphans(true); + tskAutoJava->setAddUnallocSpace(false); + tskAutoJava->setNoFatFsOrphans(true); } - // we don't use the block map and it slows it down - tskAuto->createBlockMap(false); - - // ingest modules calc hashes - tskAuto->hashFiles(false); + // Set up the callbacks + if (TSK_ERR == tskAutoJava->initializeJni(env, callbackObj)) { + setThrowTskCoreError(env, "Error initializing JNI callbacks"); + return 0; + } - return (jlong) tskAuto; + return (jlong)tskAutoJava; } /* @@ -1064,10 +1059,10 @@ JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_runOpenAndAddImgNat(JNIEnv * env, jclass obj, jlong process, jstring deviceId, jobjectArray paths, jint numImgs, jstring timeZone) { - TskAutoDb *tskAuto = ((TskAutoDb *) process); + TskAutoDbJava *tskAuto = ((TskAutoDbJava *) process); if (!tskAuto || tskAuto->m_tag != TSK_AUTO_TAG) { setThrowTskCoreError(env, - "runAddImgNat: Invalid TskAutoDb object passed in"); + "runAddImgNat: Invalid TskAutoDbJava object passed in"); return; } @@ -1122,18 +1117,12 @@ JNIEXPORT void JNICALL } if (ret == 1) { - //fatal error + // Fatal error setThrowTskCoreError(env, msgss.str().c_str()); } else if (ret == 2) { - if(tskAuto->isDbOpen()) { - // if we can still talk to the database, it's a non-fatal error - setThrowTskDataError(env, msgss.str().c_str()); - } - else { - // we cannot talk to the database, fatal error - setThrowTskCoreError(env, msgss.str().c_str()); - } + // Non-fatal error + setThrowTskDataError(env, msgss.str().c_str()); } } @@ -1152,7 +1141,7 @@ JNIEXPORT void JNICALL free(imagepaths8); env->ReleaseStringUTFChars(deviceId, (const char *) device_id); - // if process completes successfully, must call revertAddImgNat or commitAddImgNat to free the TskAutoDb + // // Must call finishAddImgNat to free the TskAutoDb } /* @@ -1171,10 +1160,10 @@ JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_runAddImgNat(JNIEnv * env, jclass obj, jlong process, jstring deviceId, jlong a_img_info, jstring timeZone, jstring imageWriterPathJ) { - TskAutoDb *tskAuto = ((TskAutoDb *)process); + TskAutoDbJava *tskAuto = ((TskAutoDbJava *)process); if (!tskAuto || tskAuto->m_tag != TSK_AUTO_TAG) { setThrowTskCoreError(env, - "runAddImgNat: Invalid TskAutoDb object passed in"); + "runAddImgNat: Invalid TskAutoDbJava object passed in"); return; } @@ -1226,18 +1215,12 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_runAddImgNat(JNIEnv * env, } if (ret == 1) { - //fatal error + // Fatal error setThrowTskCoreError(env, msgss.str().c_str()); } else if (ret == 2) { - if (tskAuto->isDbOpen()) { - // if we can still talk to the database, it's a non-fatal error - setThrowTskDataError(env, msgss.str().c_str()); - } - else { - // we cannot talk to the database, fatal error - setThrowTskCoreError(env, msgss.str().c_str()); - } + // Non-fatal error + setThrowTskDataError(env, msgss.str().c_str()); } } @@ -1248,7 +1231,7 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_runAddImgNat(JNIEnv * env, // Cleanup env->ReleaseStringUTFChars(deviceId, (const char *)device_id); - // if process completes successfully, must call revertAddImgNat or commitAddImgNat to free the TskAutoDb + // Must call finishAddImgNat to free the TskAutoDb } @@ -1261,10 +1244,10 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_runAddImgNat(JNIEnv * env, JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_stopAddImgNat(JNIEnv * env, jclass obj, jlong process) { - TskAutoDb *tskAuto = ((TskAutoDb *) process); + TskAutoDbJava *tskAuto = ((TskAutoDbJava *) process); if (!tskAuto || tskAuto->m_tag != TSK_AUTO_TAG) { setThrowTskCoreError(env, - "stopAddImgNat: Invalid TskAutoDb object passed in"); + "stopAddImgNat: Invalid TskAutoDbJava object passed in"); return; } tskAuto->stopAddImage(); @@ -1272,45 +1255,23 @@ JNIEXPORT void JNICALL /* - * Revert the given add-image process. Deletes the 'process' handle. - * @param env pointer to java environment this was called from - * @param obj the java object this was called from - * @param process the add-image process created by initAddImgNat - */ -JNIEXPORT void JNICALL - Java_org_sleuthkit_datamodel_SleuthkitJNI_revertAddImgNat(JNIEnv * env, - jclass obj, jlong process) { - TskAutoDb *tskAuto = ((TskAutoDb *) process); - if (!tskAuto || tskAuto->m_tag != TSK_AUTO_TAG) { - setThrowTskCoreError(env, - "revertAddImgNat: Invalid TskAutoDb object passed in"); - return; - } - if (tskAuto->revertAddImage()) { - setThrowTskCoreError(env); - return; - } - delete tskAuto; - tskAuto = 0; -} - - -/* - * Commit the given add-image process. Deletes the 'process' handle. - * @param env pointer to java environment this was called from - * @param obj the java object this was called from - * @param process the add-image process created by initAddImgNat - */ +* Completes the given add-image process. Deletes the 'process' handle and +* returns the ID of the added image. +* @param env pointer to java environment this was called from +* @param obj the java object this was called from +* @param process the add-image process created by initAddImgNat +*/ JNIEXPORT jlong JNICALL - Java_org_sleuthkit_datamodel_SleuthkitJNI_commitAddImgNat(JNIEnv * env, +Java_org_sleuthkit_datamodel_SleuthkitJNI_finishAddImgNat(JNIEnv * env, jclass obj, jlong process) { - TskAutoDb *tskAuto = ((TskAutoDb *) process); + TskAutoDbJava *tskAuto = ((TskAutoDbJava *)process); if (!tskAuto || tskAuto->m_tag != TSK_AUTO_TAG) { setThrowTskCoreError(env, - "commitAddImgNat: Invalid TskAutoDb object passed in"); + "commitAddImgNat: Invalid TskAutoDb object passed in"); return -1; } - int64_t imgId = tskAuto->commitAddImage(); + int64_t imgId = tskAuto->getImageID(); + tskAuto->close(); delete tskAuto; tskAuto = 0; if (imgId == -1) { @@ -2150,7 +2111,7 @@ JNIEXPORT jstring JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getCurDirNat (JNIEnv * env,jclass obj, jlong dbHandle) { - TskAutoDb *tskAuto = ((TskAutoDb *) dbHandle); + TskAutoDbJava *tskAuto = ((TskAutoDbJava *) dbHandle); const std::string curDir = tskAuto->getCurDir(); jstring jdir = (*env).NewStringUTF(curDir.c_str()); return jdir; diff --git a/bindings/java/jni/dataModel_SleuthkitJNI.h b/bindings/java/jni/dataModel_SleuthkitJNI.h index 17bbaa66526e52f9a04d01c970b64148bdb4186c..52e06743f70d20a621e8eeef662966f37812c056 100644 --- a/bindings/java/jni/dataModel_SleuthkitJNI.h +++ b/bindings/java/jni/dataModel_SleuthkitJNI.h @@ -210,18 +210,18 @@ JNIEXPORT jobject JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_hashDbLookup /* * Class: org_sleuthkit_datamodel_SleuthkitJNI * Method: initAddImgNat - * Signature: (JLjava/lang/String;ZZ)J + * Signature: (JLorg/sleuthkit/datamodel/JniDbHelper;Ljava/lang/String;ZZ)J */ JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_initAddImgNat - (JNIEnv *, jclass, jlong, jstring, jboolean, jboolean); + (JNIEnv *, jclass, jlong, jobject, jstring, jboolean, jboolean); /* * Class: org_sleuthkit_datamodel_SleuthkitJNI * Method: initializeAddImgNat - * Signature: (JLjava/lang/String;ZZZ)J + * Signature: (JLorg/sleuthkit/datamodel/JniDbHelper;Ljava/lang/String;ZZZ)J */ JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_initializeAddImgNat - (JNIEnv *, jclass, jlong, jstring, jboolean, jboolean, jboolean); + (JNIEnv *, jclass, jlong, jobject, jstring, jboolean, jboolean, jboolean); /* * Class: org_sleuthkit_datamodel_SleuthkitJNI @@ -249,18 +249,10 @@ JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_stopAddImgNat /* * Class: org_sleuthkit_datamodel_SleuthkitJNI - * Method: revertAddImgNat - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_revertAddImgNat - (JNIEnv *, jclass, jlong); - -/* - * Class: org_sleuthkit_datamodel_SleuthkitJNI - * Method: commitAddImgNat + * Method: finishAddImgNat * Signature: (J)J */ -JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_commitAddImgNat +JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_finishAddImgNat (JNIEnv *, jclass, jlong); /* diff --git a/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java b/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..9efb3f0589c6ce37f326ed9f21ebe9357624eb01 --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java @@ -0,0 +1,401 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.datamodel; + +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; + +/** + * This is a utility class to allow the native C code to write to the + * case database. All callbacks from the native code should come through this class. + * Any changes to the method signatures in this class will require changes to the + * native code. + */ +class JniDbHelper { + + private static final Logger logger = Logger.getLogger(JniDbHelper.class.getName()); + + private final SleuthkitCase caseDb; + private CaseDbTransaction trans = null; + + private final Map<Long, Long> fsIdToRootDir = new HashMap<>(); + + JniDbHelper(SleuthkitCase caseDb) { + this.caseDb = caseDb; + trans = null; + } + + /** + * Start the add image transaction + * + * @throws TskCoreException + */ + void beginTransaction() throws TskCoreException { + trans = caseDb.beginTransaction(); + } + + /** + * Commit the add image transaction + * + * @throws TskCoreException + */ + void commitTransaction() throws TskCoreException { + trans.commit(); + trans = null; + } + + /** + * Revert the add image transaction + * + * @throws TskCoreException + */ + void revertTransaction() throws TskCoreException { + trans.rollback(); + trans = null; + } + + /** + * Add a new image to the database. + * Intended to be called from the native code during the add image process. + * + * @param type Type of image. + * @param ssize Sector size. + * @param timezone Time zone. + * @param size Image size. + * @param md5 MD5 hash. + * @param sha1 SHA1 hash. + * @param sha256 SHA256 hash. + * @param deviceId Device ID. + * @param collectionDetails The collection details. + * + * @return The object ID of the new image or -1 if an error occurred + */ + long addImageInfo(int type, long ssize, String timezone, + long size, String md5, String sha1, String sha256, String deviceId, + String collectionDetails) { + try { + return caseDb.addImageJNI(TskData.TSK_IMG_TYPE_ENUM.valueOf(type), ssize, size, + timezone, md5, sha1, sha256, deviceId, collectionDetails, trans); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding image to the database", ex); + return -1; + } + } + + /** + * Add an image name to the database. + * Intended to be called from the native code during the add image process. + * + * @param objId The object id of the image. + * @param name The file name for the image + * @param sequence The sequence number of this file. + * + * @return 0 if successful, -1 if not + */ + int addImageName(long objId, String name, long sequence) { + try { + caseDb.addImageNameJNI(objId, name, sequence, trans); + 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); + return -1; + } + } + + /** + * Add a volume system to the database. + * Intended to be called from the native code during the add image process. + * + * @param parentObjId + * @param vsType + * @param imgOffset + * @param blockSize + * + * @return The object ID of the new volume system or -1 if an error occurred + */ + long addVsInfo(long parentObjId, int vsType, long imgOffset, long blockSize) { + try { + VolumeSystem vs = caseDb.addVolumeSystem(parentObjId, TskData.TSK_VS_TYPE_ENUM.valueOf(vsType), imgOffset, blockSize, trans); + 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); + return -1; + } + } + + /** + * Add a volume to the database. + * Intended to be called from the native code during the add image process. + * + * @param parentObjId + * @param addr + * @param start + * @param length + * @param desc + * @param flags + * + * @return The object ID of the new volume or -1 if an error occurred + */ + long addVolume(long parentObjId, long addr, long start, long length, String desc, + long flags) { + try { + Volume vol = caseDb.addVolume(parentObjId, addr, start, length, desc, flags, trans); + return vol.getId(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding volume to the database - parent object ID: " + parentObjId + + ", addr: " + addr, ex); + return -1; + } + } + + /** + * Add a pool to the database. + * Intended to be called from the native code during the add image process. + * + * @param parentObjId + * @param poolType + * + * @return The object ID of the new pool or -1 if an error occurred + */ + long addPool(long parentObjId, int poolType) { + try { + Pool pool = caseDb.addPool(parentObjId, TskData.TSK_POOL_TYPE_ENUM.valueOf(poolType), trans); + return pool.getId(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding pool to the database - parent object ID: " + parentObjId, ex); + return -1; + } + } + + /** + * Add a file system to the database. + * Intended to be called from the native code during the add image process. + * + * @param parentObjId + * @param imgOffset + * @param fsType + * @param blockSize + * @param blockCount + * @param rootInum + * @param firstInum + * @param lastInum + * + * @return The object ID of the new file system or -1 if an error occurred + */ + long addFileSystem(long parentObjId, long imgOffset, int fsType, long blockSize, long blockCount, + long rootInum, long firstInum, long lastInum) { + try { + FileSystem fs = caseDb.addFileSystem(parentObjId, imgOffset, TskData.TSK_FS_TYPE_ENUM.valueOf(fsType), blockSize, blockCount, + rootInum, firstInum, lastInum, null, trans); + return fs.getId(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding file system to the database - parent object ID: " + parentObjId + + ", offset: " + imgOffset, ex); + return -1; + } + } + + /** + * Add a file to the database. + * Intended to be called from the native code during the add image process. + * + * @param parentObjId The parent of the file. + * @param fsObjId The object ID of the file system. + * @param dataSourceObjId The data source object ID. + * @param fsType The type. + * @param attrType The type attribute given to the file by the file system. + * @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 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 + * the metadata structure of the file system. + * @param dirFlags The allocated status of the file, usually as + * reported in the name structure of the file system. + * @param metaFlags The allocated status of the file, usually as + * reported in the metadata structure of the file system. + * @param size The file size. + * @param crtime The created time. + * @param ctime The last changed time + * @param atime The last accessed time. + * @param mtime The last modified time. + * @param meta_mode The modes for the file. + * @param gid The group identifier. + * @param uid The user identifier. + * @param md5 The MD5 hash. + * @param known The file known status. + * @param escaped_path The escaped path to the file. + * @param extension The file extension. + * + * @return The object ID of the new file or -1 if an error occurred + */ + long addFile(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) { + try { + long objId = caseDb.addFileJNI(parentObjId, + fsObjId, dataSourceObjId, + fsType, + attrType, attrId, name, + metaAddr, metaSeq, + dirType, metaType, dirFlags, metaFlags, + 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); + } + return objId; + } 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); + return -1; + } + } + + /** + * Add a layout file to the database. + * Intended to be called from the native code during the add image process. + * + * @param parentObjId The parent object ID of the layout file. + * @param fsObjId The file system object ID. + * @param dataSourceObjId The data source object ID. + * @param fileType The file type. + * @param name The file name. + * @param size The file size. + * + * @return The object ID of the new file or -1 if an error occurred + */ + long addLayoutFile(long parentObjId, + long fsObjId, long dataSourceObjId, + int fileType, + String name, long size) { + try { + // The file system may be null for layout files + Long fsObjIdForDb = fsObjId; + if (fsObjId == 0) { + fsObjIdForDb = null; + } + + long objId = caseDb.addFileJNI(parentObjId, + fsObjIdForDb, dataSourceObjId, + fileType, + null, null, name, + null, null, + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue(), + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue(), + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue(), + TskData.TSK_FS_META_FLAG_ENUM.UNALLOC.getValue(), + size, + null, null, null, null, + null, null, null, + null, TskData.FileKnown.UNKNOWN, + null, null, + true, trans); + 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); + return -1; + } + } + + /** + * Add a layout file range to the database. + * Intended to be called from the native code during the add image process. + * + * @param objId Object ID of the layout file. + * @param byteStart Start byte. + * @param byteLen Length in bytes. + * @param seq Sequence number of this range. + * + * @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; + } + } + + /** + * 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 + * + * @return The object ID of the parent or -1 if not found + */ + long findParentObjId(long metaAddr, long fsObjId, String path, String name) { + try { + return caseDb.findParentObjIdJNI(metaAddr, fsObjId, path, name, trans); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error looking up parent with meta addr: " + metaAddr + " and name " + name, ex); + return -1; + } + } + + /** + * Add a virtual directory to hold unallocated file system blocks. + * Intended to be called from the native code during the add image process. + * + * @param fsObjId + * @param name + * + * @return The object ID of the new virtual directory or -1 if an error occurred + */ + long addUnallocFsBlockFilesParent(long fsObjId, String name) { + try { + if (! fsIdToRootDir.containsKey(fsObjId)) { + 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); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error creating virtual directory " + name + " under file system ID " + fsObjId, ex); + return -1; + } + } +} diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 8d32c771006cd1a886d83ddea6590dd993210ec7..40bfa848eb68c3ea8beb03a027470a640d16cae3 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -6006,7 +6006,7 @@ public FileSystem addFileSystem(long parentObjId, long imgOffset, TskData.TSK_FS preparedStatement.clearParameters(); preparedStatement.setLong(1, newObjId); preparedStatement.setLong(2, imgOffset); - preparedStatement.setShort(3, (short) type.getValue()); + preparedStatement.setInt(3, type.getValue()); preparedStatement.setLong(4, blockSize); preparedStatement.setLong(5, blockCount); preparedStatement.setLong(6, rootInum); @@ -10965,6 +10965,453 @@ private List<IngestModuleInfo> getIngestModules(int ingestJobId, CaseDbConnectio } } + + /** + * Add an image to the database. + * For use with the JNI callbacks associated with the add image process. + * + * @param type Type of image. + * @param sectorSize Sector size. + * @param size Image size. + * @param timezone Time zone. + * @param md5 MD5 hash. + * @param sha1 SHA1 hash. + * @param sha256 SHA256 hash. + * @param deviceId Device ID. + * @param collectionDetails Collection details. + * @param transaction Case DB transaction. + * + * @return The newly added Image object ID. + * + * @throws TskCoreException + */ + long addImageJNI(TskData.TSK_IMG_TYPE_ENUM type, long sectorSize, long size, + String timezone, String md5, String sha1, String sha256, + String deviceId, String collectionDetails, + CaseDbTransaction transaction) throws TskCoreException { + acquireSingleUserCaseWriteLock(); + try { + // Insert a row for the Image into the tsk_objects table. + CaseDbConnection connection = transaction.getConnection(); + long newObjId = addObject(0, TskData.ObjectType.IMG.getObjectType(), connection); + + // Add a row to tsk_image_info + // INSERT INTO tsk_image_info (obj_id, type, ssize, tzone, size, md5, sha1, sha256, display_name) + PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_IMAGE_INFO); + preparedStatement.clearParameters(); + preparedStatement.setLong(1, newObjId); + preparedStatement.setShort(2, (short) type.getValue()); + preparedStatement.setLong(3, sectorSize); + preparedStatement.setString(4, timezone); + //prevent negative size + long savedSize = size < 0 ? 0 : size; + preparedStatement.setLong(5, savedSize); + preparedStatement.setString(6, md5); + preparedStatement.setString(7, sha1); + preparedStatement.setString(8, sha256); + preparedStatement.setString(9, null); + connection.executeUpdate(preparedStatement); + + // Add a row to data_source_info + preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_DATA_SOURCE_INFO_WITH_ACQ_DETAIL); + preparedStatement.setLong(1, newObjId); + preparedStatement.setString(2, deviceId); + preparedStatement.setString(3, timezone); + preparedStatement.setString(4, collectionDetails); + connection.executeUpdate(preparedStatement); + + return newObjId; + } catch (SQLException ex) { + throw new TskCoreException(String.format("Error adding image to database"), ex); + } finally { + releaseSingleUserCaseWriteLock(); + } + } + + /** + * Add an image name to the database. + * For use with the JNI callbacks associated with the add image process. + * + * @param objId The object id of the image. + * @param name The file name for the image + * @param sequence The sequence number of this file. + * @param transaction The open transaction. + * + * @throws TskCoreException + */ + void addImageNameJNI(long objId, String name, long sequence, + CaseDbTransaction transaction) throws TskCoreException { + acquireSingleUserCaseWriteLock(); + try { + CaseDbConnection connection = transaction.getConnection(); + PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_IMAGE_NAME); + preparedStatement.clearParameters(); + preparedStatement.setLong(1, objId); + preparedStatement.setString(2, name); + preparedStatement.setLong(3, sequence); + connection.executeUpdate(preparedStatement); + } catch (SQLException ex) { + throw new TskCoreException(String.format("Error adding image name %s to image with object ID %d", name, objId), ex); + } finally { + releaseSingleUserCaseWriteLock(); + } + } + + /** + * Looks up a parent file object ID. + * The calling thread is expected to have a case read lock. + * For use with the JNI callbacks associated with the add image process. + * + * @param metaAddr The metadata address. + * @param fsObjId The file system object ID. + * @param path The file path. + * @param name The file name. + * @param transaction The open transaction. + * + * @return The object ID if found, -1 otherwise. + * + * @throws TskCoreException + */ + long findParentObjIdJNI(long metaAddr, long fsObjId, String path, String name, CaseDbTransaction transaction) throws TskCoreException { + ResultSet resultSet = null; + try { + CaseDbConnection connection = transaction.getConnection(); + PreparedStatement preparedStatement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_OBJ_ID_BY_META_ADDR_AND_PATH); + preparedStatement.clearParameters(); + preparedStatement.setLong(1, metaAddr); + preparedStatement.setLong(2, fsObjId); + preparedStatement.setString(3, path); + preparedStatement.setString(4, name); + resultSet = connection.executeQuery(preparedStatement); + if (resultSet.next()) { + return resultSet.getLong("obj_id"); + } else { + throw new TskCoreException(String.format("Error looking up parent meta addr %d", metaAddr)); + } + } catch (SQLException ex) { + throw new TskCoreException(String.format("Error looking up parent meta addr %d", metaAddr), ex); + } finally { + closeResultSet(resultSet); + } + } + + /** + * Add a file system file to the database. + * For use with the JNI callbacks associated with the add image process. + * + * @param parentObjId The parent of the file. + * @param fsObjId The object ID of the file system. + * @param dataSourceObjId The data source object ID. + * @param fsType The type. + * @param attrType The type attribute given to the file by the file system. + * @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 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 + * the metadata structure of the file system. + * @param dirFlags The allocated status of the file, usually as + * reported in the name structure of the file system. + * @param metaFlags The allocated status of the file, usually as + * reported in the metadata structure of the file system. + * @param size The file size. + * @param crtime The created time. + * @param ctime The last changed time + * @param atime The last accessed time. + * @param mtime The last modified time. + * @param meta_mode The modes for the file. + * @param gid The group identifier. + * @param uid The user identifier. + * @param md5 The MD5 hash. + * @param known The file known status. + * @param escaped_path The escaped path to the file. + * @param extension The file extension. + * @param hasLayout True if this is a layout file, false otherwise. + * @param transaction The open transaction. + * + * @return The object ID of the new file system + * + * @throws TskCoreException + */ + long addFileJNI(long parentObjId, + Long fsObjId, long dataSourceObjId, + int fsType, + Integer attrType, Integer 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, + Integer meta_mode, Integer gid, Integer uid, + String md5, TskData.FileKnown known, + String escaped_path, String extension, + boolean hasLayout, CaseDbTransaction transaction) throws TskCoreException { + + Statement queryStatement = null; + try { + acquireSingleUserCaseWriteLock(); + CaseDbConnection connection = transaction.getConnection(); + + // Insert a row for the local/logical file into the tsk_objects table. + // INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?) + long objectId = addObject(parentObjId, TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection); + + // INSERT INTO tsk_files (fs_obj_id, obj_id, data_source_obj_id, type, attr_type, attr_id, name, meta_addr, meta_seq, + // dir_type, meta_type, dir_flags, meta_flags, size, crtime, ctime, atime, mtime, + // mode, gid, uid, md5, known, parent_path, extension) + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_FILE_SYSTEM_FILE_All_FIELDS); + statement.clearParameters(); + if (fsObjId != null) { + statement.setLong(1, fsObjId); // fs_obj_id + } else { + statement.setNull(1, java.sql.Types.BIGINT); + } + statement.setLong(2, objectId); // obj_id + statement.setLong(3, dataSourceObjId); // data_source_obj_id + statement.setShort(4, (short)fsType); // type + if (attrType != null) { + statement.setShort(5, attrType.shortValue()); // attr_type + } else { + statement.setNull(5, java.sql.Types.SMALLINT); + } + if (attrId != null) { + statement.setInt(6, attrId); // attr_id + } else { + statement.setNull(6, java.sql.Types.INTEGER); + } + statement.setString(7, name); // name + if (metaAddr != null) { + statement.setLong(8, metaAddr); // meta_addr + } else { + statement.setNull(8, java.sql.Types.BIGINT); + } + if (metaSeq != null) { + statement.setInt(9, metaSeq.intValue()); // meta_seq + } else { + statement.setNull(9, java.sql.Types.INTEGER); + } + statement.setShort(10, (short)dirType); // dir_type + statement.setShort(11, (short)metaType); // meta_type + statement.setShort(12, (short)dirFlags); // dir_flags + statement.setShort(13, (short)metaFlags); // meta_flags + statement.setLong(14, size < 0 ? 0 : size); // size + if (crtime != null) { + statement.setLong(15, crtime); // crtime + } else { + statement.setNull(15, java.sql.Types.BIGINT); + } + if (ctime != null) { + statement.setLong(16, ctime); // ctime + } else { + statement.setNull(16, java.sql.Types.BIGINT); + } + if (atime != null) { + statement.setLong(17, atime); // atime + } else { + statement.setNull(17, java.sql.Types.BIGINT); + } + if (mtime != null) { + statement.setLong(18, mtime); // mtime + } else { + statement.setNull(18, java.sql.Types.BIGINT); + } + if (meta_mode != null) { + statement.setLong(19, meta_mode); // mode + } else { + statement.setNull(19, java.sql.Types.BIGINT); + } + if (gid != null) { + statement.setLong(20, gid); // gid + } else { + statement.setNull(20, java.sql.Types.BIGINT); + } + if (uid != null) { + statement.setLong(21, uid); // uid + } else { + statement.setNull(21, java.sql.Types.BIGINT); + } + statement.setString(22, md5); // md5 + statement.setInt(23, known.getFileKnownValue());// known + statement.setString(24, escaped_path); // parent_path + statement.setString(25, extension); // extension + if (hasLayout) { + statement.setInt(26, 1); // has_layout + } else { + statement.setNull(26, java.sql.Types.INTEGER); + } + connection.executeUpdate(statement); + + // If this is not a slack file create the timeline events + if (! hasLayout + && TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType() != fsType + && (!name.equals(".")) && (!name.equals(".."))) { + TimelineManager timelineManager = getTimelineManager(); + DerivedFile derivedFile = new DerivedFile(this, objectId, dataSourceObjId, name, + TSK_FS_NAME_TYPE_ENUM.valueOf((short)dirType), + TSK_FS_META_TYPE_ENUM.valueOf((short)metaType), + TSK_FS_NAME_FLAG_ENUM.valueOf(dirFlags), + (short)metaFlags, + size, ctime, crtime, atime, mtime, null, null, escaped_path, null, parentObjId, null, null, extension); + + timelineManager.addEventsForNewFileQuiet(derivedFile, connection); + } + + return objectId; + } catch (SQLException ex) { + throw new TskCoreException("Failed to add file system file", ex); + } finally { + closeStatement(queryStatement); + releaseSingleUserCaseWriteLock(); + } + } + + /** + * Add a layout file range to the database. + * For use with the JNI callbacks associated with the add image process. + * + * @param objId Object ID of the layout file. + * @param byteStart Start byte. + * @param byteLen Length in bytes. + * @param seq Sequence number of this range. + * @param transaction The open transaction. + * + * @throws TskCoreException + */ + void addLayoutFileRangeJNI(long objId, long byteStart, long byteLen, + long seq, CaseDbTransaction transaction) throws TskCoreException { + try { + acquireSingleUserCaseWriteLock(); + CaseDbConnection connection = transaction.getConnection(); + + PreparedStatement prepStmt = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_LAYOUT_FILE); + prepStmt.clearParameters(); + prepStmt.setLong(1, objId); + prepStmt.setLong(2, byteStart); + prepStmt.setLong(3, byteLen); + prepStmt.setLong(4, seq); + connection.executeUpdate(prepStmt); + } catch (SQLException ex) { + throw new TskCoreException("Error adding layout range to file with obj ID " + objId, ex); + } finally { + 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 @@ -11061,6 +11508,8 @@ private enum PREPARED_STATEMENT { + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), //NON-NLS INSERT_FILE_SYSTEM_FILE("INSERT INTO tsk_files(obj_id, fs_obj_id, data_source_obj_id, attr_type, attr_id, name, meta_addr, meta_seq, type, has_path, dir_type, meta_type, dir_flags, meta_flags, size, ctime, crtime, atime, mtime, parent_path, extension)" + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), // NON-NLS + INSERT_FILE_SYSTEM_FILE_All_FIELDS("INSERT INTO tsk_files (fs_obj_id, obj_id, data_source_obj_id, type, attr_type, attr_id, name, meta_addr, meta_seq, dir_type, meta_type, dir_flags, meta_flags, size, crtime, ctime, atime, mtime, mode, gid, uid, md5, known, parent_path, extension, has_layout)" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), // NON-NLS UPDATE_DERIVED_FILE("UPDATE tsk_files SET type = ?, dir_type = ?, meta_type = ?, dir_flags = ?, meta_flags = ?, size= ?, ctime= ?, crtime= ?, atime= ?, mtime= ?, mime_type = ? " + "WHERE obj_id = ?"), //NON-NLS INSERT_LAYOUT_FILE("INSERT INTO tsk_file_layout (obj_id, byte_start, byte_len, sequence) " //NON-NLS @@ -11173,12 +11622,14 @@ private enum PREPARED_STATEMENT { INSERT_IMAGE_INFO("INSERT INTO tsk_image_info (obj_id, type, ssize, tzone, size, md5, sha1, sha256, display_name)" + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"), INSERT_DATA_SOURCE_INFO("INSERT INTO data_source_info (obj_id, device_id, time_zone) VALUES (?, ?, ?)"), + INSERT_DATA_SOURCE_INFO_WITH_ACQ_DETAIL("INSERT INTO data_source_info (obj_id, device_id, time_zone, acquisition_details) VALUES (?, ?, ?, ?)"), INSERT_VS_INFO("INSERT INTO tsk_vs_info (obj_id, vs_type, img_offset, block_size) VALUES (?, ?, ?, ?)"), 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 (?, ?, ?, ?, ?, ?, ?, ?, ?)"); + + "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 = ?"); private final String sql; diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java index 030284cc1d9a6612697c1c61a63067ad9ba075e2..c1d398602ede2354f42559fb0db1feb5225ec661 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java @@ -411,13 +411,17 @@ void free() throws TskCoreException { * case database. */ long addImageInfo(long deviceObjId, List<String> imageFilePaths, String timeZone, SleuthkitCase skCase) throws TskCoreException { + JniDbHelper dbHelper = new JniDbHelper(skCase); try { - long tskAutoDbPointer = initializeAddImgNat(caseDbPointer, timezoneLongToShort(timeZone), false, false, false); - runOpenAndAddImgNat(tskAutoDbPointer, UUID.randomUUID().toString(), imageFilePaths.toArray(new String[0]), imageFilePaths.size(), timeZone); - long id = commitAddImgNat(tskAutoDbPointer); + dbHelper.beginTransaction(); + long tskAutoDbPointer = initializeAddImgNat(caseDbPointer, 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(); skCase.addDataSourceToHasChildrenMap(); return id; } catch (TskDataException ex) { + dbHelper.revertTransaction(); throw new TskCoreException("Error adding image to case database", ex); } } @@ -455,6 +459,7 @@ public class AddImageProcess { private volatile long tskAutoDbPointer; private boolean isCanceled; private final SleuthkitCase skCase; + private final JniDbHelper dbHelper; /** * Constructs an object that encapsulates a multi-step process to @@ -477,6 +482,7 @@ private AddImageProcess(String timeZone, boolean addUnallocSpace, boolean skipFa tskAutoDbPointer = 0; this.isCanceled = false; this.skCase = skCase; + this.dbHelper = new JniDbHelper(skCase); } /** @@ -501,14 +507,14 @@ public void run(String deviceId, String[] imageFilePaths, int sectorSize) throws getTSKReadLock(); try { long imageHandle = 0; - synchronized (this) { if (0 != tskAutoDbPointer) { throw new TskCoreException("Add image process already started"); } if (!isCanceled) { //with isCanceled being guarded by this it will have the same value everywhere in this synchronized block imageHandle = openImage(imageFilePaths, sectorSize, false, caseDbPointer); - tskAutoDbPointer = initAddImgNat(caseDbPointer, timezoneLongToShort(timeZone), addUnallocSpace, skipFatFsOrphans); + dbHelper.beginTransaction(); + tskAutoDbPointer = initAddImgNat(caseDbPointer, dbHelper, timezoneLongToShort(timeZone), addUnallocSpace, skipFatFsOrphans); } if (0 == tskAutoDbPointer) { throw new TskCoreException("initAddImgNat returned a NULL TskAutoDb pointer"); @@ -556,9 +562,11 @@ public synchronized void revert() throws TskCoreException { if (tskAutoDbPointer == 0) { throw new TskCoreException("AddImgProcess::revert: AutoDB pointer is NULL"); } - - revertAddImgNat(tskAutoDbPointer); - // the native code deleted the object + + dbHelper.revertTransaction(); + + // Delete the object in the native code + finishAddImgNat(tskAutoDbPointer); tskAutoDbPointer = 0; } finally { releaseTSKReadLock(); @@ -581,12 +589,13 @@ public synchronized long commit() throws TskCoreException { throw new TskCoreException("AddImgProcess::commit: AutoDB pointer is NULL"); } - long id = commitAddImgNat(tskAutoDbPointer); + dbHelper.commitTransaction(); - skCase.addDataSourceToHasChildrenMap(); - - // the native code deleted the object + // Get the image ID and delete the object in the native code + long id = finishAddImgNat(tskAutoDbPointer); tskAutoDbPointer = 0; + + skCase.addDataSourceToHasChildrenMap(); return id; } finally { releaseTSKReadLock(); @@ -1950,9 +1959,9 @@ public static long openFile(long fsHandle, long fileId, TSK_FS_ATTR_TYPE_ENUM at private static native HashHitInfo hashDbLookupVerbose(String hash, int dbHandle) throws TskCoreException; - private static native long initAddImgNat(long db, String timezone, boolean addUnallocSpace, boolean skipFatFsOrphans) throws TskCoreException; + private static native long initAddImgNat(long db, JniDbHelper dbHelperObj, String timezone, boolean addUnallocSpace, boolean skipFatFsOrphans) throws TskCoreException; - private static native long initializeAddImgNat(long db, String timezone, boolean addFileSystems, boolean addUnallocSpace, boolean skipFatFsOrphans) throws TskCoreException; + private static native long initializeAddImgNat(long db, JniDbHelper dbHelperObj, String timezone, boolean addFileSystems, boolean addUnallocSpace, boolean skipFatFsOrphans) throws TskCoreException; private static native void runOpenAndAddImgNat(long process, String deviceId, String[] imgPath, int splits, String timezone) throws TskCoreException, TskDataException; @@ -1960,9 +1969,7 @@ public static long openFile(long fsHandle, long fileId, TSK_FS_ATTR_TYPE_ENUM at private static native void stopAddImgNat(long process) throws TskCoreException; - private static native void revertAddImgNat(long process) throws TskCoreException; - - private static native long commitAddImgNat(long process) throws TskCoreException; + private static native long finishAddImgNat(long process) throws TskCoreException; private static native long openImgNat(String[] imgPath, int splits, int sSize) throws TskCoreException; diff --git a/bindings/java/src/org/sleuthkit/datamodel/TimelineManager.java b/bindings/java/src/org/sleuthkit/datamodel/TimelineManager.java index 5ddc48de94519d2271f6140e080ff83eeb2e81c9..296b548373a99bb50606118a778c6f5f4d949754 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/TimelineManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TimelineManager.java @@ -487,6 +487,29 @@ private long addEventDescription(long dataSourceObjId, long fileObjId, Long arti } Collection<TimelineEvent> addEventsForNewFile(AbstractFile file, CaseDbConnection connection) throws TskCoreException { + Set<TimelineEvent> events = addEventsForNewFileQuiet(file, connection); + events.stream() + .map(TimelineEventAddedEvent::new) + .forEach(caseDB::fireTSKEvent); + + return events; + } + + /** + * Adds timeline events for the new file to the database. + * Does not fire TSKEvents for each addition. This method should only be used if an + * update event will be sent later. For example, a data source processor may + * send out a single event that a data source has been added rather than an event + * for each timeline event. + * + * @param file The new file + * @param connection Database connection to use + * + * @return Set of new events + * + * @throws TskCoreException + */ + Set<TimelineEvent> addEventsForNewFileQuiet(AbstractFile file, CaseDbConnection connection) throws TskCoreException { //gather time stamps into map Map<TimelineEventType, Long> timeMap = ImmutableMap.of(TimelineEventType.FILE_CREATED, file.getCrtime(), TimelineEventType.FILE_ACCESSED, file.getAtime(), @@ -532,9 +555,6 @@ Collection<TimelineEvent> addEventsForNewFile(AbstractFile file, CaseDbConnectio } finally { caseDB.releaseSingleUserCaseWriteLock(); } - events.stream() - .map(TimelineEventAddedEvent::new) - .forEach(caseDB::fireTSKEvent); return events; } diff --git a/tsk/auto/tsk_db.h b/tsk/auto/tsk_db.h index a95aaecc5fa95db67f4d3cb6146d7ac9d4ea51e8..edb300dcee781cb2cb7462f1ca842f3e71507dfc 100755 --- a/tsk/auto/tsk_db.h +++ b/tsk/auto/tsk_db.h @@ -228,7 +228,8 @@ class TskDb { @param name A file name @param extension The file name extension will be extracted to extension. - */void extractExtension(char *name, char *extension ) { + */ + void extractExtension(char *name, char *extension ) { char *ext = strrchr(name, '.'); //if ext is not null and is not the entire filename... diff --git a/win32/tsk_jni/tsk_jni.vcxproj b/win32/tsk_jni/tsk_jni.vcxproj index 182be991cda54a1634ec7c0a28a028b42e6cc6c7..c5ecd0a12553a1d6b86a187d49512d0859a04643 100755 --- a/win32/tsk_jni/tsk_jni.vcxproj +++ b/win32/tsk_jni/tsk_jni.vcxproj @@ -578,9 +578,11 @@ </PostBuildEvent> </ItemDefinitionGroup> <ItemGroup> + <ClCompile Include="..\..\bindings\java\jni\auto_db_java.cpp" /> <ClCompile Include="..\..\bindings\java\jni\dataModel_SleuthkitJNI.cpp" /> </ItemGroup> <ItemGroup> + <ClInclude Include="..\..\bindings\java\jni\auto_db_java.h" /> <ClInclude Include="..\..\bindings\java\jni\dataModel_SleuthkitJNI.h" /> </ItemGroup> <ItemGroup> diff --git a/win32/tsk_jni/tsk_jni.vcxproj.filters b/win32/tsk_jni/tsk_jni.vcxproj.filters index 3a1bcb722218d3405eb6cf8a73c15364285c6df1..ce3a1c769220e3e9bc605b36cdd46c98fc70f8cf 100755 --- a/win32/tsk_jni/tsk_jni.vcxproj.filters +++ b/win32/tsk_jni/tsk_jni.vcxproj.filters @@ -14,10 +14,16 @@ <ClCompile Include="..\..\bindings\java\jni\dataModel_SleuthkitJNI.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\..\bindings\java\jni\auto_db_java.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\bindings\java\jni\dataModel_SleuthkitJNI.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="..\..\bindings\java\jni\auto_db_java.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> </Project> \ No newline at end of file