Autopsy  4.9.1
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.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 = archiveFile.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  }
710 
711  } catch (TskCoreException | NoCurrentCaseException e) {
712  logger.log(Level.SEVERE, "Error populating complete derived file hierarchy from the unpacked dir structure", e); //NON-NLS
713  //TODO decide if anything to cleanup, for now bailing
714  }
715 
716  } catch (SevenZipException ex) {
717  logger.log(Level.WARNING, "Error unpacking file: " + archiveFile, ex); //NON-NLS
718  //inbox message
719 
720  // print a message if the file is allocated
721  if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
722  String msg = NbBundle.getMessage(SevenZipExtractor.class,
723  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg",
724  currentArchiveName);
725  String details = NbBundle.getMessage(SevenZipExtractor.class,
726  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details",
727  escapedArchiveFilePath, ex.getMessage());
728  services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
729  }
730  } finally {
731  if (inArchive != null) {
732  try {
733  inArchive.close();
734  } catch (SevenZipException e) {
735  logger.log(Level.SEVERE, "Error closing archive: " + archiveFile, e); //NON-NLS
736  }
737  }
738 
739  if (stream != null) {
740  try {
741  stream.close();
742  } catch (IOException ex) {
743  logger.log(Level.SEVERE, "Error closing stream after unpacking archive: " + archiveFile, ex); //NON-NLS
744  }
745  }
746 
747  //close progress bar
748  if (progressStarted) {
749  progress.finish();
750  }
751  }
752 
753  //create artifact and send user message
754  if (hasEncrypted) {
755  String encryptionType = fullEncryption ? ENCRYPTION_FULL : ENCRYPTION_FILE_LEVEL;
756  try {
757  BlackboardArtifact artifact = archiveFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
758  artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, EmbeddedFileExtractorModuleFactory.getModuleName(), encryptionType));
759 
760  try {
761  // index the artifact for keyword search
762  blackboard.indexArtifact(artifact);
763  } catch (Blackboard.BlackboardException ex) {
764  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
765  MessageNotifyUtil.Notify.error(
766  Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
767  }
768 
769  services.fireModuleDataEvent(new ModuleDataEvent(EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED));
770  } catch (TskCoreException ex) {
771  logger.log(Level.SEVERE, "Error creating blackboard artifact for encryption detected for file: " + escapedArchiveFilePath, ex); //NON-NLS
772  }
773 
774  String msg = NbBundle.getMessage(SevenZipExtractor.class,
775  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg");
776  String details = NbBundle.getMessage(SevenZipExtractor.class,
777  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.details",
778  currentArchiveName, EmbeddedFileExtractorModuleFactory.getModuleName());
779  services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
780  }
781 
782  // adding unpacked extracted derived files to the job after closing relevant resources.
783  if (!unpackedFiles.isEmpty()) {
784  //currently sending a single event for all new files
785  services.fireModuleContentEvent(new ModuleContentEvent(archiveFile));
786  if (context != null) {
787  context.addFilesToJob(unpackedFiles);
788  }
789  }
790  return unpackSuccessful;
791  }
792 
797  private int[] getExtractableFilesFromDetailsMap(
798  Map<Integer, InArchiveItemDetails> archiveDetailsMap) {
799 
800  Integer[] wrappedExtractionIndices = archiveDetailsMap.keySet()
801  .toArray(new Integer[archiveDetailsMap.size()]);
802 
803  return Arrays.stream(wrappedExtractionIndices)
804  .mapToInt(Integer::intValue)
805  .toArray();
806  }
807 
815  private final static class UnpackStream implements ISequentialOutStream {
816 
817  private EncodedFileOutputStream output;
818  private String localAbsPath;
819  private int bytesWritten;
820 
821  UnpackStream(String localAbsPath) throws IOException {
822  this.output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
823  this.localAbsPath = localAbsPath;
824  this.bytesWritten = 0;
825  }
826 
827  public void setNewOutputStream(String localAbsPath) throws IOException {
828  this.output.close();
829  this.output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
830  this.localAbsPath = localAbsPath;
831  this.bytesWritten = 0;
832  }
833 
834  public int getSize() {
835  return bytesWritten;
836  }
837 
838  @Override
839  public int write(byte[] bytes) throws SevenZipException {
840  try {
841  output.write(bytes);
842  this.bytesWritten += bytes.length;
843  } catch (IOException ex) {
844  throw new SevenZipException(
845  NbBundle.getMessage(SevenZipExtractor.class,
846  "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg",
847  localAbsPath), ex);
848  }
849  return bytes.length;
850  }
851 
852  public void close() throws IOException {
853  try (EncodedFileOutputStream out = output) {
854  out.flush();
855  }
856  }
857 
858  }
859 
863  private static class InArchiveItemDetails {
864 
865  private final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode;
866  private final String localAbsPath;
867  private final String localRelPath;
868 
870  SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode,
871  String localAbsPath, String localRelPath) {
872  this.unpackedNode = unpackedNode;
873  this.localAbsPath = localAbsPath;
874  this.localRelPath = localRelPath;
875  }
876 
877  public SevenZipExtractor.UnpackedTree.UnpackedNode getUnpackedNode() {
878  return unpackedNode;
879  }
880 
881  public String getLocalAbsPath() {
882  return localAbsPath;
883  }
884 
885  public String getLocalRelPath() {
886  return localRelPath;
887  }
888  }
889 
894  private static class StandardIArchiveExtractCallback
895  implements IArchiveExtractCallback, ICryptoGetTextPassword {
896 
897  private final AbstractFile archiveFile;
898  private final ISevenZipInArchive inArchive;
899  private UnpackStream unpackStream = null;
900  private final Map<Integer, InArchiveItemDetails> archiveDetailsMap;
901  private final ProgressHandle progressHandle;
902 
903  private int inArchiveItemIndex;
904 
905  private long createTimeInSeconds;
906  private long modTimeInSeconds;
907  private long accessTimeInSeconds;
908 
909  private boolean isFolder;
910  private final String password;
911 
912  private boolean unpackSuccessful = true;
913 
914  public StandardIArchiveExtractCallback(ISevenZipInArchive inArchive,
915  AbstractFile archiveFile, ProgressHandle progressHandle,
916  Map<Integer, InArchiveItemDetails> archiveDetailsMap,
917  String password, long freeDiskSpace) {
918 
919  this.inArchive = inArchive;
920  this.progressHandle = progressHandle;
921  this.archiveFile = archiveFile;
922  this.archiveDetailsMap = archiveDetailsMap;
923  this.password = password;
924  }
925 
940  @Override
941  public ISequentialOutStream getStream(int inArchiveItemIndex,
942  ExtractAskMode mode) throws SevenZipException {
943 
944  this.inArchiveItemIndex = inArchiveItemIndex;
945 
946  isFolder = (Boolean) inArchive
947  .getProperty(inArchiveItemIndex, PropID.IS_FOLDER);
948  if (isFolder || mode != ExtractAskMode.EXTRACT) {
949  return null;
950  }
951 
952  final String localAbsPath = archiveDetailsMap.get(
953  inArchiveItemIndex).getLocalAbsPath();
954 
955  //If the Unpackstream has been allocated, then set the Outputstream
956  //to another file rather than creating a new unpack stream. The 7Zip
957  //binding has a memory leak, so creating new unpack streams will not be
958  //dereferenced. As a fix, we create one UnpackStream, and mutate its state,
959  //so that there only exists one 8192 byte buffer in memory per archive.
960  try {
961  if (unpackStream != null) {
962  unpackStream.setNewOutputStream(localAbsPath);
963  } else {
964  unpackStream = new UnpackStream(localAbsPath);
965  }
966  } catch (IOException ex) {
967  logger.log(Level.WARNING, String.format("Error opening or setting new stream " //NON-NLS
968  + "for archive file at %s", localAbsPath), ex.getMessage()); //NON-NLS
969  return null;
970  }
971 
972  return unpackStream;
973  }
974 
983  @Override
984  public void prepareOperation(ExtractAskMode mode) throws SevenZipException {
985  final Date createTime = (Date) inArchive.getProperty(
986  inArchiveItemIndex, PropID.CREATION_TIME);
987  final Date accessTime = (Date) inArchive.getProperty(
988  inArchiveItemIndex, PropID.LAST_ACCESS_TIME);
989  final Date writeTime = (Date) inArchive.getProperty(
990  inArchiveItemIndex, PropID.LAST_WRITE_TIME);
991 
992  createTimeInSeconds = createTime == null ? 0L
993  : createTime.getTime() / 1000;
994  modTimeInSeconds = writeTime == null ? 0L
995  : writeTime.getTime() / 1000;
996  accessTimeInSeconds = accessTime == null ? 0L
997  : accessTime.getTime() / 1000;
998 
999  progressHandle.progress(archiveFile.getName() + ": "
1000  + (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH),
1002 
1003  }
1004 
1013  @Override
1014  public void setOperationResult(ExtractOperationResult result) throws SevenZipException {
1015  final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode
1016  = archiveDetailsMap.get(inArchiveItemIndex).getUnpackedNode();
1017  final String localRelPath = archiveDetailsMap.get(
1018  inArchiveItemIndex).getLocalRelPath();
1019  if (isFolder) {
1020  unpackedNode.addDerivedInfo(0,
1021  !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1023  localRelPath);
1024  return;
1025  }
1026 
1027  final String localAbsPath = archiveDetailsMap.get(
1028  inArchiveItemIndex).getLocalAbsPath();
1029  if (result != ExtractOperationResult.OK) {
1030  logger.log(Level.WARNING, "Extraction of : {0} encountered error {1}", //NON-NLS
1031  new Object[]{localAbsPath, result});
1032  unpackSuccessful = false;
1033  }
1034 
1035  //record derived data in unode, to be traversed later after unpacking the archive
1036  unpackedNode.addDerivedInfo(unpackStream.getSize(),
1037  !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
1039 
1040  try {
1041  unpackStream.close();
1042  } catch (IOException e) {
1043  logger.log(Level.WARNING, "Error closing unpack stream for file: {0}", localAbsPath); //NON-NLS
1044  }
1045  }
1046 
1047  @Override
1048  public void setTotal(long value) throws SevenZipException {
1049  //Not necessary for extract, left intenionally blank
1050  }
1051 
1052  @Override
1053  public void setCompleted(long value) throws SevenZipException {
1054  //Not necessary for extract, left intenionally blank
1055  }
1056 
1064  @Override
1065  public String cryptoGetTextPassword() throws SevenZipException {
1066  return password;
1067  }
1068 
1069  public boolean wasSuccessful() {
1070  return unpackSuccessful;
1071  }
1072  }
1073 
1081  private class UnpackedTree {
1082 
1083  final UnpackedNode rootNode;
1084  private int nodesProcessed = 0;
1085 
1093  UnpackedTree(String localPathRoot, AbstractFile archiveFile) {
1094  this.rootNode = new UnpackedNode();
1095  this.rootNode.setFile(archiveFile);
1096  this.rootNode.setFileName(archiveFile.getName());
1097  this.rootNode.setLocalRelPath(localPathRoot);
1098  }
1099 
1109  UnpackedNode addNode(String filePath) {
1110  String[] toks = filePath.split("[\\/\\\\]");
1111  List<String> tokens = new ArrayList<>();
1112  for (int i = 0; i < toks.length; ++i) {
1113  if (!toks[i].isEmpty()) {
1114  tokens.add(toks[i]);
1115  }
1116  }
1117  return addNode(rootNode, tokens);
1118  }
1119 
1128  private UnpackedNode addNode(UnpackedNode parent, List<String> tokenPath) {
1129  // we found all of the tokens
1130  if (tokenPath.isEmpty()) {
1131  return parent;
1132  }
1133 
1134  // get the next name in the path and look it up
1135  String childName = tokenPath.remove(0);
1136  UnpackedNode child = parent.getChild(childName);
1137  // create new node
1138  if (child == null) {
1139  child = new UnpackedNode(childName, parent);
1140  parent.addChild(child);
1141  }
1142 
1143  // go down one more level
1144  return addNode(child, tokenPath);
1145  }
1146 
1153  List<AbstractFile> getRootFileObjects() {
1154  List<AbstractFile> ret = new ArrayList<>();
1155  rootNode.getChildren().forEach((child) -> {
1156  ret.add(child.getFile());
1157  });
1158  return ret;
1159  }
1160 
1167  List<AbstractFile> getAllFileObjects() {
1168  List<AbstractFile> ret = new ArrayList<>();
1169  rootNode.getChildren().forEach((child) -> {
1170  getAllFileObjectsRec(ret, child);
1171  });
1172  return ret;
1173  }
1174 
1175  private void getAllFileObjectsRec(List<AbstractFile> list, UnpackedNode parent) {
1176  list.add(parent.getFile());
1177  parent.getChildren().forEach((child) -> {
1178  getAllFileObjectsRec(list, child);
1179  });
1180  }
1181 
1186  void updateOrAddFileToCaseRec(HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath) throws TskCoreException, NoCurrentCaseException {
1188  for (UnpackedNode child : rootNode.getChildren()) {
1189  updateOrAddFileToCaseRec(child, fileManager, statusMap, archiveFilePath);
1190  }
1191  }
1192 
1207  private void updateOrAddFileToCaseRec(UnpackedNode node, FileManager fileManager, HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath) throws TskCoreException {
1208  DerivedFile df;
1209  progress.progress(String.format("%s: Adding/updating files in case database (%d of %d)", currentArchiveName, ++nodesProcessed, numItems));
1210  try {
1211  String nameInDatabase = getKeyFromUnpackedNode(node, archiveFilePath);
1212  ZipFileStatusWrapper existingFile = nameInDatabase == null ? null : statusMap.get(nameInDatabase);
1213  if (existingFile == null) {
1214  df = fileManager.addDerivedFile(node.getFileName(), node.getLocalRelPath(), node.getSize(),
1215  node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1216  node.isIsFile(), node.getParent().getFile(), "", EmbeddedFileExtractorModuleFactory.getModuleName(),
1217  "", "", TskData.EncodingType.XOR1);
1218  statusMap.put(getKeyAbstractFile(df), new ZipFileStatusWrapper(df, ZipFileStatus.EXISTS));
1219  } else {
1220  String key = getKeyAbstractFile(existingFile.getFile());
1221  if (existingFile.getStatus() == ZipFileStatus.EXISTS && existingFile.getFile().getSize() < node.getSize()) {
1222  existingFile.setStatus(ZipFileStatus.UPDATE);
1223  statusMap.put(key, existingFile);
1224  }
1225  if (existingFile.getStatus() == ZipFileStatus.UPDATE) {
1226  //if the we are updating a file and its mime type was octet-stream we want to re-type it
1227  String mimeType = existingFile.getFile().getMIMEType().equalsIgnoreCase("application/octet-stream") ? null : existingFile.getFile().getMIMEType();
1228  df = fileManager.updateDerivedFile((DerivedFile) existingFile.getFile(), node.getLocalRelPath(), node.getSize(),
1229  node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(),
1230  node.isIsFile(), mimeType, "", EmbeddedFileExtractorModuleFactory.getModuleName(),
1231  "", "", TskData.EncodingType.XOR1);
1232  } else {
1233  //ALREADY CURRENT - SKIP
1234  statusMap.put(key, new ZipFileStatusWrapper(existingFile.getFile(), ZipFileStatus.SKIP));
1235  df = (DerivedFile) existingFile.getFile();
1236  }
1237  }
1238  node.setFile(df);
1239  } catch (TskCoreException ex) {
1240  logger.log(Level.SEVERE, "Error adding a derived file to db:" + node.getFileName(), ex); //NON-NLS
1241  throw new TskCoreException(
1242  NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackedTree.exception.msg",
1243  node.getFileName()), ex);
1244  }
1245  //recurse adding the children if this file was incomplete the children presumably need to be added
1246  for (UnpackedNode child : node.getChildren()) {
1247  updateOrAddFileToCaseRec(child, fileManager, statusMap, getKeyFromUnpackedNode(node, archiveFilePath));
1248  }
1249  }
1250 
1254  private class UnpackedNode {
1255 
1256  private String fileName;
1257  private AbstractFile file;
1258  private final List<UnpackedNode> children = new ArrayList<>();
1259  private String localRelPath = "";
1260  private long size;
1261  private long ctime, crtime, atime, mtime;
1262  private boolean isFile;
1264 
1265  //root constructor
1266  UnpackedNode() {
1267  }
1268 
1269  //child node constructor
1270  UnpackedNode(String fileName, UnpackedNode parent) {
1271  this.fileName = fileName;
1272  this.parent = parent;
1273  this.localRelPath = parent.getLocalRelPath() + File.separator + fileName;
1274  }
1275 
1276  long getCtime() {
1277  return ctime;
1278  }
1279 
1280  long getCrtime() {
1281  return crtime;
1282  }
1283 
1284  long getAtime() {
1285  return atime;
1286  }
1287 
1288  long getMtime() {
1289  return mtime;
1290  }
1291 
1292  void setFileName(String fileName) {
1293  this.fileName = fileName;
1294  }
1295 
1301  void addChild(UnpackedNode child) {
1302  children.add(child);
1303  }
1304 
1311  List<UnpackedNode> getChildren() {
1312  return children;
1313  }
1314 
1320  UnpackedNode getParent() {
1321  return parent;
1322  }
1323 
1324  void addDerivedInfo(long size,
1325  boolean isFile,
1326  long ctime, long crtime, long atime, long mtime, String relLocalPath) {
1327  this.size = size;
1328  this.isFile = isFile;
1329  this.ctime = ctime;
1330  this.crtime = crtime;
1331  this.atime = atime;
1332  this.mtime = mtime;
1333  this.localRelPath = relLocalPath;
1334  }
1335 
1336  void setFile(AbstractFile file) {
1337  this.file = file;
1338  }
1339 
1347  UnpackedNode getChild(String childFileName) {
1348  UnpackedNode ret = null;
1349  for (UnpackedNode child : children) {
1350  if (child.getFileName().equals(childFileName)) {
1351  ret = child;
1352  break;
1353  }
1354  }
1355  return ret;
1356  }
1357 
1358  String getFileName() {
1359  return fileName;
1360  }
1361 
1362  AbstractFile getFile() {
1363  return file;
1364  }
1365 
1366  String getLocalRelPath() {
1367  return localRelPath;
1368  }
1369 
1376  void setLocalRelPath(String localRelativePath) {
1377  localRelPath = localRelativePath;
1378  }
1379 
1380  long getSize() {
1381  return size;
1382  }
1383 
1384  boolean isIsFile() {
1385  return isFile;
1386  }
1387  }
1388  }
1389 
1394  static class Archive {
1395 
1396  //depth will be 0 for the root archive unpack was called on, and increase as unpack recurses down through archives contained within
1397  private final int depth;
1398  private final List<Archive> children;
1399  private final long rootArchiveId;
1400  private boolean flaggedAsZipBomb = false;
1401  private final AbstractFile archiveFile;
1402 
1415  Archive(int depth, long rootArchiveId, AbstractFile archiveFile) {
1416  this.children = new ArrayList<>();
1417  this.depth = depth;
1418  this.rootArchiveId = rootArchiveId;
1419  this.archiveFile = archiveFile;
1420  }
1421 
1428  void addChild(Archive child) {
1429  children.add(child);
1430  }
1431 
1436  synchronized void flagAsZipBomb() {
1437  flaggedAsZipBomb = true;
1438  }
1439 
1445  synchronized boolean isFlaggedAsZipBomb() {
1446  return flaggedAsZipBomb;
1447  }
1448 
1454  AbstractFile getArchiveFile() {
1455  return archiveFile;
1456  }
1457 
1463  long getRootArchiveId() {
1464  return rootArchiveId;
1465  }
1466 
1472  long getObjectId() {
1473  return archiveFile.getId();
1474  }
1475 
1483  int getDepth() {
1484  return depth;
1485  }
1486  }
1487 
1492  private final class ZipFileStatusWrapper {
1493 
1494  private final AbstractFile abstractFile;
1496 
1504  private ZipFileStatusWrapper(AbstractFile file, ZipFileStatus status) {
1505  abstractFile = file;
1506  zipStatus = status;
1507  }
1508 
1514  private AbstractFile getFile() {
1515  return abstractFile;
1516  }
1517 
1524  return zipStatus;
1525  }
1526 
1532  private void setStatus(ZipFileStatus status) {
1533  zipStatus = status;
1534  }
1535 
1536  }
1537 
1542  private enum ZipFileStatus {
1543  UPDATE, //Should be updated //NON-NLS
1544  SKIP, //File is current can be skipped //NON-NLS
1545  EXISTS //File exists but it is unknown if it is current //NON-NLS
1546  }
1547 }
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: Tue Dec 18 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.