Autopsy  4.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ExtractUnallocAction.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011 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.directorytree;
20 
21 import java.awt.Component;
22 import java.awt.Frame;
23 import java.awt.event.ActionEvent;
24 import java.io.File;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.OutputStream;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.Comparator;
31 import java.util.List;
32 import java.util.concurrent.ExecutionException;
33 import java.util.logging.Level;
34 import javax.swing.AbstractAction;
35 import javax.swing.JFileChooser;
36 import javax.swing.JOptionPane;
37 import javax.swing.SwingWorker;
38 import org.netbeans.api.progress.ProgressHandle;
39 import org.netbeans.api.progress.ProgressHandleFactory;
40 import org.openide.util.Cancellable;
41 import org.openide.util.NbBundle;
45 import org.sleuthkit.datamodel.AbstractFile;
46 import org.sleuthkit.datamodel.Content;
47 import org.sleuthkit.datamodel.ContentVisitor;
48 import org.sleuthkit.datamodel.Directory;
49 import org.sleuthkit.datamodel.FileSystem;
50 import org.sleuthkit.datamodel.Image;
51 import org.sleuthkit.datamodel.LayoutFile;
52 import org.sleuthkit.datamodel.TskCoreException;
53 import org.sleuthkit.datamodel.VirtualDirectory;
54 import org.sleuthkit.datamodel.Volume;
55 import org.sleuthkit.datamodel.VolumeSystem;
56 
60 final class ExtractUnallocAction extends AbstractAction {
61 
62  private final List<UnallocStruct> LstUnallocs = new ArrayList<UnallocStruct>();
63  private static final List<String> lockedVols = new ArrayList<String>();
64  private static final List<Long> lockedImages = new ArrayList<Long>();
65  private long currentImage = 0L;
66  private static final Logger logger = Logger.getLogger(ExtractUnallocAction.class.getName());
67  private boolean isImage = false;
68 
69  public ExtractUnallocAction(String title, Volume volu) {
70  super(title);
71  UnallocStruct us = new UnallocStruct(volu);
72  LstUnallocs.add(us);
73  }
74 
75  public ExtractUnallocAction(String title, Image img) {
76  super(title);
77  isImage = true;
78  currentImage = img.getId();
79  if (hasVolumeSystem(img)) {
80  for (Volume v : getVolumes(img)) {
81  UnallocStruct us = new UnallocStruct(v);
82  LstUnallocs.add(us);
83  }
84  } else {
85  UnallocStruct us = new UnallocStruct(img);
86  LstUnallocs.add(us);
87  }
88  }
89 
96  @Override
97  public void actionPerformed(ActionEvent e) {
98  if (LstUnallocs != null && LstUnallocs.size() > 0) {
99  if (lockedImages.contains(currentImage)) {
100  MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.notifyMsg.unallocAlreadyBeingExtr.msg"));
101  //JOptionPane.showMessageDialog(new Frame(), "Unallocated Space is already being extracted on this Image. Please select a different Image.");
102  return;
103  }
104  List<UnallocStruct> copyList = new ArrayList<UnallocStruct>() {
105  {
106  addAll(LstUnallocs);
107  }
108  };
109 
110  JFileChooser fc = new JFileChooser() {
111  @Override
112  public void approveSelection() {
113  File f = getSelectedFile();
114  if (!f.exists() && getDialogType() == SAVE_DIALOG || !f.canWrite()) {
115  JOptionPane.showMessageDialog(this, NbBundle.getMessage(this.getClass(),
116  "ExtractUnallocAction.msgDlg.folderDoesntExist.msg"));
117  return;
118  }
119  super.approveSelection();
120  }
121  };
122 
123  fc.setCurrentDirectory(new File(Case.getCurrentCase().getExportDirectory()));
124  fc.setDialogTitle(
125  NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg"));
126  fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
127  fc.setAcceptAllFileFilterUsed(false);
128  int returnValue = fc.showSaveDialog((Component) e.getSource());
129  if (returnValue == JFileChooser.APPROVE_OPTION) {
130  String destination = fc.getSelectedFile().getPath();
131  for (UnallocStruct u : LstUnallocs) {
132  u.setPath(destination);
133  if (u.llf != null && u.llf.size() > 0 && !lockedVols.contains(u.getFileName())) {
134  //Format for single Unalloc File is ImgName-Unalloc-ImgObjectID-VolumeID.dat
135  if (u.FileInstance.exists()) {
136  int res = JOptionPane.showConfirmDialog(new Frame(), NbBundle.getMessage(this.getClass(),
137  "ExtractUnallocAction.confDlg.unallocFileAlreadyExist.msg",
138  u.getFileName()));
139  if (res == JOptionPane.YES_OPTION) {
140  u.FileInstance.delete();
141  } else {
142  copyList.remove(u);
143  }
144  }
145  if (!isImage & !copyList.isEmpty()) {
146  ExtractUnallocWorker uw = new ExtractUnallocWorker(u);
147  uw.execute();
148  }
149  } else {
150  logger.log(Level.WARNING, "Tried to get unallocated content from volume ID but " + u.VolumeId + u.llf == null ? "its list of unallocated files was null" : "the volume is locked"); //NON-NLS
151  }
152  }
153  if (isImage && !copyList.isEmpty()) {
154  ExtractUnallocWorker uw = new ExtractUnallocWorker(copyList);
155  uw.execute();
156  }
157  }
158  }
159 
160  }
161 
169  private List<LayoutFile> getUnallocFiles(Content c) {
170  UnallocVisitor uv = new UnallocVisitor();
171  try {
172  List<Content> unallocFiles = c.getChildren();
173  if (null != unallocFiles && unallocFiles.isEmpty() == false) {
174  return unallocFiles.get(0).accept(uv); //Launching it on the root directory
175  }
176  } catch (TskCoreException tce) {
177  logger.log(Level.WARNING, "Couldn't get a list of Unallocated Files, failed at sending out the visitor ", tce); //NON-NLS
178  }
179  return Collections.emptyList();
180  }
181 
185  private class ExtractUnallocWorker extends SwingWorker<Integer, Integer> {
186 
187  private ProgressHandle progress;
188  private boolean canceled = false;
189  private List<UnallocStruct> lus = new ArrayList<UnallocStruct>();
190  private File currentlyProcessing;
191  private int totalSizeinMegs;
192  long totalBytes = 0;
193 
195  //Getting the total megs this worker is going to be doing
196  if (!lockedVols.contains(us.getFileName())) {
197  this.lus.add(us);
198  totalBytes = us.getSizeInBytes();
199  totalSizeinMegs = toMb(totalBytes);
200  lockedVols.add(us.getFileName());
201  }
202  }
203 
204  ExtractUnallocWorker(List<UnallocStruct> lst) {
205  //Getting the total megs this worker is going to be doing
206  for (UnallocStruct lu : lst) {
207  if (!lockedVols.contains(lu.getFileName())) {
208  totalBytes += lu.getSizeInBytes();
209  lockedVols.add(lu.getFileName());
210  this.lus.add(lu);
211  }
212  }
213  totalSizeinMegs = toMb(totalBytes);
214  lockedImages.add(currentImage);
215  }
216 
217  private int toMb(long bytes) {
218  if (bytes > 1024 && (bytes / 1024.0) <= Double.MAX_VALUE) {
219  double Mb = ((bytes / 1024.0) / 1024.0);//Bytes -> Megabytes
220  if (Mb <= Integer.MAX_VALUE) {
221  return (int) Math.ceil(Mb);
222  }
223  }
224  return 0;
225  }
226 
227  @Override
228  protected Integer doInBackground() {
229  try {
230  progress = ProgressHandleFactory.createHandle(
231  NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.progress.extractUnalloc.title"), new Cancellable() {
232  @Override
233  public boolean cancel() {
234  logger.log(Level.INFO, "Canceling extraction of unallocated space"); //NON-NLS
235  canceled = true;
236  if (progress != null) {
237  progress.setDisplayName(NbBundle.getMessage(this.getClass(),
238  "ExtractUnallocAction.progress.displayName.cancelling.text"));
239  }
240  return true;
241  }
242  });
243  int MAX_BYTES = 8192;
244  byte[] buf = new byte[MAX_BYTES]; //read 8kb at a time
245 
246  //Begin the actual File IO
247  progress.start(totalSizeinMegs);
248  int kbs = 0; //Each completion of the while loop adds one to kbs. 16kb * 64 = 1mb.
249  int mbs = 0; //Increments every 128th tick of kbs
250  for (UnallocStruct u : this.lus) {
251  currentlyProcessing = u.getFile();
252  logger.log(Level.INFO, "Writing Unalloc file to " + currentlyProcessing.getPath()); //NON-NLS
253  OutputStream dos = new FileOutputStream(currentlyProcessing);
254  long bytes = 0;
255  int i = 0;
256  while (i < u.getLayouts().size() && bytes != u.getSizeInBytes()) {
257  LayoutFile f = u.getLayouts().get(i);
258  long offsetPerFile = 0L;
259  int bytesRead;
260  while (offsetPerFile != f.getSize() && !canceled) {
261  if (++kbs % 128 == 0) {
262  mbs++;
263  progress.progress(NbBundle.getMessage(this.getClass(),
264  "ExtractUnallocAction.processing.counter.msg",
265  mbs, totalSizeinMegs), mbs - 1);
266  }
267  bytesRead = f.read(buf, offsetPerFile, MAX_BYTES);
268  offsetPerFile += bytesRead;
269  dos.write(buf, 0, bytesRead);
270  }
271  bytes += f.getSize();
272  i++;
273  }
274  dos.flush();
275  dos.close();
276 
277  if (canceled) {
278  u.getFile().delete();
279  logger.log(Level.INFO, "Canceled extraction of " + u.getFileName() + " and deleted file"); //NON-NLS
280  } else {
281  logger.log(Level.INFO, "Finished writing unalloc file " + u.getFile().getPath()); //NON-NLS
282  }
283  }
284  progress.finish();
285 
286  } catch (IOException ioe) {
287  logger.log(Level.WARNING, "Could not create Unalloc File; error writing file", ioe); //NON-NLS
288  return -1;
289  } catch (TskCoreException tce) {
290  logger.log(Level.WARNING, "Could not create Unalloc File; error getting image info", tce); //NON-NLS
291  return -1;
292  }
293  return 1;
294  }
295 
296  @Override
297  protected void done() {
298  if (isImage) {
299  lockedImages.remove(currentImage);
300  }
301  for (UnallocStruct u : lus) {
302  lockedVols.remove(u.getFileName());
303  }
304 
305  try {
306  get();
307  if (!canceled && !lus.isEmpty()) {
308  MessageNotifyUtil.Notify.info(NbBundle.getMessage(this.getClass(),
309  "ExtractUnallocAction.done.notifyMsg.completedExtract.title"),
310  NbBundle.getMessage(this.getClass(),
311  "ExtractUnallocAction.done.notifyMsg.completedExtract.msg",
312  lus.get(0).getFile().getParent()));
313  }
314  } catch (InterruptedException | ExecutionException ex) {
316  NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.done.errMsg.title"),
317  NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.done.errMsg.msg", ex.getMessage()));
318  } // catch and ignore if we were cancelled
319  catch (java.util.concurrent.CancellationException ex) {
320  }
321  }
322  }
323 
331  private boolean hasVolumeSystem(Image img) {
332  try {
333  return (img.getChildren().get(0) instanceof VolumeSystem);
334  } catch (TskCoreException tce) {
335  logger.log(Level.WARNING, "Unable to determine if image has a volume system, extraction may be incomplete", tce); //NON-NLS
336  return false;
337  }
338  }
339 
348  private List<Volume> getVolumes(Image img) {
349  List<Volume> lstVol = new ArrayList<Volume>();
350  try {
351  for (Content v : img.getChildren().get(0).getChildren()) {
352  if (v instanceof Volume) {
353  lstVol.add((Volume) v);
354  }
355  }
356  } catch (TskCoreException tce) {
357  logger.log(Level.WARNING, "Could not get volume information from image. Extraction may be incomplete", tce); //NON-NLS
358  }
359  return lstVol;
360  }
361 
366  private static class UnallocVisitor extends ContentVisitor.Default<List<LayoutFile>> {
367 
376  @Override
377  public List<LayoutFile> visit(final org.sleuthkit.datamodel.LayoutFile lf) {
378  return new ArrayList<LayoutFile>() {
379  {
380  add(lf);
381  }
382  };
383  }
384 
394  @Override
395  public List<LayoutFile> visit(FileSystem fs) {
396  try {
397  for (Content c : fs.getChildren()) {
398  if (((AbstractFile) c).isRoot()) {
399  return c.accept(this);
400  }
401  }
402  } catch (TskCoreException tce) {
403  logger.log(Level.WARNING, "Couldn't get a list of Unallocated Files, failed at visiting FileSystem " + fs.getId(), tce); //NON-NLS
404  }
405  return Collections.emptyList();
406  }
407 
416  @Override
417  public List<LayoutFile> visit(VirtualDirectory vd) {
418  try {
419  List<LayoutFile> lflst = new ArrayList<>();
420  for (Content layout : vd.getChildren()) {
421  lflst.add((LayoutFile) layout);
422  }
423  return lflst;
424  } catch (TskCoreException tce) {
425  logger.log(Level.WARNING, "Could not get list of Layout Files, failed at visiting Layout Directory", tce); //NON-NLS
426  }
427  return Collections.emptyList();
428  }
429 
439  @Override
440  public List<LayoutFile> visit(Directory dir) {
441  try {
442  for (Content c : dir.getChildren()) {
443  if (c instanceof VirtualDirectory) {
444  return c.accept(this);
445  }
446  }
447  } catch (TskCoreException tce) {
448  logger.log(Level.WARNING, "Couldn't get a list of Unallocated Files, failed at visiting Directory " + dir.getId(), tce); //NON-NLS
449  }
450  return Collections.emptyList();
451  }
452 
453  @Override
454  protected List<LayoutFile> defaultVisit(Content cntnt) {
455  return Collections.emptyList();
456  }
457  }
458 
464  private class SortObjId implements Comparator<LayoutFile> {
465 
466  @Override
467  public int compare(LayoutFile o1, LayoutFile o2) {
468  if (o1.getId() == o2.getId()) {
469  return 0;
470  }
471  if (o1.getId() > o2.getId()) {
472  return 1;
473  } else {
474  return -1;
475  }
476  }
477  }
478 
483  private class UnallocStruct {
484 
485  private List<LayoutFile> llf;
486  private long SizeInBytes;
487  private long VolumeId;
488  private long ImageId;
489  private String ImageName;
490  private String FileName;
491  private File FileInstance;
492 
498  UnallocStruct(Image img) {
499  this.llf = getUnallocFiles(img);
500  Collections.sort(llf, new SortObjId());
501  this.VolumeId = 0;
502  this.ImageId = img.getId();
503  this.ImageName = img.getName();
504  this.FileName = this.ImageName + "-Unalloc-" + this.ImageId + "-" + 0 + ".dat"; //NON-NLS
505  this.FileInstance = new File(Case.getCurrentCase().getExportDirectory() + File.separator + this.FileName);
506  this.SizeInBytes = calcSizeInBytes();
507  }
508 
514  UnallocStruct(Volume volu) {
515  try {
516  this.ImageName = volu.getDataSource().getName();
517  this.ImageId = volu.getDataSource().getId();
518  this.VolumeId = volu.getId();
519  } catch (TskCoreException tce) {
520  logger.log(Level.WARNING, "Unable to properly create ExtractUnallocAction, extraction may be incomplete", tce); //NON-NLS
521  this.ImageName = "";
522  this.ImageId = 0;
523  }
524  this.FileName = this.ImageName + "-Unalloc-" + this.ImageId + "-" + VolumeId + ".dat"; //NON-NLS
525  this.FileInstance = new File(Case.getCurrentCase().getExportDirectory() + File.separator + this.FileName);
526  this.llf = getUnallocFiles(volu);
527  Collections.sort(llf, new SortObjId());
528  this.SizeInBytes = calcSizeInBytes();
529  }
530 
531  //Getters
532  int size() {
533  return llf.size();
534  }
535 
536  private long calcSizeInBytes() {
537  long size = 0L;
538  for (LayoutFile f : llf) {
539  size += f.getSize();
540  }
541  return size;
542  }
543 
544  long getSizeInBytes() {
545  return this.SizeInBytes;
546  }
547 
548  long getVolumeId() {
549  return this.VolumeId;
550  }
551 
552  long getImageId() {
553  return this.ImageId;
554  }
555 
556  String getImageName() {
557  return this.ImageName;
558  }
559 
560  List<LayoutFile> getLayouts() {
561  return this.llf;
562  }
563 
564  String getFileName() {
565  return this.FileName;
566  }
567 
568  File getFile() {
569  return this.FileInstance;
570  }
571 
572  void setPath(String path) {
573  this.FileInstance = new File(path + File.separator + this.FileName);
574  }
575  }
576 }
static void info(String title, String message)
List< LayoutFile > visit(final org.sleuthkit.datamodel.LayoutFile lf)
static void error(String title, String message)

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