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

Copyright © 2012-2016 Basis Technology. Generated on: Tue Feb 20 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.