From 17c85aec2863bae32b07124025716499693cf27a Mon Sep 17 00:00:00 2001 From: Ann Priestman <apriestman@basistech.com> Date: Mon, 11 Nov 2019 15:32:02 -0500 Subject: [PATCH] Pass in pool img info from Autopsy --- bindings/java/jni/dataModel_SleuthkitJNI.cpp | 114 ++++++++++++++---- bindings/java/jni/dataModel_SleuthkitJNI.h | 28 ++++- .../src/org/sleuthkit/datamodel/Pool.java | 17 ++- .../sleuthkit/datamodel/SleuthkitCase.java | 2 +- .../org/sleuthkit/datamodel/SleuthkitJNI.java | 79 ++++++++++-- tsk/auto/db_sqlite.cpp | 6 +- tsk/pool/apfs_pool_compat.cpp | 18 +-- tsk/pool/pool_compat.hpp | 1 + tsk/pool/tsk_pool.h | 1 + tsk/pool/tsk_pool.hpp | 6 + 10 files changed, 216 insertions(+), 56 deletions(-) diff --git a/bindings/java/jni/dataModel_SleuthkitJNI.cpp b/bindings/java/jni/dataModel_SleuthkitJNI.cpp index a3561d990..52eaaf4d0 100644 --- a/bindings/java/jni/dataModel_SleuthkitJNI.cpp +++ b/bindings/java/jni/dataModel_SleuthkitJNI.cpp @@ -1445,7 +1445,8 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_openPoolNat(JNIEnv * env, printf(" Casted img_info\n"); // TODO - use pool type - const TSK_POOL_INFO *pool = tsk_pool_open_img_sing(img_info, offset * img_info->sector_size, TSK_POOL_TYPE_DETECT); + //const TSK_POOL_INFO *pool = tsk_pool_open_img_sing(img_info, offset * img_info->sector_size, TSK_POOL_TYPE_DETECT); + const TSK_POOL_INFO *pool = tsk_pool_open_img_sing(img_info, offset, TSK_POOL_TYPE_DETECT); if (pool == NULL) { printf(" Failed to load pool\n"); tsk_error_print(stderr); @@ -1458,6 +1459,7 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_openPoolNat(JNIEnv * env, } /* +TODO UPDATe * Open file system with the given offset * @return the created TSK_FS_INFO pointer * @param env pointer to java environment this was called from @@ -1465,39 +1467,27 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_openPoolNat(JNIEnv * env, * @param a_img_info the pointer to the parent img object * @param fs_offset the offset in bytes to the file system */ -JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_openFsPoolNat -(JNIEnv * env, jclass obj, jlong a_img_info, jlong fs_offset, jlong a_pool_info, jlong pool_block) { - TSK_IMG_INFO *img_info = castImgInfo(env, a_img_info); - if (img_info == 0) { - //exception already set - return 0; - } +JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getImgInfoNat +(JNIEnv * env, jclass obj, jlong a_pool_info, jlong pool_block) { - printf("@@@ openFsPoolNat - trying to cast pool with address 0x%x\n", a_pool_info); + + printf("@@@ openGetImgInfoNat - trying to cast pool with address 0x%x\n", a_pool_info); TSK_POOL_INFO *pool_info = castPoolInfo(env, a_pool_info); if (pool_info == 0) { - printf("@@@ openFsPoolNat - Invalid cast to pool???\n"); + printf("@@@ openGetImgInfoNat - Invalid cast to pool???\n"); fflush(stdout); //exception already set return 0; } - TSK_FS_INFO *fs_info; - printf("Java_org_sleuthkit_datamodel_SleuthkitJNI_openFsPoolNat - pool_block = %lld\n", pool_block); + printf("Java_org_sleuthkit_datamodel_SleuthkitJNI_openGetImgInfoNat - pool_block = %lld\n", pool_block); fflush(stdout); - printf(" Ok have a pool\n"); printf(" Making new img_info\n"); fflush(stdout); - img_info = pool_info->get_img_info(pool_info, pool_block); + TSK_IMG_INFO *img_info = pool_info->get_img_info(pool_info, pool_block); - fs_info = - tsk_fs_open_img(img_info, (TSK_OFF_T)fs_offset, - TSK_FS_TYPE_DETECT); - if (fs_info == NULL) { - setThrowTskCoreError(env, tsk_error_get()); - } - return (jlong)fs_info; + return (jlong)img_info; } /* @@ -1679,6 +1669,70 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_readImgNat(JNIEnv * env, return (jint)copiedbytes; } +/* +TODO UPDATE +* Read bytes from the given volume system +* @return number of bytes read from the volume system, -1 on error +* @param env pointer to java environment this was called from +* @param obj the java object this was called from +* @param a_vs_info the pointer to the volume system object +* @param offset the offset in bytes to start at +* @param len number of bytes to read +*/ +JNIEXPORT jint JNICALL +Java_org_sleuthkit_datamodel_SleuthkitJNI_readPoolNat(JNIEnv * env, + jclass obj, jlong a_pool_info, jbyteArray jbuf, jlong offset, jlong len) +{ + //use fixed size stack-allocated buffer if possible + char fixed_buf[FIXED_BUF_SIZE]; + + char * buf = fixed_buf; + bool dynBuf = false; + if (len > FIXED_BUF_SIZE) { + dynBuf = true; + buf = (char *)tsk_malloc((size_t)len); + if (buf == NULL) { + setThrowTskCoreError(env); + return -1; + } + } + + TSK_POOL_INFO *pool_info = castPoolInfo(env, a_pool_info); + if (pool_info == 0) { + //exception already set + if (dynBuf) { + free(buf); + } + return -1; + } + + ssize_t bytesread = tsk_pool_read(pool_info, (TSK_DADDR_T)offset, buf, + (size_t)len); + if (bytesread == -1) { + setThrowTskCoreError(env, tsk_error_get()); + if (dynBuf) { + free(buf); + } + return -1; + } + + // package it up for return + // adjust number bytes to copy + ssize_t copybytes = bytesread; + jsize jbuflen = env->GetArrayLength(jbuf); + if (jbuflen < copybytes) + copybytes = jbuflen; + + ssize_t copiedbytes = copyBufToByteArray(env, jbuf, buf, copybytes); + if (dynBuf) { + free(buf); + } + if (copiedbytes == -1) { + setThrowTskCoreError(env, tsk_error_get()); + } + return (jint)copiedbytes; +} + /* * Read bytes from the given volume system @@ -2034,7 +2088,7 @@ JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_closeVsNat } /* - * Close the given volume system + * Close the given file system * @param env pointer to java environment this was called from * @param obj the java object this was called from * @param a_fs_info the pointer to the file system object @@ -2049,6 +2103,22 @@ JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_closeFsNat tsk_fs_close(fs_info); } +/* +* Close the given pool +* @param env pointer to java environment this was called from +* @param obj the java object this was called from +* @param a_pool_info the pointer to the pool object +*/ +JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_closePoolNat +(JNIEnv * env, jclass obj, jlong a_pool_info) { + TSK_POOL_INFO *pool_info = castPoolInfo(env, a_pool_info); + if (pool_info == 0) { + //exception already set + return; + } + tsk_pool_close(pool_info); +} + /* * Close the given file * @param env pointer to java environment this was called from diff --git a/bindings/java/jni/dataModel_SleuthkitJNI.h b/bindings/java/jni/dataModel_SleuthkitJNI.h index ef2523e30..14eb67533 100644 --- a/bindings/java/jni/dataModel_SleuthkitJNI.h +++ b/bindings/java/jni/dataModel_SleuthkitJNI.h @@ -297,19 +297,19 @@ JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_openPoolNat /* * Class: org_sleuthkit_datamodel_SleuthkitJNI - * Method: openFsNat + * Method: getImgInfoNat * Signature: (JJ)J */ -JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_openFsNat +JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getImgInfoNat (JNIEnv *, jclass, jlong, jlong); /* * Class: org_sleuthkit_datamodel_SleuthkitJNI - * Method: openFsPoolNat - * Signature: (JJJJ)J + * Method: openFsNat + * Signature: (JJ)J */ -JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_openFsPoolNat - (JNIEnv *, jclass, jlong, jlong, jlong, jlong); +JNIEXPORT jlong JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_openFsNat + (JNIEnv *, jclass, jlong, jlong); /* * Class: org_sleuthkit_datamodel_SleuthkitJNI @@ -335,6 +335,14 @@ JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_readImgNat JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_readVsNat (JNIEnv *, jclass, jlong, jbyteArray, jlong, jlong); +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: readPoolNat + * Signature: (J[BJJ)I + */ +JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_readPoolNat + (JNIEnv *, jclass, jlong, jbyteArray, jlong, jlong); + /* * Class: org_sleuthkit_datamodel_SleuthkitJNI * Method: readVolNat @@ -375,6 +383,14 @@ JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_saveFileMetaDat JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_closeImgNat (JNIEnv *, jclass, jlong); +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: closePoolNat + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_closePoolNat + (JNIEnv *, jclass, jlong); + /* * Class: org_sleuthkit_datamodel_SleuthkitJNI * Method: closeVsNat diff --git a/bindings/java/src/org/sleuthkit/datamodel/Pool.java b/bindings/java/src/org/sleuthkit/datamodel/Pool.java index cae02f115..754daa56c 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Pool.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Pool.java @@ -43,18 +43,17 @@ protected Pool(SleuthkitCase db, long obj_id, String name, long type, long imgOf super(db, obj_id, name); this.type = type; this.imgOffset = imgOffset; - System.out.println("### made a pool!\n"); + System.out.println("### made a pool! Image offset " + imgOffset + "\n"); } @Override public int read(byte[] readBuffer, long offset, long len) throws TskCoreException { - throw new TskCoreException("Not yet implemented"); - //synchronized (this) { - // if (volumeSystemHandle == 0) { - // getVolumeSystemHandle(); - // } - //} - //return SleuthkitJNI.readVs(volumeSystemHandle, readBuffer, offset, len); + synchronized (this) { + if (poolHandle == 0) { + getPoolHandle(); + } + } + return SleuthkitJNI.readPool(poolHandle, readBuffer, offset, len); } @Override @@ -97,7 +96,7 @@ long getPoolHandle() throws TskCoreException { Content dataSource = getDataSource(); if ((dataSource != null) && (dataSource instanceof Image)) { Image image = (Image) dataSource; - poolHandle = SleuthkitJNI.openPool(image.getImageHandle(), imgOffset, getType().getPoolType()); + poolHandle = SleuthkitJNI.openPool(image.getImageHandle(), imgOffset, getType().getPoolType(), getSleuthkitCase()); } else { throw new TskCoreException("Data Source of pool is not an image"); } diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index a0ba56532..d4a7c849e 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -7346,7 +7346,7 @@ private Pool getPoolByIdHelper(long id, Content parent) throws TskCoreException + "where obj_id = " + id); //NON-NLS if (rs.next()) { - Pool pool = new Pool(this, rs.getLong("obj_id"), "POOL!", rs.getLong("pool_type"), rs.getLong("addr")); + Pool pool = new Pool(this, rs.getLong("obj_id"), "POOL!", rs.getLong("pool_type"), rs.getLong("img_offset")); pool.setParent(parent); return pool; diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java index c76366de3..254f4f3e6 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java @@ -93,6 +93,10 @@ private static class CaseHandles { private final Map<Long, List<Long>> fileSystemToFileHandles = new HashMap<>(); + private final Map<Long, Long> poolHandleCache = new HashMap<>(); + + private final List<Long> poolImgCache = new ArrayList<>(); + private CaseHandles() { // Nothing to do here } @@ -169,6 +173,7 @@ private static void removeCaseHandlesCache(long caseDbPointer) { caseHandlesCache.get(caseDbPointer).imageHandleCache.clear(); caseHandlesCache.get(caseDbPointer).fileHandleCache.clear(); caseHandlesCache.get(caseDbPointer).fileSystemToFileHandles.clear(); + caseHandlesCache.get(caseDbPointer).poolHandleCache.clear(); caseHandlesCache.remove(caseDbPointer); } } @@ -270,6 +275,20 @@ private static void closeHandlesAndClearCache(long caseDbPointer) throws TskCore closeFsNat(fsHandle); } } + + /* + * Close any cached pools + */ + for (Long poolHandle : getCaseHandles(caseDbPointer).poolHandleCache.values()) { + closePoolNat(poolHandle); + } + + /* + * Close any open pool images + */ + for (Long imageHandle : getCaseHandles(caseDbPointer).poolImgCache) { + closeImgNat(imageHandle); + } /* * Close any cached image handles. @@ -816,12 +835,34 @@ public static long openVsPart(long vsHandle, long volId) throws TskCoreException * @throws TskCoreException exception thrown if critical error occurs within * TSK */ - public static long openPool(long imgHandle, long offset, long poolType) throws TskCoreException { + public static long openPool(long imgHandle, long offset, long poolType, SleuthkitCase skCase) throws TskCoreException { getTSKReadLock(); try { - // TODO CACHE - //returned long is ptr to pool Handle object in tsk - return openPoolNat(imgHandle, offset, poolType); + if(! imgHandleIsValid(imgHandle)) { + throw new TskCoreException("Image handle " + imgHandle + " is closed"); + } + + synchronized (HandleCache.cacheLock) { + long caseDbPointer; + if (skCase == null) { + caseDbPointer = HandleCache.getDefaultCaseDbPointer(); + } else { + caseDbPointer = skCase.getCaseHandle().caseDbPointer; + } + + if (HandleCache.getCaseHandles(caseDbPointer).poolHandleCache.containsKey(offset)) { + System.out.println("\n#### Using the pool cache!"); + return HandleCache.getCaseHandles(caseDbPointer).poolHandleCache.get(offset); + } else { + //returned long is ptr to pool Handle object in tsk + System.out.println("\n#### Trying to open pool at offset " + offset); + long poolHandle = openPoolNat(imgHandle, offset, poolType); + HandleCache.getCaseHandles(caseDbPointer).poolHandleCache.put(offset, poolHandle); + return poolHandle; + } + } + + } finally { releaseTSKReadLock(); } @@ -891,7 +932,9 @@ public static long openFsPool(long imgHandle, long fsOffset, long poolHandle, lo //return cached fsHandle = imgOffSetToFsHandle.get(combinedOffset); } else { - fsHandle = openFsPoolNat(imgHandle, fsOffset, poolHandle, poolBlock); + long poolImgHandle = getImgInfoNat(poolHandle, poolBlock); + HandleCache.getCaseHandles(caseDbPointer).poolImgCache.add(poolImgHandle); + fsHandle = openFsNat(poolImgHandle, fsOffset); //cache it imgOffSetToFsHandle.put(combinedOffset, fsHandle); } @@ -1022,6 +1065,24 @@ public static int readVs(long vsHandle, byte[] readBuffer, long offset, long len releaseTSKReadLock(); } } + + /** + * TODO + * @param poolHandle + * @param readBuffer + * @param offset + * @param len + * @return + * @throws TskCoreException + */ + public static int readPool(long poolHandle, byte[] readBuffer, long offset, long len) throws TskCoreException { + getTSKReadLock(); + try { + return readPoolNat(poolHandle, readBuffer, offset, len); + } finally { + releaseTSKReadLock(); + } + } /** * reads data from an volume @@ -1720,15 +1781,17 @@ public static long openFile(long fsHandle, long fileId, TSK_FS_ATTR_TYPE_ENUM at private static native long openPoolNat(long imgHandle, long offset, long poolType) throws TskCoreException; + private static native long getImgInfoNat(long poolHandle, long poolOffset) throws TskCoreException; + private static native long openFsNat(long imgHandle, long fsId) throws TskCoreException; - private static native long openFsPoolNat(long imgHandle, long fsId, long poolHandle, long poolOffset) throws TskCoreException; - private static native long openFileNat(long fsHandle, long fileId, int attrType, int attrId) throws TskCoreException; private static native int readImgNat(long imgHandle, byte[] readBuffer, long offset, long len) throws TskCoreException; private static native int readVsNat(long vsHandle, byte[] readBuffer, long offset, long len) throws TskCoreException; + + private static native int readPoolNat(long poolHandle, byte[] readBuffer, long offset, long len) throws TskCoreException; private static native int readVolNat(long volHandle, byte[] readBuffer, long offset, long len) throws TskCoreException; @@ -1739,6 +1802,8 @@ public static long openFile(long fsHandle, long fileId, TSK_FS_ATTR_TYPE_ENUM at private static native int saveFileMetaDataTextNat(long fileHandle, String fileName) throws TskCoreException; private static native void closeImgNat(long imgHandle); + + private static native void closePoolNat(long poolHandle); private static native void closeVsNat(long vsHandle); diff --git a/tsk/auto/db_sqlite.cpp b/tsk/auto/db_sqlite.cpp index 5b67f0516..921d27055 100755 --- a/tsk/auto/db_sqlite.cpp +++ b/tsk/auto/db_sqlite.cpp @@ -305,7 +305,7 @@ TskDbSqlite::initialize() "Error creating tsk_vol_info table: %s\n") || attempt_exec - ("CREATE TABLE tsk_pool_info (obj_id INTEGER PRIMARY KEY, addr INTEGER NOT NULL, pool_type INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id));", + ("CREATE TABLE tsk_pool_info (obj_id INTEGER PRIMARY KEY, img_offset INTEGER NOT NULL, pool_type INTEGER NOT NULL, FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id));", "Error creating tsk_pool_info table: %s\n") || attempt_exec @@ -764,7 +764,7 @@ TskDbSqlite::addPoolInfo(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64 return 1; snprintf(stmt, 1024, - "INSERT INTO tsk_pool_info (obj_id, addr, pool_type) VALUES (%" PRId64 ",%" PRIuDADDR ",%d)", poolObjId, 0, pool_info->ctype); // TODO - offset + "INSERT INTO tsk_pool_info (obj_id, img_offset, pool_type) VALUES (%" PRId64 ",%" PRIuDADDR ",%d)", poolObjId, pool_info->img_offset, pool_info->ctype); // TODO - offset int retVal = attempt_exec(stmt, @@ -777,7 +777,7 @@ TskDbSqlite::addPoolInfo(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64 return 1; snprintf(stmt, 1024, - "INSERT INTO tsk_vs_info (obj_id, vs_type, img_offset, block_size) VALUES (%" PRId64 ", %d,%" PRIuDADDR ",%d)", objId, 0, 0, pool_info->block_size); // TODO - offset + "INSERT INTO tsk_vs_info (obj_id, vs_type, img_offset, block_size) VALUES (%" PRId64 ", %d,%" PRIuDADDR ",%d)", objId, 0, pool_info->img_offset, pool_info->block_size); // TODO - offset return attempt_exec(stmt, "Error adding data to tsk_vs_info table: %s\n"); diff --git a/tsk/pool/apfs_pool_compat.cpp b/tsk/pool/apfs_pool_compat.cpp index 4d514c467..9102776b7 100755 --- a/tsk/pool/apfs_pool_compat.cpp +++ b/tsk/pool/apfs_pool_compat.cpp @@ -270,14 +270,14 @@ apfs_img_close(TSK_IMG_INFO * img_info) IMG_POOL_INFO *pool_img_info = (IMG_POOL_INFO *)img_info; // Close the pool and original image - if (pool_img_info->pool_info != NULL) { - const auto pool = static_cast<APFSPoolCompat*>(pool_img_info->pool_info->impl); - TSK_IMG_INFO *origInfo = pool->getTSKImgInfo(0); - tsk_img_close(origInfo); + //if (pool_img_info->pool_info != NULL) { + // const auto pool = static_cast<APFSPoolCompat*>(pool_img_info->pool_info->impl); + // TSK_IMG_INFO *origInfo = pool->getTSKImgInfo(0); + // tsk_img_close(origInfo); - pool_img_info->pool_info->close(pool_img_info->pool_info); - pool_img_info->pool_info = NULL; - } + //pool_img_info->pool_info->close(pool_img_info->pool_info); + //pool_img_info->pool_info = NULL; + //} // Close the pool image tsk_deinit_lock(&(img_info->cache_lock)); @@ -296,11 +296,13 @@ apfs_img_imgstat(TSK_IMG_INFO * img_info, FILE *file) static ssize_t apfs_img_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len) { + printf("apfs_img_read: reading offset 0x%llx\n", offset); + fflush(stdout); IMG_POOL_INFO *pool_img_info = (IMG_POOL_INFO *)img_info; const auto pool = static_cast<APFSPoolCompat*>(pool_img_info->pool_info->impl); TSK_IMG_INFO *origInfo = pool->getTSKImgInfo(0); - return origInfo->read(origInfo, offset, buf, len); + return origInfo->read(origInfo, offset + pool->first_img_offset(), buf, len); } TSK_IMG_INFO * APFSPoolCompat::getImageInfo(const TSK_POOL_INFO *pool_info, TSK_DADDR_T pvol_block) noexcept try { diff --git a/tsk/pool/pool_compat.hpp b/tsk/pool/pool_compat.hpp index ec2cbd260..4cc7f5fd4 100644 --- a/tsk/pool/pool_compat.hpp +++ b/tsk/pool/pool_compat.hpp @@ -29,6 +29,7 @@ class TSKPoolCompat : public T { _info.ctype = type; _info.block_size = this->block_size(); _info.num_blocks = this->num_blocks(); + _info.img_offset = this->first_img_offset(); _info.num_vols = this->num_vols(); _info.vol_list = nullptr; _info.close = [](const TSK_POOL_INFO *pool) { diff --git a/tsk/pool/tsk_pool.h b/tsk/pool/tsk_pool.h index 7902617df..ee2b8c2a7 100644 --- a/tsk/pool/tsk_pool.h +++ b/tsk/pool/tsk_pool.h @@ -48,6 +48,7 @@ typedef struct _TSK_POOL_INFO { uint32_t block_size; ///< Block size uint64_t num_blocks; ///< Number of blocks int num_vols; ///< Number of volumes + uint64_t img_offset; ///< The image offset of the pool TSK_POOL_VOLUME_INFO *vol_list; ///< Linked list of volume info structs // Callbacks diff --git a/tsk/pool/tsk_pool.hpp b/tsk/pool/tsk_pool.hpp index 4195b2ae3..a8d8d3104 100644 --- a/tsk/pool/tsk_pool.hpp +++ b/tsk/pool/tsk_pool.hpp @@ -34,6 +34,12 @@ class TSKPool { inline uint32_t block_size() const noexcept { return _block_size; } inline uint32_t dev_block_size() const noexcept { return _dev_block_size; } inline uint64_t num_blocks() const noexcept { return _num_blocks; } + inline uint64_t first_img_offset() const noexcept { + if (_members.size() >= 1) { + return _members[0].second; + } + return 0; + } inline int num_vols() const noexcept { return _num_vols; } virtual ssize_t read(uint64_t address, char *buf, size_t buf_size) const -- GitLab