19 package org.sleuthkit.autopsy.logicalimager.dsp;
 
   21 import java.io.BufferedReader;
 
   23 import java.io.FileInputStream;
 
   24 import java.io.IOException;
 
   25 import java.io.InputStreamReader;
 
   26 import java.nio.file.Files;
 
   27 import java.nio.file.Path;
 
   28 import java.nio.file.Paths;
 
   29 import java.util.ArrayList;
 
   30 import java.util.Collection;
 
   31 import java.util.HashMap;
 
   32 import java.util.List;
 
   34 import java.util.logging.Level;
 
   35 import javax.annotation.concurrent.GuardedBy;
 
   36 import org.apache.commons.io.FileUtils;
 
   37 import org.openide.util.NbBundle.Messages;
 
   58 final class AddLogicalImageTask 
implements Runnable {
 
   61     private final static String SEARCH_RESULTS_TXT = 
"SearchResults.txt"; 
 
   62     private final static String USERS_TXT = 
"users.txt"; 
 
   63     private final static String MODULE_NAME = 
"Logical Imager"; 
 
   64     private final static String ROOT_STR = 
"root"; 
 
   65     private final static String VHD_EXTENSION = 
".vhd"; 
 
   66     private final String deviceId;
 
   67     private final String timeZone;
 
   68     private final File src;
 
   69     private final File dest;
 
   72     private final Blackboard blackboard;
 
   73     private final Case currentCase;
 
   75     private volatile boolean cancelled;
 
   76     private volatile boolean createVHD;
 
   77     private long totalFiles;
 
   78     private Map<String, Long> imagePathToObjIdMap;
 
   80     private final Object addMultipleImagesLock;
 
   81     @GuardedBy(
"addMultipleImagesLock")
 
   82     private AddMultipleImagesTask addMultipleImagesTask = null;
 
   84     AddLogicalImageTask(String deviceId,
 
   90         this.deviceId = deviceId;
 
   91         this.timeZone = timeZone;
 
   94         this.progressMonitor = progressMonitor;
 
   95         this.callback = callback;
 
   98         this.addMultipleImagesLock = 
new Object();
 
  106         "# {0} - src", 
"# {1} - dest", 
"AddLogicalImageTask.copyingImageFromTo=Copying image from {0} to {1}",
 
  107         "AddLogicalImageTask.doneCopying=Done copying",
 
  108         "# {0} - src", 
"# {1} - dest", 
"AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1}",
 
  109         "# {0} - file", 
"AddLogicalImageTask.addingToReport=Adding {0} to report",
 
  110         "# {0} - file", 
"AddLogicalImageTask.doneAddingToReport=Done adding {0} to report",
 
  111         "AddLogicalImageTask.ingestionCancelled=Ingestion cancelled",
 
  112         "# {0} - file", 
"AddLogicalImageTask.failToGetCanonicalPath=Fail to get canonical path for {0}",
 
  113         "# {0} - sparseImageDirectory", 
"AddLogicalImageTask.directoryDoesNotContainSparseImage=Directory {0} does not contain any images",
 
  114         "AddLogicalImageTask.noCurrentCase=No current case",
 
  115         "AddLogicalImageTask.addingInterestingFiles=Adding search results as interesting files",
 
  116         "AddLogicalImageTask.doneAddingInterestingFiles=Done adding search results as interesting files",
 
  117         "# {0} - SearchResults.txt", 
"# {1} - directory", 
"AddLogicalImageTask.cannotFindFiles=Cannot find {0} in {1}",
 
  118         "# {0} - reason", 
"AddLogicalImageTask.failedToAddInterestingFiles=Failed to add interesting files: {0}",
 
  119         "AddLogicalImageTask.addingExtractedFiles=Adding extracted files",
 
  120         "AddLogicalImageTask.doneAddingExtractedFiles=Done adding extracted files",
 
  121         "# {0} - reason", 
"AddLogicalImageTask.failedToGetTotalFilesCount=Failed to get total files count: {0}",
 
  122         "AddLogicalImageTask.addImageCancelled=Add image cancelled" 
  126         List<String> errorList = 
new ArrayList<>();
 
  127         List<Content> emptyDataSources = 
new ArrayList<>();
 
  130             progressMonitor.
setProgressText(Bundle.AddLogicalImageTask_copyingImageFromTo(src.toString(), dest.toString()));
 
  131             FileUtils.copyDirectory(src, dest);
 
  132             progressMonitor.
setProgressText(Bundle.AddLogicalImageTask_doneCopying());
 
  133         } 
catch (IOException ex) {
 
  135             String msg = Bundle.AddLogicalImageTask_failedToCopyDirectory(src.toString(), dest.toString());
 
  142             deleteDestinationDirectory();
 
  143             errorList.add(Bundle.AddLogicalImageTask_addImageCancelled());
 
  149         String resultsFilename;
 
  150         if (Paths.get(dest.toString(), SEARCH_RESULTS_TXT).toFile().exists()) {
 
  151             resultsFilename = SEARCH_RESULTS_TXT;
 
  153             errorList.add(Bundle.AddLogicalImageTask_cannotFindFiles(SEARCH_RESULTS_TXT, dest.toString()));
 
  158         progressMonitor.
setProgressText(Bundle.AddLogicalImageTask_addingToReport(resultsFilename));
 
  159         String status = addReport(Paths.get(dest.toString(), resultsFilename), resultsFilename + 
" " + src.getName());
 
  160         if (status != null) {
 
  161             errorList.add(status);
 
  165         progressMonitor.
setProgressText(Bundle.AddLogicalImageTask_doneAddingToReport(resultsFilename));
 
  167         progressMonitor.
setProgressText(Bundle.AddLogicalImageTask_addingToReport(USERS_TXT));
 
  168         status = addReport(Paths.get(dest.toString(), USERS_TXT), USERS_TXT + 
" " + src.getName());
 
  169         if (status != null) {
 
  170             errorList.add(status);
 
  174         progressMonitor.
setProgressText(Bundle.AddLogicalImageTask_doneAddingToReport(USERS_TXT));
 
  177         List<String> imagePaths = 
new ArrayList<>();
 
  178         for (File f : dest.listFiles()) {
 
  179             if (f.getName().endsWith(VHD_EXTENSION)) {
 
  181                     imagePaths.add(f.getCanonicalPath());
 
  182                 } 
catch (IOException ioe) {
 
  183                     String msg = Bundle.AddLogicalImageTask_failToGetCanonicalPath(f.getName());
 
  191         Path resultsPath = Paths.get(dest.toString(), resultsFilename);
 
  193             totalFiles = Files.lines(resultsPath).count() - 1; 
 
  194         } 
catch (IOException ex) {
 
  195             errorList.add(Bundle.AddLogicalImageTask_failedToGetTotalFilesCount(ex.getMessage()));
 
  200         List<Content> newDataSources = 
new ArrayList<>();
 
  202         if (imagePaths.isEmpty()) {
 
  205             File root = Paths.get(dest.toString(), ROOT_STR).toFile();
 
  206             if (root.exists() && root.isDirectory()) {
 
  207                 imagePaths.add(root.getAbsolutePath());
 
  209                 String msg = Bundle.AddLogicalImageTask_directoryDoesNotContainSparseImage(dest);
 
  216                 progressMonitor.
setProgressText(Bundle.AddLogicalImageTask_addingExtractedFiles());
 
  217                 addExtractedFiles(dest, resultsPath, newDataSources);
 
  218                 progressMonitor.
setProgressText(Bundle.AddLogicalImageTask_doneAddingExtractedFiles());
 
  219             } 
catch (IOException | TskCoreException ex) {
 
  220                 errorList.add(ex.getMessage());
 
  221                 LOGGER.log(Level.SEVERE, String.format(
"Failed to add datasource: %s", ex.getMessage()), ex); 
 
  229                 synchronized (addMultipleImagesLock) {
 
  231                         LOGGER.log(Level.SEVERE, 
"Add VHD cancelled"); 
 
  232                         errorList.add(Bundle.AddLogicalImageTask_addImageCancelled());
 
  236                     addMultipleImagesTask = 
new AddMultipleImagesTask(deviceId, imagePaths, timeZone , progressMonitor);
 
  238                 addMultipleImagesTask.run();
 
  240                     LOGGER.log(Level.SEVERE, 
"Failed to add VHD datasource"); 
 
  245                 String msg = Bundle.AddLogicalImageTask_noCurrentCase();
 
  255                 deleteDestinationDirectory();
 
  257             errorList.add(Bundle.AddLogicalImageTask_addImageCancelled());
 
  263             progressMonitor.
setProgressText(Bundle.AddLogicalImageTask_addingInterestingFiles());
 
  264             addInterestingFiles(Paths.get(dest.toString(), resultsFilename), createVHD);
 
  265             progressMonitor.
setProgressText(Bundle.AddLogicalImageTask_doneAddingInterestingFiles());
 
  267                 callback.
done(addMultipleImagesTask.getResult(), addMultipleImagesTask.getErrorMessages(), addMultipleImagesTask.getNewDataSources());
 
  271         } 
catch (IOException | TskCoreException ex) {
 
  272             errorList.add(Bundle.AddLogicalImageTask_failedToAddInterestingFiles(ex.getMessage()));
 
  273             LOGGER.log(Level.SEVERE, 
"Failed to add interesting files", ex); 
 
  288         "# {0} - file", 
"# {1} - exception message", 
"AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1}" 
  290     private String addReport(Path reportPath, String reportName) {
 
  291         if (!reportPath.toFile().exists()) {
 
  297         } 
catch (TskCoreException ex) {
 
  298             String msg = Bundle.AddLogicalImageTask_failedToAddReport(reportPath.toString(), ex.getMessage());
 
  299             LOGGER.log(Level.SEVERE, String.format(
"Failed to add report %s. Reason= %s", reportPath.toString(), ex.getMessage()), ex); 
 
  309         LOGGER.log(Level.WARNING, 
"AddLogicalImageTask cancelled, processing may be incomplete"); 
 
  310         synchronized (addMultipleImagesLock) {
 
  312             if (addMultipleImagesTask != null) {
 
  313                 addMultipleImagesTask.cancelTask();
 
  318     private Map<String, Long> imagePathsToDataSourceObjId(Map<Long, List<String>> imagePaths) {
 
  319         Map<String, Long> imagePathToObjId = 
new HashMap<>();
 
  320         for (Map.Entry<Long, List<String>> entry : imagePaths.entrySet()) {
 
  321             Long key = entry.getKey();
 
  322             List<String> names = entry.getValue();
 
  323             for (String name : names) {
 
  324                 imagePathToObjId.put(name, key);
 
  327         return imagePathToObjId;
 
  331         "# {0} - line number", 
"# {1} - fields length", 
"# {2} - expected length", 
"AddLogicalImageTask.notEnoughFields=File does not contain enough fields at line {0}, got {1}, expecting {2}",
 
  332         "# {0} - target image path", 
"AddLogicalImageTask.cannotFindDataSourceObjId=Cannot find obj_id in tsk_image_names for {0}",
 
  333         "# {0} - file number", 
"# {1} - total files", 
"AddLogicalImageTask.addingInterestingFile=Adding interesting files ({0}/{1})" 
  335     private void addInterestingFiles(Path resultsPath, 
boolean createVHD) 
throws IOException, TskCoreException {
 
  336         Map<Long, List<String>> objIdToimagePathsMap = currentCase.
getSleuthkitCase().getImagePaths();
 
  337         imagePathToObjIdMap = imagePathsToDataSourceObjId(objIdToimagePathsMap);
 
  339         try (BufferedReader br = 
new BufferedReader(
new InputStreamReader(
 
  340                       new FileInputStream(resultsPath.toFile()), 
"UTF8"))) { 
 
  341             List<BlackboardArtifact> artifacts = 
new ArrayList<>();
 
  345             while ((line = br.readLine()) != null) {
 
  351                 String[] fields = line.split(
"\t", -1); 
 
  352                 if (fields.length != 14) {
 
  353                     throw new IOException(Bundle.AddLogicalImageTask_notEnoughFields(lineNumber, fields.length, 14));
 
  355                 String vhdFilename = fields[0];
 
  357                 String fileMetaAddressStr = fields[2];
 
  359                 String ruleSetName = fields[4];
 
  360                 String ruleName = fields[5];
 
  362                 String filename = fields[7];
 
  363                 String parentPath = fields[8];
 
  365                 if (lineNumber % 100 == 0) {
 
  366                     progressMonitor.
setProgressText(Bundle.AddLogicalImageTask_addingInterestingFile(lineNumber, totalFiles));
 
  368                 String query = makeQuery(createVHD, vhdFilename, fileMetaAddressStr, parentPath, filename);
 
  372                 for (AbstractFile file : matchedFiles) {
 
  373                     addInterestingFileToArtifacts(file, ruleSetName, ruleName, artifacts);
 
  380                 blackboard.postArtifacts(artifacts, MODULE_NAME);
 
  381             } 
catch (Blackboard.BlackboardException ex) {
 
  382                 LOGGER.log(Level.SEVERE, 
"Unable to post artifacts to blackboard", ex); 
 
  387     private void addInterestingFileToArtifacts(AbstractFile file, String ruleSetName, String ruleName, List<BlackboardArtifact> artifacts) 
throws TskCoreException {
 
  388         Collection<BlackboardAttribute> attributes = 
new ArrayList<>();
 
  389         BlackboardAttribute setNameAttribute = 
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, ruleSetName);
 
  390         attributes.add(setNameAttribute);
 
  391         BlackboardAttribute ruleNameAttribute = 
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, MODULE_NAME, ruleName);
 
  392         attributes.add(ruleNameAttribute);
 
  393         if (!blackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, attributes)) {
 
  394             BlackboardArtifact artifact = this.currentCase.
getSleuthkitCase().newBlackboardArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, file.getId());
 
  395             artifact.addAttributes(attributes);
 
  396             artifacts.add(artifact);
 
  401         "# {0} - file number", 
"# {1} - total files", 
"AddLogicalImageTask.addingExtractedFile=Adding extracted files ({0}/{1})" 
  403     private void addExtractedFiles(File src, Path resultsPath, List<Content> newDataSources) 
throws TskCoreException, IOException {
 
  405         SleuthkitCase.CaseDbTransaction trans = null;
 
  408             trans = skCase.beginTransaction();
 
  409             LocalFilesDataSource localFilesDataSource = skCase.addLocalFilesDataSource(deviceId, this.src.getName(), timeZone, trans);
 
  412             try (BufferedReader br = 
new BufferedReader(
new InputStreamReader(
 
  413                     new FileInputStream(resultsPath.toFile()), 
"UTF8"))) { 
 
  417                 while ((line = br.readLine()) != null) {
 
  419                         rollbackTransaction(trans);
 
  422                     String[] fields = line.split(
"\t", -1); 
 
  423                     if (fields.length != 14) {
 
  424                         rollbackTransaction(trans);
 
  425                         throw new IOException(Bundle.AddLogicalImageTask_notEnoughFields(lineNumber, fields.length, 14));
 
  427                     String vhdFilename = fields[0];
 
  434                     String filename = fields[7];
 
  435                     String parentPath = fields[8];
 
  436                     String extractedFilePath = fields[9];
 
  437                     String crtime = fields[10];
 
  438                     String mtime = fields[11];
 
  439                     String atime = fields[12];
 
  440                     String ctime = fields[13];
 
  441                     parentPath = ROOT_STR + 
"/" + vhdFilename + 
"/" + parentPath;
 
  443                     if (lineNumber % 100 == 0) {
 
  444                         progressMonitor.
setProgressText(Bundle.AddLogicalImageTask_addingExtractedFile(lineNumber, totalFiles));
 
  449                         Paths.get(src.toString(), extractedFilePath).toFile(),
 
  452                         Long.parseLong(ctime),
 
  453                         Long.parseLong(crtime),
 
  454                         Long.parseLong(atime),
 
  455                         Long.parseLong(mtime),
 
  456                         localFilesDataSource);
 
  462             newDataSources.add(localFilesDataSource);
 
  464         } 
catch (NumberFormatException | TskCoreException ex) {
 
  465             LOGGER.log(Level.SEVERE, 
"Error adding extracted files", ex); 
 
  466             rollbackTransaction(trans);
 
  467             throw new TskCoreException(
"Error adding extracted files", ex);
 
  471     private void rollbackTransaction(SleuthkitCase.CaseDbTransaction trans) 
throws TskCoreException {
 
  475             } 
catch (TskCoreException ex) {
 
  476                 LOGGER.log(Level.SEVERE, String.format(
"Failed to rollback transaction: %s", ex.getMessage()), ex); 
 
  481     private boolean deleteDestinationDirectory() {
 
  483             FileUtils.deleteDirectory(dest);
 
  484             LOGGER.log(Level.INFO, String.format(
"Cancellation: Deleted directory %s", dest.toString())); 
 
  486         } 
catch (IOException ex) {
 
  487             LOGGER.log(Level.WARNING, String.format(
"Cancellation: Failed to delete directory %s", dest.toString()), ex);  
 
  492     String makeQuery(
boolean createVHD, String vhdFilename, String fileMetaAddressStr, String parentPath, String filename) 
throws TskCoreException {
 
  495             String targetImagePath = Paths.get(dest.toString(), vhdFilename).toString();
 
  496             Long dataSourceObjId = imagePathToObjIdMap.get(targetImagePath);
 
  497             if (dataSourceObjId == null) {
 
  498                 throw new TskCoreException(Bundle.AddLogicalImageTask_cannotFindDataSourceObjId(targetImagePath));
 
  500             query = String.format(
"data_source_obj_id = '%s' AND meta_addr = '%s' AND name = '%s'", 
 
  501                     dataSourceObjId.toString(), fileMetaAddressStr, filename.replace(
"'", 
"''"));
 
  503             String newParentPath = 
"/" + ROOT_STR + 
"/" + vhdFilename + 
"/" + parentPath;
 
  504             query = String.format(
"name = '%s' AND parent_path = '%s'", 
 
  505                     filename.replace(
"'", 
"''"), newParentPath.replace(
"'", 
"''"));
 
org.sleuthkit.datamodel.Blackboard getArtifactsBlackboard()
 
void setProgressText(String text)
 
void addReport(String localPath, String srcModuleName, String reportName)
 
void done(DataSourceProcessorResult result, List< String > errList, List< Content > newDataSources)
 
AbstractFile addLocalFile(File fileOnDisk, String name, String parentPath, Long ctime, Long crtime, Long atime, Long mtime, DataSource dataSource)
 
SleuthkitCase getSleuthkitCase()
 
static Case getCurrentCase()
 
synchronized static Logger getLogger(String name)