Sleuth Kit Java Bindings (JNI)  4.9.0
Java bindings for using The Sleuth Kit
JniDbHelper.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2020 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.datamodel;
20 
21 import org.apache.commons.lang3.StringUtils;
22 import java.util.List;
23 import java.util.Arrays;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.LinkedList;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.Queue;
30 import java.util.logging.Level;
31 import java.util.logging.Logger;
33 
43 class JniDbHelper {
44 
45  private static final Logger logger = Logger.getLogger(JniDbHelper.class.getName());
46 
47  private final SleuthkitCase caseDb;
48  private CaseDbTransaction trans = null;
49  private final AddDataSourceCallbacks addDataSourceCallbacks;
50 
51  private final Map<Long, Long> fsIdToRootDir = new HashMap<>();
52  private final Map<Long, TskData.TSK_FS_TYPE_ENUM> fsIdToFsType = new HashMap<>();
53  private final Map<ParentCacheKey, Long> parentDirCache = new HashMap<>();
54 
55  private static final long BATCH_FILE_THRESHOLD = 500;
56  private final Queue<FileInfo> batchedFiles = new LinkedList<>();
57  private final Queue<LayoutRangeInfo> batchedLayoutRanges = new LinkedList<>();
58  private final List<Long> layoutFileIds = new ArrayList<>();
59 
60  JniDbHelper(SleuthkitCase caseDb, AddDataSourceCallbacks addDataSourceCallbacks) {
61  this.caseDb = caseDb;
62  this.addDataSourceCallbacks = addDataSourceCallbacks;
63  trans = null;
64  }
65 
71  private void beginTransaction() throws TskCoreException {
72  trans = caseDb.beginTransaction();
73  }
74 
80  private void commitTransaction() throws TskCoreException {
81  trans.commit();
82  trans = null;
83  }
84 
88  private void revertTransaction() {
89  try {
90  if (trans != null) {
91  trans.rollback();
92  trans = null;
93  }
94  } catch (TskCoreException ex) {
95  logger.log(Level.SEVERE, "Error rolling back transaction", ex);
96  }
97  }
98 
102  void finish() {
103  addBatchedFilesToDb();
104  addBatchedLayoutRangesToDb();
105  processLayoutFiles();
106  }
107 
126  long addImageInfo(int type, long ssize, String timezone,
127  long size, String md5, String sha1, String sha256, String deviceId,
128  String collectionDetails, String[] paths) {
129  try {
130  beginTransaction();
131  long objId = caseDb.addImageJNI(TskData.TSK_IMG_TYPE_ENUM.valueOf(type), ssize, size,
132  timezone, md5, sha1, sha256, deviceId, collectionDetails, trans);
133  for (int i = 0;i < paths.length;i++) {
134  caseDb.addImageNameJNI(objId, paths[i], i, trans);
135  }
136  commitTransaction();
137  return objId;
138  } catch (TskCoreException ex) {
139  logger.log(Level.SEVERE, "Error adding image to the database", ex);
140  revertTransaction();
141  return -1;
142  }
143  }
144 
151  void addAcquisitionDetails(long imgId, String details) {
152  try {
153  beginTransaction();
154  caseDb.setAcquisitionDetails(imgId, details, trans);
155  commitTransaction();
156  } catch (TskCoreException ex) {
157  logger.log(Level.SEVERE, "Error adding image details \"" + details + "\" to image with ID " + imgId, ex);
158  revertTransaction();
159  }
160  }
161 
173  long addVsInfo(long parentObjId, int vsType, long imgOffset, long blockSize) {
174  try {
175  beginTransaction();
176  VolumeSystem vs = caseDb.addVolumeSystem(parentObjId, TskData.TSK_VS_TYPE_ENUM.valueOf(vsType), imgOffset, blockSize, trans);
177  commitTransaction();
178  return vs.getId();
179  } catch (TskCoreException ex) {
180  logger.log(Level.SEVERE, "Error adding volume system to the database - parent obj ID: " + parentObjId
181  + ", image offset: " + imgOffset, ex);
182  revertTransaction();
183  return -1;
184  }
185  }
186 
200  long addVolume(long parentObjId, long addr, long start, long length, String desc,
201  long flags) {
202  try {
203  beginTransaction();
204  Volume vol = caseDb.addVolume(parentObjId, addr, start, length, desc, flags, trans);
205  commitTransaction();
206  return vol.getId();
207  } catch (TskCoreException ex) {
208  logger.log(Level.SEVERE, "Error adding volume to the database - parent object ID: " + parentObjId
209  + ", addr: " + addr, ex);
210  revertTransaction();
211  return -1;
212  }
213  }
214 
224  long addPool(long parentObjId, int poolType) {
225  try {
226  beginTransaction();
227  Pool pool = caseDb.addPool(parentObjId, TskData.TSK_POOL_TYPE_ENUM.valueOf(poolType), trans);
228  commitTransaction();
229  return pool.getId();
230  } catch (TskCoreException ex) {
231  logger.log(Level.SEVERE, "Error adding pool to the database - parent object ID: " + parentObjId, ex);
232  revertTransaction();
233  return -1;
234  }
235  }
236 
252  long addFileSystem(long parentObjId, long imgOffset, int fsType, long blockSize, long blockCount,
253  long rootInum, long firstInum, long lastInum) {
254  try {
255  beginTransaction();
256  FileSystem fs = caseDb.addFileSystem(parentObjId, imgOffset, TskData.TSK_FS_TYPE_ENUM.valueOf(fsType), blockSize, blockCount,
257  rootInum, firstInum, lastInum, null, trans);
258  commitTransaction();
259  fsIdToFsType.put(fs.getId(), TskData.TSK_FS_TYPE_ENUM.valueOf(fsType));
260  return fs.getId();
261  } catch (TskCoreException ex) {
262  logger.log(Level.SEVERE, "Error adding file system to the database - parent object ID: " + parentObjId
263  + ", offset: " + imgOffset, ex);
264  revertTransaction();
265  return -1;
266  }
267  }
268 
309  long addFile(long parentObjId,
310  long fsObjId, long dataSourceObjId,
311  int fsType,
312  int attrType, int attrId, String name,
313  long metaAddr, long metaSeq,
314  int dirType, int metaType, int dirFlags, int metaFlags,
315  long size,
316  long crtime, long ctime, long atime, long mtime,
317  int meta_mode, int gid, int uid,
318  String escaped_path, String extension,
319  long seq, long parMetaAddr, long parSeq) {
320 
321  // Add the new file to the list
322  batchedFiles.add(new FileInfo(parentObjId,
323  fsObjId, dataSourceObjId,
324  fsType,
325  attrType, attrId, name,
326  metaAddr, metaSeq,
327  dirType, metaType, dirFlags, metaFlags,
328  size,
329  crtime, ctime, atime, mtime,
330  meta_mode, gid, uid,
331  escaped_path, extension,
332  seq, parMetaAddr, parSeq));
333 
334  // Add the current files to the database if we've exceeded the threshold or if we
335  // have the root folder.
336  if ((fsObjId == parentObjId)
337  || (batchedFiles.size() > BATCH_FILE_THRESHOLD)) {
338  return addBatchedFilesToDb();
339  }
340  return 0;
341  }
342 
348  private long addBatchedFilesToDb() {
349  List<Long> newObjIds = new ArrayList<>();
350  try {
351  beginTransaction();
352  FileInfo fileInfo;
353  while ((fileInfo = batchedFiles.poll()) != null) {
354  long computedParentObjId = fileInfo.parentObjId;
355  try {
356  // If we weren't given the parent object ID, look it up
357  if (fileInfo.parentObjId == 0) {
358  computedParentObjId = getParentObjId(fileInfo);
359  }
360 
361  long objId = caseDb.addFileJNI(computedParentObjId,
362  fileInfo.fsObjId, fileInfo.dataSourceObjId,
363  fileInfo.fsType,
364  fileInfo.attrType, fileInfo.attrId, fileInfo.name,
365  fileInfo.metaAddr, fileInfo.metaSeq,
366  fileInfo.dirType, fileInfo.metaType, fileInfo.dirFlags, fileInfo.metaFlags,
367  fileInfo.size,
368  fileInfo.crtime, fileInfo.ctime, fileInfo.atime, fileInfo.mtime,
369  fileInfo.meta_mode, fileInfo.gid, fileInfo.uid,
370  null, TskData.FileKnown.UNKNOWN,
371  fileInfo.escaped_path, fileInfo.extension,
372  false, trans);
373  if (fileInfo.fsObjId != fileInfo.parentObjId) {
374  // Add new file ID to the list to send to ingest unless it is the root folder
375  newObjIds.add(objId);
376  }
377 
378  // If we're adding the root directory for the file system, cache it
379  if (fileInfo.parentObjId == fileInfo.fsObjId) {
380  fsIdToRootDir.put(fileInfo.fsObjId, objId);
381  }
382 
383  // If the file is a directory, cache the object ID
384  if ((fileInfo.metaType == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()
385  || (fileInfo.metaType == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT_DIR.getValue()))
386  && (fileInfo.name != null)
387  && ! fileInfo.name.equals(".")
388  && ! fileInfo.name.equals("..")) {
389  String dirName = fileInfo.escaped_path + fileInfo.name;
390  ParentCacheKey key = new ParentCacheKey(fileInfo.fsObjId, fileInfo.metaAddr, fileInfo.seq, dirName);
391  parentDirCache.put(key, objId);
392  }
393  } catch (TskCoreException ex) {
394  logger.log(Level.SEVERE, "Error adding file to the database - parent object ID: " + computedParentObjId
395  + ", file system object ID: " + fileInfo.fsObjId + ", name: " + fileInfo.name, ex);
396  }
397  }
398  commitTransaction();
399  try {
400  addDataSourceCallbacks.onFilesAdded(newObjIds);
401  } catch (Exception ex) {
402  // Exception firewall to prevent unexpected return to the native code
403  logger.log(Level.SEVERE, "Unexpected error from files added callback", ex);
404  }
405  } catch (TskCoreException ex) {
406  logger.log(Level.SEVERE, "Error adding batched files to database", ex);
407  revertTransaction();
408  return -1;
409  }
410  return 0;
411  }
412 
422  private long getParentObjId(FileInfo fileInfo) throws TskCoreException {
423  // Remove the final slash from the path unless we're in the root folder
424  String parentPath = fileInfo.escaped_path;
425  if(parentPath.endsWith("/") && ! parentPath.equals("/")) {
426  parentPath = parentPath.substring(0, parentPath.lastIndexOf('/'));
427  }
428 
429  // Look up the parent
430  ParentCacheKey key = new ParentCacheKey(fileInfo.fsObjId, fileInfo.parMetaAddr, fileInfo.parSeq, parentPath);
431  if (parentDirCache.containsKey(key)) {
432  return parentDirCache.get(key);
433  } else {
434  // There's no reason to do a database query since every folder added is being
435  // stored in the cache.
436  throw new TskCoreException("Parent not found in cache (fsObjId: " +fileInfo.fsObjId + ", parMetaAddr: " + fileInfo.parMetaAddr
437  + ", parSeq: " + fileInfo.parSeq + ", parentPath: " + parentPath + ")");
438  }
439  }
440 
454  long addLayoutFile(long parentObjId,
455  long fsObjId, long dataSourceObjId,
456  int fileType,
457  String name, long size) {
458  try {
459 
460  // The file system may be null for layout files
461  Long fsObjIdForDb = fsObjId;
462  if (fsObjId == 0) {
463  fsObjIdForDb = null;
464  }
465 
466  beginTransaction();
467  long objId = caseDb.addFileJNI(parentObjId,
468  fsObjIdForDb, dataSourceObjId,
469  fileType,
470  null, null, name,
471  null, null,
472  TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue(),
473  TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue(),
474  TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue(),
475  TskData.TSK_FS_META_FLAG_ENUM.UNALLOC.getValue(),
476  size,
477  null, null, null, null,
478  null, null, null,
479  null, TskData.FileKnown.UNKNOWN,
480  null, null,
481  true, trans);
482  commitTransaction();
483 
484  // Store the layout file ID for later processing
485  layoutFileIds.add(objId);
486 
487  return objId;
488  } catch (TskCoreException ex) {
489  logger.log(Level.SEVERE, "Error adding layout file to the database - parent object ID: " + parentObjId
490  + ", file system object ID: " + fsObjId + ", name: " + name, ex);
491  revertTransaction();
492  return -1;
493  }
494  }
495 
507  long addLayoutFileRange(long objId, long byteStart, long byteLen, long seq) {
508  batchedLayoutRanges.add(new LayoutRangeInfo(objId, byteStart, byteLen, seq));
509 
510  if (batchedLayoutRanges.size() > BATCH_FILE_THRESHOLD) {
511  return addBatchedLayoutRangesToDb();
512  }
513  return 0;
514  }
515 
521  private long addBatchedLayoutRangesToDb() {
522  try {
523  beginTransaction();
524  LayoutRangeInfo range;
525  while ((range = batchedLayoutRanges.poll()) != null) {
526  try {
527  caseDb.addLayoutFileRangeJNI(range.objId, range.byteStart, range.byteLen, range.seq, trans);
528  } catch (TskCoreException ex) {
529  logger.log(Level.SEVERE, "Error adding layout file range to the database - layout file ID: " + range.objId
530  + ", byte start: " + range.byteStart + ", length: " + range.byteLen + ", seq: " + range.seq, ex);
531  }
532  }
533  commitTransaction();
534  return 0;
535  } catch (TskCoreException ex) {
536  logger.log(Level.SEVERE, "Error adding batched files to database", ex);
537  revertTransaction();
538  return -1;
539  }
540  }
541 
547  void processLayoutFiles() {
548  addDataSourceCallbacks.onFilesAdded(layoutFileIds);
549  layoutFileIds.clear();
550  }
551 
561  long addUnallocFsBlockFilesParent(long fsObjId, String name) {
562  try {
563  if (! fsIdToRootDir.containsKey(fsObjId)) {
564  logger.log(Level.SEVERE, "Error - root directory for file system ID {0} not found", fsObjId);
565  return -1;
566  }
567  beginTransaction();
568  VirtualDirectory dir = caseDb.addVirtualDirectory(fsIdToRootDir.get(fsObjId), name, trans);
569  commitTransaction();
570  addDataSourceCallbacks.onFilesAdded(Arrays.asList(dir.getId()));
571  return dir.getId();
572  } catch (TskCoreException ex) {
573  logger.log(Level.SEVERE, "Error creating virtual directory " + name + " under file system ID " + fsObjId, ex);
574  revertTransaction();
575  return -1;
576  }
577  }
578 
582  private class ParentCacheKey {
583  long fsObjId;
584  long metaAddr;
585  long seqNum;
586  String path;
587 
597  ParentCacheKey(long fsObjId, long metaAddr, long seqNum, String path) {
598  this.fsObjId = fsObjId;
599  this.metaAddr = metaAddr;
600  if (fsIdToFsType.containsKey(fsObjId)
601  && (fsIdToFsType.get(fsObjId).equals(TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_NTFS)
602  || fsIdToFsType.get(fsObjId).equals(TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_NTFS_DETECT))) {
603  this.seqNum = seqNum;
604  } else {
605  this.seqNum = 0;
606  }
607  this.path = path;
608  }
609 
610  @Override
611  public boolean equals(Object obj) {
612  if (! (obj instanceof ParentCacheKey)) {
613  return false;
614  }
615 
616  ParentCacheKey otherKey = (ParentCacheKey) obj;
617  if (this.fsObjId != otherKey.fsObjId
618  || this.metaAddr != otherKey.metaAddr
619  || this.seqNum != otherKey.seqNum) {
620  return false;
621  }
622 
623  return StringUtils.equals(this.path, otherKey.path);
624  }
625 
626  @Override
627  public int hashCode() {
628  int hash = 3;
629  hash = 31 * hash + (int) (this.fsObjId ^ (this.fsObjId >>> 32));
630  hash = 31 * hash + (int) (this.metaAddr ^ (this.metaAddr >>> 32));
631  hash = 31 * hash + (int) (this.seqNum ^ (this.seqNum >>> 32));
632  hash = 31 * hash + Objects.hashCode(this.path);
633  return hash;
634  }
635  }
636 
641  private class LayoutRangeInfo {
642  long objId;
643  long byteStart;
644  long byteLen;
645  long seq;
646 
647  LayoutRangeInfo(long objId, long byteStart, long byteLen, long seq) {
648  this.objId = objId;
649  this.byteStart = byteStart;
650  this.byteLen = byteLen;
651  this.seq = seq;
652  }
653  }
654 
659  private class FileInfo {
660  long parentObjId;
661  long fsObjId;
662  long dataSourceObjId;
663  int fsType;
664  int attrType;
665  int attrId;
666  String name;
667  long metaAddr;
668  long metaSeq;
669  int dirType;
670  int metaType;
671  int dirFlags;
672  int metaFlags;
673  long size;
674  long crtime;
675  long ctime;
676  long atime;
677  long mtime;
678  int meta_mode;
679  int gid;
680  int uid;
681  String escaped_path;
682  String extension;
683  long seq;
684  long parMetaAddr;
685  long parSeq;
686 
687  FileInfo(long parentObjId,
688  long fsObjId, long dataSourceObjId,
689  int fsType,
690  int attrType, int attrId, String name,
691  long metaAddr, long metaSeq,
692  int dirType, int metaType, int dirFlags, int metaFlags,
693  long size,
694  long crtime, long ctime, long atime, long mtime,
695  int meta_mode, int gid, int uid,
696  String escaped_path, String extension,
697  long seq, long parMetaAddr, long parSeq) {
698 
699  this.parentObjId = parentObjId;
700  this.fsObjId = fsObjId;
701  this.dataSourceObjId = dataSourceObjId;
702  this.fsType = fsType;
703  this.attrType = attrType;
704  this.attrId = attrId;
705  this.name = name;
706  this.metaAddr = metaAddr;
707  this.metaSeq = metaSeq;
708  this.dirType = dirType;
709  this.metaType = metaType;
710  this.dirFlags = dirFlags;
711  this.metaFlags = metaFlags;
712  this.size = size;
713  this.crtime = crtime;
714  this.ctime = ctime;
715  this.atime = atime;
716  this.mtime = mtime;
717  this.meta_mode = meta_mode;
718  this.gid = gid;
719  this.uid = uid;
720  this.escaped_path = escaped_path;
721  this.extension = extension;
722  this.seq = seq;
723  this.parMetaAddr = parMetaAddr;
724  this.parSeq = parSeq;
725  }
726  }
727 }

Copyright © 2011-2020 Brian Carrier. (carrier -at- sleuthkit -dot- org)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.