Autopsy  4.8.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
SevenZipExtractor.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-2018 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.modules.embeddedfileextractor;
20 
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.OutputStream;
25 import java.nio.file.Files;
26 import java.nio.file.Paths;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.LinkedHashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.concurrent.ConcurrentHashMap;
36 import java.util.logging.Level;
37 import net.sf.sevenzipjbinding.ArchiveFormat;
38 import static net.sf.sevenzipjbinding.ArchiveFormat.RAR;
39 import net.sf.sevenzipjbinding.ExtractAskMode;
40 import net.sf.sevenzipjbinding.ISequentialOutStream;
41 import net.sf.sevenzipjbinding.ISevenZipInArchive;
42 import net.sf.sevenzipjbinding.SevenZip;
43 import net.sf.sevenzipjbinding.SevenZipException;
44 import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
45 import net.sf.sevenzipjbinding.ExtractOperationResult;
46 import net.sf.sevenzipjbinding.IArchiveExtractCallback;
47 import net.sf.sevenzipjbinding.ICryptoGetTextPassword;
48 import net.sf.sevenzipjbinding.PropID;
49 import org.netbeans.api.progress.ProgressHandle;
50 import org.openide.util.NbBundle;
51 import org.openide.util.NbBundle.Messages;
66 import org.sleuthkit.datamodel.AbstractFile;
67 import org.sleuthkit.datamodel.BlackboardArtifact;
68 import org.sleuthkit.datamodel.BlackboardAttribute;
69 import org.sleuthkit.datamodel.Content;
70 import org.sleuthkit.datamodel.DerivedFile;
71 import org.sleuthkit.datamodel.EncodedFileOutputStream;
72 import org.sleuthkit.datamodel.ReadContentInputStream;
73 import org.sleuthkit.datamodel.TskCoreException;
74 import org.sleuthkit.datamodel.TskData;
75 
76 class SevenZipExtractor {
77 
78  private static final Logger logger = Logger.getLogger(SevenZipExtractor.class.getName());
79  private IngestServices services = IngestServices.getInstance();
80  private final IngestJobContext context;
81  private final FileTypeDetector fileTypeDetector;
82  //encryption type strings
83  private static final String ENCRYPTION_FILE_LEVEL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
84  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel");
85  private static final String ENCRYPTION_FULL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
86  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull");
87  //zip bomb detection
88  private static final int MAX_DEPTH = 4;
89  private static final int MAX_COMPRESSION_RATIO = 600;
90  private static final long MIN_COMPRESSION_RATIO_SIZE = 500 * 1000000L;
91  private static final long MIN_FREE_DISK_SPACE = 1 * 1000 * 1000000L; //1GB
92 
93  private String moduleDirRelative;
94  private String moduleDirAbsolute;
95 
96  private Blackboard blackboard;
97 
98  private String getLocalRootAbsPath(String uniqueArchiveFileName) {
99  return moduleDirAbsolute + File.separator + uniqueArchiveFileName;
100  }
101 
106 
107  ZIP("application/zip"), //NON-NLS
108  SEVENZ("application/x-7z-compressed"), //NON-NLS
109  GZIP("application/gzip"), //NON-NLS
110  XGZIP("application/x-gzip"), //NON-NLS
111  XBZIP2("application/x-bzip2"), //NON-NLS
112  XTAR("application/x-tar"), //NON-NLS
113  XGTAR("application/x-gtar"),
114  XRAR("application/x-rar-compressed"); //NON-NLS
115 
116  private final String mimeType;
117 
118  SupportedArchiveExtractionFormats(final String mimeType) {
119  this.mimeType = mimeType;
120  }
121 
122  @Override
123  public String toString() {
124  return this.mimeType;
125  }
126  // TODO Expand to support more formats after upgrading Tika
127  }
128 
129  SevenZipExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) throws SevenZipNativeInitializationException {
130  if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) {
131  SevenZip.initSevenZipFromPlatformJAR();
132  }
133  this.context = context;
134  this.fileTypeDetector = fileTypeDetector;
135  this.moduleDirRelative = moduleDirRelative;
136  this.moduleDirAbsolute = moduleDirAbsolute;
137  }
138 
147  boolean isSevenZipExtractionSupported(AbstractFile file) {
148  String fileMimeType = fileTypeDetector.getMIMEType(file);
149  for (SupportedArchiveExtractionFormats mimeType : SupportedArchiveExtractionFormats.values()) {
150  if (mimeType.toString().equals(fileMimeType)) {
151  return true;
152  }
153  }
154  return false;
155  }
156 
179  private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISevenZipInArchive inArchive, int inArchiveItemIndex, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) {
180  try {
181  final Long archiveItemSize = (Long) inArchive.getProperty(
182  inArchiveItemIndex, PropID.SIZE);
183 
184  //skip the check for small files
185  if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) {
186  return false;
187  }
188 
189  final Long archiveItemPackedSize = (Long) inArchive.getProperty(
190  inArchiveItemIndex, PropID.PACKED_SIZE);
191 
192  if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) {
193  logger.log(Level.WARNING, "Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}", //NON-NLS
194  new Object[]{archiveFile.getName(), (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH)}); //NON-NLS
195  return false;
196  }
197 
198  int cRatio = (int) (archiveItemSize / archiveItemPackedSize);
199 
200  if (cRatio >= MAX_COMPRESSION_RATIO) {
201  Archive rootArchive = depthMap.get(depthMap.get(archiveFile.getId()).getRootArchiveId());
202  String details = NbBundle.getMessage(SevenZipExtractor.class,
203  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails",
204  cRatio, FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
205 
206  flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedFilePath);
207  return true;
208  } else {
209  return false;
210  }
211 
212  } catch (SevenZipException ex) {
213  logger.log(Level.WARNING, "Error getting archive item size and cannot detect if zipbomb. ", ex); //NON-NLS
214  return false;
215  }
216  }
217 
229  private void flagRootArchiveAsZipBomb(Archive rootArchive, AbstractFile archiveFile, String details, String escapedFilePath) {
230  rootArchive.flagAsZipBomb();
231  logger.log(Level.INFO, details); //NON-NLS
232  String msg = NbBundle.getMessage(SevenZipExtractor.class,
233  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg", archiveFile.getName(), escapedFilePath);
234  try {
235  BlackboardArtifact artifact = rootArchive.getArchiveFile().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
236  artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, EmbeddedFileExtractorModuleFactory.getModuleName(),
237  "Possible Zip Bomb"));
238  artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION,
239  EmbeddedFileExtractorModuleFactory.getModuleName(),
240  Bundle.SevenZipExtractor_zipBombArtifactCreation_text(archiveFile.getName())));
241  artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT,
242  EmbeddedFileExtractorModuleFactory.getModuleName(),
243  details));
244  try {
245  // index the artifact for keyword search
246  blackboard.indexArtifact(artifact);
247  } catch (Blackboard.BlackboardException ex) {
248  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
249  MessageNotifyUtil.Notify.error(
250  Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
251  }
252  services.fireModuleDataEvent(new ModuleDataEvent(EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT));
253  } catch (TskCoreException ex) {
254  logger.log(Level.SEVERE, "Error creating blackboard artifact for Zip Bomb Detection for file: " + escapedFilePath, ex); //NON-NLS
255  }
256  services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
257  }
258 
267  private ArchiveFormat get7ZipOptions(AbstractFile archiveFile) {
268  // try to get the file type from the BB
269  String detectedFormat;
270  detectedFormat = archiveFile.getMIMEType();
271 
272  if (detectedFormat == null) {
273  logger.log(Level.WARNING, "Could not detect format for file: {0}", archiveFile); //NON-NLS
274 
275  // if we don't have attribute info then use file extension
276  String extension = archiveFile.getNameExtension();
277  if ("rar".equals(extension)) //NON-NLS
278  {
279  // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
280  // it will be opened incorrectly when using 7zip's built-in auto-detect functionality
281  return RAR;
282  }
283 
284  // Otherwise open the archive using 7zip's built-in auto-detect functionality
285  return null;
286  } else if (detectedFormat.contains("application/x-rar-compressed")) //NON-NLS
287  {
288  // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
289  // it will be opened incorrectly when using 7zip's built-in auto-detect functionality
290  return RAR;
291  }
292 
293  // Otherwise open the archive using 7zip's built-in auto-detect functionality
294  return null;
295  }
296 
307  private long getRootArchiveId(AbstractFile file) throws TskCoreException {
308  long id = file.getId();
309  Content parentContent = file.getParent();
310  while (parentContent != null) {
311  id = parentContent.getId();
312  parentContent = parentContent.getParent();
313  }
314  return id;
315  }
316 
331  private List<AbstractFile> getAlreadyExtractedFiles(AbstractFile archiveFile, String archiveFilePath) throws TskCoreException, NoCurrentCaseException {
332  //check if already has derived files, skip
333  //check if local unpacked dir exists
334  if (archiveFile.hasChildren() && new File(moduleDirAbsolute, EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)).exists()) {
335  return Case.getCurrentCaseThrows().getServices().getFileManager().findFilesByParentPath(getRootArchiveId(archiveFile), archiveFilePath);
336  }
337  return new ArrayList<>();
338  }
339 
347  private String getArchiveFilePath(AbstractFile archiveFile) {
348  try {
349  return archiveFile.getUniquePath();
350  } catch (TskCoreException ex) {
351  return archiveFile.getParentPath() + archiveFile.getName();
352  }
353  }
354 
361  private void makeLocalDirectories(String uniqueArchiveFileName) {
362  final String localRootAbsPath = getLocalRootAbsPath(uniqueArchiveFileName);
363  final File localRoot = new File(localRootAbsPath);
364  if (!localRoot.exists()) {
365  localRoot.mkdirs();
366  }
367  }
368 
381  private String getPathInArchive(ISevenZipInArchive archive, int inArchiveItemIndex, AbstractFile archiveFile) throws SevenZipException {
382  String pathInArchive = (String) archive.getProperty(
383  inArchiveItemIndex, PropID.PATH);
384 
385  if (pathInArchive == null || pathInArchive.isEmpty()) {
386  //some formats (.tar.gz) may not be handled correctly -- file in archive has no name/path
387  //handle this for .tar.gz and tgz but assuming the child is tar,
388  //otherwise, unpack using itemNumber as name
389 
390  //TODO this should really be signature based, not extension based
391  String archName = archiveFile.getName();
392  int dotI = archName.lastIndexOf(".");
393  String useName = null;
394  if (dotI != -1) {
395  String base = archName.substring(0, dotI);
396  String ext = archName.substring(dotI);
397  int colonIndex = ext.lastIndexOf(":");
398  if (colonIndex != -1) {
399  // If alternate data stream is found, fix the name
400  // so Windows doesn't choke on the colon character.
401  ext = ext.substring(0, colonIndex);
402  }
403  switch (ext) {
404  case ".gz": //NON-NLS
405  useName = base;
406  break;
407  case ".tgz": //NON-NLS
408  useName = base + ".tar"; //NON-NLS
409  break;
410  case ".bz2": //NON-NLS
411  useName = base;
412  break;
413  }
414  }
415  if (useName == null) {
416  pathInArchive = "/" + archName + "/" + Integer.toString(inArchiveItemIndex);
417  } else {
418  pathInArchive = "/" + useName;
419  }
420  String msg = NbBundle.getMessage(SevenZipExtractor.class,
421  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg",
422  getArchiveFilePath(archiveFile), pathInArchive);
423  logger.log(Level.WARNING, msg);
424  }
425  return pathInArchive;
426  }
427 
428  /*
429  * Get the String that will represent the key for the hashmap which keeps
430  * track of existing files from an AbstractFile
431  */
432  private String getKeyAbstractFile(AbstractFile fileInDatabase) {
433  return fileInDatabase == null ? null : fileInDatabase.getParentPath() + fileInDatabase.getName();
434  }
435 
436  /*
437  * Get the String that will represent the key for the hashmap which keeps
438  * track of existing files from an unpacked node and the archiveFilePath
439  */
440  private String getKeyFromUnpackedNode(UnpackedTree.UnpackedNode node, String archiveFilePath) {
441  return node == null ? null : archiveFilePath + "/" + node.getFileName();
442  }
443 
453  void unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap) {
454  unpack(archiveFile, depthMap, null);
455  }
456 
468  @Messages({"SevenZipExtractor.indexError.message=Failed to index encryption detected artifact for keyword search.",
469  "# {0} - rootArchive",
470  "SevenZipExtractor.zipBombArtifactCreation.text=Zip Bomb Detected {0}"})
471  boolean unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap, String password) {
472  boolean unpackSuccessful = true; //initialized to true change to false if any files fail to extract and
473  boolean hasEncrypted = false;
474  boolean fullEncryption = true;
475  boolean progressStarted = false;
476  final String archiveFilePath = getArchiveFilePath(archiveFile);
477  final String escapedArchiveFilePath = FileUtil.escapeFileName(archiveFilePath);
478  HashMap<String, ZipFileStatusWrapper> statusMap = new HashMap<>();
479  List<AbstractFile> unpackedFiles = Collections.<AbstractFile>emptyList();
480  ISevenZipInArchive inArchive = null;
481 
482  SevenZipContentReadStream stream = null;
483  final ProgressHandle progress = ProgressHandle.createHandle(Bundle.EmbeddedFileExtractorIngestModule_ArchiveExtractor_moduleName());
484  //recursion depth check for zip bomb
485  Archive parentAr;
486  try {
487  blackboard = Case.getCurrentCaseThrows().getServices().getBlackboard();
488  } catch (NoCurrentCaseException ex) {
489  logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
490  unpackSuccessful = false;
491  return unpackSuccessful;
492  }
493  try {
494 
495  List<AbstractFile> existingFiles = getAlreadyExtractedFiles(archiveFile, archiveFilePath);
496  for (AbstractFile file : existingFiles) {
497  statusMap.put(getKeyAbstractFile(file), new ZipFileStatusWrapper(file, ZipFileStatus.EXISTS));
498  }
499  } catch (TskCoreException e) {
500  logger.log(Level.INFO, "Error checking if file already has been processed, skipping: {0}", escapedArchiveFilePath); //NON-NLS
501  unpackSuccessful = false;
502  return unpackSuccessful;
503  } catch (NoCurrentCaseException ex) {
504  logger.log(Level.INFO, "No open case was found while trying to unpack the archive file {0}", escapedArchiveFilePath); //NON-NLS
505  unpackSuccessful = false;
506  return unpackSuccessful;
507  }
508  parentAr = depthMap.get(archiveFile.getId());
509  if (parentAr == null) {
510  parentAr = new Archive(0, archiveFile.getId(), archiveFile);
511  depthMap.put(archiveFile.getId(), parentAr);
512  } else {
513  Archive rootArchive = depthMap.get(parentAr.getRootArchiveId());
514  if (rootArchive.isFlaggedAsZipBomb()) {
515  //skip this archive as the root archive has already been determined to contain a zip bomb
516  unpackSuccessful = false;
517  return unpackSuccessful;
518  } else if (parentAr.getDepth() == MAX_DEPTH) {
519  String details = NbBundle.getMessage(SevenZipExtractor.class,
520  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb",
521  parentAr.getDepth(), FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
522  flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedArchiveFilePath);
523  unpackSuccessful = false;
524  return unpackSuccessful;
525  }
526  }
527  try {
528  stream = new SevenZipContentReadStream(new ReadContentInputStream(archiveFile));
529  // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
530  // it will be opened incorrectly when using 7zip's built-in auto-detect functionality.
531  // All other archive formats are still opened using 7zip built-in auto-detect functionality.
532  ArchiveFormat options = get7ZipOptions(archiveFile);
533  if (password == null) {
534  inArchive = SevenZip.openInArchive(options, stream);
535  } else {
536  inArchive = SevenZip.openInArchive(options, stream, password);
537  }
538  int numItems = inArchive.getNumberOfItems();
539  logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{escapedArchiveFilePath, numItems}); //NON-NLS
540  progress.start(numItems);
541  progressStarted = true;
542  progress.progress(archiveFile.getName() + ": Analyzing archive metadata and creating local files");
543 
544  //setup the archive local root folder
545  final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile));
546  try {
547  makeLocalDirectories(uniqueArchiveFileName);
548  } catch (SecurityException e) {
549  logger.log(Level.SEVERE, "Error setting up output path for archive root: {0}", getLocalRootAbsPath(uniqueArchiveFileName)); //NON-NLS
550  //bail
551  unpackSuccessful = false;
552  return unpackSuccessful;
553  }
554 
555  //initialize tree hierarchy to keep track of unpacked file structure
556  SevenZipExtractor.UnpackedTree unpackedTree = new SevenZipExtractor.UnpackedTree(moduleDirRelative + "/" + uniqueArchiveFileName, archiveFile);
557 
558  long freeDiskSpace;
559  try {
560  freeDiskSpace = services.getFreeDiskSpace();
561  } catch (NullPointerException ex) {
562  //If ingest has not been run at least once getFreeDiskSpace() will throw a null pointer exception
563  //currently getFreeDiskSpace always returns DISK_FREE_SPACE_UNKNOWN
564  freeDiskSpace = IngestMonitor.DISK_FREE_SPACE_UNKNOWN;
565  }
566 
567  Map<Integer, InArchiveItemDetails> archiveDetailsMap = new LinkedHashMap<>();
568  for (int inArchiveItemIndex = 0; inArchiveItemIndex < numItems; inArchiveItemIndex++) {
569  if (isZipBombArchiveItemCheck(archiveFile, inArchive, inArchiveItemIndex, depthMap, escapedArchiveFilePath)) {
570  unpackSuccessful = false;
571  return unpackSuccessful;
572  }
573 
574  String pathInArchive = getPathInArchive(inArchive, inArchiveItemIndex, archiveFile);
575  SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive);
576 
577  final boolean isEncrypted = (Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.ENCRYPTED);
578 
579  if (isEncrypted && password == null) {
580  logger.log(Level.WARNING, "Skipping encrypted file in archive: {0}", pathInArchive); //NON-NLS
581  hasEncrypted = true;
582  unpackSuccessful = false;
583  continue;
584  } else {
585  fullEncryption = false;
586  }
587 
588  // NOTE: item size may return null in case of certain
589  // archiving formats. Eg: BZ2
590  //check if unpacking this file will result in out of disk space
591  //this is additional to zip bomb prevention mechanism
592  Long archiveItemSize = (Long) inArchive.getProperty(
593  inArchiveItemIndex, PropID.SIZE);
594  if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && archiveItemSize != null && archiveItemSize > 0) { //if free space is known and file is not empty.
595  String archiveItemPath = (String) inArchive.getProperty(
596  inArchiveItemIndex, PropID.PATH);
597  long newDiskSpace = freeDiskSpace - archiveItemSize;
598  if (newDiskSpace < MIN_FREE_DISK_SPACE) {
599  String msg = NbBundle.getMessage(SevenZipExtractor.class,
600  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
601  escapedArchiveFilePath, archiveItemPath);
602  String details = NbBundle.getMessage(SevenZipExtractor.class,
603  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
604  services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
605  logger.log(Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new String[]{escapedArchiveFilePath, archiveItemPath}); //NON-NLS
606  logger.log(Level.INFO, "Available disk space: {0}", new Object[]{freeDiskSpace}); //NON-NLS
607  unpackSuccessful = false;
608  continue; //skip this file
609  } else {
610  //update est. disk space during this archive, so we don't need to poll for every file extracted
611  freeDiskSpace = newDiskSpace;
612  }
613  }
614  final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (inArchiveItemIndex / 1000) + File.separator + inArchiveItemIndex + "_" + new File(pathInArchive).getName());
615  final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName;
616  final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName;
617 
618  //create local dirs and empty files before extracted
619  File localFile = new java.io.File(localAbsPath);
620  //cannot rely on files in top-bottom order
621  if (!localFile.exists()) {
622  try {
623  if ((Boolean) inArchive.getProperty(
624  inArchiveItemIndex, PropID.IS_FOLDER)) {
625  localFile.mkdirs();
626  } else {
627  localFile.getParentFile().mkdirs();
628  try {
629  localFile.createNewFile();
630  } catch (IOException e) {
631  logger.log(Level.SEVERE, "Error creating extracted file: "//NON-NLS
632  + localFile.getAbsolutePath(), e);
633  }
634  }
635  } catch (SecurityException e) {
636  logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", //NON-NLS
637  pathInArchive); //NON-NLS
638  //TODO consider bail out / msg to the user
639  }
640  }
641  // skip the rest of this loop if we couldn't create the file
642  //continue will skip details from being added to the map
643  if (localFile.exists() == false) {
644  continue;
645  }
646 
647  //Store archiveItemIndex with local paths and unpackedNode reference.
648  //Necessary for the extract call back to write the current archive
649  //file to the correct disk location and to correctly update it's
650  //corresponding unpackedNode
651  archiveDetailsMap.put(inArchiveItemIndex, new InArchiveItemDetails(
652  unpackedNode, localAbsPath, localRelPath));
653  }
654 
655  int[] extractionIndices = getExtractableFilesFromDetailsMap(archiveDetailsMap);
656 
657  StandardIArchiveExtractCallback archiveCallBack
658  = new StandardIArchiveExtractCallback(
659  inArchive, archiveFile, progress,
660  archiveDetailsMap, password, freeDiskSpace);
661 
662  //According to the documentation, indices in sorted order are optimal
663  //for efficiency. Hence, the LinkedHashMap and linear processing of
664  //inArchiveItemIndex. False indicates non-test mode
665  inArchive.extract(extractionIndices, false, archiveCallBack);
666 
667  unpackSuccessful = unpackSuccessful & archiveCallBack.wasSuccessful();
668 
669  // add them to the DB. We wait until the end so that we have the metadata on all of the
670  // intermediate nodes since the order is not guaranteed
671  try {
672  unpackedTree.updateOrAddFileToCaseRec(statusMap, archiveFilePath);
673  unpackedFiles = unpackedTree.getAllFileObjects();
674  //check if children are archives, update archive depth tracking
675  for (AbstractFile unpackedFile : unpackedFiles) {
676  if (unpackedFile == null) {
677  continue;
678  }
679  if (isSevenZipExtractionSupported(unpackedFile)) {
680  Archive child = new Archive(parentAr.getDepth() + 1, parentAr.getRootArchiveId(), archiveFile);
681  parentAr.addChild(child);
682  depthMap.put(unpackedFile.getId(), child);
683  }
684  }
685 
686  } catch (TskCoreException | NoCurrentCaseException e) {
687  logger.log(Level.SEVERE, "Error populating complete derived file hierarchy from the unpacked dir structure", e); //NON-NLS
688  //TODO decide if anything to cleanup, for now bailing
689  }
690 
691  } catch (SevenZipException ex) {
692  logger.log(Level.WARNING, "Error unpacking file: " + archiveFile, ex); //NON-NLS
693  //inbox message
694 
695  // print a message if the file is allocated
696  if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
697  String msg = NbBundle.getMessage(SevenZipExtractor.class,
698  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg",
699  archiveFile.getName());
700  String details = NbBundle.getMessage(SevenZipExtractor.class,
701  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details",
702  escapedArchiveFilePath, ex.getMessage());
703  services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
704  }
705  } finally {
706  if (inArchive != null) {
707  try {
708  inArchive.close();
709  } catch (SevenZipException e) {
710  logger.log(Level.SEVERE, "Error closing archive: " + archiveFile, e); //NON-NLS
711  }
712  }
713 
714  if (stream != null) {
715  try {
716  stream.close();
717  } catch (IOException ex) {
718  logger.log(Level.SEVERE, "Error closing stream after unpacking archive: " + archiveFile, ex); //NON-NLS
719  }
720  }
721 
722  //close progress bar
723  if (progressStarted) {
724  progress.finish();
725  }
726  }
727 
728  //create artifact and send user message
729  if (hasEncrypted) {
730  String encryptionType = fullEncryption ? ENCRYPTION_FULL : ENCRYPTION_FILE_LEVEL;
731  try {
732  BlackboardArtifact artifact = archiveFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
733  artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, EmbeddedFileExtractorModuleFactory.getModuleName(), encryptionType));
734 
735  try {
736  // index the artifact for keyword search
737  blackboard.indexArtifact(artifact);
738  } catch (Blackboard.BlackboardException ex) {
739  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
740  MessageNotifyUtil.Notify.error(
741  Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
742  }
743 
744  services.fireModuleDataEvent(new ModuleDataEvent(EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED));
745  } catch (TskCoreException ex) {
746  logger.log(Level.SEVERE, "Error creating blackboard artifact for encryption detected for file: " + escapedArchiveFilePath, ex); //NON-NLS
747  }
748 
749  String msg = NbBundle.getMessage(SevenZipExtractor.class,
750  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg");
751  String details = NbBundle.getMessage(SevenZipExtractor.class,
752  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details",
753  archiveFile.getName(), EmbeddedFileExtractorModuleFactory.getModuleName());
754  services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
755  }
756 
757  // adding unpacked extracted derived files to the job after closing relevant resources.
758  if (!unpackedFiles.isEmpty()) {
759  //currently sending a single event for all new files
760  services.fireModuleContentEvent(new ModuleContentEvent(archiveFile));
761  if (context != null) {
762  context.addFilesToJob(unpackedFiles);
763  }
764  }
765  return unpackSuccessful;
766  }
767 
772  private int[] getExtractableFilesFromDetailsMap(
773  Map<Integer, InArchiveItemDetails> archiveDetailsMap) {
774 
775  Integer[] wrappedExtractionIndices = archiveDetailsMap.keySet()
776  .toArray(new Integer[archiveDetailsMap.size()]);
777 
778  return Arrays.stream(wrappedExtractionIndices)
779  .mapToInt(Integer::intValue)
780  .toArray();
781  }
782 
786  private abstract static class UnpackStream implements ISequentialOutStream {
787 
788  private OutputStream output;
789  private String localAbsPath;
790 
791  UnpackStream(String localAbsPath) {
792  this.localAbsPath = localAbsPath;
793  try {
794  output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
795  } catch (IOException ex) {
796  logger.log(Level.SEVERE, "Error writing extracted file: " + localAbsPath, ex); //NON-NLS
797  }
798 
799  }
800 
801  public abstract long getSize();
802 
803  OutputStream getOutput() {
804  return output;
805  }
806 
807  String getLocalAbsPath() {
808  return localAbsPath;
809  }
810 
811  public void close() {
812  if (output != null) {
813  try {
814  output.flush();
815  output.close();
816  } catch (IOException e) {
817  logger.log(Level.SEVERE, "Error closing unpack stream for file: {0}", localAbsPath); //NON-NLS
818  }
819  }
820  }
821  }
822 
826  private static class UnknownSizeUnpackStream extends UnpackStream {
827 
828  private long freeDiskSpace;
829  private boolean outOfSpace = false;
830  private long bytesWritten = 0;
831 
832  UnknownSizeUnpackStream(String localAbsPath, long freeDiskSpace) {
833  super(localAbsPath);
834  this.freeDiskSpace = freeDiskSpace;
835  }
836 
837  @Override
838  public long getSize() {
839  return this.bytesWritten;
840  }
841 
842  @Override
843  public int write(byte[] bytes) throws SevenZipException {
844  try {
845  // If the content size is unknown, cautiously write to disk.
846  // Write only if byte array is less than 80% of the current
847  // free disk space.
848  if (freeDiskSpace == IngestMonitor.DISK_FREE_SPACE_UNKNOWN || bytes.length < 0.8 * freeDiskSpace) {
849  getOutput().write(bytes);
850  // NOTE: this method is called multiple times for a
851  // single extractSlow() call. Update bytesWritten and
852  // freeDiskSpace after every write operation.
853  this.bytesWritten += bytes.length;
854  this.freeDiskSpace -= bytes.length;
855  } else {
856  this.outOfSpace = true;
857  logger.log(Level.INFO, NbBundle.getMessage(
858  SevenZipExtractor.class,
859  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.noSpace.msg"));
860  throw new SevenZipException(
861  NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.noSpace.msg"));
862  }
863  } catch (IOException ex) {
864  throw new SevenZipException(
865  NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg",
866  getLocalAbsPath()), ex);
867  }
868  return bytes.length;
869  }
870 
871  @Override
872  public void close() {
873  if (getOutput() != null) {
874  try {
875  getOutput().flush();
876  getOutput().close();
877  if (this.outOfSpace) {
878  Files.delete(Paths.get(getLocalAbsPath()));
879  }
880  } catch (IOException e) {
881  logger.log(Level.SEVERE, "Error closing unpack stream for file: {0}", getLocalAbsPath()); //NON-NLS
882  }
883  }
884  }
885  }
886 
890  private static class KnownSizeUnpackStream extends UnpackStream {
891 
892  private long size;
893 
894  KnownSizeUnpackStream(String localAbsPath, long size) {
895  super(localAbsPath);
896  this.size = size;
897  }
898 
899  @Override
900  public long getSize() {
901  return this.size;
902  }
903 
904  @Override
905  public int write(byte[] bytes) throws SevenZipException {
906  try {
907  getOutput().write(bytes);
908  } catch (IOException ex) {
909  throw new SevenZipException(
910  NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg",
911  getLocalAbsPath()), ex);
912  }
913  return bytes.length;
914  }
915  }
916 
920  private static class InArchiveItemDetails {
921 
922  private final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode;
923  private final String localAbsPath;
924  private final String localRelPath;
925 
927  SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode,
928  String localAbsPath, String localRelPath) {
929  this.unpackedNode = unpackedNode;
930  this.localAbsPath = localAbsPath;
931  this.localRelPath = localRelPath;
932  }
933 
934  public SevenZipExtractor.UnpackedTree.UnpackedNode getUnpackedNode() {
935  return unpackedNode;
936  }
937 
938  public String getLocalAbsPath() {
939  return localAbsPath;
940  }
941 
942  public String getLocalRelPath() {
943  return localRelPath;
944  }
945  }
946 
951  private static class StandardIArchiveExtractCallback
952  implements IArchiveExtractCallback, ICryptoGetTextPassword {
953 
954  private final AbstractFile archiveFile;
955  private final ISevenZipInArchive inArchive;
956  private SevenZipExtractor.UnpackStream unpackStream = null;
957  private final Map<Integer, InArchiveItemDetails> archiveDetailsMap;
958  private final ProgressHandle progressHandle;
959 
960  private int inArchiveItemIndex;
961  private final long freeDiskSpace;
962 
963  private long createTimeInSeconds;
964  private long modTimeInSeconds;
965  private long accessTimeInSeconds;
966 
967  private boolean isFolder;
968  private final String password;
969 
970  private boolean unpackSuccessful = true;
971 
972  public StandardIArchiveExtractCallback(ISevenZipInArchive inArchive,
973  AbstractFile archiveFile, ProgressHandle progressHandle,
974  Map<Integer, InArchiveItemDetails> archiveDetailsMap,
975  String password, long freeDiskSpace) {
976 
977  this.inArchive = inArchive;
978  this.freeDiskSpace = freeDiskSpace;
979  this.progressHandle = progressHandle;
980  this.archiveFile = archiveFile;
981  this.archiveDetailsMap = archiveDetailsMap;
982  this.password = password;
983  }
984 
997  @Override
998  public ISequentialOutStream getStream(int inArchiveItemIndex,
999  ExtractAskMode mode) throws SevenZipException {
1000 
1001  this.inArchiveItemIndex = inArchiveItemIndex;
1002 
1003  isFolder = (Boolean) inArchive
1004  .getProperty(inArchiveItemIndex, PropID.IS_FOLDER);
1005  if (isFolder || mode != ExtractAskMode.EXTRACT) {
1006  return null;
1007  }
1008 
1009  final Long archiveItemSize = (Long) inArchive.getProperty(
1010  inArchiveItemIndex, PropID.SIZE);
1011  final String localAbsPath = archiveDetailsMap.get(
1012  inArchiveItemIndex).getLocalAbsPath();
1013 
1014  if (archiveItemSize != null) {
1015  unpackStream = new SevenZipExtractor.KnownSizeUnpackStream(
1016  localAbsPath, archiveItemSize);
1017  } else {
1018  unpackStream = new SevenZipExtractor.UnknownSizeUnpackStream(
1019  localAbsPath, freeDiskSpace);
1020  }
1021 
1022  return unpackStream;
1023  }
1024 
1032  @Override
1033  public void prepareOperation(ExtractAskMode mode) throws SevenZipException {
1034  final Date createTime = (Date) inArchive.getProperty(
1035  inArchiveItemIndex, PropID.CREATION_TIME);
1036  final Date accessTime = (Date) inArchive.getProperty(
1037  inArchiveItemIndex, PropID.LAST_ACCESS_TIME);
1038  final Date writeTime = (Date) inArchive.getProperty(
1039  inArchiveItemIndex, PropID.LAST_WRITE_TIME);
1040 
1041  createTimeInSeconds = createTime == null ? 0L
1042  : createTime.getTime() / 1000;
1043  modTimeInSeconds = writeTime == null ? 0L
1044  : writeTime.getTime() / 1000;
1045  accessTimeInSeconds = accessTime == null ? 0L
1046  : accessTime.getTime() / 1000;
1047  }
1048 
1057  @Override
1058  public void setOperationResult(ExtractOperationResult result) throws SevenZipException {
1059  progressHandle.progress(archiveFile.getName() + ": "
1060  + (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH),
1062 
1063  final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode
1064  = archiveDetailsMap.get(inArchiveItemIndex).getUnpackedNode();
1065  final String localRelPath = archiveDetailsMap.get(
1066  inArchiveItemIndex).getLocalRelPath();
1067  if (isFolder) {
1068  unpackedNode.addDerivedInfo(0,
1069  !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1071  localRelPath);
1072  return;
1073  }
1074 
1075  final String localAbsPath = archiveDetailsMap.get(
1076  inArchiveItemIndex).getLocalAbsPath();
1077  if (result != ExtractOperationResult.OK) {
1078  logger.log(Level.WARNING, "Extraction of : {0} encountered error {1}", //NON-NLS
1079  new Object[]{localAbsPath, result});
1080  unpackSuccessful = false;
1081  }
1082 
1083  //record derived data in unode, to be traversed later after unpacking the archive
1084  unpackedNode.addDerivedInfo(unpackStream.getSize(),
1085  !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1087 
1088  unpackStream.close();
1089  }
1090 
1091  @Override
1092  public void setTotal(long value) throws SevenZipException {
1093  //Not necessary for extract, left intenionally blank
1094  }
1095 
1096  @Override
1097  public void setCompleted(long value) throws SevenZipException {
1098  //Not necessary for extract, left intenionally blank
1099  }
1100 
1108  @Override
1109  public String cryptoGetTextPassword() throws SevenZipException {
1110  return password;
1111  }
1112 
1113  public boolean wasSuccessful() {
1114  return unpackSuccessful;
1115  }
1116  }
1117 
1125  private class UnpackedTree {
1126 
1127  final UnpackedNode rootNode;
1128 
1136  UnpackedTree(String localPathRoot, AbstractFile archiveFile) {
1137  this.rootNode = new UnpackedNode();
1138  this.rootNode.setFile(archiveFile);
1139  this.rootNode.setFileName(archiveFile.getName());
1140  this.rootNode.setLocalRelPath(localPathRoot);
1141  }
1142 
1152  UnpackedNode addNode(String filePath) {
1153  String[] toks = filePath.split("[\\/\\\\]");
1154  List<String> tokens = new ArrayList<>();
1155  for (int i = 0; i < toks.length; ++i) {
1156  if (!toks[i].isEmpty()) {
1157  tokens.add(toks[i]);
1158  }
1159  }
1160  return addNode(rootNode, tokens);
1161  }
1162 
1171  private UnpackedNode addNode(UnpackedNode parent, List<String> tokenPath) {
1172  // we found all of the tokens
1173  if (tokenPath.isEmpty()) {
1174  return parent;
1175  }
1176 
1177  // get the next name in the path and look it up
1178  String childName = tokenPath.remove(0);
1179  UnpackedNode child = parent.getChild(childName);
1180  // create new node
1181  if (child == null) {
1182  child = new UnpackedNode(childName, parent);
1183  parent.addChild(child);
1184  }
1185 
1186  // go down one more level
1187  return addNode(child, tokenPath);
1188  }
1189 
1196  List<AbstractFile> getRootFileObjects() {
1197  List<AbstractFile> ret = new ArrayList<>();
1198  for (UnpackedNode child : rootNode.getChildren()) {
1199  ret.add(child.getFile());
1200  }
1201  return ret;
1202  }
1203 
1210  List<AbstractFile> getAllFileObjects() {
1211  List<AbstractFile> ret = new ArrayList<>();
1212  for (UnpackedNode child : rootNode.getChildren()) {
1213  getAllFileObjectsRec(ret, child);
1214  }
1215  return ret;
1216  }
1217 
1218  private void getAllFileObjectsRec(List<AbstractFile> list, UnpackedNode parent) {
1219  list.add(parent.getFile());
1220  for (UnpackedNode child : parent.getChildren()) {
1221  getAllFileObjectsRec(list, child);
1222  }
1223  }
1224 
1229  void updateOrAddFileToCaseRec(HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath) throws TskCoreException, NoCurrentCaseException {
1231  for (UnpackedNode child : rootNode.getChildren()) {
1232  updateOrAddFileToCaseRec(child, fileManager, statusMap, archiveFilePath);
1233  }
1234  }
1235 
1250  private void updateOrAddFileToCaseRec(UnpackedNode node, FileManager fileManager, HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath) throws TskCoreException {
1251  DerivedFile df;
1252  try {
1253  String nameInDatabase = getKeyFromUnpackedNode(node, archiveFilePath);
1254  ZipFileStatusWrapper existingFile = nameInDatabase == null ? null : statusMap.get(nameInDatabase);
1255  if (existingFile == null) {
1256  df = fileManager.addDerivedFile(node.getFileName(), node.getLocalRelPath(), node.getSize(),
1257  node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1258  node.isIsFile(), node.getParent().getFile(), "", EmbeddedFileExtractorModuleFactory.getModuleName(),
1259  "", "", TskData.EncodingType.XOR1);
1260  statusMap.put(getKeyAbstractFile(df), new ZipFileStatusWrapper(df, ZipFileStatus.EXISTS));
1261  } else {
1262  String key = getKeyAbstractFile(existingFile.getFile());
1263  if (existingFile.getStatus() == ZipFileStatus.EXISTS && existingFile.getFile().getSize() < node.getSize()) {
1264  existingFile.setStatus(ZipFileStatus.UPDATE);
1265  statusMap.put(key, existingFile);
1266  }
1267  if (existingFile.getStatus() == ZipFileStatus.UPDATE) {
1268  //if the we are updating a file and its mime type was octet-stream we want to re-type it
1269  String mimeType = existingFile.getFile().getMIMEType().equalsIgnoreCase("application/octet-stream") ? null : existingFile.getFile().getMIMEType();
1270  df = fileManager.updateDerivedFile((DerivedFile) existingFile.getFile(), node.getLocalRelPath(), node.getSize(),
1271  node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1272  node.isIsFile(), mimeType, "", EmbeddedFileExtractorModuleFactory.getModuleName(),
1273  "", "", TskData.EncodingType.XOR1);
1274  } else {
1275  //ALREADY CURRENT - SKIP
1276  statusMap.put(key, new ZipFileStatusWrapper(existingFile.getFile(), ZipFileStatus.SKIP));
1277  df = (DerivedFile) existingFile.getFile();
1278  }
1279  }
1280  node.setFile(df);
1281  } catch (TskCoreException ex) {
1282  logger.log(Level.SEVERE, "Error adding a derived file to db:" + node.getFileName(), ex); //NON-NLS
1283  throw new TskCoreException(
1284  NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg",
1285  node.getFileName()), ex);
1286  }
1287  //recurse adding the children if this file was incomplete the children presumably need to be added
1288  for (UnpackedNode child : node.getChildren()) {
1289  updateOrAddFileToCaseRec(child, fileManager, statusMap, getKeyFromUnpackedNode(node, archiveFilePath));
1290  }
1291  }
1292 
1296  private class UnpackedNode {
1297 
1298  private String fileName;
1299  private AbstractFile file;
1300  private final List<UnpackedNode> children = new ArrayList<>();
1301  private String localRelPath = "";
1302  private long size;
1303  private long ctime, crtime, atime, mtime;
1304  private boolean isFile;
1306 
1307  //root constructor
1308  UnpackedNode() {
1309  }
1310 
1311  //child node constructor
1312  UnpackedNode(String fileName, UnpackedNode parent) {
1313  this.fileName = fileName;
1314  this.parent = parent;
1315  this.localRelPath = parent.getLocalRelPath() + File.separator + fileName;
1316  }
1317 
1318  long getCtime() {
1319  return ctime;
1320  }
1321 
1322  long getCrtime() {
1323  return crtime;
1324  }
1325 
1326  long getAtime() {
1327  return atime;
1328  }
1329 
1330  long getMtime() {
1331  return mtime;
1332  }
1333 
1334  void setFileName(String fileName) {
1335  this.fileName = fileName;
1336  }
1337 
1343  void addChild(UnpackedNode child) {
1344  children.add(child);
1345  }
1346 
1353  List<UnpackedNode> getChildren() {
1354  return children;
1355  }
1356 
1362  UnpackedNode getParent() {
1363  return parent;
1364  }
1365 
1366  void addDerivedInfo(long size,
1367  boolean isFile,
1368  long ctime, long crtime, long atime, long mtime, String relLocalPath) {
1369  this.size = size;
1370  this.isFile = isFile;
1371  this.ctime = ctime;
1372  this.crtime = crtime;
1373  this.atime = atime;
1374  this.mtime = mtime;
1375  this.localRelPath = relLocalPath;
1376  }
1377 
1378  void setFile(AbstractFile file) {
1379  this.file = file;
1380  }
1381 
1389  UnpackedNode getChild(String childFileName) {
1390  UnpackedNode ret = null;
1391  for (UnpackedNode child : children) {
1392  if (child.getFileName().equals(childFileName)) {
1393  ret = child;
1394  break;
1395  }
1396  }
1397  return ret;
1398  }
1399 
1400  String getFileName() {
1401  return fileName;
1402  }
1403 
1404  AbstractFile getFile() {
1405  return file;
1406  }
1407 
1408  String getLocalRelPath() {
1409  return localRelPath;
1410  }
1411 
1418  void setLocalRelPath(String localRelativePath) {
1419  localRelPath = localRelativePath;
1420  }
1421 
1422  long getSize() {
1423  return size;
1424  }
1425 
1426  boolean isIsFile() {
1427  return isFile;
1428  }
1429  }
1430  }
1431 
1436  static class Archive {
1437 
1438  //depth will be 0 for the root archive unpack was called on, and increase as unpack recurses down through archives contained within
1439  private final int depth;
1440  private final List<Archive> children;
1441  private final long rootArchiveId;
1442  private boolean flaggedAsZipBomb = false;
1443  private final AbstractFile archiveFile;
1444 
1457  Archive(int depth, long rootArchiveId, AbstractFile archiveFile) {
1458  this.children = new ArrayList<>();
1459  this.depth = depth;
1460  this.rootArchiveId = rootArchiveId;
1461  this.archiveFile = archiveFile;
1462  }
1463 
1470  void addChild(Archive child) {
1471  children.add(child);
1472  }
1473 
1478  synchronized void flagAsZipBomb() {
1479  flaggedAsZipBomb = true;
1480  }
1481 
1487  synchronized boolean isFlaggedAsZipBomb() {
1488  return flaggedAsZipBomb;
1489  }
1490 
1496  AbstractFile getArchiveFile() {
1497  return archiveFile;
1498  }
1499 
1505  long getRootArchiveId() {
1506  return rootArchiveId;
1507  }
1508 
1514  long getObjectId() {
1515  return archiveFile.getId();
1516  }
1517 
1525  int getDepth() {
1526  return depth;
1527  }
1528  }
1529 
1534  private final class ZipFileStatusWrapper {
1535 
1536  private final AbstractFile abstractFile;
1538 
1546  private ZipFileStatusWrapper(AbstractFile file, ZipFileStatus status) {
1547  abstractFile = file;
1548  zipStatus = status;
1549  }
1550 
1556  private AbstractFile getFile() {
1557  return abstractFile;
1558  }
1559 
1566  return zipStatus;
1567  }
1568 
1574  private void setStatus(ZipFileStatus status) {
1575  zipStatus = status;
1576  }
1577 
1578  }
1579 
1584  private enum ZipFileStatus {
1585  UPDATE, //Should be updated //NON-NLS
1586  SKIP, //File is current can be skipped //NON-NLS
1587  EXISTS //File exists but it is unknown if it is current //NON-NLS
1588  }
1589 }
void updateOrAddFileToCaseRec(UnpackedNode node, FileManager fileManager, HashMap< String, ZipFileStatusWrapper > statusMap, String archiveFilePath)
synchronized DerivedFile addDerivedFile(String fileName, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, Content parentObj, String rederiveDetails, String toolName, String toolVersion, String otherDetails, TskData.EncodingType encodingType)
UnpackedNode addNode(UnpackedNode parent, List< String > tokenPath)
InArchiveItemDetails(SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode, String localAbsPath, String localRelPath)
StandardIArchiveExtractCallback(ISevenZipInArchive inArchive, AbstractFile archiveFile, ProgressHandle progressHandle, Map< Integer, InArchiveItemDetails > archiveDetailsMap, String password, long freeDiskSpace)
synchronized DerivedFile updateDerivedFile(DerivedFile derivedFile, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, String mimeType, String rederiveDetails, String toolName, String toolVersion, String otherDetails, TskData.EncodingType encodingType)

Copyright © 2012-2018 Basis Technology. Generated on: Thu Oct 4 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.