Initial changes

This commit is contained in:
Joe Ho 2019-08-27 16:28:30 -04:00
parent c58b602fa2
commit fe3eeb77dc
3 changed files with 232 additions and 92 deletions

View File

@ -71,7 +71,11 @@ final class AddLogicalImageTask implements Runnable {
private final Case currentCase; private final Case currentCase;
private volatile boolean cancelled; private volatile boolean cancelled;
private boolean addingInterestingFiles;
private AddMultipleImageTask addMultipleImageTask;
private Thread multipleImageThread;
private boolean createVHD;
AddLogicalImageTask(String deviceId, AddLogicalImageTask(String deviceId,
String timeZone, String timeZone,
File src, File dest, File src, File dest,
@ -134,10 +138,6 @@ final class AddLogicalImageTask implements Runnable {
return; return;
} }
if (cancelled) {
return;
}
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingToReport(resultsFilename)); progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingToReport(resultsFilename));
String status = addReport(Paths.get(dest.toString(), resultsFilename), resultsFilename + " " + src.getName()); String status = addReport(Paths.get(dest.toString(), resultsFilename), resultsFilename + " " + src.getName());
if (status != null) { if (status != null) {
@ -171,13 +171,13 @@ final class AddLogicalImageTask implements Runnable {
} }
} }
AddMultipleImageTask addMultipleImageTask = null; addMultipleImageTask = null;
AddDataSourceCallback privateCallback = null;
List<Content> newDataSources = new ArrayList<>(); List<Content> newDataSources = new ArrayList<>();
boolean createVHD;
if (imagePaths.isEmpty()) { if (imagePaths.isEmpty()) {
createVHD = false; createVHD = false;
// No VHD in src directory, try ingest the root directory using Logical File Set // No VHD in src directory, try ingest the root directory as local files
File root = Paths.get(dest.toString(), ROOT_STR).toFile(); File root = Paths.get(dest.toString(), ROOT_STR).toFile();
if (root.exists() && root.isDirectory()) { if (root.exists() && root.isDirectory()) {
imagePaths.add(root.getAbsolutePath()); imagePaths.add(root.getAbsolutePath());
@ -202,12 +202,10 @@ final class AddLogicalImageTask implements Runnable {
createVHD = true; createVHD = true;
// ingest the VHDs // ingest the VHDs
try { try {
addMultipleImageTask = new AddMultipleImageTask(deviceId, imagePaths, timeZone , progressMonitor, callback); privateCallback = new AddDataSourceCallback();
addMultipleImageTask.run(); addMultipleImageTask = new AddMultipleImageTask(deviceId, imagePaths, timeZone , progressMonitor, privateCallback);
if (addMultipleImageTask.getResult() == DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS) { multipleImageThread = new Thread(addMultipleImageTask);
callback.done(addMultipleImageTask.getResult(), addMultipleImageTask.getErrorMessages(), addMultipleImageTask.getNewDataSources()); multipleImageThread.start();
return;
}
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
String msg = Bundle.AddLogicalImageTask_noCurrentCase(); String msg = Bundle.AddLogicalImageTask_noCurrentCase();
errorList.add(msg); errorList.add(msg);
@ -217,11 +215,40 @@ final class AddLogicalImageTask implements Runnable {
} }
try { try {
if (createVHD) {
// Wait for addMultipleImageTask to finish, via its privateCallback
while (privateCallback.inProgress()) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
// Got the addMultipleImageTask stop (cancel)
LOGGER.log(Level.INFO, "AddMultipleImageTask interrupted", ex); // NON-NLS
// now wait for addMultipleImageTask to revert and finally callback
while (privateCallback.inProgress()) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex2) {
LOGGER.log(Level.INFO, "AddMultipleImageTask interrupted 2", ex2); // NON-NLS
}
}
}
}
// TODO: Delete destination directory when 5453 (VHD file is not closed upon revert) is fixed.
// if (cancelled) {
// deleteDestinationDirectory();
// }
if (privateCallback.getResult() == DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS) {
// bait out
callback.done(privateCallback.getResult(), privateCallback.getErrorMessages(), privateCallback.getNewDataSources());
return;
}
}
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingInterestingFiles()); progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingInterestingFiles());
addingInterestingFiles = true;
addInterestingFiles(dest, Paths.get(dest.toString(), resultsFilename), createVHD); addInterestingFiles(dest, Paths.get(dest.toString(), resultsFilename), createVHD);
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingInterestingFiles()); progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingInterestingFiles());
if (addMultipleImageTask != null) { if (addMultipleImageTask != null && privateCallback != null) {
callback.done(addMultipleImageTask.getResult(), addMultipleImageTask.getErrorMessages(), addMultipleImageTask.getNewDataSources()); callback.done(privateCallback.getResult(), privateCallback.getErrorMessages(), privateCallback.getNewDataSources());
} else { } else {
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS, errorList, newDataSources); callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS, errorList, newDataSources);
} }
@ -265,6 +292,17 @@ final class AddLogicalImageTask implements Runnable {
void cancelTask() { void cancelTask() {
LOGGER.log(Level.WARNING, "AddLogicalImageTask cancelled, processing may be incomplete"); // NON-NLS LOGGER.log(Level.WARNING, "AddLogicalImageTask cancelled, processing may be incomplete"); // NON-NLS
cancelled = true; cancelled = true;
if (addMultipleImageTask != null) {
multipleImageThread.interrupt();
addMultipleImageTask.cancelTask();
}
if (!createVHD) {
// Don't delete destination directory once we started adding interesting files.
// At this point the database and destination directory are complete.
if (!addingInterestingFiles) {
deleteDestinationDirectory();
}
}
} }
private Map<String, Long> imagePathsToDataSourceObjId(Map<Long, List<String>> imagePaths) { private Map<String, Long> imagePathsToDataSourceObjId(Map<Long, List<String>> imagePaths) {
@ -429,4 +467,51 @@ final class AddLogicalImageTask implements Runnable {
} }
} }
private boolean deleteDestinationDirectory() {
try {
FileUtils.deleteDirectory(dest);
LOGGER.log(Level.INFO, String.format("Cancellation: Deleted directory %s", dest.toString())); // NON-NLS
return true;
} catch (IOException ex) {
LOGGER.log(Level.WARNING, String.format("Cancellation: Failed to delete directory %s", dest.toString()), ex); // NON-NLS
return false;
}
}
private class AddDataSourceCallback extends DataSourceProcessorCallback {
private List<String> errorMessages;
private List<Content> newDataSources;
private DataSourceProcessorResult result;
private boolean inProgress = true;
@Override
public void done(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errorMessages, List<Content> newDataSources) {
LOGGER.log(Level.INFO, "privateCallback done"); // NON-NLS
this.errorMessages = errorMessages;
this.newDataSources = newDataSources;
this.result = result;
this.inProgress = false;
}
@Override
public void doneEDT(DataSourceProcessorResult result, List<String> errorMessages, List<Content> newDataSources) {
done(result, errorMessages, newDataSources);
}
public List<Content> getNewDataSources() {
return newDataSources;
}
public List<String> getErrorMessages() {
return errorMessages;
}
public DataSourceProcessorResult getResult() {
return result;
}
private boolean inProgress() {
return inProgress;
}
}
} }

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.logicalimager.dsp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import javax.annotation.concurrent.GuardedBy;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -56,13 +57,18 @@ class AddMultipleImageTask implements Runnable {
private final DataSourceProcessorProgressMonitor progressMonitor; private final DataSourceProcessorProgressMonitor progressMonitor;
private final DataSourceProcessorCallback callback; private final DataSourceProcessorCallback callback;
private final Case currentCase; private final Case currentCase;
private boolean criticalErrorOccurred; private boolean criticalErrorOccurred;
private volatile boolean cancelled; private SleuthkitJNI.CaseDbHandle.AddImageProcess addImageProcess = null;
private List<Content> newDataSources; /*
private List<String> errorMessages; * The cancellation requested flag and SleuthKit add image process are
private DataSourceProcessorResult result; * guarded by a lock to synchronize cancelling the process (setting the flag
* and calling its stop method) and calling either its commit or revert
* method.
*/
private final Object tskAddImageProcessLock;
@GuardedBy("tskAddImageProcessLock")
private boolean tskAddImageProcessStopped;
/** /**
* Constructs a runnable that adds multiple image files to a case database. * Constructs a runnable that adds multiple image files to a case database.
@ -95,36 +101,53 @@ class AddMultipleImageTask implements Runnable {
this.progressMonitor = progressMonitor; this.progressMonitor = progressMonitor;
currentCase = Case.getCurrentCaseThrows(); currentCase = Case.getCurrentCaseThrows();
this.criticalErrorOccurred = false; this.criticalErrorOccurred = false;
this.result = DataSourceProcessorResult.NO_ERRORS; tskAddImageProcessLock = new Object();
} }
@Messages({
"AddMultipleImageTask.cancelled=Cancellation: Add image process reverted",
})
@Override @Override
public void run() { public void run() {
newDataSources = new ArrayList<>(); List<String> errorMessages = new ArrayList<>();
errorMessages = new ArrayList<>(); List<Content> newDataSources = new ArrayList<>();
List<Content> emptyDataSources = new ArrayList<>();
/* /*
* Try to add the input image files as images. * Try to add the input image files as images.
*/ */
List<String> corruptedImageFilePaths = new ArrayList<>(); List<String> corruptedImageFilePaths = new ArrayList<>();
currentCase.getSleuthkitCase().acquireSingleUserCaseWriteLock();
try { try {
currentCase.getSleuthkitCase().acquireSingleUserCaseWriteLock();
progressMonitor.setIndeterminate(true); progressMonitor.setIndeterminate(true);
for (String imageFilePath : imageFilePaths) { for (String imageFilePath : imageFilePaths) {
if (!cancelled) { synchronized (tskAddImageProcessLock) {
addImageToCase(imageFilePath, newDataSources, corruptedImageFilePaths, errorMessages); if (!tskAddImageProcessStopped) {
addImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, false, false, "");
} else {
return;
}
}
run(imageFilePath, corruptedImageFilePaths, errorMessages);
commitOrRevertAddImageProcess(imageFilePath, errorMessages, newDataSources);
synchronized (tskAddImageProcessLock) {
if (tskAddImageProcessStopped) {
errorMessages.add(Bundle.AddMultipleImageTask_cancelled());
callback.done(DataSourceProcessorResult.CRITICAL_ERRORS, errorMessages, emptyDataSources);
return;
}
} }
} }
} finally { } finally {
currentCase.getSleuthkitCase().releaseSingleUserCaseWriteLock(); currentCase.getSleuthkitCase().releaseSingleUserCaseWriteLock();
} }
/* /*
* Try to add any input image files that did not have file systems as a * Try to add any input image files that did not have file systems as a
* single an unallocated space file with the device id as the root virtual * single an unallocated space file with the device id as the root virtual
* directory name. * directory name.
*/ */
if (!cancelled && !corruptedImageFilePaths.isEmpty()) { if (!tskAddImageProcessStopped && !corruptedImageFilePaths.isEmpty()) {
SleuthkitCase caseDatabase; SleuthkitCase caseDatabase;
caseDatabase = currentCase.getSleuthkitCase(); caseDatabase = currentCase.getSleuthkitCase();
try { try {
@ -146,7 +169,6 @@ class AddMultipleImageTask implements Runnable {
start += TWO_GB; start += TWO_GB;
sequence++; sequence++;
} }
} }
double leftoverSize = imageSize - sequence * TWO_GB; double leftoverSize = imageSize - sequence * TWO_GB;
fileRanges.add(new TskFileRange(start, (long)leftoverSize, sequence)); fileRanges.add(new TskFileRange(start, (long)leftoverSize, sequence));
@ -170,6 +192,7 @@ class AddMultipleImageTask implements Runnable {
/* /*
* Pass the results back via the callback. * Pass the results back via the callback.
*/ */
DataSourceProcessorCallback.DataSourceProcessorResult result;
if (criticalErrorOccurred) { if (criticalErrorOccurred) {
result = DataSourceProcessorResult.CRITICAL_ERRORS; result = DataSourceProcessorResult.CRITICAL_ERRORS;
} else if (!errorMessages.isEmpty()) { } else if (!errorMessages.isEmpty()) {
@ -177,6 +200,7 @@ class AddMultipleImageTask implements Runnable {
} else { } else {
result = DataSourceProcessorResult.NO_ERRORS; result = DataSourceProcessorResult.NO_ERRORS;
} }
callback.done(result, errorMessages, newDataSources);
} }
/** /**
@ -185,16 +209,30 @@ class AddMultipleImageTask implements Runnable {
*/ */
void cancelTask() { void cancelTask() {
LOGGER.log(Level.WARNING, "AddMultipleImageTask cancelled, processing may be incomplete"); // NON-NLS LOGGER.log(Level.WARNING, "AddMultipleImageTask cancelled, processing may be incomplete"); // NON-NLS
cancelled = true; synchronized (tskAddImageProcessLock) {
tskAddImageProcessStopped = true;
if (addImageProcess != null) {
try {
/*
* All this does is set a flag that will make the TSK add
* image process exit when the flag is checked between
* processing steps. The state of the flag is not
* accessible, so record it here so that it is known that
* the revert method of the process object needs to be
* called.
*/
addImageProcess.stop();
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Cancellation: addImagePRocess.stop failed", ex); // NON-NLS
}
}
}
} }
/** /**
* Attempts to add an input image to the case. * Attempts to add an input image to the case.
* *
* @param imageFilePath The image file path. * @param imageFilePath The image file path.
* @param newDataSources If the image is added, a data source is
* added to this list for eventual return to
* the caller via the callback.
* @param corruptedImageFilePaths If the image cannot be added because * @param corruptedImageFilePaths If the image cannot be added because
* Sleuth Kit cannot detect a filesystem, * Sleuth Kit cannot detect a filesystem,
* the image file path is added to this list * the image file path is added to this list
@ -209,13 +247,11 @@ class AddMultipleImageTask implements Runnable {
"# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImageTask.criticalErrorAdding=Critical error adding {0} for device {1}: {2}", "# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImageTask.criticalErrorAdding=Critical error adding {0} for device {1}: {2}",
"# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImageTask.criticalErrorReverting=Critical error reverting add image process for {0} for device {1}: {2}", "# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImageTask.criticalErrorReverting=Critical error reverting add image process for {0} for device {1}: {2}",
"# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImageTask.nonCriticalErrorAdding=Non-critical error adding {0} for device {1}: {2}",}) "# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImageTask.nonCriticalErrorAdding=Non-critical error adding {0} for device {1}: {2}",})
private void addImageToCase(String imageFilePath, List<Content> newDataSources, List<String> corruptedImageFilePaths, List<String> errorMessages) { private void run(String imageFilePath, List<String> corruptedImageFilePaths, List<String> errorMessages) {
/* /*
* Try to add the image to the case database as a data source. * Try to add the image to the case database as a data source.
*/ */
progressMonitor.setProgressText(Bundle.AddMultipleImageTask_adding(imageFilePath)); progressMonitor.setProgressText(Bundle.AddMultipleImageTask_adding(imageFilePath));
SleuthkitCase caseDatabase = currentCase.getSleuthkitCase();
SleuthkitJNI.CaseDbHandle.AddImageProcess addImageProcess = caseDatabase.makeAddImageProcess(timeZone, false, false, "");
try { try {
addImageProcess.run(deviceId, new String[]{imageFilePath}); addImageProcess.run(deviceId, new String[]{imageFilePath});
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
@ -231,59 +267,64 @@ class AddMultipleImageTask implements Runnable {
errorMessages.add(Bundle.AddMultipleImageTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage())); errorMessages.add(Bundle.AddMultipleImageTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
criticalErrorOccurred = true; criticalErrorOccurred = true;
} }
/*
* Either way, the add image process needs to be reverted.
*/
try {
addImageProcess.revert();
} catch (TskCoreException e) {
errorMessages.add(Bundle.AddMultipleImageTask_criticalErrorReverting(imageFilePath, deviceId, e.getLocalizedMessage()));
criticalErrorOccurred = true;
}
return;
} catch (TskDataException ex) { } catch (TskDataException ex) {
errorMessages.add(Bundle.AddMultipleImageTask_nonCriticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage())); errorMessages.add(Bundle.AddMultipleImageTask_nonCriticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
} }
}
/*
* Try to commit the results of the add image process, retrieve the new /**
* image from the case database, and add it to the list of new data * Commits or reverts the results of the TSK add image process. If the
* sources to be returned via the callback. * process was stopped before it completed or there was a critical error the
*/ * results are reverted, otherwise they are committed.
try { *
long imageId = addImageProcess.commit(); * @param imageFilePath The image file path.
Image dataSource = caseDatabase.getImageById(imageId); * @param errorMessages Error messages, if any, are added to this list for
newDataSources.add(dataSource); * eventual return via the callback.
* @param newDataSources If the new image is successfully committed, it is
/* * added to this list for eventual return via the
* Verify the size of the new image. Note that it may not be what is * callback.
* expected, but at least part of it was added to the case. */
*/ private void commitOrRevertAddImageProcess(String imageFilePath, List<String> errorMessages, List<Content> newDataSources) {
String verificationError = dataSource.verifyImageSize(); synchronized (tskAddImageProcessLock) {
if (!verificationError.isEmpty()) { if (tskAddImageProcessStopped || criticalErrorOccurred) {
errorMessages.add(Bundle.AddMultipleImageTask_nonCriticalErrorAdding(imageFilePath, deviceId, verificationError)); try {
addImageProcess.revert();
} catch (TskCoreException ex) {
errorMessages.add(Bundle.AddMultipleImageTask_criticalErrorReverting(imageFilePath, deviceId, ex.getLocalizedMessage()));
criticalErrorOccurred = true;
}
return;
}
if (!tskAddImageProcessStopped) {
/*
* Try to commit the results of the add image process, retrieve the new
* image from the case database, and add it to the list of new data
* sources to be returned via the callback.
*/
try {
long imageId = addImageProcess.commit();
Image dataSource = currentCase.getSleuthkitCase().getImageById(imageId);
newDataSources.add(dataSource);
/*
* Verify the size of the new image. Note that it may not be what is
* expected, but at least part of it was added to the case.
*/
String verificationError = dataSource.verifyImageSize();
if (!verificationError.isEmpty()) {
errorMessages.add(Bundle.AddMultipleImageTask_nonCriticalErrorAdding(imageFilePath, deviceId, verificationError));
}
} catch (TskCoreException ex) {
/*
* The add image process commit failed or querying the case database
* for the newly added image failed. Either way, this is a critical
* error.
*/
errorMessages.add(Bundle.AddMultipleImageTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
criticalErrorOccurred = true;
}
} }
} catch (TskCoreException ex) {
/*
* The add image process commit failed or querying the case database
* for the newly added image failed. Either way, this is a critical
* error.
*/
errorMessages.add(Bundle.AddMultipleImageTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
criticalErrorOccurred = true;
} }
} }
public List<Content> getNewDataSources() {
return newDataSources;
}
public List<String> getErrorMessages() {
return errorMessages;
}
public DataSourceProcessorResult getResult() {
return result;
}
} }

