Autopsy  4.14.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  currentCase.getSleuthkitCase().acquireSingleUserCaseWriteLock();
136  synchronized (tskAddImageProcessLock) {
137  if (!tskAddImageProcessStopped) {
138  tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, true, ignoreFatOrphanFiles, imageWriterPath);
139  } else {
140  return;
141  }
142  }
143  Thread progressUpdateThread = new Thread(new ProgressUpdater(progressMonitor, tskAddImageProcess));
144  progressUpdateThread.start();
145  runAddImageProcess(errorMessages);
146  progressUpdateThread.interrupt();
147  commitOrRevertAddImageProcess(currentCase, errorMessages, newDataSources);
148  progressMonitor.setProgress(100);
149  } finally {
150  currentCase.getSleuthkitCase().releaseSingleUserCaseWriteLock();
151  DataSourceProcessorCallback.DataSourceProcessorResult result;
152  if (criticalErrorOccurred) {
153  result = DataSourceProcessorResult.CRITICAL_ERRORS;
154  } else if (!errorMessages.isEmpty()) {
155  result = DataSourceProcessorResult.NONCRITICAL_ERRORS;
156  } else {
157  result = DataSourceProcessorResult.NO_ERRORS;
158  }
159  callback.done(result, errorMessages, newDataSources);
160  }
161  }
162 
163  /*
164  * Attempts to cancel adding the image to the case database.
165  */
166  public void cancelTask() {
167  synchronized (tskAddImageProcessLock) {
168  tskAddImageProcessStopped = true;
169  if (null != tskAddImageProcess) {
170  try {
171  /*
172  * All this does is set a flag that will make the TSK add
173  * image process exit when the flag is checked between
174  * processing steps. The state of the flag is not
175  * accessible, so record it here so that it is known that
176  * the revert method of the process object needs to be
177  * called.
178  */
179  tskAddImageProcess.stop();
180 
181  } catch (TskCoreException ex) {
182  logger.log(Level.SEVERE, String.format("Error cancelling adding image %s to the case database", imagePath), ex); //NON-NLS
183  }
184  }
185  }
186  }
187 
194  private void runAddImageProcess(List<String> errorMessages) {
195  try {
196  tskAddImageProcess.run(deviceId, new String[]{imagePath}, sectorSize);
197  } catch (TskCoreException ex) {
198  logger.log(Level.SEVERE, String.format("Critical error occurred adding image %s", imagePath), ex); //NON-NLS
199  criticalErrorOccurred = true;
200  errorMessages.add(ex.getMessage());
201  } catch (TskDataException ex) {
202  logger.log(Level.WARNING, String.format("Non-critical error occurred adding image %s", imagePath), ex); //NON-NLS
203  errorMessages.add(ex.getMessage());
204  }
205  }
206 
221  private void commitOrRevertAddImageProcess(Case currentCase, List<String> errorMessages, List<Content> newDataSources) {
222  synchronized (tskAddImageProcessLock) {
223  if (tskAddImageProcessStopped || criticalErrorOccurred) {
224  try {
225  tskAddImageProcess.revert();
226  } catch (TskCoreException ex) {
227  logger.log(Level.SEVERE, String.format("Error reverting after adding image %s to the case database", imagePath), ex); //NON-NLS
228  errorMessages.add(ex.getMessage());
229  criticalErrorOccurred = true;
230  }
231  } else {
232  try {
233  long imageId = tskAddImageProcess.commit();
234  if (imageId != 0) {
235  Image newImage = currentCase.getSleuthkitCase().getImageById(imageId);
236  String verificationError = newImage.verifyImageSize();
237  if (!verificationError.isEmpty()) {
238  errorMessages.add(verificationError);
239  }
240  if (imageWriterSettings != null) {
241  ImageWriterService.createImageWriter(imageId, imageWriterSettings);
242  }
243  newDataSources.add(newImage);
244  if (!StringUtils.isBlank(md5)) {
245  try {
246  newImage.setMD5(md5);
247  } catch (TskCoreException ex) {
248  logger.log(Level.SEVERE, String.format("Failed to add MD5 hash for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex);
249  errorMessages.add(ex.getMessage());
250  criticalErrorOccurred = true;
251  } catch (TskDataException ignored) {
252  /*
253  * The only reasonable way for this to happen at
254  * present is through C/C++ processing of an EWF
255  * image, which is not an error.
256  */
257  }
258  }
259  if (!StringUtils.isBlank(sha1)) {
260  try {
261  newImage.setSha1(sha1);
262  } catch (TskCoreException ex) {
263  logger.log(Level.SEVERE, String.format("Failed to add SHA1 hash for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex);
264  errorMessages.add(ex.getMessage());
265  criticalErrorOccurred = true;
266  } catch (TskDataException ignored) {
267  /*
268  * The only reasonable way for this to happen at
269  * present is through C/C++ processing of an EWF
270  * image, which is not an error.
271  */
272  }
273  }
274  if (!StringUtils.isBlank(sha256)) {
275  try {
276  newImage.setSha256(sha256);
277  } catch (TskCoreException ex) {
278  logger.log(Level.SEVERE, String.format("Failed to add SHA256 for image data source %s (objId=%d)", newImage.getName(), newImage.getId()), ex);
279  errorMessages.add(ex.getMessage());
280  criticalErrorOccurred = true;
281  } catch (TskDataException ignored) {
282  /*
283  * The only reasonable way for this to happen at
284  * present is through C/C++ processing of an EWF
285  * image, which is not an error.
286  */
287  }
288  }
289  } else {
290  String errorMessage = String.format("Error commiting after adding image %s to the case database, no object id returned", imagePath); //NON-NLS
291  logger.log(Level.SEVERE, errorMessage);
292  errorMessages.add(errorMessage);
293  criticalErrorOccurred = true;
294  }
295  } catch (TskCoreException ex) {
296  logger.log(Level.SEVERE, String.format("Error committing adding image %s to the case database", imagePath), ex); //NON-NLS
297  errorMessages.add(ex.getMessage());
298  criticalErrorOccurred = true;
299  }
300  }
301  }
302  }
303 
308  private class ProgressUpdater implements Runnable {
309 
311  private final SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess;
312 
320  ProgressUpdater(DataSourceProcessorProgressMonitor progressMonitor, SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess) {
321  this.progressMonitor = progressMonitor;
323  }
324 
329  @Override
330  public void run() {
331  try {
332  while (!Thread.currentThread().isInterrupted()) {
333  String currDir = tskAddImageProcess.currentDirectory();
334  if (currDir != null) {
335  if (!currDir.isEmpty()) {
336  progressMonitor.setProgressText(
337  NbBundle.getMessage(this.getClass(), "AddImageTask.run.progress.adding",
338  currDir));
339  }
340  }
341  /*
342  * The sleep here throttles the UI updates and provides a
343  * non-standard mechanism for completing this task by
344  * interrupting the thread in which it is running.
345  *
346  * TODO (AUT-1870): Replace this with giving the task to a
347  * java.util.concurrent.ScheduledThreadPoolExecutor that is
348  * shut down when the main task completes.
349  */
350  Thread.sleep(500);
351  }
352  } catch (InterruptedException expected) {
353  }
354  }
355  }
356 
357 }
final SleuthkitJNI.CaseDbHandle.AddImageProcess tskAddImageProcess
final DataSourceProcessorProgressMonitor progressMonitor

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