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

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.