Autopsy  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;
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()
static void cancelFinishImage(long imgHandle)
static int finishImageWriter(long imgHandle)
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)
static int getFinishImageProgress(long imgHandle)
void updateImagePath(String newPath, long objectId)
synchronized static Logger getLogger(String name)
Definition: Logger.java:161
synchronized long getImageHandle()

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