Autopsy  4.4.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
ImageWriter.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2016 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.imagewriter;
20 
21 import com.google.common.util.concurrent.ThreadFactoryBuilder;
22 import java.beans.PropertyChangeEvent;
23 import java.beans.PropertyChangeListener;
24 import java.util.concurrent.Callable;
25 import java.util.concurrent.Executors;
26 import java.util.concurrent.Future;
27 import java.util.concurrent.ScheduledFuture;
28 import java.util.concurrent.ScheduledThreadPoolExecutor;
29 import java.util.concurrent.TimeUnit;
30 import java.util.concurrent.ExecutionException;
31 import java.util.logging.Level;
32 import org.netbeans.api.progress.ProgressHandle;
36 import org.sleuthkit.datamodel.Image;
37 import org.sleuthkit.datamodel.SleuthkitCase;
40 import org.sleuthkit.datamodel.SleuthkitJNI;
41 import org.sleuthkit.datamodel.TskCoreException;
42 
50 class ImageWriter implements PropertyChangeListener{
51 
52  private final Logger logger = Logger.getLogger(ImageWriter.class.getName());
53 
54  private final Long dataSourceId;
55  private final ImageWriterSettings settings;
56 
57  private Long imageHandle = null;
58  private Future<Integer> finishTask = null;
59  private ProgressHandle progressHandle = null;
60  private ScheduledFuture<?> progressUpdateTask = null;
61  private boolean isCancelled = false;
62  private boolean isStarted = false;
63  private final Object currentTasksLock = new Object(); // Get this lock before accessing imageHandle, finishTask, progressHandle, progressUpdateTask,
64  // isCancelled, isStarted, or isFinished
65 
66  private ScheduledThreadPoolExecutor periodicTasksExecutor = null;
67  private final boolean doUI;
68  private SleuthkitCase caseDb = null;
69 
75  ImageWriter(Long dataSourceId, ImageWriterSettings settings){
76  this.dataSourceId = dataSourceId;
77  this.settings = settings;
79 
80  // We save the reference to the sleuthkit case here in case getCurrentCase() is set to
81  // null before Image Writer finishes. The user can still elect to wait for image writer
82  // (in ImageWriterService.closeCaseResources) even though the case is closing.
83  try{
85  } catch (IllegalStateException ex){
86  logger.log(Level.SEVERE, "Unable to load case. Image writer will be cancelled.");
87  this.isCancelled = true;
88  }
89  }
90 
94  void subscribeToEvents(){
96  }
97 
101  void unsubscribeFromEvents(){
103  }
104 
109  @Override
110  public void propertyChange(PropertyChangeEvent evt) {
111  if(evt instanceof DataSourceAnalysisCompletedEvent){
112 
113  DataSourceAnalysisCompletedEvent event = (DataSourceAnalysisCompletedEvent)evt;
114 
115  if(event.getDataSource() != null){
116  long imageId = event.getDataSource().getId();
117  String name = event.getDataSource().getName();
118 
119  // Check that the event corresponds to this datasource
120  if(imageId != dataSourceId){
121  return;
122  }
123  new Thread(() -> {
124  startFinishImage(name);
125  }).start();
126 
127  } else {
128  logger.log(Level.SEVERE, "DataSourceAnalysisCompletedEvent did not contain a dataSource object"); //NON-NLS
129  }
130  }
131  }
132 
133  private void startFinishImage(String dataSourceName){
134 
135  synchronized(currentTasksLock){
136  if(isCancelled){
137  return;
138  }
139 
140  // If we've already started the finish process for this datasource, return.
141  // Multiple DataSourceAnalysisCompletedEvent events can come from
142  // the same image if more ingest modules are run later
143  if(isStarted){
144  return;
145  }
146 
147  Image image;
148  try{
149  image = Case.getCurrentCase().getSleuthkitCase().getImageById(dataSourceId);
150  imageHandle = image.getImageHandle();
151  } catch (IllegalStateException ex){
152  // This exception means that getCurrentCase() failed because no case was open.
153  // This can happen when the user closes the case while ingest is ongoing - canceling
154  // ingest fires off the DataSourceAnalysisCompletedEvent while the case is in the
155  // process of closing.
156  logger.log(Level.WARNING, String.format("Case closed before ImageWriter could start the finishing process for %s",
157  dataSourceName));
158  return;
159  } catch (TskCoreException ex){
160  logger.log(Level.SEVERE, "Error loading image", ex);
161  return;
162  }
163 
164  logger.log(Level.INFO, String.format("Finishing VHD image for %s",
165  dataSourceName)); //NON-NLS
166 
167  if(doUI){
168  periodicTasksExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("image-writer-progress-update-%d").build()); //NON-NLS
169  progressHandle = ProgressHandle.createHandle("Image writer - " + dataSourceName);
170  progressHandle.start(100);
171  progressUpdateTask = periodicTasksExecutor.scheduleAtFixedRate(
172  new ProgressUpdateTask(progressHandle, imageHandle), 0, 250, TimeUnit.MILLISECONDS);
173  }
174 
175  // The added complexity here with the Future is because we absolutely need to make sure
176  // the call to finishImageWriter returns before allowing the TSK data structures to be freed
177  // during case close.
178  finishTask = Executors.newSingleThreadExecutor().submit(new Callable<Integer>(){
179  @Override
180  public Integer call() throws TskCoreException{
181  try{
182  int result = SleuthkitJNI.finishImageWriter(imageHandle);
183 
184  // We've decided to always update the path to the VHD, even if it wasn't finished.
185  // This supports the case where an analyst has partially ingested a device
186  // but has to stop before completion. They will at least have part of the image.
187  if(settings.getUpdateDatabasePath()){
188  caseDb.updateImagePath(settings.getPath(), dataSourceId);
189  }
190  return result;
191  } catch (TskCoreException ex){
192  logger.log(Level.SEVERE, "Error finishing VHD image", ex); //NON-NLS
193  return -1;
194  }
195  }
196  });
197 
198  // Setting this means that finishTask and all the UI updaters are initialized (if running UI)
199  isStarted = true;
200  }
201 
202  // Wait for finishImageWriter to complete
203  int result = 0;
204  try{
205  // The call to get() can happen multiple times if the user closes the case, which is ok
206  result = finishTask.get();
207  } catch (InterruptedException | ExecutionException ex){
208  logger.log(Level.SEVERE, "Error finishing VHD image", ex); //NON-NLS
209  }
210 
211  synchronized(currentTasksLock){
212  if(doUI){
213  // Some of these may be called twice if the user closes the case
214  progressUpdateTask.cancel(true);
215  progressHandle.finish();
216  periodicTasksExecutor.shutdown();
217  }
218  }
219 
220  if(result == 0){
221  logger.log(Level.INFO, String.format("Successfully finished writing VHD image for %s", dataSourceName)); //NON-NLS
222  } else {
223  logger.log(Level.INFO, String.format("Finished VHD image for %s with errors", dataSourceName)); //NON-NLS
224  }
225  }
226 
233  void cancelIfNotStarted(){
234  synchronized(currentTasksLock){
235  if(! isStarted){
236  isCancelled = true;
237  }
238  }
239  }
240 
246  boolean jobIsInProgress(){
247  synchronized(currentTasksLock){
248  return((isStarted) && (! finishTask.isDone()));
249  }
250  }
251 
256  void cancelJob(){
257  synchronized(currentTasksLock){
258  // All of the following is redundant but safe to call on a complete job
259  isCancelled = true;
260 
261  if(isStarted){
262  SleuthkitJNI.cancelFinishImage(imageHandle);
263 
264  // Stop the progress bar update task.
265  // The thread from startFinishImage will also stop it
266  // once the task completes, but we don't have a guarantee on
267  // when that happens.
268  // Since we've stopped the update task, we'll stop the associated progress
269  // bar now, too.
270  if(doUI){
271  progressUpdateTask.cancel(true);
272  progressHandle.finish();
273  }
274  }
275  }
276  }
277 
282  void waitForJobToFinish(){
283  synchronized(currentTasksLock){
284  // Wait for the finish task to end
285  if(isStarted){
286  try{
287  finishTask.get();
288  } catch (InterruptedException | ExecutionException ex){
289  Logger.getLogger(ImageWriter.class.getName()).log(Level.SEVERE, "Error finishing VHD image", ex); //NON-NLS
290  }
291  if(doUI){
292  progressUpdateTask.cancel(true);
293  }
294  }
295  }
296  }
297 
301  private final class ProgressUpdateTask implements Runnable {
302  final long imageHandle;
303  final ProgressHandle progressHandle;
304 
305  ProgressUpdateTask(ProgressHandle progressHandle, long imageHandle){
306  this.imageHandle = imageHandle;
307  this.progressHandle = progressHandle;
308  }
309 
310  @Override
311  public void run() {
312  try {
313  int progress = SleuthkitJNI.getFinishImageProgress(imageHandle);
314  progressHandle.progress(progress);
315  } catch (Exception ex) {
316  logger.log(Level.SEVERE, "Unexpected exception in ProgressUpdateTask", ex); //NON-NLS
317  }
318  }
319  }
320 }
static synchronized IngestManager getInstance()
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
Definition: Logger.java:161

Copyright © 2012-2016 Basis Technology. Generated on: Fri Sep 29 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.