View File

@ -25,8 +25,9 @@ import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.swing.JOptionPane;
import static javax.swing.JOptionPane.YES_OPTION;
import javax.swing.JPanel; import javax.swing.JPanel;
import org.apache.commons.io.FileUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders; import org.openide.util.lookup.ServiceProviders;
@ -51,6 +52,7 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
private static final String LOGICAL_IMAGER_DIR = "LogicalImager"; //NON-NLS private static final String LOGICAL_IMAGER_DIR = "LogicalImager"; //NON-NLS
private final LogicalImagerPanel configPanel; private final LogicalImagerPanel configPanel;
private AddLogicalImageTask addLogicalImageTask; private AddLogicalImageTask addLogicalImageTask;
private Thread thread;
/* /*
* Constructs a Logical Imager data source processor that implements the * Constructs a Logical Imager data source processor that implements the
@ -163,10 +165,20 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
File dest = Paths.get(logicalImagerDir.toString(), imageDirPath.getFileName().toString()).toFile(); File dest = Paths.get(logicalImagerDir.toString(), imageDirPath.getFileName().toString()).toFile();
if (dest.exists()) { if (dest.exists()) {
// Destination directory already exists // Destination directory already exists
String msg = Bundle.LogicalImagerDSProcessor_directoryAlreadyExists(dest.toString()); int showConfirmDialog = JOptionPane.showConfirmDialog(configPanel,
errorList.add(msg); String.format("The logical imager folder %s already exists,\ndo you want to add it again using a new folder name?", dest.toString()),
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); "Destination directory confirmation",
return; JOptionPane.YES_NO_OPTION);
if (showConfirmDialog == YES_OPTION) {
// Get unique dest directory
String uniqueDirectory = imageDirPath.getFileName() + "_" + UUID.randomUUID();
dest = Paths.get(logicalImagerDir.toString(), uniqueDirectory).toFile();
} else {
String msg = Bundle.LogicalImagerDSProcessor_directoryAlreadyExists(dest.toString());
errorList.add(msg);
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
return;
}
} }
File src = imageDirPath.toFile(); File src = imageDirPath.toFile();
@ -207,12 +219,14 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
) throws NoCurrentCaseException { ) throws NoCurrentCaseException {
addLogicalImageTask = new AddLogicalImageTask(deviceId, timeZone, src, dest, addLogicalImageTask = new AddLogicalImageTask(deviceId, timeZone, src, dest,
progressMonitor, callback); progressMonitor, callback);
new Thread(addLogicalImageTask).start(); thread = new Thread(addLogicalImageTask);
thread.start();
} }
@Override @Override
public void cancel() { public void cancel() {
if (addLogicalImageTask != null) { if (addLogicalImageTask != null) {
thread.interrupt();
addLogicalImageTask.cancelTask(); addLogicalImageTask.cancelTask();
} }
} }