19 package org.sleuthkit.datamodel;
21 import java.sql.PreparedStatement;
22 import java.sql.SQLException;
23 import java.sql.Statement;
24 import org.apache.commons.lang3.StringUtils;
25 import java.util.List;
26 import java.util.Arrays;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.LinkedList;
31 import java.util.Objects;
32 import java.util.Queue;
33 import java.util.logging.Level;
34 import java.util.logging.Logger;
46 class TskCaseDbBridge {
48 private static final Logger logger = Logger.getLogger(TskCaseDbBridge.class.getName());
50 private final SleuthkitCase caseDb;
51 private CaseDbTransaction trans = null;
52 private final AddDataSourceCallbacks addDataSourceCallbacks;
54 private final Map<Long, Long> fsIdToRootDir =
new HashMap<>();
55 private final Map<Long, TskData.TSK_FS_TYPE_ENUM> fsIdToFsType =
new HashMap<>();
56 private final Map<ParentCacheKey, Long> parentDirCache =
new HashMap<>();
58 private static final long BATCH_FILE_THRESHOLD = 500;
59 private final Queue<FileInfo> batchedFiles =
new LinkedList<>();
60 private final Queue<LayoutRangeInfo> batchedLayoutRanges =
new LinkedList<>();
61 private final List<Long> layoutFileIds =
new ArrayList<>();
63 TskCaseDbBridge(SleuthkitCase caseDb, AddDataSourceCallbacks addDataSourceCallbacks) {
65 this.addDataSourceCallbacks = addDataSourceCallbacks;
74 private void beginTransaction() throws TskCoreException {
75 trans = caseDb.beginTransaction();
83 private void commitTransaction() throws TskCoreException {
91 private void revertTransaction() {
97 }
catch (TskCoreException ex) {
98 logger.log(Level.SEVERE,
"Error rolling back transaction", ex);
106 addBatchedFilesToDb();
107 addBatchedLayoutRangesToDb();
108 processLayoutFiles();
129 long addImageInfo(
int type,
long ssize, String timezone,
130 long size, String md5, String sha1, String sha256, String deviceId,
131 String collectionDetails, String[] paths) {
134 long objId = addImageToDb(TskData.TSK_IMG_TYPE_ENUM.valueOf(type), ssize, size,
135 timezone, md5, sha1, sha256, deviceId, collectionDetails, trans);
136 for (
int i = 0;i < paths.length;i++) {
137 addImageNameToDb(objId, paths[i], i, trans);
141 }
catch (TskCoreException ex) {
142 logger.log(Level.SEVERE,
"Error adding image to the database", ex);
154 void addAcquisitionDetails(
long imgId, String details) {
157 caseDb.setAcquisitionDetails(imgId, details, trans);
159 }
catch (TskCoreException ex) {
160 logger.log(Level.SEVERE,
"Error adding image details \"" + details +
"\" to image with ID " + imgId, ex);
176 long addVsInfo(
long parentObjId,
int vsType,
long imgOffset,
long blockSize) {
179 VolumeSystem vs = caseDb.addVolumeSystem(parentObjId, TskData.TSK_VS_TYPE_ENUM.valueOf(vsType), imgOffset, blockSize, trans);
182 }
catch (TskCoreException ex) {
183 logger.log(Level.SEVERE,
"Error adding volume system to the database - parent obj ID: " + parentObjId
184 +
", image offset: " + imgOffset, ex);
203 long addVolume(
long parentObjId,
long addr,
long start,
long length, String desc,
207 Volume vol = caseDb.addVolume(parentObjId, addr, start, length, desc, flags, trans);
210 }
catch (TskCoreException ex) {
211 logger.log(Level.SEVERE,
"Error adding volume to the database - parent object ID: " + parentObjId
212 +
", addr: " + addr, ex);
227 long addPool(
long parentObjId,
int poolType) {
230 Pool pool = caseDb.addPool(parentObjId, TskData.TSK_POOL_TYPE_ENUM.valueOf(poolType), trans);
233 }
catch (TskCoreException ex) {
234 logger.log(Level.SEVERE,
"Error adding pool to the database - parent object ID: " + parentObjId, ex);
255 long addFileSystem(
long parentObjId,
long imgOffset,
int fsType,
long blockSize,
long blockCount,
256 long rootInum,
long firstInum,
long lastInum) {
259 FileSystem fs = caseDb.addFileSystem(parentObjId, imgOffset, TskData.TSK_FS_TYPE_ENUM.valueOf(fsType), blockSize, blockCount,
260 rootInum, firstInum, lastInum, null, trans);
262 fsIdToFsType.put(fs.getId(), TskData.TSK_FS_TYPE_ENUM.valueOf(fsType));
264 }
catch (TskCoreException ex) {
265 logger.log(Level.SEVERE,
"Error adding file system to the database - parent object ID: " + parentObjId
266 +
", offset: " + imgOffset, ex);
312 long addFile(
long parentObjId,
313 long fsObjId,
long dataSourceObjId,
315 int attrType,
int attrId, String name,
316 long metaAddr,
long metaSeq,
317 int dirType,
int metaType,
int dirFlags,
int metaFlags,
319 long crtime,
long ctime,
long atime,
long mtime,
320 int meta_mode,
int gid,
int uid,
321 String escaped_path, String extension,
322 long seq,
long parMetaAddr,
long parSeq) {
325 batchedFiles.add(
new FileInfo(parentObjId,
326 fsObjId, dataSourceObjId,
328 attrType, attrId, name,
330 dirType, metaType, dirFlags, metaFlags,
332 crtime, ctime, atime, mtime,
334 escaped_path, extension,
335 seq, parMetaAddr, parSeq));
339 if ((fsObjId == parentObjId)
340 || (batchedFiles.size() > BATCH_FILE_THRESHOLD)) {
341 return addBatchedFilesToDb();
351 private long addBatchedFilesToDb() {
352 List<Long> newObjIds =
new ArrayList<>();
356 while ((fileInfo = batchedFiles.poll()) != null) {
357 long computedParentObjId = fileInfo.parentObjId;
360 if (fileInfo.parentObjId == 0) {
361 computedParentObjId = getParentObjId(fileInfo);
364 long objId = addFileToDb(computedParentObjId,
365 fileInfo.fsObjId, fileInfo.dataSourceObjId,
367 fileInfo.attrType, fileInfo.attrId, fileInfo.name,
368 fileInfo.metaAddr, fileInfo.metaSeq,
369 fileInfo.dirType, fileInfo.metaType, fileInfo.dirFlags, fileInfo.metaFlags,
371 fileInfo.crtime, fileInfo.ctime, fileInfo.atime, fileInfo.mtime,
372 fileInfo.meta_mode, fileInfo.gid, fileInfo.uid,
373 null, TskData.FileKnown.UNKNOWN,
374 fileInfo.escaped_path, fileInfo.extension,
376 if (fileInfo.fsObjId != fileInfo.parentObjId) {
378 newObjIds.add(objId);
382 if (fileInfo.parentObjId == fileInfo.fsObjId) {
383 fsIdToRootDir.put(fileInfo.fsObjId, objId);
387 if ((fileInfo.metaType == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()
388 || (fileInfo.metaType == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT_DIR.getValue()))
389 && (fileInfo.name != null)
390 && ! fileInfo.name.equals(
".")
391 && ! fileInfo.name.equals(
"..")) {
392 String dirName = fileInfo.escaped_path + fileInfo.name;
393 ParentCacheKey key =
new ParentCacheKey(fileInfo.fsObjId, fileInfo.metaAddr, fileInfo.seq, dirName);
394 parentDirCache.put(key, objId);
396 }
catch (TskCoreException ex) {
397 logger.log(Level.SEVERE,
"Error adding file to the database - parent object ID: " + computedParentObjId
398 +
", file system object ID: " + fileInfo.fsObjId +
", name: " + fileInfo.name, ex);
403 addDataSourceCallbacks.onFilesAdded(newObjIds);
404 }
catch (Exception ex) {
406 logger.log(Level.SEVERE,
"Unexpected error from files added callback", ex);
408 }
catch (TskCoreException ex) {
409 logger.log(Level.SEVERE,
"Error adding batched files to database", ex);
425 private long getParentObjId(FileInfo fileInfo)
throws TskCoreException {
427 String parentPath = fileInfo.escaped_path;
428 if(parentPath.endsWith(
"/") && ! parentPath.equals(
"/")) {
429 parentPath = parentPath.substring(0, parentPath.lastIndexOf(
'/'));
433 ParentCacheKey key =
new ParentCacheKey(fileInfo.fsObjId, fileInfo.parMetaAddr, fileInfo.parSeq, parentPath);
434 if (parentDirCache.containsKey(key)) {
435 return parentDirCache.get(key);
439 throw new TskCoreException(
"Parent not found in cache (fsObjId: " +fileInfo.fsObjId +
", parMetaAddr: " + fileInfo.parMetaAddr
440 +
", parSeq: " + fileInfo.parSeq +
", parentPath: " + parentPath +
")");
457 long addLayoutFile(
long parentObjId,
458 long fsObjId,
long dataSourceObjId,
460 String name,
long size) {
464 Long fsObjIdForDb = fsObjId;
470 long objId = addFileToDb(parentObjId,
471 fsObjIdForDb, dataSourceObjId,
475 TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue(),
476 TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue(),
477 TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue(),
478 TskData.TSK_FS_META_FLAG_ENUM.UNALLOC.getValue(),
480 null, null, null, null,
482 null, TskData.FileKnown.UNKNOWN,
488 layoutFileIds.add(objId);
491 }
catch (TskCoreException ex) {
492 logger.log(Level.SEVERE,
"Error adding layout file to the database - parent object ID: " + parentObjId
493 +
", file system object ID: " + fsObjId +
", name: " + name, ex);
510 long addLayoutFileRange(
long objId,
long byteStart,
long byteLen,
long seq) {
511 batchedLayoutRanges.add(
new LayoutRangeInfo(objId, byteStart, byteLen, seq));
513 if (batchedLayoutRanges.size() > BATCH_FILE_THRESHOLD) {
514 return addBatchedLayoutRangesToDb();
524 private long addBatchedLayoutRangesToDb() {
527 LayoutRangeInfo range;
528 while ((range = batchedLayoutRanges.poll()) != null) {
530 addLayoutFileRangeToDb(range.objId, range.byteStart, range.byteLen, range.seq, trans);
531 }
catch (TskCoreException ex) {
532 logger.log(Level.SEVERE,
"Error adding layout file range to the database - layout file ID: " + range.objId
533 +
", byte start: " + range.byteStart +
", length: " + range.byteLen +
", seq: " + range.seq, ex);
538 }
catch (TskCoreException ex) {
539 logger.log(Level.SEVERE,
"Error adding batched files to database", ex);
550 void processLayoutFiles() {
551 addDataSourceCallbacks.onFilesAdded(layoutFileIds);
552 layoutFileIds.clear();
564 long addUnallocFsBlockFilesParent(
long fsObjId, String name) {
566 if (! fsIdToRootDir.containsKey(fsObjId)) {
567 logger.log(Level.SEVERE,
"Error - root directory for file system ID {0} not found", fsObjId);
571 VirtualDirectory dir = caseDb.addVirtualDirectory(fsIdToRootDir.get(fsObjId), name, trans);
573 addDataSourceCallbacks.onFilesAdded(Arrays.asList(dir.getId()));
575 }
catch (TskCoreException ex) {
576 logger.log(Level.SEVERE,
"Error creating virtual directory " + name +
" under file system ID " + fsObjId, ex);
585 private class ParentCacheKey {
600 ParentCacheKey(
long fsObjId,
long metaAddr,
long seqNum, String path) {
601 this.fsObjId = fsObjId;
602 this.metaAddr = metaAddr;
603 if (fsIdToFsType.containsKey(fsObjId)
604 && (fsIdToFsType.get(fsObjId).equals(TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_NTFS)
605 || fsIdToFsType.get(fsObjId).equals(TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_NTFS_DETECT))) {
606 this.seqNum = seqNum;
614 public boolean equals(Object obj) {
615 if (! (obj instanceof ParentCacheKey)) {
619 ParentCacheKey otherKey = (ParentCacheKey) obj;
620 if (this.fsObjId != otherKey.fsObjId
621 ||
this.metaAddr != otherKey.metaAddr
622 ||
this.seqNum != otherKey.seqNum) {
626 return StringUtils.equals(this.path, otherKey.path);
630 public int hashCode() {
632 hash = 31 * hash + (int) (this.fsObjId ^ (this.fsObjId >>> 32));
633 hash = 31 * hash + (int) (this.metaAddr ^ (this.metaAddr >>> 32));
634 hash = 31 * hash + (int) (this.seqNum ^ (this.seqNum >>> 32));
635 hash = 31 * hash + Objects.hashCode(this.path);
644 private class LayoutRangeInfo {
650 LayoutRangeInfo(
long objId,
long byteStart,
long byteLen,
long seq) {
652 this.byteStart = byteStart;
653 this.byteLen = byteLen;
662 private class FileInfo {
665 long dataSourceObjId;
690 FileInfo(
long parentObjId,
691 long fsObjId,
long dataSourceObjId,
693 int attrType,
int attrId, String name,
694 long metaAddr,
long metaSeq,
695 int dirType,
int metaType,
int dirFlags,
int metaFlags,
697 long crtime,
long ctime,
long atime,
long mtime,
698 int meta_mode,
int gid,
int uid,
699 String escaped_path, String extension,
700 long seq,
long parMetaAddr,
long parSeq) {
702 this.parentObjId = parentObjId;
703 this.fsObjId = fsObjId;
704 this.dataSourceObjId = dataSourceObjId;
705 this.fsType = fsType;
706 this.attrType = attrType;
707 this.attrId = attrId;
709 this.metaAddr = metaAddr;
710 this.metaSeq = metaSeq;
711 this.dirType = dirType;
712 this.metaType = metaType;
713 this.dirFlags = dirFlags;
714 this.metaFlags = metaFlags;
716 this.crtime = crtime;
720 this.meta_mode = meta_mode;
723 this.escaped_path = escaped_path;
724 this.extension = extension;
726 this.parMetaAddr = parMetaAddr;
727 this.parSeq = parSeq;
772 private long addFileToDb(
long parentObjId,
773 Long fsObjId,
long dataSourceObjId,
775 Integer attrType, Integer attrId, String name,
776 Long metaAddr, Long metaSeq,
777 int dirType,
int metaType,
int dirFlags,
int metaFlags,
779 Long crtime, Long ctime, Long atime, Long mtime,
780 Integer meta_mode, Integer gid, Integer uid,
781 String md5, TskData.FileKnown known,
782 String escaped_path, String extension,
783 boolean hasLayout, CaseDbTransaction transaction)
throws TskCoreException {
786 SleuthkitCase.CaseDbConnection connection = transaction.getConnection();
790 long objectId = caseDb.addObject(parentObjId, TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection);
792 String fileInsert =
"INSERT INTO tsk_files (fs_obj_id, obj_id, data_source_obj_id, type, attr_type, attr_id, name, meta_addr, meta_seq, dir_type, meta_type, dir_flags, meta_flags, size, crtime, ctime, atime, mtime, mode, gid, uid, md5, known, parent_path, extension, has_layout)"
793 +
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
794 PreparedStatement preparedStatement = connection.getPreparedStatement(fileInsert, Statement.NO_GENERATED_KEYS);
795 preparedStatement.clearParameters();
797 if (fsObjId != null) {
798 preparedStatement.setLong(1, fsObjId);
800 preparedStatement.setNull(1, java.sql.Types.BIGINT);
802 preparedStatement.setLong(2, objectId);
803 preparedStatement.setLong(3, dataSourceObjId);
804 preparedStatement.setShort(4, (
short) fsType);
805 if (attrType != null) {
806 preparedStatement.setShort(5, attrType.shortValue());
808 preparedStatement.setNull(5, java.sql.Types.SMALLINT);
810 if (attrId != null) {
811 preparedStatement.setInt(6, attrId);
813 preparedStatement.setNull(6, java.sql.Types.INTEGER);
815 preparedStatement.setString(7, name);
816 if (metaAddr != null) {
817 preparedStatement.setLong(8, metaAddr);
819 preparedStatement.setNull(8, java.sql.Types.BIGINT);
821 if (metaSeq != null) {
822 preparedStatement.setInt(9, metaSeq.intValue());
824 preparedStatement.setNull(9, java.sql.Types.INTEGER);
826 preparedStatement.setShort(10, (
short) dirType);
827 preparedStatement.setShort(11, (
short) metaType);
828 preparedStatement.setShort(12, (
short) dirFlags);
829 preparedStatement.setShort(13, (
short) metaFlags);
830 preparedStatement.setLong(14, size < 0 ? 0 : size);
831 if (crtime != null) {
832 preparedStatement.setLong(15, crtime);
834 preparedStatement.setNull(15, java.sql.Types.BIGINT);
837 preparedStatement.setLong(16, ctime);
839 preparedStatement.setNull(16, java.sql.Types.BIGINT);
842 preparedStatement.setLong(17, atime);
844 preparedStatement.setNull(17, java.sql.Types.BIGINT);
847 preparedStatement.setLong(18, mtime);
849 preparedStatement.setNull(18, java.sql.Types.BIGINT);
851 if (meta_mode != null) {
852 preparedStatement.setLong(19, meta_mode);
854 preparedStatement.setNull(19, java.sql.Types.BIGINT);
857 preparedStatement.setLong(20, gid);
859 preparedStatement.setNull(20, java.sql.Types.BIGINT);
862 preparedStatement.setLong(21, uid);
864 preparedStatement.setNull(21, java.sql.Types.BIGINT);
866 preparedStatement.setString(22, md5);
867 preparedStatement.setInt(23, known.getFileKnownValue());
868 preparedStatement.setString(24, escaped_path);
869 preparedStatement.setString(25, extension);
871 preparedStatement.setInt(26, 1);
873 preparedStatement.setNull(26, java.sql.Types.INTEGER);
875 connection.executeUpdate(preparedStatement);
879 && TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType() != fsType
880 && (!name.equals(
".")) && (!name.equals(
".."))) {
881 TimelineManager timelineManager = caseDb.getTimelineManager();
882 DerivedFile derivedFile =
new DerivedFile(caseDb, objectId, dataSourceObjId, name,
883 TskData.TSK_FS_NAME_TYPE_ENUM.valueOf((
short) dirType),
884 TskData.TSK_FS_META_TYPE_ENUM.valueOf((
short) metaType),
885 TskData.TSK_FS_NAME_FLAG_ENUM.valueOf(dirFlags),
887 size, ctime, crtime, atime, mtime, null, null, escaped_path, null, parentObjId, null, null, extension);
889 timelineManager.addEventsForNewFileQuiet(derivedFile, connection);
893 }
catch (SQLException ex) {
894 throw new TskCoreException(
"Failed to add file system file", ex);
916 private long addImageToDb(TskData.TSK_IMG_TYPE_ENUM type,
long sectorSize,
long size,
917 String timezone, String md5, String sha1, String sha256,
918 String deviceId, String collectionDetails,
919 CaseDbTransaction transaction)
throws TskCoreException {
922 SleuthkitCase.CaseDbConnection connection = transaction.getConnection();
923 long newObjId = caseDb.addObject(0, TskData.ObjectType.IMG.getObjectType(), connection);
927 String imageInfoSql =
"INSERT INTO tsk_image_info (obj_id, type, ssize, tzone, size, md5, sha1, sha256, display_name)"
928 +
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
929 PreparedStatement preparedStatement = connection.getPreparedStatement(imageInfoSql, Statement.NO_GENERATED_KEYS);
930 preparedStatement.clearParameters();
931 preparedStatement.setLong(1, newObjId);
932 preparedStatement.setShort(2, (
short) type.getValue());
933 preparedStatement.setLong(3, sectorSize);
934 preparedStatement.setString(4, timezone);
936 long savedSize = size < 0 ? 0 : size;
937 preparedStatement.setLong(5, savedSize);
938 preparedStatement.setString(6, md5);
939 preparedStatement.setString(7, sha1);
940 preparedStatement.setString(8, sha256);
941 preparedStatement.setString(9, null);
942 connection.executeUpdate(preparedStatement);
945 String dataSourceInfoSql =
"INSERT INTO data_source_info (obj_id, device_id, time_zone, acquisition_details) VALUES (?, ?, ?, ?)";
946 preparedStatement = connection.getPreparedStatement(dataSourceInfoSql, Statement.NO_GENERATED_KEYS);
947 preparedStatement.clearParameters();
948 preparedStatement.setLong(1, newObjId);
949 preparedStatement.setString(2, deviceId);
950 preparedStatement.setString(3, timezone);
951 preparedStatement.setString(4, collectionDetails);
952 connection.executeUpdate(preparedStatement);
955 }
catch (SQLException ex) {
956 throw new TskCoreException(String.format(
"Error adding image to database"), ex);
970 private void addImageNameToDb(
long objId, String name,
long sequence,
971 CaseDbTransaction transaction)
throws TskCoreException {
973 SleuthkitCase.CaseDbConnection connection = transaction.getConnection();
975 String imageNameSql =
"INSERT INTO tsk_image_names (obj_id, name, sequence) VALUES (?, ?, ?)";
976 PreparedStatement preparedStatement = connection.getPreparedStatement(imageNameSql, Statement.NO_GENERATED_KEYS);
977 preparedStatement.clearParameters();
978 preparedStatement.setLong(1, objId);
979 preparedStatement.setString(2, name);
980 preparedStatement.setLong(3, sequence);
981 connection.executeUpdate(preparedStatement);
982 }
catch (SQLException ex) {
983 throw new TskCoreException(String.format(
"Error adding image name %s to image with object ID %d", name, objId), ex);
998 void addLayoutFileRangeToDb(
long objId,
long byteStart,
long byteLen,
999 long seq, CaseDbTransaction transaction)
throws TskCoreException {
1001 SleuthkitCase.CaseDbConnection connection = transaction.getConnection();
1003 String insertRangeSql =
"INSERT INTO tsk_file_layout (obj_id, byte_start, byte_len, sequence) "
1004 +
"VALUES (?, ?, ?, ?)";
1005 PreparedStatement preparedStatement = connection.getPreparedStatement(insertRangeSql, Statement.NO_GENERATED_KEYS);
1006 preparedStatement.clearParameters();
1007 preparedStatement.setLong(1, objId);
1008 preparedStatement.setLong(2, byteStart);
1009 preparedStatement.setLong(3, byteLen);
1010 preparedStatement.setLong(4, seq);
1011 connection.executeUpdate(preparedStatement);
1012 }
catch (SQLException ex) {
1013 throw new TskCoreException(
"Error adding layout range to file with obj ID " + objId, ex);