diff --git a/bindings/java/jni/auto_db_java.cpp b/bindings/java/jni/auto_db_java.cpp index 969d87ba117d3a9b4959ad27feb27c116d42b62f..eb009c7d3f80e656ef39259b29e6c02bffff095f 100644 --- a/bindings/java/jni/auto_db_java.cpp +++ b/bindings/java/jni/auto_db_java.cpp @@ -79,7 +79,7 @@ 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; } @@ -184,7 +184,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 +205,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) { @@ -1003,13 +1014,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 @@ -1043,14 +1047,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 diff --git a/bindings/java/jni/auto_db_java.h b/bindings/java/jni/auto_db_java.h index e6c5e68dd7217d784695749cf53c24df090db75d..a4a55e509cf2c9cbac9bb8db521e3095e19953d1 100644 --- a/bindings/java/jni/auto_db_java.h +++ b/bindings/java/jni/auto_db_java.h @@ -204,7 +204,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/src/org/sleuthkit/datamodel/AddDataSourceCallbacks.java b/bindings/java/src/org/sleuthkit/datamodel/AddDataSourceCallbacks.java new file mode 100644 index 0000000000000000000000000000000000000000..2e5ee97dc7d1090fcd1963cd45d2aba474730f7c --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/AddDataSourceCallbacks.java @@ -0,0 +1,40 @@ +/* + * 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 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. + * + * @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..920f6079dcb79813f1faaacbdd30eb40988441ac --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/DefaultAddDataSourceCallbacks.java @@ -0,0 +1,38 @@ +/* + * 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 onDataSourceAdded(long dataSourceObjectId) { + // Do nothing + } + + @Override + public void onFilesAdded(List<Long> fileObjectIds) { + // Do nothing + } + +} diff --git a/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java b/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java index 4476402935fdc0e9c1f41b9d1e3fdbb6bfd1143e..5d61e57c7d66e18cf876584eb7f58e0b99c89e00 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java +++ b/bindings/java/src/org/sleuthkit/datamodel/JniDbHelper.java @@ -40,6 +40,7 @@ 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<>(); @@ -49,8 +50,9 @@ class JniDbHelper { private final List<FileInfo> batchedFiles = new ArrayList<>(); private final List<LayoutRangeInfo> batchedLayoutRanges = new ArrayList<>(); - JniDbHelper(SleuthkitCase caseDb) { + JniDbHelper(SleuthkitCase caseDb, AddDataSourceCallbacks addDataSourceCallbacks) { this.caseDb = caseDb; + this.addDataSourceCallbacks = addDataSourceCallbacks; trans = null; } @@ -113,18 +115,23 @@ 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(); + + addDataSourceCallbacks.onDataSourceAdded(objId); return objId; } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding image to the database", ex); revertTransaction(); return -1; - } + } } /** @@ -338,6 +345,7 @@ 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) { @@ -360,6 +368,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) { @@ -380,45 +389,48 @@ private long addBatchedFilesToDb() { 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(); + batchedFiles.clear(); return -1; } } commitTransaction(); + addDataSourceCallbacks.onFilesAdded(newObjIds); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding batched files to database", ex); revertTransaction(); + batchedFiles.clear(); 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 { + // The parent wasn't found in the cache so do a database query + java.io.File parentAsFile = new java.io.File(parentPath); + return caseDb.findParentObjIdJNI(fileInfo.parMetaAddr, fileInfo.fsObjId, parentAsFile.getPath(), parentAsFile.getName(), trans); + } + } /** * Add a layout file to the database. diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java index 4fe44e69095ea9e9a78621ed570f2a5b3417896d..ca10fccca3e7a63440edc703f4025d48d4053a76 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java @@ -424,7 +424,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); @@ -470,7 +470,7 @@ public class AddImageProcess { private volatile long tskAutoDbPointer; 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,7 +493,7 @@ private AddImageProcess(String timeZone, boolean addUnallocSpace, boolean skipFa tskAutoDbPointer = 0; this.isCanceled = false; this.skCase = skCase; - this.dbHelper = new JniDbHelper(skCase); + } /** @@ -515,6 +515,31 @@ 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()); + } + + /** + * 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 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, String[] imageFilePaths, int sectorSize, + AddDataSourceCallbacks addDataSourceCallbacks) throws TskCoreException, TskDataException { + dbHelper = new JniDbHelper(skCase, addDataSourceCallbacks); getTSKReadLock(); try { long imageHandle = 0; @@ -536,7 +561,7 @@ public void run(String deviceId, String[] imageFilePaths, int sectorSize) throws } finally { releaseTSKReadLock(); } - } + } /** * Stops the process of adding the image to the case database that @@ -597,7 +622,9 @@ public synchronized long commit() throws TskCoreException { throw new TskCoreException("AddImgProcess::commit: AutoDB pointer is NULL"); } - dbHelper.finish(); + if (dbHelper != null) { + dbHelper.finish(); + } // Get the image ID and delete the object in the native code long id = finishAddImgNat(tskAutoDbPointer);