diff --git a/bindings/java/jni/auto_db_java.cpp b/bindings/java/jni/auto_db_java.cpp index 276277dbf90f25479a9c286aad84d84f063613bb..a20fd04b4a53c1d9938093babd2ad1e0be6b928a 100644 --- a/bindings/java/jni/auto_db_java.cpp +++ b/bindings/java/jni/auto_db_java.cpp @@ -79,16 +79,11 @@ TskAutoDbJava::initializeJni(JNIEnv * jniEnv, jobject jobj) { } 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"); + 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;[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; @@ -184,7 +179,8 @@ TskAutoDbJava::getObjectInfo(uint64_t objId, TSK_DB_OBJECT** obj_info) { */ 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 string& sha1, const string& sha256, const string& deviceId, const string& collectionDetails, + char** img_ptrs, int num_imgs) { const char *tz_cstr = timezone.c_str(); jstring tzj = m_jniEnv->NewStringUTF(tz_cstr); @@ -204,8 +200,18 @@ TskAutoDbJava::addImageInfo(int type, TSK_OFF_T ssize, int64_t & objId, const st const char *coll_cstr = collectionDetails.c_str(); jstring collj = m_jniEnv->NewStringUTF(coll_cstr); + jobjectArray imgNamesj = (jobjectArray)m_jniEnv->NewObjectArray( + num_imgs, + m_jniEnv->FindClass("java/lang/String"), + m_jniEnv->NewStringUTF("")); + + for (int i = 0; i < num_imgs; i++) { + m_jniEnv->SetObjectArrayElement( + imgNamesj, i, m_jniEnv->NewStringUTF(img_ptrs[i])); + } + jlong objIdj = m_jniEnv->CallLongMethod(m_javaDbObj, m_addImageMethodID, - type, ssize, tzj, size, md5j, sha1j, sha256j, devIdj, collj); + type, ssize, tzj, size, md5j, sha1j, sha256j, devIdj, collj, imgNamesj); objId = (int64_t)objIdj; if (objId < 0) { @@ -216,29 +222,6 @@ TskAutoDbJava::addImageInfo(int type, TSK_OFF_T ssize, int64_t & objId, const st 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. * @@ -1046,6 +1029,11 @@ TskAutoDbJava::openImage(const char* a_deviceId) uint8_t TskAutoDbJava::addImageDetails(const char* deviceId) { + // The image has already been added to the database + if (m_curImgId > 0) { + return 0; + } + string md5 = ""; string sha1 = ""; string collectionDetails = ""; @@ -1070,13 +1058,6 @@ TskAutoDbJava::addImageDetails(const char* 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 @@ -1110,14 +1091,12 @@ TskAutoDbJava::addImageDetails(const char* deviceId) 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; - } + 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, + img_ptrs, m_img_info->num_img)) { + registerError(); + return 1; } #ifdef TSK_WIN32 @@ -1530,6 +1509,15 @@ TskAutoDbJava::setTz(string tzone) m_curImgTZone = tzone; } +/** + * Set the object ID for the data source + */ +void +TskAutoDbJava::setDatasourceObjId(int64_t img_id) +{ + m_curImgId = img_id; +} + TSK_RETVAL_ENUM TskAutoDbJava::processFile(TSK_FS_FILE * fs_file, const char *path) { diff --git a/bindings/java/jni/auto_db_java.h b/bindings/java/jni/auto_db_java.h index 3609d6a38c274f9829d13f403c21921387d9a92e..26e370d093b7229cad868d813969be22324930a5 100644 --- a/bindings/java/jni/auto_db_java.h +++ b/bindings/java/jni/auto_db_java.h @@ -44,6 +44,7 @@ class TskAutoDbJava :public TskAuto { virtual void closeImage(); void close(); virtual void setTz(string tzone); + virtual void setDatasourceObjId(int64_t img_id); virtual TSK_FILTER_ENUM filterVs(const TSK_VS_INFO * vs_info); virtual TSK_FILTER_ENUM filterVol(const TSK_VS_PART_INFO * vs_part); @@ -206,7 +207,7 @@ class TskAutoDbJava :public TskAuto { // 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); + const string& sha1, const string& sha256, const string& deviceId, const string& collectionDetails, char** img_ptrs, int num_imgs); 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); diff --git a/bindings/java/jni/dataModel_SleuthkitJNI.cpp b/bindings/java/jni/dataModel_SleuthkitJNI.cpp index 5452ccbeac183314b784bbc25a37ce681511a3af..f4da3b4b62a2c5b29289ca6962b732af7a9bf1eb 100644 --- a/bindings/java/jni/dataModel_SleuthkitJNI.cpp +++ b/bindings/java/jni/dataModel_SleuthkitJNI.cpp @@ -15,6 +15,10 @@ #include "tsk/img/img_writer.h" #include "tsk/img/raw.h" #include "auto_db_java.h" +#if HAVE_LIBEWF +#include "tsk/img/ewf.h" +#include "tsk/img/tsk_img_i.h" +#endif #include "jni.h" #include "dataModel_SleuthkitJNI.h" #include <locale.h> @@ -905,7 +909,7 @@ JNIEXPORT void JNICALL TskAutoDbJava *tskAuto = ((TskAutoDbJava *) process); if (!tskAuto || tskAuto->m_tag != TSK_AUTO_TAG) { setThrowTskCoreError(env, - "runAddImgNat: Invalid TskAutoDbJava object passed in"); + "runOpenAndAddImgNat: Invalid TskAutoDbJava object passed in"); return; } @@ -914,7 +918,7 @@ JNIEXPORT void JNICALL if (NULL != deviceId) { device_id = (const char *) env->GetStringUTFChars(deviceId, &isCopy); if (NULL == device_id) { - setThrowTskCoreError(env, "runAddImgNat: Can't convert data source id string"); + setThrowTskCoreError(env, "runOpenAndAddImgNat: Can't convert data source id string"); return; } } @@ -933,7 +937,7 @@ JNIEXPORT void JNICALL GetStringUTFChars(jsPath, &isCopy); if (imagepaths8[i] == NULL) { setThrowTskCoreError(env, - "runAddImgNat: Can't convert path strings."); + "runOpenAndAddImgNat: Can't convert path strings."); // @@@ should cleanup here paths that have been converted in imagepaths8[i] return; } @@ -997,11 +1001,12 @@ JNIEXPORT void JNICALL * @param process the add-image process created by initAddImgNat * @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 a_img_info image info object +* @param img_id The object ID of the image in the database * @param timeZone the timezone the image is from */ 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) { + jclass obj, jlong process, jstring deviceId, jlong a_img_info, jlong img_id, jstring timeZone, jstring imageWriterPathJ) { TskAutoDbJava *tskAuto = ((TskAutoDbJava *)process); if (!tskAuto || tskAuto->m_tag != TSK_AUTO_TAG) { @@ -1020,6 +1025,9 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_runAddImgNat(JNIEnv * env, } } + // Set the data source object ID + tskAuto->setDatasourceObjId(img_id); + // Set the time zone. if (env->GetStringLength(timeZone) > 0) { const char *time_zone = env->GetStringUTFChars(timeZone, &isCopy); @@ -1125,7 +1133,6 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_finishAddImgNat(JNIEnv * env, } - /* * Open an image pointer for the given image. * @return the created TSK_IMG_INFO pointer @@ -1142,23 +1149,23 @@ JNIEXPORT jlong JNICALL jboolean isCopy; // get pointers to each of the file names - char **imagepaths8 = (char **) tsk_malloc(num_imgs * sizeof(char *)); + char **imagepaths8 = (char **)tsk_malloc(num_imgs * sizeof(char *)); if (imagepaths8 == NULL) { setThrowTskCoreError(env); return 0; } for (int i = 0; i < num_imgs; i++) { imagepaths8[i] = - (char *) env-> - GetStringUTFChars((jstring) env->GetObjectArrayElement(paths, + (char *)env-> + GetStringUTFChars((jstring)env->GetObjectArrayElement(paths, i), &isCopy); // @@@ Error check } // open the image img_info = - tsk_img_open_utf8((int) num_imgs, imagepaths8, TSK_IMG_TYPE_DETECT, - sector_size); + tsk_img_open_utf8((int)num_imgs, imagepaths8, TSK_IMG_TYPE_DETECT, + sector_size); if (img_info == NULL) { setThrowTskCoreError(env, tsk_error_get()); } @@ -1167,14 +1174,186 @@ JNIEXPORT jlong JNICALL for (int i = 0; i < num_imgs; i++) { env-> ReleaseStringUTFChars((jstring) - env->GetObjectArrayElement(paths, i), imagepaths8[i]); + env->GetObjectArrayElement(paths, i), imagepaths8[i]); } free(imagepaths8); return (jlong) img_info; } +/* + * Get the full list of paths associated with an image. + */ +JNIEXPORT jobjectArray JNICALL +Java_org_sleuthkit_datamodel_SleuthkitJNI_getPathsForImageNat(JNIEnv * env, + jclass obj, jlong a_img_info) { + + TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info); + if (img_info == 0) { + //exception already set + return 0; + } + + char **img_ptrs; +#ifdef TSK_WIN32 + // convert image paths to UTF-8 + img_ptrs = (char **)tsk_malloc(img_info->num_img * sizeof(char *)); + if (img_ptrs == NULL) { + return (jobjectArray)env->NewObjectArray(0, env->FindClass("java/lang/String"), env->NewStringUTF("")); + } + + for (int i = 0; i < img_info->num_img; i++) { + char * img2 = (char*)tsk_malloc(1024 * sizeof(char)); + UTF8 *ptr8; + UTF16 *ptr16; + + ptr8 = (UTF8 *)img2; + ptr16 = (UTF16 *)img_info->images[i]; + + uint8_t retval = + tsk_UTF16toUTF8_lclorder((const UTF16 **)&ptr16, (UTF16 *) + & ptr16[TSTRLEN(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 (jobjectArray)env->NewObjectArray(0, env->FindClass("java/lang/String"), env->NewStringUTF("")); + } + img_ptrs[i] = img2; + } +#else + img_ptrs = img_info->images; +#endif + + jobjectArray path_list = (jobjectArray)env->NewObjectArray(img_info->num_img, env->FindClass("java/lang/String"), env->NewStringUTF("")); + for (int i = 0; i < img_info->num_img; i++) { + env->SetObjectArrayElement(path_list, i, env->NewStringUTF(img_ptrs[i])); + } + + return path_list; +} + + +/* + * Get the size of an image. + */ +JNIEXPORT jlong JNICALL +Java_org_sleuthkit_datamodel_SleuthkitJNI_getSizeForImageNat(JNIEnv * env, + jclass obj, jlong a_img_info) { + + TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info); + if (img_info == 0) { + //exception already set + return 0; + } + + return img_info->size; +} + + +/* + * Get the type of an image. + */ +JNIEXPORT jlong JNICALL +Java_org_sleuthkit_datamodel_SleuthkitJNI_getTypeForImageNat(JNIEnv * env, + jclass obj, jlong a_img_info) { + + TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info); + if (img_info == 0) { + //exception already set + return 0; + } + + return img_info->itype; +} + + +/* +* Get the computed sector size of an image. +*/ +JNIEXPORT jlong JNICALL +Java_org_sleuthkit_datamodel_SleuthkitJNI_getSectorSizeForImageNat(JNIEnv * env, + jclass obj, jlong a_img_info) { + + TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info); + if (img_info == 0) { + //exception already set + return 0; + } + + return img_info->sector_size; +} + +/* +* Get the md5 hash of an image. +*/ +JNIEXPORT jstring JNICALL +Java_org_sleuthkit_datamodel_SleuthkitJNI_getMD5HashForImageNat(JNIEnv * env, + jclass obj, jlong a_img_info) { + + TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info); + if (img_info == 0) { + //exception already set + return 0; + } + // env->NewStringUTF(img_ptrs[i]) +#if HAVE_LIBEWF + if (img_info->itype == TSK_IMG_TYPE_EWF_EWF) { + IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)img_info; + if (ewf_info->md5hash_isset) { + return env->NewStringUTF(ewf_info->md5hash); + } + } +#endif + return env->NewStringUTF(""); +} +/* +* Get the sha1 hash of an image. +*/ +JNIEXPORT jstring JNICALL +Java_org_sleuthkit_datamodel_SleuthkitJNI_getSha1HashForImageNat(JNIEnv * env, + jclass obj, jlong a_img_info) { + + TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info); + if (img_info == 0) { + //exception already set + return 0; + } + // env->NewStringUTF(img_ptrs[i]) +#if HAVE_LIBEWF + if (img_info->itype == TSK_IMG_TYPE_EWF_EWF) { + IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)img_info; + if (ewf_info->sha1hash_isset) { + return env->NewStringUTF(ewf_info->sha1hash); + } + } +#endif + return env->NewStringUTF(""); +} + +/* +* Get the collection details of an image. +*/ +JNIEXPORT jstring JNICALL +Java_org_sleuthkit_datamodel_SleuthkitJNI_getCollectionDetailsForImageNat(JNIEnv * env, + jclass obj, jlong a_img_info) { + + TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info); + if (img_info == 0) { + //exception already set + return 0; + } + // env->NewStringUTF(img_ptrs[i]) +#if HAVE_LIBEWF + if (img_info->itype == TSK_IMG_TYPE_EWF_EWF) { + IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)img_info; + ewf_get_details(ewf_info); + } +#endif + return env->NewStringUTF(""); +} /* * Open the volume system at the given offset diff --git a/bindings/java/jni/dataModel_SleuthkitJNI.h b/bindings/java/jni/dataModel_SleuthkitJNI.h index ebd2b8fc39ef3a1ebe032e5bc153b00f24cf16bf..ed49532dc0106f329d0e134e5909ac8d41504036 100644 --- a/bindings/java/jni/dataModel_SleuthkitJNI.h +++ b/bindings/java/jni/dataModel_SleuthkitJNI.h @@ -194,10 +194,10 @@ JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_runOpenAndAddIm /* * Class: org_sleuthkit_datamodel_SleuthkitJNI * Method: runAddImgNat - * Signature: (JLjava/lang/String;JLjava/lang/String;Ljava/lang/String;)V + * Signature: (JLjava/lang/String;JJLjava/lang/String;Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_runAddImgNat - (JNIEnv *, jclass, jlong, jstring, jlong, jstring, jstring); + (JNIEnv *, jclass, jlong, jstring, jlong, jlong, jstring, jstring); /* * Class: org_sleuthkit_datamodel_SleuthkitJNI @@ -327,6 +327,62 @@ JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_readFileNat JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_saveFileMetaDataTextNat (JNIEnv *, jclass, jlong, jstring); +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: getPathsForImageNat + * Signature: (J)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getPathsForImageNat + (JNIEnv *, jclass, jlong); + +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: getSizeForImageNat + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getSizeForImageNat + (JNIEnv *, jclass, jlong); + +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: getTypeForImageNat + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getTypeForImageNat + (JNIEnv *, jclass, jlong); + +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: getSectorSizeForImageNat + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getSectorSizeForImageNat + (JNIEnv *, jclass, jlong); + +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: getMD5HashForImageNat + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getMD5HashForImageNat + (JNIEnv *, jclass, jlong); + +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: getSha1HashForImageNat + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getSha1HashForImageNat + (JNIEnv *, jclass, jlong); + +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: getCollectionDetailsForImageNat + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getCollectionDetailsForImageNat + (JNIEnv *, jclass, jlong); + /* * Class: org_sleuthkit_datamodel_SleuthkitJNI * Method: closeImgNat diff --git a/bindings/java/src/org/sleuthkit/datamodel/AddDataSourceCallbacks.java b/bindings/java/src/org/sleuthkit/datamodel/AddDataSourceCallbacks.java new file mode 100644 index 0000000000000000000000000000000000000000..ac4912ad33434d58ee146ba4ff1479730d5c47d0 --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/AddDataSourceCallbacks.java @@ -0,0 +1,33 @@ +/* + * SleuthKit Java Bindings + * + * 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.List; + +/** + * Provides callbacks at key points during the process of adding a data source to a case database. + */ +public interface AddDataSourceCallbacks { + /** + * Call to add a set of file object IDs that have been added to the database. + * + * @param fileObjectIds List of file object IDs. + */ + void onFilesAdded(List<Long> fileObjectIds); +} diff --git a/bindings/java/src/org/sleuthkit/datamodel/DefaultAddDataSourceCallbacks.java b/bindings/java/src/org/sleuthkit/datamodel/DefaultAddDataSourceCallbacks.java new file mode 100644 index 0000000000000000000000000000000000000000..db378ae750103d5f4b08f0f389a6fef766cd8f62 --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/DefaultAddDataSourceCallbacks.java @@ -0,0 +1,31 @@ +/* + * SleuthKit Java Bindings + * + * 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.List; + +/** + * Do-nothing version of AddDataSourceCallbacks + */ +public class DefaultAddDataSourceCallbacks implements AddDataSourceCallbacks { + @Override + public void onFilesAdded(List<Long> fileObjectIds) { + // Do nothing + } +} diff --git a/bindings/java/src/org/sleuthkit/datamodel/Examples/Sample.java b/bindings/java/src/org/sleuthkit/datamodel/Examples/Sample.java index 01c2504980304f61e174835fccd4bee43816121e..c6b3031b60a05f378b21fff9d7af28213fb20ed3 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/Examples/Sample.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Examples/Sample.java @@ -50,7 +50,6 @@ public static void run(String imagePath) { } catch (TskDataException ex) { Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, null, ex); } - process.commit(); // print out all the images found, and their children List<Image> images = sk.getImages(); diff --git a/bindings/java/src/org/sleuthkit/datamodel/Image.java b/bindings/java/src/org/sleuthkit/datamodel/Image.java index 161e51f3adb132a14d83394f521c0349293ac93b..a465af5975966e2f9cc12870732f1af7f1a3328c 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Image.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Image.java @@ -129,6 +129,10 @@ public synchronized long getImageHandle() throws TskCoreException { return imageHandle; } + + synchronized void setImageHandle(long imageHandle) { + this.imageHandle = imageHandle; + } @Override public Content getDataSource() { diff --git a/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java b/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java index 6596b0dd5caabc9a243a2af105530143942731de..0b753b6246c96cda74b4f8a196a7094cb36a89ef 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java +++ b/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java @@ -22,8 +22,10 @@ import java.util.List; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.Map; import java.util.Objects; +import java.util.Queue; import java.util.logging.Level; import java.util.logging.Logger; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; @@ -33,6 +35,9 @@ * 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. + * + * Note that this code should only be used for the add image process, and not + * to add additional files afterward. */ class JniDbHelper { @@ -40,17 +45,20 @@ class JniDbHelper { private final SleuthkitCase caseDb; private CaseDbTransaction trans = null; + private final AddDataSourceCallbacks addDataSourceCallbacks; private final Map<Long, Long> fsIdToRootDir = new HashMap<>(); private final Map<Long, TskData.TSK_FS_TYPE_ENUM> fsIdToFsType = new HashMap<>(); private final Map<ParentCacheKey, Long> parentDirCache = new HashMap<>(); private static final long BATCH_FILE_THRESHOLD = 500; - private final List<FileInfo> batchedFiles = new ArrayList<>(); - private final List<LayoutRangeInfo> batchedLayoutRanges = new ArrayList<>(); + private final Queue<FileInfo> batchedFiles = new LinkedList<>(); + private final Queue<LayoutRangeInfo> batchedLayoutRanges = new LinkedList<>(); + private final List<Long> layoutFileIds = new ArrayList<>(); - JniDbHelper(SleuthkitCase caseDb) { + JniDbHelper(SleuthkitCase caseDb, AddDataSourceCallbacks addDataSourceCallbacks) { this.caseDb = caseDb; + this.addDataSourceCallbacks = addDataSourceCallbacks; trans = null; } @@ -93,11 +101,14 @@ private void revertTransaction() { void finish() { addBatchedFilesToDb(); addBatchedLayoutRangesToDb(); + processLayoutFiles(); } /** * Add a new image to the database. * Intended to be called from the native code during the add image process. + * Will not be called if the image was added to the database prior to starting + * the add image process. * * @param type Type of image. * @param ssize Sector size. @@ -113,41 +124,20 @@ void finish() { */ long addImageInfo(int type, long ssize, String timezone, long size, String md5, String sha1, String sha256, String deviceId, - String collectionDetails) { + String collectionDetails, String[] paths) { try { beginTransaction(); long objId = caseDb.addImageJNI(TskData.TSK_IMG_TYPE_ENUM.valueOf(type), ssize, size, timezone, md5, sha1, sha256, deviceId, collectionDetails, trans); + for (int i = 0;i < paths.length;i++) { + caseDb.addImageNameJNI(objId, paths[i], i, trans); + } commitTransaction(); return objId; } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding image to the database", ex); revertTransaction(); 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 { - beginTransaction(); - caseDb.addImageNameJNI(objId, name, sequence, trans); - commitTransaction(); - return 0; - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding image name to the database - image obj ID: " + objId + ", image name: " + name - + ", sequence: " + sequence, ex); - revertTransaction(); - return -1; } } @@ -338,9 +328,11 @@ long addFile(long parentObjId, * @return 0 if successful, -1 if not */ private long addBatchedFilesToDb() { + List<Long> newObjIds = new ArrayList<>(); try { beginTransaction(); - for (FileInfo fileInfo : batchedFiles) { + FileInfo fileInfo; + while ((fileInfo = batchedFiles.poll()) != null) { long computedParentObjId = fileInfo.parentObjId; try { // If we weren't given the parent object ID, look it up @@ -360,6 +352,7 @@ private long addBatchedFilesToDb() { null, TskData.FileKnown.UNKNOWN, fileInfo.escaped_path, fileInfo.extension, false, trans); + newObjIds.add(objId); // If we're adding the root directory for the file system, cache it if (fileInfo.parentObjId == fileInfo.fsObjId) { @@ -379,46 +372,50 @@ private long addBatchedFilesToDb() { } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding file to the database - parent object ID: " + computedParentObjId + ", file system object ID: " + fileInfo.fsObjId + ", name: " + fileInfo.name, ex); - revertTransaction(); - return -1; } } commitTransaction(); + try { + addDataSourceCallbacks.onFilesAdded(newObjIds); + } catch (Exception ex) { + // Exception firewall to prevent unexpected return to the native code + logger.log(Level.SEVERE, "Unexpected error from files added callback", ex); + } } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding batched files to database", ex); revertTransaction(); return -1; } - batchedFiles.clear(); return 0; } - - /** - * Look up the parent object ID for a file using the cache or the database. - * - * @param fileInfo The file to find the parent of - * - * @return Parent object ID - * - * @throws TskCoreException - */ - private long getParentObjId(FileInfo fileInfo) throws TskCoreException { - // Remove the final slash from the path unless we're in the root folder - String parentPath = fileInfo.escaped_path; - if(parentPath.endsWith("/") && ! parentPath.equals("/")) { - parentPath = parentPath.substring(0, parentPath.lastIndexOf('/')); - } + + /** + * Look up the parent object ID for a file using the cache or the database. + * + * @param fileInfo The file to find the parent of + * + * @return Parent object ID + * + * @throws TskCoreException + */ + private long getParentObjId(FileInfo fileInfo) throws TskCoreException { + // Remove the final slash from the path unless we're in the root folder + String parentPath = fileInfo.escaped_path; + if(parentPath.endsWith("/") && ! parentPath.equals("/")) { + parentPath = parentPath.substring(0, parentPath.lastIndexOf('/')); + } - // Look up the parent - ParentCacheKey key = new ParentCacheKey(fileInfo.fsObjId, fileInfo.parMetaAddr, fileInfo.parSeq, parentPath); - if (parentDirCache.containsKey(key)) { - return parentDirCache.get(key); - } else { - // The parent wasn't found in the cache so do a database query - java.io.File parentAsFile = new java.io.File(parentPath); - return caseDb.findParentObjIdJNI(fileInfo.parMetaAddr, fileInfo.fsObjId, parentAsFile.getPath(), parentAsFile.getName(), trans); - } - } + // Look up the parent + ParentCacheKey key = new ParentCacheKey(fileInfo.fsObjId, fileInfo.parMetaAddr, fileInfo.parSeq, parentPath); + if (parentDirCache.containsKey(key)) { + return parentDirCache.get(key); + } else { + // There's no reason to do a database query since every folder added is being + // stored in the cache. + throw new TskCoreException("Parent not found in cache (fsObjId: " +fileInfo.fsObjId + ", parMetaAddr: " + fileInfo.parMetaAddr + + ", parSeq: " + fileInfo.parSeq + ", parentPath: " + parentPath + ")"); + } + } /** * Add a layout file to the database. @@ -462,6 +459,10 @@ long addLayoutFile(long parentObjId, null, null, true, trans); commitTransaction(); + + // Store the layout file ID for later processing + layoutFileIds.add(objId); + return objId; } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding layout file to the database - parent object ID: " + parentObjId @@ -499,19 +500,16 @@ long addLayoutFileRange(long objId, long byteStart, long byteLen, long seq) { private long addBatchedLayoutRangesToDb() { try { beginTransaction(); - - for (LayoutRangeInfo range : batchedLayoutRanges) { + LayoutRangeInfo range; + while ((range = batchedLayoutRanges.poll()) != null) { try { caseDb.addLayoutFileRangeJNI(range.objId, range.byteStart, range.byteLen, range.seq, trans); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding layout file range to the database - layout file ID: " + range.objId + ", byte start: " + range.byteStart + ", length: " + range.byteLen + ", seq: " + range.seq, ex); - revertTransaction(); - return -1; } } commitTransaction(); - batchedLayoutRanges.clear(); return 0; } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding batched files to database", ex); @@ -519,6 +517,16 @@ private long addBatchedLayoutRangesToDb() { return -1; } } + + /** + * Send completed layout files on for further processing. + * Note that this must wait until we know all the ranges for each + * file have been added to the database. + */ + void processLayoutFiles() { + addDataSourceCallbacks.onFilesAdded(layoutFileIds); + layoutFileIds.clear(); + } /** * Add a virtual directory to hold unallocated file system blocks. diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 69d98bd4400ac9a83b721616b506a1d96f34440f..cc6b85ba950d1df91ec5e650054ce038624835eb 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -5920,7 +5920,16 @@ public Image addImage(TskData.TSK_IMG_TYPE_ENUM type, long sectorSize, long size connection.executeUpdate(preparedStatement); // Create the new Image object - return new Image(this, newObjId, type.getValue(), deviceId, sectorSize, displayName, + String name = displayName; + if (name == null || name.isEmpty()) { + if (imagePaths.size() > 0) { + String path = imagePaths.get(0); + name = (new java.io.File(path)).getName(); + } else { + name = ""; + } + } + return new Image(this, newObjId, type.getValue(), deviceId, sectorSize, name, imagePaths.toArray(new String[imagePaths.size()]), timezone, md5, sha1, sha256, savedSize); } catch (SQLException ex) { if (!imagePaths.isEmpty()) { @@ -11065,44 +11074,6 @@ void addImageNameJNI(long objId, String name, long sequence, } } - /** - * 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, path: %s, name: %s", metaAddr, path, name)); - } - } catch (SQLException ex) { - throw new TskCoreException(String.format("Error looking up parent - meta addr: %d, path: %s, name: %s", metaAddr, path, name), ex); - } finally { - closeResultSet(resultSet); - } - } - /** * Add a file system file to the database. For use with the JNI callbacks * associated with the add image process. @@ -11526,7 +11497,6 @@ private enum PREPARED_STATEMENT { INSERT_POOL_INFO("INSERT INTO tsk_pool_info (obj_id, pool_type) VALUES (?, ?)"), INSERT_FS_INFO("INSERT INTO tsk_fs_info (obj_id, data_source_obj_id, img_offset, fs_type, block_size, block_count, root_inum, first_inum, last_inum, display_name)" + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), - SELECT_OBJ_ID_BY_META_ADDR_AND_PATH("SELECT obj_id FROM tsk_files WHERE meta_addr = ? AND fs_obj_id = ? AND parent_path = ? AND name = ?"), SELECT_TAG_NAME_BY_ID("SELECT * FROM tag_names where tag_name_id = ?"); 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 4fe44e69095ea9e9a78621ed570f2a5b3417896d..104488c1e4fd1e6bf7e10b302d2ebcc6fd63099b 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java @@ -35,7 +35,9 @@ import java.util.UUID; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import org.apache.commons.lang3.StringUtils; import org.sleuthkit.datamodel.TskData.TSK_FS_ATTR_TYPE_ENUM; +import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; /** * A utility class that provides a interface to the SleuthKit via JNI. Supports @@ -424,7 +426,7 @@ void free() throws TskCoreException { * case database. */ long addImageInfo(long deviceObjId, List<String> imageFilePaths, String timeZone, SleuthkitCase skCase) throws TskCoreException { - JniDbHelper dbHelper = new JniDbHelper(skCase); + JniDbHelper dbHelper = new JniDbHelper(skCase, new DefaultAddDataSourceCallbacks()); try { long tskAutoDbPointer = initializeAddImgNat(dbHelper, timezoneLongToShort(timeZone), false, false, false); runOpenAndAddImgNat(tskAutoDbPointer, UUID.randomUUID().toString(), imageFilePaths.toArray(new String[0]), imageFilePaths.size(), timeZone); @@ -468,9 +470,10 @@ public class AddImageProcess { private final boolean skipFatFsOrphans; private final String imageWriterPath; private volatile long tskAutoDbPointer; + private long imageId = 0; private boolean isCanceled; private final SleuthkitCase skCase; - private final JniDbHelper dbHelper; + private JniDbHelper dbHelper; /** * Constructs an object that encapsulates a multi-step process to @@ -493,13 +496,11 @@ private AddImageProcess(String timeZone, boolean addUnallocSpace, boolean skipFa tskAutoDbPointer = 0; this.isCanceled = false; this.skCase = skCase; - this.dbHelper = new JniDbHelper(skCase); + } /** * Starts the process of adding an image to the case database. - * Either AddImageProcess.commit or AddImageProcess.revert MUST be - * called after calling AddImageProcess.run. * * @param deviceId An ASCII-printable identifier for the * device associated with the image that @@ -515,6 +516,30 @@ private AddImageProcess(String timeZone, boolean addUnallocSpace, boolean skipFa * the process) */ public void run(String deviceId, String[] imageFilePaths, int sectorSize) throws TskCoreException, TskDataException { + Image img = addImageToDatabase(skCase, imageFilePaths, sectorSize, "", "", "", "", deviceId); + run(deviceId, img, sectorSize, new DefaultAddDataSourceCallbacks()); + } + + /** + * Starts the process of adding an image to the case database. + * + * @param deviceId An ASCII-printable identifier for the + * device associated with the image that + * should be unique across multiple cases + * (e.g., a UUID). + * @param image The image object (has already been added to the database) + * @param sectorSize The sector size (no longer used). + * @param addDataSourceCallbacks The callbacks to use to send data to ingest (may do nothing). + * + * @throws TskCoreException if a critical error occurs within the + * SleuthKit. + * @throws TskDataException if a non-critical error occurs within + * the SleuthKit (should be OK to continue + * the process) + */ + public void run(String deviceId, Image image, int sectorSize, + AddDataSourceCallbacks addDataSourceCallbacks) throws TskCoreException, TskDataException { + dbHelper = new JniDbHelper(skCase, addDataSourceCallbacks); getTSKReadLock(); try { long imageHandle = 0; @@ -523,7 +548,7 @@ public void run(String deviceId, String[] imageFilePaths, int sectorSize) throws 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, caseDbIdentifier); + imageHandle = image.getImageHandle(); tskAutoDbPointer = initAddImgNat(dbHelper, timezoneLongToShort(timeZone), addUnallocSpace, skipFatFsOrphans); } if (0 == tskAutoDbPointer) { @@ -531,12 +556,13 @@ public void run(String deviceId, String[] imageFilePaths, int sectorSize) throws } } if (imageHandle != 0) { - runAddImgNat(tskAutoDbPointer, deviceId, imageHandle, timeZone, imageWriterPath); + runAddImgNat(tskAutoDbPointer, deviceId, imageHandle, image.getId(), timeZone, imageWriterPath); } } finally { + finishAddImageProcess(); releaseTSKReadLock(); } - } + } /** * Stops the process of adding the image to the case database that @@ -558,56 +584,62 @@ public synchronized void stop() throws TskCoreException { releaseTSKReadLock(); } } + + /** + * Call at the end of the add image process regardless of the error/canceled state. + * + * Note that the new image is no longer deleted on error/cancellation + * + * If the process was not canceled, will add the final batch of files to the database + * and submit for any further processing through the callback. + * + * @throws TskCoreException + */ + private synchronized void finishAddImageProcess() throws TskCoreException { + if (tskAutoDbPointer == 0) { + return; + } + + // If the process wasn't cancelled, finish up processing the + // remaining files. + if (! this.isCanceled && dbHelper != null) { + dbHelper.finish(); + } + + // Free the auto DB pointer and get the image ID + imageId = finishAddImgNat(tskAutoDbPointer); + tskAutoDbPointer = 0; + + skCase.addDataSourceToHasChildrenMap(); + } /** - * Rolls back the process of adding an image to the case database - * that was started by calling AddImageProcess.run. + * This no longer needs to be called. * * @throws TskCoreException if a critical error occurs within the * SleuthKit. + * + * @deprecated No longer necessary */ + @Deprecated public synchronized void revert() throws TskCoreException { - getTSKReadLock(); - try { - if (tskAutoDbPointer == 0) { - throw new TskCoreException("AddImgProcess::revert: AutoDB pointer is NULL"); - } - - // Delete the object in the native code - finishAddImgNat(tskAutoDbPointer); - tskAutoDbPointer = 0; - } finally { - releaseTSKReadLock(); - } + // No-op } /** - * Completes the process of adding an image to the case database - * that was started by calling AddImageProcess.run. + * This no longer needs to be called. Will simply return the + * object ID of the new image. * * @return The object id of the image that was added. * * @throws TskCoreException if a critical error occurs within the * SleuthKit. + * + * @deprecated No longer necessary */ + @Deprecated public synchronized long commit() throws TskCoreException { - getTSKReadLock(); - try { - if (tskAutoDbPointer == 0) { - throw new TskCoreException("AddImgProcess::commit: AutoDB pointer is NULL"); - } - - dbHelper.finish(); - - // Get the image ID and delete the object in the native code - long id = finishAddImgNat(tskAutoDbPointer); - tskAutoDbPointer = 0; - - skCase.addDataSourceToHasChildrenMap(); - return id; - } finally { - releaseTSKReadLock(); - } + return imageId; } /** @@ -642,8 +674,6 @@ public void run(String[] imageFilePaths) throws TskCoreException, TskDataExcepti /** * Starts the process of adding an image to the case database. - * Either AddImageProcess.commit or AddImageProcess.revert MUST be - * called after calling AddImageProcess.run. * * @param deviceId An ASCII-printable identifier for the * device associated with the image that @@ -800,7 +830,6 @@ public static long openImage(String[] imageFiles, int sSize, SleuthkitCase skCas * TSK */ private static long openImage(String[] imageFiles, int sSize, boolean useCache, String caseIdentifer) throws TskCoreException { - getTSKReadLock(); try { long imageHandle; @@ -847,6 +876,105 @@ private static long openImage(String[] imageFiles, int sSize, boolean useCache, releaseTSKReadLock(); } } + + /** + * This is a temporary measure to support opening an image at the beginning + * of the add image process. The open image handle is put into the normal image cache so + * it won't be opened a second time and it will be closed during case closing. + * + * This will change when all image opens are done by object ID and not paths. + * + * @param skCase The case the image belongs to. + * @param imagePaths The complete list of paths for the image. + * @param imageHandle The open image handle from TSK. + */ + private static void cacheImageHandle(SleuthkitCase skCase, List<String> imagePaths, long imageHandle) { + + // Construct the hash key from the image paths + StringBuilder keyBuilder = new StringBuilder(); + for (int i = 0; i < imagePaths.size(); ++i) { + keyBuilder.append(imagePaths.get(i)); + } + final String imageKey = keyBuilder.toString(); + + // Get the case identifier + try { + String caseIdentifier = skCase.getUniqueCaseIdentifier(); + + synchronized (HandleCache.cacheLock) { + HandleCache.getCaseHandles(caseIdentifier).fsHandleCache.put(imageHandle, new HashMap<>()); + HandleCache.getCaseHandles(caseIdentifier).imageHandleCache.put(imageKey, imageHandle); + } + } catch (TskCoreException ex) { + // getUniqueCaseIdentfier() will only fail if the case is closed + } + } + + /** + * Add an image to the database and return the open image. + * + * @param skCase The current case. + * @param imagePaths The path(s) to the image (will just be the first for .e01, .001, etc). + * @param sectorSize The sector size (0 for auto-detect). + * @param timeZone The time zone. + * @param md5fromSettings MD5 hash (if known). + * @param sha1fromSettings SHA1 hash (if known). + * @param sha256fromSettings SHA256 hash (if known). + * @param deviceId Device ID. + * + * @return The Image object. + * + * @throws TskCoreException + */ + public static Image addImageToDatabase(SleuthkitCase skCase, String[] imagePaths, int sectorSize, + String timeZone, String md5fromSettings, String sha1fromSettings, String sha256fromSettings, String deviceId) throws TskCoreException { + + // Open the image + long imageHandle = openImgNat(imagePaths, 1, sectorSize); + + // Get the fields stored in the native code + List<String> computedPaths = Arrays.asList(getPathsForImageNat(imageHandle)); + long size = getSizeForImageNat(imageHandle); + long type = getTypeForImageNat(imageHandle); + long computedSectorSize = getSectorSizeForImageNat(imageHandle); + String md5 = md5fromSettings; + if (StringUtils.isEmpty(md5)) { + md5 = getMD5HashForImageNat(imageHandle); + } + String sha1 = sha1fromSettings; + if (StringUtils.isEmpty(sha1)) { + sha1 = getSha1HashForImageNat(imageHandle); + } + // Sleuthkit does not currently generate any SHA256 hashes. Set to empty + // string for consistency. + String sha256 = sha256fromSettings; + if (sha256 == null) { + sha256 = ""; + } + String collectionDetails = getCollectionDetailsForImageNat(imageHandle); + + // Now save to database + CaseDbTransaction transaction = skCase.beginTransaction(); + try { + Image img = skCase.addImage(TskData.TSK_IMG_TYPE_ENUM.valueOf(type), computedSectorSize, + size, null, computedPaths, + timeZone, md5, sha1, sha256, + deviceId, transaction); + if (!StringUtils.isEmpty(collectionDetails)) { + skCase.setAcquisitionDetails(img, collectionDetails); + } + transaction.commit(); + + img.setImageHandle(imageHandle); + cacheImageHandle(skCase, computedPaths, imageHandle); + return img; + } catch (TskCoreException ex) { + transaction.rollback(); + throw(ex); + } + } + + /** * Get volume system Handle @@ -1963,7 +2091,7 @@ public static long openFile(long fsHandle, long fileId, TSK_FS_ATTR_TYPE_ENUM at private static native void runOpenAndAddImgNat(long process, String deviceId, String[] imgPath, int splits, String timezone) throws TskCoreException, TskDataException; - private static native void runAddImgNat(long process, String deviceId, long a_img_info, String timeZone, String imageWriterPath) throws TskCoreException, TskDataException; + private static native void runAddImgNat(long process, String deviceId, long a_img_info, long image_id, String timeZone, String imageWriterPath) throws TskCoreException, TskDataException; private static native void stopAddImgNat(long process) throws TskCoreException; @@ -1996,6 +2124,20 @@ public static long openFile(long fsHandle, long fileId, TSK_FS_ATTR_TYPE_ENUM at private static native int readFileNat(long fileHandle, byte[] readBuffer, long offset, int offset_type, long len) throws TskCoreException; private static native int saveFileMetaDataTextNat(long fileHandle, String fileName) throws TskCoreException; + + private static native String[] getPathsForImageNat(long imgHandle); + + private static native long getSizeForImageNat(long imgHandle); + + private static native long getTypeForImageNat(long imgHandle); + + private static native long getSectorSizeForImageNat(long imgHandle); + + private static native String getMD5HashForImageNat(long imgHandle); + + private static native String getSha1HashForImageNat(long imgHandle); + + private static native String getCollectionDetailsForImageNat(long imgHandle); private static native void closeImgNat(long imgHandle);