Autopsy  4.14.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
AddMultipleImagesTask.java
Go to the documentation of this file.
1 /*
2  * Autopsy
3  *
4  * Copyright 2019 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.logicalimager.dsp;
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.openide.util.NbBundle.Messages;
31 import org.sleuthkit.datamodel.Content;
32 import org.sleuthkit.datamodel.Image;
33 import org.sleuthkit.datamodel.SleuthkitCase;
34 import org.sleuthkit.datamodel.SleuthkitJNI;
35 import org.sleuthkit.datamodel.TskCoreException;
36 import org.sleuthkit.datamodel.TskDataException;
37 import org.sleuthkit.datamodel.TskFileRange;
38 
44 @Messages({
45  "AddMultipleImagesTask.fsTypeUnknownErr=Cannot determine file system type"
46 })
47 class AddMultipleImagesTask implements Runnable {
48 
49  private static final Logger LOGGER = Logger.getLogger(AddMultipleImagesTask.class.getName());
50  public static final String TSK_FS_TYPE_UNKNOWN_ERR_MSG = Bundle.AddMultipleImagesTask_fsTypeUnknownErr();
51  private static final long TWO_GB = 2000000000L;
52  private final String deviceId;
53  private final List<String> imageFilePaths;
54  private final String timeZone;
55  private final long chunkSize = TWO_GB;
56  private final DataSourceProcessorProgressMonitor progressMonitor;
57  private final Case currentCase;
58  private boolean criticalErrorOccurred;
59  private SleuthkitJNI.CaseDbHandle.AddImageProcess addImageProcess = null;
60  private List<String> errorMessages = new ArrayList<>();
61  private DataSourceProcessorResult result;
62  private List<Content> newDataSources = new ArrayList<>();
63 
64  /*
65  * The cancellation requested flag and SleuthKit add image process are
66  * guarded by a lock to synchronize cancelling the process (setting the flag
67  * and calling its stop method) and calling either its commit or revert
68  * method.
69  */
70  private final Object tskAddImageProcessLock;
71  @GuardedBy("tskAddImageProcessLock")
72  private boolean tskAddImageProcessStopped;
73 
91  @Messages({
92  "# {0} - file", "AddMultipleImagesTask.addingFileAsLogicalFile=Adding: {0} as an unallocated space file.",
93  "# {0} - deviceId", "# {1} - exceptionMessage",
94  "AddMultipleImagesTask.errorAddingImgWithoutFileSystem=Error adding images without file systems for device {0}: {1}",})
95  AddMultipleImagesTask(String deviceId, List<String> imageFilePaths, String timeZone,
96  DataSourceProcessorProgressMonitor progressMonitor) throws NoCurrentCaseException {
97  this.deviceId = deviceId;
98  this.imageFilePaths = imageFilePaths;
99  this.timeZone = timeZone;
100  this.progressMonitor = progressMonitor;
101  currentCase = Case.getCurrentCaseThrows();
102  this.criticalErrorOccurred = false;
103  tskAddImageProcessLock = new Object();
104  }
105 
106  @Messages({
107  "AddMultipleImagesTask.cancelled=Cancellation: Add image process reverted",
108  })
109  @Override
110  public void run() {
111  errorMessages = new ArrayList<>();
112  newDataSources = new ArrayList<>();
113  List<Content> emptyDataSources = new ArrayList<>();
114 
115  /*
116  * Try to add the input image files as images.
117  */
118  List<String> corruptedImageFilePaths = new ArrayList<>();
119  try {
120  currentCase.getSleuthkitCase().acquireSingleUserCaseWriteLock();
121  progressMonitor.setIndeterminate(true);
122  for (String imageFilePath : imageFilePaths) {
123  synchronized (tskAddImageProcessLock) {
124  if (!tskAddImageProcessStopped) {
125  addImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, false, false, "");
126  } else {
127  return;
128  }
129  }
130  run(imageFilePath, corruptedImageFilePaths, errorMessages);
131  commitOrRevertAddImageProcess(imageFilePath, errorMessages, newDataSources);
132  synchronized (tskAddImageProcessLock) {
133  if (tskAddImageProcessStopped) {
134  errorMessages.add(Bundle.AddMultipleImagesTask_cancelled());
135  result = DataSourceProcessorResult.CRITICAL_ERRORS;
136  newDataSources = emptyDataSources;
137  return;
138  }
139  }
140  }
141  } finally {
142  currentCase.getSleuthkitCase().releaseSingleUserCaseWriteLock();
143  }
144 
145  /*
146  * Try to add any input image files that did not have file systems as a
147  * single an unallocated space file with the device id as the root virtual
148  * directory name.
149  */
150  if (!tskAddImageProcessStopped && !corruptedImageFilePaths.isEmpty()) {
151  SleuthkitCase caseDatabase;
152  caseDatabase = currentCase.getSleuthkitCase();
153  try {
154  progressMonitor.setProgressText(Bundle.AddMultipleImagesTask_addingFileAsLogicalFile(corruptedImageFilePaths.toString()));
155 
156  caseDatabase.acquireSingleUserCaseWriteLock();
157 
158  Image dataSource = caseDatabase.addImageInfo(0, corruptedImageFilePaths, timeZone);
159  newDataSources.add(dataSource);
160  List<TskFileRange> fileRanges = new ArrayList<>();
161 
162  long imageSize = dataSource.getSize();
163  int sequence = 0;
164  //start byte and end byte
165  long start = 0;
166  if (chunkSize > 0 && imageSize >= TWO_GB) {
167  for (double size = TWO_GB; size < dataSource.getSize(); size += TWO_GB) {
168  fileRanges.add(new TskFileRange(start, TWO_GB, sequence));
169  start += TWO_GB;
170  sequence++;
171  }
172  }
173  double leftoverSize = imageSize - sequence * TWO_GB;
174  fileRanges.add(new TskFileRange(start, (long)leftoverSize, sequence));
175 
176  caseDatabase.addLayoutFiles(dataSource, fileRanges);
177  } catch (TskCoreException ex) {
178  errorMessages.add(Bundle.AddMultipleImagesTask_errorAddingImgWithoutFileSystem(deviceId, ex.getLocalizedMessage()));
179  criticalErrorOccurred = true;
180  } finally {
181  caseDatabase.releaseSingleUserCaseWriteLock();
182  }
183  }
184 
185  /*
186  * This appears to be the best that can be done to indicate completion
187  * with the DataSourceProcessorProgressMonitor in its current form.
188  */
189  progressMonitor.setProgress(0);
190  progressMonitor.setProgress(100);
191 
192  if (criticalErrorOccurred) {
193  result = DataSourceProcessorResult.CRITICAL_ERRORS;
194  } else if (!errorMessages.isEmpty()) {
195  result = DataSourceProcessorResult.NONCRITICAL_ERRORS;
196  } else {
197  result = DataSourceProcessorResult.NO_ERRORS;
198  }
199  }
200 
205  void cancelTask() {
206  LOGGER.log(Level.WARNING, "AddMultipleImagesTask cancelled, processing may be incomplete"); // NON-NLS
207  synchronized (tskAddImageProcessLock) {
208  tskAddImageProcessStopped = true;
209  if (addImageProcess != null) {
210  try {
211  /*
212  * All this does is set a flag that will make the TSK add
213  * image process exit when the flag is checked between
214  * processing steps. The state of the flag is not
215  * accessible, so record it here so that it is known that
216  * the revert method of the process object needs to be
217  * called.
218  */
219  addImageProcess.stop();
220  } catch (TskCoreException ex) {
221  LOGGER.log(Level.SEVERE, "Cancellation: addImagePRocess.stop failed", ex); // NON-NLS
222  }
223  }
224  }
225  }
226 
240  @Messages({
241  "# {0} - imageFilePath", "AddMultipleImagesTask.adding=Adding: {0}",
242  "# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImagesTask.criticalErrorAdding=Critical error adding {0} for device {1}: {2}",
243  "# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImagesTask.criticalErrorReverting=Critical error reverting add image process for {0} for device {1}: {2}",
244  "# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImagesTask.nonCriticalErrorAdding=Non-critical error adding {0} for device {1}: {2}",})
245  private void run(String imageFilePath, List<String> corruptedImageFilePaths, List<String> errorMessages) {
246  /*
247  * Try to add the image to the case database as a data source.
248  */
249  progressMonitor.setProgressText(Bundle.AddMultipleImagesTask_adding(imageFilePath));
250  try {
251  addImageProcess.run(deviceId, new String[]{imageFilePath});
252  } catch (TskCoreException ex) {
253  if (ex.getMessage().contains(TSK_FS_TYPE_UNKNOWN_ERR_MSG)) {
254  /*
255  * If Sleuth Kit failed to add the image because it did not find
256  * a file system, save the image path so it can be added to the
257  * case as an unallocated space file. All other
258  * errors are critical.
259  */
260  corruptedImageFilePaths.add(imageFilePath);
261  } else {
262  errorMessages.add(Bundle.AddMultipleImagesTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
263  criticalErrorOccurred = true;
264  }
265  } catch (TskDataException ex) {
266  errorMessages.add(Bundle.AddMultipleImagesTask_nonCriticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
267  }
268  }
269 
282  private void commitOrRevertAddImageProcess(String imageFilePath, List<String> errorMessages, List<Content> newDataSources) {
283  synchronized (tskAddImageProcessLock) {
284  if (tskAddImageProcessStopped || criticalErrorOccurred) {
285  try {
286  addImageProcess.revert();
287  } catch (TskCoreException ex) {
288  errorMessages.add(Bundle.AddMultipleImagesTask_criticalErrorReverting(imageFilePath, deviceId, ex.getLocalizedMessage()));
289  criticalErrorOccurred = true;
290  }
291  return;
292  }
293 
294  /*
295  * Try to commit the results of the add image process, retrieve the new
296  * image from the case database, and add it to the list of new data
297  * sources to be returned via the getter method.
298  */
299  try {
300  long imageId = addImageProcess.commit();
301  Image dataSource = currentCase.getSleuthkitCase().getImageById(imageId);
302  newDataSources.add(dataSource);
303 
304  /*
305  * Verify the size of the new image. Note that it may not be what is
306  * expected, but at least part of it was added to the case.
307  */
308  String verificationError = dataSource.verifyImageSize();
309  if (!verificationError.isEmpty()) {
310  errorMessages.add(Bundle.AddMultipleImagesTask_nonCriticalErrorAdding(imageFilePath, deviceId, verificationError));
311  }
312  } catch (TskCoreException ex) {
313  /*
314  * The add image process commit failed or querying the case database
315  * for the newly added image failed. Either way, this is a critical
316  * error.
317  */
318  errorMessages.add(Bundle.AddMultipleImagesTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
319  criticalErrorOccurred = true;
320  }
321  }
322  }
323 
328  public List<String> getErrorMessages() {
329  return errorMessages;
330  }
331 
336  public DataSourceProcessorResult getResult() {
337  return result;
338  }
339 
344  public List<Content> getNewDataSources() {
345  return newDataSources;
346  }
347 }

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.