Autopsy  4.15.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.Content;
34 import org.sleuthkit.datamodel.Image;
35 import org.sleuthkit.datamodel.SleuthkitJNI;
36 import org.sleuthkit.datamodel.TskCoreException;
37 import org.sleuthkit.datamodel.TskDataException;
38 
39 /*
40  * A runnable that adds an image data source to the case database.
41  */
42 class AddImageTask implements Runnable {
43 
44  private final Logger logger = Logger.getLogger(AddImageTask.class.getName());
45  private final String deviceId;
46  private final String imagePath;
47  private final int sectorSize;
48  private final String timeZone;
49  private final ImageWriterSettings imageWriterSettings;
50  private final boolean ignoreFatOrphanFiles;
51  private final String md5;
52  private final String sha1;
53  private final String sha256;
54  private final DataSourceProcessorProgressMonitor progressMonitor;
55  private final DataSourceProcessorCallback callback;
56  private boolean criticalErrorOccurred;
57 
58  /*
59  * The cancellation requested flag and SleuthKit add image process are
60  * guarded by a monitor (called a lock here to avoid confusion with the
61  * progress monitor) to synchronize cancelling the process (setting the flag
62  * and calling its stop method) and calling either its commit or revert
63  * method. The built-in monitor of the add image process can't be used for
64  * this because it is already used to synchronize its run (init part),
65  * commit, revert, and currentDirectory methods.
66  *
67  * TODO (AUT-2021): Merge SleuthkitJNI.AddImageProcess and AddImageTask
68  */
69  private final Object tskAddImageProcessLock;
70  @GuardedBy("tskAddImageProcessLock")
71  private boolean tskAddImageProcessStopped;
72  private SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess;
73 
98  AddImageTask(String deviceId, String imagePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, ImageWriterSettings imageWriterSettings,
99  DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
100  this.deviceId = deviceId;
101  this.imagePath = imagePath;
102  this.sectorSize = sectorSize;
103  this.timeZone = timeZone;
104  this.ignoreFatOrphanFiles = ignoreFatOrphanFiles;
105  this.md5 = md5;
106  this.sha1 = sha1;
107  this.sha256 = sha256;
108  this.imageWriterSettings = imageWriterSettings;
109  this.callback = callback;
110  this.progressMonitor = progressMonitor;
111  tskAddImageProcessLock = new Object();
112  }
113 
117  @Override
118  public void run() {
119  Case currentCase;
120  try {
121  currentCase = Case.getCurrentCaseThrows();
122  } catch (NoCurrentCaseException ex) {
123  logger.log(Level.SEVERE, String.format("Failed to add image data source at %s, no current case", imagePath), ex);
124  return;
125  }
126  progressMonitor.setIndeterminate(true);
127  progressMonitor.setProgress(0);
128  String imageWriterPath = "";
129  if (imageWriterSettings != null) {
130  imageWriterPath = imageWriterSettings.getPath();
131  }
132  List<String> errorMessages = new ArrayList<>();
133  List<Content> newDataSources = new ArrayList<>();
134  try {
135  synchronized (tskAddImageProcessLock) {
136  if (!tskAddImageProcessStopped) {
137  tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, true, ignoreFatOrphanFiles, imageWriterPath);
138  } else {
139  return;
140  }
141  }
142  Thread progressUpdateThread = new Thread(new ProgressUpdater(progressMonitor, tskAddImageProcess));
143  progressUpdateThread.start();
144  runAddImageProcess(errorMessages);
145  progressUpdateThread.interrupt();
146  commitOrRevertAddImageProcess(currentCase, errorMessages, newDataSources);
147  progressMonitor.setProgress(100);
148  } finally {
149  DataSourceProcessorCallback.DataSourceProcessorResult result;
150  if (criticalErrorOccurred) {
151  result = DataSourceProcessorResult.CRITICAL_ERRORS;
152  } else if (!errorMessages.isEmpty()) {
153  result = DataSourceProcessorResult.NONCRITICAL_ERRORS;
154  } else {
155  result = DataSourceProcessorResult.NO_ERRORS;
156  }
157  callback.done(result, errorMessages, newDataSources);
158  }
159  }
160 
161  /*
162  * Attempts to cancel adding the image to the case database.
163  */
164  public void cancelTask() {
165  synchronized (tskAddImageProcessLock) {
166  tskAddImageProcessStopped = true;
167  if (null != tskAddImageProcess) {
168  try {
169  /*
170  * All this does is set a flag that will make the TSK add
171  * image process exit when the flag is checked between
172  * processing steps. The state of the flag is not
173  * accessible, so record it here so that it is known that
174  * the revert method of the process object needs to be
175  * called.
176  */
177  tskAddImageProcess.stop();
178 
179  } catch (TskCoreException ex) {
180  logger.log(Level.SEVERE, String.format("Error cancelling adding image %s to the case database", imagePath), ex); //NON-NLS
181  }
182  }
183  }
184  }
185 
192  private void runAddImageProcess(List<String> errorMessages) {
193  try {
194  tskAddImageProcess.run(deviceId, new String[]{imagePath}, sectorSize);
195  } catch (TskCoreException ex) {
196  logger.log(Level.SEVERE, String.format("Critical error occurred adding image %s", imagePath), ex); //NON-NLS
197  criticalErrorOccurred = true;
198  errorMessages.add(ex.getMessage());
199  } catch (TskDataException ex) {
200  logger.log(Level.WARNING, String.format("Non-critical error occurred adding image %s", imagePath), ex); //NON-NLS
201  errorMessages.add(ex.getMessage());
202  }
203  }
204 
219  private void commitOrRevertAddImageProcess(Case currentCase, List<String> errorMessages, List<Content> newDataSources) {
220  synchronized (tskAddImageProcessLock) {
221  if (tskAddImageProcessStopped || criticalErrorOccurred) {
222  try {
223  tskAddImageProcess.revert();
224  } catch (TskCoreException ex) {
225  logger.log(Level.SEVERE, String.format("Error reverting after adding image %s to the case database", imagePath), ex); //NON-NLS
226  errorMessages.add(ex.getMessage());
227  criticalErrorOccurred = true;
228  }
229  } else {
230  try {
231  long imageId = tskAddImageProcess.commit();
232  if (imageId != 0) {
233  Image newImage = currentCase.getSleuthkitCase().getImageById(imageId);
234  String verificationError = newImage.verifyImageSize();
235  if (!verificationError.isEmpty()) {
236  errorMessages.add(verificationError);
237  }
238  if (imageWriterSettings != null) {
239  ImageWriterService.createImageWriter(imageId, imageWriterSettings);
240  }
241  newDataSources.add(newImage);
242  if (!StringUtils.isBlank(md5)) {
243  try {
244  newImage.setMD5(md5);
245  } catch (TskCoreException ex) {
246  logger.log(Level.SEVERE, String.format("Failed to add MD5 hash for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex);
247  errorMessages.add(ex.getMessage());
248  criticalErrorOccurred = true;
249  } catch (TskDataException ignored) {
250  /*
251  * The only reasonable way for this to happen at
252  * present is through C/C++ processing of an EWF
253  * image, which is not an error.
254  */
255  }
256  }
257  if (!StringUtils.isBlank(sha1)) {
258  try {
259  newImage.setSha1(sha1);
260  } catch (TskCoreException ex) {
261  logger.log(Level.SEVERE, String.format("Failed to add SHA1 hash for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex);
262  errorMessages.add(ex.getMessage());
263  criticalErrorOccurred = true;
264  } catch (TskDataException ignored) {
265  /*
266  * The only reasonable way for this to happen at
267  * present is through C/C++ processing of an EWF
268  * image, which is not an error.
269  */
270  }
271  }
272  if (!StringUtils.isBlank(sha256)) {
273  try {
274  newImage.setSha256(sha256);
275  } catch (TskCoreException ex) {
276  logger.log(Level.SEVERE, String.format("Failed to add SHA256 for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex);
277  errorMessages.add(ex.getMessage());
278  criticalErrorOccurred = true;
279  } catch (TskDataException ignored) {
280  /*
281  * The only reasonable way for this to happen at
282  * present is through C/C++ processing of an EWF
283  * image, which is not an error.
284  */
285  }
286  }
287  } else {
288  String errorMessage = String.format("Error commiting after adding image %s to the case database, no object id returned", imagePath); //NON-NLS
289  logger.log(Level.SEVERE, errorMessage);
290  errorMessages.add(errorMessage);
291  criticalErrorOccurred = true;
292  }
293  } catch (TskCoreException ex) {
294  logger.log(Level.SEVERE, String.format("Error committing adding image %s to the case database", imagePath), ex); //NON-NLS
295  errorMessages.add(ex.getMessage());
296  criticalErrorOccurred = true;
297  }
298  }
299  }
300  }
301 
306  private class ProgressUpdater implements Runnable {
307 
309  private final SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess;
310 
318  ProgressUpdater(DataSourceProcessorProgressMonitor progressMonitor, SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess) {
319  this.progressMonitor = progressMonitor;
321  }
322 
327  @Override
328  public void run() {
329  try {
330  while (!Thread.currentThread().isInterrupted()) {
331  String currDir = tskAddImageProcess.currentDirectory();
332  if (currDir != null) {
333  if (!currDir.isEmpty()) {
334  progressMonitor.setProgressText(
335  NbBundle.getMessage(this.getClass(), "AddImageTask.run.progress.adding",
336  currDir));
337  }
338  }
339  /*
340  * The sleep here throttles the UI updates and provides a
341  * non-standard mechanism for completing this task by
342  * interrupting the thread in which it is running.
343  *
344  * TODO (AUT-1870): Replace this with giving the task to a
345  * java.util.concurrent.ScheduledThreadPoolExecutor that is
346  * shut down when the main task completes.
347  */
348  Thread.sleep(500);
349  }
350  } catch (InterruptedException expected) {
351  }
352  }
353  }
354 
355 }
final SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess
final DataSourceProcessorProgressMonitor progressMonitor

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