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 if (computedParentObjId > 0) {
399 logger.log(Level.SEVERE,
"Error adding file to the database - parent object ID: " + computedParentObjId
400 +
", file system object ID: " + fileInfo.fsObjId +
", name: " + fileInfo.name, ex);
403 logger.log(Level.SEVERE,
"Error adding file to the database", ex);
409 addDataSourceCallbacks.onFilesAdded(newObjIds);
410 }
catch (Exception ex) {
412 logger.log(Level.SEVERE,
"Unexpected error from files added callback", ex);
414 }
catch (TskCoreException ex) {
415 logger.log(Level.SEVERE,
"Error adding batched files to database", ex);
431 private long getParentObjId(FileInfo fileInfo)
throws TskCoreException {
433 String parentPath = fileInfo.escaped_path;
434 if(parentPath.endsWith(
"/") && ! parentPath.equals(
"/")) {
435 parentPath = parentPath.substring(0, parentPath.lastIndexOf(
'/'));
439 ParentCacheKey key =
new ParentCacheKey(fileInfo.fsObjId, fileInfo.parMetaAddr, fileInfo.parSeq, parentPath);
440 if (parentDirCache.containsKey(key)) {
441 return parentDirCache.get(key);
445 throw new TskCoreException(
"Could not find parent (fsObjId: " +fileInfo.fsObjId +
", parMetaAddr: " + fileInfo.parMetaAddr
446 +
", parSeq: " + fileInfo.parSeq +
", parentPath: " + parentPath +
")");
463 long addLayoutFile(
long parentObjId,
464 long fsObjId,
long dataSourceObjId,
466 String name,
long size) {
470 Long fsObjIdForDb = fsObjId;
476 long objId = addFileToDb(parentObjId,
477 fsObjIdForDb, dataSourceObjId,
481 TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue(),
482 TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue(),
483 TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue(),
484 TskData.TSK_FS_META_FLAG_ENUM.UNALLOC.getValue(),
486 null, null, null, null,
488 null, TskData.FileKnown.UNKNOWN,
494 layoutFileIds.add(objId);
497 }
catch (TskCoreException ex) {
498 logger.log(Level.SEVERE,
"Error adding layout file to the database - parent object ID: " + parentObjId
499 +
", file system object ID: " + fsObjId +
", name: " + name, ex);
516 long addLayoutFileRange(
long objId,
long byteStart,
long byteLen,
long seq) {
517 batchedLayoutRanges.add(
new LayoutRangeInfo(objId, byteStart, byteLen, seq));
519 if (batchedLayoutRanges.size() > BATCH_FILE_THRESHOLD) {
520 return addBatchedLayoutRangesToDb();
530 private long addBatchedLayoutRangesToDb() {
533 LayoutRangeInfo range;
534 while ((range = batchedLayoutRanges.poll()) != null) {
536 addLayoutFileRangeToDb(range.objId, range.byteStart, range.byteLen, range.seq, trans);
537 }
catch (TskCoreException ex) {
538 logger.log(Level.SEVERE,
"Error adding layout file range to the database - layout file ID: " + range.objId
539 +
", byte start: " + range.byteStart +
", length: " + range.byteLen +
", seq: " + range.seq, ex);
544 }
catch (TskCoreException ex) {
545 logger.log(Level.SEVERE,
"Error adding batched files to database", ex);
556 void processLayoutFiles() {
557 addDataSourceCallbacks.onFilesAdded(layoutFileIds);
558 layoutFileIds.clear();
570 long addUnallocFsBlockFilesParent(
long fsObjId, String name) {
572 if (! fsIdToRootDir.containsKey(fsObjId)) {
573 logger.log(Level.SEVERE,
"Error - root directory for file system ID {0} not found", fsObjId);
577 VirtualDirectory dir = caseDb.addVirtualDirectory(fsIdToRootDir.get(fsObjId), name, trans);
579 addDataSourceCallbacks.onFilesAdded(Arrays.asList(dir.getId()));
581 }
catch (TskCoreException ex) {
582 logger.log(Level.SEVERE,
"Error creating virtual directory " + name +
" under file system ID " + fsObjId, ex);
591 private class ParentCacheKey {
606 ParentCacheKey(
long fsObjId,
long metaAddr,
long seqNum, String path) {
607 this.fsObjId = fsObjId;
608 this.metaAddr = metaAddr;
609 if (fsIdToFsType.containsKey(fsObjId)
610 && (fsIdToFsType.get(fsObjId).equals(TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_NTFS)
611 || fsIdToFsType.get(fsObjId).equals(TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_NTFS_DETECT))) {
612 this.seqNum = seqNum;
620 public boolean equals(Object obj) {
621 if (! (obj instanceof ParentCacheKey)) {
625 ParentCacheKey otherKey = (ParentCacheKey) obj;
626 if (this.fsObjId != otherKey.fsObjId
627 ||
this.metaAddr != otherKey.metaAddr
628 ||
this.seqNum != otherKey.seqNum) {
632 return StringUtils.equals(this.path, otherKey.path);
636 public int hashCode() {
638 hash = 31 * hash + (int) (this.fsObjId ^ (this.fsObjId >>> 32));
639 hash = 31 * hash + (int) (this.metaAddr ^ (this.metaAddr >>> 32));
640 hash = 31 * hash + (int) (this.seqNum ^ (this.seqNum >>> 32));
641 hash = 31 * hash + Objects.hashCode(this.path);
650 private class LayoutRangeInfo {
656 LayoutRangeInfo(
long objId,
long byteStart,
long byteLen,
long seq) {
658 this.byteStart = byteStart;
659 this.byteLen = byteLen;
668 private class FileInfo {
671 long dataSourceObjId;
696 FileInfo(
long parentObjId,
697 long fsObjId,
long dataSourceObjId,
699 int attrType,
int attrId, String name,
700 long metaAddr,
long metaSeq,
701 int dirType,
int metaType,
int dirFlags,
int metaFlags,
703 long crtime,
long ctime,
long atime,
long mtime,
704 int meta_mode,
int gid,
int uid,
705 String escaped_path, String extension,
706 long seq,
long parMetaAddr,
long parSeq) {
708 this.parentObjId = parentObjId;
709 this.fsObjId = fsObjId;
710 this.dataSourceObjId = dataSourceObjId;
711 this.fsType = fsType;
712 this.attrType = attrType;
713 this.attrId = attrId;
715 this.metaAddr = metaAddr;
716 this.metaSeq = metaSeq;
717 this.dirType = dirType;
718 this.metaType = metaType;
719 this.dirFlags = dirFlags;
720 this.metaFlags = metaFlags;
722 this.crtime = crtime;
726 this.meta_mode = meta_mode;
729 this.escaped_path = escaped_path;
730 this.extension = extension;
732 this.parMetaAddr = parMetaAddr;
733 this.parSeq = parSeq;
778 private long addFileToDb(
long parentObjId,
779 Long fsObjId,
long dataSourceObjId,
781 Integer attrType, Integer attrId, String name,
782 Long metaAddr, Long metaSeq,
783 int dirType,
int metaType,
int dirFlags,
int metaFlags,
785 Long crtime, Long ctime, Long atime, Long mtime,
786 Integer meta_mode, Integer gid, Integer uid,
787 String md5, TskData.FileKnown known,
788 String escaped_path, String extension,
789 boolean hasLayout, CaseDbTransaction transaction)
throws TskCoreException {
792 SleuthkitCase.CaseDbConnection connection = transaction.getConnection();
796 long objectId = caseDb.addObject(parentObjId, TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection);
798 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)"
799 +
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
800 PreparedStatement preparedStatement = connection.getPreparedStatement(fileInsert, Statement.NO_GENERATED_KEYS);
801 preparedStatement.clearParameters();
803 if (fsObjId != null) {
804 preparedStatement.setLong(1, fsObjId);
806 preparedStatement.setNull(1, java.sql.Types.BIGINT);
808 preparedStatement.setLong(2, objectId);
809 preparedStatement.setLong(3, dataSourceObjId);
810 preparedStatement.setShort(4, (
short) fsType);
811 if (attrType != null) {
812 preparedStatement.setShort(5, attrType.shortValue());
814 preparedStatement.setNull(5, java.sql.Types.SMALLINT);
816 if (attrId != null) {
817 preparedStatement.setInt(6, attrId);
819 preparedStatement.setNull(6, java.sql.Types.INTEGER);
821 preparedStatement.setString(7, name);
822 if (metaAddr != null) {
823 preparedStatement.setLong(8, metaAddr);
825 preparedStatement.setNull(8, java.sql.Types.BIGINT);
827 if (metaSeq != null) {
828 preparedStatement.setInt(9, metaSeq.intValue());
830 preparedStatement.setNull(9, java.sql.Types.INTEGER);
832 preparedStatement.setShort(10, (
short) dirType);
833 preparedStatement.setShort(11, (
short) metaType);
834 preparedStatement.setShort(12, (
short) dirFlags);
835 preparedStatement.setShort(13, (
short) metaFlags);
836 preparedStatement.setLong(14, size < 0 ? 0 : size);
837 if (crtime != null) {
838 preparedStatement.setLong(15, crtime);
840 preparedStatement.setNull(15, java.sql.Types.BIGINT);
843 preparedStatement.setLong(16, ctime);
845 preparedStatement.setNull(16, java.sql.Types.BIGINT);
848 preparedStatement.setLong(17, atime);
850 preparedStatement.setNull(17, java.sql.Types.BIGINT);
853 preparedStatement.setLong(18, mtime);
855 preparedStatement.setNull(18, java.sql.Types.BIGINT);
857 if (meta_mode != null) {
858 preparedStatement.setLong(19, meta_mode);
860 preparedStatement.setNull(19, java.sql.Types.BIGINT);
863 preparedStatement.setLong(20, gid);
865 preparedStatement.setNull(20, java.sql.Types.BIGINT);
868 preparedStatement.setLong(21, uid);
870 preparedStatement.setNull(21, java.sql.Types.BIGINT);
872 preparedStatement.setString(22, md5);
873 preparedStatement.setInt(23, known.getFileKnownValue());
874 preparedStatement.setString(24, escaped_path);
875 preparedStatement.setString(25, extension);
877 preparedStatement.setInt(26, 1);
879 preparedStatement.setNull(26, java.sql.Types.INTEGER);
881 connection.executeUpdate(preparedStatement);
885 && TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType() != fsType
886 && (!name.equals(
".")) && (!name.equals(
".."))) {
887 TimelineManager timelineManager = caseDb.getTimelineManager();
888 DerivedFile derivedFile =
new DerivedFile(caseDb, objectId, dataSourceObjId, name,
889 TskData.TSK_FS_NAME_TYPE_ENUM.valueOf((
short) dirType),
890 TskData.TSK_FS_META_TYPE_ENUM.valueOf((
short) metaType),
891 TskData.TSK_FS_NAME_FLAG_ENUM.valueOf(dirFlags),
893 size, ctime, crtime, atime, mtime, null, null, null, escaped_path, null, parentObjId, null, null, extension);
895 timelineManager.addEventsForNewFileQuiet(derivedFile, connection);
899 }
catch (SQLException ex) {
900 throw new TskCoreException(
"Failed to add file system file", ex);
922 private long addImageToDb(TskData.TSK_IMG_TYPE_ENUM type,
long sectorSize,
long size,
923 String timezone, String md5, String sha1, String sha256,
924 String deviceId, String collectionDetails,
925 CaseDbTransaction transaction)
throws TskCoreException {
928 SleuthkitCase.CaseDbConnection connection = transaction.getConnection();
929 long newObjId = caseDb.addObject(0, TskData.ObjectType.IMG.getObjectType(), connection);
933 String imageInfoSql =
"INSERT INTO tsk_image_info (obj_id, type, ssize, tzone, size, md5, sha1, sha256, display_name)"
934 +
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
935 PreparedStatement preparedStatement = connection.getPreparedStatement(imageInfoSql, Statement.NO_GENERATED_KEYS);
936 preparedStatement.clearParameters();
937 preparedStatement.setLong(1, newObjId);
938 preparedStatement.setShort(2, (
short) type.getValue());
939 preparedStatement.setLong(3, sectorSize);
940 preparedStatement.setString(4, timezone);
942 long savedSize = size < 0 ? 0 : size;
943 preparedStatement.setLong(5, savedSize);
944 preparedStatement.setString(6, md5);
945 preparedStatement.setString(7, sha1);
946 preparedStatement.setString(8, sha256);
947 preparedStatement.setString(9, null);
948 connection.executeUpdate(preparedStatement);
951 String dataSourceInfoSql =
"INSERT INTO data_source_info (obj_id, device_id, time_zone, acquisition_details) VALUES (?, ?, ?, ?)";
952 preparedStatement = connection.getPreparedStatement(dataSourceInfoSql, Statement.NO_GENERATED_KEYS);
953 preparedStatement.clearParameters();
954 preparedStatement.setLong(1, newObjId);
955 preparedStatement.setString(2, deviceId);
956 preparedStatement.setString(3, timezone);
957 preparedStatement.setString(4, collectionDetails);
958 connection.executeUpdate(preparedStatement);
961 }
catch (SQLException ex) {
962 throw new TskCoreException(String.format(
"Error adding image to database"), ex);
976 private void addImageNameToDb(
long objId, String name,
long sequence,
977 CaseDbTransaction transaction)
throws TskCoreException {
979 SleuthkitCase.CaseDbConnection connection = transaction.getConnection();
981 String imageNameSql =
"INSERT INTO tsk_image_names (obj_id, name, sequence) VALUES (?, ?, ?)";
982 PreparedStatement preparedStatement = connection.getPreparedStatement(imageNameSql, Statement.NO_GENERATED_KEYS);
983 preparedStatement.clearParameters();
984 preparedStatement.setLong(1, objId);
985 preparedStatement.setString(2, name);
986 preparedStatement.setLong(3, sequence);
987 connection.executeUpdate(preparedStatement);
988 }
catch (SQLException ex) {
989 throw new TskCoreException(String.format(
"Error adding image name %s to image with object ID %d", name, objId), ex);
1004 void addLayoutFileRangeToDb(
long objId,
long byteStart,
long byteLen,
1005 long seq, CaseDbTransaction transaction)
throws TskCoreException {
1007 SleuthkitCase.CaseDbConnection connection = transaction.getConnection();
1009 String insertRangeSql =
"INSERT INTO tsk_file_layout (obj_id, byte_start, byte_len, sequence) "
1010 +
"VALUES (?, ?, ?, ?)";
1011 PreparedStatement preparedStatement = connection.getPreparedStatement(insertRangeSql, Statement.NO_GENERATED_KEYS);
1012 preparedStatement.clearParameters();
1013 preparedStatement.setLong(1, objId);
1014 preparedStatement.setLong(2, byteStart);
1015 preparedStatement.setLong(3, byteLen);
1016 preparedStatement.setLong(4, seq);
1017 connection.executeUpdate(preparedStatement);
1018 }
catch (SQLException ex) {
1019 throw new TskCoreException(
"Error adding layout range to file with obj ID " + objId, ex);