Autopsy  4.16.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
AddImageTask.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-2018 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.autopsy.casemodule;
20 
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.logging.Level;
24 import javax.annotation.concurrent.GuardedBy;
25 import org.apache.commons.lang3.StringUtils;
26 import org.openide.util.NbBundle;
33 import org.sleuthkit.datamodel.AddDataSourceCallbacks;
34 import org.sleuthkit.datamodel.Content;
35 import org.sleuthkit.datamodel.Image;
36 import org.sleuthkit.datamodel.SleuthkitJNI;
37 import org.sleuthkit.datamodel.TskCoreException;
38 import org.sleuthkit.datamodel.TskDataException;
39 
40 /*
41  * A runnable that adds an image data source to the case database.
42  */
43 class AddImageTask implements Runnable {
44 
45  private final Logger logger = Logger.getLogger(AddImageTask.class.getName());
46  private final ImageDetails imageDetails;
47  private final DataSourceProcessorProgressMonitor progressMonitor;
48  private final AddDataSourceCallbacks addDataSourceCallbacks;
49  private final AddImageTaskCallback addImageTaskCallback;
50  private boolean criticalErrorOccurred;
51 
52  /*
53  * The cancellation requested flag and SleuthKit add image process are
54  * guarded by a monitor (called a lock here to avoid confusion with the
55  * progress monitor) to synchronize cancelling the process (setting the flag
56  * and calling its stop method) and calling either its commit or revert
57  * method. The built-in monitor of the add image process can't be used for
58  * this because it is already used to synchronize its run (init part),
59  * commit, revert, and currentDirectory methods.
60  *
61  * TODO (AUT-2021): Merge SleuthkitJNI.AddImageProcess and AddImageTask
62  */
63  private final Object tskAddImageProcessLock;
64  @GuardedBy("tskAddImageProcessLock")
65  private boolean tskAddImageProcessStopped;
66  private SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess;
67 
77  AddImageTask(ImageDetails imageDetails, DataSourceProcessorProgressMonitor progressMonitor, AddDataSourceCallbacks addDataSourceCallbacks,
78  AddImageTaskCallback addImageTaskCallback) {
79  this.imageDetails = imageDetails;
80  this.addDataSourceCallbacks = addDataSourceCallbacks;
81  this.addImageTaskCallback = addImageTaskCallback;
82  this.progressMonitor = progressMonitor;
83  tskAddImageProcessLock = new Object();
84  }
85 
89  @Override
90  public void run() {
91  Case currentCase;
92  try {
93  currentCase = Case.getCurrentCaseThrows();
94  } catch (NoCurrentCaseException ex) {
95  logger.log(Level.SEVERE, String.format("Failed to start AddImageTask for %s, no current case", imageDetails.getImagePath()), ex);
96  return;
97  }
98  progressMonitor.setIndeterminate(true);
99  progressMonitor.setProgress(0);
100  String imageWriterPath = "";
101  if (imageDetails.imageWriterSettings != null) {
102  imageWriterPath = imageDetails.imageWriterSettings.getPath();
103  }
104  List<String> errorMessages = new ArrayList<>();
105  List<Content> newDataSources = new ArrayList<>();
106  try {
107  synchronized (tskAddImageProcessLock) {
108  if (!tskAddImageProcessStopped) {
109  tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(imageDetails.timeZone, true, imageDetails.ignoreFatOrphanFiles, imageWriterPath);
110  } else {
111  return;
112  }
113  }
114  Thread progressUpdateThread = new Thread(new ProgressUpdater(progressMonitor, tskAddImageProcess));
115  progressUpdateThread.start();
116  runAddImageProcess(errorMessages);
117  progressUpdateThread.interrupt();
118  finishAddImageProcess(errorMessages, newDataSources);
119  progressMonitor.setProgress(100);
120  } finally {
121  DataSourceProcessorCallback.DataSourceProcessorResult result;
122  if (criticalErrorOccurred) {
123  result = DataSourceProcessorResult.CRITICAL_ERRORS;
124  } else if (!errorMessages.isEmpty()) {
125  result = DataSourceProcessorResult.NONCRITICAL_ERRORS;
126  } else {
127  result = DataSourceProcessorResult.NO_ERRORS;
128  }
129  addImageTaskCallback.onCompleted(result, errorMessages, newDataSources);
130  }
131  }
132 
133  /*
134  * Attempts to cancel adding the image to the case database.
135  */
136  public void cancelTask() {
137  synchronized (tskAddImageProcessLock) {
138  tskAddImageProcessStopped = true;
139  if (null != tskAddImageProcess) {
140  try {
141  /*
142  * All this does is set a flag that will make the TSK add
143  * image process exit when the flag is checked between
144  * processing steps. The state of the flag is not
145  * accessible, so record it here so that it is known that
146  * the revert method of the process object needs to be
147  * called.
148  */
149  tskAddImageProcess.stop();
150 
151  } catch (TskCoreException ex) {
152  logger.log(Level.SEVERE, String.format("Error cancelling adding image %s to the case database", imageDetails.getImagePath()), ex); //NON-NLS
153  }
154  }
155  }
156  }
157 
164  private void runAddImageProcess(List<String> errorMessages) {
165  try {
166  tskAddImageProcess.run(imageDetails.deviceId, imageDetails.image, imageDetails.sectorSize, this.addDataSourceCallbacks);
167  } catch (TskCoreException ex) {
168  logger.log(Level.SEVERE, String.format("Critical error occurred adding image %s", imageDetails.getImagePath()), ex); //NON-NLS
169  criticalErrorOccurred = true;
170  errorMessages.add(ex.getMessage());
171  } catch (TskDataException ex) {
172  logger.log(Level.WARNING, String.format("Non-critical error occurred adding image %s", imageDetails.getImagePath()), ex); //NON-NLS
173  errorMessages.add(ex.getMessage());
174  }
175  }
176 
190  private void finishAddImageProcess(List<String> errorMessages, List<Content> newDataSources) {
191  synchronized (tskAddImageProcessLock) {
192  Image newImage = imageDetails.image;
193  String verificationError = newImage.verifyImageSize();
194  if (!verificationError.isEmpty()) {
195  errorMessages.add(verificationError);
196  }
197  if (imageDetails.imageWriterSettings != null) {
198  ImageWriterService.createImageWriter(newImage.getId(), imageDetails.imageWriterSettings);
199  }
200  newDataSources.add(newImage);
201 
202  // If the add image process was cancelled don't do any further processing here
203  if (tskAddImageProcessStopped) {
204  return;
205  }
206 
207  if (!StringUtils.isBlank(imageDetails.md5)) {
208  try {
209  newImage.setMD5(imageDetails.md5);
210  } catch (TskCoreException ex) {
211  logger.log(Level.SEVERE, String.format("Failed to add MD5 hash for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex);
212  errorMessages.add(ex.getMessage());
213  criticalErrorOccurred = true;
214  } catch (TskDataException ignored) {
215  /*
216  * The only reasonable way for this to happen at
217  * present is through C/C++ processing of an EWF
218  * image, which is not an error.
219  */
220  }
221  }
222  if (!StringUtils.isBlank(imageDetails.sha1)) {
223  try {
224  newImage.setSha1(imageDetails.sha1);
225  } catch (TskCoreException ex) {
226  logger.log(Level.SEVERE, String.format("Failed to add SHA1 hash for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex);
227  errorMessages.add(ex.getMessage());
228  criticalErrorOccurred = true;
229  } catch (TskDataException ignored) {
230  /*
231  * The only reasonable way for this to happen at
232  * present is through C/C++ processing of an EWF
233  * image, which is not an error.
234  */
235  }
236  }
237  if (!StringUtils.isBlank(imageDetails.sha256)) {
238  try {
239  newImage.setSha256(imageDetails.sha256);
240  } catch (TskCoreException ex) {
241  logger.log(Level.SEVERE, String.format("Failed to add SHA256 for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex);
242  errorMessages.add(ex.getMessage());
243  criticalErrorOccurred = true;
244  } catch (TskDataException ignored) {
245  /*
246  * The only reasonable way for this to happen at
247  * present is through C/C++ processing of an EWF
248  * image, which is not an error.
249  */
250  }
251  }
252  }
253  }
254 
259  private class ProgressUpdater implements Runnable {
260 
262  private final SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess;
263 
271  ProgressUpdater(DataSourceProcessorProgressMonitor progressMonitor, SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess) {
272  this.progressMonitor = progressMonitor;
274  }
275 
280  @Override
281  public void run() {
282  try {
283  while (!Thread.currentThread().isInterrupted()) {
284  String currDir = tskAddImageProcess.currentDirectory();
285  if (currDir != null) {
286  if (!currDir.isEmpty()) {
287  progressMonitor.setProgressText(
288  NbBundle.getMessage(this.getClass(), "AddImageTask.run.progress.adding",
289  currDir));
290  }
291  }
292  /*
293  * The sleep here throttles the UI updates and provides a
294  * non-standard mechanism for completing this task by
295  * interrupting the thread in which it is running.
296  *
297  * TODO (AUT-1870): Replace this with giving the task to a
298  * java.util.concurrent.ScheduledThreadPoolExecutor that is
299  * shut down when the main task completes.
300  */
301  Thread.sleep(500);
302  }
303  } catch (InterruptedException expected) {
304  }
305  }
306  }
307 
311  static class ImageDetails {
312  String deviceId;
313  Image image;
314  int sectorSize;
315  String timeZone;
316  boolean ignoreFatOrphanFiles;
317  String md5;
318  String sha1;
319  String sha256;
320  ImageWriterSettings imageWriterSettings;
321 
322  ImageDetails(String deviceId, Image image, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, ImageWriterSettings imageWriterSettings) {
323  this.deviceId = deviceId;
324  this.image = image;
325  this.sectorSize = sectorSize;
326  this.timeZone = timeZone;
327  this.ignoreFatOrphanFiles = ignoreFatOrphanFiles;
328  this.md5 = md5;
329  this.sha1 = sha1;
330  this.sha256 = sha256;
331  this.imageWriterSettings = imageWriterSettings;
332  }
333 
334  String getImagePath() {
335  if (image.getPaths().length > 0) {
336  return image.getPaths()[0];
337  }
338  return "Unknown data source path";
339  }
340  }
341 }
final SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess
final DataSourceProcessorProgressMonitor progressMonitor

Copyright © 2012-2020 Basis Technology. Generated on: Tue Sep 22 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.