diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java new file mode 100644 index 0000000000..6b2f5e2e92 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java @@ -0,0 +1,139 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.casemodule; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import org.apache.commons.io.FileUtils; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +public class AddLogicalImageTask extends AddImageTask { + + private final Logger logger = Logger.getLogger(AddLogicalImageTask.class.getName()); + private final static String ALERT_TXT = "alert.txt"; //NON-NLS + private final static String USERS_TXT = "users.txt"; //NON-NLS + private final File src; + private final File dest; + private final DataSourceProcessorCallback callback; + private final DataSourceProcessorProgressMonitor progressMonitor; + + public AddLogicalImageTask(String deviceId, String imagePath, int sectorSize, + String timeZone, boolean ignoreFatOrphanFiles, + String md5, String sha1, String sha256, + ImageWriterSettings imageWriterSettings, + File src, File dest, + DataSourceProcessorProgressMonitor progressMonitor, + DataSourceProcessorCallback callback + ) { + super(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, + md5, sha1, sha256, imageWriterSettings, progressMonitor, callback); + this.src = src; + this.dest = dest; + this.progressMonitor = progressMonitor; + this.callback = callback; + } + + /** + * Copy the src directory to dest. + * Add alert.txt and users.txt to the case report + * Adds the image to the case database. + */ + @Messages({ + "AddLogicalImageTask.copyingImageFromTo=Copying image from {0} to {1}", + "AddLogicalImageTask.doneCopying=Done copying", + "AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1}", + "AddLogicalImageTask.addingToReport=Adding {0} to report", + "AddLogicalImageTask.doneAddingToReport=Done adding {0} to report" + }) + @Override + public void run() { + List errorList = new ArrayList<>(); + List emptyDataSources = new ArrayList<>(); + + try { + progressMonitor.setProgressText(Bundle.AddLogicalImageTask_copyingImageFromTo(src.toString(), dest.toString())); + FileUtils.copyDirectory(src, dest); + progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneCopying()); + } catch (IOException ex) { + // Copy directory failed + String msg = Bundle.AddLogicalImageTask_failedToCopyDirectory(src.toString(), dest.toString()); + logger.log(Level.SEVERE, msg); + errorList.add(msg); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); + return; + } + + // Add the alert.txt and users.txt to the case report + progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingToReport(ALERT_TXT)); + String status = addReport(Paths.get(dest.toString(), ALERT_TXT), ALERT_TXT + " " + src.getName()); + if (status != null) { + errorList.add(status); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); + return; + } + progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingToReport(ALERT_TXT)); + + progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingToReport(USERS_TXT)); + status = addReport(Paths.get(dest.toString(), USERS_TXT), USERS_TXT + " " + src.getName()); + if (status != null) { + errorList.add(status); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); + return; + } + progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingToReport(USERS_TXT)); + + super.run(); + } + + /** + * Add a file specified by the reportPath to the case report. + * + * @param reportPath Path to the report to be added + * @param reportName Name associated the report + * @returns null if success, or exception message if failure + * + */ + @Messages({ + "AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1}" + }) + private String addReport(Path reportPath, String reportName) { + if (!reportPath.toFile().exists()) { + return null; // if the reportPath doesn't exist, just ignore it. + } + try { + Case.getCurrentCase().addReport(reportPath.toString(), "LogicalImager", reportName); //NON-NLS + return null; + } catch (TskCoreException ex) { + String msg = Bundle.AddLogicalImageTask_failedToAddReport(reportPath.toString(), ex.getMessage()); + logger.log(Level.SEVERE, msg); + return msg; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java index 045d9a067f..56c1193143 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java @@ -19,23 +19,21 @@ package org.sleuthkit.autopsy.casemodule; import java.io.File; -import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.List; import java.util.Calendar; +import java.util.List; import java.util.UUID; import javax.swing.JPanel; -import org.apache.commons.io.FileUtils; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProviders; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; /** * A Logical Imager data source processor that implements the DataSourceProcessor service @@ -48,10 +46,11 @@ import org.sleuthkit.datamodel.TskCoreException; ) public class LogicalImagerDSProcessor implements DataSourceProcessor { + private final Logger logger = Logger.getLogger(LogicalImagerDSProcessor.class.getName()); private static final String LOGICAL_IMAGER_DIR = "LogicalImager"; //NON-NLS private static final String SPARSE_IMAGE_VHD = "sparse_image.vhd"; //NON-NLS private final LogicalImagerPanel configPanel; - private AddImageTask addImageTask; + private AddLogicalImageTask addLogicalImageTask; /* * Constructs a Logical Imager data source processor that implements the @@ -127,26 +126,36 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { * @param callback Callback that will be used by the background task * to return results. */ + @Messages({ + "LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.", + "LogicalImagerDSProcessor.failToCreateDirectory=Fail to create directory {0}", + "LogicalImagerDSProcessor.directoryAlreadyExists=Directory {0} already exists", + }) @Override public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { configPanel.storeSettings(); + Path imageDirPath = configPanel.getImageDirPath(); List errorList = new ArrayList<>(); List emptyDataSources = new ArrayList<>(); + if (!imageDirPath.toFile().exists()) { + // This can happen if the USB drive was selected in the panel, but + // was ejected before pressing the NEXT button // TODO: Better ways to detect ejected USB drive? - String msg = imageDirPath.toString() + " not found.\nUSB drive has been ejected."; + String msg = Bundle.LogicalImagerDSProcessor_imageDirPathNotFound(imageDirPath.toString()); errorList.add(msg); callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); return; } + // Create the LogicalImager directory under ModuleDirectory String moduleDirectory = Case.getCurrentCase().getModuleDirectory(); File logicalImagerDir = Paths.get(moduleDirectory, LOGICAL_IMAGER_DIR).toFile(); if (!logicalImagerDir.exists()) { if (!logicalImagerDir.mkdir()) { // create failed - String msg = "Fail to create directory " + logicalImagerDir; + String msg = Bundle.LogicalImagerDSProcessor_failToCreateDirectory(logicalImagerDir); errorList.add(msg); callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); return; @@ -154,58 +163,22 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { } File dest = Paths.get(logicalImagerDir.toString(), imageDirPath.getFileName().toString()).toFile(); if (dest.exists()) { - // directory already exists - String msg = "Directory " + dest.toString() + " already exists"; + // Destination directory already exists + String msg = Bundle.LogicalImagerDSProcessor_directoryAlreadyExists(dest.toString()); errorList.add(msg); callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); return; } File src = imageDirPath.toFile(); - try { - configPanel.setMessageLabel("Copying " + src.toString() + " directory to " + dest.toString()); - FileUtils.copyDirectory(src, dest); - configPanel.setMessageLabel(""); - } catch (IOException ex) { - // Copy directory failed - String msg = "Failed to copy directory " + src.toString() + " to " + dest.toString() ; - errorList.add(msg); - callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); - return; - } - - // Add the alert.txt and users.txt into the case report - String status = addReport(Paths.get(dest.toString(), "alert.txt"), "alert.txt for " + imageDirPath.toString()); - if (status != null) { - errorList.add(status); - callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); - return; - } - status = addReport(Paths.get(dest.toString(), "users.txt"), "users.txt for " + imageDirPath.toString()); - if (status != null) { - errorList.add(status); - callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); - return; - } - + String deviceId = UUID.randomUUID().toString(); String timeZone = Calendar.getInstance().getTimeZone().getID(); boolean ignoreFatOrphanFiles = false; - run(deviceId, Paths.get(src.toString(), SPARSE_IMAGE_VHD).toString(), 0, timeZone, ignoreFatOrphanFiles, null, null, null, progressMonitor, callback); + run(deviceId, Paths.get(src.toString(), SPARSE_IMAGE_VHD).toString(), 0, + timeZone, ignoreFatOrphanFiles, null, null, null, src, dest, + progressMonitor, callback); } - /** - * returns null if success, or exception message - * - */ - private String addReport(Path reportPath, String reportName) { - try { - Case.getCurrentCase().addReport(reportPath.toString(), "LogicalImager", reportName); - return null; - } catch (TskCoreException ex) { - String msg = "Failed to add report " + reportPath.toString() + ". Reason= " + ex.getMessage(); - return msg; - } - } /** * Adds a "Logical Imager" data source to the case database using a background task in * a separate thread and the given settings instead of those provided by the @@ -224,19 +197,27 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { * @param chunkSize The maximum size of each chunk of the raw * data source as it is divided up into virtual * unallocated space files. + * @param src The source directory of image. + * @param dest The destination directory to copy the source. * @param progressMonitor Progress monitor for reporting progress * during processing. * @param callback Callback to call when processing is done. */ - private void run(String deviceId, String imagePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { - addImageTask = new AddImageTask(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, progressMonitor, callback); - new Thread(addImageTask).start(); + private void run(String deviceId, String imagePath, int sectorSize, String timeZone, + boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, + File src, File dest, + DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback + ) { + addLogicalImageTask = new AddLogicalImageTask(deviceId, imagePath, sectorSize, + timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, src, dest, + progressMonitor, callback); + new Thread(addLogicalImageTask).start(); } @Override public void cancel() { - if (addImageTask != null) { - addImageTask.cancelTask(); + if (addLogicalImageTask != null) { + addLogicalImageTask.cancelTask(); } }