Autopsy  4.15.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  progressMonitor.setIndeterminate(true);
120  for (String imageFilePath : imageFilePaths) {
121  synchronized (tskAddImageProcessLock) {
122  if (!tskAddImageProcessStopped) {
123  addImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, false, false, "");
124  } else {
125  return;
126  }
127  }
128  run(imageFilePath, corruptedImageFilePaths, errorMessages);
129  commitOrRevertAddImageProcess(imageFilePath, errorMessages, newDataSources);
130  synchronized (tskAddImageProcessLock) {
131  if (tskAddImageProcessStopped) {
132  errorMessages.add(Bundle.AddMultipleImagesTask_cancelled());
133  result = DataSourceProcessorResult.CRITICAL_ERRORS;
134  newDataSources = emptyDataSources;
135  return;
136  }
137  }
138  }
139 
140  /*
141  * Try to add any input image files that did not have file systems as a
142  * single an unallocated space file with the device id as the root virtual
143  * directory name.
144  */
145  if (!tskAddImageProcessStopped && !corruptedImageFilePaths.isEmpty()) {
146  SleuthkitCase caseDatabase;
147  caseDatabase = currentCase.getSleuthkitCase();
148  try {
149  progressMonitor.setProgressText(Bundle.AddMultipleImagesTask_addingFileAsLogicalFile(corruptedImageFilePaths.toString()));
150 
151  Image dataSource = caseDatabase.addImageInfo(0, corruptedImageFilePaths, timeZone);
152  newDataSources.add(dataSource);
153  List<TskFileRange> fileRanges = new ArrayList<>();
154 
155  long imageSize = dataSource.getSize();
156  int sequence = 0;
157  //start byte and end byte
158  long start = 0;
159  if (chunkSize > 0 && imageSize >= TWO_GB) {
160  for (double size = TWO_GB; size < dataSource.getSize(); size += TWO_GB) {
161  fileRanges.add(new TskFileRange(start, TWO_GB, sequence));
162  start += TWO_GB;
163  sequence++;
164  }
165  }
166  double leftoverSize = imageSize - sequence * TWO_GB;
167  fileRanges.add(new TskFileRange(start, (long)leftoverSize, sequence));
168 
169  caseDatabase.addLayoutFiles(dataSource, fileRanges);
170  } catch (TskCoreException ex) {
171  errorMessages.add(Bundle.AddMultipleImagesTask_errorAddingImgWithoutFileSystem(deviceId, ex.getLocalizedMessage()));
172  criticalErrorOccurred = true;
173  }
174  }
175 
176  /*
177  * This appears to be the best that can be done to indicate completion
178  * with the DataSourceProcessorProgressMonitor in its current form.
179  */
180  progressMonitor.setProgress(0);
181  progressMonitor.setProgress(100);
182 
183  if (criticalErrorOccurred) {
184  result = DataSourceProcessorResult.CRITICAL_ERRORS;
185  } else if (!errorMessages.isEmpty()) {
186  result = DataSourceProcessorResult.NONCRITICAL_ERRORS;
187  } else {
188  result = DataSourceProcessorResult.NO_ERRORS;
189  }
190  }
191 
196  void cancelTask() {
197  LOGGER.log(Level.WARNING, "AddMultipleImagesTask cancelled, processing may be incomplete"); // NON-NLS
198  synchronized (tskAddImageProcessLock) {
199  tskAddImageProcessStopped = true;
200  if (addImageProcess != null) {
201  try {
202  /*
203  * All this does is set a flag that will make the TSK add
204  * image process exit when the flag is checked between
205  * processing steps. The state of the flag is not
206  * accessible, so record it here so that it is known that
207  * the revert method of the process object needs to be
208  * called.
209  */
210  addImageProcess.stop();
211  } catch (TskCoreException ex) {
212  LOGGER.log(Level.SEVERE, "Cancellation: addImagePRocess.stop failed", ex); // NON-NLS
213  }
214  }
215  }
216  }
217 
231  @Messages({
232  "# {0} - imageFilePath", "AddMultipleImagesTask.adding=Adding: {0}",
233  "# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImagesTask.criticalErrorAdding=Critical error adding {0} for device {1}: {2}",
234  "# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImagesTask.criticalErrorReverting=Critical error reverting add image process for {0} for device {1}: {2}",
235  "# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImagesTask.nonCriticalErrorAdding=Non-critical error adding {0} for device {1}: {2}",})
236  private void run(String imageFilePath, List<String> corruptedImageFilePaths, List<String> errorMessages) {
237  /*
238  * Try to add the image to the case database as a data source.
239  */
240  progressMonitor.setProgressText(Bundle.AddMultipleImagesTask_adding(imageFilePath));
241  try {
242  addImageProcess.run(deviceId, new String[]{imageFilePath});
243  } catch (TskCoreException ex) {
244  if (ex.getMessage().contains(TSK_FS_TYPE_UNKNOWN_ERR_MSG)) {
245  /*
246  * If Sleuth Kit failed to add the image because it did not find
247  * a file system, save the image path so it can be added to the
248  * case as an unallocated space file. All other
249  * errors are critical.
250  */
251  corruptedImageFilePaths.add(imageFilePath);
252  } else {
253  errorMessages.add(Bundle.AddMultipleImagesTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
254  criticalErrorOccurred = true;
255  }
256  } catch (TskDataException ex) {
257  errorMessages.add(Bundle.AddMultipleImagesTask_nonCriticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
258  }
259  }
260 
273  private void commitOrRevertAddImageProcess(String imageFilePath, List<String> errorMessages, List<Content> newDataSources) {
274  synchronized (tskAddImageProcessLock) {
275  if (tskAddImageProcessStopped || criticalErrorOccurred) {
276  try {
277  addImageProcess.revert();
278  } catch (TskCoreException ex) {
279  errorMessages.add(Bundle.AddMultipleImagesTask_criticalErrorReverting(imageFilePath, deviceId, ex.getLocalizedMessage()));
280  criticalErrorOccurred = true;
281  }
282  return;
283  }
284 
285  /*
286  * Try to commit the results of the add image process, retrieve the new
287  * image from the case database, and add it to the list of new data
288  * sources to be returned via the getter method.
289  */
290  try {
291  long imageId = addImageProcess.commit();
292  Image dataSource = currentCase.getSleuthkitCase().getImageById(imageId);
293  newDataSources.add(dataSource);
294 
295  /*
296  * Verify the size of the new image. Note that it may not be what is
297  * expected, but at least part of it was added to the case.
298  */
299  String verificationError = dataSource.verifyImageSize();
300  if (!verificationError.isEmpty()) {
301  errorMessages.add(Bundle.AddMultipleImagesTask_nonCriticalErrorAdding(imageFilePath, deviceId, verificationError));
302  }
303  } catch (TskCoreException ex) {
304  /*
305  * The add image process commit failed or querying the case database
306  * for the newly added image failed. Either way, this is a critical
307  * error.
308  */
309  errorMessages.add(Bundle.AddMultipleImagesTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
310  criticalErrorOccurred = true;
311  }
312  }
313  }
314 
319  public List<String> getErrorMessages() {
320  return errorMessages;
321  }
322 
327  public DataSourceProcessorResult getResult() {
328  return result;
329  }
330 
335  public List<Content> getNewDataSources() {
336  return newDataSources;
337  }
338 }

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.