diff --git a/bindings/java/jni/auto_db_java.cpp b/bindings/java/jni/auto_db_java.cpp index 625739f8a5a374dc18ef49a0cf983277f1c493c6..a20fd04b4a53c1d9938093babd2ad1e0be6b928a 100644 --- a/bindings/java/jni/auto_db_java.cpp +++ b/bindings/java/jni/auto_db_java.cpp @@ -1029,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 = ""; @@ -1504,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 f2570cf2294b6ebacd0978857c7a17b672269447..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); 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 index 2e5ee97dc7d1090fcd1963cd45d2aba474730f7c..ac4912ad33434d58ee146ba4ff1479730d5c47d0 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/AddDataSourceCallbacks.java +++ b/bindings/java/src/org/sleuthkit/datamodel/AddDataSourceCallbacks.java @@ -24,13 +24,6 @@ * Provides callbacks at key points during the process of adding a data source to a case database. */ public interface AddDataSourceCallbacks { - /** - * Call when the data source has been completely added to the case database. - * - * @param dataSourceObjectId The object ID of the new data source - */ - void onDataSourceAdded(long dataSourceObjectId); - /** * Call to add a set of file object IDs that have been added to the database. * diff --git a/bindings/java/src/org/sleuthkit/datamodel/DefaultAddDataSourceCallbacks.java b/bindings/java/src/org/sleuthkit/datamodel/DefaultAddDataSourceCallbacks.java index 920f6079dcb79813f1faaacbdd30eb40988441ac..db378ae750103d5f4b08f0f389a6fef766cd8f62 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/DefaultAddDataSourceCallbacks.java +++ b/bindings/java/src/org/sleuthkit/datamodel/DefaultAddDataSourceCallbacks.java @@ -24,15 +24,8 @@ * Do-nothing version of AddDataSourceCallbacks */ public class DefaultAddDataSourceCallbacks implements AddDataSourceCallbacks { - - @Override - public void onDataSourceAdded(long dataSourceObjectId) { - // Do nothing - } - - @Override - public void onFilesAdded(List<Long> fileObjectIds) { - // Do nothing - } - + @Override + public void onFilesAdded(List<Long> fileObjectIds) { + // Do nothing + } } 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 8c6c1d545bf191500fcf022558d8af95c3e2e502..95df24bf8c5fbc19fca86e9eaa08bfb88206d64c 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java +++ b/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java @@ -107,6 +107,8 @@ void finish() { /** * 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. @@ -131,14 +133,6 @@ long addImageInfo(int type, long ssize, String timezone, caseDb.addImageNameJNI(objId, paths[i], i, trans); } commitTransaction(); - - try { - addDataSourceCallbacks.onDataSourceAdded(objId); - } catch (Exception ex) { - // Exception firewall - we do not want to return to the native code without - // passing it the data source ID - logger.log(Level.SEVERE, "Unexpected error from data source added callback", ex); - } return objId; } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding image to the database", ex); diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 3c9ea38d733ae1fcc1ca3093c2fa6199d42d1f73..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()) { diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java index d0ad7bb3d437b07288b78a71a7c2f73dd609c8e3..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 @@ -514,32 +516,29 @@ private AddImageProcess(String timeZone, boolean addUnallocSpace, boolean skipFa * the process) */ public void run(String deviceId, String[] imageFilePaths, int sectorSize) throws TskCoreException, TskDataException { - run(deviceId, imageFilePaths, sectorSize, new DefaultAddDataSourceCallbacks()); + Image img = addImageToDatabase(skCase, imageFilePaths, sectorSize, "", "", "", "", deviceId); + run(deviceId, img, sectorSize, new DefaultAddDataSourceCallbacks()); } - + /** * 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 * should be unique across multiple cases * (e.g., a UUID). - * @param imageFilePaths Full path(s) to the image file(s). - * @param sectorSize The sector size (use '0' for autodetect). + * @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). * - * @return The object ID of the new image. - * * @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 long run(String deviceId, String[] imageFilePaths, int sectorSize, - AddDataSourceCallbacks addDataSourceCallbacks) throws TskCoreException, TskDataException { + public void run(String deviceId, Image image, int sectorSize, + AddDataSourceCallbacks addDataSourceCallbacks) throws TskCoreException, TskDataException { dbHelper = new JniDbHelper(skCase, addDataSourceCallbacks); getTSKReadLock(); try { @@ -549,7 +548,7 @@ public long run(String deviceId, String[] imageFilePaths, int sectorSize, 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) { @@ -557,16 +556,12 @@ public long run(String deviceId, String[] imageFilePaths, int sectorSize, } } if (imageHandle != 0) { - runAddImgNat(tskAutoDbPointer, deviceId, imageHandle, timeZone, imageWriterPath); - + runAddImgNat(tskAutoDbPointer, deviceId, imageHandle, image.getId(), timeZone, imageWriterPath); } } finally { finishAddImageProcess(); releaseTSKReadLock(); } - synchronized (this) { - return imageId; - } } /** @@ -679,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 @@ -837,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; @@ -884,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 @@ -2000,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; @@ -2033,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);