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)