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