Autopsy  4.10.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-2019 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.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.Date;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.concurrent.ConcurrentHashMap;
33 import java.util.logging.Level;
34 import net.sf.sevenzipjbinding.ArchiveFormat;
35 import static net.sf.sevenzipjbinding.ArchiveFormat.RAR;
36 import net.sf.sevenzipjbinding.ExtractAskMode;
37 import net.sf.sevenzipjbinding.ISequentialOutStream;
38 import net.sf.sevenzipjbinding.ISevenZipInArchive;
39 import net.sf.sevenzipjbinding.SevenZip;
40 import net.sf.sevenzipjbinding.SevenZipException;
41 import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
42 import net.sf.sevenzipjbinding.ExtractOperationResult;
43 import net.sf.sevenzipjbinding.IArchiveExtractCallback;
44 import net.sf.sevenzipjbinding.ICryptoGetTextPassword;
45 import net.sf.sevenzipjbinding.PropID;
46 import org.netbeans.api.progress.ProgressHandle;
47 import org.openide.util.NbBundle;
48 import org.openide.util.NbBundle.Messages;
63 import org.sleuthkit.datamodel.AbstractFile;
64 import org.sleuthkit.datamodel.BlackboardArtifact;
65 import org.sleuthkit.datamodel.BlackboardAttribute;
66 import org.sleuthkit.datamodel.Content;
67 import org.sleuthkit.datamodel.DerivedFile;
68 import org.sleuthkit.datamodel.EncodedFileOutputStream;
69 import org.sleuthkit.datamodel.ReadContentInputStream;
70 import org.sleuthkit.datamodel.SleuthkitCase;
71 import org.sleuthkit.datamodel.TskCoreException;
72 import org.sleuthkit.datamodel.TskData;
73 
74 class SevenZipExtractor {
75 
76  private static final Logger logger = Logger.getLogger(SevenZipExtractor.class.getName());
77  private IngestServices services = IngestServices.getInstance();
78  private final IngestJobContext context;
79  private final FileTypeDetector fileTypeDetector;
80  //encryption type strings
81  private static final String ENCRYPTION_FILE_LEVEL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
82  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel");
83  private static final String ENCRYPTION_FULL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
84  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull");
85  //zip bomb detection
86  private static final int MAX_DEPTH = 4;
87  private static final int MAX_COMPRESSION_RATIO = 600;
88  private static final long MIN_COMPRESSION_RATIO_SIZE = 500 * 1000000L;
89  private static final long MIN_FREE_DISK_SPACE = 1 * 1000 * 1000000L; //1GB
90 
91  private String moduleDirRelative;
92  private String moduleDirAbsolute;
93 
94  private Blackboard blackboard;
95 
96  private ProgressHandle progress;
97  private int numItems;
98  private String currentArchiveName;
99 
100  private String getLocalRootAbsPath(String uniqueArchiveFileName) {
101  return moduleDirAbsolute + File.separator + uniqueArchiveFileName;
102  }
103 
108 
109  ZIP("application/zip"), //NON-NLS
110  SEVENZ("application/x-7z-compressed"), //NON-NLS
111  GZIP("application/gzip"), //NON-NLS
112  XGZIP("application/x-gzip"), //NON-NLS
113  XBZIP2("application/x-bzip2"), //NON-NLS
114  XTAR("application/x-tar"), //NON-NLS
115  XGTAR("application/x-gtar"),
116  XRAR("application/x-rar-compressed"); //NON-NLS
117 
118  private final String mimeType;
119 
120  SupportedArchiveExtractionFormats(final String mimeType) {
121  this.mimeType = mimeType;
122  }
123 
124  @Override
125  public String toString() {
126  return this.mimeType;
127  }
128  // TODO Expand to support more formats after upgrading Tika
129  }
130 
131  SevenZipExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) throws SevenZipNativeInitializationException {
132  if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) {
133  SevenZip.initSevenZipFromPlatformJAR();
134  }
135  this.context = context;
136  this.fileTypeDetector = fileTypeDetector;
137  this.moduleDirRelative = moduleDirRelative;
138  this.moduleDirAbsolute = moduleDirAbsolute;
139  }
140 
149  boolean isSevenZipExtractionSupported(AbstractFile file) {
150  String fileMimeType = fileTypeDetector.getMIMEType(file);
151  for (SupportedArchiveExtractionFormats mimeType : SupportedArchiveExtractionFormats.values()) {
152  if (mimeType.toString().equals(fileMimeType)) {
153  return true;
154  }
155  }
156  return false;
157  }
158 
182  private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISevenZipInArchive inArchive, int inArchiveItemIndex, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) {
183  //If a file is corrupted as a result of reconstructing it from unallocated space, then
184  //7zip does a poor job estimating the original uncompressed file size.
185  //As a result, many corrupted files have wonky compression ratios and could flood the UI
186  //with false zip bomb notifications. The decision was made to skip compression ratio checks
187  //for unallocated zip files. Instead, we let the depth be an indicator of a zip bomb.
188  if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC)) {
189  return false;
190  }
191 
192  try {
193  final Long archiveItemSize = (Long) inArchive.getProperty(
194  inArchiveItemIndex, PropID.SIZE);
195 
196  //skip the check for small files
197  if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) {
198  return false;
199  }
200 
201  final Long archiveItemPackedSize = (Long) inArchive.getProperty(
202  inArchiveItemIndex, PropID.PACKED_SIZE);
203 
204  if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) {
205  logger.log(Level.WARNING, "Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}", //NON-NLS
206  new Object[]{archiveFile.getName(), (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH)}); //NON-NLS
207  return false;
208  }
209 
210  int cRatio = (int) (archiveItemSize / archiveItemPackedSize);
211 
212  if (cRatio >= MAX_COMPRESSION_RATIO) {
213  Archive rootArchive = depthMap.get(depthMap.get(archiveFile.getId()).getRootArchiveId());
214  String details = NbBundle.getMessage(SevenZipExtractor.class,
215  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails",
216  cRatio, FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
217 
218  flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedFilePath);
219  return true;
220  } else {
221  return false;
222  }
223 
224  } catch (SevenZipException ex) {
225  logger.log(Level.WARNING, "Error getting archive item size and cannot detect if zipbomb. ", ex); //NON-NLS
226  return false;
227  }
228  }
229 
241  private void flagRootArchiveAsZipBomb(Archive rootArchive, AbstractFile archiveFile, String details, String escapedFilePath) {
242  rootArchive.flagAsZipBomb();
243  logger.log(Level.INFO, details); //NON-NLS
244  String msg = NbBundle.getMessage(SevenZipExtractor.class,
245  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg", archiveFile.getName(), escapedFilePath);
246  try {
247  Collection<BlackboardAttribute> attributes = new ArrayList<>();
248  attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, EmbeddedFileExtractorModuleFactory.getModuleName(),
249  "Possible Zip Bomb"));
250  attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION,
251  EmbeddedFileExtractorModuleFactory.getModuleName(),
252  Bundle.SevenZipExtractor_zipBombArtifactCreation_text(archiveFile.getName())));
253  attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT,
254  EmbeddedFileExtractorModuleFactory.getModuleName(),
255  details));
256 
257  SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
258  org.sleuthkit.datamodel.Blackboard tskBlackboard = tskCase.getBlackboard();
259  // Create artifact if it doesn't already exist.
260  if (!tskBlackboard.artifactExists(archiveFile, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, attributes)) {
261  BlackboardArtifact artifact = rootArchive.getArchiveFile().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
262  artifact.addAttributes(attributes);
263 
264  try {
265  // index the artifact for keyword search
266  blackboard.indexArtifact(artifact);
267  } catch (Blackboard.BlackboardException ex) {
268  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
269  MessageNotifyUtil.Notify.error(
270  Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
271  }
272 
273  services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
274 
275  services.fireModuleDataEvent(new ModuleDataEvent(EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT));
276  }
277  } catch (TskCoreException ex) {
278  logger.log(Level.SEVERE, "Error creating blackboard artifact for Zip Bomb Detection for file: " + escapedFilePath, ex); //NON-NLS
279  } catch (NoCurrentCaseException ex) {
280  logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
281  }
282  }
283 
292  private ArchiveFormat get7ZipOptions(AbstractFile archiveFile) {
293  // try to get the file type from the BB
294  String detectedFormat;
295  detectedFormat = archiveFile.getMIMEType();
296 
297  if (detectedFormat == null) {
298  logger.log(Level.WARNING, "Could not detect format for file: {0}", archiveFile); //NON-NLS
299 
300  // if we don't have attribute info then use file extension
301  String extension = archiveFile.getNameExtension();
302  if ("rar".equals(extension)) //NON-NLS
303  {
304  // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
305  // it will be opened incorrectly when using 7zip's built-in auto-detect functionality
306  return RAR;
307  }
308 
309  // Otherwise open the archive using 7zip's built-in auto-detect functionality
310  return null;
311  } else if (detectedFormat.contains("application/x-rar-compressed")) //NON-NLS
312  {
313  // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
314  // it will be opened incorrectly when using 7zip's built-in auto-detect functionality
315  return RAR;
316  }
317 
318  // Otherwise open the archive using 7zip's built-in auto-detect functionality
319  return null;
320  }
321 
332  private long getRootArchiveId(AbstractFile file) throws TskCoreException {
333  long id = file.getId();
334  Content parentContent = file.getParent();
335  while (parentContent != null) {
336  id = parentContent.getId();
337  parentContent = parentContent.getParent();
338  }
339  return id;
340  }
341 
356  private List<AbstractFile> getAlreadyExtractedFiles(AbstractFile archiveFile, String archiveFilePath) throws TskCoreException, NoCurrentCaseException {
357  //check if already has derived files, skip
358  //check if local unpacked dir exists
359  if (archiveFile.hasChildren() && new File(moduleDirAbsolute, EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)).exists()) {
360  return Case.getCurrentCaseThrows().getServices().getFileManager().findFilesByParentPath(getRootArchiveId(archiveFile), archiveFilePath);
361  }
362  return new ArrayList<>();
363  }
364 
372  private String getArchiveFilePath(AbstractFile archiveFile) {
373  return archiveFile.getParentPath() + archiveFile.getName();
374  }
375 
382  private void makeLocalDirectories(String uniqueArchiveFileName) {
383  final String localRootAbsPath = getLocalRootAbsPath(uniqueArchiveFileName);
384  final File localRoot = new File(localRootAbsPath);
385  if (!localRoot.exists()) {
386  localRoot.mkdirs();
387  }
388  }
389 
402  private String getPathInArchive(ISevenZipInArchive archive, int inArchiveItemIndex, AbstractFile archiveFile) throws SevenZipException {
403  String pathInArchive = (String) archive.getProperty(
404  inArchiveItemIndex, PropID.PATH);
405 
406  if (pathInArchive == null || pathInArchive.isEmpty()) {
407  //some formats (.tar.gz) may not be handled correctly -- file in archive has no name/path
408  //handle this for .tar.gz and tgz but assuming the child is tar,
409  //otherwise, unpack using itemNumber as name
410 
411  //TODO this should really be signature based, not extension based
412  String archName = archiveFile.getName();
413  int dotI = archName.lastIndexOf(".");
414  String useName = null;
415  if (dotI != -1) {
416  String base = archName.substring(0, dotI);
417  String ext = archName.substring(dotI);
418  int colonIndex = ext.lastIndexOf(":");
419  if (colonIndex != -1) {
420  // If alternate data stream is found, fix the name
421  // so Windows doesn't choke on the colon character.
422  ext = ext.substring(0, colonIndex);
423  }
424  switch (ext) {
425  case ".gz": //NON-NLS
426  useName = base;
427  break;
428  case ".tgz": //NON-NLS
429  useName = base + ".tar"; //NON-NLS
430  break;
431  case ".bz2": //NON-NLS
432  useName = base;
433  break;
434  }
435  }
436  if (useName == null) {
437  pathInArchive = "/" + archName + "/" + Integer.toString(inArchiveItemIndex);
438  } else {
439  pathInArchive = "/" + useName;
440  }
441  String msg = NbBundle.getMessage(SevenZipExtractor.class,
442  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg",
443  getArchiveFilePath(archiveFile), pathInArchive);
444  logger.log(Level.WARNING, msg);
445  }
446  return pathInArchive;
447  }
448 
449  /*
450  * Get the String that will represent the key for the hashmap which keeps
451  * track of existing files from an AbstractFile
452  */
453  private String getKeyAbstractFile(AbstractFile fileInDatabase) {
454  return fileInDatabase == null ? null : fileInDatabase.getParentPath() + fileInDatabase.getName();
455  }
456 
457  /*
458  * Get the String that will represent the key for the hashmap which keeps
459  * track of existing files from an unpacked node and the archiveFilePath
460  */
461  private String getKeyFromUnpackedNode(UnpackedTree.UnpackedNode node, String archiveFilePath) {
462  return node == null ? null : archiveFilePath + "/" + node.getFileName();
463  }
464 
474  void unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap) {
475  unpack(archiveFile, depthMap, null);
476  }
477 
489  @Messages({"SevenZipExtractor.indexError.message=Failed to index encryption detected artifact for keyword search.",
490  "# {0} - rootArchive",
491  "SevenZipExtractor.zipBombArtifactCreation.text=Zip Bomb Detected {0}"})
492  boolean unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap, String password) {
493  boolean unpackSuccessful = true; //initialized to true change to false if any files fail to extract and
494  boolean hasEncrypted = false;
495  boolean fullEncryption = true;
496  boolean progressStarted = false;
497  final String archiveFilePath = getArchiveFilePath(archiveFile);
498  final String escapedArchiveFilePath = FileUtil.escapeFileName(archiveFilePath);
499  HashMap<String, ZipFileStatusWrapper> statusMap = new HashMap<>();
500  List<AbstractFile> unpackedFiles = Collections.<AbstractFile>emptyList();
501  ISevenZipInArchive inArchive = null;
502  currentArchiveName = archiveFile.getName();
503 
504  SevenZipContentReadStream stream = null;
505  progress = ProgressHandle.createHandle(Bundle.EmbeddedFileExtractorIngestModule_ArchiveExtractor_moduleName());
506  //recursion depth check for zip bomb
507  Archive parentAr;
508  try {
509  blackboard = Case.getCurrentCaseThrows().getServices().getBlackboard();
510  } catch (NoCurrentCaseException ex) {
511  logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
512  unpackSuccessful = false;
513  return unpackSuccessful;
514  }
515  try {
516 
517  List<AbstractFile> existingFiles = getAlreadyExtractedFiles(archiveFile, archiveFilePath);
518  for (AbstractFile file : existingFiles) {
519  statusMap.put(getKeyAbstractFile(file), new ZipFileStatusWrapper(file, ZipFileStatus.EXISTS));
520  }
521  } catch (TskCoreException e) {
522  logger.log(Level.INFO, "Error checking if file already has been processed, skipping: {0}", escapedArchiveFilePath); //NON-NLS
523  unpackSuccessful = false;
524  return unpackSuccessful;
525  } catch (NoCurrentCaseException ex) {
526  logger.log(Level.INFO, "No open case was found while trying to unpack the archive file {0}", escapedArchiveFilePath); //NON-NLS
527  unpackSuccessful = false;
528  return unpackSuccessful;
529  }
530  parentAr = depthMap.get(archiveFile.getId());
531  if (parentAr == null) {
532  parentAr = new Archive(0, archiveFile.getId(), archiveFile);
533  depthMap.put(archiveFile.getId(), parentAr);
534  } else {
535  Archive rootArchive = depthMap.get(parentAr.getRootArchiveId());
536  if (rootArchive.isFlaggedAsZipBomb()) {
537  //skip this archive as the root archive has already been determined to contain a zip bomb
538  unpackSuccessful = false;
539  return unpackSuccessful;
540  } else if (parentAr.getDepth() == MAX_DEPTH) {
541  String details = NbBundle.getMessage(SevenZipExtractor.class,
542  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb",
543  parentAr.getDepth(), FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
544  flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedArchiveFilePath);
545  unpackSuccessful = false;
546  return unpackSuccessful;
547  }
548  }
549  try {
550  stream = new SevenZipContentReadStream(new ReadContentInputStream(archiveFile));
551  // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
552  // it will be opened incorrectly when using 7zip's built-in auto-detect functionality.
553  // All other archive formats are still opened using 7zip built-in auto-detect functionality.
554  ArchiveFormat options = get7ZipOptions(archiveFile);
555  if (password == null) {
556  inArchive = SevenZip.openInArchive(options, stream);
557  } else {
558  inArchive = SevenZip.openInArchive(options, stream, password);
559  }
560  numItems = inArchive.getNumberOfItems();
561  progress.start(numItems);
562  progressStarted = true;
563 
564  //setup the archive local root folder
565  final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile));
566  try {
567  makeLocalDirectories(uniqueArchiveFileName);
568  } catch (SecurityException e) {
569  logger.log(Level.SEVERE, "Error setting up output path for archive root: {0}", getLocalRootAbsPath(uniqueArchiveFileName)); //NON-NLS
570  //bail
571  unpackSuccessful = false;
572  return unpackSuccessful;
573  }
574 
575  //initialize tree hierarchy to keep track of unpacked file structure
576  SevenZipExtractor.UnpackedTree unpackedTree = new SevenZipExtractor.UnpackedTree(moduleDirRelative + "/" + uniqueArchiveFileName, archiveFile);
577 
578  long freeDiskSpace;
579  try {
580  freeDiskSpace = services.getFreeDiskSpace();
581  } catch (NullPointerException ex) {
582  //If ingest has not been run at least once getFreeDiskSpace() will throw a null pointer exception
583  //currently getFreeDiskSpace always returns DISK_FREE_SPACE_UNKNOWN
584  freeDiskSpace = IngestMonitor.DISK_FREE_SPACE_UNKNOWN;
585  }
586 
587  Map<Integer, InArchiveItemDetails> archiveDetailsMap = new HashMap<>();
588  for (int inArchiveItemIndex = 0; inArchiveItemIndex < numItems; inArchiveItemIndex++) {
589  progress.progress(String.format("%s: Analyzing archive metadata and creating local files (%d of %d)", currentArchiveName, inArchiveItemIndex + 1, numItems), 0);
590  if (isZipBombArchiveItemCheck(archiveFile, inArchive, inArchiveItemIndex, depthMap, escapedArchiveFilePath)) {
591  unpackSuccessful = false;
592  return unpackSuccessful;
593  }
594 
595  String pathInArchive = getPathInArchive(inArchive, inArchiveItemIndex, archiveFile);
596  UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive);
597 
598  final boolean isEncrypted = (Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.ENCRYPTED);
599 
600  if (isEncrypted && password == null) {
601  logger.log(Level.WARNING, "Skipping encrypted file in archive: {0}", pathInArchive); //NON-NLS
602  hasEncrypted = true;
603  unpackSuccessful = false;
604  continue;
605  } else {
606  fullEncryption = false;
607  }
608 
609  // NOTE: item size may return null in case of certain
610  // archiving formats. Eg: BZ2
611  //check if unpacking this file will result in out of disk space
612  //this is additional to zip bomb prevention mechanism
613  Long archiveItemSize = (Long) inArchive.getProperty(
614  inArchiveItemIndex, PropID.SIZE);
615  if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && archiveItemSize != null && archiveItemSize > 0) { //if free space is known and file is not empty.
616  String archiveItemPath = (String) inArchive.getProperty(
617  inArchiveItemIndex, PropID.PATH);
618  long newDiskSpace = freeDiskSpace - archiveItemSize;
619  if (newDiskSpace < MIN_FREE_DISK_SPACE) {
620  String msg = NbBundle.getMessage(SevenZipExtractor.class,
621  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
622  escapedArchiveFilePath, archiveItemPath);
623  String details = NbBundle.getMessage(SevenZipExtractor.class,
624  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
625  services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
626  logger.log(Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new String[]{escapedArchiveFilePath, archiveItemPath}); //NON-NLS
627  logger.log(Level.INFO, "Available disk space: {0}", new Object[]{freeDiskSpace}); //NON-NLS
628  unpackSuccessful = false;
629  continue; //skip this file
630  } else {
631  //update est. disk space during this archive, so we don't need to poll for every file extracted
632  freeDiskSpace = newDiskSpace;
633  }
634  }
635  final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (inArchiveItemIndex / 1000) + File.separator + inArchiveItemIndex + "_" + new File(pathInArchive).getName());
636  final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName;
637  final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName;
638 
639  //create local dirs and empty files before extracted
640  File localFile = new java.io.File(localAbsPath);
641  //cannot rely on files in top-bottom order
642  if (!localFile.exists()) {
643  try {
644  if ((Boolean) inArchive.getProperty(
645  inArchiveItemIndex, PropID.IS_FOLDER)) {
646  localFile.mkdirs();
647  } else {
648  localFile.getParentFile().mkdirs();
649  try {
650  localFile.createNewFile();
651  } catch (IOException e) {
652  logger.log(Level.SEVERE, "Error creating extracted file: "//NON-NLS
653  + localFile.getAbsolutePath(), e);
654  }
655  }
656  } catch (SecurityException e) {
657  logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", //NON-NLS
658  pathInArchive); //NON-NLS
659  //TODO consider bail out / msg to the user
660  }
661  }
662  // skip the rest of this loop if we couldn't create the file
663  //continue will skip details from being added to the map
664  if (localFile.exists() == false) {
665  continue;
666  }
667 
668  //Store archiveItemIndex with local paths and unpackedNode reference.
669  //Necessary for the extract call back to write the current archive
670  //file to the correct disk location and to correctly update it's
671  //corresponding unpackedNode
672  archiveDetailsMap.put(inArchiveItemIndex, new InArchiveItemDetails(
673  unpackedNode, localAbsPath, localRelPath));
674  }
675 
676  int[] extractionIndices = getExtractableFilesFromDetailsMap(archiveDetailsMap);
677 
678  StandardIArchiveExtractCallback archiveCallBack
679  = new StandardIArchiveExtractCallback(
680  inArchive, archiveFile, progress,
681  archiveDetailsMap, password, freeDiskSpace);
682 
683  //According to the documentation, indices in sorted order are optimal
684  //for efficiency. Hence, the HashMap and linear processing of
685  //inArchiveItemIndex. False indicates non-test mode
686  inArchive.extract(extractionIndices, false, archiveCallBack);
687 
688  unpackSuccessful = unpackSuccessful & archiveCallBack.wasSuccessful();
689 
690  archiveDetailsMap = null;
691 
692  // add them to the DB. We wait until the end so that we have the metadata on all of the
693  // intermediate nodes since the order is not guaranteed
694  try {
695  unpackedTree.updateOrAddFileToCaseRec(statusMap, archiveFilePath);
696  unpackedFiles = unpackedTree.getAllFileObjects();
697  //check if children are archives, update archive depth tracking
698  for (int i = 0; i < unpackedFiles.size(); i++) {
699  progress.progress(String.format("%s: Searching for nested archives (%d of %d)", currentArchiveName, i + 1, unpackedFiles.size()));
700  AbstractFile unpackedFile = unpackedFiles.get(i);
701  if (unpackedFile == null) {
702  continue;
703  }
704  if (isSevenZipExtractionSupported(unpackedFile)) {
705  Archive child = new Archive(parentAr.getDepth() + 1, parentAr.getRootArchiveId(), archiveFile);
706  parentAr.addChild(child);
707  depthMap.put(unpackedFile.getId(), child);
708  }
709  unpackedFile.close();
710  }
711 
712  } catch (TskCoreException | NoCurrentCaseException e) {
713  logger.log(Level.SEVERE, "Error populating complete derived file hierarchy from the unpacked dir structure", e); //NON-NLS
714  //TODO decide if anything to cleanup, for now bailing
715  }
716 
717  } catch (SevenZipException ex) {
718  logger.log(Level.WARNING, "Error unpacking file: " + archiveFile, ex); //NON-NLS
719  //inbox message
720 
721  // print a message if the file is allocated
722  if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
723  String msg = NbBundle.getMessage(SevenZipExtractor.class,
724  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg",
725  currentArchiveName);
726  String details = NbBundle.getMessage(SevenZipExtractor.class,
727  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details",
728  escapedArchiveFilePath, ex.getMessage());
729  services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
730  }
731  } finally {
732  if (inArchive != null) {
733  try {
734  inArchive.close();
735  } catch (SevenZipException e) {
736  logger.log(Level.SEVERE, "Error closing archive: " + archiveFile, e); //NON-NLS
737  }
738  }
739 
740  if (stream != null) {
741  try {
742  stream.close();
743  } catch (IOException ex) {
744  logger.log(Level.SEVERE, "Error closing stream after unpacking archive: " + archiveFile, ex); //NON-NLS
745  }
746  }
747 
748  //close progress bar
749  if (progressStarted) {
750  progress.finish();
751  }
752  }
753 
754  //create artifact and send user message
755  if (hasEncrypted) {
756  String encryptionType = fullEncryption ? ENCRYPTION_FULL : ENCRYPTION_FILE_LEVEL;
757  try {
758  BlackboardArtifact artifact = archiveFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
759  artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, EmbeddedFileExtractorModuleFactory.getModuleName(), encryptionType));
760 
761  try {
762  // index the artifact for keyword search
763  blackboard.indexArtifact(artifact);
764  } catch (Blackboard.BlackboardException ex) {
765  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
766  MessageNotifyUtil.Notify.error(
767  Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
768  }
769 
770  services.fireModuleDataEvent(new ModuleDataEvent(EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED));
771  } catch (TskCoreException ex) {
772  logger.log(Level.SEVERE, "Error creating blackboard artifact for encryption detected for file: " + escapedArchiveFilePath, ex); //NON-NLS
773  }
774 
775  String msg = NbBundle.getMessage(SevenZipExtractor.class,
776  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg");
777  String details = NbBundle.getMessage(SevenZipExtractor.class,
778  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details",
779  currentArchiveName, EmbeddedFileExtractorModuleFactory.getModuleName());
780  services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
781  }
782 
783  // adding unpacked extracted derived files to the job after closing relevant resources.
784  if (!unpackedFiles.isEmpty()) {
785  //currently sending a single event for all new files
786  services.fireModuleContentEvent(new ModuleContentEvent(archiveFile));
787  if (context != null) {
788  context.addFilesToJob(unpackedFiles);
789  }
790  }
791  return unpackSuccessful;
792  }
793 
798  private int[] getExtractableFilesFromDetailsMap(
799  Map<Integer, InArchiveItemDetails> archiveDetailsMap) {
800 
801  Integer[] wrappedExtractionIndices = archiveDetailsMap.keySet()
802  .toArray(new Integer[archiveDetailsMap.size()]);
803 
804  return Arrays.stream(wrappedExtractionIndices)
805  .mapToInt(Integer::intValue)
806  .toArray();
807  }
808 
816  private final static class UnpackStream implements ISequentialOutStream {
817 
818  private EncodedFileOutputStream output;
819  private String localAbsPath;
820  private int bytesWritten;
821 
822  UnpackStream(String localAbsPath) throws IOException {
823  this.output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
824  this.localAbsPath = localAbsPath;
825  this.bytesWritten = 0;
826  }
827 
828  public void setNewOutputStream(String localAbsPath) throws IOException {
829  this.output.close();
830  this.output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
831  this.localAbsPath = localAbsPath;
832  this.bytesWritten = 0;
833  }
834 
835  public int getSize() {
836  return bytesWritten;
837  }
838 
839  @Override
840  public int write(byte[] bytes) throws SevenZipException {
841  try {
842  output.write(bytes);
843  this.bytesWritten += bytes.length;
844  } catch (IOException ex) {
845  throw new SevenZipException(
846  NbBundle.getMessage(SevenZipExtractor.class,
847  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg",
848  localAbsPath), ex);
849  }
850  return bytes.length;
851  }
852 
853  public void close() throws IOException {
854  try (EncodedFileOutputStream out = output) {
855  out.flush();
856  }
857  }
858 
859  }
860 
864  private static class InArchiveItemDetails {
865 
866  private final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode;
867  private final String localAbsPath;
868  private final String localRelPath;
869 
871  SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode,
872  String localAbsPath, String localRelPath) {
873  this.unpackedNode = unpackedNode;
874  this.localAbsPath = localAbsPath;
875  this.localRelPath = localRelPath;
876  }
877 
878  public SevenZipExtractor.UnpackedTree.UnpackedNode getUnpackedNode() {
879  return unpackedNode;
880  }
881 
882  public String getLocalAbsPath() {
883  return localAbsPath;
884  }
885 
886  public String getLocalRelPath() {
887  return localRelPath;
888  }
889  }
890 
895  private static class StandardIArchiveExtractCallback
896  implements IArchiveExtractCallback, ICryptoGetTextPassword {
897 
898  private final AbstractFile archiveFile;
899  private final ISevenZipInArchive inArchive;
900  private UnpackStream unpackStream = null;
901  private final Map<Integer, InArchiveItemDetails> archiveDetailsMap;
902  private final ProgressHandle progressHandle;
903 
904  private int inArchiveItemIndex;
905 
906  private long createTimeInSeconds;
907  private long modTimeInSeconds;
908  private long accessTimeInSeconds;
909 
910  private boolean isFolder;
911  private final String password;
912 
913  private boolean unpackSuccessful = true;
914 
915  public StandardIArchiveExtractCallback(ISevenZipInArchive inArchive,
916  AbstractFile archiveFile, ProgressHandle progressHandle,
917  Map<Integer, InArchiveItemDetails> archiveDetailsMap,
918  String password, long freeDiskSpace) {
919 
920  this.inArchive = inArchive;
921  this.progressHandle = progressHandle;
922  this.archiveFile = archiveFile;
923  this.archiveDetailsMap = archiveDetailsMap;
924  this.password = password;
925  }
926 
941  @Override
942  public ISequentialOutStream getStream(int inArchiveItemIndex,
943  ExtractAskMode mode) throws SevenZipException {
944 
945  this.inArchiveItemIndex = inArchiveItemIndex;
946 
947  isFolder = (Boolean) inArchive
948  .getProperty(inArchiveItemIndex, PropID.IS_FOLDER);
949  if (isFolder || mode != ExtractAskMode.EXTRACT) {
950  return null;
951  }
952 
953  final String localAbsPath = archiveDetailsMap.get(
954  inArchiveItemIndex).getLocalAbsPath();
955 
956  //If the Unpackstream has been allocated, then set the Outputstream
957  //to another file rather than creating a new unpack stream. The 7Zip
958  //binding has a memory leak, so creating new unpack streams will not be
959  //dereferenced. As a fix, we create one UnpackStream, and mutate its state,
960  //so that there only exists one 8192 byte buffer in memory per archive.
961  try {
962  if (unpackStream != null) {
963  unpackStream.setNewOutputStream(localAbsPath);
964  } else {
965  unpackStream = new UnpackStream(localAbsPath);
966  }
967  } catch (IOException ex) {
968  logger.log(Level.WARNING, String.format("Error opening or setting new stream " //NON-NLS
969  + "for archive file at %s", localAbsPath), ex.getMessage()); //NON-NLS
970  return null;
971  }
972 
973  return unpackStream;
974  }
975 
984  @Override
985  public void prepareOperation(ExtractAskMode mode) throws SevenZipException {
986  final Date createTime = (Date) inArchive.getProperty(
987  inArchiveItemIndex, PropID.CREATION_TIME);
988  final Date accessTime = (Date) inArchive.getProperty(
989  inArchiveItemIndex, PropID.LAST_ACCESS_TIME);
990  final Date writeTime = (Date) inArchive.getProperty(
991  inArchiveItemIndex, PropID.LAST_WRITE_TIME);
992 
993  createTimeInSeconds = createTime == null ? 0L
994  : createTime.getTime() / 1000;
995  modTimeInSeconds = writeTime == null ? 0L
996  : writeTime.getTime() / 1000;
997  accessTimeInSeconds = accessTime == null ? 0L
998  : accessTime.getTime() / 1000;
999 
1000  progressHandle.progress(archiveFile.getName() + ": "
1001  + (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH),
1003 
1004  }
1005 
1014  @Override
1015  public void setOperationResult(ExtractOperationResult result) throws SevenZipException {
1016  final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode
1017  = archiveDetailsMap.get(inArchiveItemIndex).getUnpackedNode();
1018  final String localRelPath = archiveDetailsMap.get(
1019  inArchiveItemIndex).getLocalRelPath();
1020  if (isFolder) {
1021  unpackedNode.addDerivedInfo(0,
1022  !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1024  localRelPath);
1025  return;
1026  }
1027 
1028  final String localAbsPath = archiveDetailsMap.get(
1029  inArchiveItemIndex).getLocalAbsPath();
1030  if (result != ExtractOperationResult.OK) {
1031  logger.log(Level.WARNING, "Extraction of : {0} encountered error {1}", //NON-NLS
1032  new Object[]{localAbsPath, result});
1033  unpackSuccessful = false;
1034  }
1035 
1036  //record derived data in unode, to be traversed later after unpacking the archive
1037  unpackedNode.addDerivedInfo(unpackStream.getSize(),
1038  !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1040 
1041  try {
1042  unpackStream.close();
1043  } catch (IOException e) {
1044  logger.log(Level.WARNING, "Error closing unpack stream for file: {0}", localAbsPath); //NON-NLS
1045  }
1046  }
1047 
1048  @Override
1049  public void setTotal(long value) throws SevenZipException {
1050  //Not necessary for extract, left intenionally blank
1051  }
1052 
1053  @Override
1054  public void setCompleted(long value) throws SevenZipException {
1055  //Not necessary for extract, left intenionally blank
1056  }
1057 
1065  @Override
1066  public String cryptoGetTextPassword() throws SevenZipException {
1067  return password;
1068  }
1069 
1070  public boolean wasSuccessful() {
1071  return unpackSuccessful;
1072  }
1073  }
1074 
1082  private class UnpackedTree {
1083 
1084  final UnpackedNode rootNode;
1085  private int nodesProcessed = 0;
1086 
1094  UnpackedTree(String localPathRoot, AbstractFile archiveFile) {
1095  this.rootNode = new UnpackedNode();
1096  this.rootNode.setFile(archiveFile);
1097  this.rootNode.setFileName(archiveFile.getName());
1098  this.rootNode.setLocalRelPath(localPathRoot);
1099  }
1100 
1110  UnpackedNode addNode(String filePath) {
1111  String[] toks = filePath.split("[\\/\\\\]");
1112  List<String> tokens = new ArrayList<>();
1113  for (int i = 0; i < toks.length; ++i) {
1114  if (!toks[i].isEmpty()) {
1115  tokens.add(toks[i]);
1116  }
1117  }
1118  return addNode(rootNode, tokens);
1119  }
1120 
1129  private UnpackedNode addNode(UnpackedNode parent, List<String> tokenPath) {
1130  // we found all of the tokens
1131  if (tokenPath.isEmpty()) {
1132  return parent;
1133  }
1134 
1135  // get the next name in the path and look it up
1136  String childName = tokenPath.remove(0);
1137  UnpackedNode child = parent.getChild(childName);
1138  // create new node
1139  if (child == null) {
1140  child = new UnpackedNode(childName, parent);
1141  parent.addChild(child);
1142  }
1143 
1144  // go down one more level
1145  return addNode(child, tokenPath);
1146  }
1147 
1154  List<AbstractFile> getRootFileObjects() {
1155  List<AbstractFile> ret = new ArrayList<>();
1156  rootNode.getChildren().forEach((child) -> {
1157  ret.add(child.getFile());
1158  });
1159  return ret;
1160  }
1161 
1168  List<AbstractFile> getAllFileObjects() {
1169  List<AbstractFile> ret = new ArrayList<>();
1170  rootNode.getChildren().forEach((child) -> {
1171  getAllFileObjectsRec(ret, child);
1172  });
1173  return ret;
1174  }
1175 
1176  private void getAllFileObjectsRec(List<AbstractFile> list, UnpackedNode parent) {
1177  list.add(parent.getFile());
1178  parent.getChildren().forEach((child) -> {
1179  getAllFileObjectsRec(list, child);
1180  });
1181  }
1182 
1187  void updateOrAddFileToCaseRec(HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath) throws TskCoreException, NoCurrentCaseException {
1189  for (UnpackedNode child : rootNode.getChildren()) {
1190  updateOrAddFileToCaseRec(child, fileManager, statusMap, archiveFilePath);
1191  }
1192  }
1193 
1208  private void updateOrAddFileToCaseRec(UnpackedNode node, FileManager fileManager, HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath) throws TskCoreException {
1209  DerivedFile df;
1210  progress.progress(String.format("%s: Adding/updating files in case database (%d of %d)", currentArchiveName, ++nodesProcessed, numItems));
1211  try {
1212  String nameInDatabase = getKeyFromUnpackedNode(node, archiveFilePath);
1213  ZipFileStatusWrapper existingFile = nameInDatabase == null ? null : statusMap.get(nameInDatabase);
1214  if (existingFile == null) {
1215  df = fileManager.addDerivedFile(node.getFileName(), node.getLocalRelPath(), node.getSize(),
1216  node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1217  node.isIsFile(), node.getParent().getFile(), "", EmbeddedFileExtractorModuleFactory.getModuleName(),
1218  "", "", TskData.EncodingType.XOR1);
1219  statusMap.put(getKeyAbstractFile(df), new ZipFileStatusWrapper(df, ZipFileStatus.EXISTS));
1220  } else {
1221  String key = getKeyAbstractFile(existingFile.getFile());
1222  if (existingFile.getStatus() == ZipFileStatus.EXISTS && existingFile.getFile().getSize() < node.getSize()) {
1223  existingFile.setStatus(ZipFileStatus.UPDATE);
1224  statusMap.put(key, existingFile);
1225  }
1226  if (existingFile.getStatus() == ZipFileStatus.UPDATE) {
1227  //if the we are updating a file and its mime type was octet-stream we want to re-type it
1228  String mimeType = existingFile.getFile().getMIMEType().equalsIgnoreCase("application/octet-stream") ? null : existingFile.getFile().getMIMEType();
1229  df = fileManager.updateDerivedFile((DerivedFile) existingFile.getFile(), node.getLocalRelPath(), node.getSize(),
1230  node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1231  node.isIsFile(), mimeType, "", EmbeddedFileExtractorModuleFactory.getModuleName(),
1232  "", "", TskData.EncodingType.XOR1);
1233  } else {
1234  //ALREADY CURRENT - SKIP
1235  statusMap.put(key, new ZipFileStatusWrapper(existingFile.getFile(), ZipFileStatus.SKIP));
1236  df = (DerivedFile) existingFile.getFile();
1237  }
1238  }
1239  node.setFile(df);
1240  } catch (TskCoreException ex) {
1241  logger.log(Level.SEVERE, "Error adding a derived file to db:" + node.getFileName(), ex); //NON-NLS
1242  throw new TskCoreException(
1243  NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg",
1244  node.getFileName()), ex);
1245  }
1246  //recurse adding the children if this file was incomplete the children presumably need to be added
1247  for (UnpackedNode child : node.getChildren()) {
1248  updateOrAddFileToCaseRec(child, fileManager, statusMap, getKeyFromUnpackedNode(node, archiveFilePath));
1249  }
1250  }
1251 
1255  private class UnpackedNode {
1256 
1257  private String fileName;
1258  private AbstractFile file;
1259  private final List<UnpackedNode> children = new ArrayList<>();
1260  private String localRelPath = "";
1261  private long size;
1262  private long ctime, crtime, atime, mtime;
1263  private boolean isFile;
1265 
1266  //root constructor
1267  UnpackedNode() {
1268  }
1269 
1270  //child node constructor
1271  UnpackedNode(String fileName, UnpackedNode parent) {
1272  this.fileName = fileName;
1273  this.parent = parent;
1274  this.localRelPath = parent.getLocalRelPath() + File.separator + fileName;
1275  }
1276 
1277  long getCtime() {
1278  return ctime;
1279  }
1280 
1281  long getCrtime() {
1282  return crtime;
1283  }
1284 
1285  long getAtime() {
1286  return atime;
1287  }
1288 
1289  long getMtime() {
1290  return mtime;
1291  }
1292 
1293  void setFileName(String fileName) {
1294  this.fileName = fileName;
1295  }
1296 
1302  void addChild(UnpackedNode child) {
1303  children.add(child);
1304  }
1305 
1312  List<UnpackedNode> getChildren() {
1313  return children;
1314  }
1315 
1321  UnpackedNode getParent() {
1322  return parent;
1323  }
1324 
1325  void addDerivedInfo(long size,
1326  boolean isFile,
1327  long ctime, long crtime, long atime, long mtime, String relLocalPath) {
1328  this.size = size;
1329  this.isFile = isFile;
1330  this.ctime = ctime;
1331  this.crtime = crtime;
1332  this.atime = atime;
1333  this.mtime = mtime;
1334  this.localRelPath = relLocalPath;
1335  }
1336 
1337  void setFile(AbstractFile file) {
1338  this.file = file;
1339  }
1340 
1348  UnpackedNode getChild(String childFileName) {
1349  UnpackedNode ret = null;
1350  for (UnpackedNode child : children) {
1351  if (child.getFileName().equals(childFileName)) {
1352  ret = child;
1353  break;
1354  }
1355  }
1356  return ret;
1357  }
1358 
1359  String getFileName() {
1360  return fileName;
1361  }
1362 
1363  AbstractFile getFile() {
1364  return file;
1365  }
1366 
1367  String getLocalRelPath() {
1368  return localRelPath;
1369  }
1370 
1377  void setLocalRelPath(String localRelativePath) {
1378  localRelPath = localRelativePath;
1379  }
1380 
1381  long getSize() {
1382  return size;
1383  }
1384 
1385  boolean isIsFile() {
1386  return isFile;
1387  }
1388  }
1389  }
1390 
1395  static class Archive {
1396 
1397  //depth will be 0 for the root archive unpack was called on, and increase as unpack recurses down through archives contained within
1398  private final int depth;
1399  private final List<Archive> children;
1400  private final long rootArchiveId;
1401  private boolean flaggedAsZipBomb = false;
1402  private final AbstractFile archiveFile;
1403 
1416  Archive(int depth, long rootArchiveId, AbstractFile archiveFile) {
1417  this.children = new ArrayList<>();
1418  this.depth = depth;
1419  this.rootArchiveId = rootArchiveId;
1420  this.archiveFile = archiveFile;
1421  }
1422 
1429  void addChild(Archive child) {
1430  children.add(child);
1431  }
1432 
1437  synchronized void flagAsZipBomb() {
1438  flaggedAsZipBomb = true;
1439  }
1440 
1446  synchronized boolean isFlaggedAsZipBomb() {
1447  return flaggedAsZipBomb;
1448  }
1449 
1455  AbstractFile getArchiveFile() {
1456  return archiveFile;
1457  }
1458 
1464  long getRootArchiveId() {
1465  return rootArchiveId;
1466  }
1467 
1473  long getObjectId() {
1474  return archiveFile.getId();
1475  }
1476 
1484  int getDepth() {
1485  return depth;
1486  }
1487  }
1488 
1493  private final class ZipFileStatusWrapper {
1494 
1495  private final AbstractFile abstractFile;
1497 
1505  private ZipFileStatusWrapper(AbstractFile file, ZipFileStatus status) {
1506  abstractFile = file;
1507  zipStatus = status;
1508  }
1509 
1515  private AbstractFile getFile() {
1516  return abstractFile;
1517  }
1518 
1525  return zipStatus;
1526  }
1527 
1533  private void setStatus(ZipFileStatus status) {
1534  zipStatus = status;
1535  }
1536 
1537  }
1538 
1543  private enum ZipFileStatus {
1544  UPDATE, //Should be updated //NON-NLS
1545  SKIP, //File is current can be skipped //NON-NLS
1546  EXISTS //File exists but it is unknown if it is current //NON-NLS
1547  }
1548 }
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: Fri Mar 22 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.