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

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.